@titan-design/active-work 0.1.0 → 0.2.0

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
@@ -43,16 +43,24 @@ active-work task add my-feature --title "Write tests" --priority 2
43
43
  aw my-feature # launches `claude` with the bootstrap prompt
44
44
  ```
45
45
 
46
- `active-work new` scaffolds the directory: brief.md (frontmatter + body), handoff.md, an empty tasks/ folder, and an artifacts.yml. `active-work task add` writes a YAML task file with a sequential ID derived from the initiative's `task_prefix` (e.g. `MF-1`, `MF-2`). `aw <slug>` is the launcher: it calls `active-work open <slug>` under the hood to assemble the bootstrap prompt — brief excerpt, full handoff, last session, top open tasks, open artifacts, time since last session — then execs `claude` with that prompt in the initiative's worktree. Run `aw` with no slug and it resolves the initiative from your current directory — if the cwd sits inside an initiative's registered worktree, it opens that one straight away; otherwise (or with `aw --pick`) it drops to the interactive picker. Use `active-work open <slug>` directly when you want the prompt without launching Claude. To re-seed context inside an already-running session, `active-work prompt [slug]` prints the same bootstrap prompt (cwd-resolved, no side effects); the bundled `/aw-prompt` Claude Code slash command runs it and injects the output. Add `--adhoc` to any of these (`aw <slug> --adhoc`) to reframe the prompt for ad-hoc work on the workstream — the context becomes background rather than a directive to continue the handoff, and the session waits for you to describe the task.
46
+ `active-work new` scaffolds the directory: brief.md (frontmatter + body), handoff.md, an empty tasks/ folder, and an artifacts.yml. `active-work task add` writes a YAML task file with a sequential ID derived from the initiative's `task_prefix` (e.g. `MF-1`, `MF-2`). `aw <slug>` is the launcher: it calls `active-work open <slug>` under the hood to assemble the bootstrap prompt — brief excerpt, open loops with the age of each hang, last session, top open tasks, open artifacts, time since last session — then execs `claude` with that prompt in the initiative's worktree. Run `aw` with no slug and it resolves the initiative from your current directory — if the cwd sits inside an initiative's registered worktree, it opens that one straight away; otherwise (or with `aw --pick`) it drops to the interactive picker. Use `active-work open <slug>` directly when you want the prompt without launching Claude. To re-seed context inside an already-running session, `active-work prompt [slug]` prints the same bootstrap prompt (cwd-resolved, no side effects); the bundled `/aw-prompt` Claude Code slash command runs it and injects the output. Add `--adhoc` to any of these (`aw <slug> --adhoc`) to reframe the prompt for ad-hoc work on the workstream — the context becomes background rather than a directive to continue the handoff, and the session waits for you to describe the task.
47
47
 
48
48
  When you wrap up a session, capture it:
49
49
 
50
50
  ```bash
51
- active-work session record my-feature --session-id <id> \
51
+ active-work wrap my-feature --session-id <id> \
52
52
  --started 2026-05-12T09:00:00Z --ended 2026-05-12T11:30:00Z \
53
- --track canonical --body "Wired up the OAuth flow; tests still pending."
53
+ --track canonical --body "Wired up the OAuth flow; tests still pending." \
54
+ --next-steps '[{"id":"n1","text":"Finish the OAuth tests","kind":"task","ref":"MF-4"}]'
54
55
  ```
55
56
 
57
+ `wrap` writes the session, files the ledger, and bumps `brief.updated` in one
58
+ locked operation. It records what this session left hanging (`--next-steps`) and
59
+ what it closed from earlier sessions (`--resolves`); the unresolved remainder is
60
+ what the next bootstrap shows as open loops, with the age of each hang. It
61
+ refuses an empty ledger unless you state deliberately that nothing is hanging —
62
+ see `active-work wrap --help`.
63
+
56
64
  The skill's "wrap up" / "I'm done" trigger phrases prompt Claude to do this for you automatically.
57
65
 
58
66
  ## Concepts
@@ -110,7 +118,7 @@ The most-used surface, grouped by purpose. Run `active-work <command> --help` fo
110
118
  | Tasks | `active-work task add <slug> --title ... --priority N` | Add a task |
111
119
  | Tasks | `active-work task done <slug> <id>` | Mark a task done |
112
120
  | Tasks | `active-work task list [slug]` | List tasks for an initiative or across all |
113
- | Sessions | `active-work session record <slug> ...` | Capture a session summary |
121
+ | Sessions | `active-work wrap <slug> ...` | End a session: summary + open-loop ledger + brief bump |
114
122
  | Sessions | `aw <slug>` | Launch Claude with the bootstrap prompt |
115
123
  | Sessions | `active-work open <slug>` | Print the bootstrap prompt to stdout (no claude spawn) |
116
124
  | Daemon | `active-work mcp serve [--detach]` | Start the HTTP + MCP daemon |
@@ -118,6 +126,7 @@ The most-used surface, grouped by purpose. Run `active-work <command> --help` fo
118
126
  | Daemon | `active-work mcp stop` | Stop the daemon |
119
127
  | Discover | `active-work discover` | Scan gh / git / projects / Claude sessions for untracked work |
120
128
  | Discover | `active-work audit` | Cross-initiative health summary |
129
+ | Discover | `active-work context graph <id> [--slug S]` | Trace every exact-ID reference to a task id, session, or loop ref |
121
130
 
122
131
  ## Configuration
123
132
 
package/dist/aw.js CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  EXIT,
4
+ acquireLease,
4
5
  color,
5
6
  formatError,
6
7
  getActiveRoot,
7
- open_default
8
- } from "./chunk-OET6AFME.js";
8
+ open_default,
9
+ releaseLease,
10
+ releaseLeaseSync
11
+ } from "./chunk-FM2KVFDO.js";
9
12
 
10
13
  // src/aw.ts
11
14
  import { spawn } from "child_process";
@@ -14,14 +17,68 @@ import * as clackPrompts from "@clack/prompts";
14
17
  // src/launcher-args.ts
15
18
  function buildChannelArgs(channels) {
16
19
  if (!channels || channels.length === 0) return [];
17
- const targets = channels.map(
18
- (raw) => /^(server|plugin):/.test(raw) ? raw : `server:${raw}`
19
- );
20
- return ["--dangerously-load-development-channels", ...targets];
20
+ const targets = channels.map((raw) => /^(server|plugin):/.test(raw) ? raw : `server:${raw}`);
21
+ const plugins = targets.filter((t) => t.startsWith("plugin:"));
22
+ const servers = targets.filter((t) => !t.startsWith("plugin:"));
23
+ return [
24
+ ...plugins.length > 0 ? ["--channels", ...plugins] : [],
25
+ ...servers.length > 0 ? ["--dangerously-load-development-channels", ...servers] : []
26
+ ];
21
27
  }
22
28
  function buildClaudeArgs(prompt, channels) {
23
29
  return [...buildChannelArgs(channels), "--", prompt];
24
30
  }
31
+ var ADHOC_FLAGS = ["--adhoc", "--ad-hoc"];
32
+ function parseLauncherFlags(args) {
33
+ const known = /* @__PURE__ */ new Set(["--pick", ...ADHOC_FLAGS]);
34
+ const positional = args.filter((a) => !known.has(a));
35
+ return {
36
+ pick: args.includes("--pick"),
37
+ adhoc: args.some((a) => ADHOC_FLAGS.includes(a)),
38
+ positional,
39
+ usageError: positional.some((a) => a.startsWith("-")) || positional.length > 1
40
+ };
41
+ }
42
+
43
+ // src/launcher-lease.ts
44
+ var LEASE_ENV_VAR = "AW_LEASE_ID";
45
+ function buildLauncherEnv(base, leaseId) {
46
+ if (!leaseId) return { ...base };
47
+ return { ...base, [LEASE_ENV_VAR]: leaseId };
48
+ }
49
+ var CLEANUP_SIGNALS = ["SIGINT", "SIGTERM"];
50
+ async function withLauncherLease(input, run) {
51
+ const { activeRoot, slug, cwd, pid = process.pid } = input;
52
+ let leaseId;
53
+ try {
54
+ ({ leaseId } = await acquireLease({
55
+ activeRoot,
56
+ slug,
57
+ cwd,
58
+ mode: "launcher",
59
+ pid
60
+ }));
61
+ } catch {
62
+ return run(void 0);
63
+ }
64
+ const id = leaseId;
65
+ const onExit = () => releaseLeaseSync(activeRoot, slug, id);
66
+ const onSignal = (signal) => {
67
+ releaseLeaseSync(activeRoot, slug, id);
68
+ process.removeListener(signal, onSignal);
69
+ process.kill(process.pid, signal);
70
+ };
71
+ process.on("exit", onExit);
72
+ for (const signal of CLEANUP_SIGNALS) process.on(signal, onSignal);
73
+ try {
74
+ return await run(id);
75
+ } finally {
76
+ process.removeListener("exit", onExit);
77
+ for (const signal of CLEANUP_SIGNALS) process.removeListener(signal, onSignal);
78
+ await releaseLease(activeRoot, slug, id).catch(() => {
79
+ });
80
+ }
81
+ }
25
82
 
26
83
  // src/aw.ts
27
84
  async function runOpen(opts = {}) {
@@ -34,7 +91,11 @@ async function runOpen(opts = {}) {
34
91
  const parsed = open_default.args.parse({
35
92
  ...opts.slug ? { slug: opts.slug } : {},
36
93
  ...opts.pick ? { pick: true } : {},
37
- ...opts.adhoc ? { adhoc: true } : {}
94
+ ...opts.adhoc ? { adhoc: true } : {},
95
+ // This launcher records a `launcher` lease of its own around the spawned
96
+ // session, so `open` must not also file a `oneshot` one — two leases for
97
+ // one session make it its own sibling on the next bootstrap.
98
+ lease_mode: "defer"
38
99
  });
39
100
  return await open_default.run(parsed, ctx);
40
101
  }
@@ -47,9 +108,7 @@ var STATE_LABEL = {
47
108
  async function pickInitiative(initiatives) {
48
109
  if (initiatives.length === 0) {
49
110
  process.stderr.write(
50
- color.red(
51
- "No initiatives found. Create one with `active-work new <slug>`.\n"
52
- )
111
+ color.red("No initiatives found. Create one with `active-work new <slug>`.\n")
53
112
  );
54
113
  return null;
55
114
  }
@@ -67,11 +126,14 @@ async function pickInitiative(initiatives) {
67
126
  if (clackPrompts.isCancel(choice)) return null;
68
127
  return String(choice);
69
128
  }
70
- function spawnClaude(prompt, cwd, channels) {
129
+ function spawnClaude(prompt, cwd, channels, leaseId) {
71
130
  return new Promise((resolve) => {
72
131
  const child = spawn("claude", buildClaudeArgs(prompt, channels), {
73
132
  cwd,
74
- stdio: "inherit"
133
+ stdio: "inherit",
134
+ // Explicit env (the default is an implicit `process.env`) so the session
135
+ // can recognize its own lease and not warn about itself.
136
+ env: buildLauncherEnv(process.env, leaseId)
75
137
  });
76
138
  child.on("error", (err) => {
77
139
  const e = err;
@@ -84,10 +146,8 @@ function spawnClaude(prompt, cwd, channels) {
84
146
  resolve(127);
85
147
  return;
86
148
  }
87
- process.stderr.write(
88
- color.red(`error: failed to launch claude: ${err.message}
89
- `)
90
- );
149
+ process.stderr.write(color.red(`error: failed to launch claude: ${err.message}
150
+ `));
91
151
  resolve(EXIT.GENERIC);
92
152
  });
93
153
  child.on("exit", (code, signal) => {
@@ -112,6 +172,7 @@ function printHelp() {
112
172
  " aw <slug> --adhoc",
113
173
  " Frame the session as ad-hoc work on the workstream",
114
174
  " (awaiting your task), not a handoff continuation.",
175
+ " `--ad-hoc` is accepted as an alias.",
115
176
  " aw --help Show this message.",
116
177
  " aw --version Print version.",
117
178
  "",
@@ -130,10 +191,8 @@ async function main(argv) {
130
191
  process.stdout.write("0.1.0\n");
131
192
  process.exit(EXIT.OK);
132
193
  }
133
- const pick = args.includes("--pick");
134
- const adhoc = args.includes("--adhoc");
135
- const positional = args.filter((a) => a !== "--pick" && a !== "--adhoc");
136
- if (positional.some((a) => a.startsWith("-")) || positional.length > 1) {
194
+ const { pick, adhoc, positional, usageError } = parseLauncherFlags(args);
195
+ if (usageError) {
137
196
  process.stderr.write(
138
197
  color.red(
139
198
  "error: `aw` only launches a Claude session for an initiative. Use `active-work` for other commands.\n"
@@ -153,20 +212,19 @@ async function main(argv) {
153
212
  opened = await runOpen({ slug: choice, adhoc });
154
213
  } else {
155
214
  opened = result;
156
- process.stderr.write(
157
- color.dim(
158
- `Opening ${opened.slug} \u2014 matched current directory.
159
- `
160
- )
161
- );
215
+ process.stderr.write(color.dim(`Opening ${opened.slug} \u2014 matched current directory.
216
+ `));
162
217
  }
163
218
  } else {
164
219
  opened = await runOpen({ slug: positional[0], adhoc });
165
220
  }
166
- const code = await spawnClaude(
167
- opened.prompt,
168
- opened.cwd_hint,
169
- opened.channels
221
+ const code = await withLauncherLease(
222
+ {
223
+ activeRoot: getActiveRoot(),
224
+ slug: opened.slug,
225
+ cwd: opened.cwd_hint
226
+ },
227
+ (leaseId) => spawnClaude(opened.prompt, opened.cwd_hint, opened.channels, leaseId)
170
228
  );
171
229
  process.exit(code);
172
230
  } catch (err) {
package/dist/aw.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/aw.ts","../src/launcher-args.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `aw` — thin launcher that bootstraps a Claude Code session for an\n * active-work initiative.\n *\n * Usage:\n * aw [slug] Launch claude with the bootstrap prompt and the\n * initiative's worktree as cwd. Omit the slug to pick\n * one interactively.\n *\n * For the management CLI (new, task, focus, session, etc.), use\n * `active-work`. This launcher deliberately rejects sub-command\n * invocations so the two surfaces stay distinct.\n */\nimport { spawn } from 'node:child_process';\nimport * as clackPrompts from '@clack/prompts';\nimport openCommand from './commands/open.js';\nimport { buildClaudeArgs } from './launcher-args.js';\nimport { getActiveRoot } from './utils/paths.js';\nimport { formatError, EXIT } from './errors.js';\nimport { color } from './utils/color.js';\nimport type { CommandContext } from './registry/index.js';\n\ninterface InitiativeSummary {\n slug: string;\n title: string;\n state: 'focused' | 'backburner' | 'paused' | 'done';\n rank?: number;\n}\n\ninterface OpenSuccess {\n slug: string;\n prompt: string;\n cwd_hint: string;\n channels?: string[];\n resolved_from?: 'slug' | 'cwd';\n}\n\ninterface PickerResult {\n picker: true;\n initiatives: InitiativeSummary[];\n}\n\ntype OpenResult = OpenSuccess | PickerResult;\n\nasync function runOpen(\n opts: { slug?: string; pick?: boolean; adhoc?: boolean } = {},\n): Promise<OpenResult> {\n const ctx: CommandContext = {\n activeRoot: getActiveRoot(),\n warnings: [],\n format: 'json',\n cwd: process.cwd(),\n };\n const parsed = openCommand.args.parse({\n ...(opts.slug ? { slug: opts.slug } : {}),\n ...(opts.pick ? { pick: true } : {}),\n ...(opts.adhoc ? { adhoc: true } : {}),\n });\n return (await openCommand.run(parsed, ctx)) as OpenResult;\n}\n\nconst STATE_LABEL: Record<InitiativeSummary['state'], string> = {\n focused: 'focused',\n backburner: 'backburner',\n paused: 'paused',\n done: 'done',\n};\n\nasync function pickInitiative(\n initiatives: InitiativeSummary[],\n): Promise<string | null> {\n if (initiatives.length === 0) {\n process.stderr.write(\n color.red(\n 'No initiatives found. Create one with `active-work new <slug>`.\\n',\n ),\n );\n return null;\n }\n const choice = await clackPrompts.select({\n message: 'Pick an initiative to open',\n options: initiatives.map((i) => {\n const rank = i.rank !== undefined ? ` · rank ${i.rank}` : '';\n return {\n value: i.slug,\n label: `${i.title} (${i.slug})`,\n hint: `${STATE_LABEL[i.state]}${rank}`,\n };\n }),\n });\n if (clackPrompts.isCancel(choice)) return null;\n return String(choice);\n}\n\nfunction spawnClaude(\n prompt: string,\n cwd: string,\n channels?: string[],\n): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn('claude', buildClaudeArgs(prompt, channels), {\n cwd,\n stdio: 'inherit',\n });\n child.on('error', (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === 'ENOENT') {\n process.stderr.write(\n color.red(\n 'error: `claude` not found on PATH. Install Claude Code, ' +\n 'or run `active-work open <slug>` to print the prompt.\\n',\n ),\n );\n resolve(127);\n return;\n }\n process.stderr.write(\n color.red(`error: failed to launch claude: ${err.message}\\n`),\n );\n resolve(EXIT.GENERIC);\n });\n child.on('exit', (code, signal) => {\n if (signal) {\n resolve(128 + (signal === 'SIGINT' ? 2 : 1));\n return;\n }\n resolve(code ?? 0);\n });\n });\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n [\n 'aw — launch a Claude session for an active-work initiative.',\n '',\n 'Usage:',\n ' aw [slug] Bootstrap and launch a Claude session for <slug>.',\n ' Omit slug to resolve the initiative from the current',\n ' directory, falling back to an interactive picker.',\n ' aw --pick Skip cwd resolution and always show the picker.',\n ' aw <slug> --adhoc',\n ' Frame the session as ad-hoc work on the workstream',\n ' (awaiting your task), not a handoff continuation.',\n ' aw --help Show this message.',\n ' aw --version Print version.',\n '',\n 'For the full management CLI (new, task, focus, session, …) use `active-work`.',\n '',\n ].join('\\n'),\n );\n}\n\nexport async function main(argv: string[]): Promise<void> {\n const args = argv.slice(2);\n if (args.includes('--help') || args.includes('-h')) {\n printHelp();\n process.exit(EXIT.OK);\n }\n if (args.includes('--version') || args.includes('-V')) {\n process.stdout.write('0.1.0\\n');\n process.exit(EXIT.OK);\n }\n // `--pick` forces the interactive picker instead of resolving from cwd;\n // `--adhoc` reframes the prompt as ad-hoc work on the workstream.\n const pick = args.includes('--pick');\n const adhoc = args.includes('--adhoc');\n const positional = args.filter((a) => a !== '--pick' && a !== '--adhoc');\n if (positional.some((a) => a.startsWith('-')) || positional.length > 1) {\n process.stderr.write(\n color.red(\n 'error: `aw` only launches a Claude session for an initiative. ' +\n 'Use `active-work` for other commands.\\n',\n ),\n );\n process.exit(EXIT.USAGE);\n }\n\n try {\n let opened: OpenSuccess;\n if (positional.length === 0) {\n // No slug: `open` first tries to resolve the initiative from the\n // current directory (unless `--pick`). It returns the picker list only\n // when the cwd doesn't uniquely match a worktree.\n const result = await runOpen({ pick, adhoc });\n if ('picker' in result) {\n const choice = await pickInitiative(result.initiatives);\n if (!choice) {\n process.exit(EXIT.OK);\n }\n opened = (await runOpen({ slug: choice, adhoc })) as OpenSuccess;\n } else {\n opened = result;\n process.stderr.write(\n color.dim(\n `Opening ${opened.slug} — matched current directory.\\n`,\n ),\n );\n }\n } else {\n opened = (await runOpen({ slug: positional[0], adhoc })) as OpenSuccess;\n }\n const code = await spawnClaude(\n opened.prompt,\n opened.cwd_hint,\n opened.channels,\n );\n process.exit(code);\n } catch (err) {\n const { message, code } = formatError(err);\n process.stderr.write(color.red(`error: ${message}\\n`));\n process.exit(code);\n }\n}\n\nvoid main(process.argv);\n","/**\n * Pure helpers for assembling the `claude` argv the `aw` launcher spawns.\n * Kept side-effect-free (no top-level `main()`) so they are unit-testable\n * without executing the launcher on import.\n */\n\n/**\n * Build the `--dangerously-load-development-channels` flag for an initiative's\n * MCP push channels.\n *\n * Each frontmatter `channels` entry is a target: an explicit\n * `server:<name>` / `plugin:<name>@<marketplace>`, or a bare server name that\n * is normalized to `server:<name>`. All targets are passed under a single\n * variadic flag (still required as of Claude Code v2.1.197 — there is no\n * config-file equivalent).\n */\nexport function buildChannelArgs(channels: string[] | undefined): string[] {\n if (!channels || channels.length === 0) return [];\n const targets = channels.map((raw) =>\n /^(server|plugin):/.test(raw) ? raw : `server:${raw}`,\n );\n return ['--dangerously-load-development-channels', ...targets];\n}\n\n/**\n * Assemble the full `claude` argv. The prompt always follows a `--` so the\n * variadic channels flag can never swallow it as a channel target — the bug\n * that made `aw <slug>` collide the channel name with the bootstrap prompt.\n */\nexport function buildClaudeArgs(prompt: string, channels?: string[]): string[] {\n return [...buildChannelArgs(channels), '--', prompt];\n}\n"],"mappings":";;;;;;;;;;AAcA,SAAS,aAAa;AACtB,YAAY,kBAAkB;;;ACCvB,SAAS,iBAAiB,UAA0C;AACzE,MAAI,CAAC,YAAY,SAAS,WAAW,EAAG,QAAO,CAAC;AAChD,QAAM,UAAU,SAAS;AAAA,IAAI,CAAC,QAC5B,oBAAoB,KAAK,GAAG,IAAI,MAAM,UAAU,GAAG;AAAA,EACrD;AACA,SAAO,CAAC,2CAA2C,GAAG,OAAO;AAC/D;AAOO,SAAS,gBAAgB,QAAgB,UAA+B;AAC7E,SAAO,CAAC,GAAG,iBAAiB,QAAQ,GAAG,MAAM,MAAM;AACrD;;;ADcA,eAAe,QACb,OAA2D,CAAC,GACvC;AACrB,QAAM,MAAsB;AAAA,IAC1B,YAAY,cAAc;AAAA,IAC1B,UAAU,CAAC;AAAA,IACX,QAAQ;AAAA,IACR,KAAK,QAAQ,IAAI;AAAA,EACnB;AACA,QAAM,SAAS,aAAY,KAAK,MAAM;AAAA,IACpC,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,IACvC,GAAI,KAAK,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAAA,IAClC,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,EACtC,CAAC;AACD,SAAQ,MAAM,aAAY,IAAI,QAAQ,GAAG;AAC3C;AAEA,IAAM,cAA0D;AAAA,EAC9D,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,MAAM;AACR;AAEA,eAAe,eACb,aACwB;AACxB,MAAI,YAAY,WAAW,GAAG;AAC5B,YAAQ,OAAO;AAAA,MACb,MAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,QAAM,SAAS,MAAmB,oBAAO;AAAA,IACvC,SAAS;AAAA,IACT,SAAS,YAAY,IAAI,CAAC,MAAM;AAC9B,YAAM,OAAO,EAAE,SAAS,SAAY,cAAW,EAAE,IAAI,KAAK;AAC1D,aAAO;AAAA,QACL,OAAO,EAAE;AAAA,QACT,OAAO,GAAG,EAAE,KAAK,KAAK,EAAE,IAAI;AAAA,QAC5B,MAAM,GAAG,YAAY,EAAE,KAAK,CAAC,GAAG,IAAI;AAAA,MACtC;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACD,MAAiB,sBAAS,MAAM,EAAG,QAAO;AAC1C,SAAO,OAAO,MAAM;AACtB;AAEA,SAAS,YACP,QACA,KACA,UACiB;AACjB,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ,MAAM,UAAU,gBAAgB,QAAQ,QAAQ,GAAG;AAAA,MAC/D;AAAA,MACA,OAAO;AAAA,IACT,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,QAAQ;AACzB,YAAM,IAAI;AACV,UAAI,EAAE,SAAS,UAAU;AACvB,gBAAQ,OAAO;AAAA,UACb,MAAM;AAAA,YACJ;AAAA,UAEF;AAAA,QACF;AACA,gBAAQ,GAAG;AACX;AAAA,MACF;AACA,cAAQ,OAAO;AAAA,QACb,MAAM,IAAI,mCAAmC,IAAI,OAAO;AAAA,CAAI;AAAA,MAC9D;AACA,cAAQ,KAAK,OAAO;AAAA,IACtB,CAAC;AACD,UAAM,GAAG,QAAQ,CAAC,MAAM,WAAW;AACjC,UAAI,QAAQ;AACV,gBAAQ,OAAO,WAAW,WAAW,IAAI,EAAE;AAC3C;AAAA,MACF;AACA,cAAQ,QAAQ,CAAC;AAAA,IACnB,CAAC;AAAA,EACH,CAAC;AACH;AAEA,SAAS,YAAkB;AACzB,UAAQ,OAAO;AAAA,IACb;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,EACb;AACF;AAEA,eAAsB,KAAK,MAA+B;AACxD,QAAM,OAAO,KAAK,MAAM,CAAC;AACzB,MAAI,KAAK,SAAS,QAAQ,KAAK,KAAK,SAAS,IAAI,GAAG;AAClD,cAAU;AACV,YAAQ,KAAK,KAAK,EAAE;AAAA,EACtB;AACA,MAAI,KAAK,SAAS,WAAW,KAAK,KAAK,SAAS,IAAI,GAAG;AACrD,YAAQ,OAAO,MAAM,SAAS;AAC9B,YAAQ,KAAK,KAAK,EAAE;AAAA,EACtB;AAGA,QAAM,OAAO,KAAK,SAAS,QAAQ;AACnC,QAAM,QAAQ,KAAK,SAAS,SAAS;AACrC,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,MAAM,YAAY,MAAM,SAAS;AACvE,MAAI,WAAW,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,CAAC,KAAK,WAAW,SAAS,GAAG;AACtE,YAAQ,OAAO;AAAA,MACb,MAAM;AAAA,QACJ;AAAA,MAEF;AAAA,IACF;AACA,YAAQ,KAAK,KAAK,KAAK;AAAA,EACzB;AAEA,MAAI;AACF,QAAI;AACJ,QAAI,WAAW,WAAW,GAAG;AAI3B,YAAM,SAAS,MAAM,QAAQ,EAAE,MAAM,MAAM,CAAC;AAC5C,UAAI,YAAY,QAAQ;AACtB,cAAM,SAAS,MAAM,eAAe,OAAO,WAAW;AACtD,YAAI,CAAC,QAAQ;AACX,kBAAQ,KAAK,KAAK,EAAE;AAAA,QACtB;AACA,iBAAU,MAAM,QAAQ,EAAE,MAAM,QAAQ,MAAM,CAAC;AAAA,MACjD,OAAO;AACL,iBAAS;AACT,gBAAQ,OAAO;AAAA,UACb,MAAM;AAAA,YACJ,WAAW,OAAO,IAAI;AAAA;AAAA,UACxB;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,eAAU,MAAM,QAAQ,EAAE,MAAM,WAAW,CAAC,GAAG,MAAM,CAAC;AAAA,IACxD;AACA,UAAM,OAAO,MAAM;AAAA,MACjB,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AACA,YAAQ,KAAK,IAAI;AAAA,EACnB,SAAS,KAAK;AACZ,UAAM,EAAE,SAAS,KAAK,IAAI,YAAY,GAAG;AACzC,YAAQ,OAAO,MAAM,MAAM,IAAI,UAAU,OAAO;AAAA,CAAI,CAAC;AACrD,YAAQ,KAAK,IAAI;AAAA,EACnB;AACF;AAEA,KAAK,KAAK,QAAQ,IAAI;","names":[]}
1
+ {"version":3,"sources":["../src/aw.ts","../src/launcher-args.ts","../src/launcher-lease.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `aw` — thin launcher that bootstraps a Claude Code session for an\n * active-work initiative.\n *\n * Usage:\n * aw [slug] Launch claude with the bootstrap prompt and the\n * initiative's worktree as cwd. Omit the slug to pick\n * one interactively.\n *\n * For the management CLI (new, task, focus, session, etc.), use\n * `active-work`. This launcher deliberately rejects sub-command\n * invocations so the two surfaces stay distinct.\n */\nimport { spawn } from 'node:child_process';\nimport * as clackPrompts from '@clack/prompts';\nimport openCommand from './commands/open.js';\nimport { buildClaudeArgs, parseLauncherFlags } from './launcher-args.js';\nimport { buildLauncherEnv, withLauncherLease } from './launcher-lease.js';\nimport { getActiveRoot } from './utils/paths.js';\nimport { formatError, EXIT } from './errors.js';\nimport { color } from './utils/color.js';\nimport type { CommandContext } from './registry/index.js';\n\ninterface InitiativeSummary {\n slug: string;\n title: string;\n state: 'focused' | 'backburner' | 'paused' | 'done';\n rank?: number;\n}\n\ninterface OpenSuccess {\n slug: string;\n prompt: string;\n cwd_hint: string;\n channels?: string[];\n resolved_from?: 'slug' | 'cwd';\n}\n\ninterface PickerResult {\n picker: true;\n initiatives: InitiativeSummary[];\n}\n\ntype OpenResult = OpenSuccess | PickerResult;\n\nasync function runOpen(\n opts: { slug?: string; pick?: boolean; adhoc?: boolean } = {},\n): Promise<OpenResult> {\n const ctx: CommandContext = {\n activeRoot: getActiveRoot(),\n warnings: [],\n format: 'json',\n cwd: process.cwd(),\n };\n const parsed = openCommand.args.parse({\n ...(opts.slug ? { slug: opts.slug } : {}),\n ...(opts.pick ? { pick: true } : {}),\n ...(opts.adhoc ? { adhoc: true } : {}),\n // This launcher records a `launcher` lease of its own around the spawned\n // session, so `open` must not also file a `oneshot` one — two leases for\n // one session make it its own sibling on the next bootstrap.\n lease_mode: 'defer' as const,\n });\n return (await openCommand.run(parsed, ctx)) as OpenResult;\n}\n\nconst STATE_LABEL: Record<InitiativeSummary['state'], string> = {\n focused: 'focused',\n backburner: 'backburner',\n paused: 'paused',\n done: 'done',\n};\n\nasync function pickInitiative(initiatives: InitiativeSummary[]): Promise<string | null> {\n if (initiatives.length === 0) {\n process.stderr.write(\n color.red('No initiatives found. Create one with `active-work new <slug>`.\\n'),\n );\n return null;\n }\n const choice = await clackPrompts.select({\n message: 'Pick an initiative to open',\n options: initiatives.map((i) => {\n const rank = i.rank !== undefined ? ` · rank ${i.rank}` : '';\n return {\n value: i.slug,\n label: `${i.title} (${i.slug})`,\n hint: `${STATE_LABEL[i.state]}${rank}`,\n };\n }),\n });\n if (clackPrompts.isCancel(choice)) return null;\n return String(choice);\n}\n\nfunction spawnClaude(\n prompt: string,\n cwd: string,\n channels?: string[],\n leaseId?: string,\n): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn('claude', buildClaudeArgs(prompt, channels), {\n cwd,\n stdio: 'inherit',\n // Explicit env (the default is an implicit `process.env`) so the session\n // can recognize its own lease and not warn about itself.\n env: buildLauncherEnv(process.env, leaseId),\n });\n child.on('error', (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === 'ENOENT') {\n process.stderr.write(\n color.red(\n 'error: `claude` not found on PATH. Install Claude Code, ' +\n 'or run `active-work open <slug>` to print the prompt.\\n',\n ),\n );\n resolve(127);\n return;\n }\n process.stderr.write(color.red(`error: failed to launch claude: ${err.message}\\n`));\n resolve(EXIT.GENERIC);\n });\n child.on('exit', (code, signal) => {\n if (signal) {\n resolve(128 + (signal === 'SIGINT' ? 2 : 1));\n return;\n }\n resolve(code ?? 0);\n });\n });\n}\n\nfunction printHelp(): void {\n process.stdout.write(\n [\n 'aw — launch a Claude session for an active-work initiative.',\n '',\n 'Usage:',\n ' aw [slug] Bootstrap and launch a Claude session for <slug>.',\n ' Omit slug to resolve the initiative from the current',\n ' directory, falling back to an interactive picker.',\n ' aw --pick Skip cwd resolution and always show the picker.',\n ' aw <slug> --adhoc',\n ' Frame the session as ad-hoc work on the workstream',\n ' (awaiting your task), not a handoff continuation.',\n ' `--ad-hoc` is accepted as an alias.',\n ' aw --help Show this message.',\n ' aw --version Print version.',\n '',\n 'For the full management CLI (new, task, focus, session, …) use `active-work`.',\n '',\n ].join('\\n'),\n );\n}\n\nexport async function main(argv: string[]): Promise<void> {\n const args = argv.slice(2);\n if (args.includes('--help') || args.includes('-h')) {\n printHelp();\n process.exit(EXIT.OK);\n }\n if (args.includes('--version') || args.includes('-V')) {\n process.stdout.write('0.1.0\\n');\n process.exit(EXIT.OK);\n }\n // `--pick` forces the interactive picker instead of resolving from cwd;\n // `--adhoc` (alias `--ad-hoc`) reframes the prompt as ad-hoc work.\n const { pick, adhoc, positional, usageError } = parseLauncherFlags(args);\n if (usageError) {\n process.stderr.write(\n color.red(\n 'error: `aw` only launches a Claude session for an initiative. ' +\n 'Use `active-work` for other commands.\\n',\n ),\n );\n process.exit(EXIT.USAGE);\n }\n\n try {\n let opened: OpenSuccess;\n if (positional.length === 0) {\n // No slug: `open` first tries to resolve the initiative from the\n // current directory (unless `--pick`). It returns the picker list only\n // when the cwd doesn't uniquely match a worktree.\n const result = await runOpen({ pick, adhoc });\n if ('picker' in result) {\n const choice = await pickInitiative(result.initiatives);\n if (!choice) {\n process.exit(EXIT.OK);\n }\n opened = (await runOpen({ slug: choice, adhoc })) as OpenSuccess;\n } else {\n opened = result;\n process.stderr.write(color.dim(`Opening ${opened.slug} — matched current directory.\\n`));\n }\n } else {\n opened = (await runOpen({ slug: positional[0], adhoc })) as OpenSuccess;\n }\n const code = await withLauncherLease(\n {\n activeRoot: getActiveRoot(),\n slug: opened.slug,\n cwd: opened.cwd_hint,\n },\n (leaseId) => spawnClaude(opened.prompt, opened.cwd_hint, opened.channels, leaseId),\n );\n process.exit(code);\n } catch (err) {\n const { message, code } = formatError(err);\n process.stderr.write(color.red(`error: ${message}\\n`));\n process.exit(code);\n }\n}\n\nvoid main(process.argv);\n","/**\n * Pure helpers for assembling the `claude` argv the `aw` launcher spawns.\n * Kept side-effect-free (no top-level `main()`) so they are unit-testable\n * without executing the launcher on import.\n */\n\n/**\n * Build the channel flags for an initiative's MCP push channels.\n *\n * Each frontmatter `channels` entry is a target: an explicit\n * `server:<name>` / `plugin:<name>@<marketplace>`, or a bare server name that\n * is normalized to `server:<name>`.\n *\n * The two target kinds take different flags, and the choice is not cosmetic.\n * Claude Code's channel gate only has an allowlist path for plugin targets:\n * `plugin:` entries go under `--channels`, which is satisfied by an\n * `allowedChannelPlugins` entry in managed settings, while `server:` entries\n * have no allowlist path at all and must use the dev flag. Routing a properly\n * allowlisted plugin through the dev flag still works, but re-triggers the\n * development-channels dialog that packaging it as a plugin exists to avoid.\n *\n * Both flags are variadic, so callers must keep the prompt behind a `--`.\n */\nexport function buildChannelArgs(channels: string[] | undefined): string[] {\n if (!channels || channels.length === 0) return [];\n const targets = channels.map((raw) => (/^(server|plugin):/.test(raw) ? raw : `server:${raw}`));\n const plugins = targets.filter((t) => t.startsWith('plugin:'));\n const servers = targets.filter((t) => !t.startsWith('plugin:'));\n return [\n ...(plugins.length > 0 ? ['--channels', ...plugins] : []),\n ...(servers.length > 0 ? ['--dangerously-load-development-channels', ...servers] : []),\n ];\n}\n\n/**\n * Assemble the full `claude` argv. The prompt always follows a `--` so the\n * variadic channel flags can never swallow it as a channel target — the bug\n * that made `aw <slug>` collide the channel name with the bootstrap prompt.\n */\nexport function buildClaudeArgs(prompt: string, channels?: string[]): string[] {\n return [...buildChannelArgs(channels), '--', prompt];\n}\n\n/**\n * `--adhoc` is the canonical spelling; `--ad-hoc` is accepted as an alias so\n * the natural hyphenated form doesn't trip the unknown-flag guard below.\n */\nexport const ADHOC_FLAGS = ['--adhoc', '--ad-hoc'];\n\nexport interface LauncherFlags {\n pick: boolean;\n adhoc: boolean;\n positional: string[];\n /** True when a positional looks like an unknown flag, or more than one slug was given. */\n usageError: boolean;\n}\n\n/**\n * Parse the `aw` launcher flags out of `argv.slice(2)`. Pure and\n * side-effect-free so the flag handling — including the `--adhoc` / `--ad-hoc`\n * alias — is unit-testable without executing the launcher.\n */\nexport function parseLauncherFlags(args: string[]): LauncherFlags {\n const known = new Set(['--pick', ...ADHOC_FLAGS]);\n const positional = args.filter((a) => !known.has(a));\n return {\n pick: args.includes('--pick'),\n adhoc: args.some((a) => ADHOC_FLAGS.includes(a)),\n positional,\n usageError: positional.some((a) => a.startsWith('-')) || positional.length > 1,\n };\n}\n","/**\n * Lease lifecycle for the `aw` launcher.\n *\n * Kept out of `src/aw.ts` because that module calls `main()` on import and so\n * cannot be pulled into a unit test — the same reason `launcher-args.ts` and\n * `registry/cli-options.ts` exist.\n *\n * `aw` spawns `claude` with `stdio: 'inherit'` and awaits its exit, so the\n * launcher's own process lifetime brackets the session exactly. That is what\n * makes a `launcher` lease a fact rather than a guess — and it is also what\n * makes cleanup fiddly: an inherited stdio child shares the foreground process\n * group, so Ctrl-C is delivered to `aw` as well, and `aw` may be gone before\n * any promise-based cleanup gets a turn. Hence three layers:\n *\n * 1. the normal path — `finally`, async unlink after the child exits;\n * 2. signals — release synchronously, then re-raise so the default action\n * still terminates the process (installing a handler suppresses it);\n * 3. `process.on('exit')` — the last-resort synchronous sweep, which is the\n * only thing that runs under `process.exit()` from an error path.\n *\n * Every layer tolerates the lease already being gone.\n */\nimport { acquireLease, releaseLease, releaseLeaseSync } from './sessions/lease.js';\n\n/** Env var carrying this session's lease id into the spawned `claude`. */\nexport const LEASE_ENV_VAR = 'AW_LEASE_ID';\n\n/**\n * The child's environment, with the lease id added.\n *\n * The child is the Claude session this lease describes, so any `open` /\n * `prompt` run *inside* it must exclude this lease rather than report the\n * session as its own sibling.\n */\nexport function buildLauncherEnv(\n base: NodeJS.ProcessEnv,\n leaseId: string | undefined,\n): NodeJS.ProcessEnv {\n if (!leaseId) return { ...base };\n return { ...base, [LEASE_ENV_VAR]: leaseId };\n}\n\nconst CLEANUP_SIGNALS: NodeJS.Signals[] = ['SIGINT', 'SIGTERM'];\n\nexport interface LauncherLeaseInput {\n activeRoot: string;\n slug: string;\n cwd: string;\n /** Defaults to this process — the one whose lifetime brackets the session. */\n pid?: number;\n}\n\n/**\n * Hold a `launcher` lease for the duration of `run`.\n *\n * A lease that cannot be written is not an error: `run` is invoked with\n * `undefined` and the session proceeds without a lease. The warning it would\n * have produced is advisory, and refusing to launch over it would be absurd.\n */\nexport async function withLauncherLease<T>(\n input: LauncherLeaseInput,\n run: (leaseId: string | undefined) => Promise<T>,\n): Promise<T> {\n const { activeRoot, slug, cwd, pid = process.pid } = input;\n let leaseId: string | undefined;\n try {\n ({ leaseId } = await acquireLease({\n activeRoot,\n slug,\n cwd,\n mode: 'launcher',\n pid,\n }));\n } catch {\n return run(undefined);\n }\n\n const id = leaseId;\n const onExit = (): void => releaseLeaseSync(activeRoot, slug, id);\n const onSignal = (signal: NodeJS.Signals): void => {\n releaseLeaseSync(activeRoot, slug, id);\n // Restore the default action and re-raise: swallowing the signal here would\n // leave `aw` alive after a Ctrl-C that already killed the session below it.\n process.removeListener(signal, onSignal);\n process.kill(process.pid, signal);\n };\n\n process.on('exit', onExit);\n for (const signal of CLEANUP_SIGNALS) process.on(signal, onSignal);\n\n try {\n return await run(id);\n } finally {\n process.removeListener('exit', onExit);\n for (const signal of CLEANUP_SIGNALS) process.removeListener(signal, onSignal);\n await releaseLease(activeRoot, slug, id).catch(() => {});\n }\n}\n"],"mappings":"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","names":[]}