@fitlab-ai/agent-infra 0.6.5 → 0.7.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 (181) hide show
  1. package/README.md +51 -25
  2. package/README.zh-CN.md +49 -23
  3. package/dist/lib/defaults.json +1 -0
  4. package/dist/lib/init.js +3 -0
  5. package/dist/lib/sandbox/commands/create.js +4 -2
  6. package/dist/lib/sandbox/commands/enter.js +15 -4
  7. package/dist/lib/sandbox/commands/list-running.js +108 -0
  8. package/dist/lib/sandbox/commands/ls.js +24 -45
  9. package/dist/lib/sandbox/commands/rebuild.js +4 -2
  10. package/dist/lib/sandbox/config.js +3 -0
  11. package/dist/lib/sandbox/index.js +2 -1
  12. package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +10 -6
  13. package/dist/lib/sandbox/tools.js +213 -8
  14. package/dist/lib/update.js +12 -1
  15. package/lib/defaults.json +1 -0
  16. package/lib/init.ts +10 -0
  17. package/lib/sandbox/commands/create.ts +10 -2
  18. package/lib/sandbox/commands/enter.ts +14 -4
  19. package/lib/sandbox/commands/list-running.ts +135 -0
  20. package/lib/sandbox/commands/ls.ts +28 -49
  21. package/lib/sandbox/commands/rebuild.ts +9 -2
  22. package/lib/sandbox/config.ts +7 -0
  23. package/lib/sandbox/index.ts +2 -1
  24. package/lib/sandbox/runtimes/ai-tools.dockerfile +10 -6
  25. package/lib/sandbox/tools.ts +248 -9
  26. package/lib/update.ts +15 -1
  27. package/package.json +1 -1
  28. package/templates/.agents/QUICKSTART.en.md +1 -1
  29. package/templates/.agents/QUICKSTART.zh-CN.md +1 -1
  30. package/templates/.agents/README.en.md +79 -2
  31. package/templates/.agents/README.zh-CN.md +79 -2
  32. package/templates/.agents/rules/create-issue.en.md +1 -1
  33. package/templates/.agents/rules/create-issue.github.en.md +1 -1
  34. package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
  35. package/templates/.agents/rules/create-issue.zh-CN.md +1 -1
  36. package/templates/.agents/rules/issue-sync.github.en.md +6 -5
  37. package/templates/.agents/rules/issue-sync.github.zh-CN.md +6 -5
  38. package/templates/.agents/rules/milestone-inference.github.en.md +2 -2
  39. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +2 -2
  40. package/templates/.agents/rules/no-mid-flow-questions.en.md +57 -0
  41. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +57 -0
  42. package/templates/.agents/rules/pr-sync.github.en.md +4 -5
  43. package/templates/.agents/rules/pr-sync.github.zh-CN.md +4 -5
  44. package/templates/.agents/rules/task-management.en.md +9 -6
  45. package/templates/.agents/rules/task-management.zh-CN.md +9 -6
  46. package/templates/.agents/rules/testing-discipline.en.md +2 -2
  47. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -2
  48. package/templates/.agents/scripts/validate-artifact.js +1 -1
  49. package/templates/.agents/skills/analyze-task/SKILL.en.md +16 -4
  50. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +16 -4
  51. package/templates/.agents/skills/check-task/SKILL.en.md +43 -32
  52. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +42 -31
  53. package/templates/.agents/skills/code-task/SKILL.en.md +117 -0
  54. package/templates/.agents/skills/{implement-task → code-task}/SKILL.zh-CN.md +51 -24
  55. package/templates/.agents/skills/{implement-task → code-task}/config/verify.en.json +4 -4
  56. package/templates/.agents/skills/{implement-task → code-task}/config/verify.zh-CN.json +4 -4
  57. package/templates/.agents/skills/{implement-task → code-task}/reference/branch-management.zh-CN.md +2 -2
  58. package/templates/.agents/skills/{implement-task/reference/implementation-rules.en.md → code-task/reference/code-rules.en.md} +6 -6
  59. package/templates/.agents/skills/{implement-task/reference/implementation-rules.zh-CN.md → code-task/reference/code-rules.zh-CN.md} +3 -3
  60. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +69 -0
  61. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +69 -0
  62. package/templates/.agents/skills/{refine-task/reference/fix-workflow.en.md → code-task/reference/fix-mode.en.md} +12 -12
  63. package/templates/.agents/skills/{refine-task/reference/fix-workflow.zh-CN.md → code-task/reference/fix-mode.zh-CN.md} +8 -8
  64. package/templates/.agents/skills/code-task/reference/output-template.en.md +20 -0
  65. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +20 -0
  66. package/templates/.agents/skills/{implement-task → code-task}/reference/report-template.en.md +4 -4
  67. package/templates/.agents/skills/{implement-task → code-task}/reference/report-template.zh-CN.md +3 -3
  68. package/templates/.agents/skills/code-task/scripts/detect-mode.js +370 -0
  69. package/templates/.agents/skills/commit/SKILL.en.md +2 -2
  70. package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
  71. package/templates/.agents/skills/commit/reference/task-status-update.en.md +10 -6
  72. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +10 -6
  73. package/templates/.agents/skills/complete-task/SKILL.en.md +5 -3
  74. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +5 -3
  75. package/templates/.agents/skills/create-pr/SKILL.en.md +17 -1
  76. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +17 -1
  77. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  78. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  79. package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
  80. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
  81. package/templates/.agents/skills/import-issue/SKILL.en.md +3 -3
  82. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +3 -3
  83. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -4
  84. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -4
  85. package/templates/.agents/skills/restore-task/SKILL.en.md +4 -3
  86. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -3
  87. package/templates/.agents/skills/review-analysis/SKILL.en.md +76 -0
  88. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +102 -0
  89. package/templates/.agents/skills/review-analysis/config/verify.en.json +51 -0
  90. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +51 -0
  91. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +87 -0
  92. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +87 -0
  93. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +90 -0
  94. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +91 -0
  95. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +47 -0
  96. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +47 -0
  97. package/templates/.agents/skills/{review-task → review-code}/SKILL.en.md +11 -9
  98. package/templates/.agents/skills/{review-task → review-code}/SKILL.zh-CN.md +15 -9
  99. package/templates/.agents/skills/{review-task → review-code}/config/verify.en.json +7 -5
  100. package/templates/.agents/skills/{review-task → review-code}/config/verify.zh-CN.json +6 -4
  101. package/templates/.agents/skills/{review-task → review-code}/reference/output-templates.en.md +21 -17
  102. package/templates/.agents/skills/{review-task → review-code}/reference/output-templates.zh-CN.md +19 -15
  103. package/templates/.agents/skills/{review-task → review-code}/reference/report-template.en.md +5 -6
  104. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +91 -0
  105. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +48 -0
  106. package/templates/.agents/skills/{review-task → review-code}/reference/review-criteria.zh-CN.md +10 -4
  107. package/templates/.agents/skills/review-plan/SKILL.en.md +76 -0
  108. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +102 -0
  109. package/templates/.agents/skills/{refine-task → review-plan}/config/verify.en.json +14 -10
  110. package/templates/.agents/skills/{refine-task → review-plan}/config/verify.zh-CN.json +14 -10
  111. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +87 -0
  112. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +87 -0
  113. package/templates/.agents/skills/review-plan/reference/report-template.en.md +90 -0
  114. package/templates/.agents/skills/{review-task → review-plan}/reference/report-template.zh-CN.md +3 -3
  115. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +47 -0
  116. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +47 -0
  117. package/templates/.agents/skills/test/SKILL.en.md +2 -2
  118. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -31
  119. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +1 -0
  120. package/templates/.agents/templates/task.en.md +3 -3
  121. package/templates/.agents/templates/task.zh-CN.md +2 -2
  122. package/templates/.agents/workflows/bug-fix.en.yaml +126 -80
  123. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +90 -44
  124. package/templates/.agents/workflows/feature-development.en.yaml +115 -70
  125. package/templates/.agents/workflows/feature-development.zh-CN.yaml +92 -47
  126. package/templates/.agents/workflows/refactoring.en.yaml +123 -78
  127. package/templates/.agents/workflows/refactoring.zh-CN.yaml +89 -44
  128. package/templates/.claude/commands/code-task.en.md +8 -0
  129. package/templates/.claude/commands/code-task.zh-CN.md +8 -0
  130. package/templates/.claude/commands/review-analysis.en.md +8 -0
  131. package/templates/.claude/commands/review-analysis.zh-CN.md +8 -0
  132. package/templates/.claude/commands/review-code.en.md +8 -0
  133. package/templates/.claude/commands/review-code.zh-CN.md +8 -0
  134. package/templates/.claude/commands/review-plan.en.md +8 -0
  135. package/templates/.claude/commands/review-plan.zh-CN.md +8 -0
  136. package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +1 -1
  137. package/templates/.gemini/commands/_project_/code-task.en.toml +8 -0
  138. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +8 -0
  139. package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +1 -1
  140. package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +1 -1
  141. package/templates/.gemini/commands/_project_/review-analysis.en.toml +8 -0
  142. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +8 -0
  143. package/templates/.gemini/commands/_project_/review-code.en.toml +8 -0
  144. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +8 -0
  145. package/templates/.gemini/commands/_project_/review-plan.en.toml +8 -0
  146. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +8 -0
  147. package/templates/.opencode/commands/code-task.en.md +11 -0
  148. package/templates/.opencode/commands/code-task.zh-CN.md +11 -0
  149. package/templates/.opencode/commands/review-analysis.en.md +11 -0
  150. package/templates/.opencode/commands/review-analysis.zh-CN.md +11 -0
  151. package/templates/.opencode/commands/review-code.en.md +11 -0
  152. package/templates/.opencode/commands/review-code.zh-CN.md +11 -0
  153. package/templates/.opencode/commands/review-plan.en.md +11 -0
  154. package/templates/.opencode/commands/review-plan.zh-CN.md +11 -0
  155. package/templates/.agents/skills/implement-task/SKILL.en.md +0 -173
  156. package/templates/.agents/skills/implement-task/reference/output-template.en.md +0 -20
  157. package/templates/.agents/skills/implement-task/reference/output-template.zh-CN.md +0 -20
  158. package/templates/.agents/skills/refine-task/SKILL.en.md +0 -153
  159. package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +0 -153
  160. package/templates/.agents/skills/refine-task/reference/report-template.en.md +0 -64
  161. package/templates/.agents/skills/refine-task/reference/report-template.zh-CN.md +0 -64
  162. package/templates/.agents/skills/review-task/reference/review-criteria.en.md +0 -42
  163. package/templates/.claude/commands/implement-task.en.md +0 -8
  164. package/templates/.claude/commands/implement-task.zh-CN.md +0 -8
  165. package/templates/.claude/commands/refine-task.en.md +0 -8
  166. package/templates/.claude/commands/refine-task.zh-CN.md +0 -8
  167. package/templates/.claude/commands/review-task.en.md +0 -8
  168. package/templates/.claude/commands/review-task.zh-CN.md +0 -8
  169. package/templates/.gemini/commands/_project_/implement-task.en.toml +0 -8
  170. package/templates/.gemini/commands/_project_/implement-task.zh-CN.toml +0 -8
  171. package/templates/.gemini/commands/_project_/refine-task.en.toml +0 -8
  172. package/templates/.gemini/commands/_project_/refine-task.zh-CN.toml +0 -8
  173. package/templates/.gemini/commands/_project_/review-task.en.toml +0 -8
  174. package/templates/.gemini/commands/_project_/review-task.zh-CN.toml +0 -8
  175. package/templates/.opencode/commands/implement-task.en.md +0 -11
  176. package/templates/.opencode/commands/implement-task.zh-CN.md +0 -11
  177. package/templates/.opencode/commands/refine-task.en.md +0 -11
  178. package/templates/.opencode/commands/refine-task.zh-CN.md +0 -11
  179. package/templates/.opencode/commands/review-task.en.md +0 -11
  180. package/templates/.opencode/commands/review-task.zh-CN.md +0 -11
  181. /package/templates/.agents/skills/{implement-task → code-task}/reference/branch-management.en.md +0 -0
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <strong>From issue to merged PR in 9 commands.</strong> Define a requirement, let AI handle analysis, planning, coding, review, and delivery — you only step in when it matters.
12
+ <strong>From issue to merged PR in 11 commands.</strong> Define a requirement, let AI handle analysis, planning, coding, and three-stage review — you only step in when it matters.
13
13
  </p>
14
14
 
15
15
  <p align="center">
@@ -66,11 +66,23 @@ Once initialized, open the project in your AI TUI and install the latest skills:
66
66
 
67
67
  > AI scans the codebase, identifies `src/auth/login.ts` as the root cause, and writes `analysis.md`.
68
68
 
69
+ ```bash
70
+ /review-analysis TASK-20260319-100000
71
+ ```
72
+
73
+ > AI self-reviews the analysis: *"Approved. 0 blockers, 0 major, 0 minor — scope and root cause are clear, proceed to design."*
74
+
69
75
  ```bash
70
76
  /plan-task TASK-20260319-100000
71
77
  ```
72
78
 
73
79
  > AI proposes a fix plan: *"Sanitize the email input in `LoginService.validate()` and add a dedicated unit test."*
80
+
81
+ ```bash
82
+ /review-plan TASK-20260319-100000
83
+ ```
84
+
85
+ > AI self-reviews the plan: *"Approved with no findings. Ready for implementation."*
74
86
  >
75
87
  > **You review the plan and reply in natural language:**
76
88
 
@@ -79,22 +91,22 @@ The plan looks right, but don't change the DB schema.
79
91
  Just fix it at the application layer in LoginService.
80
92
  ```
81
93
 
82
- > AI updates the plan accordingly and confirms.
94
+ > AI re-runs `/plan-task` to update the plan accordingly and confirms.
83
95
 
84
96
  ```bash
85
- /implement-task TASK-20260319-100000
97
+ /code-task TASK-20260319-100000
86
98
  ```
87
99
 
88
100
  > AI writes the fix, adds a test case for `user+tag@example.com`, and runs all tests — green.
89
101
 
90
102
  ```bash
91
- /review-task TASK-20260319-100000
103
+ /review-code TASK-20260319-100000
92
104
  ```
93
105
 
94
- > AI reviews its own implementation: *"Approved. 0 blockers, 0 major, 1 minor (missing JSDoc)."*
106
+ > AI reviews its own code: *"Approved. 0 blockers, 0 major, 1 minor (missing JSDoc)."*
95
107
 
96
108
  ```bash
97
- /refine-task TASK-20260319-100000
109
+ /code-task TASK-20260319-100000
98
110
  ```
99
111
 
100
112
  > AI fixes the minor issue and re-validates.
@@ -107,7 +119,7 @@ Just fix it at the application layer in LoginService.
107
119
 
108
120
  > Commit created, PR #43 opened (auto-linked to issue #42), task archived.
109
121
 
110
- **9 commands. 1 natural-language correction. From issue to merged PR.** That is the entire SOP — programming can have a standard operating procedure too.
122
+ **11 commands. 1 natural-language correction. From issue to merged PR.** That is the entire SOP — programming can have a standard operating procedure too.
111
123
 
112
124
  Every command above works the same way in Claude Code, Codex, Gemini CLI, and OpenCode. Switch tools mid-task — the workflow state follows.
113
125
 
@@ -115,9 +127,9 @@ Every command above works the same way in Claude Code, Codex, Gemini CLI, and Op
115
127
 
116
128
  These are not thin command aliases. Each skill encapsulates standardized processes that are tedious and error-prone when done by hand:
117
129
 
118
- - **Structured artifacts** — every step produces a templated document (`analysis.md`, `plan.md`, `review.md`) with consistent structure, not free-form notes
130
+ - **Structured artifacts** — every step produces a templated document (`analysis.md`, `review-analysis.md`, `plan.md`, `review-plan.md`, `code.md`, `review-code.md`) with consistent structure, not free-form notes
119
131
  - **Multi-round versioning** — requirements changed? Run `analyze-task` again to get `analysis-r2.md`; the full revision history is preserved
120
- - **Severity-classified reviews** — `review-task` categorizes findings into Blocker / Major / Minor with file paths and fix suggestions, not a vague "looks good"
132
+ - **Severity-classified reviews** — `review-code` categorizes findings into Blocker / Major / Minor with file paths and fix suggestions, not a vague "looks good"
121
133
  - **Cross-tool state continuity** — `task.md` records who did what and when; Claude can analyze, Codex can implement, Gemini can review — context transfers seamlessly
122
134
  - **Audit trail and co-authorship** — every step appends to the Activity Log; the final commit includes `Co-Authored-By` lines for all participating AI agents
123
135
 
@@ -327,7 +339,7 @@ agent-infra is intentionally simple: a bootstrap CLI creates the seed configurat
327
339
  1. **Install** — `npm install -g @fitlab-ai/agent-infra` (or `brew install fitlab-ai/tap/agent-infra` on macOS, or use the shell script wrapper)
328
340
  2. **Initialize** — `ai init` in the project root to generate `.agents/.airc.json` and install the seed command
329
341
  3. **Render** — run `update-agent-infra` in any AI TUI to detect the bundled template version and generate all managed files
330
- 4. **Develop** — use built-in skills to drive the full lifecycle: `analysis → designimplementation → review → fix → commit`
342
+ 4. **Develop** — use built-in skills to drive the full lifecycle: `analysis → analysis-reviewdesigndesign-review → codecode-review → commit`
331
343
  5. **Update** — run `update-agent-infra` again whenever a new template version is available
332
344
 
333
345
  ### Layered Architecture
@@ -524,10 +536,11 @@ agent-infra ships with **a rich set of built-in AI skills**. They are organized
524
536
  | `create-task` | Create a task scaffold from a natural-language request and cascade Issue creation through the platform rule when available. | `description` | Start a new feature, bug-fix, or improvement from scratch. |
525
537
  | `import-issue` | Import a GitHub Issue into the local task workspace. | `issue-number` | Convert an existing Issue into an actionable task folder. |
526
538
  | `analyze-task` | Produce a requirement analysis artifact for an existing task. | `task-id` | Capture scope, risks, and impacted files before designing. |
527
- | `plan-task` | Write the technical implementation plan with a review checkpoint. | `task-id` | Define the approach after analysis is complete. |
528
- | `implement-task` | Implement the approved plan and produce an implementation report. | `task-id` | Write code, tests, and docs after plan approval. |
529
- | `review-task` | Review the implementation and classify findings by severity. | `task-id` | Run a structured code review before merging. |
530
- | `refine-task` | Fix review findings in priority order without expanding scope. | `task-id` | Address review feedback and re-validate the task. |
539
+ | `review-analysis` | Review the requirement analysis and classify findings by severity. | `task-id` | Confirm the analysis is complete before design. |
540
+ | `plan-task` | Write the technical plan with a review checkpoint. | `task-id` | Define the approach after analysis approval. |
541
+ | `review-plan` | Review the technical plan and classify findings by severity. | `task-id` | Confirm the design is actionable before coding. |
542
+ | `code-task` | Implement the approved plan or fix code review findings, producing a code report. | `task-id` | Write code, tests, and docs after plan approval, or handle review feedback. |
543
+ | `review-code` | Review the code and classify findings by severity. | `task-id` | Run a structured code review before merging. |
531
544
  | `complete-task` | Mark the task complete and archive it after all gates pass. | `task-id` | Close out a task after review, tests, and commit are done. |
532
545
 
533
546
  <a id="task-status"></a>
@@ -694,8 +707,8 @@ Supported `invoke` placeholders:
694
707
 
695
708
  | Placeholder | Replaced with | Example |
696
709
  |-------------|---------------|---------|
697
- | `${skillName}` | The skill command name, such as `review-task` or `commit`. | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
698
- | `${projectName}` | The `.airc.json` `project` value. Use this for namespaced commands. | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
710
+ | `${skillName}` | The skill command name, such as `review-code` or `commit`. | `<your-cli> ${skillName}` -> `<your-cli> review-code` |
711
+ | `${projectName}` | The `.airc.json` `project` value. Use this for namespaced commands. | `/${projectName}:${skillName}` -> `/agent-infra:review-code` |
699
712
 
700
713
  Non-namespaced custom TUI:
701
714
 
@@ -732,17 +745,17 @@ Namespaced custom TUI:
732
745
 
733
746
  ## Prebuilt Workflows
734
747
 
735
- agent-infra includes **4 prebuilt workflows**. Three of them share the same gated delivery lifecycle:
748
+ agent-infra includes **4 prebuilt workflows**. Three of them share the same symmetric gated delivery lifecycle:
736
749
 
737
- `analysis -> design -> implementation -> review -> fix -> commit`
750
+ `analysis -> analysis-review -> design -> design-review -> code -> code-review -> commit`
738
751
 
739
752
  The fourth, `code-review`, is intentionally smaller and optimized for reviewing an existing PR or branch.
740
753
 
741
754
  | Workflow | Best for | Step chain |
742
755
  |----------|----------|------------|
743
- | `feature-development` | Building a new feature or capability | `analysis -> design -> implementation -> review -> fix -> commit` |
744
- | `bug-fix` | Diagnosing and fixing a defect with regression coverage | `analysis -> design -> implementation -> review -> fix -> commit` |
745
- | `refactoring` | Structural changes that should preserve behavior | `analysis -> design -> implementation -> review -> fix -> commit` |
756
+ | `feature-development` | Building a new feature or capability | `analysis -> analysis-review -> design -> design-review -> code -> code-review -> commit` |
757
+ | `bug-fix` | Diagnosing and fixing a defect with regression coverage | `analysis -> analysis-review -> design -> design-review -> code -> code-review -> commit` |
758
+ | `refactoring` | Structural changes that should preserve behavior | `analysis -> analysis-review -> design -> design-review -> code -> code-review -> commit` |
746
759
  | `code-review` | Reviewing an existing PR or branch | `analysis -> review -> report` |
747
760
 
748
761
  ### Example lifecycle
@@ -758,20 +771,33 @@ import-issue #42 Import task from GitHub Issue
758
771
  analyze-task T1 Requirement analysis
759
772
  |
760
773
  v
761
- plan-task T1 Design solution <-- human review
774
+ review-analysis T1 Review analysis
775
+ |
776
+ Issues?
777
+ +--YES----> analyze-task T1
778
+ |
779
+ v
780
+ plan-task T1 Design solution
781
+ |
782
+ v
783
+ review-plan T1 Review plan
784
+ |
785
+ Issues?
786
+ +--YES----> plan-task T1
787
+ |
762
788
  |
763
789
  v
764
- implement-task T1 Write code and tests
790
+ code-task T1 Write code and tests
765
791
  |
766
792
  v
767
- +-> review-task T1 Automated code review
793
+ +-> review-code T1 Automated code review
768
794
  | |
769
795
  | Issues?
770
796
  | +--NO-------+
771
797
  | YES |
772
798
  | | |
773
799
  | v |
774
- | refine-task T1 |
800
+ | code-task T1 (fix mode)
775
801
  | | |
776
802
  +------+ |
777
803
  |
package/README.zh-CN.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <strong>从 Issue 到合并 PR,只需 9 条命令。</strong> 定义需求,让 AI 完成分析、方案设计、编码、审查和交付 —— 你只需在关键节点介入。
12
+ <strong>从 Issue 到合并 PR,只需 11 条命令。</strong> 定义需求,让 AI 完成分析、方案设计、编码与三阶段审查 —— 你只需在关键节点介入。
13
13
  </p>
14
14
 
15
15
  <p align="center">
@@ -66,11 +66,23 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
66
66
 
67
67
  > AI 扫描代码库,定位 `src/auth/login.ts` 为根因,输出 `analysis.md`。
68
68
 
69
+ ```bash
70
+ /review-analysis TASK-20260319-100000
71
+ ```
72
+
73
+ > AI 自审分析报告:*"通过。0 阻塞项,0 主要,0 次要 —— 范围与根因清晰,可进入方案设计。"*
74
+
69
75
  ```bash
70
76
  /plan-task TASK-20260319-100000
71
77
  ```
72
78
 
73
79
  > AI 提出修复方案:*"在 `LoginService.validate()` 中清洗邮箱输入,并添加专项单元测试。"*
80
+
81
+ ```bash
82
+ /review-plan TASK-20260319-100000
83
+ ```
84
+
85
+ > AI 自审方案:*"通过且无问题。可进入编码。"*
74
86
  >
75
87
  > **你审查方案后用自然语言回复:**
76
88
 
@@ -79,22 +91,22 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
79
91
  只在应用层的 LoginService 里修复就行。
80
92
  ```
81
93
 
82
- > AI 按你的要求更新方案并确认。
94
+ > AI 按你的要求重跑 `/plan-task` 更新方案并确认。
83
95
 
84
96
  ```bash
85
- /implement-task TASK-20260319-100000
97
+ /code-task TASK-20260319-100000
86
98
  ```
87
99
 
88
100
  > AI 编写修复代码,添加 `user+tag@example.com` 的测试用例,运行全部测试 —— 通过。
89
101
 
90
102
  ```bash
91
- /review-task TASK-20260319-100000
103
+ /review-code TASK-20260319-100000
92
104
  ```
93
105
 
94
106
  > AI 审查自己的实现:*"通过。0 阻塞项,0 主要问题,1 次要问题(缺少 JSDoc)。"*
95
107
 
96
108
  ```bash
97
- /refine-task TASK-20260319-100000
109
+ /code-task TASK-20260319-100000
98
110
  ```
99
111
 
100
112
  > AI 修复次要问题并重新验证。
@@ -107,7 +119,7 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
107
119
 
108
120
  > 提交完成,PR #43 已创建(自动关联 Issue #42),任务归档。
109
121
 
110
- **9 条命令,1 次自然语言纠正,从 Issue 到合并 PR。** 这就是完整的 SOP —— 编程也可以有标准作业流程。
122
+ **11 条命令,1 次自然语言纠正,从 Issue 到合并 PR。** 这就是完整的 SOP —— 编程也可以有标准作业流程。
111
123
 
112
124
  以上每条命令在 Claude Code、Codex、Gemini CLI、OpenCode 中完全通用。任务进行到一半切换工具,工作流状态照常延续。
113
125
 
@@ -115,9 +127,9 @@ agent-infra 的目标就是把这层共享基础设施标准化。它为所有
115
127
 
116
128
  这些不是简单的命令别名。每个 skill 都封装了手动操作时容易遗漏或不一致的标准化流程:
117
129
 
118
- - **结构化产物** — 每个步骤都输出模板化的文档(`analysis.md`、`plan.md`、`review.md`),格式统一,而非自由发挥的散文
130
+ - **结构化产物** — 每个步骤都输出模板化的文档(`analysis.md`、`review-analysis.md`、`plan.md`、`review-plan.md`、`code.md`、`review-code.md`),格式统一,而非自由发挥的散文
119
131
  - **多轮版本化** — 需求变了?再执行一次 `analyze-task` 会生成 `analysis-r2.md`,完整修订历史自动保留
120
- - **分级审查机制** — `review-task` 按 Blocker / Major / Minor 分类问题,附带文件路径和修复建议,而非含糊的"看着没问题"
132
+ - **分级审查机制** — `review-code` 按 Blocker / Major / Minor 分类问题,附带文件路径和修复建议,而非含糊的"看着没问题"
121
133
  - **跨工具状态延续** — `task.md` 记录了谁在什么时间做了什么;Claude 分析、Codex 实现、Gemini 审查——上下文无缝衔接
122
134
  - **审计轨迹与联合署名** — 每个步骤自动追加 Activity Log;最终提交包含所有参与 AI 的 `Co-Authored-By` 署名
123
135
 
@@ -300,7 +312,7 @@ agent-infra 的结构刻意保持简单:引导 CLI 负责生成种子配置,
300
312
  1. **安装** — `npm install -g @fitlab-ai/agent-infra`(或在 macOS 上使用 `brew install fitlab-ai/tap/agent-infra`,或使用 shell 脚本便捷封装)
301
313
  2. **初始化** — 在项目根目录运行 `ai init`,生成 `.agents/.airc.json` 并安装种子命令
302
314
  3. **渲染** — 在任意 AI TUI 中执行 `update-agent-infra`,检测当前打包模板版本并生成所有受管理文件
303
- 4. **开发** — 使用内置 skill 驱动完整生命周期:`analysis → designimplementation → review → fix → commit`
315
+ 4. **开发** — 使用内置 skill 驱动完整生命周期:`analysis → analysis-reviewdesigndesign-review → codecode-review → commit`
304
316
  5. **升级** — 有新模板版本时再次执行 `update-agent-infra` 即可
305
317
 
306
318
  ### 分层架构
@@ -497,10 +509,11 @@ agent-infra 提供 **丰富的内置 AI skills**。它们按使用场景分组
497
509
  | `create-task` | 根据自然语言请求创建任务骨架,并在平台规则可用时级联创建 Issue。 | `description` | 从零开始记录新功能、缺陷或改进需求。 |
498
510
  | `import-issue` | 将 GitHub Issue 导入本地任务工作区。 | `issue-number` | 把已有 Issue 转成可执行的任务目录。 |
499
511
  | `analyze-task` | 为已有任务输出需求分析产物。 | `task-id` | 在设计前明确范围、风险和受影响文件。 |
500
- | `plan-task` | 编写技术实施方案,并设置人工审查检查点。 | `task-id` | 分析完成后定义具体实现路径。 |
501
- | `implement-task` | 按批准方案实施并生成实现报告。 | `task-id` | 在方案获批后编写代码、测试和文档。 |
502
- | `review-task` | 审查实现结果,并按严重程度分类问题。 | `task-id` | 合入前执行结构化代码审查。 |
503
- | `refine-task` | 按优先级修复审查问题,不额外扩张范围。 | `task-id` | 根据 review 反馈完成修正。 |
512
+ | `review-analysis` | 审查需求分析产物,并按严重程度分类问题。 | `task-id` | 在设计前确认分析完整可用。 |
513
+ | `plan-task` | 编写技术实施方案,并设置审查检查点。 | `task-id` | 分析获批后定义具体实现路径。 |
514
+ | `review-plan` | 审查技术方案,并按严重程度分类问题。 | `task-id` | 在编码前确认方案可执行。 |
515
+ | `code-task` | 按批准方案实施,或修复代码审查问题,并生成实现报告。 | `task-id` | 在方案获批后编写代码、测试和文档,或处理 review 反馈。 |
516
+ | `review-code` | 审查实现结果,并按严重程度分类问题。 | `task-id` | 合入前执行结构化代码审查。 |
504
517
  | `complete-task` | 在所有关卡通过后标记任务完成并归档。 | `task-id` | 测试、审查和提交都完成后收尾。 |
505
518
 
506
519
  <a id="task-status"></a>
@@ -667,8 +680,8 @@ args: "<task-id>" # 可选
667
680
 
668
681
  | 占位符 | 替换为 | 示例 |
669
682
  |--------|--------|------|
670
- | `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
671
- | `${projectName}` | `.airc.json` 中的 `project` 值,适用于带命名空间的命令。 | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
683
+ | `${skillName}` | skill 命令名,例如 `review-code` 或 `commit`。 | `<your-cli> ${skillName}` -> `<your-cli> review-code` |
684
+ | `${projectName}` | `.airc.json` 中的 `project` 值,适用于带命名空间的命令。 | `/${projectName}:${skillName}` -> `/agent-infra:review-code` |
672
685
 
673
686
  不带命名空间的自定义 TUI:
674
687
 
@@ -707,15 +720,15 @@ args: "<task-id>" # 可选
707
720
 
708
721
  agent-infra 内置 **4 个预置工作流**。其中 3 个共享同一条分阶段交付链路:
709
722
 
710
- `analysis -> design -> implementation -> review -> fix -> commit`
723
+ `analysis -> analysis-review -> design -> design-review -> code -> code-review -> commit`
711
724
 
712
725
  第 4 个 `code-review` 则更轻量,专门用于审查已有 PR 或分支。
713
726
 
714
727
  | Workflow | 适用场景 | 步骤链 |
715
728
  |----------|----------|--------|
716
- | `feature-development` | 开发新功能或新能力 | `analysis -> design -> implementation -> review -> fix -> commit` |
717
- | `bug-fix` | 诊断并修复缺陷,同时补回归验证 | `analysis -> design -> implementation -> review -> fix -> commit` |
718
- | `refactoring` | 进行应保持行为稳定的结构性重构 | `analysis -> design -> implementation -> review -> fix -> commit` |
729
+ | `feature-development` | 开发新功能或新能力 | `analysis -> analysis-review -> design -> design-review -> code -> code-review -> commit` |
730
+ | `bug-fix` | 诊断并修复缺陷,同时补回归验证 | `analysis -> analysis-review -> design -> design-review -> code -> code-review -> commit` |
731
+ | `refactoring` | 进行应保持行为稳定的结构性重构 | `analysis -> analysis-review -> design -> design-review -> code -> code-review -> commit` |
719
732
  | `code-review` | 审查已有 Pull Request 或分支 | `analysis -> review -> report` |
720
733
 
721
734
  ### 生命周期示例
@@ -731,20 +744,33 @@ import-issue #42 从 GitHub Issue 导入任务
731
744
  analyze-task T1 需求分析
732
745
  |
733
746
  v
734
- plan-task T1 设计方案 <-- 人工审查
747
+ review-analysis T1 审查需求分析
748
+ |
749
+ 有问题?
750
+ +--YES----> analyze-task T1
751
+ |
752
+ v
753
+ plan-task T1 设计方案
754
+ |
755
+ v
756
+ review-plan T1 审查技术方案
757
+ |
758
+ 有问题?
759
+ +--YES----> plan-task T1
760
+ |
735
761
  |
736
762
  v
737
- implement-task T1 编写代码与测试
763
+ code-task T1 编写代码与测试
738
764
  |
739
765
  v
740
- +-> review-task T1 自动代码审查
766
+ +-> review-code T1 自动代码审查
741
767
  | |
742
768
  | 有问题?
743
769
  | +--NO-------+
744
770
  | YES |
745
771
  | | |
746
772
  | v |
747
- | refine-task T1 |
773
+ | code-task T1 (fix mode)
748
774
  | | |
749
775
  +------+ |
750
776
  |
@@ -2,6 +2,7 @@
2
2
  "platform": {
3
3
  "type": "github"
4
4
  },
5
+ "requiresPullRequest": true,
5
6
  "sandbox": {
6
7
  "engine": null,
7
8
  "runtimes": [
package/dist/lib/init.js CHANGED
@@ -156,6 +156,8 @@ async function cmdInit() {
156
156
  info(`Custom platform '${platformType}' selected. Built-in templates are only complete for github;`
157
157
  + ` provide matching '.${platformType}.' or generic templates before running update-agent-infra.`);
158
158
  }
159
+ const requiresPRChoice = await select('Require Pull Request flow?', ['yes', 'no'], 'yes');
160
+ const requiresPullRequest = requiresPRChoice !== 'no';
159
161
  const templateSources = parseLocalSources(await prompt('Template sources (optional, comma-separated local paths, e.g. ~/my-templates; Enter to skip)', ''));
160
162
  const skillSources = parseLocalSources(await prompt('Skill sources (optional, comma-separated local paths, e.g. ~/my-skills; Enter to skip)', ''));
161
163
  closePrompt();
@@ -199,6 +201,7 @@ async function cmdInit() {
199
201
  org: orgName,
200
202
  language,
201
203
  platform: { type: platformType },
204
+ requiresPullRequest,
202
205
  templateVersion: VERSION,
203
206
  sandbox: structuredClone(defaults.sandbox),
204
207
  labels: structuredClone(defaults.labels),
@@ -13,7 +13,7 @@ import { prepareDockerfile } from "../dockerfile.js";
13
13
  import { detectEngine, ensureDocker } from "../engine.js";
14
14
  import { commandForEngine, execEngine, run, runEngine, runOk, runOkEngine, runSafe, runSafeEngine, runVerboseEngine } from "../shell.js";
15
15
  import { resolveTaskBranch } from "../task-resolver.js";
16
- import { resolveTools, toolConfigDirCandidates, toolNpmPackagesArg } from "../tools.js";
16
+ import { imageSignatureFields, resolveTools, toolConfigDirCandidates, toolNpmPackagesArg, toolShellInstallScriptBase64 } from "../tools.js";
17
17
  import { hostJoin, toEnginePath, volumeArg } from "../engines/wsl2-paths.js";
18
18
  import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from "../clipboard/paths.js";
19
19
  import { validateSelinuxDisableEnv } from "../engines/selinux.js";
@@ -58,7 +58,7 @@ function buildSignature(preparedDockerfile, tools) {
58
58
  return createHash('sha256')
59
59
  .update(JSON.stringify({
60
60
  dockerfile: preparedDockerfile.signature,
61
- tools: tools.map((tool) => tool.npmPackage)
61
+ tools: imageSignatureFields(tools)
62
62
  }))
63
63
  .digest('hex')
64
64
  .slice(0, 12);
@@ -789,6 +789,8 @@ export function buildImage(config, tools, dockerfilePath, imageSignature, { engi
789
789
  `HOST_GID=${hostGid}`,
790
790
  '--build-arg',
791
791
  `AI_TOOL_PACKAGES=${toolNpmPackagesArg(tools)}`,
792
+ '--build-arg',
793
+ `AI_TOOLS_SHELL_INSTALL_B64=${toolShellInstallScriptBase64(tools)}`,
792
794
  '--label',
793
795
  sandboxLabel(config),
794
796
  '--label',
@@ -1,5 +1,5 @@
1
1
  import { loadConfig } from "../config.js";
2
- import { assertValidBranchName, containerNameCandidates } from "../constants.js";
2
+ import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel } from "../constants.js";
3
3
  import { detectEngine } from "../engine.js";
4
4
  import { formatCredentialWarnings, formatRemaining, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from "../credentials.js";
5
5
  import { runInteractiveEngine, runSafeEngine } from "../shell.js";
@@ -7,7 +7,11 @@ import { resolveTaskBranch } from "../task-resolver.js";
7
7
  import { dotfilesCacheDir, materializeDotfiles } from "../dotfiles.js";
8
8
  import { runInteractiveWithClipboardBridge } from "../clipboard/bridge.js";
9
9
  import { detectHostTimezone } from "../host-timezone.js";
10
- const USAGE = `Usage: ai sandbox exec <branch> [cmd...]`;
10
+ import { fetchSandboxRows, isTaskShortRef, resolveTaskShortRef } from "./list-running.js";
11
+ const USAGE = `Usage: ai sandbox exec <branch | TASK-id | '#N'> [cmd...]
12
+
13
+ '#N' references the N-th running sandbox in 'ai sandbox ls' order (1-based).
14
+ Quote it as '#N' to avoid shell '#' comment handling.`;
11
15
  const TMUX_ENTRY_PATH = '/usr/local/bin/sandbox-tmux-entry';
12
16
  // Terminal-detection variables that interactive TUIs (e.g. claude-code)
13
17
  // inspect to enable progressive enhancements such as the kitty keyboard
@@ -79,8 +83,15 @@ export async function enter(args) {
79
83
  const config = loadConfig();
80
84
  validateClaudeCredentialsEnvOverride();
81
85
  const engine = detectEngine(config);
82
- const [branchOrTaskId = '', ...cmd] = args;
83
- const branch = resolveTaskBranch(branchOrTaskId, config.repoRoot);
86
+ const [firstArg = '', ...cmd] = args;
87
+ let branch;
88
+ if (isTaskShortRef(firstArg)) {
89
+ const { running } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config));
90
+ branch = resolveTaskShortRef(firstArg, { running });
91
+ }
92
+ else {
93
+ branch = resolveTaskBranch(firstArg, config.repoRoot);
94
+ }
84
95
  assertValidBranchName(branch);
85
96
  const running = runSafeEngine(engine, 'docker', ['ps', '--format', '{{.Names}}']).split('\n');
86
97
  const container = containerNameCandidates(config, branch).find((name) => running.includes(name));
@@ -0,0 +1,108 @@
1
+ import { runSafeEngine } from "../shell.js";
2
+ export function containerListFormat() {
3
+ return '{{.Names}}\t{{.Status}}\t{{.Labels}}';
4
+ }
5
+ export function parseLabels(csv) {
6
+ if (!csv) {
7
+ return {};
8
+ }
9
+ const labels = {};
10
+ for (const pair of csv.split(',')) {
11
+ if (!pair) {
12
+ continue;
13
+ }
14
+ const eq = pair.indexOf('=');
15
+ if (eq < 0) {
16
+ continue;
17
+ }
18
+ labels[pair.slice(0, eq)] = pair.slice(eq + 1);
19
+ }
20
+ return labels;
21
+ }
22
+ export function parseSandboxRows(rawOutput, branchKey) {
23
+ if (!rawOutput) {
24
+ return [];
25
+ }
26
+ return rawOutput.split('\n').map((line) => {
27
+ const [name = '', status = '', labelsCsv = ''] = line.split('\t');
28
+ const branch = parseLabels(labelsCsv)[branchKey] ?? '';
29
+ return {
30
+ name,
31
+ status,
32
+ branch,
33
+ running: status.startsWith('Up '),
34
+ index: null
35
+ };
36
+ });
37
+ }
38
+ export function sortAndIndexSandboxRows(rows) {
39
+ const byName = (a, b) => {
40
+ if (a.name < b.name)
41
+ return -1;
42
+ if (a.name > b.name)
43
+ return 1;
44
+ return 0;
45
+ };
46
+ const running = rows.filter((row) => row.running).sort(byName).map((row, i) => ({
47
+ ...row,
48
+ index: i + 1
49
+ }));
50
+ const nonRunning = rows.filter((row) => !row.running).sort(byName).map((row) => ({
51
+ ...row,
52
+ index: null
53
+ }));
54
+ return { running, nonRunning };
55
+ }
56
+ export function fetchSandboxRows(engine, label, branchKey) {
57
+ const raw = runSafeEngine(engine, 'docker', [
58
+ 'ps',
59
+ '-a',
60
+ '--filter',
61
+ `label=${label}`,
62
+ '--format',
63
+ containerListFormat()
64
+ ]);
65
+ return sortAndIndexSandboxRows(parseSandboxRows(raw, branchKey));
66
+ }
67
+ /**
68
+ * Returns true iff `arg` is a syntactically valid task short reference ('#N').
69
+ * Zero IO. Callers MUST use this as the gate before constructing any context
70
+ * for resolveTaskShortRef — that way non-matching arguments (e.g. '#abc',
71
+ * '#1.5', '#') never trigger sandbox list IO.
72
+ */
73
+ export function isTaskShortRef(arg) {
74
+ return /^#\d+$/.test(arg);
75
+ }
76
+ /**
77
+ * Resolve a task short reference ('#N') to a branch name.
78
+ *
79
+ * Current implementation: treats the digits as a 1-based index into the
80
+ * supplied running-sandbox list (ls view order). This is the *only*
81
+ * resolution path until the global task-short-id registry lands in a
82
+ * follow-up task; do NOT read task.md or scan .agents/workspace/ from this
83
+ * helper here.
84
+ *
85
+ * Precondition: callers MUST gate on isTaskShortRef(arg) === true before
86
+ * constructing ctx and calling this function. Throws when arg is a valid
87
+ * short ref but cannot be resolved (out of range, no running sandboxes,
88
+ * etc.); the caller surfaces the error to the user.
89
+ */
90
+ export function resolveTaskShortRef(arg, ctx) {
91
+ const n = Number(arg.slice(1));
92
+ if (n < 1) {
93
+ throw new Error(`Invalid sandbox index '${arg}': must be >= 1`);
94
+ }
95
+ const { running } = ctx;
96
+ if (running.length === 0) {
97
+ throw new Error(`No running sandbox to reference with '${arg}'`);
98
+ }
99
+ if (n > running.length) {
100
+ throw new Error(`No running sandbox at index '${arg}' (only ${running.length} running)`);
101
+ }
102
+ const row = running[n - 1];
103
+ if (!row.branch) {
104
+ throw new Error(`Cannot resolve branch for sandbox '${arg}' (container '${row.name}' missing branch label)`);
105
+ }
106
+ return row.branch;
107
+ }
108
+ //# sourceMappingURL=list-running.js.map
@@ -5,39 +5,24 @@ import pc from 'picocolors';
5
5
  import { loadConfig } from "../config.js";
6
6
  import { sandboxBranchLabel, sandboxLabel } from "../constants.js";
7
7
  import { detectEngine } from "../engine.js";
8
- import { runSafeEngine } from "../shell.js";
9
8
  import { resolveTools, toolProjectDirCandidates } from "../tools.js";
10
- const USAGE = 'Usage: ai sandbox ls';
11
- const CONTAINER_TABLE_HEADERS = ['NAMES', 'STATUS', 'BRANCH'];
12
- // Exported to lock the docker/podman-compatible format in unit tests.
13
- export function containerListFormat() {
14
- return '{{.Names}}\t{{.Status}}\t{{.Labels}}';
15
- }
16
- export function parseLabels(csv) {
17
- if (!csv) {
18
- return {};
19
- }
20
- const labels = {};
21
- for (const pair of csv.split(',')) {
22
- if (!pair) {
23
- continue;
24
- }
25
- const eq = pair.indexOf('=');
26
- if (eq < 0) {
27
- continue;
28
- }
29
- labels[pair.slice(0, eq)] = pair.slice(eq + 1);
30
- }
31
- return labels;
32
- }
9
+ import { fetchSandboxRows } from "./list-running.js";
10
+ export { containerListFormat, parseLabels } from "./list-running.js";
11
+ const USAGE = `Usage: ai sandbox ls
12
+
13
+ Lists all containers for the current project. The leftmost '#' column
14
+ numbers running sandboxes; use it as "ai sandbox exec '#N'" to enter one.
15
+ Quote '#N' to avoid shell '#' comment handling.`;
16
+ const CONTAINER_TABLE_HEADERS = ['#', 'NAMES', 'STATUS', 'BRANCH'];
33
17
  export function formatContainerTable(rows) {
34
- const columns = rows.map((row) => [row.name, row.status, row.branch]);
18
+ const columns = rows.map((row) => [row.index, row.name, row.status, row.branch]);
35
19
  const widths = [
36
- Math.max(CONTAINER_TABLE_HEADERS[0].length, ...rows.map((row) => row.name.length)),
37
- Math.max(CONTAINER_TABLE_HEADERS[1].length, ...rows.map((row) => row.status.length)),
38
- Math.max(CONTAINER_TABLE_HEADERS[2].length, ...rows.map((row) => row.branch.length))
20
+ Math.max(CONTAINER_TABLE_HEADERS[0].length, ...rows.map((row) => row.index.length)),
21
+ Math.max(CONTAINER_TABLE_HEADERS[1].length, ...rows.map((row) => row.name.length)),
22
+ Math.max(CONTAINER_TABLE_HEADERS[2].length, ...rows.map((row) => row.status.length)),
23
+ Math.max(CONTAINER_TABLE_HEADERS[3].length, ...rows.map((row) => row.branch.length))
39
24
  ];
40
- const renderRow = (values) => `${values[0].padEnd(widths[0])} ${values[1].padEnd(widths[1])} ${values[2]}`.trimEnd();
25
+ const renderRow = (values) => `${values[0].padEnd(widths[0])} ${values[1].padEnd(widths[1])} ${values[2].padEnd(widths[2])} ${values[3]}`.trimEnd();
41
26
  return [
42
27
  renderRow(CONTAINER_TABLE_HEADERS),
43
28
  ...columns.map((column) => renderRow(column))
@@ -58,27 +43,21 @@ export function ls(args = []) {
58
43
  const engine = detectEngine(config);
59
44
  const tools = resolveTools(config);
60
45
  const label = sandboxLabel(config);
61
- const containers = runSafeEngine(engine, 'docker', [
62
- 'ps',
63
- '-a',
64
- '--filter',
65
- `label=${label}`,
66
- '--format',
67
- containerListFormat()
68
- ]);
46
+ const { running, nonRunning } = fetchSandboxRows(engine, label, sandboxBranchLabel(config));
69
47
  p.intro(pc.cyan(`Sandbox status for ${config.project}`));
70
48
  p.log.step('Containers');
71
- if (!containers) {
49
+ const ordered = [...running, ...nonRunning];
50
+ if (ordered.length === 0) {
72
51
  p.log.warn(' No sandbox containers');
73
52
  }
74
53
  else {
75
- const branchKey = sandboxBranchLabel(config);
76
- const rows = containers.split('\n').map((line) => {
77
- const [name = '', status = '', labelsCsv = ''] = line.split('\t');
78
- const branch = parseLabels(labelsCsv)[branchKey] ?? '';
79
- return { name, status, branch };
80
- });
81
- for (const line of formatContainerTable(rows)) {
54
+ const tableRows = ordered.map((row) => ({
55
+ index: row.index === null ? '' : String(row.index),
56
+ name: row.name,
57
+ status: row.status,
58
+ branch: row.branch
59
+ }));
60
+ for (const line of formatContainerTable(tableRows)) {
82
61
  process.stdout.write(` ${line}\n`);
83
62
  }
84
63
  }