@chongyan/autospec 1.0.1

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 (243) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +472 -0
  3. package/README.md +476 -0
  4. package/bin/autospec.js +3 -0
  5. package/knowledge/README.md +144 -0
  6. package/knowledge/checklists/code.md +182 -0
  7. package/knowledge/checklists/design.md +196 -0
  8. package/knowledge/checklists/release.md +70 -0
  9. package/knowledge/checklists/requirement.md +169 -0
  10. package/knowledge/checklists/test.md +46 -0
  11. package/knowledge/config/README.en.md +44 -0
  12. package/knowledge/config/README.md +44 -0
  13. package/knowledge/config/role-composition.yaml +98 -0
  14. package/knowledge/config/role-extensions.yaml +140 -0
  15. package/knowledge/config/skill-compositions.yaml +142 -0
  16. package/knowledge/config/team-stage.yaml +95 -0
  17. package/knowledge/config/team-tasks.yaml +139 -0
  18. package/knowledge/config/team-triggers.yaml +198 -0
  19. package/knowledge/config/validation-patterns.yaml +137 -0
  20. package/knowledge/domain/README.md +115 -0
  21. package/knowledge/domain/flows/README.md +194 -0
  22. package/knowledge/domain/glossary.md +143 -0
  23. package/knowledge/domain/rules.md +138 -0
  24. package/knowledge/environment/README.en.md +36 -0
  25. package/knowledge/environment/README.md +87 -0
  26. package/knowledge/environment/component-knowledge.md +316 -0
  27. package/knowledge/environment/detection-patterns.yaml +502 -0
  28. package/knowledge/environment/middleware-knowledge.md +237 -0
  29. package/knowledge/environment/template-registry.md +321 -0
  30. package/knowledge/guides/domain-driven-design.md +345 -0
  31. package/knowledge/guides/knowledge-management.md +369 -0
  32. package/knowledge/guides/requirement-engineering.md +329 -0
  33. package/knowledge/guides/stages/ai-effect-evaluator.md +93 -0
  34. package/knowledge/guides/stages/code-implementer.md +205 -0
  35. package/knowledge/guides/stages/code-reviewer.md +111 -0
  36. package/knowledge/guides/stages/consistency-checker.md +177 -0
  37. package/knowledge/guides/stages/design-planner.md +401 -0
  38. package/knowledge/guides/stages/design-reviewer.md +83 -0
  39. package/knowledge/guides/stages/integration-test-runner.md +105 -0
  40. package/knowledge/guides/stages/release-checker.md +205 -0
  41. package/knowledge/guides/stages/requirement-analyzer.md +195 -0
  42. package/knowledge/guides/stages/requirement-reviewer.md +83 -0
  43. package/knowledge/guides/stages/security-reviewer.md +89 -0
  44. package/knowledge/guides/stages/test-context-analyzer.md +250 -0
  45. package/knowledge/guides/stages/test-generator.md +241 -0
  46. package/knowledge/guides/stages/test-planner.md +183 -0
  47. package/knowledge/guides/stages/test-reviewer.md +76 -0
  48. package/knowledge/guides/stages/unit-test-runner.md +83 -0
  49. package/knowledge/guides/support/ai-agent-analyzer.md +362 -0
  50. package/knowledge/guides/support/ai-anomaly-analyzer.md +213 -0
  51. package/knowledge/guides/support/ai-artifact-evaluator.md +192 -0
  52. package/knowledge/guides/support/ai-capability-analyzer.md +193 -0
  53. package/knowledge/guides/support/ai-component-analyzer.md +169 -0
  54. package/knowledge/guides/support/ai-data-validator.md +276 -0
  55. package/knowledge/guides/support/ai-evaluation-planner.md +374 -0
  56. package/knowledge/guides/support/ai-path-evaluator.md +274 -0
  57. package/knowledge/guides/support/ai-pipeline-evaluator.md +219 -0
  58. package/knowledge/guides/support/ai-rag-analyzer.md +339 -0
  59. package/knowledge/guides/support/ai-task-assessor.md +418 -0
  60. package/knowledge/guides/support/ai-test-diagnostics.md +133 -0
  61. package/knowledge/guides/support/complexity-assessor.md +268 -0
  62. package/knowledge/guides/support/component-discovery.md +183 -0
  63. package/knowledge/guides/support/environment-scanner.md +207 -0
  64. package/knowledge/guides/support/environment-validator.md +207 -0
  65. package/knowledge/guides/support/knowledge-generator.md +234 -0
  66. package/knowledge/guides/support/methodology-extractor.md +55 -0
  67. package/knowledge/guides/support/pipeline-protocol.md +438 -0
  68. package/knowledge/guides/support/practice-logger.md +359 -0
  69. package/knowledge/guides/support/scope-inference.md +174 -0
  70. package/knowledge/guides/support/skill-distiller.md +91 -0
  71. package/knowledge/guides/support/skill-updater.md +45 -0
  72. package/knowledge/guides/support/skill-validator.md +72 -0
  73. package/knowledge/guides/support/team-orchestrator.md +323 -0
  74. package/knowledge/guides/support/tech-stack-analyzer.md +139 -0
  75. package/knowledge/guides/support/test-runner.md +254 -0
  76. package/knowledge/guides/system-design.md +352 -0
  77. package/knowledge/organization/ai-native-team.md +318 -0
  78. package/knowledge/organization/team-metrics.md +228 -0
  79. package/knowledge/principles/constitution.md +134 -0
  80. package/knowledge/principles/core-principles.md +368 -0
  81. package/knowledge/principles/design-philosophy.md +877 -0
  82. package/knowledge/principles/evolution.md +553 -0
  83. package/knowledge/process/01-requirement.md +113 -0
  84. package/knowledge/process/02-design.md +123 -0
  85. package/knowledge/process/03-implementation.md +90 -0
  86. package/knowledge/process/04-review.md +80 -0
  87. package/knowledge/process/05-testing.md +90 -0
  88. package/knowledge/process/06-delivery.md +88 -0
  89. package/knowledge/process/README.en.md +38 -0
  90. package/knowledge/process/README.md +48 -0
  91. package/knowledge/process/ai-sdlc.md +475 -0
  92. package/knowledge/process/overview.md +319 -0
  93. package/knowledge/standards/code-review.md +876 -0
  94. package/knowledge/standards/coding-style.md +940 -0
  95. package/knowledge/standards/data-consistency.md +1085 -0
  96. package/knowledge/standards/document-versioning.md +210 -0
  97. package/knowledge/standards/risk-detection.md +186 -0
  98. package/knowledge/templates/ai-evaluation.md +150 -0
  99. package/knowledge/templates/api-design.md +117 -0
  100. package/knowledge/templates/database-design.md +132 -0
  101. package/knowledge/templates/domain-driven-design.md +321 -0
  102. package/knowledge/templates/product-proposal.md +201 -0
  103. package/knowledge/templates/system-design.md +227 -0
  104. package/knowledge/templates/task-breakdown.md +107 -0
  105. package/knowledge/templates/test-case.md +170 -0
  106. package/package.json +53 -0
  107. package/plugins/.claude-plugin/plugin.json +134 -0
  108. package/plugins/agents/roles/ai-engineer.md +129 -0
  109. package/plugins/agents/roles/backend-engineer.md +165 -0
  110. package/plugins/agents/roles/ceo.md +94 -0
  111. package/plugins/agents/roles/data-engineer.md +135 -0
  112. package/plugins/agents/roles/devops-engineer.md +181 -0
  113. package/plugins/agents/roles/frontend-engineer.md +129 -0
  114. package/plugins/agents/roles/product-owner.md +98 -0
  115. package/plugins/agents/roles/quality-engineer.md +129 -0
  116. package/plugins/agents/roles/security-engineer.md +180 -0
  117. package/plugins/agents/roles/tech-lead.md +97 -0
  118. package/plugins/agents/support/blind-comparator.md +88 -0
  119. package/plugins/agents/support/consistency-checker.md +103 -0
  120. package/plugins/agents/support/failure-diagnostician.md +141 -0
  121. package/plugins/agents/support/independent-reviewer.md +80 -0
  122. package/plugins/agents/support/safety-auditor.md +121 -0
  123. package/plugins/agents/support/skill-benchmarker.md +86 -0
  124. package/plugins/agents/support/skill-forger.md +105 -0
  125. package/plugins/agents/support/stage-gate-evaluator.md +121 -0
  126. package/plugins/agents/support/test-coverage-reviewer.md +73 -0
  127. package/plugins/benchmarks/templates/README.md +44 -0
  128. package/plugins/benchmarks/templates/commands/explore-template.yaml +48 -0
  129. package/plugins/benchmarks/templates/pipeline/agile-template.yaml +84 -0
  130. package/plugins/benchmarks/templates/pipeline/waterfall-template.yaml +106 -0
  131. package/plugins/benchmarks/templates/skills/requirement-analyzer-template.yaml +48 -0
  132. package/plugins/commands/README.en.md +96 -0
  133. package/plugins/commands/README.md +96 -0
  134. package/plugins/commands/apply.md +191 -0
  135. package/plugins/commands/archive.md +76 -0
  136. package/plugins/commands/env-export.md +79 -0
  137. package/plugins/commands/env-sync.md +640 -0
  138. package/plugins/commands/env-template.md +223 -0
  139. package/plugins/commands/env-update.md +264 -0
  140. package/plugins/commands/env-validate.md +176 -0
  141. package/plugins/commands/env.md +79 -0
  142. package/plugins/commands/explore.md +76 -0
  143. package/plugins/commands/field-evolve.md +536 -0
  144. package/plugins/commands/memory.md +249 -0
  145. package/plugins/commands/project-evolve.md +821 -0
  146. package/plugins/commands/propose.md +93 -0
  147. package/plugins/commands/review.md +140 -0
  148. package/plugins/commands/run.md +224 -0
  149. package/plugins/commands/status.md +62 -0
  150. package/plugins/commands/validate.md +108 -0
  151. package/plugins/hooks/README.en.md +56 -0
  152. package/plugins/hooks/README.md +56 -0
  153. package/plugins/hooks/ai-project-guard.js +329 -0
  154. package/plugins/hooks/artifact-evaluation-hook.js +237 -0
  155. package/plugins/hooks/constitution-guard.js +211 -0
  156. package/plugins/hooks/environment-autocommit.js +264 -0
  157. package/plugins/hooks/environment-manager.js +778 -0
  158. package/plugins/hooks/execution-tracker.js +354 -0
  159. package/plugins/hooks/frozen-zone-guard.js +140 -0
  160. package/plugins/hooks/layer1-validator.js +423 -0
  161. package/plugins/hooks/lib/artifact-evaluator.js +414 -0
  162. package/plugins/hooks/lib/benchmarks/change-detector.js +390 -0
  163. package/plugins/hooks/lib/benchmarks/evaluator.js +605 -0
  164. package/plugins/hooks/lib/benchmarks/integration-example.js +169 -0
  165. package/plugins/hooks/lib/data-and-ai-detector.js +275 -0
  166. package/plugins/hooks/lib/detection-pattern-loader.js +865 -0
  167. package/plugins/hooks/lib/directory-discovery.js +395 -0
  168. package/plugins/hooks/lib/environment-config-loader.js +341 -0
  169. package/plugins/hooks/lib/environment-detector.js +553 -0
  170. package/plugins/hooks/lib/environment-evolver.js +564 -0
  171. package/plugins/hooks/lib/environment-registry.js +813 -0
  172. package/plugins/hooks/lib/execution-path.js +427 -0
  173. package/plugins/hooks/lib/hook-error-recorder.js +245 -0
  174. package/plugins/hooks/lib/hook-logger.js +538 -0
  175. package/plugins/hooks/lib/hook-runner.js +97 -0
  176. package/plugins/hooks/lib/hook-runner.sh +44 -0
  177. package/plugins/hooks/lib/hook-state-manager.js +480 -0
  178. package/plugins/hooks/lib/memory-extractor.js +377 -0
  179. package/plugins/hooks/lib/memory-manager.js +673 -0
  180. package/plugins/hooks/lib/metrics-analyzer.js +489 -0
  181. package/plugins/hooks/lib/project-evolution/auto-fixer.js +511 -0
  182. package/plugins/hooks/lib/project-evolution/memory-manager.js +346 -0
  183. package/plugins/hooks/lib/project-evolution/pattern-detector.js +476 -0
  184. package/plugins/hooks/lib/project-evolution/semantic-indexer.js +480 -0
  185. package/plugins/hooks/lib/project-structure-detector.js +326 -0
  186. package/plugins/hooks/lib/rollback-tracker.js +346 -0
  187. package/plugins/hooks/lib/source-code-scanner.js +596 -0
  188. package/plugins/hooks/lib/technology-stack-detector.js +374 -0
  189. package/plugins/hooks/lib/test-failure-analyzer.js +375 -0
  190. package/plugins/hooks/lib/test-failure-fixer.js +268 -0
  191. package/plugins/hooks/lib/trace-context.js +277 -0
  192. package/plugins/hooks/lib/validation-patterns.js +415 -0
  193. package/plugins/hooks/memory-sync.js +171 -0
  194. package/plugins/hooks/pipeline-observer.js +413 -0
  195. package/plugins/hooks/scope-sentinel.js +204 -0
  196. package/plugins/hooks/trace-initialization.js +169 -0
  197. package/plugins/memory/templates/code-quality.yaml +149 -0
  198. package/plugins/memory/templates/multi-system.yaml +155 -0
  199. package/plugins/memory/templates/team-habits.yaml +119 -0
  200. package/plugins/memory/templates/testing.yaml +121 -0
  201. package/plugins/skills/README.en.md +47 -0
  202. package/plugins/skills/README.md +104 -0
  203. package/plugins/skills/benchmark-executor/README.md +93 -0
  204. package/plugins/skills/benchmark-executor/SKILL.md +647 -0
  205. package/plugins/skills/benchmark-generator/SKILL.md +349 -0
  206. package/plugins/skills/delivery-stage/SKILL.md +203 -0
  207. package/plugins/skills/design-stage/SKILL.md +216 -0
  208. package/plugins/skills/evolution-process/SKILL.md +291 -0
  209. package/plugins/skills/exploration-phase/SKILL.md +133 -0
  210. package/plugins/skills/implementation-stage/SKILL.md +179 -0
  211. package/plugins/skills/layer1-validation/SKILL.md +79 -0
  212. package/plugins/skills/pending-dashboard/SKILL.md +109 -0
  213. package/plugins/skills/project-evolution/SKILL.md +847 -0
  214. package/plugins/skills/requirement-stage/SKILL.md +183 -0
  215. package/plugins/skills/skill-forge/SKILL.md +223 -0
  216. package/plugins/skills/skill-forge/references/description-guide.md +92 -0
  217. package/plugins/skills/skill-forge/references/quality-rubric.md +104 -0
  218. package/plugins/skills/skill-forge/references/skill-template.md +106 -0
  219. package/plugins/skills/startup-guard/SKILL.md +38 -0
  220. package/plugins/skills/testing-stage/SKILL.md +195 -0
  221. package/scripts/cli/global-init.js +288 -0
  222. package/scripts/cli/global.js +324 -0
  223. package/scripts/cli/index.js +55 -0
  224. package/scripts/cli/init.js +382 -0
  225. package/scripts/cli/list.js +69 -0
  226. package/scripts/cli/org.js +340 -0
  227. package/scripts/cli/update.js +44 -0
  228. package/scripts/config/commands.config.js +145 -0
  229. package/scripts/config/hooks.config.js +197 -0
  230. package/scripts/evolution/evolution-router.js +273 -0
  231. package/scripts/evolution/evolution-signal-collector.js +307 -0
  232. package/scripts/evolution/knowledge-loader.js +346 -0
  233. package/scripts/evolution/marketplace.js +317 -0
  234. package/scripts/evolution/version-manager.js +371 -0
  235. package/scripts/install/agents.js +106 -0
  236. package/scripts/install/commands.js +133 -0
  237. package/scripts/install/constants.js +424 -0
  238. package/scripts/install/hook-logger.js +536 -0
  239. package/scripts/install/hooks.js +110 -0
  240. package/scripts/install/index.js +39 -0
  241. package/scripts/install/skills.js +95 -0
  242. package/scripts/postinstall.js +25 -0
  243. package/scripts/state.js +376 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Skills generator for Claude Code integration
3
+ *
4
+ * Generates skill files from templates directory with variable replacement.
5
+ * Source: plugins/skills/{skill-name}/SKILL.md
6
+ * Target: .claude/skills/{skill-name}/SKILL.md
7
+ */
8
+
9
+ import fs from 'fs';
10
+ import path from 'path';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ // ESM __dirname equivalent
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+
17
+ // Role name mapping (for backward compatibility)
18
+ const ROLE_ALIAS = {
19
+ 'backend': 'backend-engineer',
20
+ 'frontend': 'frontend-engineer',
21
+ 'data': 'data-engineer',
22
+ 'ai': 'ai-engineer',
23
+ 'quality': 'quality-engineer'
24
+ };
25
+
26
+ /**
27
+ * Replace template variables in content
28
+ * @param {string} content - Template content
29
+ * @returns {string} - Content with variables replaced
30
+ */
31
+ function replaceVariables(content) {
32
+ // Replace ${FW} with .autospec/knowledge (backward compat)
33
+ // Replace ${KNOWLEDGE} with .autospec/knowledge
34
+ return content
35
+ .replace(/\$\{FW\}/g, '.autospec/knowledge')
36
+ .replace(/\$\{KNOWLEDGE\}/g, '.autospec/knowledge');
37
+ }
38
+
39
+ /**
40
+ * Generate all skill files from templates
41
+ * @param {string} claudeDir - Target .claude directory path
42
+ */
43
+ export function generateSkills(claudeDir) {
44
+ const skillsDir = path.join(claudeDir, 'skills');
45
+ fs.mkdirSync(skillsDir, { recursive: true });
46
+
47
+ // Get templates directory path (relative to this file)
48
+ // __dirname is scripts/install/, templates are in ../../plugins/skills/
49
+ const templatesDir = path.join(__dirname, '../../plugins/skills');
50
+
51
+ // Check if templates directory exists
52
+ if (!fs.existsSync(templatesDir)) {
53
+ console.warn('Warning: plugins/skills/ directory not found, skipping skill generation');
54
+ return;
55
+ }
56
+
57
+ // Get all skill directories
58
+ const skillDirs = fs.readdirSync(templatesDir).filter(f => {
59
+ const skillPath = path.join(templatesDir, f);
60
+ return fs.statSync(skillPath).isDirectory();
61
+ });
62
+
63
+ // Process each skill
64
+ for (const skillName of skillDirs) {
65
+ const skillFile = path.join(templatesDir, skillName, 'SKILL.md');
66
+ if (fs.existsSync(skillFile)) {
67
+ const content = fs.readFileSync(skillFile, 'utf-8');
68
+ const processed = replaceVariables(content);
69
+
70
+ const targetDir = path.join(skillsDir, skillName);
71
+ fs.mkdirSync(targetDir, { recursive: true });
72
+ fs.writeFileSync(path.join(targetDir, 'SKILL.md'), processed, 'utf-8');
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Get list of available skill names
79
+ * @returns {string[]} - Array of skill names
80
+ */
81
+ export function getAvailableSkills() {
82
+ const templatesDir = path.join(__dirname, '../../plugins/skills');
83
+
84
+ if (!fs.existsSync(templatesDir)) {
85
+ return [];
86
+ }
87
+
88
+ return fs.readdirSync(templatesDir).filter(f => {
89
+ const skillPath = path.join(templatesDir, f);
90
+ const skillFile = path.join(skillPath, 'SKILL.md');
91
+ return fs.statSync(skillPath).isDirectory() && fs.existsSync(skillFile);
92
+ });
93
+ }
94
+
95
+ export { ROLE_ALIAS };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * AutoSpec postinstall script
3
+ * Shows quick start guide after npm install
4
+ */
5
+
6
+ const isGlobal = process.env.npm_config_global === 'true';
7
+
8
+ if (isGlobal) {
9
+ console.log(`
10
+ ┌─────────────────────────────────────────────┐
11
+ │ │
12
+ │ AutoSpec installed successfully! │
13
+ │ │
14
+ │ Quick start: │
15
+ │ cd your-project │
16
+ │ autospec init │
17
+ │ │
18
+ │ Then in Claude Code: │
19
+ │ /autospec:run → Full pipeline │
20
+ │ /autospec:run --workflow=hotfix │
21
+ │ /autospec:run --workflow=experiment │
22
+ │ │
23
+ └─────────────────────────────────────────────┘
24
+ `);
25
+ }
@@ -0,0 +1,376 @@
1
+ /**
2
+ * Pipeline state management
3
+ * Tracks which stage the pipeline is at for a given task
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { STATE_FILE, METRICS_FILE, STAGES, WORKFLOWS, AUTOSPEC_DIRS } from './constants.js';
9
+
10
+ const ARCHIVES_DIR = `.autospec/${AUTOSPEC_DIRS.ARCHIVES}`;
11
+
12
+ /**
13
+ * Initialize or load pipeline state
14
+ */
15
+ export function loadState(projectDir) {
16
+ const statePath = path.join(projectDir, STATE_FILE);
17
+ if (fs.existsSync(statePath)) {
18
+ return JSON.parse(fs.readFileSync(statePath, 'utf-8'));
19
+ }
20
+ return null;
21
+ }
22
+
23
+ /**
24
+ * Save pipeline state
25
+ */
26
+ export function saveState(projectDir, state) {
27
+ const statePath = path.join(projectDir, STATE_FILE);
28
+ const dir = path.dirname(statePath);
29
+ if (!fs.existsSync(dir)) {
30
+ fs.mkdirSync(dir, { recursive: true });
31
+ }
32
+ state.updatedAt = new Date().toISOString();
33
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf-8');
34
+ }
35
+
36
+ /**
37
+ * Check if there's an active (incomplete) pipeline
38
+ */
39
+ export function hasActivePipeline(projectDir) {
40
+ const state = loadState(projectDir);
41
+ if (!state) return false;
42
+ return !state.completedAt;
43
+ }
44
+
45
+ /**
46
+ * Archive completed pipeline to archives/{spec-id}/
47
+ * Called when a pipeline completes or when starting a new one
48
+ */
49
+ export function archivePipeline(projectDir, specId) {
50
+ const state = loadState(projectDir);
51
+ if (!state) return false;
52
+
53
+ // Determine specId from state if not provided
54
+ const runId = specId || state.specs || generateRunId(state.taskName, state.createdAt);
55
+
56
+ // Create archives directory
57
+ const archivesDir = path.join(projectDir, ARCHIVES_DIR);
58
+ const archiveDir = path.join(archivesDir, runId);
59
+ if (!fs.existsSync(archiveDir)) {
60
+ fs.mkdirSync(archiveDir, { recursive: true });
61
+ }
62
+
63
+ // Copy state.json to archive
64
+ const archivedStatePath = path.join(archiveDir, 'state.json');
65
+ fs.writeFileSync(archivedStatePath, JSON.stringify(state, null, 2), 'utf-8');
66
+
67
+ // Copy metrics.json to archive if exists
68
+ const metricsPath = path.join(projectDir, METRICS_FILE);
69
+ if (fs.existsSync(metricsPath)) {
70
+ const archivedMetricsPath = path.join(archiveDir, 'metrics.json');
71
+ fs.copyFileSync(metricsPath, archivedMetricsPath);
72
+ }
73
+
74
+ // Mark state as archived
75
+ state.archivedAt = new Date().toISOString();
76
+ state.archivedTo = runId;
77
+
78
+ return runId;
79
+ }
80
+
81
+ /**
82
+ * Generate run ID from task name and timestamp
83
+ */
84
+ function generateRunId(taskName, createdAt) {
85
+ const timestamp = createdAt ? new Date(createdAt).toISOString().slice(0, 10) : new Date().toISOString().slice(0, 10);
86
+ const slug = (taskName || 'unknown')
87
+ .toLowerCase()
88
+ .replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
89
+ .slice(0, 30);
90
+ return `${timestamp}-${slug}`;
91
+ }
92
+
93
+ /**
94
+ * Get list of archived runs
95
+ */
96
+ export function listArchivedRuns(projectDir) {
97
+ const archivesDir = path.join(projectDir, ARCHIVES_DIR);
98
+ if (!fs.existsSync(archivesDir)) return [];
99
+
100
+ return fs.readdirSync(archivesDir, { withFileTypes: true })
101
+ .filter(dirent => dirent.isDirectory())
102
+ .map(dirent => {
103
+ const archiveDir = path.join(archivesDir, dirent.name);
104
+ const statePath = path.join(archiveDir, 'state.json');
105
+ let runInfo = { id: dirent.name };
106
+ if (fs.existsSync(statePath)) {
107
+ try {
108
+ const state = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
109
+ runInfo.taskName = state.taskName;
110
+ runInfo.workflow = state.workflow;
111
+ runInfo.completedAt = state.completedAt;
112
+ runInfo.archivedAt = state.archivedAt;
113
+ } catch { /* ignore */ }
114
+ }
115
+ return runInfo;
116
+ })
117
+ .sort((a, b) => (b.archivedAt || '').localeCompare(a.archivedAt || ''));
118
+ }
119
+
120
+ /**
121
+ * Load archived run state
122
+ */
123
+ export function loadArchivedRun(projectDir, runId) {
124
+ const archiveDir = path.join(projectDir, ARCHIVES_DIR, runId);
125
+ const statePath = path.join(archiveDir, 'state.json');
126
+ if (fs.existsSync(statePath)) {
127
+ return JSON.parse(fs.readFileSync(statePath, 'utf-8'));
128
+ }
129
+ return null;
130
+ }
131
+
132
+ /**
133
+ * Standard subsystem types
134
+ */
135
+ export const SUBSYSTEM_TYPES = {
136
+ BACKEND: 'backend',
137
+ FRONTEND: 'frontend',
138
+ MOBILE: 'mobile',
139
+ DATA: 'data',
140
+ AI: 'ai',
141
+ PIPELINE: 'pipeline'
142
+ };
143
+
144
+ /**
145
+ * Labels for subsystem types
146
+ */
147
+ export const SUBSYSTEM_TYPE_LABELS = {
148
+ [SUBSYSTEM_TYPES.BACKEND]: '后端',
149
+ [SUBSYSTEM_TYPES.FRONTEND]: '前端',
150
+ [SUBSYSTEM_TYPES.MOBILE]: '移动端',
151
+ [SUBSYSTEM_TYPES.DATA]: '数据系统',
152
+ [SUBSYSTEM_TYPES.AI]: 'AI/模型',
153
+ [SUBSYSTEM_TYPES.PIPELINE]: '数据管道'
154
+ };
155
+
156
+ /**
157
+ * Validate and normalize subsystem names to ensure uniqueness
158
+ * @param {Array} subsystems - subsystems from config
159
+ * @returns {Array} normalized subsystems with unique names
160
+ */
161
+ export function normalizeSubsystems(subsystems) {
162
+ if (!subsystems || subsystems.length === 0) return [];
163
+
164
+ const nameCount = {};
165
+ const seenNames = new Set();
166
+
167
+ return subsystems.map(sub => {
168
+ let name = sub.name;
169
+
170
+ // Handle duplicate names by adding parent directory suffix
171
+ if (seenNames.has(name)) {
172
+ const parentDir = sub.path.split('/').slice(-2)[0];
173
+ name = `${name}-${parentDir}`;
174
+ }
175
+
176
+ // Track name occurrences
177
+ nameCount[name] = (nameCount[name] || 0) + 1;
178
+ if (nameCount[name] > 1) {
179
+ name = `${name}-${nameCount[name]}`;
180
+ }
181
+
182
+ seenNames.add(name);
183
+
184
+ return {
185
+ name,
186
+ type: sub.type || 'unknown',
187
+ path: sub.path
188
+ };
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Create new pipeline state for a task
194
+ * @param {string} taskName - task name
195
+ * @param {string} workflow - workflow type
196
+ * @param {Array} roles - involved roles
197
+ * @param {Array} subsystems - optional subsystems config
198
+ */
199
+ export function createState(taskName, workflow = 'waterfall', roles = [], subsystems = []) {
200
+ const wf = WORKFLOWS[workflow];
201
+ if (!wf) throw new Error(`Unknown workflow: ${workflow}`);
202
+
203
+ // Normalize subsystem names to ensure uniqueness
204
+ const normalizedSubsystems = normalizeSubsystems(subsystems);
205
+
206
+ const stages = {};
207
+ for (const stageId of wf.stages) {
208
+ const stage = STAGES.find(s => s.id === stageId);
209
+ const stageDef = {
210
+ name: stage.name,
211
+ label: stage.label,
212
+ status: 'pending', // pending | in_progress | layer1_pass | layer2_pass | completed | blocked | failed
213
+ layer1: null, // null | pass | fail | blocked
214
+ layer2: null, // null | pass | fail
215
+ retryCount: 0,
216
+ artifacts: [],
217
+ assumptions: [],
218
+ startedAt: null,
219
+ completedAt: null
220
+ };
221
+
222
+ // Add subsystems array for multi-system projects
223
+ if (normalizedSubsystems.length > 0) {
224
+ stageDef.subsystems = normalizedSubsystems.map(sub => ({
225
+ name: sub.name,
226
+ type: sub.type,
227
+ status: 'pending',
228
+ layer1: null,
229
+ layer2: null,
230
+ retryCount: 0,
231
+ startedAt: null,
232
+ completedAt: null
233
+ }));
234
+ }
235
+
236
+ stages[stageId] = stageDef;
237
+ }
238
+
239
+ return {
240
+ version: '0.1.0',
241
+ taskName,
242
+ workflow,
243
+ roles,
244
+ hasSubsystems: normalizedSubsystems.length > 0,
245
+ subsystemsConfig: normalizedSubsystems.map(s => ({ name: s.name, type: s.type, path: s.path })),
246
+ currentStage: wf.stages[0],
247
+ stages,
248
+ pendingConfirmations: [],
249
+ autoDecisions: [],
250
+ createdAt: new Date().toISOString(),
251
+ updatedAt: new Date().toISOString()
252
+ };
253
+ }
254
+
255
+ /**
256
+ * Get status of a specific subsystem in a stage
257
+ * @param {Object} state - pipeline state
258
+ * @param {string} stageId - stage ID (e.g., '03')
259
+ * @param {string} subsystemName - subsystem name
260
+ * @returns {Object|null} subsystem status or null
261
+ */
262
+ export function getSubsystemStatus(state, stageId, subsystemName) {
263
+ if (!state?.stages?.[stageId]?.subsystems) return null;
264
+ return state.stages[stageId].subsystems.find(s => s.name === subsystemName) || null;
265
+ }
266
+
267
+ /**
268
+ * Update status of a specific subsystem
269
+ * @param {Object} state - pipeline state
270
+ * @param {string} stageId - stage ID
271
+ * @param {string} subsystemName - subsystem name
272
+ * @param {Object} updates - status updates
273
+ */
274
+ export function updateSubsystemStatus(state, stageId, subsystemName, updates) {
275
+ if (!state?.stages?.[stageId]?.subsystems) return false;
276
+
277
+ const subsystem = state.stages[stageId].subsystems.find(s => s.name === subsystemName);
278
+ if (!subsystem) return false;
279
+
280
+ Object.assign(subsystem, updates);
281
+ return true;
282
+ }
283
+
284
+ /**
285
+ * Group subsystems by type for display
286
+ * @param {Array} subsystems - subsystem status array
287
+ * @returns {Object} grouped by type
288
+ */
289
+ function groupSubsystemsByType(subsystems) {
290
+ const groups = {};
291
+ for (const sub of subsystems) {
292
+ const type = sub.type || 'unknown';
293
+ if (!groups[type]) groups[type] = [];
294
+ groups[type].push(sub);
295
+ }
296
+ return groups;
297
+ }
298
+
299
+ /**
300
+ * Get status icon for display
301
+ */
302
+ function getStatusIcon(status) {
303
+ return {
304
+ pending: '○',
305
+ in_progress: '◎',
306
+ layer1_pass: '◐',
307
+ layer2_pass: '◑',
308
+ completed: '●',
309
+ blocked: '✖',
310
+ failed: '✖'
311
+ }[status] || '?';
312
+ }
313
+
314
+ /**
315
+ * Get human-readable status summary
316
+ * Supports both single-system and multi-system modes
317
+ */
318
+ export function getStatusSummary(state) {
319
+ if (!state) return null;
320
+
321
+ const wf = WORKFLOWS[state.workflow];
322
+ const lines = [];
323
+ lines.push(`Task: ${state.taskName}`);
324
+ lines.push(`Workflow: ${wf.label} (${state.workflow})`);
325
+ lines.push(`Roles: ${state.roles.length > 0 ? state.roles.join(', ') : '(none)'}`);
326
+ lines.push(`Mode: ${state.hasSubsystems ? 'Multi-system' : 'Single-system'}`);
327
+ lines.push(`Current: Stage ${state.currentStage}`);
328
+ lines.push('');
329
+
330
+ // Show subsystems config if present
331
+ if (state.hasSubsystems && state.subsystemsConfig) {
332
+ lines.push('Subsystems:');
333
+ const groups = groupSubsystemsByType(state.subsystemsConfig);
334
+ for (const [type, subs] of Object.entries(groups)) {
335
+ const typeLabel = SUBSYSTEM_TYPE_LABELS[type] || type;
336
+ lines.push(` [${typeLabel}] ${subs.map(s => s.name).join(', ')}`);
337
+ }
338
+ lines.push('');
339
+ }
340
+
341
+ lines.push('Stages:');
342
+
343
+ for (const stageId of wf.stages) {
344
+ const s = state.stages[stageId];
345
+ const stage = STAGES.find(st => st.id === stageId);
346
+ const icon = getStatusIcon(s.status);
347
+ const current = stageId === state.currentStage ? ' ←' : '';
348
+ lines.push(` ${icon} [${stageId}] ${stage.label} — ${s.status}${current}`);
349
+
350
+ // Show subsystem details for current stage in multi-system mode
351
+ if (s.subsystems && stageId === state.currentStage) {
352
+ const groups = groupSubsystemsByType(s.subsystems);
353
+ for (const [type, subs] of Object.entries(groups)) {
354
+ const typeLabel = SUBSYSTEM_TYPE_LABELS[type] || type;
355
+ lines.push(` ${typeLabel}:`);
356
+ for (const sub of subs) {
357
+ const subIcon = getStatusIcon(sub.status);
358
+ const l1 = sub.layer1 ? `[L1:${sub.layer1}]` : '';
359
+ const l2 = sub.layer2 ? `[L2:${sub.layer2}]` : '';
360
+ lines.push(` ${subIcon} ${sub.name} ${l1}${l2}`);
361
+ }
362
+ }
363
+ }
364
+ }
365
+
366
+ // Show pending items count if any
367
+ if (state.pendingConfirmations?.length > 0) {
368
+ lines.push('');
369
+ lines.push(`Pending confirmations: ${state.pendingConfirmations.length}`);
370
+ }
371
+ if (state.autoDecisions?.length > 0) {
372
+ lines.push(`Auto decisions: ${state.autoDecisions.length}`);
373
+ }
374
+
375
+ return lines.join('\n');
376
+ }