@fitlab-ai/agent-infra 0.8.1 → 0.8.3

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 (131) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/bin/cli.ts +3 -3
  4. package/dist/bin/cli.js +4 -4
  5. package/dist/lib/cp.js +1 -1
  6. package/dist/lib/decide.js +2 -2
  7. package/dist/lib/defaults.json +2 -0
  8. package/dist/lib/init.js +7 -7
  9. package/dist/lib/merge.js +1 -1
  10. package/dist/lib/prompt.js +1 -1
  11. package/dist/lib/run/index.js +7 -7
  12. package/dist/lib/run/prompt.js +1 -1
  13. package/dist/lib/sandbox/capture.js +5 -5
  14. package/dist/lib/sandbox/clipboard/bridge.js +48 -13
  15. package/dist/lib/sandbox/clipboard/index.js +5 -6
  16. package/dist/lib/sandbox/clipboard/linux.js +91 -0
  17. package/dist/lib/sandbox/clipboard/paths.js +1 -1
  18. package/dist/lib/sandbox/clipboard/win32.js +144 -0
  19. package/dist/lib/sandbox/commands/create.js +101 -66
  20. package/dist/lib/sandbox/commands/enter.js +9 -9
  21. package/dist/lib/sandbox/commands/list-running.js +2 -2
  22. package/dist/lib/sandbox/commands/ls.js +7 -7
  23. package/dist/lib/sandbox/commands/prune.js +8 -8
  24. package/dist/lib/sandbox/commands/rebuild.js +38 -50
  25. package/dist/lib/sandbox/commands/refresh.js +2 -2
  26. package/dist/lib/sandbox/commands/rm.js +11 -11
  27. package/dist/lib/sandbox/commands/show.js +4 -4
  28. package/dist/lib/sandbox/commands/start.js +4 -4
  29. package/dist/lib/sandbox/commands/vm.js +4 -4
  30. package/dist/lib/sandbox/config.js +11 -5
  31. package/dist/lib/sandbox/constants.js +4 -1
  32. package/dist/lib/sandbox/credentials.js +1 -1
  33. package/dist/lib/sandbox/dotfiles.js +1 -1
  34. package/dist/lib/sandbox/engine.js +3 -3
  35. package/dist/lib/sandbox/engines/index.js +5 -5
  36. package/dist/lib/sandbox/engines/wsl2-paths.js +1 -1
  37. package/dist/lib/sandbox/image-build.js +80 -0
  38. package/dist/lib/sandbox/image-prune.js +2 -2
  39. package/dist/lib/sandbox/index.js +10 -10
  40. package/dist/lib/sandbox/managed-fs.js +1 -1
  41. package/dist/lib/sandbox/readme-scaffold.js +1 -1
  42. package/dist/lib/sandbox/runtimes/node20.dockerfile +1 -1
  43. package/dist/lib/sandbox/runtimes/node22.dockerfile +1 -1
  44. package/dist/lib/sandbox/tools.js +11 -2
  45. package/dist/lib/server/adapters/feishu/index.js +12 -3
  46. package/dist/lib/server/adapters/feishu/renderer.js +88 -0
  47. package/dist/lib/server/adapters/feishu/transport.js +9 -2
  48. package/dist/lib/server/daemon.js +64 -43
  49. package/dist/lib/server/display.js +83 -0
  50. package/dist/lib/server/index.js +2 -2
  51. package/dist/lib/server/process-control.js +2 -2
  52. package/dist/lib/server/protocol.js +22 -6
  53. package/dist/lib/server/streamer.js +5 -4
  54. package/dist/lib/task/commands/cat.js +2 -2
  55. package/dist/lib/task/commands/decisions.js +4 -4
  56. package/dist/lib/task/commands/files.js +3 -3
  57. package/dist/lib/task/commands/grep.js +3 -3
  58. package/dist/lib/task/commands/issue-body.js +4 -4
  59. package/dist/lib/task/commands/log.js +5 -5
  60. package/dist/lib/task/commands/ls.js +3 -3
  61. package/dist/lib/task/commands/show.js +1 -1
  62. package/dist/lib/task/commands/status.js +71 -25
  63. package/dist/lib/task/index.js +9 -9
  64. package/dist/lib/task/resolve-ref.js +1 -1
  65. package/dist/lib/task/workflow-warnings.js +94 -0
  66. package/dist/lib/update.js +35 -4
  67. package/lib/defaults.json +2 -0
  68. package/lib/sandbox/clipboard/bridge.ts +50 -8
  69. package/lib/sandbox/clipboard/index.ts +5 -6
  70. package/lib/sandbox/clipboard/linux.ts +124 -0
  71. package/lib/sandbox/clipboard/win32.ts +173 -0
  72. package/lib/sandbox/commands/create.ts +123 -65
  73. package/lib/sandbox/commands/rebuild.ts +42 -54
  74. package/lib/sandbox/config.ts +14 -2
  75. package/lib/sandbox/constants.ts +4 -0
  76. package/lib/sandbox/image-build.ts +134 -0
  77. package/lib/sandbox/runtimes/node20.dockerfile +1 -1
  78. package/lib/sandbox/runtimes/node22.dockerfile +1 -1
  79. package/lib/sandbox/tools.ts +9 -0
  80. package/lib/server/adapters/_contract.ts +3 -0
  81. package/lib/server/adapters/feishu/index.ts +12 -2
  82. package/lib/server/adapters/feishu/renderer.ts +99 -0
  83. package/lib/server/adapters/feishu/transport.ts +19 -3
  84. package/lib/server/daemon.ts +74 -38
  85. package/lib/server/display.ts +136 -0
  86. package/lib/server/protocol.ts +20 -4
  87. package/lib/server/streamer.ts +5 -4
  88. package/lib/task/commands/log.ts +3 -3
  89. package/lib/task/commands/status.ts +102 -21
  90. package/lib/task/workflow-warnings.ts +121 -0
  91. package/lib/update.ts +34 -0
  92. package/package.json +3 -3
  93. package/templates/.agents/README.en.md +3 -3
  94. package/templates/.agents/README.zh-CN.md +3 -3
  95. package/templates/.agents/rules/create-issue.github.en.md +3 -3
  96. package/templates/.agents/rules/create-issue.github.zh-CN.md +3 -3
  97. package/templates/.agents/rules/issue-pr-commands.github.en.md +7 -2
  98. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +7 -2
  99. package/templates/.agents/rules/issue-sync.github.en.md +13 -0
  100. package/templates/.agents/rules/issue-sync.github.zh-CN.md +13 -0
  101. package/templates/.agents/rules/next-step-output.en.md +15 -1
  102. package/templates/.agents/rules/next-step-output.zh-CN.md +15 -1
  103. package/templates/.agents/rules/pr-sync.github.en.md +17 -2
  104. package/templates/.agents/rules/pr-sync.github.zh-CN.md +17 -2
  105. package/templates/.agents/rules/task-management.en.md +1 -1
  106. package/templates/.agents/rules/task-management.zh-CN.md +1 -1
  107. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +57 -0
  108. package/templates/.agents/scripts/validate-artifact.js +120 -0
  109. package/templates/.agents/scripts/workflow-warnings.js +290 -0
  110. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +118 -0
  111. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +118 -0
  112. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +49 -0
  113. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +49 -0
  114. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +48 -0
  115. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +48 -0
  116. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +60 -0
  117. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +87 -0
  118. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -0
  119. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -0
  120. package/templates/.agents/skills/create-task/SKILL.en.md +4 -1
  121. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -1
  122. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +33 -1
  123. package/templates/.agents/templates/task.en.md +7 -0
  124. package/templates/.agents/templates/task.zh-CN.md +7 -0
  125. package/templates/.claude/commands/complete-manual-validation.en.md +9 -0
  126. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +9 -0
  127. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +8 -0
  128. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +8 -0
  129. package/templates/.github/workflows/metadata-sync.yml +5 -0
  130. package/templates/.opencode/commands/complete-manual-validation.en.md +11 -0
  131. package/templates/.opencode/commands/complete-manual-validation.zh-CN.md +11 -0
@@ -1,5 +1,5 @@
1
- import { VERSION } from "../version.js";
2
- import { getSkillRunSpec } from "../run/skills.js";
1
+ import { VERSION } from '../version.js';
2
+ import { getSkillRunSpec } from '../run/skills.js';
3
3
  const TASK_READ = new Set(['ls', 'list', 'status', 'show', 'log', 'decisions']);
4
4
  const SANDBOX_READ = new Set(['ls', 'list', 'show']);
5
5
  const SANDBOX_WRITE = new Set(['create', 'start']);
@@ -12,10 +12,26 @@ function taskSubcommand(subcommand) {
12
12
  export function commandHelp() {
13
13
  return [
14
14
  `agent-infra ${VERSION}`,
15
- 'Built-ins: /help, /ping, /version',
16
- 'Read: /sandbox ls|show|vm status, /task decisions|log|ls|show|status',
17
- 'Write: /sandbox create|start',
18
- 'Exec: /decide <task-ref> <HD-id> <decision>, /run create-task <description>, /run <skill> <task-ref> ...'
15
+ 'Built-ins:',
16
+ '/help',
17
+ '/ping',
18
+ '/version',
19
+ 'Read:',
20
+ '/sandbox ls',
21
+ '/sandbox show {ref}',
22
+ '/sandbox vm status',
23
+ '/task decisions {ref}',
24
+ '/task log {ref}',
25
+ '/task ls',
26
+ '/task show {ref}',
27
+ '/task status {ref}',
28
+ 'Write:',
29
+ '/sandbox create {ref}',
30
+ '/sandbox start {ref}',
31
+ 'Exec:',
32
+ '/decide {task-ref} {HD-id} {decision}',
33
+ '/run create-task {description}',
34
+ '/run {skill} {task-ref}'
19
35
  ].join('\n');
20
36
  }
21
37
  export function parseCommand(text) {
@@ -1,4 +1,5 @@
1
- import { redactSecrets } from "./redact.js";
1
+ import { streamEvent } from './display.js';
2
+ import { redactSecrets } from './redact.js';
2
3
  function chunks(text, size) {
3
4
  if (text.length === 0)
4
5
  return [];
@@ -9,7 +10,7 @@ function chunks(text, size) {
9
10
  return out;
10
11
  }
11
12
  export async function streamCommand(options, run, send) {
12
- await send(`started ${options.title}`);
13
+ await send(streamEvent(options.title, 'started'));
13
14
  const size = options.chunkChars ?? 4000;
14
15
  const throttleMs = options.throttleMs ?? 0;
15
16
  let streamed = false;
@@ -22,7 +23,7 @@ export async function streamCommand(options, run, send) {
22
23
  buffer = '';
23
24
  lastFlush = Date.now();
24
25
  for (const chunk of chunks(text, size)) {
25
- await send(chunk);
26
+ await send(streamEvent(options.title, 'chunk', chunk));
26
27
  }
27
28
  };
28
29
  const emit = async (chunk) => {
@@ -38,7 +39,7 @@ export async function streamCommand(options, run, send) {
38
39
  buffer += [result.stdout, result.stderr].filter(Boolean).join('\n');
39
40
  }
40
41
  await flush();
41
- await send(`finished ${options.title} exitCode=${result.exitCode} signal=${result.signal ?? 'null'}`);
42
+ await send(streamEvent(options.title, 'finished', undefined, result.exitCode, result.signal));
42
43
  return result;
43
44
  }
44
45
  //# sourceMappingURL=streamer.js.map
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
- import { resolveTaskRef } from "../resolve-ref.js";
3
- import { resolveArtifact } from "../artifacts.js";
2
+ import { resolveTaskRef } from '../resolve-ref.js';
3
+ import { resolveArtifact } from '../artifacts.js';
4
4
  const USAGE = `Usage: ai task cat <N | #N | TASK-id> <artifact | N>
5
5
 
6
6
  Prints a task artifact's raw content to stdout.
@@ -1,9 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { formatTable } from "../../table.js";
4
- import { resolveTaskRef } from "../resolve-ref.js";
5
- import { parseLedger, HUMAN_DECISION_STATUSES } from "../ledger.js";
6
- import { extractSubSection } from "../sections.js";
3
+ import { formatTable } from '../../table.js';
4
+ import { resolveTaskRef } from '../resolve-ref.js';
5
+ import { parseLedger, HUMAN_DECISION_STATUSES } from '../ledger.js';
6
+ import { extractSubSection } from '../sections.js';
7
7
  const USAGE = `Usage: ai task decisions <N | #N | TASK-id> [selector] [options]
8
8
 
9
9
  Lists the human-decision (HD-) items recorded in a task's review disagreement
@@ -1,6 +1,6 @@
1
- import { formatTable } from "../../table.js";
2
- import { resolveTaskRef } from "../resolve-ref.js";
3
- import { enumerateArtifacts } from "../artifacts.js";
1
+ import { formatTable } from '../../table.js';
2
+ import { resolveTaskRef } from '../resolve-ref.js';
3
+ import { enumerateArtifacts } from '../artifacts.js';
4
4
  const USAGE = `Usage: ai task files <N | #N | TASK-id>
5
5
 
6
6
  Lists the artifacts in a task directory with stable numbers.
@@ -1,8 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { resolveTaskRef, detectRepoRoot, enumerateTaskDirs } from "../resolve-ref.js";
4
- import { enumerateArtifacts, resolveArtifact } from "../artifacts.js";
5
- import { loadShortIdByTaskId } from "../short-id.js";
3
+ import { resolveTaskRef, detectRepoRoot, enumerateTaskDirs } from '../resolve-ref.js';
4
+ import { enumerateArtifacts, resolveArtifact } from '../artifacts.js';
5
+ import { loadShortIdByTaskId } from '../short-id.js';
6
6
  const USAGE = `Usage: ai task grep <pattern> [ref] [artifact | N]
7
7
 
8
8
  Literal (non-regex) line search across task artifacts.
@@ -1,8 +1,8 @@
1
1
  import fs from 'node:fs';
2
- import { resolveTaskRef } from "../resolve-ref.js";
3
- import { extractTitle } from "../frontmatter.js";
4
- import { extractSection, findSectionHeading } from "../sections.js";
5
- import { renderTemplateBody, PLACEHOLDER } from "../issue-form.js";
2
+ import { resolveTaskRef } from '../resolve-ref.js';
3
+ import { extractTitle } from '../frontmatter.js';
4
+ import { extractSection, findSectionHeading } from '../sections.js';
5
+ import { renderTemplateBody, PLACEHOLDER } from '../issue-form.js';
6
6
  const USAGE = `Usage: ai task issue-body <N | #N | TASK-id> [--template <path>]
7
7
 
8
8
  Print a deterministic Issue body extracted from a task's task.md.
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
- import { formatTable } from "../../table.js";
3
- import { resolveTaskRef } from "../resolve-ref.js";
4
- import { parseLedger, HUMAN_DECISION_STATUSES } from "../ledger.js";
2
+ import { formatTable } from '../../table.js';
3
+ import { resolveTaskRef } from '../resolve-ref.js';
4
+ import { parseLedger } from '../ledger.js';
5
5
  const USAGE = `Usage: ai task log <N | #N | TASK-id>
6
6
 
7
7
  Renders a task's activity log as a per-step status table. A step's start and
@@ -13,7 +13,7 @@ Columns: # (row) / STEP / AGENT / STARTED / DONE / NOTE
13
13
  A human-executed step shows AGENT as 'human' and, when it has no start marker,
14
14
  a '-' STARTED placeholder. Review-step NOTE also carries two human counts in
15
15
  the verdict list, right after blockers/major/minor: manual-validation
16
- and human-decision (current ledger stage total).
16
+ and human-decision (current pending ledger stage total).
17
17
  `;
18
18
  const TABLE_HEADERS = ['#', 'STEP', 'AGENT', 'STARTED', 'DONE', 'NOTE'];
19
19
  // The activity-log H2 heading is language-dependent (zh template / en template).
@@ -100,7 +100,7 @@ function pairEntries(entries) {
100
100
  function countHumanDecisionsByStage(rows) {
101
101
  const counts = new Map();
102
102
  for (const row of rows) {
103
- if (!isReviewStage(row.stage) || !HUMAN_DECISION_STATUSES.has(row.status))
103
+ if (!isReviewStage(row.stage) || row.status !== 'needs-human-decision')
104
104
  continue;
105
105
  counts.set(row.stage, (counts.get(row.stage) ?? 0) + 1);
106
106
  }
@@ -1,9 +1,9 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { execFileSync } from 'node:child_process';
4
- import { formatTable } from "../../table.js";
5
- import { parseTaskFrontmatter, extractTitle } from "../frontmatter.js";
6
- import { loadShortIdByTaskId } from "../short-id.js";
4
+ import { formatTable } from '../../table.js';
5
+ import { parseTaskFrontmatter, extractTitle } from '../frontmatter.js';
6
+ import { loadShortIdByTaskId } from '../short-id.js';
7
7
  const USAGE = `Usage: ai task ls [--all | --blocked | --completed]
8
8
 
9
9
  Lists tasks under .agents/workspace/. Defaults to active tasks only.
@@ -1,5 +1,5 @@
1
1
  import fs from 'node:fs';
2
- import { resolveTaskRef } from "../resolve-ref.js";
2
+ import { resolveTaskRef } from '../resolve-ref.js';
3
3
  const USAGE = `Usage: ai task show <N | #N | TASK-id>
4
4
 
5
5
  Prints the task.md content for the matching task.
@@ -1,12 +1,14 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { execFileSync } from 'node:child_process';
4
- import { commandForEngine } from "../../sandbox/shell.js";
5
- import { resolveTaskRef } from "../resolve-ref.js";
6
- import { enumerateArtifacts } from "../artifacts.js";
7
- import { parseTaskFrontmatter, extractTitle } from "../frontmatter.js";
8
- import { loadShortIdByTaskId } from "../short-id.js";
9
- import { parseActivityLog, pairEntries } from "./log.js";
4
+ import { commandForEngine } from '../../sandbox/shell.js';
5
+ import { resolveTaskRef } from '../resolve-ref.js';
6
+ import { enumerateArtifacts } from '../artifacts.js';
7
+ import { parseTaskFrontmatter, extractTitle } from '../frontmatter.js';
8
+ import { loadShortIdByTaskId } from '../short-id.js';
9
+ import { getOpenWorkflowWarnings, formatWorkflowWarningSummary } from '../workflow-warnings.js';
10
+ import { parseActivityLog, pairEntries } from './log.js';
11
+ import { statusCard } from '../../server/display.js';
10
12
  const USAGE = `Usage: ai task status <N | #N | TASK-id>
11
13
 
12
14
  Prints an aggregated "health check" view for a task: header, metadata,
@@ -267,6 +269,9 @@ function renderStatus(model) {
267
269
  if (model.title)
268
270
  lines.push(model.title);
269
271
  lines.push('', 'Metadata', ...renderPairs(model.metadata));
272
+ if (model.workflowWarnings.length > 0) {
273
+ lines.push('', `Workflow Warnings (${model.workflowWarnings.length} open)`, ...formatWorkflowWarningSummary(model.workflowWarnings).map((line) => ` ${line}`));
274
+ }
270
275
  lines.push('', `Artifacts (${model.artifacts.count})`);
271
276
  if (model.artifacts.groups.length === 0) {
272
277
  lines.push(' (none)');
@@ -302,6 +307,59 @@ function renderStatus(model) {
302
307
  ]));
303
308
  return lines;
304
309
  }
310
+ function modelTone(model) {
311
+ if (model.workflow.state === 'in-progress')
312
+ return model.workflow.stale === 'yes' ? 'warning' : 'running';
313
+ if (model.metadata.some(([key, value]) => key === 'status' && value === 'completed'))
314
+ return 'success';
315
+ if (model.metadata.some(([key, value]) => key === 'status' && (value === 'blocked' || value === 'cancelled'))) {
316
+ return 'danger';
317
+ }
318
+ return 'info';
319
+ }
320
+ function buildFromResolved(input) {
321
+ const content = fs.readFileSync(input.taskMdPath, 'utf8');
322
+ const fm = parseTaskFrontmatter(content);
323
+ const run = input.run ?? makeRunner(input.repoRoot);
324
+ const artifacts = enumerateArtifacts(input.taskDir);
325
+ const workflow = collectWorkflow(content, input.now);
326
+ return {
327
+ taskId: input.taskId,
328
+ shortId: input.shortId ?? loadShortIdByTaskId(input.repoRoot).get(input.taskId) ?? DASH,
329
+ title: extractTitle(content),
330
+ metadata: collectMetadata(fm),
331
+ workflowWarnings: getOpenWorkflowWarnings(content),
332
+ artifacts: { count: artifacts.length, groups: groupArtifacts(artifacts) },
333
+ workflow,
334
+ runtime: collectRuntime(input.taskDir, workflow, run),
335
+ git: collectGit(fm.branch ?? '', run)
336
+ };
337
+ }
338
+ function buildStatusModel(refOrInput, options = {}) {
339
+ if (typeof refOrInput !== 'string') {
340
+ return buildFromResolved(refOrInput);
341
+ }
342
+ const resolved = resolveTaskRef(refOrInput);
343
+ if (!resolved.ok) {
344
+ throw new Error(resolved.message);
345
+ }
346
+ return buildFromResolved({
347
+ taskId: resolved.taskId,
348
+ taskDir: resolved.taskDir,
349
+ taskMdPath: resolved.taskMdPath,
350
+ repoRoot: resolved.repoRoot,
351
+ run: options.run,
352
+ now: options.now
353
+ });
354
+ }
355
+ function statusModelToDisplay(model) {
356
+ return statusCard(`Task ${model.taskId} (${model.shortId})`, modelTone(model), [
357
+ ['workflow', model.workflow.state],
358
+ ['step', model.workflow.step],
359
+ ['runtime', model.runtime.status],
360
+ ['git', model.git.uncommitted]
361
+ ], renderStatus(model).join('\n'));
362
+ }
305
363
  function status(args = []) {
306
364
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
307
365
  process.stdout.write(USAGE);
@@ -309,30 +367,18 @@ function status(args = []) {
309
367
  process.exitCode = 1;
310
368
  return;
311
369
  }
312
- const resolved = resolveTaskRef(args[0]);
313
- if (!resolved.ok) {
314
- process.stderr.write(`ai task status: ${resolved.message}\n`);
370
+ let model;
371
+ try {
372
+ model = buildStatusModel(args[0]);
373
+ }
374
+ catch (error) {
375
+ process.stderr.write(`ai task status: ${error instanceof Error ? error.message : String(error)}\n`);
315
376
  process.exitCode = 1;
316
377
  return;
317
378
  }
318
- const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
319
- const fm = parseTaskFrontmatter(content);
320
- const run = makeRunner(resolved.repoRoot);
321
- const artifacts = enumerateArtifacts(resolved.taskDir);
322
- const workflow = collectWorkflow(content);
323
- const model = {
324
- taskId: resolved.taskId,
325
- shortId: loadShortIdByTaskId(resolved.repoRoot).get(resolved.taskId) ?? DASH,
326
- title: extractTitle(content),
327
- metadata: collectMetadata(fm),
328
- artifacts: { count: artifacts.length, groups: groupArtifacts(artifacts) },
329
- workflow,
330
- runtime: collectRuntime(resolved.taskDir, workflow, run),
331
- git: collectGit(fm.branch ?? '', run)
332
- };
333
379
  for (const line of renderStatus(model)) {
334
380
  process.stdout.write(`${line}\n`);
335
381
  }
336
382
  }
337
- export { status, makeRunner, collectMetadata, groupArtifacts, collectGit, collectWorkflow, collectRuntime, renderStatus, METADATA_KEYS };
383
+ export { status, makeRunner, buildStatusModel, collectMetadata, groupArtifacts, collectGit, collectWorkflow, collectRuntime, renderStatus, statusModelToDisplay, METADATA_KEYS };
338
384
  //# sourceMappingURL=status.js.map
@@ -41,48 +41,48 @@ export async function runTask(args) {
41
41
  }
42
42
  switch (subcommand) {
43
43
  case 'cat': {
44
- const { cat } = await import("./commands/cat.js");
44
+ const { cat } = await import('./commands/cat.js');
45
45
  cat(rest);
46
46
  break;
47
47
  }
48
48
  case 'decisions':
49
49
  case 'd': {
50
- const { decisions } = await import("./commands/decisions.js");
50
+ const { decisions } = await import('./commands/decisions.js');
51
51
  decisions(rest);
52
52
  break;
53
53
  }
54
54
  case 'files': {
55
- const { files } = await import("./commands/files.js");
55
+ const { files } = await import('./commands/files.js');
56
56
  files(rest);
57
57
  break;
58
58
  }
59
59
  case 'grep': {
60
- const { grep } = await import("./commands/grep.js");
60
+ const { grep } = await import('./commands/grep.js');
61
61
  grep(rest);
62
62
  break;
63
63
  }
64
64
  case 'issue-body': {
65
- const { issueBody } = await import("./commands/issue-body.js");
65
+ const { issueBody } = await import('./commands/issue-body.js');
66
66
  issueBody(rest);
67
67
  break;
68
68
  }
69
69
  case 'log': {
70
- const { log } = await import("./commands/log.js");
70
+ const { log } = await import('./commands/log.js');
71
71
  log(rest);
72
72
  break;
73
73
  }
74
74
  case 'ls': {
75
- const { ls } = await import("./commands/ls.js");
75
+ const { ls } = await import('./commands/ls.js');
76
76
  ls(rest);
77
77
  break;
78
78
  }
79
79
  case 'show': {
80
- const { show } = await import("./commands/show.js");
80
+ const { show } = await import('./commands/show.js');
81
81
  show(rest);
82
82
  break;
83
83
  }
84
84
  case 'status': {
85
- const { status } = await import("./commands/status.js");
85
+ const { status } = await import('./commands/status.js');
86
86
  status(rest);
87
87
  break;
88
88
  }
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { execFileSync, spawnSync } from 'node:child_process';
4
- import { normalizeShortIdInput } from "./short-id.js";
4
+ import { normalizeShortIdInput } from './short-id.js';
5
5
  const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
6
6
  // Flat-structured workspace dirs that hold tasks under `{dir}/{taskId}/task.md`.
7
7
  // Note: `archive` uses a three-level YYYY/MM/DD layout and is handled separately.
@@ -0,0 +1,94 @@
1
+ import { extractSection } from './sections.js';
2
+ const WORKFLOW_WARNING_HEADINGS = ['工作流告警', 'Workflow Warnings'];
3
+ const WORKFLOW_WARNING_STATUSES = new Set(['open', 'resolved', 'ignored']);
4
+ const WORKFLOW_WARNING_SEVERITIES = new Set(['IMPORTANT', 'ACTION_REQUIRED']);
5
+ function splitTableRow(line) {
6
+ let value = line.trim();
7
+ if (!value.startsWith('|'))
8
+ return [];
9
+ value = value.replace(/^\|/, '').replace(/\|$/, '');
10
+ const cells = [];
11
+ let cell = '';
12
+ for (let index = 0; index < value.length; index += 1) {
13
+ const char = value[index];
14
+ if (char === '|' && !isEscapedAt(value, index)) {
15
+ cells.push(unescapeTableCell(cell.trim()));
16
+ cell = '';
17
+ continue;
18
+ }
19
+ cell += char;
20
+ }
21
+ cells.push(unescapeTableCell(cell.trim()));
22
+ return cells;
23
+ }
24
+ function unescapeTableCell(value) {
25
+ let output = '';
26
+ for (let index = 0; index < value.length; index += 1) {
27
+ const char = value[index];
28
+ const next = value[index + 1];
29
+ if (char === '\\' && (next === '\\' || next === '|')) {
30
+ output += next;
31
+ index += 1;
32
+ continue;
33
+ }
34
+ output += char;
35
+ }
36
+ return output;
37
+ }
38
+ function isEscapedAt(value, index) {
39
+ let backslashes = 0;
40
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === '\\'; cursor -= 1) {
41
+ backslashes += 1;
42
+ }
43
+ return backslashes % 2 === 1;
44
+ }
45
+ function isSeparatorRow(cells) {
46
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
47
+ }
48
+ /**
49
+ * Parses well-formed workflow warning rows. Malformed rows are intentionally
50
+ * ignored here; validate-artifact is responsible for reporting structure errors.
51
+ */
52
+ function parseWorkflowWarnings(content) {
53
+ const section = extractSection(content, WORKFLOW_WARNING_HEADINGS);
54
+ if (!section)
55
+ return [];
56
+ const rows = [];
57
+ for (const rawLine of section.split(/\r?\n/)) {
58
+ const cells = splitTableRow(rawLine);
59
+ if (cells.length === 0)
60
+ continue;
61
+ if ((cells[0] ?? '').toLowerCase() === 'id')
62
+ continue;
63
+ if (isSeparatorRow(cells))
64
+ continue;
65
+ if (cells.length < 11)
66
+ continue;
67
+ rows.push({
68
+ id: cells[0] ?? '',
69
+ time: cells[1] ?? '',
70
+ step: cells[2] ?? '',
71
+ severity: cells[3] ?? '',
72
+ code: cells[4] ?? '',
73
+ status: cells[5] ?? '',
74
+ target: cells[6] ?? '',
75
+ message: cells[7] ?? '',
76
+ action: cells[8] ?? '',
77
+ resolvedAt: cells[9] ?? '',
78
+ resolution: cells[10] ?? ''
79
+ });
80
+ }
81
+ return rows;
82
+ }
83
+ function getOpenWorkflowWarnings(content) {
84
+ return parseWorkflowWarnings(content).filter((warning) => warning.status === 'open');
85
+ }
86
+ function formatWorkflowWarningSummary(warnings) {
87
+ return warnings.map((warning) => {
88
+ const target = warning.target ? ` ${warning.target}` : '';
89
+ const action = warning.action ? ` - ${warning.action}` : '';
90
+ return `${warning.id} [${warning.severity}] ${warning.code}${target}${action}`;
91
+ });
92
+ }
93
+ export { WORKFLOW_WARNING_HEADINGS, WORKFLOW_WARNING_STATUSES, WORKFLOW_WARNING_SEVERITIES, parseWorkflowWarnings, getOpenWorkflowWarnings, formatWorkflowWarningSummary };
94
+ //# sourceMappingURL=workflow-warnings.js.map
@@ -1,12 +1,15 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { info, ok, err } from "./log.js";
4
- import { resolveTemplateDir } from "./paths.js";
5
- import { renderFile, copySkillDir, KNOWN_PLATFORMS } from "./render.js";
6
- import { isPathOwnedByDisabledTUI, resolveEnabledTUIs } from "./builtin-tuis.js";
3
+ import { info, ok, err } from './log.js';
4
+ import { resolveTemplateDir } from './paths.js';
5
+ import { renderFile, copySkillDir, KNOWN_PLATFORMS } from './render.js';
6
+ import { isPathOwnedByDisabledTUI, resolveEnabledTUIs } from './builtin-tuis.js';
7
7
  const defaults = JSON.parse(fs.readFileSync(new URL('./defaults.json', import.meta.url), 'utf8'));
8
8
  const CONFIG_DIR = '.agents';
9
9
  const CONFIG_PATH = path.join(CONFIG_DIR, '.airc.json');
10
+ const AGENT_INFRA_SANDBOX_TOOL = 'agent-infra';
11
+ const LEGACY_DEFAULT_SANDBOX_TOOLS = ['claude-code', 'codex', 'gemini-cli', 'opencode'];
12
+ const DEFAULT_SANDBOX_TOOLS = [AGENT_INFRA_SANDBOX_TOOL, ...LEGACY_DEFAULT_SANDBOX_TOOLS];
10
13
  // One-time migration of the legacy project-level PR switch to the three-state
11
14
  // `prFlow` preference. `true` (the old default / "PR flow on") maps to the
12
15
  // strong constraint `required`; `false` maps to `disabled`. A missing or
@@ -25,6 +28,24 @@ function migratePrFlow(config) {
25
28
  }
26
29
  return null;
27
30
  }
31
+ function isLegacyDefaultSandboxTools(value) {
32
+ if (!Array.isArray(value) || value.length !== LEGACY_DEFAULT_SANDBOX_TOOLS.length) {
33
+ return false;
34
+ }
35
+ const tools = new Set(value);
36
+ return LEGACY_DEFAULT_SANDBOX_TOOLS.every((tool) => tools.has(tool));
37
+ }
38
+ function migrateSandboxTools(config) {
39
+ const tools = config.sandbox?.tools;
40
+ if (!isLegacyDefaultSandboxTools(tools)) {
41
+ return false;
42
+ }
43
+ config.sandbox = {
44
+ ...config.sandbox,
45
+ tools: [...DEFAULT_SANDBOX_TOOLS]
46
+ };
47
+ return true;
48
+ }
28
49
  function isPathOwnedByOtherPlatform(relativePath, platformType) {
29
50
  const top = String(relativePath || '').replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0] ?? '';
30
51
  if (!top.startsWith('.'))
@@ -153,6 +174,7 @@ async function cmdUpdate() {
153
174
  const taskAdded = !config.task;
154
175
  const labelsAdded = !config.labels;
155
176
  const prFlowMigrated = migratePrFlow(config);
177
+ const sandboxToolsMigrated = !sandboxAdded && migrateSandboxTools(config);
156
178
  let configChanged = changed;
157
179
  if (platformAdded) {
158
180
  config.platform = structuredClone(defaults.platform);
@@ -173,6 +195,9 @@ async function cmdUpdate() {
173
195
  if (prFlowMigrated) {
174
196
  configChanged = true;
175
197
  }
198
+ if (sandboxToolsMigrated) {
199
+ configChanged = true;
200
+ }
176
201
  if (configChanged) {
177
202
  console.log('');
178
203
  if (hasNewEntries) {
@@ -200,6 +225,9 @@ async function cmdUpdate() {
200
225
  if (prFlowMigrated) {
201
226
  info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
202
227
  }
228
+ if (sandboxToolsMigrated) {
229
+ info(`Migrated default sandbox.tools to include ${AGENT_INFRA_SANDBOX_TOOL} in ${CONFIG_PATH}.`);
230
+ }
203
231
  }
204
232
  else {
205
233
  info(`File registry changed in ${CONFIG_PATH}.`);
@@ -219,6 +247,9 @@ async function cmdUpdate() {
219
247
  if (hasNewEntries && prFlowMigrated) {
220
248
  info(`Migrated legacy requiresPullRequest to prFlow="${prFlowMigrated}" in ${CONFIG_PATH}.`);
221
249
  }
250
+ if (hasNewEntries && sandboxToolsMigrated) {
251
+ info(`Migrated default sandbox.tools to include ${AGENT_INFRA_SANDBOX_TOOL} in ${CONFIG_PATH}.`);
252
+ }
222
253
  fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf8');
223
254
  ok(`Updated ${CONFIG_PATH}`);
224
255
  }
package/lib/defaults.json CHANGED
@@ -8,11 +8,13 @@
8
8
  "node22"
9
9
  ],
10
10
  "tools": [
11
+ "agent-infra",
11
12
  "claude-code",
12
13
  "codex",
13
14
  "gemini-cli",
14
15
  "opencode"
15
16
  ],
17
+ "refreshIntervalDays": 7,
16
18
  "dockerfile": null,
17
19
  "vm": {
18
20
  "cpu": null,