@workser/cli 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4090,26 +4090,70 @@ The user can configure **roles** \u2014 named specialists each backed by a local
4090
4090
  (e.g. \`qa\` \u2192 codex, \`designer\` \u2192 claude_code).
4091
4091
 
4092
4092
  \`\`\`
4093
- workser agent list # main agent + configured roles (+ which are runnable)
4094
- workser agent run <role> "<task>" # delegate a focused subtask (runs isolated)
4093
+ workser agent list # main agent + configured roles + which agents are connected
4094
+ workser agent run <role> "<task>" # delegate to a CONFIGURED role (runs isolated)
4095
+ workser agent spawn <agent> "<task>" [--role <label>] [--instructions <text>] [--model <model>]
4096
+ # spin up a TEMPORARY teammate on any connected agent CLI
4095
4097
  workser agent main # show the configured main agent
4096
4098
  \`\`\`
4097
4099
 
4098
4100
  ## How to use it
4099
4101
 
4100
- Run \`workser agent list --json\` first \u2014 it tells you which roles exist **and** which
4101
- are actually runnable on this machine. Delegating to a role that isn't installed just
4102
- fails.
4102
+ Run \`workser agent list --json\` first \u2014 it tells you which roles exist, which are
4103
+ actually runnable on this machine, and which agent CLIs are connected at all
4104
+ (\`spawnable\`). Delegating to a role that isn't installed, or spawning an agent that
4105
+ isn't connected, just fails.
4106
+
4107
+ \`workser agent run <role> "<task>" --json\` runs a **configured** role as an isolated
4108
+ local subagent and returns \`{role, agent, output, exitCode}\`.
4109
+
4110
+ \`workser agent spawn <agent> "<task>" --json\` does the same thing without a
4111
+ pre-configured role \u2014 \`<agent>\` is one of \`claude_code|codex|kimi|opencode|grok\`, any
4112
+ of which can be connected on this machine. Use it to fan work out in parallel for
4113
+ one-off work with no matching role: e.g. spawn \`codex\` to research a topic while you
4114
+ keep working, or run a second \`claude_code\` instance on a different part of the same
4115
+ task. \`--instructions\` sets that teammate's system prompt for this one run \u2014 give it
4116
+ real scope, not just a repeat of the task.
4117
+
4118
+ - Hand off focused subtasks \u2014 review this diff, design this screen, research this API \u2014
4119
+ to keep your own context lean and get a specialized second perspective.
4120
+ - **A non-zero \`exitCode\` means the run failed.** Surface that; don't quietly treat
4121
+ empty output as success.
4122
+ - The subagent doesn't share your context, configured role or spawned. Put everything
4123
+ it needs in the task string (and \`--instructions\` for a spawn); it cannot see the
4124
+ conversation you're in.
4125
+ - Never give a subagent \u2014 configured or spawned \u2014 a task that tells it to delegate or
4126
+ spawn further. It has no supervision loop to stop a runaway chain.
4127
+ `
4128
+ },
4129
+ {
4130
+ topic: "search",
4131
+ title: "Web search",
4132
+ summary: "Google-grounded web search, run server-side \u2014 the API key never reaches you.",
4133
+ commands: ["search"],
4134
+ source: "skills/workser/reference/search.md",
4135
+ body: `# Web search \u2014 ask the internet, not just the model
4103
4136
 
4104
- \`workser agent run <role> "<task>" --json\` runs the role as an **isolated local
4105
- subagent** with its own context and returns \`{role, agent, output, exitCode}\`.
4137
+ \`\`\`
4138
+ workser search "<query>" [-n, --max-results <n>] # Google-grounded web search
4139
+ \`\`\`
4106
4140
 
4107
- - Hand off focused subtasks \u2014 review this diff, design this screen \u2014 to keep your own
4108
- context lean and get a specialized second perspective.
4109
- - **A non-zero \`exitCode\` means the role's run failed.** Surface that; don't quietly
4110
- treat empty output as success.
4111
- - The subagent doesn't share your context. Put everything it needs in the task
4112
- string; it cannot see the conversation you're in.
4141
+ ## Why this exists
4142
+
4143
+ A local agent can drive a browser, but it can't "search the web" without a URL
4144
+ already in hand \u2014 \`browser\` automation needs somewhere to point. This runs a
4145
+ real, grounded Google search server-side (core-api holds the API key; it never
4146
+ reaches the daemon, the CLI, or you) and returns a synthesized answer plus the
4147
+ source links behind it.
4148
+
4149
+ ## Using it well
4150
+
4151
+ - Prefer this over guessing or asking the user when you need current facts,
4152
+ prices, docs for a library, or anything time-sensitive.
4153
+ - The \`answer\` is a synthesized summary \u2014 the \`results\` list is the sources it
4154
+ drew from. Cite or open one of those links if the user needs to verify the
4155
+ claim themselves.
4156
+ - Not project-scoped: it's a general research tool, not tied to a project's data.
4113
4157
  `
4114
4158
  },
4115
4159
  {
@@ -5432,6 +5476,7 @@ function maskToken(token) {
5432
5476
 
5433
5477
  // src/commands/agent.ts
5434
5478
  var import_picocolors14 = __toESM(require_picocolors(), 1);
5479
+ var SPAWNABLE_AGENTS = ["claude_code", "codex", "kimi", "opencode", "grok"];
5435
5480
  function registerAgent(program3) {
5436
5481
  const agent = program3.command("agent").description("Delegate focused subtasks to your configured agent roles (each runs isolated)");
5437
5482
  agent.command("list").description("List the main agent (+ backup) and the configured subagents").action(
@@ -5448,9 +5493,19 @@ function registerAgent(program3) {
5448
5493
  );
5449
5494
  }
5450
5495
  const roles = cfg?.roles ?? [];
5451
- if (!roles.length) return line(import_picocolors14.default.dim("No subagents configured. Add them in the Workser Orbit Agents screen."));
5452
- line(import_picocolors14.default.bold("subagents:"));
5453
- for (const r of roles) line(" " + formatRole(r));
5496
+ if (!roles.length) {
5497
+ line(import_picocolors14.default.dim("No subagents configured. Add them in the Workser Orbit Agents screen."));
5498
+ } else {
5499
+ line(import_picocolors14.default.bold("subagents:"));
5500
+ for (const r of roles) line(" " + formatRole(r));
5501
+ }
5502
+ const detected = cfg?.detected ?? [];
5503
+ const spawnable = detected.filter(
5504
+ (d) => d?.installed && d?.authed !== false
5505
+ );
5506
+ line(
5507
+ import_picocolors14.default.bold("spawnable (workser agent spawn <agent>):") + " " + (spawnable.length ? spawnable.map((d) => toContractId(d.id)).join(", ") : import_picocolors14.default.dim("none connected"))
5508
+ );
5454
5509
  });
5455
5510
  })
5456
5511
  );
@@ -5484,6 +5539,38 @@ function registerAgent(program3) {
5484
5539
  if (exitCode !== 0) process.exitCode = exitCode;
5485
5540
  })
5486
5541
  );
5542
+ agent.command("spawn <agent> <task...>").description(
5543
+ `Spin up a TEMPORARY teammate on any connected agent CLI (not a saved role) for a one-off task. <agent>: ${SPAWNABLE_AGENTS.join("|")}`
5544
+ ).option("--role <label>", "display label for this run (default: <agent>)").option(
5545
+ "--instructions <text>",
5546
+ "system prompt for this one run \u2014 this teammate's expertise/scope"
5547
+ ).option("--model <model>", "model override for the backing CLI").option("--effort <level>", "reasoning effort, where the backing CLI supports it").action(
5548
+ action(async ({ ctx, args, opts }) => {
5549
+ const spawnAgent = args[0];
5550
+ const task = args[1].join(" ");
5551
+ const res = await api(ctx, "/v1/agents/run", {
5552
+ body: {
5553
+ agent: spawnAgent,
5554
+ task,
5555
+ role: opts.role,
5556
+ instructions: opts.instructions,
5557
+ model: opts.model,
5558
+ effort: opts.effort
5559
+ }
5560
+ });
5561
+ const exitCode = res?.exitCode ?? 0;
5562
+ ok(res, () => {
5563
+ if (res?.output) line(res.output);
5564
+ if (exitCode !== 0) {
5565
+ warn(`Spawned ${spawnAgent} exited with code ${exitCode}.`);
5566
+ }
5567
+ });
5568
+ if (exitCode !== 0) process.exitCode = exitCode;
5569
+ })
5570
+ );
5571
+ }
5572
+ function toContractId(id) {
5573
+ return id === "claude" ? "claude_code" : id;
5487
5574
  }
5488
5575
  function formatRole(r) {
5489
5576
  const label = import_picocolors14.default.yellow(r.role);
@@ -6118,9 +6205,32 @@ function extract(response) {
6118
6205
  return String(response);
6119
6206
  }
6120
6207
 
6208
+ // src/commands/search.ts
6209
+ var import_picocolors23 = __toESM(require_picocolors(), 1);
6210
+ function registerSearch(program3) {
6211
+ program3.command("search <query>").description("Search the web (Google-grounded, server-side)").option("-n, --max-results <n>", "max results", "5").action(
6212
+ action(async ({ ctx, args, opts }) => {
6213
+ const res = await api(ctx, "/v1/search", {
6214
+ query: { q: args[0], maxResults: opts.maxResults }
6215
+ });
6216
+ ok(res, () => {
6217
+ if (res?.answer) {
6218
+ line(res.answer);
6219
+ line("");
6220
+ }
6221
+ const results = res?.results ?? [];
6222
+ if (!results.length) return line(import_picocolors23.default.dim("No results."));
6223
+ for (const r of results) {
6224
+ line(`${r.title || import_picocolors23.default.dim("(untitled)")} ${import_picocolors23.default.dim(r.url)}`);
6225
+ }
6226
+ });
6227
+ })
6228
+ );
6229
+ }
6230
+
6121
6231
  // src/index.ts
6122
6232
  var pkg = {
6123
- version: true ? "0.2.1" : "0.0.0-dev"
6233
+ version: true ? "0.2.3" : "0.0.0-dev"
6124
6234
  };
6125
6235
  var program2 = new Command();
6126
6236
  program2.name("workser").description(
@@ -6161,4 +6271,5 @@ registerBusiness(program2);
6161
6271
  registerArtifact(program2);
6162
6272
  registerImage(program2);
6163
6273
  registerAsk(program2);
6274
+ registerSearch(program2);
6164
6275
  program2.parseAsync(process.argv).catch((e) => fail(e));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workser/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Workser CLI — give your local AI agent native DevOps & infrastructure on Workser. The agent runs `workser …` to provision, deploy, and manage real apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -11,23 +11,37 @@ The user can configure **roles** — named specialists each backed by a local CL
11
11
  (e.g. `qa` → codex, `designer` → claude_code).
12
12
 
13
13
  ```
14
- workser agent list # main agent + configured roles (+ which are runnable)
15
- workser agent run <role> "<task>" # delegate a focused subtask (runs isolated)
14
+ workser agent list # main agent + configured roles + which agents are connected
15
+ workser agent run <role> "<task>" # delegate to a CONFIGURED role (runs isolated)
16
+ workser agent spawn <agent> "<task>" [--role <label>] [--instructions <text>] [--model <model>]
17
+ # spin up a TEMPORARY teammate on any connected agent CLI
16
18
  workser agent main # show the configured main agent
17
19
  ```
18
20
 
19
21
  ## How to use it
20
22
 
21
- Run `workser agent list --json` first — it tells you which roles exist **and** which
22
- are actually runnable on this machine. Delegating to a role that isn't installed just
23
- fails.
23
+ Run `workser agent list --json` first — it tells you which roles exist, which are
24
+ actually runnable on this machine, and which agent CLIs are connected at all
25
+ (`spawnable`). Delegating to a role that isn't installed, or spawning an agent that
26
+ isn't connected, just fails.
24
27
 
25
- `workser agent run <role> "<task>" --json` runs the role as an **isolated local
26
- subagent** with its own context and returns `{role, agent, output, exitCode}`.
28
+ `workser agent run <role> "<task>" --json` runs a **configured** role as an isolated
29
+ local subagent and returns `{role, agent, output, exitCode}`.
27
30
 
28
- - Hand off focused subtasks review this diff, design this screen — to keep your own
29
- context lean and get a specialized second perspective.
30
- - **A non-zero `exitCode` means the role's run failed.** Surface that; don't quietly
31
- treat empty output as success.
32
- - The subagent doesn't share your context. Put everything it needs in the task
33
- string; it cannot see the conversation you're in.
31
+ `workser agent spawn <agent> "<task>" --json` does the same thing without a
32
+ pre-configured role `<agent>` is one of `claude_code|codex|kimi|opencode|grok`, any
33
+ of which can be connected on this machine. Use it to fan work out in parallel for
34
+ one-off work with no matching role: e.g. spawn `codex` to research a topic while you
35
+ keep working, or run a second `claude_code` instance on a different part of the same
36
+ task. `--instructions` sets that teammate's system prompt for this one run — give it
37
+ real scope, not just a repeat of the task.
38
+
39
+ - Hand off focused subtasks — review this diff, design this screen, research this API —
40
+ to keep your own context lean and get a specialized second perspective.
41
+ - **A non-zero `exitCode` means the run failed.** Surface that; don't quietly treat
42
+ empty output as success.
43
+ - The subagent doesn't share your context, configured role or spawned. Put everything
44
+ it needs in the task string (and `--instructions` for a spawn); it cannot see the
45
+ conversation you're in.
46
+ - Never give a subagent — configured or spawned — a task that tells it to delegate or
47
+ spawn further. It has no supervision loop to stop a runaway chain.
@@ -0,0 +1,29 @@
1
+ ---
2
+ topic: search
3
+ title: Web search
4
+ summary: Google-grounded web search, run server-side — the API key never reaches you.
5
+ commands: [search]
6
+ ---
7
+
8
+ # Web search — ask the internet, not just the model
9
+
10
+ ```
11
+ workser search "<query>" [-n, --max-results <n>] # Google-grounded web search
12
+ ```
13
+
14
+ ## Why this exists
15
+
16
+ A local agent can drive a browser, but it can't "search the web" without a URL
17
+ already in hand — `browser` automation needs somewhere to point. This runs a
18
+ real, grounded Google search server-side (core-api holds the API key; it never
19
+ reaches the daemon, the CLI, or you) and returns a synthesized answer plus the
20
+ source links behind it.
21
+
22
+ ## Using it well
23
+
24
+ - Prefer this over guessing or asking the user when you need current facts,
25
+ prices, docs for a library, or anything time-sensitive.
26
+ - The `answer` is a synthesized summary — the `results` list is the sources it
27
+ drew from. Cite or open one of those links if the user needs to verify the
28
+ claim themselves.
29
+ - Not project-scoped: it's a general research tool, not tied to a project's data.