@fitlab-ai/agent-infra 0.9.3 → 0.9.4

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 (175) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +2 -0
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +134 -123
  6. package/dist/lib/agent-clients/adapter.js +92 -2
  7. package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
  8. package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
  9. package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
  10. package/dist/lib/agent-clients/adapters/claude-code.js +17 -3
  11. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
  12. package/dist/lib/agent-clients/adapters/opencode.js +15 -3
  13. package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  14. package/dist/lib/agent-clients/registry.js +9 -1
  15. package/dist/lib/internal/sandbox-control.js +24 -0
  16. package/dist/lib/internal/task-activity.js +45 -13
  17. package/dist/lib/internal/task-ledger.js +12 -3
  18. package/dist/lib/internal/task-lifecycle.js +19 -1
  19. package/dist/lib/internal/task-orchestration.js +122 -18
  20. package/dist/lib/platform/github-client.js +19 -2
  21. package/dist/lib/platform/pr-checks.js +1 -1
  22. package/dist/lib/platform/pull-requests.js +47 -1
  23. package/dist/lib/platform/releases.js +13 -5
  24. package/dist/lib/process-data/index.js +239 -0
  25. package/dist/lib/process-data/normalize.js +198 -0
  26. package/dist/lib/process-data/privacy.js +33 -0
  27. package/dist/lib/process-data/reconcile.js +95 -0
  28. package/dist/lib/process-data/repair.js +58 -0
  29. package/dist/lib/process-data/sources.js +345 -0
  30. package/dist/lib/process-data/store.js +282 -0
  31. package/dist/lib/process-data/types.js +2 -0
  32. package/dist/lib/run/index.js +4 -3
  33. package/dist/lib/run/tui.js +7 -8
  34. package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
  35. package/dist/lib/sandbox/capture.js +7 -2
  36. package/dist/lib/sandbox/commands/create.js +109 -228
  37. package/dist/lib/sandbox/commands/enter.js +15 -47
  38. package/dist/lib/sandbox/commands/list-running.js +16 -6
  39. package/dist/lib/sandbox/commands/ls.js +10 -6
  40. package/dist/lib/sandbox/commands/rebuild.js +4 -3
  41. package/dist/lib/sandbox/commands/refresh.js +4 -84
  42. package/dist/lib/sandbox/commands/rm.js +24 -3
  43. package/dist/lib/sandbox/commands/show.js +12 -3
  44. package/dist/lib/sandbox/commands/start.js +7 -4
  45. package/dist/lib/sandbox/config.js +2 -0
  46. package/dist/lib/sandbox/constants.js +6 -0
  47. package/dist/lib/sandbox/control/client.js +44 -0
  48. package/dist/lib/sandbox/control/protocol.js +35 -0
  49. package/dist/lib/sandbox/control/server.js +109 -0
  50. package/dist/lib/sandbox/dockerfile.js +36 -13
  51. package/dist/lib/sandbox/mounts.js +17 -4
  52. package/dist/lib/sandbox/recovery.js +128 -5
  53. package/dist/lib/sandbox/redaction.js +12 -0
  54. package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
  55. package/dist/lib/sandbox/workspace-identity.js +95 -0
  56. package/dist/lib/sandbox/workspace-view.js +103 -0
  57. package/dist/lib/task/activity-intent.js +257 -51
  58. package/dist/lib/task/commit-finalization.js +267 -0
  59. package/dist/lib/task/commit-intent.js +165 -0
  60. package/dist/lib/task/decision-intents.js +31 -17
  61. package/dist/lib/task/delegation-receipts.js +2 -2
  62. package/dist/lib/task/implementation-inputs.js +54 -7
  63. package/dist/lib/task/ledger-intents.js +35 -1
  64. package/dist/lib/task/orchestration.js +363 -28
  65. package/dist/lib/task/resolve-ref.js +41 -1
  66. package/lib/agent-clients/adapter.ts +126 -2
  67. package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
  68. package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
  69. package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
  70. package/lib/agent-clients/adapters/claude-code.ts +21 -3
  71. package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
  72. package/lib/agent-clients/adapters/opencode.ts +18 -3
  73. package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  74. package/lib/agent-clients/registry.ts +9 -1
  75. package/lib/internal/sandbox-control.ts +24 -0
  76. package/lib/internal/task-activity.ts +50 -14
  77. package/lib/internal/task-ledger.ts +12 -3
  78. package/lib/internal/task-lifecycle.ts +22 -1
  79. package/lib/internal/task-orchestration.ts +81 -11
  80. package/lib/platform/github-client.ts +20 -2
  81. package/lib/platform/pr-checks.ts +1 -1
  82. package/lib/platform/pull-requests.ts +49 -1
  83. package/lib/platform/releases.ts +16 -4
  84. package/lib/process-data/index.ts +224 -0
  85. package/lib/process-data/normalize.ts +202 -0
  86. package/lib/process-data/privacy.ts +41 -0
  87. package/lib/process-data/reconcile.ts +110 -0
  88. package/lib/process-data/repair.ts +64 -0
  89. package/lib/process-data/sources.ts +354 -0
  90. package/lib/process-data/store.ts +315 -0
  91. package/lib/process-data/types.ts +135 -0
  92. package/lib/run/index.ts +5 -3
  93. package/lib/run/tui.ts +11 -8
  94. package/lib/sandbox/agent-client-reconciler.ts +44 -2
  95. package/lib/sandbox/capture.ts +9 -2
  96. package/lib/sandbox/commands/create.ts +126 -260
  97. package/lib/sandbox/commands/enter.ts +13 -59
  98. package/lib/sandbox/commands/list-running.ts +23 -6
  99. package/lib/sandbox/commands/ls.ts +22 -6
  100. package/lib/sandbox/commands/rebuild.ts +4 -3
  101. package/lib/sandbox/commands/refresh.ts +4 -126
  102. package/lib/sandbox/commands/rm.ts +24 -3
  103. package/lib/sandbox/commands/show.ts +29 -3
  104. package/lib/sandbox/commands/start.ts +6 -4
  105. package/lib/sandbox/config.ts +4 -0
  106. package/lib/sandbox/constants.ts +8 -0
  107. package/lib/sandbox/control/client.ts +55 -0
  108. package/lib/sandbox/control/protocol.ts +75 -0
  109. package/lib/sandbox/control/server.ts +117 -0
  110. package/lib/sandbox/dockerfile.ts +66 -13
  111. package/lib/sandbox/mounts.ts +24 -4
  112. package/lib/sandbox/recovery.ts +163 -6
  113. package/lib/sandbox/redaction.ts +14 -0
  114. package/lib/sandbox/runtimes/base.dockerfile +24 -74
  115. package/lib/sandbox/tool-types.ts +16 -0
  116. package/lib/sandbox/workspace-identity.ts +123 -0
  117. package/lib/sandbox/workspace-view.ts +143 -0
  118. package/lib/task/activity-intent.ts +350 -61
  119. package/lib/task/commit-finalization.ts +356 -0
  120. package/lib/task/commit-intent.ts +230 -0
  121. package/lib/task/decision-intents.ts +32 -12
  122. package/lib/task/delegation-receipts.ts +2 -2
  123. package/lib/task/implementation-inputs.ts +62 -8
  124. package/lib/task/ledger-intents.ts +43 -3
  125. package/lib/task/orchestration.ts +501 -33
  126. package/lib/task/resolve-ref.ts +46 -1
  127. package/package.json +1 -1
  128. package/templates/.agents/rules/README.en.md +1 -1
  129. package/templates/.agents/rules/README.zh-CN.md +1 -1
  130. package/templates/.agents/rules/human-decision-context.en.md +11 -2
  131. package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
  132. package/templates/.agents/rules/pr-checks-commands.en.md +1 -1
  133. package/templates/.agents/rules/pr-checks-commands.github.en.md +1 -1
  134. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +2 -2
  135. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +1 -1
  136. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  137. package/templates/.agents/rules/task-management.en.md +2 -0
  138. package/templates/.agents/rules/task-management.zh-CN.md +2 -0
  139. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  140. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +1 -1
  141. package/templates/.agents/skills/commit/SKILL.en.md +21 -11
  142. package/templates/.agents/skills/commit/SKILL.zh-CN.md +27 -11
  143. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
  144. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
  145. package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
  146. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
  147. package/templates/.agents/skills/complete-task/SKILL.en.md +1 -1
  148. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +1 -1
  149. package/templates/.agents/skills/create-pr/SKILL.en.md +4 -4
  150. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +4 -4
  151. package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
  152. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
  153. package/templates/.agents/skills/review-code/SKILL.en.md +2 -2
  154. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +2 -2
  155. package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
  156. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
  157. package/templates/.agents/skills/review-pr/SKILL.en.md +36 -8
  158. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +36 -8
  159. package/templates/.agents/skills/review-pr/reference/report-template.en.md +1 -1
  160. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +1 -1
  161. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +67 -50
  162. package/templates/.agents/skills/watch-pr/SKILL.en.md +4 -4
  163. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +4 -4
  164. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +2 -2
  165. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +2 -2
  166. package/templates/.agents/templates/task.en.md +1 -1
  167. package/templates/.agents/templates/task.zh-CN.md +1 -1
  168. package/templates/.claude/commands/watch-pr.en.md +1 -1
  169. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  170. package/templates/.claude/rules/tool-preferences.en.md +20 -0
  171. package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
  172. package/templates/.opencode/commands/watch-pr.en.md +1 -1
  173. package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
  174. package/templates/.gemini/commands/_project_/review-pr.en.toml +0 -10
  175. package/templates/.gemini/commands/_project_/review-pr.zh-CN.toml +0 -10
package/README.md CHANGED
@@ -91,6 +91,7 @@ Every command above works the same way in Claude Code, Codex, Antigravity CLI, a
91
91
  - **Bilingual project docs**: English-first docs with synchronized Chinese translations
92
92
  - **Template-source architecture**: `templates/` mirrors the rendered project structure
93
93
  - **AI-assisted updates**: template changes can be merged while preserving project-specific customization
94
+ - **Auditable process-data archive**: preserve local tasks and GitHub evidence in append-only, verifiable snapshots
94
95
 
95
96
  ## Quick Start
96
97
 
@@ -206,6 +207,7 @@ my-project/
206
207
 
207
208
  In-depth guides live under [`docs/en/`](./docs/en/README.md):
208
209
 
210
+ - [Process Data Archive](./docs/en/process-data.md) — capture, verify, audit, repair, export, privacy, and backup guidance
209
211
  - [Architecture Overview](./docs/en/architecture.md) — bootstrap CLI, end-to-end flow, layered architecture
210
212
  - [Platform Support](./docs/en/platform-support.md) — macOS, Linux, Windows; sandbox engines and resources
211
213
  - [Sandbox](./docs/en/sandbox.md) — sandbox aliases, host-sandbox file exchange, user-level dotfiles channel
package/README.zh-CN.md CHANGED
@@ -90,6 +90,7 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
90
90
  - **双语文档**:英文为主文档,配套同步的中文版本
91
91
  - **模板源架构**:`templates/` 目录镜像最终渲染出的项目结构
92
92
  - **AI 辅助升级**:模板升级时可合并变更,同时尽量保留项目侧定制
93
+ - **可审计的过程数据归档**:把本地任务与 GitHub 证据保存为只追加、可校验的快照
93
94
 
94
95
  ## 快速开始
95
96
 
@@ -205,6 +206,7 @@ my-project/
205
206
 
206
207
  深度指南位于 [`docs/zh-CN/`](./docs/zh-CN/README.md):
207
208
 
209
+ - [过程数据归档](./docs/zh-CN/process-data.md) — 采集、校验、审计、修复、导出、隐私与备份指南
208
210
  - [架构概览](./docs/zh-CN/architecture.md) — 引导 CLI、端到端流程、分层架构
209
211
  - [平台支持](./docs/zh-CN/platform-support.md) — macOS、Linux、Windows;沙箱引擎与资源配置
210
212
  - [沙箱](./docs/zh-CN/sandbox.md) — 沙箱 aliases、宿主-沙箱文件交换、用户级 dotfiles 通道
package/bin/cli.ts CHANGED
@@ -17,6 +17,7 @@ Usage: ai <command> [options]
17
17
  Commands:
18
18
  agent-client List, inspect, enable, disable, or configure Agent Clients
19
19
  cp <ssh-alias> Copy local clipboard image to a remote macOS clipboard or Linux sandbox over SSH
20
+ data Capture, verify, audit, repair, and export process data
20
21
  decide Record a ruling; code-stage decisions require explicit implementation intent
21
22
  help Show this help message
22
23
  init Initialize a new project with update-agent-infra seed command
@@ -94,6 +95,17 @@ switch (command) {
94
95
  if (code) process.exitCode = code;
95
96
  break;
96
97
  }
98
+ case 'data': {
99
+ const imported = await importCommand('../lib/process-data/index.ts');
100
+ if (!imported) break;
101
+ const { cmdData } = imported;
102
+ const code = await cmdData(process.argv.slice(3)).catch((e: unknown) => {
103
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
104
+ return 1;
105
+ });
106
+ if (code) process.exitCode = code;
107
+ break;
108
+ }
97
109
  case 'decide': {
98
110
  const imported = await importCommand('../lib/decide.ts');
99
111
  if (!imported) break;
package/dist/bin/cli.js CHANGED
@@ -21,6 +21,7 @@ Usage: ai <command> [options]
21
21
  Commands:
22
22
  agent-client List, inspect, enable, disable, or configure Agent Clients
23
23
  cp <ssh-alias> Copy local clipboard image to a remote macOS clipboard or Linux sandbox over SSH
24
+ data Capture, verify, audit, repair, and export process data
24
25
  decide Record a ruling; code-stage decisions require explicit implementation intent
25
26
  help Show this help message
26
27
  init Initialize a new project with update-agent-infra seed command
@@ -96,6 +97,19 @@ switch (command) {
96
97
  process.exitCode = code;
97
98
  break;
98
99
  }
100
+ case 'data': {
101
+ const imported = await importCommand('../lib/process-data/index.ts');
102
+ if (!imported)
103
+ break;
104
+ const { cmdData } = imported;
105
+ const code = await cmdData(process.argv.slice(3)).catch((e) => {
106
+ process.stderr.write(`Error: ${errorMessage(e)}\n`);
107
+ return 1;
108
+ });
109
+ if (code)
110
+ process.exitCode = code;
111
+ break;
112
+ }
99
113
  case 'decide': {
100
114
  const imported = await importCommand('../lib/decide.ts');
101
115
  if (!imported)
@@ -5,129 +5,140 @@ if (major < 22 || (major === 22 && minor < 9)) {
5
5
  process.exit(1);
6
6
  }
7
7
  const command = process.argv[2] || '';
8
- switch (command) {
9
- case 'agent-client': {
10
- const { agentClient } = await import('../lib/internal/agent-client.js');
11
- agentClient(process.argv.slice(3));
12
- break;
13
- }
14
- case 'git-workflow': {
15
- const { gitWorkflow } = await import('../lib/internal/git-workflow.js');
16
- gitWorkflow(process.argv.slice(3));
17
- break;
18
- }
19
- case 'release-workflow': {
20
- const { releaseWorkflow } = await import('../lib/internal/release-workflow.js');
21
- await releaseWorkflow(process.argv.slice(3));
22
- break;
23
- }
24
- case 'platform-release-notes': {
25
- const { platformReleaseNotes } = await import('../lib/internal/platform-release-notes.js');
26
- platformReleaseNotes(process.argv.slice(3));
27
- break;
28
- }
29
- case 'platform-context': {
30
- const { platformContext } = await import('../lib/internal/platform-context.js');
31
- platformContext(process.argv.slice(3));
32
- break;
33
- }
34
- case 'platform-comment': {
35
- const { platformComment } = await import('../lib/internal/platform-comment.js');
36
- platformComment(process.argv.slice(3));
37
- break;
38
- }
39
- case 'platform-issue': {
40
- const { platformIssue } = await import('../lib/internal/platform-issue.js');
41
- platformIssue(process.argv.slice(3));
42
- break;
43
- }
44
- case 'platform-pr': {
45
- const { platformPr } = await import('../lib/internal/platform-pr.js');
46
- platformPr(process.argv.slice(3));
47
- break;
48
- }
49
- case 'platform-pr-review': {
50
- const { platformPrReview } = await import('../lib/internal/platform-pr-review.js');
51
- platformPrReview(process.argv.slice(3));
52
- break;
53
- }
54
- case 'pr-review-grade': {
55
- const { prReviewGrade } = await import('../lib/internal/pr-review-grade.js');
56
- prReviewGrade(process.argv.slice(3));
57
- break;
58
- }
59
- case 'platform-checks': {
60
- const { platformChecks } = await import('../lib/internal/platform-checks.js');
61
- await platformChecks(process.argv.slice(3));
62
- break;
63
- }
64
- case 'task-context': {
65
- const { taskContext } = await import('../lib/internal/task-context.js');
66
- taskContext(process.argv.slice(3));
67
- break;
68
- }
69
- case 'task-ledger': {
70
- const { taskLedger } = await import('../lib/internal/task-ledger.js');
71
- taskLedger(process.argv.slice(3));
72
- break;
73
- }
74
- case 'task-warning': {
75
- const { taskWarning } = await import('../lib/internal/task-warning.js');
76
- taskWarning(process.argv.slice(3));
77
- break;
78
- }
79
- case 'task-activity': {
80
- const { taskActivity } = await import('../lib/internal/task-activity.js');
81
- taskActivity(process.argv.slice(3));
82
- break;
83
- }
84
- case 'task-artifact': {
85
- const { taskArtifact } = await import('../lib/internal/task-artifact.js');
86
- taskArtifact(process.argv.slice(3));
87
- break;
88
- }
89
- case 'task-orchestration': {
90
- const { taskOrchestration } = await import('../lib/internal/task-orchestration.js');
91
- taskOrchestration(process.argv.slice(3));
92
- break;
93
- }
94
- case 'task-review': {
95
- const { taskReview } = await import('../lib/internal/task-review.js');
96
- taskReview(process.argv.slice(3));
97
- break;
98
- }
99
- case 'task-event': {
100
- const { taskEvent } = await import('../lib/internal/task-event.js');
101
- taskEvent(process.argv.slice(3));
102
- break;
103
- }
104
- case 'task-lifecycle': {
105
- const { taskLifecycle } = await import('../lib/internal/task-lifecycle.js');
106
- taskLifecycle(process.argv.slice(3));
107
- break;
108
- }
109
- case 'task-short-id': {
110
- const { taskShortId } = await import('../lib/internal/task-short-id.js');
111
- taskShortId(process.argv.slice(3));
112
- break;
113
- }
114
- case 'task-snapshot': {
115
- const { taskSnapshot } = await import('../lib/internal/task-snapshot.js');
116
- taskSnapshot(process.argv.slice(3));
117
- break;
118
- }
119
- case 'task-verify': {
120
- const { taskVerify } = await import('../lib/internal/task-verify.js');
121
- taskVerify(process.argv.slice(3));
122
- break;
123
- }
124
- default:
125
- process.stdout.write(`${JSON.stringify({
126
- status: 'failed',
127
- changed: false,
128
- error: { code: 'INTERNAL_COMMAND_INVALID', message: `unknown internal command '${command}'` }
129
- })}\n`);
130
- process.exitCode = 1;
8
+ if (process.env.AGENT_INFRA_CONTROL_TOKEN
9
+ && (command === 'task-lifecycle' || command === 'task-orchestration')) {
10
+ const { sandboxControl } = await import('../lib/internal/sandbox-control.js');
11
+ sandboxControl(['client', command, ...process.argv.slice(3)]);
131
12
  }
13
+ else
14
+ switch (command) {
15
+ case 'sandbox-control': {
16
+ const { sandboxControl } = await import('../lib/internal/sandbox-control.js');
17
+ sandboxControl(process.argv.slice(3));
18
+ break;
19
+ }
20
+ case 'agent-client': {
21
+ const { agentClient } = await import('../lib/internal/agent-client.js');
22
+ agentClient(process.argv.slice(3));
23
+ break;
24
+ }
25
+ case 'git-workflow': {
26
+ const { gitWorkflow } = await import('../lib/internal/git-workflow.js');
27
+ gitWorkflow(process.argv.slice(3));
28
+ break;
29
+ }
30
+ case 'release-workflow': {
31
+ const { releaseWorkflow } = await import('../lib/internal/release-workflow.js');
32
+ await releaseWorkflow(process.argv.slice(3));
33
+ break;
34
+ }
35
+ case 'platform-release-notes': {
36
+ const { platformReleaseNotes } = await import('../lib/internal/platform-release-notes.js');
37
+ platformReleaseNotes(process.argv.slice(3));
38
+ break;
39
+ }
40
+ case 'platform-context': {
41
+ const { platformContext } = await import('../lib/internal/platform-context.js');
42
+ platformContext(process.argv.slice(3));
43
+ break;
44
+ }
45
+ case 'platform-comment': {
46
+ const { platformComment } = await import('../lib/internal/platform-comment.js');
47
+ platformComment(process.argv.slice(3));
48
+ break;
49
+ }
50
+ case 'platform-issue': {
51
+ const { platformIssue } = await import('../lib/internal/platform-issue.js');
52
+ platformIssue(process.argv.slice(3));
53
+ break;
54
+ }
55
+ case 'platform-pr': {
56
+ const { platformPr } = await import('../lib/internal/platform-pr.js');
57
+ platformPr(process.argv.slice(3));
58
+ break;
59
+ }
60
+ case 'platform-pr-review': {
61
+ const { platformPrReview } = await import('../lib/internal/platform-pr-review.js');
62
+ platformPrReview(process.argv.slice(3));
63
+ break;
64
+ }
65
+ case 'pr-review-grade': {
66
+ const { prReviewGrade } = await import('../lib/internal/pr-review-grade.js');
67
+ prReviewGrade(process.argv.slice(3));
68
+ break;
69
+ }
70
+ case 'platform-checks': {
71
+ const { platformChecks } = await import('../lib/internal/platform-checks.js');
72
+ await platformChecks(process.argv.slice(3));
73
+ break;
74
+ }
75
+ case 'task-context': {
76
+ const { taskContext } = await import('../lib/internal/task-context.js');
77
+ taskContext(process.argv.slice(3));
78
+ break;
79
+ }
80
+ case 'task-ledger': {
81
+ const { taskLedger } = await import('../lib/internal/task-ledger.js');
82
+ taskLedger(process.argv.slice(3));
83
+ break;
84
+ }
85
+ case 'task-warning': {
86
+ const { taskWarning } = await import('../lib/internal/task-warning.js');
87
+ taskWarning(process.argv.slice(3));
88
+ break;
89
+ }
90
+ case 'task-activity': {
91
+ const { taskActivity } = await import('../lib/internal/task-activity.js');
92
+ taskActivity(process.argv.slice(3));
93
+ break;
94
+ }
95
+ case 'task-artifact': {
96
+ const { taskArtifact } = await import('../lib/internal/task-artifact.js');
97
+ taskArtifact(process.argv.slice(3));
98
+ break;
99
+ }
100
+ case 'task-orchestration': {
101
+ const { taskOrchestration } = await import('../lib/internal/task-orchestration.js');
102
+ taskOrchestration(process.argv.slice(3));
103
+ break;
104
+ }
105
+ case 'task-review': {
106
+ const { taskReview } = await import('../lib/internal/task-review.js');
107
+ taskReview(process.argv.slice(3));
108
+ break;
109
+ }
110
+ case 'task-event': {
111
+ const { taskEvent } = await import('../lib/internal/task-event.js');
112
+ taskEvent(process.argv.slice(3));
113
+ break;
114
+ }
115
+ case 'task-lifecycle': {
116
+ const { taskLifecycle } = await import('../lib/internal/task-lifecycle.js');
117
+ taskLifecycle(process.argv.slice(3));
118
+ break;
119
+ }
120
+ case 'task-short-id': {
121
+ const { taskShortId } = await import('../lib/internal/task-short-id.js');
122
+ taskShortId(process.argv.slice(3));
123
+ break;
124
+ }
125
+ case 'task-snapshot': {
126
+ const { taskSnapshot } = await import('../lib/internal/task-snapshot.js');
127
+ taskSnapshot(process.argv.slice(3));
128
+ break;
129
+ }
130
+ case 'task-verify': {
131
+ const { taskVerify } = await import('../lib/internal/task-verify.js');
132
+ taskVerify(process.argv.slice(3));
133
+ break;
134
+ }
135
+ default:
136
+ process.stdout.write(`${JSON.stringify({
137
+ status: 'failed',
138
+ changed: false,
139
+ error: { code: 'INTERNAL_COMMAND_INVALID', message: `unknown internal command '${command}'` }
140
+ })}\n`);
141
+ process.exitCode = 1;
142
+ }
132
143
  export {};
133
144
  //# sourceMappingURL=internal-cli.js.map
@@ -3,6 +3,43 @@ import { SANDBOX_HOOK_PHASES } from '../sandbox/tool-types.js';
3
3
  const PROJECT_ASSET_CATEGORIES = ['managed', 'merged', 'ejected'];
4
4
  const MAX_SANDBOX_HOOK_TIMEOUT_MS = 300_000;
5
5
  const TOOL_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
6
+ function freezeSandboxImageDescriptor(id, value) {
7
+ if (value === undefined)
8
+ return undefined;
9
+ const keys = value && typeof value === 'object' && !Array.isArray(value)
10
+ ? Object.keys(value)
11
+ : [];
12
+ const fragment = value?.dockerfileFragment;
13
+ const exclusions = value?.dotfilesExclusions;
14
+ const validExclusion = (entry) => {
15
+ if (typeof entry !== 'string'
16
+ || entry === ''
17
+ || entry.startsWith('/')
18
+ || entry.endsWith('/')
19
+ || entry.includes('\\')
20
+ || /[*?[\]]/.test(entry)) {
21
+ return false;
22
+ }
23
+ return entry.split('/').every((segment) => segment !== '' && segment !== '.' && segment !== '..');
24
+ };
25
+ if (!value
26
+ || typeof value !== 'object'
27
+ || Array.isArray(value)
28
+ || keys.length === 0
29
+ || keys.some((key) => !['dockerfileFragment', 'dotfilesExclusions'].includes(key))
30
+ || (fragment !== undefined && (typeof fragment !== 'string' || !TOOL_ID_PATTERN.test(fragment)))
31
+ || (exclusions !== undefined && (!Array.isArray(exclusions)
32
+ || exclusions.some((entry) => !validExclusion(entry))
33
+ || new Set(exclusions).size !== exclusions.length))) {
34
+ throw new Error(`Agent Client '${id}' has an invalid sandbox image descriptor`);
35
+ }
36
+ return Object.freeze({
37
+ ...(fragment === undefined ? {} : { dockerfileFragment: fragment }),
38
+ ...(exclusions === undefined
39
+ ? {}
40
+ : { dotfilesExclusions: Object.freeze([...exclusions]) })
41
+ });
42
+ }
6
43
  function freezeRecoveryProbe(id, checkId, value) {
7
44
  if (!value
8
45
  || typeof value.script !== 'string'
@@ -236,9 +273,55 @@ function defineAgentClientAdapter(candidate) {
236
273
  target: seed.target
237
274
  });
238
275
  });
276
+ let customCommand;
277
+ if (candidate.project.customCommand !== undefined) {
278
+ const descriptor = candidate.project.customCommand;
279
+ const placeholders = typeof descriptor?.target === 'string'
280
+ ? [...descriptor.target.matchAll(/\$\{([^}]+)\}/g)].map((match) => match[1])
281
+ : [];
282
+ const targetProbe = typeof descriptor?.target === 'string'
283
+ ? descriptor.target.replaceAll('${skillName}', 'skill')
284
+ : '';
285
+ const frontmatter = descriptor?.frontmatter;
286
+ if (!descriptor
287
+ || typeof descriptor !== 'object'
288
+ || placeholders.length !== 1
289
+ || placeholders[0] !== 'skillName'
290
+ || targetProbe.includes('${')
291
+ || !isProjectRelativeLiteral(targetProbe)
292
+ || !paths.some((prefix) => targetProbe.startsWith(prefix))
293
+ || !frontmatter
294
+ || typeof frontmatter !== 'object'
295
+ || Array.isArray(frontmatter)
296
+ || Object.values(frontmatter).some((value) => typeof value !== 'string' && typeof value !== 'boolean')
297
+ || (descriptor.argumentsToken !== undefined
298
+ && (typeof descriptor.argumentsToken !== 'string'
299
+ || descriptor.argumentsToken === ''
300
+ || /[\r\n]/.test(descriptor.argumentsToken)))
301
+ || (descriptor.includeUsage !== undefined
302
+ && typeof descriptor.includeUsage !== 'boolean')
303
+ || (descriptor.inheritDisableModelInvocation !== undefined
304
+ && typeof descriptor.inheritDisableModelInvocation !== 'boolean')) {
305
+ throw new Error(`Agent Client '${candidate.id}' has an invalid custom command`);
306
+ }
307
+ customCommand = Object.freeze({
308
+ target: descriptor.target,
309
+ frontmatter: Object.freeze({ ...frontmatter }),
310
+ ...(descriptor.argumentsToken === undefined
311
+ ? {}
312
+ : { argumentsToken: descriptor.argumentsToken }),
313
+ ...(descriptor.includeUsage === undefined
314
+ ? {}
315
+ : { includeUsage: descriptor.includeUsage }),
316
+ ...(descriptor.inheritDisableModelInvocation === undefined
317
+ ? {}
318
+ : { inheritDisableModelInvocation: descriptor.inheritDisableModelInvocation })
319
+ });
320
+ }
239
321
  if (!candidate.sandbox || typeof candidate.sandbox.createTool !== 'function') {
240
322
  throw new Error(`Agent Client '${candidate.id}' requires a sandbox descriptor`);
241
323
  }
324
+ const image = freezeSandboxImageDescriptor(candidate.id, candidate.sandbox.image);
242
325
  const hookIds = new Set();
243
326
  const hooks = Object.freeze(candidate.sandbox.hooks.map((hook) => {
244
327
  if (!hook.id
@@ -328,9 +411,16 @@ function defineAgentClientAdapter(candidate) {
328
411
  project: Object.freeze({
329
412
  ownedPathPrefixes: Object.freeze(paths),
330
413
  ...projectAssets,
331
- seedCommands: Object.freeze(seedCommands)
414
+ seedCommands: Object.freeze(seedCommands),
415
+ ...(customCommand === undefined ? {} : { customCommand })
332
416
  }),
333
- sandbox: Object.freeze({ createTool, aliases, hooks, recoveryChecks })
417
+ sandbox: Object.freeze({
418
+ createTool,
419
+ aliases,
420
+ hooks,
421
+ recoveryChecks,
422
+ ...(image === undefined ? {} : { image })
423
+ })
334
424
  });
335
425
  }
336
426
  export { defineAgentClientAdapter };
@@ -2,15 +2,10 @@ import { execFileSync } from 'node:child_process';
2
2
  import { randomBytes } from 'node:crypto';
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
- import { hostJoin } from './engines/wsl2-paths.js';
5
+ import { hostJoin } from '../../sandbox/engines/wsl2-paths.js';
6
+ import { redactCommandError } from '../../sandbox/redaction.js';
6
7
  const LOCKED_PATTERN = /errSecInteractionNotAllowed|User interaction is not allowed/i;
7
8
  const NOT_FOUND_PATTERN = /errSecItemNotFound|specified item could not be found/i;
8
- const REDACTION_PATTERNS = [
9
- { pattern: /\{[^{}]*"claudeAiOauth"[\s\S]*?\}\s*\}/g, replacement: '[REDACTED credentials blob]' },
10
- { pattern: /sk-ant-[A-Za-z0-9_-]{20,}/g, replacement: '[REDACTED claude token]' },
11
- { pattern: /gh[psoru]_[A-Za-z0-9]{30,}/g, replacement: '[REDACTED github token]' },
12
- { pattern: /Bearer\s+[A-Za-z0-9._~+/=-]{20,}/gi, replacement: 'Bearer [REDACTED]' }
13
- ];
14
9
  function isRecord(value) {
15
10
  return typeof value === 'object' && value !== null && !Array.isArray(value);
16
11
  }
@@ -20,13 +15,8 @@ function hasNonEmptyString(value) {
20
15
  function errorMessage(error) {
21
16
  return error instanceof Error ? error.message : 'unknown error';
22
17
  }
23
- export function redactCommandError(text) {
24
- if (!text || typeof text !== 'string') {
25
- return '';
26
- }
27
- return REDACTION_PATTERNS.reduce((result, { pattern, replacement }) => result.replace(pattern, replacement), text);
28
- }
29
18
  export const redactSecurityOutput = redactCommandError;
19
+ export { redactCommandError };
30
20
  function extractStderrSafely(error) {
31
21
  const stderr = typeof error === 'object' && error !== null && 'stderr' in error
32
22
  ? error.stderr
@@ -492,4 +482,4 @@ export function prepareClaudeCredentials(home, project, resolvedTools, extractFn
492
482
  writeFn(home, project, blob);
493
483
  return { status: 'OK' };
494
484
  }
495
- //# sourceMappingURL=credentials.js.map
485
+ //# sourceMappingURL=claude-code-credentials.js.map
@@ -0,0 +1,85 @@
1
+ import { homedir } from 'node:os';
2
+ import { parseArgs } from 'node:util';
3
+ import { buildLockedGuidance, discoverProjects, formatCredentialWarnings, formatRemaining, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from './claude-code-credentials.js';
4
+ import { runProbe } from '../../sandbox/shell.js';
5
+ const USAGE = 'Usage: ai sandbox refresh';
6
+ export function probeClaudeStatus(spawnFn = runProbe) {
7
+ const result = spawnFn('claude', ['/status'], {
8
+ encoding: 'utf8',
9
+ stdio: ['ignore', 'pipe', 'pipe'],
10
+ timeout: 30_000
11
+ });
12
+ return {
13
+ ok: result.status === 0,
14
+ stderr: result.stderr?.toString() ?? '',
15
+ error: result.error?.message ?? null
16
+ };
17
+ }
18
+ export async function runClaudeCodeRefresh(args, deps = {}) {
19
+ const { spawnFn = runProbe, execFn, readFn, existsFn, writeFn, writeHostFn, discoverFn = discoverProjects, writeStdout = (chunk) => process.stdout.write(chunk), writeStderr = (chunk) => process.stderr.write(chunk) } = deps;
20
+ if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
21
+ writeStdout(`${USAGE}\n`);
22
+ return 0;
23
+ }
24
+ const { positionals } = parseArgs({ args, allowPositionals: true, strict: true });
25
+ if (positionals.length > 0) {
26
+ throw new Error(USAGE);
27
+ }
28
+ validateClaudeCredentialsEnvOverride();
29
+ const home = homedir();
30
+ if (!home) {
31
+ throw new Error('sandbox: home directory is required');
32
+ }
33
+ const projects = discoverFn(home);
34
+ if (projects.length === 0) {
35
+ writeStdout('No project credentials to refresh.\n');
36
+ return 0;
37
+ }
38
+ const reconcileOptions = { execFn, readFn, existsFn, writeFn, writeHostFn, projects };
39
+ let result = reconcileClaudeCredentials(home, reconcileOptions);
40
+ if (result.status === 'STALE_ACCESS' && result.authoritative === null) {
41
+ writeStdout('Host credentials appear stale; probing claude /status to trigger refresh...\n');
42
+ const probe = probeClaudeStatus(spawnFn);
43
+ if (!probe.ok) {
44
+ writeStderr(`Probe failed: ${redactCommandError(probe.stderr || probe.error || 'unknown error')}\n`);
45
+ writeStderr('Run "claude /login" on the host to renew credentials.\n');
46
+ return 1;
47
+ }
48
+ writeStdout('Probe succeeded; re-inspecting host credentials.\n');
49
+ result = reconcileClaudeCredentials(home, reconcileOptions);
50
+ }
51
+ if (result.status === 'MISSING') {
52
+ writeStderr('No Claude Code credentials found on host.\n');
53
+ writeStderr('Run "claude /login" on the host to authenticate.\n');
54
+ return 1;
55
+ }
56
+ if (result.status === 'KEYCHAIN_LOCKED') {
57
+ writeStderr(`${buildLockedGuidance()}\n`);
58
+ return 1;
59
+ }
60
+ if (result.status === 'KEYCHAIN_ERROR') {
61
+ writeStderr(`Host keychain error: ${redactCommandError(result.detail || 'unknown error')}\n`);
62
+ writeStderr(`${buildLockedGuidance()}\n`);
63
+ return 1;
64
+ }
65
+ if (result.status === 'KEYCHAIN_WRITE_FAILED') {
66
+ writeStderr(`[host] keychain write failed: ${formatCredentialWarnings(result.warnings) || 'unknown error'}\n`);
67
+ return 1;
68
+ }
69
+ if (result.status !== 'OK') {
70
+ writeStderr('Host credentials still invalid after probe; run "claude /login".\n');
71
+ return 1;
72
+ }
73
+ if (result.authoritative && result.authoritative !== 'host' && result.hostWritten) {
74
+ writeStdout(`[host] reconciled from ${result.authoritative}\n`);
75
+ }
76
+ for (const project of projects) {
77
+ const action = result.filesWritten.includes(project) ? 'updated' : 'unchanged';
78
+ writeStdout(`[${project}] ${action}; expires in ${formatRemaining(result.expiresAt)}\n`);
79
+ }
80
+ for (const failure of result.fileErrors) {
81
+ writeStderr(`[${failure.project}] sync failed: ${failure.error}\n`);
82
+ }
83
+ return result.fileErrors.length > 0 ? 1 : 0;
84
+ }
85
+ //# sourceMappingURL=claude-code-refresh.js.map