@titan-design/active-work 0.1.0 → 0.3.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 +15 -6
- package/dist/aw.js +136 -35
- package/dist/aw.js.map +1 -1
- package/dist/chunk-HSGZOWS3.js +2605 -0
- package/dist/chunk-HSGZOWS3.js.map +1 -0
- package/dist/cli.js +6083 -2052
- package/dist/cli.js.map +1 -1
- package/dist/dashboard/index.html +13 -5
- package/docs/cli-reference.md +1090 -0
- package/package.json +28 -5
- package/scripts/gen-cli-reference.mjs +23 -2
- package/skill/SKILL.md +46 -10
- package/skill/references/auditing-existing-work.md +14 -10
- package/skill/references/cli-dev.md +3 -2
- package/skill/references/onboarding.md +5 -9
- package/dist/chunk-OET6AFME.js +0 -1276
- package/dist/chunk-OET6AFME.js.map +0 -1
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,
|
|
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
|
|
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
|
|
@@ -98,7 +106,7 @@ See [`docs/architecture.md`](docs/architecture.md) for the full breakdown — la
|
|
|
98
106
|
|
|
99
107
|
## Commands
|
|
100
108
|
|
|
101
|
-
The most-used surface, grouped by purpose. Run `active-work <command> --help` for flags or
|
|
109
|
+
The most-used surface, grouped by purpose. Run `active-work <command> --help` for flags, or `active-work help --markdown` for the whole list. That same list ships as `docs/cli-reference.md` inside the published package; it is generated at release rather than committed, so it is not browsable in this repo.
|
|
102
110
|
|
|
103
111
|
| Group | Command | Purpose |
|
|
104
112
|
|---|---|---|
|
|
@@ -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
|
|
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
|
|
|
@@ -158,7 +167,7 @@ pnpm build # tsup CLI bundle + vite dashboard bundle
|
|
|
158
167
|
|
|
159
168
|
Node 22+, pnpm. The build produces `dist/cli.js` (single ESM bundle) and `dist/dashboard/index.html` (single-file React app).
|
|
160
169
|
|
|
161
|
-
|
|
170
|
+
The CLI reference is generated, gitignored, and written during release. Adding a command needs no doc commit. To read it locally:
|
|
162
171
|
|
|
163
172
|
```bash
|
|
164
173
|
pnpm build # ensure dist/cli.js is current
|
package/dist/aw.js
CHANGED
|
@@ -1,26 +1,61 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
EXIT,
|
|
4
|
+
acquireLease,
|
|
5
|
+
buildClaudeArgs,
|
|
4
6
|
color,
|
|
5
7
|
formatError,
|
|
6
8
|
getActiveRoot,
|
|
7
|
-
open_default
|
|
8
|
-
|
|
9
|
+
open_default,
|
|
10
|
+
parseLauncherFlags,
|
|
11
|
+
releaseLease,
|
|
12
|
+
releaseLeaseSync,
|
|
13
|
+
resolveLaunchCwd,
|
|
14
|
+
resume_default
|
|
15
|
+
} from "./chunk-HSGZOWS3.js";
|
|
9
16
|
|
|
10
17
|
// src/aw.ts
|
|
11
18
|
import { spawn } from "child_process";
|
|
12
19
|
import * as clackPrompts from "@clack/prompts";
|
|
13
20
|
|
|
14
|
-
// src/launcher-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
);
|
|
20
|
-
return ["--dangerously-load-development-channels", ...targets];
|
|
21
|
+
// src/launcher-lease.ts
|
|
22
|
+
var LEASE_ENV_VAR = "AW_LEASE_ID";
|
|
23
|
+
function buildLauncherEnv(base, leaseId) {
|
|
24
|
+
if (!leaseId) return { ...base };
|
|
25
|
+
return { ...base, [LEASE_ENV_VAR]: leaseId };
|
|
21
26
|
}
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
var CLEANUP_SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
28
|
+
async function withLauncherLease(input, run) {
|
|
29
|
+
const { activeRoot, slug, cwd, pid = process.pid } = input;
|
|
30
|
+
let leaseId;
|
|
31
|
+
try {
|
|
32
|
+
({ leaseId } = await acquireLease({
|
|
33
|
+
activeRoot,
|
|
34
|
+
slug,
|
|
35
|
+
cwd,
|
|
36
|
+
mode: "launcher",
|
|
37
|
+
pid
|
|
38
|
+
}));
|
|
39
|
+
} catch {
|
|
40
|
+
return run(void 0);
|
|
41
|
+
}
|
|
42
|
+
const id = leaseId;
|
|
43
|
+
const onExit = () => releaseLeaseSync(activeRoot, slug, id);
|
|
44
|
+
const onSignal = (signal) => {
|
|
45
|
+
releaseLeaseSync(activeRoot, slug, id);
|
|
46
|
+
process.removeListener(signal, onSignal);
|
|
47
|
+
process.kill(process.pid, signal);
|
|
48
|
+
};
|
|
49
|
+
process.on("exit", onExit);
|
|
50
|
+
for (const signal of CLEANUP_SIGNALS) process.on(signal, onSignal);
|
|
51
|
+
try {
|
|
52
|
+
return await run(id);
|
|
53
|
+
} finally {
|
|
54
|
+
process.removeListener("exit", onExit);
|
|
55
|
+
for (const signal of CLEANUP_SIGNALS) process.removeListener(signal, onSignal);
|
|
56
|
+
await releaseLease(activeRoot, slug, id).catch(() => {
|
|
57
|
+
});
|
|
58
|
+
}
|
|
24
59
|
}
|
|
25
60
|
|
|
26
61
|
// src/aw.ts
|
|
@@ -34,7 +69,11 @@ async function runOpen(opts = {}) {
|
|
|
34
69
|
const parsed = open_default.args.parse({
|
|
35
70
|
...opts.slug ? { slug: opts.slug } : {},
|
|
36
71
|
...opts.pick ? { pick: true } : {},
|
|
37
|
-
...opts.adhoc ? { adhoc: true } : {}
|
|
72
|
+
...opts.adhoc ? { adhoc: true } : {},
|
|
73
|
+
// This launcher records a `launcher` lease of its own around the spawned
|
|
74
|
+
// session, so `open` must not also file a `oneshot` one — two leases for
|
|
75
|
+
// one session make it its own sibling on the next bootstrap.
|
|
76
|
+
lease_mode: "defer"
|
|
38
77
|
});
|
|
39
78
|
return await open_default.run(parsed, ctx);
|
|
40
79
|
}
|
|
@@ -47,9 +86,7 @@ var STATE_LABEL = {
|
|
|
47
86
|
async function pickInitiative(initiatives) {
|
|
48
87
|
if (initiatives.length === 0) {
|
|
49
88
|
process.stderr.write(
|
|
50
|
-
color.red(
|
|
51
|
-
"No initiatives found. Create one with `active-work new <slug>`.\n"
|
|
52
|
-
)
|
|
89
|
+
color.red("No initiatives found. Create one with `active-work new <slug>`.\n")
|
|
53
90
|
);
|
|
54
91
|
return null;
|
|
55
92
|
}
|
|
@@ -67,11 +104,14 @@ async function pickInitiative(initiatives) {
|
|
|
67
104
|
if (clackPrompts.isCancel(choice)) return null;
|
|
68
105
|
return String(choice);
|
|
69
106
|
}
|
|
70
|
-
function spawnClaude(prompt, cwd, channels) {
|
|
107
|
+
function spawnClaude(prompt, cwd, channels, leaseId) {
|
|
71
108
|
return new Promise((resolve) => {
|
|
72
109
|
const child = spawn("claude", buildClaudeArgs(prompt, channels), {
|
|
73
110
|
cwd,
|
|
74
|
-
stdio: "inherit"
|
|
111
|
+
stdio: "inherit",
|
|
112
|
+
// Explicit env (the default is an implicit `process.env`) so the session
|
|
113
|
+
// can recognize its own lease and not warn about itself.
|
|
114
|
+
env: buildLauncherEnv(process.env, leaseId)
|
|
75
115
|
});
|
|
76
116
|
child.on("error", (err) => {
|
|
77
117
|
const e = err;
|
|
@@ -84,10 +124,35 @@ function spawnClaude(prompt, cwd, channels) {
|
|
|
84
124
|
resolve(127);
|
|
85
125
|
return;
|
|
86
126
|
}
|
|
87
|
-
process.stderr.write(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
127
|
+
process.stderr.write(color.red(`error: failed to launch claude: ${err.message}
|
|
128
|
+
`));
|
|
129
|
+
resolve(EXIT.GENERIC);
|
|
130
|
+
});
|
|
131
|
+
child.on("exit", (code, signal) => {
|
|
132
|
+
if (signal) {
|
|
133
|
+
resolve(128 + (signal === "SIGINT" ? 2 : 1));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
resolve(code ?? 0);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function spawnClaudeResume(sessionId, cwd) {
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
const child = spawn("claude", ["--resume", sessionId], {
|
|
143
|
+
cwd,
|
|
144
|
+
stdio: "inherit",
|
|
145
|
+
env: process.env
|
|
146
|
+
});
|
|
147
|
+
child.on("error", (err) => {
|
|
148
|
+
const e = err;
|
|
149
|
+
if (e.code === "ENOENT") {
|
|
150
|
+
process.stderr.write(color.red("error: `claude` not found on PATH.\n"));
|
|
151
|
+
resolve(127);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
process.stderr.write(color.red(`error: failed to launch claude: ${err.message}
|
|
155
|
+
`));
|
|
91
156
|
resolve(EXIT.GENERIC);
|
|
92
157
|
});
|
|
93
158
|
child.on("exit", (code, signal) => {
|
|
@@ -99,6 +164,35 @@ function spawnClaude(prompt, cwd, channels) {
|
|
|
99
164
|
});
|
|
100
165
|
});
|
|
101
166
|
}
|
|
167
|
+
async function runResume(argv) {
|
|
168
|
+
const rest = argv.slice(3);
|
|
169
|
+
if (rest.length !== 1 || rest[0].startsWith("-")) {
|
|
170
|
+
process.stderr.write(color.red("usage: aw resume <session_id>\n"));
|
|
171
|
+
process.exit(EXIT.USAGE);
|
|
172
|
+
}
|
|
173
|
+
const sessionId = rest[0];
|
|
174
|
+
const ctx = {
|
|
175
|
+
activeRoot: getActiveRoot(),
|
|
176
|
+
warnings: [],
|
|
177
|
+
format: "json",
|
|
178
|
+
cwd: process.cwd()
|
|
179
|
+
};
|
|
180
|
+
try {
|
|
181
|
+
const parsed = resume_default.args.parse({ session_id: sessionId });
|
|
182
|
+
const resolved = await resume_default.run(parsed, ctx);
|
|
183
|
+
process.stderr.write(
|
|
184
|
+
color.dim(`Resuming ${sessionId} in ${resolved.cwd} (found via ${resolved.source}).
|
|
185
|
+
`)
|
|
186
|
+
);
|
|
187
|
+
const code = await spawnClaudeResume(sessionId, resolved.cwd);
|
|
188
|
+
process.exit(code);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
const { message, code } = formatError(err);
|
|
191
|
+
process.stderr.write(color.red(`error: ${message}
|
|
192
|
+
`));
|
|
193
|
+
process.exit(code);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
102
196
|
function printHelp() {
|
|
103
197
|
process.stdout.write(
|
|
104
198
|
[
|
|
@@ -112,6 +206,10 @@ function printHelp() {
|
|
|
112
206
|
" aw <slug> --adhoc",
|
|
113
207
|
" Frame the session as ad-hoc work on the workstream",
|
|
114
208
|
" (awaiting your task), not a handoff continuation.",
|
|
209
|
+
" `--ad-hoc` is accepted as an alias.",
|
|
210
|
+
" aw resume <session_id>",
|
|
211
|
+
" Find the directory a session id ran in (active-work's",
|
|
212
|
+
" session log, then ~/.claude/projects) and resume it there.",
|
|
115
213
|
" aw --help Show this message.",
|
|
116
214
|
" aw --version Print version.",
|
|
117
215
|
"",
|
|
@@ -130,10 +228,12 @@ async function main(argv) {
|
|
|
130
228
|
process.stdout.write("0.1.0\n");
|
|
131
229
|
process.exit(EXIT.OK);
|
|
132
230
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
231
|
+
if (args[0] === "resume") {
|
|
232
|
+
await runResume(argv);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const { pick, adhoc, positional, usageError } = parseLauncherFlags(args);
|
|
236
|
+
if (usageError) {
|
|
137
237
|
process.stderr.write(
|
|
138
238
|
color.red(
|
|
139
239
|
"error: `aw` only launches a Claude session for an initiative. Use `active-work` for other commands.\n"
|
|
@@ -153,20 +253,21 @@ async function main(argv) {
|
|
|
153
253
|
opened = await runOpen({ slug: choice, adhoc });
|
|
154
254
|
} else {
|
|
155
255
|
opened = result;
|
|
156
|
-
process.stderr.write(
|
|
157
|
-
|
|
158
|
-
`Opening ${opened.slug} \u2014 matched current directory.
|
|
159
|
-
`
|
|
160
|
-
)
|
|
161
|
-
);
|
|
256
|
+
process.stderr.write(color.dim(`Opening ${opened.slug} \u2014 matched current directory.
|
|
257
|
+
`));
|
|
162
258
|
}
|
|
163
259
|
} else {
|
|
164
260
|
opened = await runOpen({ slug: positional[0], adhoc });
|
|
165
261
|
}
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
262
|
+
const activeRoot = getActiveRoot();
|
|
263
|
+
const launchCwd = resolveLaunchCwd(activeRoot, opened.slug);
|
|
264
|
+
const code = await withLauncherLease(
|
|
265
|
+
{
|
|
266
|
+
activeRoot,
|
|
267
|
+
slug: opened.slug,
|
|
268
|
+
cwd: launchCwd
|
|
269
|
+
},
|
|
270
|
+
(leaseId) => spawnClaude(opened.prompt, launchCwd, opened.channels, leaseId)
|
|
170
271
|
);
|
|
171
272
|
process.exit(code);
|
|
172
273
|
} 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":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/aw.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 own directory under the active root as cwd.\n * Omit the slug to pick 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 resumeCommand from './commands/resume.js';\nimport { resolveLaunchCwd } from './commands/_open-helpers.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 spawnClaudeResume(sessionId: string, cwd: string): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn('claude', ['--resume', sessionId], {\n cwd,\n stdio: 'inherit',\n env: process.env,\n });\n child.on('error', (err) => {\n const e = err as NodeJS.ErrnoException;\n if (e.code === 'ENOENT') {\n process.stderr.write(color.red('error: `claude` not found on PATH.\\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\n/**\n * `aw resume <session_id>` — find the directory a session id belongs to\n * (active-work's own session log, then a direct match under\n * `~/.claude/projects`) and launch `claude --resume` there, so the caller\n * doesn't need to already know or remember where the session ran.\n */\nasync function runResume(argv: string[]): Promise<void> {\n const rest = argv.slice(3);\n if (rest.length !== 1 || rest[0]!.startsWith('-')) {\n process.stderr.write(color.red('usage: aw resume <session_id>\\n'));\n process.exit(EXIT.USAGE);\n }\n const sessionId = rest[0]!;\n const ctx: CommandContext = {\n activeRoot: getActiveRoot(),\n warnings: [],\n format: 'json',\n cwd: process.cwd(),\n };\n try {\n const parsed = resumeCommand.args.parse({ session_id: sessionId });\n const resolved = (await resumeCommand.run(parsed, ctx)) as { cwd: string; source: string };\n process.stderr.write(\n color.dim(`Resuming ${sessionId} in ${resolved.cwd} (found via ${resolved.source}).\\n`),\n );\n const code = await spawnClaudeResume(sessionId, resolved.cwd);\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\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 resume <session_id>',\n \" Find the directory a session id ran in (active-work's\",\n ' session log, then ~/.claude/projects) and resume it there.',\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 if (args[0] === 'resume') {\n await runResume(argv);\n return;\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 // Not `opened.cwd_hint`: that is the dispatch answer (a registered\n // worktree, for agents that must commit). An operator's session belongs in\n // the initiative's own directory — see resolveLaunchCwd (AW-115).\n const activeRoot = getActiveRoot();\n const launchCwd = resolveLaunchCwd(activeRoot, opened.slug);\n const code = await withLauncherLease(\n {\n activeRoot,\n slug: opened.slug,\n cwd: launchCwd,\n },\n (leaseId) => spawnClaude(opened.prompt, launchCwd, 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 * 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 * the registry package's own CLI helpers 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\n// SIGHUP is what a closed terminal/iTerm pane sends the foreground process\n// group. Without a listener here, the default disposition kills `aw` before\n// the event loop gets a turn — 'exit' never fires for that abrupt a death —\n// so the lease is orphaned. Its pid then keeps warning future sessions of a\n// \"live\" sibling until LAUNCHER_MAX_AGE_MS elapses or the pid gets recycled\n// by an unrelated process (see readLiveLeases in sessions/lease.ts).\nconst CLEANUP_SIGNALS: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGHUP'];\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":[]}
|