Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 164 additions & 86 deletions .github/workflows/publish.yml

Large diffs are not rendered by default.

27 changes: 19 additions & 8 deletions .github/workflows/verify-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ on:
type: choice
options:
- '@agentworkforce/cli'
- 'agentworkforce'
- '@agentworkforce/harness-kit'
- '@agentworkforce/workload-router'
version:
Expand Down Expand Up @@ -54,31 +55,41 @@ jobs:
echo "Verifying ${{ github.event.inputs.package }}@$RESOLVED"

- name: CLI smoke test
if: ${{ github.event.inputs.package == '@agentworkforce/cli' }}
if: ${{ github.event.inputs.package == '@agentworkforce/cli' || github.event.inputs.package == 'agentworkforce' }}
run: |
set -euo pipefail
# The two CLI packages install different bin names: the scoped
# @agentworkforce/cli ships `agent-workforce`; the top-level
# `agentworkforce` package ships `agentworkforce`. The CLI itself
# derives its USAGE bin name from process.argv[1], so we expect
# the help block to use whichever name is on PATH.
if [ "${{ github.event.inputs.package }}" = "agentworkforce" ]; then
BIN="agentworkforce"
else
BIN="agent-workforce"
fi
npm install -g "${{ github.event.inputs.package }}@${{ steps.resolve.outputs.version }}"
# --help should exit 0 and print the usage block.
output=$(agent-workforce --help)
output=$("$BIN" --help)
echo "$output"
echo "$output" | grep -q "Usage: agent-workforce agent" || {
echo "::error::--help output did not include expected usage block" >&2
echo "$output" | grep -q "Usage: $BIN " || {
echo "::error::--help output did not include expected usage block (looking for 'Usage: $BIN ')" >&2
exit 1
}
# Bare invocation should exit non-zero with the usage text.
if agent-workforce > /dev/null 2>&1; then
echo "::error::bare agent-workforce should exit non-zero (missing subcommand)" >&2
if "$BIN" > /dev/null 2>&1; then
echo "::error::bare $BIN should exit non-zero (missing subcommand)" >&2
exit 1
fi
# Unknown persona should exit non-zero and list the catalog.
if agent-workforce agent __definitely_not_a_persona__ > /dev/null 2>&1; then
if "$BIN" agent __definitely_not_a_persona__ > /dev/null 2>&1; then
echo "::error::unknown persona should exit non-zero" >&2
exit 1
fi
echo "CLI smoke test passed."

- name: Library smoke test (ESM import + exports surface)
if: ${{ github.event.inputs.package != '@agentworkforce/cli' }}
if: ${{ github.event.inputs.package != '@agentworkforce/cli' && github.event.inputs.package != 'agentworkforce' }}
run: |
set -euo pipefail
WORKDIR=$(mktemp -d)
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,25 @@ model, system prompt, env vars, MCP servers, and permissions wired up.

### Install

From npm (recommended):

```bash
npm i -g agentworkforce
```

That puts the `agentworkforce` command on your PATH. (The same CLI is also
published as `@agentworkforce/cli`, which exposes the same code under the
historical `agent-workforce` bin name — pick whichever name you prefer.)

From the monorepo checkout:

```bash
corepack pnpm -r build
corepack pnpm --filter @agentworkforce/cli link --global
# or: corepack pnpm --filter agentworkforce link --global
```

`agent-workforce` is now on your PATH. (Or run the built bin directly:
`./packages/cli/dist/cli.js …`.)
(Or run the built bin directly: `./packages/cli/dist/cli.js …`.)

### Usage

Expand Down
16 changes: 16 additions & 0 deletions packages/agentworkforce/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Changelog

All notable changes to `agentworkforce` will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.4.0] - 2026-04-29

### Added

- Initial release. This package installs the same CLI as `@agentworkforce/cli`
under the unscoped `agentworkforce` name, so `npm i -g agentworkforce`
provides the `agentworkforce` command on your PATH.
25 changes: 25 additions & 0 deletions packages/agentworkforce/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# agentworkforce

Top-level installer for the Agent Workforce CLI.

```sh
npm i -g agentworkforce
```

Then:

```sh
agentworkforce agent <persona>[@<tier>]
agentworkforce list [flags]
agentworkforce show <persona>
agentworkforce harness check
```

This package is a thin wrapper around [`@agentworkforce/cli`](https://www.npmjs.com/package/@agentworkforce/cli).
It exists so the global install command and the binary name match the
project name. Functionally identical to installing `@agentworkforce/cli`
and invoking `agent-workforce` — both bins call into the same code path.

See the [main README](https://github.com/AgentWorkforce/workforce#readme)
for the full feature tour, and [`packages/cli/README.md`](https://github.com/AgentWorkforce/workforce/blob/main/packages/cli/README.md)
for command reference.
12 changes: 12 additions & 0 deletions packages/agentworkforce/bin/agentworkforce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env node
// Top-level wrapper. Re-exports the @agentworkforce/cli entry point under the
// shorter `agentworkforce` bin name so users can `npm i -g agentworkforce` and
// run `agentworkforce agent <persona>`. The CLI derives its help-text bin name
// from process.argv[1], so this file's basename (sans extension) is what
// shows up in usage strings.
import { main } from '@agentworkforce/cli/dist/cli.js';

main().catch((err) => {
process.stderr.write(`${err?.stack ?? String(err)}\n`);
process.exit(1);
});
37 changes: 37 additions & 0 deletions packages/agentworkforce/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "agentworkforce",
"version": "0.4.0",
"private": false,
"description": "Top-level installer for the Agent Workforce CLI (installs the `agentworkforce` command). Wraps @agentworkforce/cli.",
"type": "module",
"bin": {
"agentworkforce": "./bin/agentworkforce.js"
},
"files": [
"bin",
"README.md",
"CHANGELOG.md",
"package.json"
],
"scripts": {
"build": "chmod +x bin/agentworkforce.js",
"dev": "chmod +x bin/agentworkforce.js",
"typecheck": "node --check bin/agentworkforce.js",
"lint": "node --check bin/agentworkforce.js",
"test": "node --check bin/agentworkforce.js"
},
"engines": {
"node": ">=22"
},
"dependencies": {
"@agentworkforce/cli": "workspace:*"
},
"repository": {
"type": "git",
"url": "https://github.com/AgentWorkforce/workforce",
"directory": "packages/agentworkforce"
},
"publishConfig": {
"access": "public"
}
}
26 changes: 21 additions & 5 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,33 @@ agent-workforce harness check

## Install

The CLI ships as a `bin` in `@agentworkforce/cli` (which depends on
`@agentworkforce/workload-router` via the pnpm workspace). From the repo
checkout:
The CLI ships under two npm names that point at the same code:

- **`@agentworkforce/cli`** — the scoped package; installs the
`agent-workforce` bin.
- **`agentworkforce`** — a thin top-level wrapper; installs the
`agentworkforce` bin so the global install command and command name match
(`npm i -g agentworkforce`).

Both depend on `@agentworkforce/workload-router` and `@agentworkforce/harness-kit`
via the pnpm workspace. The CLI derives its help-text bin name from
`process.argv[1]`, so `--help` shows whichever name you invoked.

From npm:

```sh
npm i -g agentworkforce # provides `agentworkforce`
# or
npm i -g @agentworkforce/cli # provides `agent-workforce`
```

From the repo checkout:

```sh
corepack pnpm -r build
corepack pnpm --filter @agentworkforce/cli link --global
```

That puts `agent-workforce` on your PATH.

## Selectors

```
Expand Down
35 changes: 25 additions & 10 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,22 @@ import { launchOnMount } from '@relayfile/local-mount';
import ora, { type Ora } from 'ora';
import { loadLocalPersonas, type PersonaSource } from './local-personas.js';

const USAGE = `Usage: agent-workforce <command> [args...]
// Derived from the binary the user actually invoked. Lets the same dist file
// drive multiple npm bins (`agent-workforce` from @agentworkforce/cli,
// `agentworkforce` from the top-level wrapper package) without baking either
// name into the help text. Falls back to `agent-workforce` for the bare
// `node dist/cli.js` case so the test suite, which spawns cli.js directly,
// still sees the historical name.
const BIN_NAME = (() => {
const arg1 = process.argv[1];
if (!arg1) return 'agent-workforce';
const base = arg1.split(/[/\\]/).pop() ?? '';
const stripped = base.replace(/\.(m?js|cjs)$/, '');
if (!stripped || stripped === 'cli') return 'agent-workforce';
return stripped;
})();

const USAGE = `Usage: ${BIN_NAME} <command> [args...]

Commands:
agent [flags] <persona>[@<tier>] [task...]
Expand Down Expand Up @@ -98,12 +113,12 @@ loader implicitly inherits from the same-id persona below. (Override the home
layer path via AGENT_WORKFORCE_CONFIG_DIR.)

Examples:
agent-workforce agent npm-provenance-publisher@best
agent-workforce agent my-posthog@best
agent-workforce agent review@best-value "look at the diff on this branch"
agent-workforce list
agent-workforce show posthog
agent-workforce harness check
${BIN_NAME} agent npm-provenance-publisher@best
${BIN_NAME} agent my-posthog@best
${BIN_NAME} agent review@best-value "look at the diff on this branch"
${BIN_NAME} list
${BIN_NAME} show posthog
${BIN_NAME} harness check
`;

function die(msg: string, withUsage = true): never {
Expand Down Expand Up @@ -984,7 +999,7 @@ function parseListArgs(args: readonly string[]): {
json = true;
} else if (arg === '-h' || arg === '--help') {
process.stdout.write(
'Usage: agent-workforce list [--all] [--json] [--filter-rating <tier>] [--filter-harness <harness>] [--filter-tag <tag>] [--no-display-description]\n'
`Usage: ${BIN_NAME} list [--all] [--json] [--filter-rating <tier>] [--filter-harness <harness>] [--filter-tag <tag>] [--no-display-description]\n`
);
process.exit(0);
} else if (arg === '--all' || arg === '--no-recommended') {
Expand Down Expand Up @@ -1064,7 +1079,7 @@ function parseShowArgs(args: readonly string[]): {
} else if (arg === '--all') {
all = true;
} else if (arg === '-h' || arg === '--help') {
process.stdout.write('Usage: agent-workforce show <persona>[@<tier>] [--all] [--json]\n');
process.stdout.write(`Usage: ${BIN_NAME} show <persona>[@<tier>] [--all] [--json]\n`);
process.exit(0);
} else if (arg.startsWith('--')) {
die(`show: unexpected flag "${arg}".`);
Expand Down Expand Up @@ -1253,7 +1268,7 @@ function runHarnessCheck(): never {
process.exit(0);
}

async function main(): Promise<void> {
export async function main(): Promise<void> {
const argv = process.argv.slice(2);
const [subcommand, ...rest] = argv;

Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading