agentpack-cli 0.1.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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/SECURITY.md +44 -0
  4. package/assets/agentpack-logo.jpg +0 -0
  5. package/dist/src/agentpack.d.ts +2 -0
  6. package/dist/src/agentpack.js +8 -0
  7. package/dist/src/agentpack.js.map +1 -0
  8. package/dist/src/cli/index.d.ts +3 -0
  9. package/dist/src/cli/index.js +412 -0
  10. package/dist/src/cli/index.js.map +1 -0
  11. package/dist/src/core/budget.d.ts +14 -0
  12. package/dist/src/core/budget.js +56 -0
  13. package/dist/src/core/budget.js.map +1 -0
  14. package/dist/src/core/checkpoints.d.ts +24 -0
  15. package/dist/src/core/checkpoints.js +93 -0
  16. package/dist/src/core/checkpoints.js.map +1 -0
  17. package/dist/src/core/doctor.d.ts +4 -0
  18. package/dist/src/core/doctor.js +304 -0
  19. package/dist/src/core/doctor.js.map +1 -0
  20. package/dist/src/core/git.d.ts +2 -0
  21. package/dist/src/core/git.js +34 -0
  22. package/dist/src/core/git.js.map +1 -0
  23. package/dist/src/core/hash.d.ts +5 -0
  24. package/dist/src/core/hash.js +29 -0
  25. package/dist/src/core/hash.js.map +1 -0
  26. package/dist/src/core/ids.d.ts +1 -0
  27. package/dist/src/core/ids.js +7 -0
  28. package/dist/src/core/ids.js.map +1 -0
  29. package/dist/src/core/presets.d.ts +12 -0
  30. package/dist/src/core/presets.js +24 -0
  31. package/dist/src/core/presets.js.map +1 -0
  32. package/dist/src/core/redaction.d.ts +4 -0
  33. package/dist/src/core/redaction.js +18 -0
  34. package/dist/src/core/redaction.js.map +1 -0
  35. package/dist/src/core/resume.d.ts +13 -0
  36. package/dist/src/core/resume.js +398 -0
  37. package/dist/src/core/resume.js.map +1 -0
  38. package/dist/src/core/store.d.ts +20 -0
  39. package/dist/src/core/store.js +240 -0
  40. package/dist/src/core/store.js.map +1 -0
  41. package/dist/src/core/types.d.ts +43 -0
  42. package/dist/src/core/types.js +2 -0
  43. package/dist/src/core/types.js.map +1 -0
  44. package/dist/src/integrations/install.d.ts +5 -0
  45. package/dist/src/integrations/install.js +361 -0
  46. package/dist/src/integrations/install.js.map +1 -0
  47. package/dist/src/mcp/server.d.ts +9 -0
  48. package/dist/src/mcp/server.js +347 -0
  49. package/dist/src/mcp/server.js.map +1 -0
  50. package/dist/src/operations.d.ts +29 -0
  51. package/dist/src/operations.js +199 -0
  52. package/dist/src/operations.js.map +1 -0
  53. package/docs/DOGFOOD.md +72 -0
  54. package/docs/INTEGRATIONS.md +183 -0
  55. package/docs/MCP.md +83 -0
  56. package/docs/MVP.md +97 -0
  57. package/docs/ROADMAP.md +129 -0
  58. package/docs/SETUP.md +56 -0
  59. package/docs/agentpack-flow.md +63 -0
  60. package/package.json +55 -0
  61. package/scripts/mcp-smoke.mjs +197 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Agentpack contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # Agentpack
2
+
3
+ Local task-state ledger for AI coding agents.
4
+
5
+ > Coding agents forget. Agentpack gives them the task state they need to continue.
6
+
7
+ Agentpack helps coding agents continue long-running repo work without rediscovering context, re-reading unchanged sources, or repeating dead ends.
8
+
9
+ ## Product Contract
10
+
11
+ Agentpack is a local-first open-source tool for repo-scoped coding work. It is not a general AI memory, a knowledge graph, or a chat archive. Instead, it keeps a compact task ledger in `.agentpack/` and exposes that state through simple surfaces:
12
+
13
+ - files in `.agentpack/`
14
+ - CLI commands
15
+ - a local MCP server
16
+ - project instructions such as `AGENTS.md`, `CLAUDE.md`, and Cursor rules
17
+
18
+ `.agentpack/` is local task state and is ignored by git by default. Agentpack is designed first for coding agents such as Codex, Claude Code, Cursor, and other MCP clients. Markdown export exists as a fallback for manual handoff, not as the primary workflow.
19
+
20
+ ## v0 Scope
21
+
22
+ The first version is intentionally small:
23
+
24
+ - initialize a local `.agentpack/`
25
+ - record decisions, dead ends, evidence, and inspected sources
26
+ - store file hashes so agents can avoid re-reading unchanged files
27
+ - check whether recorded sources are unchanged, changed, or missing
28
+ - create checkpoints with git status, git diff, and generated resume context
29
+ - run a minimal local MCP server for coding-agent clients
30
+ - export a budgeted markdown handoff for manual fallback workflows
31
+
32
+ ## Quick Start
33
+
34
+ ```bash
35
+ npm install -g agentpack-cli
36
+ agentpack init
37
+ agentpack install codex --write
38
+ # or: agentpack install claude --write
39
+ # or: agentpack install cursor --write
40
+ ```
41
+
42
+ Restart or reconnect the coding-agent client. The generated project instructions tell the agent to load Agentpack context at the start, record durable decisions/sources/evidence while working, and checkpoint meaningful progress.
43
+
44
+ Use `agentpack doctor` to verify the local setup. Use `agentpack resume --preset agent --query "<topic>"` when you want to inspect the task state yourself.
45
+
46
+ For local development in this repo:
47
+
48
+ ```bash
49
+ fnm use 22
50
+ npm ci --ignore-scripts
51
+ npm test
52
+ npm run mcp:smoke
53
+ node dist/src/agentpack.js --help
54
+ ```
55
+
56
+ If `npm` is not available yet, install Node through `fnm` first:
57
+
58
+ ```bash
59
+ brew install fnm
60
+ fnm install 22
61
+ fnm default 22
62
+ ```
63
+
64
+ Then add this to `~/.zshrc` and restart the terminal:
65
+
66
+ ```bash
67
+ eval "$(fnm env --use-on-cd --shell zsh)"
68
+ ```
69
+
70
+ See [docs/SETUP.md](docs/SETUP.md) for the full setup guide.
71
+
72
+ This repo uses Agentpack itself through MCP. See [docs/DOGFOOD.md](docs/DOGFOOD.md) for the working protocol.
73
+
74
+ To verify the local MCP server without configuring an agent client yet:
75
+
76
+ ```bash
77
+ npm run mcp:smoke
78
+ ```
79
+
80
+ The smoke runner creates a temporary Agentpack workspace, starts `agentpack mcp`, sends `initialize`, `tools/list`, and a short `resume` flow, then deletes the temporary workspace.
81
+
82
+ See [docs/INTEGRATIONS.md](docs/INTEGRATIONS.md) for safe Codex, Claude Code, and Cursor setup.
83
+ See [docs/agentpack-flow.md](docs/agentpack-flow.md) for a visual execution flow.
84
+
85
+ ## Coding-Agent Loop
86
+
87
+ Agentpack's core loop is built for coding agents working in the same repo over long sessions, compaction, restarts, or handoffs:
88
+
89
+ ```bash
90
+ agentpack resume --preset agent --query "MCP install"
91
+ agentpack source status
92
+ agentpack checkpoint -m "MCP install tested" --status "Ready for docs polish" --next "Update integration docs"
93
+ ```
94
+
95
+ `resume --preset agent` gives the next coding agent the current goal, status, next actions, git state, durable decisions, dead ends, evidence, and source-cache guidance under a rough context budget. `source status` tells the agent which recorded source conclusions are still valid and which files need to be reopened. It compares the current file content to the hash recorded with the source conclusion; it is not a replacement for `git status`. Each recorded source shows hash status and git status separately, and git changes that were never recorded as sources are listed separately.
96
+
97
+ For manual web-chat fallback, `export --to markdown --preset chat` writes a handoff file under `.agentpack/exports/`. For the normal coding-agent workflow, `resume --preset agent` prints a larger task state directly in the terminal or MCP response. Add `--query` when you want Source Cache to include full summaries for sources relevant to the next task, always include changed/missing source records in full, and keep compact path/status/topic stubs for the rest.
98
+
99
+ In a new session, start by loading Agentpack MCP context, or by pasting a markdown handoff when MCP is not available. Then inspect only the files marked changed or missing. During work, record durable decisions, failed approaches, evidence, and source conclusions. End a coherent step with a checkpoint so the next agent inherits a compact state instead of a pile of chat history.
100
+
101
+ ## Security Posture
102
+
103
+ Agentpack keeps the v0 supply chain deliberately small:
104
+
105
+ - zero runtime dependencies
106
+ - exact dev dependency versions
107
+ - committed `package-lock.json`
108
+ - `ignore-scripts=true`
109
+ - no telemetry
110
+ - no network calls during normal CLI or MCP operation
111
+ - best-effort redaction for common secret-looking values in stored context and handoff outputs
112
+ - npm provenance prepared for future public releases
113
+
114
+ ## Core Idea
115
+
116
+ Agentpack stores:
117
+
118
+ - goal and current status
119
+ - next actions
120
+ - decisions
121
+ - dead ends and failed approaches
122
+ - evidence and test outputs
123
+ - relevant files and source conclusions
124
+ - repo name, branch, commit hash, and git diff
125
+ - compact resume context under a rough token budget
126
+
127
+ The source cache is deliberately lightweight. Agentpack stores metadata, hashes, summaries, and optional snippets, not a full copy of the repository.
128
+
129
+ ## Context Budgets
130
+
131
+ `--budget` is an approximate token target for generated handoff context. v0 uses a simple estimate, so the number is a practical target, not an exact API token count. Resume output includes estimated usage and a budget status line that says whether any sections were omitted or truncated.
132
+
133
+ `--query` is an optional local filter for Source Cache. It uses deterministic lexical matching, not embeddings or network calls. Matching sources keep their summaries and snippets; changed or missing source records are always shown in full; non-matching unchanged sources stay visible as compact stubs with path, short topic, hash status, meaning, and guidance. If nothing matches, Agentpack keeps the full Source Cache to avoid false-negative filtering.
134
+
135
+ Suggested defaults:
136
+
137
+ - `1200`: quick status ping
138
+ - `4000`: compact manual handoff
139
+ - `8000`: deeper coding-agent handoff
140
+ - `16000`: large debugging session or review
141
+
142
+ When unsure, start with:
143
+
144
+ ```bash
145
+ agentpack resume --preset quick --query "MCP install"
146
+ agentpack resume --preset agent --query "MCP install"
147
+ ```
148
+
149
+ ## MCP
150
+
151
+ `agentpack mcp` starts a stdio MCP server with tools for reading and writing task state:
152
+
153
+ - `load_context`
154
+ - `record_decision`
155
+ - `record_dead_end`
156
+ - `attach_evidence`
157
+ - `record_source`
158
+ - `source_status`
159
+ - `checkpoint`
160
+ - `resume`
161
+ - `diff`
162
+ - `replay`
163
+
164
+ See [docs/MCP.md](docs/MCP.md) for the current MCP contract and smoke-test flow.
165
+
166
+ ## Roadmap
167
+
168
+ ```text
169
+ v0: CLI + local source cache + markdown fallback export
170
+ v1: local MCP server + coding-agent installers
171
+ v2: stronger repo/file hashes, richer retrieval, and smarter budget packing
172
+ v3: shareable .agentpack bundle
173
+ v4: optional hosted sync/share
174
+ ```
package/SECURITY.md ADDED
@@ -0,0 +1,44 @@
1
+ # Security Policy
2
+
3
+ Agentpack is designed as a local-first developer tool. The default threat model assumes task state may contain source paths, command output, stack traces, and implementation notes that should stay on the developer machine.
4
+
5
+ ## v0 Security Commitments
6
+
7
+ - No telemetry.
8
+ - No network calls in normal CLI or MCP operation.
9
+ - No dependency install or download during `agentpack install`.
10
+ - No `postinstall` script.
11
+ - No shell hooks installed silently.
12
+ - No source upload or hosted sync.
13
+ - No full repository copy in `.agentpack/` by default.
14
+ - Runtime package has zero third-party dependencies in v0.
15
+
16
+ ## npm Supply Chain
17
+
18
+ The project uses a conservative npm setup:
19
+
20
+ - exact dependency versions
21
+ - committed lockfile
22
+ - `ignore-scripts=true` for installs
23
+ - TypeScript compiler only for builds
24
+ - npm provenance enabled for future public releases
25
+ - trusted publishing preferred over long-lived npm tokens
26
+
27
+ Before publishing, maintainers should run:
28
+
29
+ ```bash
30
+ npm ci
31
+ npm audit signatures
32
+ npm test
33
+ npm pack --dry-run
34
+ ```
35
+
36
+ ## Sensitive Data
37
+
38
+ Agentpack redacts common secret-looking values and configured environment variable values from generated context and key local records such as source summaries, evidence, checkpoints, replay output, and MCP context responses.
39
+
40
+ Redaction is best-effort, not a guarantee. Users should treat `.agentpack/` as project-sensitive data and review exported handoff files before sharing them.
41
+
42
+ ## Reporting
43
+
44
+ Until a public repository security contact exists, report issues privately to the project maintainer.
Binary file
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "./cli/index.js";
3
+ runCli(process.argv.slice(2), process.cwd()).catch((error) => {
4
+ const message = error instanceof Error ? error.message : String(error);
5
+ process.stderr.write(`agentpack: ${message}\n`);
6
+ process.exitCode = 1;
7
+ });
8
+ //# sourceMappingURL=agentpack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentpack.js","sourceRoot":"","sources":["../../src/agentpack.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAExC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IACpE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,OAAO,IAAI,CAAC,CAAC;IAChD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type ArgValue = string | boolean | string[];
2
+ export declare function runCli(argv: string[], cwd: string): Promise<void>;
3
+ export declare function resolveMcpStartDir(options: Record<string, ArgValue>, cwd: string): string;
@@ -0,0 +1,412 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { spawnSync } from "node:child_process";
5
+ import { createCheckpoint, diffCheckpoints } from "../core/checkpoints.js";
6
+ import { buildResume } from "../core/resume.js";
7
+ import { formatBudgetPresets, resolveBudget } from "../core/presets.js";
8
+ import { buildDoctorReport } from "../core/doctor.js";
9
+ import { redactForRoot } from "../core/redaction.js";
10
+ import { appendEvent, getPackPath, initPack, readState, requirePackRoot, withPackWriteLock, writeState } from "../core/store.js";
11
+ import { addEvidence, addSourceRecord, formatSourceStatuses, getSourceStatuses, replayEvents } from "../operations.js";
12
+ import { installIntegration } from "../integrations/install.js";
13
+ import { startMcpServer } from "../mcp/server.js";
14
+ export async function runCli(argv, cwd) {
15
+ const command = argv[0];
16
+ const rest = argv.slice(1);
17
+ if (!command || command === "--help" || command === "-h") {
18
+ printHelp();
19
+ return;
20
+ }
21
+ if (command === "--version" || command === "-v") {
22
+ process.stdout.write(`${readPackageVersion()}\n`);
23
+ return;
24
+ }
25
+ if (command === "init") {
26
+ const packPath = initPack(cwd);
27
+ process.stdout.write(`Initialized Agentpack at ${packPath}\n`);
28
+ return;
29
+ }
30
+ if (command === "mcp") {
31
+ const parsed = parseArgs(rest);
32
+ startMcpServer(resolveMcpStartDir(parsed.options, cwd));
33
+ return;
34
+ }
35
+ if (command === "doctor") {
36
+ const report = buildDoctorReport(cwd);
37
+ process.stdout.write(`${report.text}\n`);
38
+ process.exitCode = report.ok ? 0 : 1;
39
+ return;
40
+ }
41
+ const root = requirePackRoot(cwd);
42
+ if (command === "status") {
43
+ const state = readState(root);
44
+ process.stdout.write(`${JSON.stringify(state, null, 2)}\n`);
45
+ return;
46
+ }
47
+ if (command === "record") {
48
+ recordCommand(root, rest);
49
+ return;
50
+ }
51
+ if (command === "note") {
52
+ noteCommand(root, rest);
53
+ return;
54
+ }
55
+ if (command === "source") {
56
+ sourceCommand(root, rest);
57
+ return;
58
+ }
59
+ if (command === "evidence") {
60
+ evidenceCommand(root, rest);
61
+ return;
62
+ }
63
+ if (command === "run") {
64
+ runCommand(root, rest);
65
+ return;
66
+ }
67
+ if (command === "checkpoint") {
68
+ const parsed = parseArgs(rest);
69
+ const checkpoint = createCheckpoint(root, {
70
+ summary: stringOption(parsed.options.message) || stringOption(parsed.options.m) || stringOption(parsed.options.summary),
71
+ status: stringOption(parsed.options.status),
72
+ nextActions: toArray(parsed.options.next)
73
+ });
74
+ process.stdout.write(`Created checkpoint ${checkpoint.id}\n`);
75
+ return;
76
+ }
77
+ if (command === "resume") {
78
+ const parsed = parseArgs(rest);
79
+ const resume = buildResume(root, {
80
+ budget: budgetOption(parsed.options),
81
+ query: stringOption(parsed.options.query)
82
+ });
83
+ process.stdout.write(`${resume.markdown}\n`);
84
+ return;
85
+ }
86
+ if (command === "export") {
87
+ const parsed = parseArgs(rest);
88
+ const target = stringOption(parsed.options.to) || parsed.positionals[0] || "markdown";
89
+ const resume = buildResume(root, {
90
+ budget: budgetOption(parsed.options, 4000),
91
+ query: stringOption(parsed.options.query)
92
+ });
93
+ const filePath = exportPath(root, target);
94
+ writeFileSync(filePath, resume.markdown, "utf8");
95
+ process.stdout.write(`Exported ${target} handoff to ${filePath}\n`);
96
+ return;
97
+ }
98
+ if (command === "diff") {
99
+ const parsed = parseArgs(rest);
100
+ const diff = diffCheckpoints(root, parsed.positionals[0], parsed.positionals[1]);
101
+ process.stdout.write(`${redactForRoot(root, diff)}\n`);
102
+ return;
103
+ }
104
+ if (command === "replay") {
105
+ const parsed = parseArgs(rest);
106
+ const replay = replayEvents(root, numberOption(parsed.options.limit) || 50);
107
+ process.stdout.write(`${redactForRoot(root, replay)}\n`);
108
+ return;
109
+ }
110
+ if (command === "install") {
111
+ const target = rest[0];
112
+ if (!target) {
113
+ throw new Error("install requires target: codex, claude, claude-desktop, or cursor");
114
+ }
115
+ const parsed = parseArgs(rest.slice(1));
116
+ const dryRun = installDryRun(parsed.options);
117
+ const message = installIntegration(root, target, { dryRun });
118
+ process.stdout.write(`${message}\n`);
119
+ return;
120
+ }
121
+ if (command === "set") {
122
+ setCommand(root, rest);
123
+ return;
124
+ }
125
+ throw new Error(`Unknown command: ${command}`);
126
+ }
127
+ export function resolveMcpStartDir(options, cwd) {
128
+ return stringOption(options.root) || process.env.AGENTPACK_ROOT || cwd;
129
+ }
130
+ function printHelp() {
131
+ process.stdout.write(`Agentpack
132
+
133
+ Local task-state ledger for AI coding agents.
134
+
135
+ Usage:
136
+ agentpack init
137
+ agentpack set goal <text>
138
+ agentpack set status <text>
139
+ agentpack set next <item> [--next <item>]
140
+ agentpack source add <file> --summary <text>
141
+ agentpack source status [--json]
142
+ agentpack record decision <text>
143
+ agentpack record dead-end <text> --reason <text>
144
+ agentpack note <text>
145
+ agentpack evidence add --kind test-output --file test.log
146
+ agentpack run "npm test"
147
+ agentpack checkpoint -m <summary> --status <text> --next <item>
148
+ agentpack resume --preset agent [--query <text>]
149
+ agentpack export --to markdown --preset chat [--query <text>]
150
+ agentpack diff [from] [to]
151
+ agentpack replay
152
+ agentpack doctor
153
+ agentpack mcp [--root <path>]
154
+ agentpack install codex|claude|claude-desktop|cursor [--dry-run|--write]
155
+ agentpack --version
156
+ agentpack --help
157
+
158
+ MCP root resolution: --root, then AGENTPACK_ROOT, then current working directory.
159
+ Budget presets: ${formatBudgetPresets()}
160
+ `);
161
+ }
162
+ function recordCommand(root, rest) {
163
+ const type = rest[0];
164
+ const args = rest.slice(1);
165
+ const parsed = parseArgs(args);
166
+ const text = stringOption(parsed.options.text) || parsed.positionals.join(" ");
167
+ if (!isRecordType(type)) {
168
+ throw new Error("record type must be decision, dead-end, or note");
169
+ }
170
+ if (!text) {
171
+ throw new Error("record requires text");
172
+ }
173
+ const event = appendEvent(root, type, {
174
+ text: redactForRoot(root, text),
175
+ reason: redactForRoot(root, stringOption(parsed.options.reason) || ""),
176
+ files: toArray(parsed.options.file),
177
+ evidence: toArray(parsed.options.evidence)
178
+ });
179
+ process.stdout.write(`Recorded ${type} ${event.id}\n`);
180
+ }
181
+ function noteCommand(root, rest) {
182
+ const parsed = parseArgs(rest);
183
+ const text = stringOption(parsed.options.text) || parsed.positionals.join(" ");
184
+ if (!text) {
185
+ throw new Error("note requires text");
186
+ }
187
+ const event = appendEvent(root, "note", { text: redactForRoot(root, text) });
188
+ process.stdout.write(`Recorded note ${event.id}\n`);
189
+ }
190
+ function sourceCommand(root, rest) {
191
+ const subcommand = rest[0];
192
+ const args = rest.slice(1);
193
+ if (subcommand === "status") {
194
+ const parsed = parseArgs(args);
195
+ if (parsed.options.json) {
196
+ const statuses = JSON.stringify(getSourceStatuses(root), null, 2);
197
+ process.stdout.write(`${redactForRoot(root, statuses)}\n`);
198
+ }
199
+ else {
200
+ process.stdout.write(`${formatSourceStatuses(root)}\n`);
201
+ }
202
+ return;
203
+ }
204
+ if (subcommand !== "add") {
205
+ throw new Error("source command supports `add` and `status`");
206
+ }
207
+ const parsed = parseArgs(args);
208
+ const filePath = parsed.positionals[0];
209
+ if (!filePath) {
210
+ throw new Error("source add requires a file path");
211
+ }
212
+ const source = addSourceRecord(root, filePath, {
213
+ summary: stringOption(parsed.options.summary) || stringOption(parsed.options.s) || parsed.positionals.slice(1).join(" "),
214
+ snippet: stringOption(parsed.options.snippet) || ""
215
+ });
216
+ process.stdout.write(`Recorded source ${source.path} (${source.hash.slice(0, 12)})\n`);
217
+ }
218
+ function evidenceCommand(root, rest) {
219
+ const subcommand = rest[0];
220
+ const args = rest.slice(1);
221
+ if (subcommand !== "add") {
222
+ throw new Error("evidence command supports only `add` in v0");
223
+ }
224
+ const parsed = parseArgs(args);
225
+ const event = addEvidence(root, {
226
+ kind: stringOption(parsed.options.kind) || "note",
227
+ file: stringOption(parsed.options.file),
228
+ content: stringOption(parsed.options.content) || parsed.positionals.join(" "),
229
+ command: stringOption(parsed.options.command),
230
+ exitCode: stringOption(parsed.options.exitCode)
231
+ });
232
+ process.stdout.write(`Attached evidence ${event.id}\n`);
233
+ }
234
+ function runCommand(root, rest) {
235
+ const command = rest.join(" ").trim();
236
+ if (!command) {
237
+ throw new Error("run requires a command");
238
+ }
239
+ const startedAt = new Date().toISOString();
240
+ const result = spawnSync(command, {
241
+ cwd: root,
242
+ shell: true,
243
+ encoding: "utf8",
244
+ maxBuffer: 10 * 1024 * 1024
245
+ });
246
+ const stdout = result.stdout || "";
247
+ const stderr = result.stderr || "";
248
+ if (stdout) {
249
+ process.stdout.write(stdout);
250
+ }
251
+ if (stderr) {
252
+ process.stderr.write(stderr);
253
+ }
254
+ const exitCode = typeof result.status === "number" ? result.status : 1;
255
+ const content = [
256
+ `Command: ${command}`,
257
+ `Started: ${startedAt}`,
258
+ `Exit code: ${exitCode}`,
259
+ "",
260
+ "## stdout",
261
+ stdout || "(empty)",
262
+ "",
263
+ "## stderr",
264
+ stderr || "(empty)"
265
+ ].join("\n");
266
+ const event = addEvidence(root, {
267
+ kind: "command-output",
268
+ content,
269
+ command,
270
+ exitCode
271
+ });
272
+ process.stdout.write(`\nAttached command evidence ${event.id}\n`);
273
+ process.exitCode = exitCode;
274
+ }
275
+ function setCommand(root, rest) {
276
+ const field = rest[0];
277
+ const args = rest.slice(1);
278
+ const parsed = parseArgs(args);
279
+ const text = parsed.positionals.join(" ");
280
+ withPackWriteLock(root, () => {
281
+ const state = readState(root);
282
+ if (field === "goal") {
283
+ state.goal = redactForRoot(root, text);
284
+ }
285
+ else if (field === "status") {
286
+ state.currentStatus = redactForRoot(root, text);
287
+ }
288
+ else if (field === "next") {
289
+ state.nextActions = [text, ...toArray(parsed.options.next)]
290
+ .filter(Boolean)
291
+ .map((item) => redactForRoot(root, item));
292
+ }
293
+ else {
294
+ throw new Error("set supports goal, status, or next");
295
+ }
296
+ writeState(root, state);
297
+ });
298
+ process.stdout.write(`Updated ${field}\n`);
299
+ }
300
+ function exportPath(root, target) {
301
+ const normalized = String(target).toLowerCase();
302
+ const fileName = normalized === "chatgpt" ? "chatgpt-handoff.md" : `${normalized}-handoff.md`;
303
+ const filePath = getPackPath(root, "exports", fileName);
304
+ if (!existsSync(path.dirname(filePath))) {
305
+ throw new Error("Agentpack exports directory is missing. Run `agentpack init` again.");
306
+ }
307
+ return filePath;
308
+ }
309
+ function parseArgs(args) {
310
+ const options = {};
311
+ const positionals = [];
312
+ for (let index = 0; index < args.length; index += 1) {
313
+ const item = args[index];
314
+ if (!item) {
315
+ continue;
316
+ }
317
+ if (item.startsWith("--")) {
318
+ const [rawKey, inlineValue] = item.slice(2).split("=", 2);
319
+ if (!rawKey) {
320
+ continue;
321
+ }
322
+ const value = inlineValue !== undefined ? inlineValue : args[index + 1];
323
+ if (inlineValue === undefined && value && !value.startsWith("-")) {
324
+ index += 1;
325
+ addOption(options, rawKey, value);
326
+ }
327
+ else {
328
+ addOption(options, rawKey, true);
329
+ }
330
+ continue;
331
+ }
332
+ if (item.startsWith("-") && item.length > 1) {
333
+ const key = item.slice(1);
334
+ const value = args[index + 1];
335
+ if (value && !value.startsWith("-")) {
336
+ index += 1;
337
+ addOption(options, key, value);
338
+ }
339
+ else {
340
+ addOption(options, key, true);
341
+ }
342
+ continue;
343
+ }
344
+ positionals.push(item);
345
+ }
346
+ return { options, positionals };
347
+ }
348
+ function addOption(options, key, value) {
349
+ if (options[key] === undefined) {
350
+ options[key] = value;
351
+ return;
352
+ }
353
+ if (Array.isArray(options[key])) {
354
+ const textValue = stringOption(value);
355
+ if (textValue) {
356
+ options[key].push(textValue);
357
+ }
358
+ return;
359
+ }
360
+ options[key] = [...toArray(options[key]), ...toArray(value)];
361
+ }
362
+ function toArray(value) {
363
+ if (value === undefined || value === true || value === false) {
364
+ return [];
365
+ }
366
+ return Array.isArray(value) ? value : [value];
367
+ }
368
+ function stringOption(value) {
369
+ if (value === undefined || value === true || value === false) {
370
+ return "";
371
+ }
372
+ return Array.isArray(value) ? value[0] || "" : value;
373
+ }
374
+ function numberOption(value) {
375
+ if (value === undefined || value === true || value === false) {
376
+ return 0;
377
+ }
378
+ const number = Number(value);
379
+ return Number.isFinite(number) ? number : 0;
380
+ }
381
+ function installDryRun(options) {
382
+ const dryRun = options["dry-run"] === true;
383
+ const write = options.write === true;
384
+ if (dryRun && write) {
385
+ throw new Error("install accepts either --dry-run or --write, not both");
386
+ }
387
+ return !write;
388
+ }
389
+ function budgetOption(options, fallback = 0) {
390
+ return resolveBudget({
391
+ budget: numberOption(options.budget),
392
+ preset: stringOption(options.preset)
393
+ }, fallback);
394
+ }
395
+ function isRecordType(value) {
396
+ return value === "decision" || value === "dead-end" || value === "note";
397
+ }
398
+ function readPackageVersion() {
399
+ // Resolve package.json relative to the compiled CLI file (dist/src/cli/index.js).
400
+ // Works both in the source tree and after `npm install -g`, because the package
401
+ // root is always three levels above this file.
402
+ try {
403
+ const here = path.dirname(fileURLToPath(import.meta.url));
404
+ const pkgPath = path.resolve(here, "..", "..", "..", "package.json");
405
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
406
+ return pkg.version ?? "unknown";
407
+ }
408
+ catch {
409
+ return "unknown";
410
+ }
411
+ }
412
+ //# sourceMappingURL=index.js.map