@geminix/gxpm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/AGENTS.md +148 -0
  2. package/CANON.md +53 -0
  3. package/CLAUDE.md +60 -0
  4. package/CONTEXT.md +49 -0
  5. package/DEBUG.md +59 -0
  6. package/ISSUE_CONTEXT.md +25 -0
  7. package/README.md +143 -0
  8. package/VERSION +1 -0
  9. package/agents/cleanup-auditor/cleanup-auditor.md +56 -0
  10. package/agents/grill-master.md +26 -0
  11. package/agents/implementer.md +32 -0
  12. package/agents/review-army/accessibility-reviewer.md +54 -0
  13. package/agents/review-army/code-quality-reviewer.md +54 -0
  14. package/agents/review-army/security-reviewer.md +56 -0
  15. package/agents/review-army/spec-compliance-reviewer.md +51 -0
  16. package/agents/review-army/test-reviewer.md +55 -0
  17. package/agents/reviewer.md +59 -0
  18. package/agents/ship-audit-army/docs-auditor.md +53 -0
  19. package/agents/ship-audit-army/performance-auditor.md +52 -0
  20. package/agents/ship-audit-army/security-auditor.md +52 -0
  21. package/agents/specifier.md +55 -0
  22. package/agents/triage-officer.md +27 -0
  23. package/bin/gxpm +17 -0
  24. package/bin/gxpm-browser +17 -0
  25. package/bin/gxpm-config +15 -0
  26. package/bin/gxpm-eval +13 -0
  27. package/bin/gxpm-global-discover +15 -0
  28. package/bin/gxpm-init +38 -0
  29. package/bin/gxpm-investigate +194 -0
  30. package/bin/gxpm-uninstall +15 -0
  31. package/bin/gxpm-update-check +165 -0
  32. package/commands/build.md +40 -0
  33. package/commands/help.md +53 -0
  34. package/commands/plan.md +34 -0
  35. package/commands/refine.md +46 -0
  36. package/commands/review.md +34 -0
  37. package/commands/ship.md +37 -0
  38. package/core/ac-check.ts +20 -0
  39. package/core/agent-runtime.ts +363 -0
  40. package/core/artifact-validator.ts +151 -0
  41. package/core/artifacts.ts +313 -0
  42. package/core/autopilot.ts +250 -0
  43. package/core/capabilities.ts +779 -0
  44. package/core/checkpoint.ts +370 -0
  45. package/core/cleanup.ts +32 -0
  46. package/core/command-probe.ts +82 -0
  47. package/core/config.ts +533 -0
  48. package/core/contracts/behavior-spec.schema.ts +38 -0
  49. package/core/contracts/converter.ts +61 -0
  50. package/core/contracts/host.ts +43 -0
  51. package/core/converters/converter.ts +93 -0
  52. package/core/converters/index.ts +8 -0
  53. package/core/converters/managed-artifact.ts +119 -0
  54. package/core/converters/parser.ts +159 -0
  55. package/core/converters/template-renderer.ts +35 -0
  56. package/core/converters/writer.ts +61 -0
  57. package/core/dag-executor.ts +426 -0
  58. package/core/dag-loader.ts +292 -0
  59. package/core/dag-schemas.ts +150 -0
  60. package/core/dispatch.ts +125 -0
  61. package/core/evidence.ts +148 -0
  62. package/core/gate.ts +269 -0
  63. package/core/hook-engine.ts +566 -0
  64. package/core/host-probe.ts +64 -0
  65. package/core/implement.ts +16 -0
  66. package/core/isolation-errors.ts +174 -0
  67. package/core/isolation-resolver.ts +921 -0
  68. package/core/issue-context.ts +381 -0
  69. package/core/issue-readiness.ts +457 -0
  70. package/core/issue-sync.ts +427 -0
  71. package/core/issues.ts +132 -0
  72. package/core/land.ts +108 -0
  73. package/core/orchestrator.ts +54 -0
  74. package/core/phase-artifact.ts +32 -0
  75. package/core/phase-gates.ts +130 -0
  76. package/core/phase-rewind.ts +94 -0
  77. package/core/plan-lint.ts +61 -0
  78. package/core/plan.ts +77 -0
  79. package/core/port-allocation.ts +50 -0
  80. package/core/pr-check.ts +15 -0
  81. package/core/preset-system/preset-resolver.ts +221 -0
  82. package/core/project-init-status.ts +127 -0
  83. package/core/qa.ts +15 -0
  84. package/core/resilience.ts +165 -0
  85. package/core/runs.ts +288 -0
  86. package/core/safe-path.test.ts +80 -0
  87. package/core/safe-path.ts +60 -0
  88. package/core/sdd-gate.test.ts +98 -0
  89. package/core/sdd-gate.ts +134 -0
  90. package/core/self-review.ts +62 -0
  91. package/core/session.ts +70 -0
  92. package/core/ship.ts +86 -0
  93. package/core/specify.ts +173 -0
  94. package/core/state.ts +1002 -0
  95. package/core/template-engine.ts +152 -0
  96. package/core/template-resolver.test.ts +70 -0
  97. package/core/template-resolver.ts +156 -0
  98. package/core/triage.ts +26 -0
  99. package/core/verify.ts +15 -0
  100. package/core/wiki-native.ts +2423 -0
  101. package/core/wiki.ts +27 -0
  102. package/core/workflow-event-emitter.ts +163 -0
  103. package/core/workflows/engine.ts +273 -0
  104. package/core/workflows/expressions.ts +76 -0
  105. package/core/workflows/index.ts +38 -0
  106. package/core/workflows/steps/command.ts +43 -0
  107. package/core/workflows/steps/gate.ts +47 -0
  108. package/core/workflows/steps/gxpm.ts +44 -0
  109. package/core/workflows/steps/linear.ts +31 -0
  110. package/core/workflows/steps/shell.ts +65 -0
  111. package/core/workflows/types.ts +62 -0
  112. package/core/workspace-runtime.ts +227 -0
  113. package/core/worktree-init-steps.ts +647 -0
  114. package/core/worktree-init.ts +330 -0
  115. package/core/worktree-owner.ts +143 -0
  116. package/docs/GXPM_VERIFY.md +98 -0
  117. package/docs/INSTALL_FOR_AGENTS.md +113 -0
  118. package/docs/README.md +57 -0
  119. package/docs/adr/adr-005-multi-platform-skill-converter.md +72 -0
  120. package/docs/agents/domain.md +30 -0
  121. package/docs/agents/issue-tracker.md +30 -0
  122. package/docs/agents/triage-labels.md +32 -0
  123. package/docs/architecture/gxpm-architecture-diagram.md +265 -0
  124. package/docs/architecture/gxpm-current-architecture.md +175 -0
  125. package/docs/architecture/gxpm-current-flow.md +278 -0
  126. package/docs/architecture/gxpm-replacement-architecture.md +211 -0
  127. package/docs/architecture/gxpm-target-architecture.md +449 -0
  128. package/docs/architecture/gxpm-v0-contract.md +311 -0
  129. package/docs/architecture/layered-workflow-boundaries.md +193 -0
  130. package/docs/architecture/preset-system.md +126 -0
  131. package/docs/architecture/scaffold-northstar.md +23 -0
  132. package/docs/brainstorms/2026-05-14-bdd-then-tdd-design.md +320 -0
  133. package/docs/brainstorms/README.md +22 -0
  134. package/docs/brainstorms/docs-knowledge-system-requirements.md +29 -0
  135. package/docs/governance/beta-skill-promotion.md +39 -0
  136. package/docs/governance/development-contract.md +144 -0
  137. package/docs/governance/gherkin-style.md +90 -0
  138. package/docs/governance/host-adapter.md +56 -0
  139. package/docs/governance/skill-authoring.md +87 -0
  140. package/docs/governance/skill-testing.md +356 -0
  141. package/docs/governance/template-authoring.md +53 -0
  142. package/docs/migrations/v0.2.md +51 -0
  143. package/docs/plans/README.md +23 -0
  144. package/docs/plans/bdd-then-tdd-plan.md +1767 -0
  145. package/docs/plans/docs-knowledge-system-plan.md +31 -0
  146. package/docs/plans/spec-kit-sdd-adoption-plan.md +305 -0
  147. package/docs/research/agents-md-best-practices.md +207 -0
  148. package/docs/research/archon-study.md +351 -0
  149. package/docs/research/claude-hooks-study.md +440 -0
  150. package/docs/research/codex-hooks-study.md +624 -0
  151. package/docs/research/everything-claude-code-study.md +252 -0
  152. package/docs/research/from-skills-to-layered-workflow.md +322 -0
  153. package/docs/research/gsd-study.md +69 -0
  154. package/docs/research/kimi-hooks-study.md +274 -0
  155. package/docs/research/mattpocock-skills-comparison.md +429 -0
  156. package/docs/research/mattpocock-skills-study.md +275 -0
  157. package/docs/research/oh-my-codex-study.md +279 -0
  158. package/docs/research/perplexity-agent-skills-design.md +168 -0
  159. package/docs/research/pmc-gstack-skill-study.md +122 -0
  160. package/docs/research/spec-kit-study.md +224 -0
  161. package/docs/research/superpowers-study.md +209 -0
  162. package/docs/roadmap/initial-roadmap.md +53 -0
  163. package/docs/solutions/README.md +45 -0
  164. package/docs/solutions/artifact-nesting-recovery.md +58 -0
  165. package/docs/solutions/session-context-restore-practice.md +67 -0
  166. package/docs/solutions/workflow/version-drift-recovery.md +49 -0
  167. package/docs/solutions/worktree-gate-recovery.md +62 -0
  168. package/docs/specs/README.md +28 -0
  169. package/docs/specs/claude.md +45 -0
  170. package/docs/specs/codex.md +44 -0
  171. package/docs/specs/cursor.md +44 -0
  172. package/hosts/adapters/claude.ts +29 -0
  173. package/hosts/adapters/codex.ts +27 -0
  174. package/hosts/adapters/cursor.ts +27 -0
  175. package/hosts/adapters/kimi.ts +27 -0
  176. package/hosts/claude.ts +23 -0
  177. package/hosts/codex.ts +26 -0
  178. package/hosts/cursor.ts +19 -0
  179. package/hosts/index.ts +33 -0
  180. package/hosts/registry.test.ts +52 -0
  181. package/hosts/registry.ts +57 -0
  182. package/hosts/schema.ts +58 -0
  183. package/package.json +52 -0
  184. package/scripts/browser.ts +185 -0
  185. package/scripts/cleanup.ts +142 -0
  186. package/scripts/commands/artifact.ts +115 -0
  187. package/scripts/commands/autopilot.ts +143 -0
  188. package/scripts/commands/capability.ts +57 -0
  189. package/scripts/commands/config.ts +69 -0
  190. package/scripts/commands/dag.ts +126 -0
  191. package/scripts/commands/feedback.ts +123 -0
  192. package/scripts/commands/gate.ts +291 -0
  193. package/scripts/commands/helpers.ts +126 -0
  194. package/scripts/commands/hook.ts +66 -0
  195. package/scripts/commands/init.ts +515 -0
  196. package/scripts/commands/issue.ts +825 -0
  197. package/scripts/commands/phase.ts +61 -0
  198. package/scripts/commands/preset.ts +159 -0
  199. package/scripts/commands/runtime.ts +199 -0
  200. package/scripts/commands/specify.ts +71 -0
  201. package/scripts/commands/upgrade.ts +243 -0
  202. package/scripts/commands/verify.ts +183 -0
  203. package/scripts/commands/wiki.ts +242 -0
  204. package/scripts/commands/workflow.ts +131 -0
  205. package/scripts/dev-skill.ts +55 -0
  206. package/scripts/discover-skills.ts +116 -0
  207. package/scripts/doctor.ts +410 -0
  208. package/scripts/dogfood-check.ts +125 -0
  209. package/scripts/eval-functional.ts +218 -0
  210. package/scripts/eval.ts +246 -0
  211. package/scripts/gen-skill-docs.ts +201 -0
  212. package/scripts/global-discover.ts +217 -0
  213. package/scripts/governance-check.ts +75 -0
  214. package/scripts/gxpm-check.ts +12 -0
  215. package/scripts/gxpm.ts +216 -0
  216. package/scripts/host-config.ts +62 -0
  217. package/scripts/install-claude-hooks.ts +138 -0
  218. package/scripts/install-codex-hooks.ts +271 -0
  219. package/scripts/install-hooks.ts +128 -0
  220. package/scripts/install-kimi-hooks.ts +92 -0
  221. package/scripts/install-skill.ts +184 -0
  222. package/scripts/phase-artifact-commands.ts +100 -0
  223. package/scripts/post-land-sync.ts +46 -0
  224. package/scripts/scaffold-check.ts +85 -0
  225. package/scripts/skill-naming-check.ts +78 -0
  226. package/scripts/skill-structure-check.ts +157 -0
  227. package/scripts/skills-lock-check.ts +60 -0
  228. package/scripts/sync-markdown-artifacts.ts +172 -0
  229. package/scripts/uninstall.ts +162 -0
  230. package/scripts/version.ts +47 -0
  231. package/scripts/wait-pr-ready.ts +407 -0
  232. package/skills/gxpm/SKILL.md +485 -0
  233. package/skills/gxpm/SKILL.md.tmpl +422 -0
  234. package/skills/gxpm/references/CANON.md +53 -0
  235. package/skills/gxpm/references/key-rules.md +130 -0
  236. package/skills/gxpm-architecture/SKILL.md +106 -0
  237. package/skills/gxpm-architecture/references/DEEPENING.md +37 -0
  238. package/skills/gxpm-architecture/references/INTERFACE-DESIGN.md +44 -0
  239. package/skills/gxpm-autopilot/SKILL.md +116 -0
  240. package/skills/gxpm-autopilot/SKILL.md.tmpl +107 -0
  241. package/skills/gxpm-browser/SKILL.md +105 -0
  242. package/skills/gxpm-browser/SKILL.md.tmpl +41 -0
  243. package/skills/gxpm-browser/references/commands.md +43 -0
  244. package/skills/gxpm-browser/references/evidence-path.md +20 -0
  245. package/skills/gxpm-build/SKILL.md +78 -0
  246. package/skills/gxpm-cleanup/SKILL.md +76 -0
  247. package/skills/gxpm-debug-issue/SKILL.md +39 -0
  248. package/skills/gxpm-diagnose/SKILL.md +220 -0
  249. package/skills/gxpm-diagnose/SKILL.md.tmpl +31 -0
  250. package/skills/gxpm-diagnose/references/feedback-loop.md +34 -0
  251. package/skills/gxpm-diagnose/references/feedback-loops.md +43 -0
  252. package/skills/gxpm-diagnose/references/phases.md +60 -0
  253. package/skills/gxpm-eval/SKILL.md +78 -0
  254. package/skills/gxpm-explore-codebase/SKILL.md +36 -0
  255. package/skills/gxpm-explore-codebase/scripts/summarize-communities.ts +51 -0
  256. package/skills/gxpm-feedback/SKILL.md +122 -0
  257. package/skills/gxpm-grill/SKILL.md +159 -0
  258. package/skills/gxpm-grill/SKILL.md.tmpl +77 -0
  259. package/skills/gxpm-grill/references/documentation-templates.md +56 -0
  260. package/skills/gxpm-grill/references/process.md +25 -0
  261. package/skills/gxpm-handoff/SKILL.md +112 -0
  262. package/skills/gxpm-hygiene/SKILL.md +69 -0
  263. package/skills/gxpm-implementer/SKILL.md +142 -0
  264. package/skills/gxpm-implementer/SKILL.md.tmpl +141 -0
  265. package/skills/gxpm-linear/SKILL.md +282 -0
  266. package/skills/gxpm-linear/SKILL.md.tmpl +86 -0
  267. package/skills/gxpm-linear/references/commands.md +75 -0
  268. package/skills/gxpm-linear/references/workflows.md +120 -0
  269. package/skills/gxpm-planning/SKILL.md +134 -0
  270. package/skills/gxpm-prototype/SKILL.md +64 -0
  271. package/skills/gxpm-refactor-safely/SKILL.md +62 -0
  272. package/skills/gxpm-review-army/SKILL.md +117 -0
  273. package/skills/gxpm-review-changes/SKILL.md +36 -0
  274. package/skills/gxpm-setup/SKILL.md +101 -0
  275. package/skills/gxpm-specifier/SKILL.md +135 -0
  276. package/skills/gxpm-tdd/SKILL.md +187 -0
  277. package/skills/gxpm-tdd/references/interface-design.md +23 -0
  278. package/skills/gxpm-tdd/references/mocking.md +27 -0
  279. package/skills/gxpm-tdd/references/red-green-refactor.md +61 -0
  280. package/skills/gxpm-tdd/references/troubleshooting.md +28 -0
  281. package/skills/gxpm-tdd/references/workflow.md +50 -0
  282. package/skills/gxpm-tdd/testing-anti-patterns.tmpl +304 -0
  283. package/skills/gxpm-triage/SKILL.md +160 -0
  284. package/skills/gxpm-verify/SKILL.md +107 -0
  285. package/skills/gxpm-write-skill/SKILL.md +131 -0
  286. package/skills/gxpm-zoom-out/SKILL.md +69 -0
  287. package/skills/maintain-hygiene-skills-lock/SKILL.md +54 -0
  288. package/skills/maintain-hygiene-skills-lock/SKILL.md.tmpl +53 -0
  289. package/templates/constitution-template.md +63 -0
  290. package/templates/hooks/gxpm-commit-msg +16 -0
  291. package/templates/hooks/gxpm-post-checkout +19 -0
  292. package/templates/hooks/gxpm-post-commit +7 -0
  293. package/templates/hooks/gxpm-post-merge +29 -0
  294. package/templates/hooks/gxpm-pre-commit +39 -0
  295. package/templates/hooks/gxpm-pre-push +33 -0
  296. package/templates/plan-template.md.tmpl +46 -0
  297. package/templates/spec-template.md.tmpl +63 -0
  298. package/templates/specify-stub.tmpl +22 -0
  299. package/templates/tasks-template.md.tmpl +32 -0
@@ -0,0 +1,363 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ /**
5
+ * Agent Army Runtime —— 多角色并行审查的基础设施。
6
+ *
7
+ * 设计原则:
8
+ * - 静态文档优先:Agent 定义以 .md 存在,运行时按需加载
9
+ * - 薄封装:只负责发现、分派、合并,不侵入 Agent 内部逻辑
10
+ * - Artifact 为契约:输入/输出全部通过 artifact JSON
11
+ * - Fallback 机制:并行不支持时自动回退到串行
12
+ */
13
+
14
+ export const ARMY_PHASES = ["self-review", "cleanup", "ship", "plan"] as const;
15
+ export type ArmyPhase = (typeof ARMY_PHASES)[number];
16
+
17
+ export const SEVERITY_LEVELS = ["blocking", "important", "suggestion"] as const;
18
+ export type SeverityLevel = (typeof SEVERITY_LEVELS)[number];
19
+
20
+ export interface AgentDefinition {
21
+ name: string;
22
+ phase: ArmyPhase;
23
+ army: string;
24
+ role: string;
25
+ description: string;
26
+ inputContract: string;
27
+ outputFormat: string;
28
+ hardGates: string[];
29
+ filePath: string;
30
+ }
31
+
32
+ export interface AgentFinding {
33
+ role: string;
34
+ severity: SeverityLevel;
35
+ location?: string;
36
+ rationale: string;
37
+ recommendation: string;
38
+ }
39
+
40
+ export interface ArmyReport {
41
+ army: string;
42
+ phase: ArmyPhase;
43
+ issueId: string;
44
+ generatedAt: string;
45
+ findings: AgentFinding[];
46
+ summary: string;
47
+ status: "completed" | "partial" | "failed";
48
+ }
49
+
50
+ export interface AgentExecutionResult {
51
+ agent: AgentDefinition;
52
+ findings: AgentFinding[];
53
+ error?: string;
54
+ }
55
+
56
+ const AGENTS_DIR = "agents";
57
+ const MD_EXTENSION = ".md";
58
+
59
+ function parseAgentMarkdown(content: string, filePath: string): AgentDefinition | null {
60
+ const lines = content.split("\n");
61
+
62
+ // Parse frontmatter (simple YAML-like)
63
+ const frontmatter: Record<string, string> = {};
64
+ let inFrontmatter = false;
65
+ let frontmatterEnd = 0;
66
+
67
+ for (let i = 0; i < lines.length; i++) {
68
+ const line = lines[i];
69
+ if (line === "---") {
70
+ if (!inFrontmatter) {
71
+ inFrontmatter = true;
72
+ continue;
73
+ } else {
74
+ frontmatterEnd = i;
75
+ break;
76
+ }
77
+ }
78
+ if (inFrontmatter) {
79
+ const colonIdx = line.indexOf(":");
80
+ if (colonIdx > 0) {
81
+ const key = line.slice(0, colonIdx).trim();
82
+ const value = line.slice(colonIdx + 1).trim();
83
+ frontmatter[key] = value;
84
+ }
85
+ }
86
+ }
87
+
88
+ const name = frontmatter.name ?? "";
89
+ const description = frontmatter.description ?? "";
90
+ if (!name) return null;
91
+
92
+ // Parse sections
93
+ const sections: Record<string, string[]> = {};
94
+ let currentSection = "";
95
+
96
+ for (let i = frontmatterEnd + 1; i < lines.length; i++) {
97
+ const line = lines[i];
98
+ if (line.startsWith("## ")) {
99
+ currentSection = line.slice(3).trim().toLowerCase();
100
+ sections[currentSection] = [];
101
+ } else if (currentSection) {
102
+ sections[currentSection].push(line);
103
+ }
104
+ }
105
+
106
+ // Extract hard gates from "红旗清单" or "hard-gate" section
107
+ const hardGates: string[] = [];
108
+ const gateSection =
109
+ sections["红旗清单"] ??
110
+ sections["hard-gate"] ??
111
+ sections["hard gate"] ??
112
+ sections["hard gates"] ??
113
+ [];
114
+
115
+ for (const line of gateSection) {
116
+ const trimmed = line.trim();
117
+ if (trimmed.startsWith("- ") || trimmed.startsWith("* ")) {
118
+ hardGates.push(trimmed.slice(2).trim());
119
+ }
120
+ }
121
+
122
+ // Extract input/output from sections
123
+ const inputContract = sections["输入"]?.join("\n").trim() ??
124
+ sections["input"]?.join("\n").trim() ??
125
+ "";
126
+
127
+ const outputFormat = sections["输出"]?.join("\n").trim() ??
128
+ sections["output"]?.join("\n").trim() ??
129
+ "";
130
+
131
+ // Derive army and phase from file path: agents/<army>/<name>.md
132
+ const pathParts = filePath.split("/");
133
+ const army = pathParts.length >= 3 ? pathParts[pathParts.length - 2] : "";
134
+
135
+ // Map army name to phase
136
+ let phase: ArmyPhase = "self-review";
137
+ if (army.includes("ship") || army.includes("audit")) {
138
+ phase = "ship";
139
+ } else if (army.includes("plan")) {
140
+ phase = "plan";
141
+ }
142
+
143
+ return {
144
+ name,
145
+ phase,
146
+ army,
147
+ role: frontmatter.role ?? name,
148
+ description,
149
+ inputContract,
150
+ outputFormat,
151
+ hardGates,
152
+ filePath,
153
+ };
154
+ }
155
+
156
+ export class AgentRegistry {
157
+ private agents: Map<string, AgentDefinition> = new Map();
158
+
159
+ discover(root: string = process.cwd()): AgentDefinition[] {
160
+ const agentsDir = join(root, AGENTS_DIR);
161
+ if (!existsSync(agentsDir)) return [];
162
+
163
+ const results: AgentDefinition[] = [];
164
+ const entries = readdirSync(agentsDir, { withFileTypes: true });
165
+
166
+ for (const entry of entries) {
167
+ if (entry.isDirectory() && entry.name.endsWith("-army")) {
168
+ const armyDir = join(agentsDir, entry.name);
169
+ const files = readdirSync(armyDir);
170
+ for (const file of files.sort()) {
171
+ if (!file.endsWith(MD_EXTENSION)) continue;
172
+ const filePath = join(armyDir, file);
173
+ const content = readFileSync(filePath, "utf-8");
174
+ const agent = parseAgentMarkdown(content, filePath);
175
+ if (agent) {
176
+ this.agents.set(agent.name, agent);
177
+ results.push(agent);
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ return results;
184
+ }
185
+
186
+ get(name: string): AgentDefinition | undefined {
187
+ return this.agents.get(name);
188
+ }
189
+
190
+ listByPhase(phase: ArmyPhase): AgentDefinition[] {
191
+ return Array.from(this.agents.values())
192
+ .filter((a) => a.phase === phase)
193
+ .sort((a, b) => a.name.localeCompare(b.name));
194
+ }
195
+
196
+ listByArmy(army: string): AgentDefinition[] {
197
+ return Array.from(this.agents.values())
198
+ .filter((a) => a.army === army)
199
+ .sort((a, b) => a.name.localeCompare(b.name));
200
+ }
201
+
202
+ clear(): void {
203
+ this.agents.clear();
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Detect whether the current runtime supports true parallel subagent execution.
209
+ * In Kimi CLI, subagent spawning may not be available.
210
+ */
211
+ export function detectParallelSupport(): boolean {
212
+ // For now, we check if we're in an environment that supports Agent tool
213
+ // This can be enhanced with actual capability probing
214
+ return typeof process.env.KIMI_CLI_VERSION !== "undefined" ||
215
+ (typeof process.env.CLAUDE_CODE !== "undefined");
216
+ }
217
+
218
+ export interface ExecuteArmyInput {
219
+ issueId: string;
220
+ army: string;
221
+ phase: ArmyPhase;
222
+ agents: AgentDefinition[];
223
+ context?: Record<string, unknown>;
224
+ }
225
+
226
+ /**
227
+ * Execute a set of agents and produce a merged report.
228
+ *
229
+ * If parallel support is available, agents run concurrently.
230
+ * Otherwise, they run sequentially with a warning.
231
+ */
232
+ export async function executeArmy(input: ExecuteArmyInput): Promise<ArmyReport> {
233
+ const parallel = detectParallelSupport();
234
+ const results: AgentExecutionResult[] = [];
235
+
236
+ if (!parallel && input.agents.length > 1) {
237
+ console.warn(
238
+ `parallel execution not supported, falling back to sequential for ${input.army}`,
239
+ );
240
+ }
241
+
242
+ if (parallel) {
243
+ // Parallel execution
244
+ const promises = input.agents.map((agent) => executeAgent(agent, input));
245
+ const settled = await Promise.allSettled(promises);
246
+ for (let i = 0; i < settled.length; i++) {
247
+ const result = settled[i];
248
+ if (result.status === "fulfilled") {
249
+ results.push(result.value);
250
+ } else {
251
+ results.push({
252
+ agent: input.agents[i],
253
+ findings: [],
254
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
255
+ });
256
+ }
257
+ }
258
+ } else {
259
+ // Sequential fallback
260
+ for (const agent of input.agents) {
261
+ try {
262
+ const result = await executeAgent(agent, input);
263
+ results.push(result);
264
+ } catch (err) {
265
+ results.push({
266
+ agent,
267
+ findings: [],
268
+ error: err instanceof Error ? err.message : String(err),
269
+ });
270
+ }
271
+ }
272
+ }
273
+
274
+ // Merge findings
275
+ const allFindings: AgentFinding[] = [];
276
+ for (const result of results) {
277
+ allFindings.push(...result.findings);
278
+ }
279
+
280
+ // Sort by severity: blocking first, then important, then suggestion
281
+ const severityOrder: Record<SeverityLevel, number> = {
282
+ blocking: 0,
283
+ important: 1,
284
+ suggestion: 2,
285
+ };
286
+ allFindings.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
287
+
288
+ const hasErrors = results.some((r) => r.error);
289
+ const allFailed = results.every((r) => r.error);
290
+
291
+ const summaryParts: string[] = [
292
+ `${input.army}: ${input.agents.length} role(s) executed`,
293
+ `${allFindings.length} finding(s) total`,
294
+ `${allFindings.filter((f) => f.severity === "blocking").length} blocking`,
295
+ `${allFindings.filter((f) => f.severity === "important").length} important`,
296
+ `${allFindings.filter((f) => f.severity === "suggestion").length} suggestion`,
297
+ ];
298
+
299
+ if (hasErrors) {
300
+ summaryParts.push(`${results.filter((r) => r.error).length} role(s) failed`);
301
+ }
302
+
303
+ return {
304
+ army: input.army,
305
+ phase: input.phase,
306
+ issueId: input.issueId,
307
+ generatedAt: new Date().toISOString(),
308
+ findings: allFindings,
309
+ summary: summaryParts.join(", "),
310
+ status: allFailed ? "failed" : hasErrors ? "partial" : "completed",
311
+ };
312
+ }
313
+
314
+ /**
315
+ * Execute a single agent. In a real implementation, this would spawn a subagent.
316
+ * For now, we produce a placeholder that marks the agent as "awaiting execution".
317
+ */
318
+ async function executeAgent(
319
+ agent: AgentDefinition,
320
+ input: ExecuteArmyInput,
321
+ ): Promise<AgentExecutionResult> {
322
+ // In the current architecture, actual agent execution happens via the host's
323
+ // subagent mechanism. This function returns a structural result that the
324
+ // caller can use to invoke subagents externally.
325
+ return {
326
+ agent,
327
+ findings: [],
328
+ };
329
+ }
330
+
331
+ /**
332
+ * Check if an army report contains any blocking findings.
333
+ */
334
+ export function hasBlockingFindings(report: ArmyReport): boolean {
335
+ return report.findings.some((f) => f.severity === "blocking");
336
+ }
337
+
338
+ /**
339
+ * Count findings by severity.
340
+ */
341
+ export function countFindingsBySeverity(
342
+ report: ArmyReport,
343
+ ): Record<SeverityLevel, number> {
344
+ const counts: Record<SeverityLevel, number> = {
345
+ blocking: 0,
346
+ important: 0,
347
+ suggestion: 0,
348
+ };
349
+ for (const finding of report.findings) {
350
+ counts[finding.severity]++;
351
+ }
352
+ return counts;
353
+ }
354
+
355
+ // Singleton registry for discoverability
356
+ let globalRegistry: AgentRegistry | null = null;
357
+
358
+ export function getGlobalAgentRegistry(): AgentRegistry {
359
+ if (!globalRegistry) {
360
+ globalRegistry = new AgentRegistry();
361
+ }
362
+ return globalRegistry;
363
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Artifact Validator — structure checker for gxpm issue artifacts.
3
+ *
4
+ * This validates the current `.gxpm/issues/<id>/artifacts/*.json`
5
+ * contract. It intentionally uses a small required-field surface so older
6
+ * hand-authored artifacts remain readable while new writes still expose drift.
7
+ */
8
+
9
+ import { ARTIFACT_TYPES, type ArtifactType } from "./artifacts";
10
+
11
+ export interface ValidationError {
12
+ field: string;
13
+ message: string;
14
+ }
15
+
16
+ export interface ValidationResult {
17
+ valid: boolean;
18
+ errors: ValidationError[];
19
+ }
20
+
21
+ interface ArtifactSchema {
22
+ requiredFields: string[];
23
+ }
24
+
25
+ const ARTIFACT_SCHEMAS: Record<ArtifactType, ArtifactSchema> = {
26
+ "issue-intake": {
27
+ requiredFields: [],
28
+ },
29
+ "triage-report": {
30
+ requiredFields: [],
31
+ },
32
+ "autopilot-grant": {
33
+ requiredFields: ["profile", "status", "allowedActions", "hardStops"],
34
+ },
35
+ "acceptance-contract": {
36
+ requiredFields: ["criteria"],
37
+ },
38
+ "implementation-plan": {
39
+ requiredFields: ["objective", "approach", "validation"],
40
+ },
41
+ "dispatch-handoff": {
42
+ requiredFields: ["status", "inputArtifacts", "workerTasks"],
43
+ },
44
+ "behavior-spec": {
45
+ requiredFields: ["feature", "scenarios"],
46
+ },
47
+ "wiki-context": {
48
+ requiredFields: [],
49
+ },
50
+ "local-verify": {
51
+ requiredFields: ["status", "commands", "results"],
52
+ },
53
+ "acceptance-check": {
54
+ requiredFields: ["status", "criteria", "findings"],
55
+ },
56
+ "self-review": {
57
+ requiredFields: ["status", "reviewedArtifacts", "findings"],
58
+ },
59
+ "ship-readiness": {
60
+ requiredFields: ["status", "checklist", "rollbackPlan"],
61
+ },
62
+ "pr-check": {
63
+ requiredFields: ["status", "pullRequest", "reviewFindings"],
64
+ },
65
+ "verify-findings": {
66
+ requiredFields: ["status", "findings", "risks"],
67
+ },
68
+ "qa-findings": {
69
+ requiredFields: ["status", "browserEvidence", "findings"],
70
+ },
71
+ "land-findings": {
72
+ requiredFields: ["status", "landReady", "mergePlan"],
73
+ },
74
+ "cleanup-report": {
75
+ requiredFields: ["status", "duplicatesExtracted", "renamesUnified", "interfacesAligned", "deadCodeRemoved", "testsDeduplicated"],
76
+ },
77
+ "review-report": {
78
+ requiredFields: ["status", "findings"],
79
+ },
80
+ "ship-audit-report": {
81
+ requiredFields: ["status", "findings"],
82
+ },
83
+ "feedback-description": {
84
+ requiredFields: [],
85
+ },
86
+ };
87
+
88
+ export function listValidatedArtifactTypes(): ArtifactType[] {
89
+ return [...ARTIFACT_TYPES];
90
+ }
91
+
92
+ /**
93
+ * Validate an artifact payload against its type schema.
94
+ */
95
+ export function validateArtifact(type: string, payload: Record<string, unknown>): ValidationResult {
96
+ if (!isArtifactType(type)) {
97
+ return { valid: false, errors: [{ field: "type", message: `Invalid artifact type: ${type}` }] };
98
+ }
99
+
100
+ const schema = ARTIFACT_SCHEMAS[type];
101
+ if (!schema) {
102
+ // No schema registered for this artifact type; skip validation.
103
+ return { valid: true, errors: [] };
104
+ }
105
+ const errors: ValidationError[] = [];
106
+
107
+ for (const field of schema.requiredFields) {
108
+ if (!(field in payload) || payload[field] === undefined || payload[field] === null) {
109
+ errors.push({ field, message: `Missing required field: ${field}` });
110
+ }
111
+ }
112
+
113
+ return { valid: errors.length === 0, errors };
114
+ }
115
+
116
+ /**
117
+ * Validate a raw artifact object (with schemaVersion, issueId, type, payload).
118
+ */
119
+ export function validateRawArtifact(raw: Record<string, unknown>): ValidationResult {
120
+ const type = raw.type as string;
121
+ if (!type) {
122
+ return { valid: false, errors: [{ field: "type", message: "Missing artifact type" }] };
123
+ }
124
+
125
+ if (!isArtifactType(type)) {
126
+ return { valid: false, errors: [{ field: "type", message: `Unsupported artifact type for validation: ${type}` }] };
127
+ }
128
+
129
+ const payload = raw.payload;
130
+ if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
131
+ return { valid: false, errors: [{ field: "payload", message: "Missing or invalid payload" }] };
132
+ }
133
+
134
+ return validateArtifact(type, payload as Record<string, unknown>);
135
+ }
136
+
137
+ function isArtifactType(value: string): value is ArtifactType {
138
+ return ARTIFACT_TYPES.includes(value as ArtifactType);
139
+ }
140
+
141
+ /**
142
+ * Format validation result for CLI output.
143
+ */
144
+ export function formatValidationResult(result: ValidationResult): string {
145
+ if (result.valid) return "✅ Artifact validation passed";
146
+ const lines = ["❌ Artifact validation failed:"];
147
+ for (const err of result.errors) {
148
+ lines.push(` - ${err.field}: ${err.message}`);
149
+ }
150
+ return lines.join("\n");
151
+ }