@fitlab-ai/agent-infra 0.8.2 → 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 (119) hide show
  1. package/dist/bin/cli.js +1 -1
  2. package/dist/lib/cp.js +1 -1
  3. package/dist/lib/decide.js +2 -2
  4. package/dist/lib/defaults.json +1 -0
  5. package/dist/lib/init.js +7 -7
  6. package/dist/lib/merge.js +1 -1
  7. package/dist/lib/prompt.js +1 -1
  8. package/dist/lib/run/index.js +7 -7
  9. package/dist/lib/run/prompt.js +1 -1
  10. package/dist/lib/sandbox/capture.js +5 -5
  11. package/dist/lib/sandbox/clipboard/bridge.js +48 -13
  12. package/dist/lib/sandbox/clipboard/index.js +5 -6
  13. package/dist/lib/sandbox/clipboard/linux.js +91 -0
  14. package/dist/lib/sandbox/clipboard/paths.js +1 -1
  15. package/dist/lib/sandbox/clipboard/win32.js +144 -0
  16. package/dist/lib/sandbox/commands/create.js +67 -65
  17. package/dist/lib/sandbox/commands/enter.js +9 -9
  18. package/dist/lib/sandbox/commands/list-running.js +2 -2
  19. package/dist/lib/sandbox/commands/ls.js +7 -7
  20. package/dist/lib/sandbox/commands/prune.js +8 -8
  21. package/dist/lib/sandbox/commands/rebuild.js +38 -50
  22. package/dist/lib/sandbox/commands/refresh.js +2 -2
  23. package/dist/lib/sandbox/commands/rm.js +11 -11
  24. package/dist/lib/sandbox/commands/show.js +4 -4
  25. package/dist/lib/sandbox/commands/start.js +4 -4
  26. package/dist/lib/sandbox/commands/vm.js +4 -4
  27. package/dist/lib/sandbox/config.js +10 -4
  28. package/dist/lib/sandbox/constants.js +4 -1
  29. package/dist/lib/sandbox/credentials.js +1 -1
  30. package/dist/lib/sandbox/dotfiles.js +1 -1
  31. package/dist/lib/sandbox/engine.js +3 -3
  32. package/dist/lib/sandbox/engines/index.js +5 -5
  33. package/dist/lib/sandbox/engines/wsl2-paths.js +1 -1
  34. package/dist/lib/sandbox/image-build.js +80 -0
  35. package/dist/lib/sandbox/image-prune.js +2 -2
  36. package/dist/lib/sandbox/index.js +10 -10
  37. package/dist/lib/sandbox/managed-fs.js +1 -1
  38. package/dist/lib/sandbox/readme-scaffold.js +1 -1
  39. package/dist/lib/sandbox/tools.js +2 -2
  40. package/dist/lib/server/adapters/feishu/index.js +10 -3
  41. package/dist/lib/server/adapters/feishu/renderer.js +88 -0
  42. package/dist/lib/server/adapters/feishu/transport.js +2 -13
  43. package/dist/lib/server/daemon.js +64 -43
  44. package/dist/lib/server/display.js +83 -0
  45. package/dist/lib/server/index.js +2 -2
  46. package/dist/lib/server/process-control.js +2 -2
  47. package/dist/lib/server/protocol.js +2 -2
  48. package/dist/lib/server/streamer.js +5 -4
  49. package/dist/lib/task/commands/cat.js +2 -2
  50. package/dist/lib/task/commands/decisions.js +4 -4
  51. package/dist/lib/task/commands/files.js +3 -3
  52. package/dist/lib/task/commands/grep.js +3 -3
  53. package/dist/lib/task/commands/issue-body.js +4 -4
  54. package/dist/lib/task/commands/log.js +5 -5
  55. package/dist/lib/task/commands/ls.js +3 -3
  56. package/dist/lib/task/commands/show.js +1 -1
  57. package/dist/lib/task/commands/status.js +71 -25
  58. package/dist/lib/task/index.js +9 -9
  59. package/dist/lib/task/resolve-ref.js +1 -1
  60. package/dist/lib/task/workflow-warnings.js +94 -0
  61. package/dist/lib/update.js +4 -4
  62. package/lib/defaults.json +1 -0
  63. package/lib/sandbox/clipboard/bridge.ts +50 -8
  64. package/lib/sandbox/clipboard/index.ts +5 -6
  65. package/lib/sandbox/clipboard/linux.ts +124 -0
  66. package/lib/sandbox/clipboard/win32.ts +173 -0
  67. package/lib/sandbox/commands/create.ts +84 -64
  68. package/lib/sandbox/commands/rebuild.ts +42 -54
  69. package/lib/sandbox/config.ts +13 -1
  70. package/lib/sandbox/constants.ts +4 -0
  71. package/lib/sandbox/image-build.ts +134 -0
  72. package/lib/server/adapters/_contract.ts +3 -0
  73. package/lib/server/adapters/feishu/index.ts +11 -3
  74. package/lib/server/adapters/feishu/renderer.ts +99 -0
  75. package/lib/server/adapters/feishu/transport.ts +5 -18
  76. package/lib/server/daemon.ts +74 -38
  77. package/lib/server/display.ts +136 -0
  78. package/lib/server/streamer.ts +5 -4
  79. package/lib/task/commands/log.ts +3 -3
  80. package/lib/task/commands/status.ts +102 -21
  81. package/lib/task/workflow-warnings.ts +121 -0
  82. package/package.json +2 -2
  83. package/templates/.agents/rules/create-issue.github.en.md +3 -3
  84. package/templates/.agents/rules/create-issue.github.zh-CN.md +3 -3
  85. package/templates/.agents/rules/issue-pr-commands.github.en.md +7 -2
  86. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +7 -2
  87. package/templates/.agents/rules/issue-sync.github.en.md +13 -0
  88. package/templates/.agents/rules/issue-sync.github.zh-CN.md +13 -0
  89. package/templates/.agents/rules/next-step-output.en.md +15 -1
  90. package/templates/.agents/rules/next-step-output.zh-CN.md +15 -1
  91. package/templates/.agents/rules/pr-sync.github.en.md +17 -2
  92. package/templates/.agents/rules/pr-sync.github.zh-CN.md +17 -2
  93. package/templates/.agents/rules/task-management.en.md +1 -1
  94. package/templates/.agents/rules/task-management.zh-CN.md +1 -1
  95. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +57 -0
  96. package/templates/.agents/scripts/validate-artifact.js +120 -0
  97. package/templates/.agents/scripts/workflow-warnings.js +290 -0
  98. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +118 -0
  99. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +118 -0
  100. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +49 -0
  101. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +49 -0
  102. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +48 -0
  103. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +48 -0
  104. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +60 -0
  105. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +87 -0
  106. package/templates/.agents/skills/create-pr/SKILL.en.md +2 -0
  107. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +2 -0
  108. package/templates/.agents/skills/create-task/SKILL.en.md +4 -1
  109. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +4 -1
  110. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +7 -0
  111. package/templates/.agents/templates/task.en.md +7 -0
  112. package/templates/.agents/templates/task.zh-CN.md +7 -0
  113. package/templates/.claude/commands/complete-manual-validation.en.md +9 -0
  114. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +9 -0
  115. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +8 -0
  116. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +8 -0
  117. package/templates/.github/workflows/metadata-sync.yml +5 -0
  118. package/templates/.opencode/commands/complete-manual-validation.en.md +11 -0
  119. package/templates/.opencode/commands/complete-manual-validation.zh-CN.md +11 -0
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import { formatTable } from '../../table.ts';
3
3
  import { resolveTaskRef } from '../resolve-ref.ts';
4
- import { parseLedger, HUMAN_DECISION_STATUSES, type LedgerRow } from '../ledger.ts';
4
+ import { parseLedger, type LedgerRow } from '../ledger.ts';
5
5
 
6
6
  const USAGE = `Usage: ai task log <N | #N | TASK-id>
7
7
 
@@ -14,7 +14,7 @@ Columns: # (row) / STEP / AGENT / STARTED / DONE / NOTE
14
14
  A human-executed step shows AGENT as 'human' and, when it has no start marker,
15
15
  a '-' STARTED placeholder. Review-step NOTE also carries two human counts in
16
16
  the verdict list, right after blockers/major/minor: manual-validation
17
- and human-decision (current ledger stage total).
17
+ and human-decision (current pending ledger stage total).
18
18
  `;
19
19
 
20
20
  const TABLE_HEADERS = ['#', 'STEP', 'AGENT', 'STARTED', 'DONE', 'NOTE'] as const;
@@ -108,7 +108,7 @@ function pairEntries(entries: LogEntry[]): StepRow[] {
108
108
  function countHumanDecisionsByStage(rows: LedgerRow[]): Map<ReviewStage, number> {
109
109
  const counts = new Map<ReviewStage, number>();
110
110
  for (const row of rows) {
111
- if (!isReviewStage(row.stage) || !HUMAN_DECISION_STATUSES.has(row.status)) continue;
111
+ if (!isReviewStage(row.stage) || row.status !== 'needs-human-decision') continue;
112
112
  counts.set(row.stage, (counts.get(row.stage) ?? 0) + 1);
113
113
  }
114
114
  return counts;
@@ -6,7 +6,9 @@ import { resolveTaskRef } from '../resolve-ref.ts';
6
6
  import { enumerateArtifacts, type Artifact } from '../artifacts.ts';
7
7
  import { parseTaskFrontmatter, extractTitle, type Frontmatter } from '../frontmatter.ts';
8
8
  import { loadShortIdByTaskId } from '../short-id.ts';
9
+ import { getOpenWorkflowWarnings, formatWorkflowWarningSummary, type WorkflowWarning } from '../workflow-warnings.ts';
9
10
  import { parseActivityLog, pairEntries } from './log.ts';
11
+ import { statusCard, type DisplayMessage } from '../../server/display.ts';
10
12
 
11
13
  const USAGE = `Usage: ai task status <N | #N | TASK-id>
12
14
 
@@ -322,12 +324,28 @@ type StatusModel = {
322
324
  shortId: string;
323
325
  title: string;
324
326
  metadata: [string, string][];
327
+ workflowWarnings: WorkflowWarning[];
325
328
  artifacts: { count: number; groups: { stage: string; files: string[] }[] };
326
329
  workflow: WorkflowInfo;
327
330
  runtime: RuntimeInfo;
328
331
  git: GitInfo;
329
332
  };
330
333
 
334
+ type BuildStatusModelInput = {
335
+ taskId: string;
336
+ taskDir: string;
337
+ taskMdPath: string;
338
+ repoRoot: string;
339
+ shortId?: string;
340
+ run?: Runner;
341
+ now?: Date;
342
+ };
343
+
344
+ type BuildStatusModelOptions = {
345
+ run?: Runner;
346
+ now?: Date;
347
+ };
348
+
331
349
  // Indent each label/value pair by two spaces and pad labels to a common width so
332
350
  // every section reads as an aligned "key value" block.
333
351
  function renderPairs(rows: [string, string][]): string[] {
@@ -343,6 +361,14 @@ function renderStatus(model: StatusModel): string[] {
343
361
 
344
362
  lines.push('', 'Metadata', ...renderPairs(model.metadata));
345
363
 
364
+ if (model.workflowWarnings.length > 0) {
365
+ lines.push(
366
+ '',
367
+ `Workflow Warnings (${model.workflowWarnings.length} open)`,
368
+ ...formatWorkflowWarningSummary(model.workflowWarnings).map((line) => ` ${line}`)
369
+ );
370
+ }
371
+
346
372
  lines.push('', `Artifacts (${model.artifacts.count})`);
347
373
  if (model.artifacts.groups.length === 0) {
348
374
  lines.push(' (none)');
@@ -394,36 +420,89 @@ function renderStatus(model: StatusModel): string[] {
394
420
  return lines;
395
421
  }
396
422
 
397
- function status(args: string[] = []): void {
398
- if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
399
- process.stdout.write(USAGE);
400
- if (args.length === 0) process.exitCode = 1;
401
- return;
402
- }
403
-
404
- const resolved = resolveTaskRef(args[0]!);
405
- if (!resolved.ok) {
406
- process.stderr.write(`ai task status: ${resolved.message}\n`);
407
- process.exitCode = 1;
408
- return;
423
+ function modelTone(model: StatusModel): 'info' | 'success' | 'warning' | 'danger' | 'running' {
424
+ if (model.workflow.state === 'in-progress') return model.workflow.stale === 'yes' ? 'warning' : 'running';
425
+ if (model.metadata.some(([key, value]) => key === 'status' && value === 'completed')) return 'success';
426
+ if (model.metadata.some(([key, value]) => key === 'status' && (value === 'blocked' || value === 'cancelled'))) {
427
+ return 'danger';
409
428
  }
429
+ return 'info';
430
+ }
410
431
 
411
- const content = fs.readFileSync(resolved.taskMdPath, 'utf8');
432
+ function buildFromResolved(input: BuildStatusModelInput): StatusModel {
433
+ const content = fs.readFileSync(input.taskMdPath, 'utf8');
412
434
  const fm = parseTaskFrontmatter(content);
413
- const run = makeRunner(resolved.repoRoot);
414
- const artifacts = enumerateArtifacts(resolved.taskDir);
415
- const workflow = collectWorkflow(content);
435
+ const run = input.run ?? makeRunner(input.repoRoot);
436
+ const artifacts = enumerateArtifacts(input.taskDir);
437
+ const workflow = collectWorkflow(content, input.now);
416
438
 
417
- const model: StatusModel = {
418
- taskId: resolved.taskId,
419
- shortId: loadShortIdByTaskId(resolved.repoRoot).get(resolved.taskId) ?? DASH,
439
+ return {
440
+ taskId: input.taskId,
441
+ shortId: input.shortId ?? loadShortIdByTaskId(input.repoRoot).get(input.taskId) ?? DASH,
420
442
  title: extractTitle(content),
421
443
  metadata: collectMetadata(fm),
444
+ workflowWarnings: getOpenWorkflowWarnings(content),
422
445
  artifacts: { count: artifacts.length, groups: groupArtifacts(artifacts) },
423
446
  workflow,
424
- runtime: collectRuntime(resolved.taskDir, workflow, run),
447
+ runtime: collectRuntime(input.taskDir, workflow, run),
425
448
  git: collectGit(fm.branch ?? '', run)
426
449
  };
450
+ }
451
+
452
+ function buildStatusModel(ref: string, options?: BuildStatusModelOptions): StatusModel;
453
+ function buildStatusModel(input: BuildStatusModelInput): StatusModel;
454
+ function buildStatusModel(
455
+ refOrInput: string | BuildStatusModelInput,
456
+ options: BuildStatusModelOptions = {}
457
+ ): StatusModel {
458
+ if (typeof refOrInput !== 'string') {
459
+ return buildFromResolved(refOrInput);
460
+ }
461
+
462
+ const resolved = resolveTaskRef(refOrInput);
463
+ if (!resolved.ok) {
464
+ throw new Error(resolved.message);
465
+ }
466
+
467
+ return buildFromResolved({
468
+ taskId: resolved.taskId,
469
+ taskDir: resolved.taskDir,
470
+ taskMdPath: resolved.taskMdPath,
471
+ repoRoot: resolved.repoRoot,
472
+ run: options.run,
473
+ now: options.now
474
+ });
475
+ }
476
+
477
+ function statusModelToDisplay(model: StatusModel): DisplayMessage {
478
+ return statusCard(
479
+ `Task ${model.taskId} (${model.shortId})`,
480
+ modelTone(model),
481
+ [
482
+ ['workflow', model.workflow.state],
483
+ ['step', model.workflow.step],
484
+ ['runtime', model.runtime.status],
485
+ ['git', model.git.uncommitted]
486
+ ],
487
+ renderStatus(model).join('\n')
488
+ );
489
+ }
490
+
491
+ function status(args: string[] = []): void {
492
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
493
+ process.stdout.write(USAGE);
494
+ if (args.length === 0) process.exitCode = 1;
495
+ return;
496
+ }
497
+
498
+ let model: StatusModel;
499
+ try {
500
+ model = buildStatusModel(args[0]!);
501
+ } catch (error) {
502
+ process.stderr.write(`ai task status: ${error instanceof Error ? error.message : String(error)}\n`);
503
+ process.exitCode = 1;
504
+ return;
505
+ }
427
506
 
428
507
  for (const line of renderStatus(model)) {
429
508
  process.stdout.write(`${line}\n`);
@@ -433,12 +512,14 @@ function status(args: string[] = []): void {
433
512
  export {
434
513
  status,
435
514
  makeRunner,
515
+ buildStatusModel,
436
516
  collectMetadata,
437
517
  groupArtifacts,
438
518
  collectGit,
439
519
  collectWorkflow,
440
520
  collectRuntime,
441
521
  renderStatus,
522
+ statusModelToDisplay,
442
523
  METADATA_KEYS
443
524
  };
444
- export type { Runner, GitInfo, WorkflowInfo, RuntimeInfo, StatusModel };
525
+ export type { Runner, GitInfo, WorkflowInfo, RuntimeInfo, StatusModel, BuildStatusModelInput };
@@ -0,0 +1,121 @@
1
+ import { extractSection } from './sections.ts';
2
+
3
+ const WORKFLOW_WARNING_HEADINGS = ['工作流告警', 'Workflow Warnings'];
4
+ const WORKFLOW_WARNING_STATUSES = new Set(['open', 'resolved', 'ignored']);
5
+ const WORKFLOW_WARNING_SEVERITIES = new Set(['IMPORTANT', 'ACTION_REQUIRED']);
6
+
7
+ type WorkflowWarning = {
8
+ id: string;
9
+ time: string;
10
+ step: string;
11
+ severity: string;
12
+ code: string;
13
+ status: string;
14
+ target: string;
15
+ message: string;
16
+ action: string;
17
+ resolvedAt: string;
18
+ resolution: string;
19
+ };
20
+
21
+ function splitTableRow(line: string): string[] {
22
+ let value = line.trim();
23
+ if (!value.startsWith('|')) return [];
24
+ value = value.replace(/^\|/, '').replace(/\|$/, '');
25
+
26
+ const cells: string[] = [];
27
+ let cell = '';
28
+ for (let index = 0; index < value.length; index += 1) {
29
+ const char = value[index]!;
30
+ if (char === '|' && !isEscapedAt(value, index)) {
31
+ cells.push(unescapeTableCell(cell.trim()));
32
+ cell = '';
33
+ continue;
34
+ }
35
+ cell += char;
36
+ }
37
+ cells.push(unescapeTableCell(cell.trim()));
38
+ return cells;
39
+ }
40
+
41
+ function unescapeTableCell(value: string): string {
42
+ let output = '';
43
+ for (let index = 0; index < value.length; index += 1) {
44
+ const char = value[index]!;
45
+ const next = value[index + 1];
46
+ if (char === '\\' && (next === '\\' || next === '|')) {
47
+ output += next;
48
+ index += 1;
49
+ continue;
50
+ }
51
+ output += char;
52
+ }
53
+ return output;
54
+ }
55
+
56
+ function isEscapedAt(value: string, index: number): boolean {
57
+ let backslashes = 0;
58
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === '\\'; cursor -= 1) {
59
+ backslashes += 1;
60
+ }
61
+ return backslashes % 2 === 1;
62
+ }
63
+
64
+ function isSeparatorRow(cells: string[]): boolean {
65
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
66
+ }
67
+
68
+ /**
69
+ * Parses well-formed workflow warning rows. Malformed rows are intentionally
70
+ * ignored here; validate-artifact is responsible for reporting structure errors.
71
+ */
72
+ function parseWorkflowWarnings(content: string): WorkflowWarning[] {
73
+ const section = extractSection(content, WORKFLOW_WARNING_HEADINGS);
74
+ if (!section) return [];
75
+
76
+ const rows: WorkflowWarning[] = [];
77
+ for (const rawLine of section.split(/\r?\n/)) {
78
+ const cells = splitTableRow(rawLine);
79
+ if (cells.length === 0) continue;
80
+ if ((cells[0] ?? '').toLowerCase() === 'id') continue;
81
+ if (isSeparatorRow(cells)) continue;
82
+ if (cells.length < 11) continue;
83
+
84
+ rows.push({
85
+ id: cells[0] ?? '',
86
+ time: cells[1] ?? '',
87
+ step: cells[2] ?? '',
88
+ severity: cells[3] ?? '',
89
+ code: cells[4] ?? '',
90
+ status: cells[5] ?? '',
91
+ target: cells[6] ?? '',
92
+ message: cells[7] ?? '',
93
+ action: cells[8] ?? '',
94
+ resolvedAt: cells[9] ?? '',
95
+ resolution: cells[10] ?? ''
96
+ });
97
+ }
98
+ return rows;
99
+ }
100
+
101
+ function getOpenWorkflowWarnings(content: string): WorkflowWarning[] {
102
+ return parseWorkflowWarnings(content).filter((warning) => warning.status === 'open');
103
+ }
104
+
105
+ function formatWorkflowWarningSummary(warnings: readonly WorkflowWarning[]): string[] {
106
+ return warnings.map((warning) => {
107
+ const target = warning.target ? ` ${warning.target}` : '';
108
+ const action = warning.action ? ` - ${warning.action}` : '';
109
+ return `${warning.id} [${warning.severity}] ${warning.code}${target}${action}`;
110
+ });
111
+ }
112
+
113
+ export {
114
+ WORKFLOW_WARNING_HEADINGS,
115
+ WORKFLOW_WARNING_STATUSES,
116
+ WORKFLOW_WARNING_SEVERITIES,
117
+ parseWorkflowWarnings,
118
+ getOpenWorkflowWarnings,
119
+ formatWorkflowWarningSummary
120
+ };
121
+ export type { WorkflowWarning };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.8.2",
3
+ "version": "0.8.3",
4
4
  "description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Gemini CLI, and OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -67,7 +67,7 @@
67
67
  "@types/cross-spawn": "^6.0.6",
68
68
  "@types/node": "^22.20.0",
69
69
  "@types/semver": "^7.7.1",
70
- "typescript": "~6.0"
70
+ "typescript": "~7.0"
71
71
  },
72
72
  "optionalDependencies": {
73
73
  "@lydell/node-pty": "^1.2.0-beta.12"
@@ -101,7 +101,7 @@ If the final label set is empty, omit the `--label` argument.
101
101
  | `feature`, `enhancement` | `Feature` |
102
102
  | `task`, `documentation`, `dependency-upgrade`, `chore`, `docs`, `refactor`, `refactoring`, others | `Task` |
103
103
 
104
- When applying the Issue Type, follow the "Set Issue Type" command in `.agents/rules/issue-pr-commands.md`; first call `gh api orgs/{owner}/issue-types` to list the org's actually available Types, and only set the inferred value when it is present in that list. Failure to set is non-blocking.
104
+ When applying the Issue Type, follow the "Set Issue Type" command in `.agents/rules/issue-pr-commands.md`; only when the owner type is `Organization`, first call `gh api orgs/{owner}/issue-types` to list the org's actually available Types, and only set the inferred value when it is present in that list. User repositories, failed owner type probes, and failed writes are non-blocking.
105
105
 
106
106
  #### milestone
107
107
 
@@ -149,7 +149,7 @@ After success, parse the Issue number from the output (match only the `https://.
149
149
 
150
150
  ### 6. Set Issue Type (Optional)
151
151
 
152
- Execute only when `has_push=true` and the Issue Type inferred in §4 is in the org's actually available list:
152
+ Execute only when `has_push=true`, the owner type is `Organization`, and the Issue Type inferred in §4 is in the org's actually available list:
153
153
 
154
154
  ```bash
155
155
  gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
@@ -188,4 +188,4 @@ Hand the following back to the caller `create-task`:
188
188
  - Auth failure / command unavailable: return a structured `{code: "AUTH_FAILED", message}` to `create-task`; do not modify task.md
189
189
  - Network timeout / DNS failure: `{code: "NETWORK", message}`
190
190
  - Template parsing failure, Issue number parsing failure, other anomalies: `{code: "VALIDATION", message}`
191
- - All failures keep task.md untouched; `create-task` takes the "Scenario C: failure fallback" output branch and prompts the user to retry manually or fill `issue_number` later
191
+ - All failures keep task.md untouched; `create-task` must record the structured error in `## Workflow Warnings` (`severity=ACTION_REQUIRED`, `code=ISSUE_CREATE_FAILED`, `target=issue`) before taking the "Scenario C: failure fallback" output branch and prompting the user to retry manually or fill `issue_number` later
@@ -101,7 +101,7 @@ ai task issue-body {task-id} > "{body-file}"
101
101
  | `feature`, `enhancement` | `Feature` |
102
102
  | `task`, `documentation`, `dependency-upgrade`, `chore`, `docs`, `refactor`, `refactoring` 及其它 | `Task` |
103
103
 
104
- 实际设置时按 `.agents/rules/issue-pr-commands.md` 的 "设置 Issue Type" 命令;先调 `gh api orgs/{owner}/issue-types` 列出 org 实际可用的 Type,仅当推断值在列表中时才设置;设置失败不阻断流程。
104
+ 实际设置时按 `.agents/rules/issue-pr-commands.md` 的 "设置 Issue Type" 命令;仅当 owner type 为 `Organization` 时,先调 `gh api orgs/{owner}/issue-types` 列出 org 实际可用的 Type,并且仅当推断值在列表中时才设置;个人仓库、owner type 探测失败或设置失败都不阻断流程。
105
105
 
106
106
  #### milestone
107
107
 
@@ -149,7 +149,7 @@ gh issue create -R "$upstream_repo" \
149
149
 
150
150
  ### 6. 设置 Issue Type(可选)
151
151
 
152
- 仅当 `has_push=true` §4 推断的 Issue Type 在 org 实际可用列表中时执行:
152
+ 仅当 `has_push=true`、owner type 为 `Organization`,且 §4 推断的 Issue Type 在 org 实际可用列表中时执行:
153
153
 
154
154
  ```bash
155
155
  gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
@@ -188,4 +188,4 @@ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
188
188
  - 认证失败 / 命令不可用:返回结构化错误 `{code: "AUTH_FAILED", message}` 给 `create-task`,不修改 task.md
189
189
  - 网络超时 / DNS 失败:`{code: "NETWORK", message}`
190
190
  - 模板解析失败、Issue 编号解析失败、其它异常:`{code: "VALIDATION", message}`
191
- - 任意失败均不回滚 task.md;`create-task` "场景 C 失败兜底"输出,提示用户手动重试或在事后写入 `issue_number`
191
+ - 任意失败均不回滚 task.md;`create-task` 必须把结构化错误写入 `## 工作流告警`(`severity=ACTION_REQUIRED`,`code=ISSUE_CREATE_FAILED`,`target=issue`),再走"场景 C 失败兜底"输出,提示用户手动重试或在事后写入 `issue_number`
@@ -83,11 +83,16 @@ gh issue create -R "$upstream_repo" --title "{title}" --body "{body}" --assignee
83
83
  Set the Issue Type:
84
84
 
85
85
  ```bash
86
- gh api "orgs/{owner}/issue-types" --jq '.[].name'
87
- gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
86
+ owner_type=$(gh api "repos/$upstream_repo" --jq '.owner.type // empty' 2>/dev/null || true)
87
+ if [ "$owner_type" = "Organization" ]; then
88
+ owner=${upstream_repo%%/*}
89
+ gh api "orgs/$owner/issue-types" --jq '.[].name'
90
+ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
91
+ fi
88
92
  ```
89
93
 
90
94
  - set the Issue Type only when `has_push=true`; otherwise skip and continue
95
+ - query and set Issue Type only when the owner type is `Organization`; skip and continue for user repositories or failed owner type probes
91
96
  - when changing an existing Issue Type, read `.agents/rules/issue-fields.md` and use Flow B so same-name pinned fields are migrated and fields absent from the new type are cleared
92
97
 
93
98
  ## Update Issues
@@ -83,11 +83,16 @@ gh issue create -R "$upstream_repo" --title "{title}" --body "{body}" --assignee
83
83
  设置 Issue Type:
84
84
 
85
85
  ```bash
86
- gh api "orgs/{owner}/issue-types" --jq '.[].name'
87
- gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
86
+ owner_type=$(gh api "repos/$upstream_repo" --jq '.owner.type // empty' 2>/dev/null || true)
87
+ if [ "$owner_type" = "Organization" ]; then
88
+ owner=${upstream_repo%%/*}
89
+ gh api "orgs/$owner/issue-types" --jq '.[].name'
90
+ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
91
+ fi
88
92
  ```
89
93
 
90
94
  - 仅当 `has_push=true` 时执行 Issue Type 设置;否则跳过并继续
95
+ - 仅当 owner type 为 `Organization` 时查询和设置 Issue Type;个人仓库或 owner type 探测失败时跳过并继续
91
96
  - 变更现有 Issue Type 时,先读取 `.agents/rules/issue-fields.md` 并使用流程 B,确保同名 pinned fields 迁移,且新 type 不包含的字段被清空
92
97
 
93
98
  ## Issue 更新
@@ -66,6 +66,17 @@ Key rules:
66
66
  - insufficient permission only affects direct Issue metadata writes and must not stop the skill
67
67
  - keep the existing `2>/dev/null || true` error-tolerance pattern
68
68
 
69
+ When the caller has a `{task-id}` / task directory, permission degradation or critical sync failure must be recorded in `## Workflow Warnings`:
70
+
71
+ ```bash
72
+ node .agents/scripts/workflow-warnings.js add .agents/workspace/active/{task-id} \
73
+ --step issue-sync --severity IMPORTANT --code PERMISSION_DEGRADED \
74
+ --target "{operation}" --message "{reason}" \
75
+ --action "Wait for the bot/maintainer to backfill, or rerun the workflow step after permissions are available"
76
+ ```
77
+
78
+ Comment create/update failures and exhausted network retries that affect reviewer visibility use `severity=ACTION_REQUIRED` with `code=COMMENT_SYNC_FAILED` or `NETWORK_RETRY_EXHAUSTED`.
79
+
69
80
  ## External Contributor Locking
70
81
 
71
82
  Maintainers (`has_triage=true`) are never blocked. External contributors (`has_triage=false`) must check whether the current task already has a `task` comment author on the Issue before they start.
@@ -210,6 +221,8 @@ EOF
210
221
 
211
222
  Comment publishing is not gated by `has_triage` or `has_push`.
212
223
 
224
+ If comment lookup, creation, or update fails and the caller has a task directory, record a Workflow Warning (`step=issue-sync`, `severity=ACTION_REQUIRED`, `code=COMMENT_SYNC_FAILED`, `target={file-stem}`) and surface it in the final Workflow Warnings output block.
225
+
213
226
  ## task.md Comment Sync
214
227
 
215
228
  Hidden marker:
@@ -66,6 +66,17 @@ has_push=$(printf '%s' "$repo_perms" | grep -q '"push":true' 2>/dev/null && echo
66
66
  - 权限不足只影响直接写 Issue 元数据的步骤,不中断整个技能
67
67
  - 现有 `2>/dev/null || true` 容错模式保持不变
68
68
 
69
+ 当调用方存在 `{task-id}` / task 目录时,权限降级或关键同步失败必须写入 `## 工作流告警`:
70
+
71
+ ```bash
72
+ node .agents/scripts/workflow-warnings.js add .agents/workspace/active/{task-id} \
73
+ --step issue-sync --severity IMPORTANT --code PERMISSION_DEGRADED \
74
+ --target "{operation}" --message "{reason}" \
75
+ --action "等待 bot/维护者补位,或在具备权限后重跑对应 workflow 步骤"
76
+ ```
77
+
78
+ 评论创建 / 更新失败、网络重试耗尽等影响后续 reviewer 可见性的失败使用 `severity=ACTION_REQUIRED` 和 `code=COMMENT_SYNC_FAILED` 或 `NETWORK_RETRY_EXHAUSTED`。
79
+
69
80
  ## 外部开发者锁定机制
70
81
 
71
82
  维护者(`has_triage=true`)不受限制。外部开发者(`has_triage=false`)在开始任务前,必须先检查 Issue 上是否已有当前任务的 `task` 留言作者。
@@ -210,6 +221,8 @@ EOF
210
221
 
211
222
  评论发布不受 `has_triage` / `has_push` 限制,认证用户可正常执行。
212
223
 
224
+ 若评论查询、创建或更新失败且调用方有关联任务目录,记录 Workflow Warning(`step=issue-sync`、`severity=ACTION_REQUIRED`、`code=COMMENT_SYNC_FAILED`、`target={file-stem}`),并在最终输出的 Workflow Warnings 块提示人工处理。
225
+
213
226
  ## task.md 评论同步
214
227
 
215
228
  隐藏标记:
@@ -1,10 +1,11 @@
1
1
  # Next-Step Output Rule
2
2
 
3
- This file defines three **independent** rules for a skill's "notify-user / Next steps" output (the 3rd applies to review-* only); read this file before rendering the final output and apply whichever rules apply:
3
+ This file defines four **independent** rules for a skill's "notify-user / Next steps" output (the 3rd applies to review-* only); read this file before rendering the final output and apply whichever rules apply:
4
4
 
5
5
  1. **Next-step output structure**: how "Next steps" commands and the "Task info" block present the task ID (placeholders / short-id lookup / fallback).
6
6
  2. **Agent output trailing line (Completed at)**: the **very last line** of user-facing output, **independent of the "Next steps" block**, applying to normal / error / early-return paths alike.
7
7
  3. **Pending human-decision pre-block**: applies only to `review-analysis` / `review-plan` / `review-code` when this stage has pending rulings (`{h} > 0`) — expand the pending items before the "Next steps" commands and prompt to resolve them first.
8
+ 4. **Workflow Warnings output block**: applies when task.md has `status=open` rows in `## Workflow Warnings` — print the warning summary after all normal information and "Next steps" commands, and before `Completed at`.
8
9
 
9
10
  ## Placeholder semantics
10
11
 
@@ -62,6 +63,19 @@ Completed at: YYYY-MM-DD HH:mm:ss
62
63
  - Position: it must be the last line of the entire user-facing output, after all "Next steps" commands. If a scenario has a conditional reminder line after the commands (e.g. the manual-validation reminder), the completion line goes after that reminder.
63
64
  - This line is for terminal scanning only; it is never written to any artifact file or Issue/PR comment. The single source of truth for completion time remains the Activity Log in task.md.
64
65
 
66
+ ## Workflow Warnings Output Block
67
+
68
+ If the current task's `## Workflow Warnings` / `## 工作流告警` table has any `status=open` row, the skill's final user output must append a summary block after all normal information and "Next steps" commands, and before `Completed at`. When no open warning exists, do not render this block.
69
+
70
+ Format:
71
+
72
+ ```text
73
+ [ACTION REQUIRED] Workflow warnings are open:
74
+ - WW-N {code} ({target}): {action}
75
+ ```
76
+
77
+ Use `[ACTION REQUIRED]` when any open row has `severity=ACTION_REQUIRED`; otherwise use `[IMPORTANT]`. `{code}`, `{target}`, and `{action}` come directly from the warning table columns.
78
+
65
79
  ## Pending human-decision pre-block (review-* only, when {h} > 0)
66
80
 
67
81
  This section is a **third standalone rule, co-equal with the two above**, used only by the "notify-user / report conclusion" step of `review-analysis` / `review-plan` / `review-code`.
@@ -1,10 +1,11 @@
1
1
  # 下一步输出规则
2
2
 
3
- 本文件定义 skill「告知用户 / 下一步」输出的三类**相互独立**的规则(第 3 类仅 review-* 适用);渲染最终输出前先读取本文件并落实其中适用的规则:
3
+ 本文件定义 skill「告知用户 / 下一步」输出的四类**相互独立**的规则(第 3 类仅 review-* 适用);渲染最终输出前先读取本文件并落实其中适用的规则:
4
4
 
5
5
  1. **下一步输出结构**:「下一步」命令与「任务信息」段如何呈现任务 ID 形态(占位符 / 取短号 / 回退)。
6
6
  2. **Agent 输出收尾行(Completed at)**:面向用户输出的**绝对最后一行**,**独立于「下一步」块**,正常 / 错误 / 早退路径都适用。
7
7
  3. **人工裁决待办前置块**:仅 `review-analysis` / `review-plan` / `review-code`,且本阶段存在待裁决项(`{h} > 0`)时适用——在「下一步」命令前展开待裁决项并提示先完成裁决。
8
+ 4. **Workflow Warnings 输出块**:当前 task.md 存在 `status=open` 的 `## 工作流告警` 行时适用——在所有常规信息和「下一步」命令之后、`Completed at` 之前输出告警摘要。
8
9
 
9
10
  ## 占位符语义
10
11
 
@@ -62,6 +63,19 @@ Completed at: YYYY-MM-DD HH:mm:ss
62
63
  - 位置:必须是整段面向用户输出的最后一行,排在所有「下一步」命令之后。若某场景在命令之后还有条件性提醒行(如 manual-validation 提醒),收尾行排在该提醒行之后。
63
64
  - 该行只用于终端扫视,不写入任何产物文件或 Issue/PR 评论;完成时刻的单一事实源仍是 task.md 的 Activity Log。
64
65
 
66
+ ## Workflow Warnings 输出块
67
+
68
+ 若当前任务的 `## 工作流告警` / `## Workflow Warnings` 表中存在 `status=open` 行,skill 最终输出必须在所有常规信息和「下一步」命令之后、`Completed at` 之前追加摘要块;无 open 告警时不输出本块。
69
+
70
+ 格式:
71
+
72
+ ```text
73
+ [ACTION REQUIRED] Workflow warnings are open:
74
+ - WW-N {code} ({target}): {action}
75
+ ```
76
+
77
+ `severity=ACTION_REQUIRED` 的行使用 `[ACTION REQUIRED]`;只有 `IMPORTANT` 行时使用 `[IMPORTANT]`。`{code}`、`{target}`、`{action}` 直接来自告警表对应列。
78
+
65
79
  ## 人工裁决待办前置块(review-* 专用,{h} > 0 时)
66
80
 
67
81
  本节是与上面两类规则**并列的第三类独立规则**,仅 `review-analysis` / `review-plan` / `review-code` 的「向用户汇报结论」步骤使用。
@@ -7,6 +7,7 @@ Read this file before syncing the single reviewer-facing PR summary comment.
7
7
  Current callers:
8
8
  - `create-pr`: create or update the summary comment when the PR is opened
9
9
  - `commit`: refresh the summary comment on an existing PR when needed
10
+ - `complete-manual-validation`: update the summary comment in place after manual validation passes, and write a validation artifact that later aggregation can reuse
10
11
 
11
12
  If another skill needs to refresh the PR summary later, point that skill's reference file at this rule first, then document only the skill-specific trigger and failure behavior there.
12
13
 
@@ -26,17 +27,22 @@ Aggregate the latest artifacts in the task directory:
26
27
  - `plan.md` or the latest `plan-r{N}.md`
27
28
  - `code.md` or the latest `code-r{N}.md`
28
29
  - `review-code.md` or the latest `review-code-r{N}.md`
30
+ - `manual-validation.md` or the latest `manual-validation-r{N}.md`
29
31
 
30
32
  Aggregation rules:
31
33
  - extract 2-4 self-contained technical decisions from `plan*`
32
34
  - build the review-history table from `review-code*` and `code*`
33
35
  - extract the test summary from `code*`
36
+ - if the latest `manual-validation*` artifact has a passed verdict, render the manual-validation section first as `### ✅ Manual Validation Passed`, with a concise validation time, scope, and notes summary
34
37
  - if one artifact class is missing, treat it as "no data for this stage" and continue
35
38
  - Manual verification section: include only post-code-stage checks that still require a human to execute or judge and that the AI cannot close on its own.
36
39
  - **Admission boundary**: the verification result depends on a real environment, permissions, account, external system, or human judgment, and cannot be closed by an agent rerunning tests, adding checks, or continuing the fix loop.
37
40
  - **Sources**: `review-code*` "Manual Validation Items", plus `code*` items that satisfy the boundary above.
38
41
  - **Wording**: each retained item must state at least "what to verify + location (file/change/scope) + why only a human can verify it".
39
- - **Empty rendering**: when there are no retained items, do NOT use the ⚠️ alarm style (it falsely implies a problem). Render the whole block as: heading `### ✅ No Manual Verification Needed` and a single line `No items in this change require manual confirmation.`, with no item list. Only use the `### ⚠️ Manual Verification Required` heading + item list when retained items exist.
42
+ - **Rendering priority**:
43
+ 1. the latest `manual-validation*` artifact has a passed verdict -> heading `### ✅ Manual Validation Passed` + validation summary.
44
+ 2. no passed artifact and retained items exist -> heading `### ⚠️ Manual Verification Required` + item list.
45
+ 3. no passed artifact and no retained items -> heading `### ✅ No Manual Verification Needed` and a single line `No items in this change require manual confirmation.`, with no item list.
40
46
 
41
47
  ## Comment Body Template
42
48
 
@@ -72,7 +78,7 @@ Use this canonical comment body template:
72
78
  *Generated by {agent} · Internal tracking: {task-id}*
73
79
  ```
74
80
 
75
- > Render `{manual-validation-section}` per the "manual verification section" aggregation rule above: with retained items → `### ⚠️ Manual Verification Required` heading + quote + item list; with none → `### ✅ No Manual Verification Needed` heading + a single line `No items in this change require manual confirmation.` (no ⚠️, no list).
81
+ > Render `{manual-validation-section}` per the "manual verification section" aggregation rule above: passed artifact → `### ✅ Manual Validation Passed` heading + validation summary; retained items → `### ⚠️ Manual Verification Required` heading + quote + item list; none → `### ✅ No Manual Verification Needed` heading + a single line `No items in this change require manual confirmation.` (no ⚠️, no list).
76
82
 
77
83
  ## Comment Lookup And Update
78
84
 
@@ -111,6 +117,15 @@ EOF
111
117
  | `gh api` GET/PATCH/POST fails | warn and continue; whether the current skill should block is decided by the caller |
112
118
  | `pr_number` points to a missing PR | warn with `PR #{pr-number} not found` and continue |
113
119
 
120
+ When the caller has a `{task-id}` / task directory and GET/PATCH/POST fails, record a Workflow Warning:
121
+
122
+ ```bash
123
+ node .agents/scripts/workflow-warnings.js add .agents/workspace/active/{task-id} \
124
+ --step pr-sync --severity ACTION_REQUIRED --code COMMENT_SYNC_FAILED \
125
+ --target "pr-summary" --message "{reason}" \
126
+ --action "Fix GitHub API / network issues and rerun the workflow step that triggers PR summary sync"
127
+ ```
128
+
114
129
  ## Result Reporting
115
130
 
116
131
  Return one of these normalized results so callers can reuse it in Activity Log entries or user output: