teamshift 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -11,9 +11,11 @@ npx teamshift add code-audit-swarm # adds it to your workspace
11
11
  npx teamshift run <team-id> "review services/api for auth mistakes"
12
12
  ```
13
13
 
14
- `teamshift teams` lists everything callable, including teams you built yourself
15
- in the portal. `add` is idempotent running it twice will not duplicate a team
16
- or overwrite edits you have made to it.
14
+ `teamshift teams` lists callable teams, including teams you built yourself in the
15
+ portal; `teamshift agents` lists callable individual agents. Keeping the two
16
+ inventories separate prevents a large internal agent library from burying the
17
+ teams a customer actually runs. `add` is idempotent — running it twice will not
18
+ duplicate a team or overwrite edits you have made to it.
17
19
 
18
20
  ## Parallel fan-out
19
21
 
@@ -36,7 +38,9 @@ completed $1.20 4.0s find dead code
36
38
  ```
37
39
 
38
40
  `--file prompts.txt` reads one prompt per line. Concurrency is bounded (default
39
- 5) so a large batch does not trip the API's write rate limit.
41
+ 5) so a large batch does not trip the API's write rate limit. Completed runs
42
+ print their scrubbed deliverable after the status table; `--json` includes it as
43
+ the outcome's `output` field.
40
44
 
41
45
  ## Already using Claude Code or Codex?
42
46
 
@@ -54,12 +58,11 @@ agent, with no second CLI to learn. Both install commands are in the portal unde
54
58
 
55
59
  | Code | Meaning |
56
60
  | --- | --- |
57
- | `0` | every run reached a terminal status |
58
- | `1` | usage error, auth failure, or at least one run failed to dispatch |
61
+ | `0` | every run completed and returned its deliverable |
62
+ | `1` | usage/auth error, timeout, failed run, or missing deliverable |
59
63
  | `2` | out of credit — add credits and rerun |
60
64
 
61
65
  ## Releasing
62
66
 
63
- This package is `private: true` so a workspace-wide publish cannot push it by
64
- accident. Releasing is deliberate: set `private: false`, `pnpm build`, then
65
- `npm publish`.
67
+ Published on npm as `teamshift`. To release: bump `version`, `pnpm build`, run
68
+ the package tests, then `npm publish --access public` from this directory.
package/dist/api.js CHANGED
@@ -73,4 +73,7 @@ export class TeamShiftClient {
73
73
  const body = await this.request(`/v1/runs/${runId}`);
74
74
  return body.run;
75
75
  }
76
+ getRunResult(runId) {
77
+ return this.request(`/v1/runs/${runId}/result`);
78
+ }
76
79
  }
package/dist/bin.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ // The bin entrypoint runs main() unconditionally. Detecting "was I executed
3
+ // directly?" from argv[1] is exactly the 0.1.0 release bug: npm invokes a bin
4
+ // through node_modules/.bin/<command> — a symlink named after the COMMAND — so
5
+ // any filename-based guard fails and the CLI exits silently. Library consumers
6
+ // import from index.ts / cli.ts, which must never self-execute; only this file
7
+ // may have side effects on import.
8
+ import { main } from './cli.js';
9
+ void main();
package/dist/cli.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  import { createInterface } from 'node:readline/promises';
3
2
  import { PaymentRequiredError, TeamShiftClient } from './api.js';
4
3
  import { DEFAULT_API_URL, loadCredentials, maskKey, saveCredentials } from './config.js';
@@ -11,7 +10,8 @@ const USAGE = `teamshift — run agent teams from your terminal
11
10
  teamshift whoami Show the active key and endpoint
12
11
  teamshift catalog Browse prebuilt teams you can add
13
12
  teamshift add <catalog-id> Add a prebuilt team to your workspace
14
- teamshift teams List callable teams and agents
13
+ teamshift teams List callable teams
14
+ teamshift agents List callable agents
15
15
  teamshift run <id> "<prompt>" Run one team, wait, print status and charge
16
16
  teamshift run <id> -p "a" -p "b" Fan out prompts in parallel
17
17
  teamshift run <id> --file prompts.txt One prompt per line, run in parallel
@@ -72,18 +72,16 @@ async function login() {
72
72
  rl.close();
73
73
  }
74
74
  }
75
- async function listCallable(json) {
75
+ async function listCallable(kind, json) {
76
76
  const payload = await requireClient().interop();
77
+ const items = kind === 'team' ? payload.teams : payload.agents;
77
78
  if (json) {
78
- console.log(JSON.stringify(payload, null, 2));
79
+ console.log(JSON.stringify(items, null, 2));
79
80
  return;
80
81
  }
81
- const rows = [
82
- ...payload.teams.map((item) => ['team', item.id, item.name]),
83
- ...payload.agents.map((item) => ['agent', item.id, item.name]),
84
- ];
82
+ const rows = items.map((item) => [kind, item.id, item.name]);
85
83
  if (rows.length === 0) {
86
- console.log('Nothing callable yet — create a team in the portal first.');
84
+ console.log(`No callable ${kind}s yet${kind === 'team' ? ' add one with `teamshift add <id>`.' : '.'}`);
87
85
  return;
88
86
  }
89
87
  console.log(table(['KIND', 'ID', 'NAME'], rows));
@@ -153,9 +151,15 @@ async function run(args) {
153
151
  const serial = outcomes.reduce((sum, outcome) => sum + outcome.elapsedMs, 0);
154
152
  console.log(`\n${outcomes.length} run(s) · ${money(total)} total · ${duration(Date.now() - startedAt)} wall clock ` +
155
153
  `(${duration(serial)} if run one at a time)`);
154
+ for (const outcome of outcomes) {
155
+ if (outcome.output === null || outcome.output === undefined)
156
+ continue;
157
+ const output = typeof outcome.output === 'string' ? outcome.output : JSON.stringify(outcome.output, null, 2);
158
+ console.log(`\n${outcomes.length > 1 ? `[${outcome.prompt}]\n` : ''}${output}`);
159
+ }
156
160
  const failed = outcomes.filter((outcome) => outcome.error).length;
157
161
  if (failed > 0) {
158
- console.log(`${failed} run(s) failed to dispatch rerun just those prompts.`);
162
+ console.log(`${failed} run(s) need attentioninspect the status above before retrying.`);
159
163
  process.exitCode = 1;
160
164
  }
161
165
  }
@@ -187,7 +191,9 @@ export async function main(argv = process.argv.slice(2)) {
187
191
  case 'add':
188
192
  return await addFromCatalog(args);
189
193
  case 'teams':
190
- return await listCallable(args.json);
194
+ return await listCallable('team', args.json);
195
+ case 'agents':
196
+ return await listCallable('agent', args.json);
191
197
  case 'run':
192
198
  return await run(args);
193
199
  default:
@@ -204,6 +210,3 @@ export async function main(argv = process.argv.slice(2)) {
204
210
  process.exit(1);
205
211
  }
206
212
  }
207
- const invokedDirectly = process.argv[1]?.endsWith('cli.js') || process.argv[1]?.endsWith('cli.ts');
208
- if (invokedDirectly)
209
- void main();
package/dist/run.js CHANGED
@@ -1,5 +1,14 @@
1
1
  import { isTerminal } from './format.js';
2
2
  const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3
+ export class RunPollingTimeoutError extends Error {
4
+ runId;
5
+ latest;
6
+ constructor(runId, latest) {
7
+ super(`run ${runId} did not finish before the polling timeout (last status: ${latest.status ?? 'unknown'})`);
8
+ this.runId = runId;
9
+ this.latest = latest;
10
+ }
11
+ }
3
12
  /** Poll one run to a terminal status, or give up at the timeout. */
4
13
  export async function waitForRun(client, runId, options = {}) {
5
14
  const { intervalMs = 2000, timeoutMs = 900_000, now = Date.now, sleep = defaultSleep } = options;
@@ -7,7 +16,7 @@ export async function waitForRun(client, runId, options = {}) {
7
16
  let latest = await client.getRun(runId);
8
17
  while (!isTerminal(latest.status)) {
9
18
  if (now() - startedAt >= timeoutMs)
10
- return latest;
19
+ throw new RunPollingTimeoutError(runId, latest);
11
20
  await sleep(intervalMs);
12
21
  latest = await client.getRun(runId);
13
22
  }
@@ -34,22 +43,39 @@ export async function runBatch(client, kind, id, prompts, options = {}) {
34
43
  if (prompt === undefined)
35
44
  return;
36
45
  const startedAt = now();
46
+ let runId = null;
37
47
  try {
38
- const runId = await client.startRun(kind, id, prompt);
48
+ runId = await client.startRun(kind, id, prompt);
39
49
  const summary = await waitForRun(client, runId, { ...pollOptions, now });
50
+ let output = null;
51
+ let resultError;
52
+ if (summary.status === 'completed') {
53
+ try {
54
+ const result = await client.getRunResult(runId);
55
+ output = result.text ?? result.document;
56
+ }
57
+ catch (error) {
58
+ resultError = `result unavailable: ${error instanceof Error ? error.message : String(error)}`;
59
+ }
60
+ }
61
+ else {
62
+ resultError = `run ended with status ${summary.status ?? 'unknown'}`;
63
+ }
40
64
  results[index] = {
41
65
  prompt,
42
66
  runId,
43
67
  status: String(summary.status ?? 'unknown'),
44
68
  costUsd: summary.cost_estimate_usd ?? null,
45
69
  elapsedMs: now() - startedAt,
70
+ output,
71
+ ...(resultError ? { error: resultError } : {}),
46
72
  };
47
73
  }
48
74
  catch (error) {
49
75
  results[index] = {
50
76
  prompt,
51
- runId: null,
52
- status: 'dispatch_failed',
77
+ runId,
78
+ status: error instanceof RunPollingTimeoutError ? 'poll_timeout' : 'dispatch_failed',
53
79
  costUsd: null,
54
80
  elapsedMs: now() - startedAt,
55
81
  error: error instanceof Error ? error.message : String(error),
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "teamshift",
3
3
  "private": false,
4
- "version": "0.1.0",
5
- "description": "Run TeamShift agent teams from your terminal. Metered by usage no model key of your own required.",
4
+ "version": "0.1.2",
5
+ "description": "Run TeamShift agent teams from your terminal. Metered by usage \u2014 no model key of your own required.",
6
6
  "type": "module",
7
7
  "bin": {
8
- "teamshift": "dist/cli.js"
8
+ "teamshift": "dist/bin.js"
9
9
  },
10
10
  "main": "src/index.ts",
11
11
  "types": "src/index.ts",