@fitlab-ai/agent-infra 0.9.10 → 0.9.11

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 (269) hide show
  1. package/README.md +13 -3
  2. package/README.zh-CN.md +13 -3
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +50 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  8. package/dist/lib/agent-clients/adapters/traecli.js +7 -8
  9. package/dist/lib/agent-clients/config.js +14 -99
  10. package/dist/lib/agent-clients/reconcile.js +1 -11
  11. package/dist/lib/agent-clients/schema.js +5 -4
  12. package/dist/lib/agent-clients/tokens.js +5 -15
  13. package/dist/lib/decide.js +14 -30
  14. package/dist/lib/defaults.json +4 -0
  15. package/dist/lib/init.js +2 -2
  16. package/dist/lib/internal/release-workflow.js +0 -1
  17. package/dist/lib/internal/task-finalization.js +19 -9
  18. package/dist/lib/internal/task-ledger.js +8 -2
  19. package/dist/lib/internal/task-lifecycle.js +17 -93
  20. package/dist/lib/internal/task-orchestration.js +30 -216
  21. package/dist/lib/internal/task-validate.js +2 -1
  22. package/dist/lib/merge.js +205 -103
  23. package/dist/lib/process-data/normalize.js +2 -9
  24. package/dist/lib/run/host.js +46 -16
  25. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  26. package/dist/lib/sandbox/commands/create.js +3 -3
  27. package/dist/lib/sandbox/commands/enter.js +18 -4
  28. package/dist/lib/sandbox/config.js +3 -12
  29. package/dist/lib/sandbox/control/executor.js +131 -39
  30. package/dist/lib/sandbox/control/lifecycle.js +29 -20
  31. package/dist/lib/sandbox/control/server.js +18 -16
  32. package/dist/lib/sandbox/mounts.js +3 -2
  33. package/dist/lib/sandbox/recovery.js +123 -22
  34. package/dist/lib/sandbox/workspace-identity.js +14 -0
  35. package/dist/lib/sandbox/workspace-view.js +8 -9
  36. package/dist/lib/self-update.js +278 -0
  37. package/dist/lib/server/protocol.js +5 -5
  38. package/dist/lib/task/activity-log.js +4 -1
  39. package/dist/lib/task/artifact-lifecycle.js +4 -2
  40. package/dist/lib/task/codex-orchestration.js +1 -1
  41. package/dist/lib/task/command-options.js +1 -3
  42. package/dist/lib/task/commands/cat.js +0 -5
  43. package/dist/lib/task/commands/decisions.js +76 -92
  44. package/dist/lib/task/commands/files.js +6 -6
  45. package/dist/lib/task/commands/grep.js +25 -61
  46. package/dist/lib/task/commands/issue-body.js +6 -6
  47. package/dist/lib/task/commands/log.js +25 -10
  48. package/dist/lib/task/commands/show.js +5 -6
  49. package/dist/lib/task/commands/status.js +8 -8
  50. package/dist/lib/task/commit-operation.js +21 -25
  51. package/dist/lib/task/commit-policy.js +1 -1
  52. package/dist/lib/task/control-authority.js +431 -0
  53. package/dist/lib/task/create.js +6 -1
  54. package/dist/lib/task/current-contract.js +35 -0
  55. package/dist/lib/task/decision-details.js +263 -0
  56. package/dist/lib/task/decision-intents.js +5 -2
  57. package/dist/lib/task/events.js +11 -5
  58. package/dist/lib/task/index.js +19 -20
  59. package/dist/lib/task/ledger-intents.js +8 -9
  60. package/dist/lib/task/ledger.js +13 -6
  61. package/dist/lib/task/lifecycle.js +16 -0
  62. package/dist/lib/task/orchestration.js +50 -13
  63. package/dist/lib/task/review-artifacts.js +31 -12
  64. package/dist/lib/task/review-finalization.js +19 -6
  65. package/dist/lib/task/verification-engine.js +38 -15
  66. package/dist/lib/task/verification-review.js +1 -1
  67. package/dist/lib/task/workspace-snapshot.js +5 -11
  68. package/dist/lib/task/write.js +11 -1
  69. package/dist/lib/update.js +6 -37
  70. package/dist/lib/version.js +5 -1
  71. package/dist/lifecycle-build-manifest.json +3 -3
  72. package/lib/agent-client.ts +0 -1
  73. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  74. package/lib/agent-clients/adapters/traecli.ts +7 -8
  75. package/lib/agent-clients/config.ts +14 -144
  76. package/lib/agent-clients/reconcile.ts +1 -12
  77. package/lib/agent-clients/schema.ts +5 -4
  78. package/lib/agent-clients/tokens.ts +5 -18
  79. package/lib/decide.ts +12 -22
  80. package/lib/defaults.json +4 -0
  81. package/lib/init.ts +2 -2
  82. package/lib/internal/release-workflow.ts +0 -1
  83. package/lib/internal/task-finalization.ts +27 -13
  84. package/lib/internal/task-ledger.ts +8 -2
  85. package/lib/internal/task-lifecycle.ts +33 -76
  86. package/lib/internal/task-orchestration.ts +33 -190
  87. package/lib/internal/task-validate.ts +3 -2
  88. package/lib/merge.ts +236 -120
  89. package/lib/process-data/normalize.ts +2 -8
  90. package/lib/run/host.ts +63 -14
  91. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  92. package/lib/sandbox/commands/create.ts +4 -4
  93. package/lib/sandbox/commands/enter.ts +24 -4
  94. package/lib/sandbox/config.ts +2 -14
  95. package/lib/sandbox/control/container-identity.ts +2 -2
  96. package/lib/sandbox/control/executor.ts +148 -51
  97. package/lib/sandbox/control/lifecycle.ts +34 -28
  98. package/lib/sandbox/control/protocol.ts +1 -3
  99. package/lib/sandbox/control/server.ts +15 -12
  100. package/lib/sandbox/control/state.ts +7 -7
  101. package/lib/sandbox/mounts.ts +4 -3
  102. package/lib/sandbox/recovery.ts +162 -34
  103. package/lib/sandbox/tools.ts +1 -1
  104. package/lib/sandbox/workspace-identity.ts +28 -0
  105. package/lib/sandbox/workspace-view.ts +14 -10
  106. package/lib/self-update.ts +341 -0
  107. package/lib/server/protocol.ts +5 -5
  108. package/lib/task/activity-log.ts +6 -1
  109. package/lib/task/artifact-lifecycle.ts +5 -3
  110. package/lib/task/codex-orchestration.ts +2 -1
  111. package/lib/task/command-options.ts +1 -2
  112. package/lib/task/commands/cat.ts +0 -4
  113. package/lib/task/commands/decisions.ts +80 -75
  114. package/lib/task/commands/files.ts +6 -6
  115. package/lib/task/commands/grep.ts +23 -51
  116. package/lib/task/commands/issue-body.ts +6 -6
  117. package/lib/task/commands/log.ts +22 -10
  118. package/lib/task/commands/show.ts +5 -6
  119. package/lib/task/commands/status.ts +8 -8
  120. package/lib/task/commit-operation.ts +25 -30
  121. package/lib/task/commit-policy.ts +1 -2
  122. package/lib/task/control-authority.ts +563 -0
  123. package/lib/task/create.ts +6 -1
  124. package/lib/task/current-contract.ts +49 -0
  125. package/lib/task/decision-details.ts +340 -0
  126. package/lib/task/decision-intents.ts +7 -2
  127. package/lib/task/events.ts +10 -5
  128. package/lib/task/index.ts +19 -20
  129. package/lib/task/ledger-intents.ts +6 -8
  130. package/lib/task/ledger.ts +17 -7
  131. package/lib/task/lifecycle.ts +20 -0
  132. package/lib/task/orchestration.ts +40 -15
  133. package/lib/task/review-artifacts.ts +53 -15
  134. package/lib/task/review-finalization.ts +33 -7
  135. package/lib/task/verification-engine.ts +33 -18
  136. package/lib/task/verification-review.ts +1 -1
  137. package/lib/task/workspace-snapshot.ts +4 -13
  138. package/lib/task/write.ts +20 -1
  139. package/lib/update.ts +6 -40
  140. package/lib/version.ts +6 -1
  141. package/package.json +1 -1
  142. package/templates/.agents/README.en.md +5 -3
  143. package/templates/.agents/README.zh-CN.md +5 -3
  144. package/templates/.agents/rules/compatibility-policy.en.md +5 -4
  145. package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
  146. package/templates/.agents/rules/create-issue.github.en.md +1 -1
  147. package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
  148. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  149. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  150. package/templates/.agents/rules/issue-fields.github.en.md +1 -1
  151. package/templates/.agents/rules/issue-fields.github.zh-CN.md +1 -1
  152. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  153. package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -1
  154. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -1
  155. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  156. package/templates/.agents/rules/issue-sync.github.en.md +1 -1
  157. package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -2
  158. package/templates/.agents/rules/milestone-inference.github.en.md +1 -1
  159. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +1 -1
  160. package/templates/.agents/rules/next-step-output.en.md +3 -3
  161. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  162. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  163. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  164. package/templates/.agents/rules/pr-sync.en.md +1 -1
  165. package/templates/.agents/rules/pr-sync.github.en.md +1 -1
  166. package/templates/.agents/rules/pr-sync.github.zh-CN.md +1 -1
  167. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
  168. package/templates/.agents/rules/release-commands.github.en.md +6 -0
  169. package/templates/.agents/rules/release-commands.github.zh-CN.md +6 -0
  170. package/templates/.agents/rules/review-handshake.en.md +1 -1
  171. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  172. package/templates/.agents/rules/task-management.en.md +1 -2
  173. package/templates/.agents/rules/task-management.zh-CN.md +1 -2
  174. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
  175. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  176. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  177. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  178. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  179. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  180. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  181. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  182. package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
  183. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
  184. package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
  185. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
  186. package/templates/.agents/skills/code-task/SKILL.en.md +3 -3
  187. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
  188. package/templates/.agents/skills/commit/SKILL.en.md +27 -7
  189. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -4
  190. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -5
  191. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -5
  192. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  193. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  194. package/templates/.agents/skills/create-pr/SKILL.en.md +5 -7
  195. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +5 -7
  196. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
  197. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
  198. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  199. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  200. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  201. package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
  202. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
  203. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  204. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  205. package/templates/.agents/skills/plan-task/SKILL.en.md +3 -3
  206. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
  207. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  208. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  209. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -3
  210. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -3
  211. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  212. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  213. package/templates/.agents/skills/review-code/SKILL.en.md +5 -3
  214. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -3
  215. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  216. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  217. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
  218. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
  219. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -3
  220. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -3
  221. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  222. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  223. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -0
  224. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -0
  225. package/templates/.agents/skills/run-task/SKILL.en.md +4 -0
  226. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +4 -0
  227. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +140 -109
  230. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  231. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  232. package/templates/.agents/templates/task.en.md +1 -1
  233. package/templates/.agents/templates/task.zh-CN.md +1 -1
  234. package/templates/.claude/commands/analyze-task.en.md +1 -1
  235. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  236. package/templates/.claude/commands/block-task.en.md +1 -1
  237. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  238. package/templates/.claude/commands/cancel-task.en.md +1 -1
  239. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  240. package/templates/.claude/commands/check-task.en.md +1 -1
  241. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  242. package/templates/.claude/commands/code-task.en.md +1 -1
  243. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  244. package/templates/.claude/commands/commit.en.md +1 -1
  245. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  246. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  247. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  248. package/templates/.claude/commands/complete-task.en.md +1 -1
  249. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  250. package/templates/.claude/commands/create-pr.en.md +1 -1
  251. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  252. package/templates/.claude/commands/plan-task.en.md +1 -1
  253. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  254. package/templates/.claude/commands/review-analysis.en.md +1 -1
  255. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  256. package/templates/.claude/commands/review-code.en.md +1 -1
  257. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  258. package/templates/.claude/commands/review-plan.en.md +1 -1
  259. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  260. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  261. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  262. package/templates/.claude/commands/run-task.en.md +1 -1
  263. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  264. package/templates/.claude/commands/watch-pr.en.md +1 -1
  265. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  266. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
  267. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
  268. package/dist/lib/builtin-tuis.js +0 -40
  269. package/lib/builtin-tuis.ts +0 -56
@@ -3,20 +3,19 @@ import path from 'node:path';
3
3
  import { formatTable } from '../../table.ts';
4
4
  import { parseTaskScope } from '../command-options.ts';
5
5
  import { resolveTaskContext } from '../resolve-ref.ts';
6
- import { isReviewStage, parseLedger, type LedgerRow, type ReviewStage } from '../ledger.ts';
6
+ import { isReviewStage, parseLedgerDocument, type LedgerRow, type ReviewStage } from '../ledger.ts';
7
7
  import { listDecisionItems, selectDecisionItem } from '../decision-items.ts';
8
- import { extractSubSection } from '../sections.ts';
8
+ import { normalizeDecisionDetailForDisplay, parseDecisionEvidence, resolveDecisionDetail } from '../decision-details.ts';
9
9
 
10
10
  const USAGE = `Usage: ai task decisions [--task <ref> | -t <ref>] [--item <selector> | -i <selector>] [options]
11
- ai task decisions <ref> [selector] [options]
12
11
 
13
12
  Shows what still needs a maintainer's decision. Without a selector, it lists
14
13
  the choices. With a selector, it shows the explanation and how to respond.
15
14
  Read-only.
16
15
 
17
- Omit <ref> Resolve the unique active task for the current branch.
18
- <ref> Legacy positional task ref.
19
- [selector] Ordinal (1-based) or ledger id (e.g. 'PL-3') to show one item's detail.
16
+ Omit the scope Resolve the unique active task for the current branch.
17
+ --task/-t <ref> Select a task explicitly.
18
+ --item/-i <s> Ordinal (1-based) or ledger id (e.g. 'PL-3') to show one item's detail.
20
19
 
21
20
  Options:
22
21
  -i, --item <s> Select an item when task scope is implicit or flag-based.
@@ -56,9 +55,8 @@ function parseArgs(args: string[]): ParsedArgs | null {
56
55
  if (out.item !== undefined) { fail("duplicate option '--item'"); return null; }
57
56
  out.item = v; i += 1;
58
57
  } else if (a.startsWith('--item=')) {
59
- if (out.item !== undefined) { fail("duplicate option '--item'"); return null; }
60
- out.item = a.slice('--item='.length);
61
- if (out.item === '') { fail('--item requires a value'); return null; }
58
+ fail("--item=... is not supported; use --item <selector> or -i <selector>");
59
+ return null;
62
60
  } else if (a === '--stage') {
63
61
  const v = args[i + 1];
64
62
  if (v === undefined) {
@@ -89,47 +87,30 @@ function parseArgs(args: string[]): ParsedArgs | null {
89
87
  return out;
90
88
  }
91
89
 
92
- // Parse `<file>.md#anchor` evidence into its filename, when present.
93
- function evidenceFile(evidence: string): string | null {
94
- const m = /([\w.-]+\.md)#/.exec(evidence);
95
- return m ? m[1]! : null;
96
- }
97
-
98
- function roundOf(file: string): number {
99
- const m = /-r(\d+)\.md$/.exec(file);
100
- return m ? Number.parseInt(m[1]!, 10) : 1;
101
- }
90
+ // Locate the canonical detail block named by the row's current evidence. Historical
91
+ // artifacts are intentionally not searched: an evidence reference is a precise
92
+ // provenance pointer, not a best-effort hint.
93
+ type DetailLookup =
94
+ | { status: 'found'; content: string }
95
+ | { status: 'missing'; reason: string }
96
+ | { status: 'ambiguous'; reason: string }
97
+ | { status: 'invalid'; reason: string };
102
98
 
103
- // Locate the `### {ledger-id}` detail block for a row. Prefer the artifact named by the
104
- // row's evidence anchor; otherwise scan analysis/plan/code artifacts and return
105
- // the block from the highest-round file that contains it. Returns '' when none
106
- // is found (caller degrades gracefully — plan B3).
107
- function findDetailBlock(row: LedgerRow, taskDir: string): string {
108
- const hinted = evidenceFile(row.evidence);
109
- if (hinted) {
110
- const p = path.join(taskDir, hinted);
111
- if (fs.existsSync(p)) {
112
- const block = extractSubSection(fs.readFileSync(p, 'utf8'), row.id);
113
- if (block) return block;
114
- }
115
- }
116
- let best = '';
117
- let bestRound = -1;
118
- let entries: string[];
99
+ function findDetailBlock(row: LedgerRow, taskDir: string): DetailLookup {
100
+ const evidence = parseDecisionEvidence(row.evidence);
101
+ if (!evidence.ok) return { status: 'invalid', reason: `invalid evidence '${row.evidence}'` };
102
+ const artifactPath = path.join(taskDir, evidence.artifact);
103
+ if (!fs.existsSync(artifactPath)) return { status: 'missing', reason: `artifact '${evidence.artifact}' is missing` };
104
+ let artifactContent: string;
119
105
  try {
120
- entries = fs.readdirSync(taskDir);
106
+ artifactContent = fs.readFileSync(artifactPath, 'utf8');
121
107
  } catch {
122
- return '';
123
- }
124
- for (const file of entries) {
125
- if (!/^(analysis|plan|code)(-r\d+)?\.md$/.test(file)) continue;
126
- const block = extractSubSection(fs.readFileSync(path.join(taskDir, file), 'utf8'), row.id);
127
- if (block && roundOf(file) > bestRound) {
128
- best = block;
129
- bestRound = roundOf(file);
130
- }
108
+ return { status: 'missing', reason: `artifact '${evidence.artifact}' cannot be read` };
131
109
  }
132
- return best;
110
+ const resolved = resolveDecisionDetail(artifactContent, row.id, evidence.anchor);
111
+ if (resolved.status === 'found') return { status: 'found', content: normalizeDecisionDetailForDisplay(resolved.block) };
112
+ if (resolved.status === 'ambiguous') return { status: 'ambiguous', reason: resolved.reason };
113
+ return { status: 'missing', reason: resolved.reason };
133
114
  }
134
115
 
135
116
  // Pull the `## 人工裁决` record lines that mention this ledger id, so a decided item
@@ -149,9 +130,9 @@ function findDecisionRecord(id: string, content: string): string[] {
149
130
  }
150
131
 
151
132
  function titleOf(row: LedgerRow, taskDir: string): string {
152
- const block = findDetailBlock(row, taskDir);
153
- if (block) {
154
- const title = block
133
+ const detail = findDetailBlock(row, taskDir);
134
+ if (detail.status === 'found') {
135
+ const title = detail.content
155
136
  .split('\n')[0]!
156
137
  .replace(/^###\s+/, '')
157
138
  .replace(/\s*\[needs-human-decision\]\s*$/, '')
@@ -165,7 +146,7 @@ function trackingOf(row: LedgerRow): string {
165
146
  return `stage=${row.stage} · severity=${row.severity} · status=${row.status} · evidence=${row.evidence}`;
166
147
  }
167
148
 
168
- function renderList(rows: LedgerRow[], format: string, taskDir: string): void {
149
+ function renderList(rows: LedgerRow[], format: string, taskDir: string, taskId: string): void {
169
150
  if (rows.length === 0) {
170
151
  process.stdout.write('No pending decisions.\n');
171
152
  return;
@@ -184,10 +165,10 @@ function renderList(rows: LedgerRow[], format: string, taskDir: string): void {
184
165
  `| ${sep.join(' | ')} |`,
185
166
  ...data.map((row) => `| ${row.join(' | ')} |`)
186
167
  ];
187
- process.stdout.write(`${md.join('\n')}\n\nView one item: ai task decisions <task-ref> <ordinal|ledger-id>\n`);
168
+ process.stdout.write(`${md.join('\n')}\n\nView one item: ai task decisions --task ${taskId} --item <ordinal|ledger-id>\n`);
188
169
  return;
189
170
  }
190
- process.stdout.write(`${formatTable(headers, data).join('\n')}\n\nView one item: ai task decisions <task-ref> <ordinal|ledger-id>\n`);
171
+ process.stdout.write(`${formatTable(headers, data).join('\n')}\n\nView one item: ai task decisions --task ${taskId} --item <ordinal|ledger-id>\n`);
191
172
  }
192
173
 
193
174
  function renderTracking(row: LedgerRow, format: string): string[] {
@@ -214,7 +195,8 @@ function renderDetail(
214
195
  selector: string,
215
196
  format: string,
216
197
  taskDir: string,
217
- content: string
198
+ content: string,
199
+ taskId: string
218
200
  ): void {
219
201
  const selected = selectDecisionItem(rows, selector);
220
202
  if (!selected.ok) {
@@ -222,21 +204,23 @@ function renderDetail(
222
204
  return;
223
205
  }
224
206
  const r = selected.row;
225
- const block = findDetailBlock(r, taskDir);
226
- const title = titleOf(r, taskDir);
207
+ const detail = findDetailBlock(r, taskDir);
208
+ if (detail.status === 'invalid' || detail.status === 'ambiguous') {
209
+ fail(`cannot resolve ${r.id}: ${detail.reason}`);
210
+ return;
211
+ }
212
+ const block = detail.status === 'found' ? detail.content : '';
213
+ const title = block
214
+ ? block.split('\n')[0]!.replace(/^###\s+/, '').replace(/\s*\[needs-human-decision\]\s*$/, '').trim()
215
+ .replace(new RegExp(`^${r.id}\\s*[::]\\s*`, 'i'), '').trim()
216
+ : '(explanation unavailable)';
227
217
  const lines: string[] = [];
228
218
  const decided = r.status === 'human-decided';
229
219
  if (format === 'markdown') {
230
220
  lines.push(`## ${decided ? 'Decision already recorded' : 'Decision needed'}: ${title}`, '');
231
- if (!decided) {
232
- lines.push('**How to record your choice**', `\`ai decide <task-ref> ${r.id} <your choice and rationale>\``, '');
233
- }
234
221
  lines.push('**Original context**', '');
235
222
  } else {
236
223
  lines.push(`${decided ? 'Decision already recorded' : 'Decision needed'}: ${title}`, '');
237
- if (!decided) {
238
- lines.push('How to record your choice:', `ai decide <task-ref> ${r.id} <your choice and rationale>`, '');
239
- }
240
224
  lines.push('Original context:');
241
225
  }
242
226
  if (block) {
@@ -252,6 +236,14 @@ function renderDetail(
252
236
  lines.push('', format === 'markdown' ? '**Recorded choice**' : 'Recorded choice:');
253
237
  lines.push(...(record.length ? record : ['No separate ruling record was found.']));
254
238
  }
239
+ if (!decided) {
240
+ lines.push('');
241
+ if (format === 'markdown') {
242
+ lines.push('**How to record your choice**', `\`ai decide --task ${taskId} --item ${r.id} <your choice and rationale>\``);
243
+ } else {
244
+ lines.push('How to record your choice:', `ai decide --task ${taskId} --item ${r.id} <your choice and rationale>`);
245
+ }
246
+ }
255
247
  lines.push('', ...renderTracking(r, format));
256
248
  process.stdout.write(`${lines.join('\n')}\n`);
257
249
  }
@@ -274,16 +266,11 @@ function decisions(args: string[] = []): void {
274
266
  return;
275
267
  }
276
268
 
277
- let taskRef = scope.taskRef;
278
- let selector = parsed.item;
279
- if (parsed.item !== undefined) {
280
- if (parsed.positionals.length > 0) { fail('positional task ref/selector cannot be combined with --item'); return; }
281
- } else if (scope.explicit) {
282
- if (parsed.positionals.length > 0) { fail('positional task ref/selector cannot be combined with --task'); return; }
283
- } else {
284
- if (parsed.positionals.length > 2) { fail('too many positional arguments'); return; }
285
- taskRef = parsed.positionals[0];
286
- selector = parsed.positionals[1];
269
+ const taskRef = scope.taskRef;
270
+ const selector = parsed.item;
271
+ if (parsed.positionals.length > 0) {
272
+ fail('positional task ref/selector is not supported; use --task <ref> or -t <ref>, and --item <selector> or -i <selector>');
273
+ return;
287
274
  }
288
275
  const resolved = resolveTaskContext(taskRef);
289
276
  if (!resolved.ok) {
@@ -291,16 +278,34 @@ function decisions(args: string[] = []): void {
291
278
  return;
292
279
  }
293
280
 
294
- const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
295
- const rows = listDecisionItems(parseLedger(content), {
281
+ let content: string;
282
+ let ledger;
283
+ try {
284
+ content = fs.readFileSync(resolved.taskMdPath, 'utf8');
285
+ ledger = parseLedgerDocument(content);
286
+ } catch (error) {
287
+ fail(`ledger unavailable [LEDGER_DOCUMENT_INVALID]: ${error instanceof Error ? error.message : String(error)}`);
288
+ return;
289
+ }
290
+ if (!ledger.present) {
291
+ fail('ledger unavailable [LEDGER_SECTION_MISSING]: review disagreement ledger section is missing');
292
+ return;
293
+ }
294
+ const rows = listDecisionItems(ledger.rows as LedgerRow[], {
296
295
  includeDecided: parsed.all,
297
296
  stage: parsed.stage as ReviewStage | undefined
298
297
  });
299
298
 
300
299
  if (selector === undefined) {
301
- renderList(rows, parsed.format, resolved.taskDir);
300
+ renderList(rows, parsed.format, resolved.taskDir, resolved.taskId);
302
301
  } else {
303
- renderDetail(rows, selector, parsed.format, resolved.taskDir, content);
302
+ const selectionRows = /^[A-Za-z]+-\d+$/.test(selector)
303
+ ? listDecisionItems(ledger.rows as LedgerRow[], {
304
+ includeDecided: true,
305
+ stage: parsed.stage as ReviewStage | undefined
306
+ })
307
+ : rows;
308
+ renderDetail(selectionRows, selector, parsed.format, resolved.taskDir, content, resolved.taskId);
304
309
  }
305
310
  }
306
311
 
@@ -3,11 +3,11 @@ import { parseTaskScope } from '../command-options.ts';
3
3
  import { resolveTaskContext } from '../resolve-ref.ts';
4
4
  import { enumerateArtifacts } from '../artifacts.ts';
5
5
 
6
- const USAGE = `Usage: ai task files [<N | TASK-id> | --task <ref> | -t <ref>]
6
+ const USAGE = `Usage: ai task files [--task <ref> | -t <ref>]
7
7
 
8
8
  Lists the artifacts in a task directory with stable numbers.
9
- Omit <ref> Resolve the unique active task for the current branch.
10
- <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
9
+ Omit the scope Resolve the unique active task for the current branch.
10
+ --task/-t <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
11
11
 
12
12
  Columns: # (artifact number, usable with 'ai task cat') / NAME / SIZE (bytes) / MTIME
13
13
  `;
@@ -35,10 +35,10 @@ function files(args: string[] = []): void {
35
35
  try { scope = parseTaskScope(args); } catch (error) {
36
36
  process.stderr.write(`ai task files: ${error instanceof Error ? error.message : String(error)}\n`); process.exitCode = 1; return;
37
37
  }
38
- if (scope.positionals.length > 1 || (scope.explicit && scope.positionals.length > 0)) {
39
- process.stderr.write('ai task files: task ref must be provided once\n'); process.exitCode = 1; return;
38
+ if (scope.positionals.length > 0) {
39
+ process.stderr.write('ai task files: positional task ref is not supported; use --task <ref> or -t <ref>\n'); process.exitCode = 1; return;
40
40
  }
41
- const resolved = resolveTaskContext(scope.taskRef ?? scope.positionals[0]);
41
+ const resolved = resolveTaskContext(scope.taskRef);
42
42
  if (!resolved.ok) {
43
43
  process.stderr.write(`ai task files: ${resolved.message}\n`);
44
44
  process.exitCode = 1;
@@ -1,25 +1,20 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { resolveTaskContext, resolveTaskRef, detectRepoRoot, enumerateTaskDirs } from '../resolve-ref.ts';
3
+ import { resolveTaskContext, resolveTaskRef } from '../resolve-ref.ts';
4
4
  import { enumerateArtifacts, resolveArtifact } from '../artifacts.ts';
5
5
  import { loadShortIdByTaskId } from '../short-id.ts';
6
6
 
7
- const USAGE = `Usage: ai task grep <pattern> [ref] [artifact | N]
8
- ai task grep <pattern> (--current | --task <ref> | -t <ref>) [artifact | N]
7
+ const USAGE = `Usage: ai task grep [--current | --task <ref> | -t <ref>] <pattern> [artifact | N]
9
8
 
10
9
  Literal (non-regex) line search across task artifacts.
11
10
  <pattern> Literal substring to match (NOT a regex). Case-sensitive by default.
12
- [ref] Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
13
- Omit to scan every task under active / blocked / completed
14
- (archive is skipped). With a ref, narrows to that single task
15
- (a TASK-id ref can also resolve an archived task).
16
- [artifact | N] Only valid with <ref>. Artifact filename (with or without '.md')
11
+ --current Limit search to the unique active task for the current branch.
12
+ -t, --task <ref> Limit search to an explicit task (bare short id or TASK-id).
13
+ [artifact | N] Artifact filename (with or without '.md')
17
14
  or the number from 'ai task files'. Narrows to a single artifact.
18
15
 
19
16
  Options:
20
17
  -i, --ignore-case Case-insensitive matching.
21
- --current Limit search to the current task context.
22
- -t, --task <ref> Limit search to an explicit task.
23
18
  -- Treat the rest as positional (use for patterns starting with '-').
24
19
 
25
20
  Output: '{taskId} [#short] {fileStem}:{line}: {matched-line}' (short id only for active tasks).
@@ -85,9 +80,7 @@ function grep(args: string[] = []): void {
85
80
  continue;
86
81
  }
87
82
  if (!optsEnded && a.startsWith('--task=')) {
88
- if (taskRef !== undefined) { process.stderr.write("ai task grep: duplicate option '--task'\n"); process.exitCode = 1; return; }
89
- taskRef = a.slice('--task='.length);
90
- if (taskRef === '') { process.stderr.write('ai task grep: --task requires a value\n'); process.exitCode = 1; return; }
83
+ process.stderr.write('ai task grep: --task=... is not supported; use --task <ref> or -t <ref>\n'); process.exitCode = 1; return;
91
84
  continue;
92
85
  }
93
86
  if (!optsEnded && a.startsWith('-') && a !== '-') {
@@ -106,61 +99,40 @@ function grep(args: string[] = []): void {
106
99
  if (current && taskRef !== undefined) {
107
100
  process.stderr.write('ai task grep: --current and --task are mutually exclusive\n'); process.exitCode = 1; return;
108
101
  }
109
- if (positional.length > 3 || ((current || taskRef !== undefined) && positional.length > 2)) {
102
+ if (positional.length > 2) {
110
103
  process.stderr.write('ai task grep: too many arguments\n');
111
104
  process.exitCode = 1;
112
105
  return;
113
106
  }
114
107
 
115
- let [pattern, ref, artifactOrN] = positional;
116
- if (current || taskRef !== undefined) {
117
- artifactOrN = ref;
118
- ref = taskRef;
119
- }
108
+ const [pattern, artifactOrN] = positional;
120
109
  const matcher = makeMatcher(pattern!, ignoreCase);
121
110
  const chunks: string[] = [];
122
111
  const emit = (line: string) => chunks.push(line);
123
112
  let total = 0;
124
113
 
125
- if (ref === undefined && !current) {
126
- // No ref: full scan across active / blocked / completed (no archive).
127
- let repoRoot: string;
114
+ const resolved = current || taskRef === undefined
115
+ ? resolveTaskContext()
116
+ : resolveTaskRef(taskRef);
117
+ if (!resolved.ok) {
118
+ process.stderr.write(`ai task grep: ${resolved.message}\n`);
119
+ process.exitCode = 1;
120
+ return;
121
+ }
122
+ const shortToken = loadShortIdByTaskId(resolved.repoRoot).get(resolved.taskId);
123
+ if (artifactOrN !== undefined) {
124
+ let artifactPath: string;
128
125
  try {
129
- repoRoot = detectRepoRoot();
126
+ artifactPath = resolveArtifact(resolved.taskDir, artifactOrN);
130
127
  } catch (e) {
131
128
  process.stderr.write(`ai task grep: ${(e as Error).message}\n`);
132
129
  process.exitCode = 1;
133
130
  return;
134
131
  }
135
- const shortMap = loadShortIdByTaskId(repoRoot);
136
- for (const { taskId, taskDir } of enumerateTaskDirs(repoRoot)) {
137
- const shortToken = shortMap.get(taskId);
138
- for (const a of enumerateArtifacts(taskDir)) {
139
- total += scanArtifact(taskId, shortToken, a.path, matcher, emit);
140
- }
141
- }
132
+ total += scanArtifact(resolved.taskId, shortToken, artifactPath, matcher, emit);
142
133
  } else {
143
- const resolved = current ? resolveTaskContext() : resolveTaskRef(ref!);
144
- if (!resolved.ok) {
145
- process.stderr.write(`ai task grep: ${resolved.message}\n`);
146
- process.exitCode = 1;
147
- return;
148
- }
149
- const shortToken = loadShortIdByTaskId(resolved.repoRoot).get(resolved.taskId);
150
- if (artifactOrN !== undefined) {
151
- let artifactPath: string;
152
- try {
153
- artifactPath = resolveArtifact(resolved.taskDir, artifactOrN);
154
- } catch (e) {
155
- process.stderr.write(`ai task grep: ${(e as Error).message}\n`);
156
- process.exitCode = 1;
157
- return;
158
- }
159
- total += scanArtifact(resolved.taskId, shortToken, artifactPath, matcher, emit);
160
- } else {
161
- for (const a of enumerateArtifacts(resolved.taskDir)) {
162
- total += scanArtifact(resolved.taskId, shortToken, a.path, matcher, emit);
163
- }
134
+ for (const a of enumerateArtifacts(resolved.taskDir)) {
135
+ total += scanArtifact(resolved.taskId, shortToken, a.path, matcher, emit);
164
136
  }
165
137
  }
166
138
 
@@ -6,11 +6,11 @@ import { extractSection, findSectionHeading } from '../sections.ts';
6
6
  import { renderTemplateBody, PLACEHOLDER } from '../issue-form.ts';
7
7
  import type { TaskFields } from '../issue-form.ts';
8
8
 
9
- const USAGE = `Usage: ai task issue-body [<ref> | --task <ref> | -t <ref>] [--template <path>]
9
+ const USAGE = `Usage: ai task issue-body [--task <ref> | -t <ref>] [--template <path>]
10
10
 
11
11
  Print a deterministic Issue body extracted from a task's task.md.
12
- Omit <ref> Resolve the unique active task for the current branch.
13
- <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
12
+ Omit the scope Resolve the unique active task for the current branch.
13
+ --task/-t <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
14
14
  --template <path> Render the final body for the given GitHub Issue Form (scenario A);
15
15
  without it, print the default '任务输入 + 描述 + 需求' body (scenario B).
16
16
 
@@ -71,10 +71,10 @@ function issueBody(args: string[] = []): void {
71
71
  try { scope = parseTaskScope(scopeArgs); } catch (error) {
72
72
  process.stderr.write(`ai task issue-body: ${error instanceof Error ? error.message : String(error)}\n`); process.exitCode = 1; return;
73
73
  }
74
- if (scope.positionals.length > 1 || (scope.explicit && scope.positionals.length > 0)) {
75
- process.stderr.write('ai task issue-body: task ref must be provided once\n'); process.exitCode = 1; return;
74
+ if (scope.positionals.length > 0) {
75
+ process.stderr.write('ai task issue-body: positional task ref is not supported; use --task <ref> or -t <ref>\n'); process.exitCode = 1; return;
76
76
  }
77
- const resolved = resolveTaskContext(scope.taskRef ?? scope.positionals[0]);
77
+ const resolved = resolveTaskContext(scope.taskRef);
78
78
  if (!resolved.ok) {
79
79
  process.stderr.write(`ai task issue-body: ${resolved.message}\n`);
80
80
  process.exitCode = 1;
@@ -3,16 +3,16 @@ import { classifyAgent } from '../../agent-clients/tokens.ts';
3
3
  import { formatTable } from '../../table.ts';
4
4
  import { parseTaskScope } from '../command-options.ts';
5
5
  import { resolveTaskContext } from '../resolve-ref.ts';
6
- import { isReviewStage, parseLedger, type LedgerRow, type ReviewStage } from '../ledger.ts';
6
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, isReviewStage, parseLedgerDocument, type LedgerRow, type ReviewStage } from '../ledger.ts';
7
7
  import { parseActivityLog, pairEntries } from '../activity-log.ts';
8
8
 
9
- const USAGE = `Usage: ai task log [<N | TASK-id> | --task <ref> | -t <ref>]
9
+ const USAGE = `Usage: ai task log [--task <ref> | -t <ref>]
10
10
 
11
11
  Renders a task's activity log as a per-step status table. A step's start and
12
12
  completion are paired onto one row: STARTED holds the start time, DONE the
13
13
  completion time (or '(in progress)' while still running).
14
- Omit <ref> Resolve the unique active task for the current branch.
15
- <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
14
+ Omit the scope Resolve the unique active task for the current branch.
15
+ --task/-t <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
16
16
 
17
17
  Columns: # (row) / STEP / AGENT / STARTED / DONE / NOTE
18
18
  A human-executed step shows AGENT as 'human' and, when it has no start marker,
@@ -92,8 +92,8 @@ function isHumanCountField(field: string): boolean {
92
92
  }
93
93
 
94
94
  // A step is human-executed when its agent token is not a known AI token.
95
- // Known long names (claude-code -> claude, gemini-cli -> gemini) classify as
96
- // AI; empty or unknown tokens classify as non-AI (human).
95
+ // Known long names classify as AI; empty or unknown tokens classify as non-AI
96
+ // (human).
97
97
  export function isHumanAgent(agent: string): boolean {
98
98
  return classifyAgent(agent).status !== 'ai';
99
99
  }
@@ -122,10 +122,10 @@ function log(args: string[] = []): void {
122
122
  try { scope = parseTaskScope(args); } catch (error) {
123
123
  process.stderr.write(`ai task log: ${error instanceof Error ? error.message : String(error)}\n`); process.exitCode = 1; return;
124
124
  }
125
- if (scope.positionals.length > 1 || (scope.explicit && scope.positionals.length > 0)) {
126
- process.stderr.write('ai task log: task ref must be provided once\n'); process.exitCode = 1; return;
125
+ if (scope.positionals.length > 0) {
126
+ process.stderr.write('ai task log: positional task ref is not supported; use --task <ref> or -t <ref>\n'); process.exitCode = 1; return;
127
127
  }
128
- const resolved = resolveTaskContext(scope.taskRef ?? scope.positionals[0]);
128
+ const resolved = resolveTaskContext(scope.taskRef);
129
129
  if (!resolved.ok) {
130
130
  process.stderr.write(`ai task log: ${resolved.message}\n`);
131
131
  process.exitCode = 1;
@@ -146,7 +146,15 @@ function log(args: string[] = []): void {
146
146
  return;
147
147
  }
148
148
  const steps = pairEntries(entries);
149
- const humanDecisionCounts = countHumanDecisionsByStage(parseLedger(content));
149
+ let humanDecisionCounts = new Map<ReviewStage, number>();
150
+ let ledgerDiagnostic: string | null = null;
151
+ try {
152
+ const ledger = parseLedgerDocument(content);
153
+ if (ledger.present) humanDecisionCounts = countHumanDecisionsByStage(ledger.rows as LedgerRow[]);
154
+ else ledgerDiagnostic = `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`;
155
+ } catch (error) {
156
+ ledgerDiagnostic = `LEDGER_DOCUMENT_INVALID: ${error instanceof Error ? error.message : String(error)}`;
157
+ }
150
158
  const rows = steps.map((s, idx) => {
151
159
  const stage = reviewStageForStep(s.step);
152
160
  const note = stage
@@ -161,6 +169,10 @@ function log(args: string[] = []): void {
161
169
  for (const line of formatTable(TABLE_HEADERS, rows, { zebra: Boolean(process.stdout.isTTY) })) {
162
170
  process.stdout.write(`${line}\n`);
163
171
  }
172
+ if (ledgerDiagnostic) {
173
+ process.stdout.write(`Ledger: unavailable [${ledgerDiagnostic}]\n`);
174
+ process.exitCode = 1;
175
+ }
164
176
  process.stdout.write(`Total: ${steps.length} steps\n`);
165
177
  }
166
178
 
@@ -2,12 +2,11 @@ import fs from 'node:fs';
2
2
  import { parseTaskScope } from '../command-options.ts';
3
3
  import { resolveTaskContext } from '../resolve-ref.ts';
4
4
 
5
- const USAGE = `Usage: ai task show [<N | TASK-id> | --task <ref> | -t <ref>]
5
+ const USAGE = `Usage: ai task show [--task <ref> | -t <ref>]
6
6
 
7
7
  Prints the task.md content for the matching task.
8
8
  Omit the ref Resolve the unique active task for the current branch.
9
- N (bare numeric) Recommended; resolves the active short id via the registry.
10
- TASK-YYYYMMDD-HHMMSS Locates a task in active / blocked / completed / archive.
9
+ --task/-t <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
11
10
  `;
12
11
 
13
12
  function show(args: string[] = []): void {
@@ -20,10 +19,10 @@ function show(args: string[] = []): void {
20
19
  process.stderr.write(`ai task show: ${error instanceof Error ? error.message : String(error)}\n`);
21
20
  process.exitCode = 1; return;
22
21
  }
23
- if (scope.positionals.length > 1 || (scope.explicit && scope.positionals.length > 0)) {
24
- process.stderr.write('ai task show: task ref must be provided once\n'); process.exitCode = 1; return;
22
+ if (scope.positionals.length > 0) {
23
+ process.stderr.write('ai task show: positional task ref is not supported; use --task <ref> or -t <ref>\n'); process.exitCode = 1; return;
25
24
  }
26
- const resolved = resolveTaskContext(scope.taskRef ?? scope.positionals[0]);
25
+ const resolved = resolveTaskContext(scope.taskRef);
27
26
  if (!resolved.ok) {
28
27
  process.stderr.write(`ai task show: ${resolved.message}\n`);
29
28
  process.exitCode = 1;
@@ -8,17 +8,17 @@ import { enumerateArtifacts, type Artifact } from '../artifacts.ts';
8
8
  import { parseTaskFrontmatter, extractTitle, type Frontmatter } from '../frontmatter.ts';
9
9
  import { loadShortIdByTaskId } from '../short-id.ts';
10
10
  import { getOpenWorkflowWarnings, formatWorkflowWarningSummary, type WorkflowWarning } from '../workflow-warnings.ts';
11
- import { parseActivityLog, pairEntries } from '../activity-log.ts';
11
+ import { parseActivityLog, pairEntries, startedBackedRows } from '../activity-log.ts';
12
12
  import { readRun, type OrchestrationRun } from '../orchestration.ts';
13
13
  import type { DelegationReceipt } from '../delegation-receipts.ts';
14
14
  import { statusCard, type DisplayMessage } from '../../server/display.ts';
15
15
 
16
- const USAGE = `Usage: ai task status [<N | TASK-id> | --task <ref> | -t <ref>]
16
+ const USAGE = `Usage: ai task status [--task <ref> | -t <ref>]
17
17
 
18
18
  Prints an aggregated "health check" view for a task: header, metadata,
19
19
  artifacts, workflow/runtime execution state, and git branch state.
20
- Omit <ref> Resolve the unique active task for the current branch.
21
- <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
20
+ Omit the scope Resolve the unique active task for the current branch.
21
+ --task/-t <ref> Bare numeric short id, or a full TASK-YYYYMMDD-HHMMSS id.
22
22
 
23
23
  Git rows are best-effort: a failed git call degrades that row to '-' without
24
24
  failing the command.
@@ -186,7 +186,7 @@ function collectWorkflow(content: string, now: Date = new Date()): WorkflowInfo
186
186
  return { state: 'unknown', step: DASH, agent: DASH, startedAt: DASH, doneAt: DASH, stale: DASH };
187
187
  }
188
188
 
189
- const rows = pairEntries(parsed.entries);
189
+ const rows = startedBackedRows(pairEntries(parsed.entries));
190
190
  const latest = rows.at(-1);
191
191
  if (!latest) {
192
192
  return { state: 'unknown', step: DASH, agent: DASH, startedAt: DASH, doneAt: DASH, stale: DASH };
@@ -577,10 +577,10 @@ function status(args: string[] = []): void {
577
577
  let model: StatusModel;
578
578
  try {
579
579
  const scope = parseTaskScope(args);
580
- if (scope.positionals.length > 1 || (scope.explicit && scope.positionals.length > 0)) {
581
- throw new Error('task ref must be provided once');
580
+ if (scope.positionals.length > 0) {
581
+ throw new Error('positional task ref is not supported; use --task <ref> or -t <ref>');
582
582
  }
583
- const resolved = resolveTaskContext(scope.taskRef ?? scope.positionals[0]);
583
+ const resolved = resolveTaskContext(scope.taskRef);
584
584
  if (!resolved.ok) throw new Error(resolved.message);
585
585
  model = buildFromResolved({
586
586
  taskId: resolved.taskId, taskDir: resolved.taskDir, taskMdPath: resolved.taskMdPath,