synarcx 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 (288) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +90 -0
  3. package/bin/synarcx.js +3 -0
  4. package/dist/cli/index.d.ts +2 -0
  5. package/dist/cli/index.js +474 -0
  6. package/dist/commands/change.d.ts +35 -0
  7. package/dist/commands/change.js +278 -0
  8. package/dist/commands/completion.d.ts +72 -0
  9. package/dist/commands/completion.js +264 -0
  10. package/dist/commands/config.d.ts +36 -0
  11. package/dist/commands/config.js +552 -0
  12. package/dist/commands/feedback.d.ts +9 -0
  13. package/dist/commands/feedback.js +170 -0
  14. package/dist/commands/schema.d.ts +6 -0
  15. package/dist/commands/schema.js +870 -0
  16. package/dist/commands/show.d.ts +14 -0
  17. package/dist/commands/show.js +132 -0
  18. package/dist/commands/spec.d.ts +15 -0
  19. package/dist/commands/spec.js +226 -0
  20. package/dist/commands/validate.d.ts +24 -0
  21. package/dist/commands/validate.js +295 -0
  22. package/dist/commands/workflow/index.d.ts +17 -0
  23. package/dist/commands/workflow/index.js +12 -0
  24. package/dist/commands/workflow/instructions.d.ts +29 -0
  25. package/dist/commands/workflow/instructions.js +327 -0
  26. package/dist/commands/workflow/new-change.d.ts +11 -0
  27. package/dist/commands/workflow/new-change.js +45 -0
  28. package/dist/commands/workflow/schemas.d.ts +10 -0
  29. package/dist/commands/workflow/schemas.js +34 -0
  30. package/dist/commands/workflow/shared.d.ts +57 -0
  31. package/dist/commands/workflow/shared.js +117 -0
  32. package/dist/commands/workflow/status.d.ts +14 -0
  33. package/dist/commands/workflow/status.js +75 -0
  34. package/dist/commands/workflow/templates.d.ts +16 -0
  35. package/dist/commands/workflow/templates.js +69 -0
  36. package/dist/commands/workspace/open.d.ts +29 -0
  37. package/dist/commands/workspace/open.js +84 -0
  38. package/dist/commands/workspace/operations.d.ts +18 -0
  39. package/dist/commands/workspace/operations.js +461 -0
  40. package/dist/commands/workspace/selection.d.ts +5 -0
  41. package/dist/commands/workspace/selection.js +90 -0
  42. package/dist/commands/workspace/types.d.ts +83 -0
  43. package/dist/commands/workspace/types.js +36 -0
  44. package/dist/commands/workspace.d.ts +3 -0
  45. package/dist/commands/workspace.js +635 -0
  46. package/dist/core/archive.d.ts +11 -0
  47. package/dist/core/archive.js +319 -0
  48. package/dist/core/artifact-graph/graph.d.ts +56 -0
  49. package/dist/core/artifact-graph/graph.js +141 -0
  50. package/dist/core/artifact-graph/index.d.ts +8 -0
  51. package/dist/core/artifact-graph/index.js +14 -0
  52. package/dist/core/artifact-graph/instruction-loader.d.ts +143 -0
  53. package/dist/core/artifact-graph/instruction-loader.js +217 -0
  54. package/dist/core/artifact-graph/outputs.d.ts +14 -0
  55. package/dist/core/artifact-graph/outputs.js +39 -0
  56. package/dist/core/artifact-graph/resolver.d.ts +81 -0
  57. package/dist/core/artifact-graph/resolver.js +258 -0
  58. package/dist/core/artifact-graph/schema.d.ts +13 -0
  59. package/dist/core/artifact-graph/schema.js +108 -0
  60. package/dist/core/artifact-graph/state.d.ts +12 -0
  61. package/dist/core/artifact-graph/state.js +31 -0
  62. package/dist/core/artifact-graph/types.d.ts +45 -0
  63. package/dist/core/artifact-graph/types.js +43 -0
  64. package/dist/core/available-tools.d.ts +17 -0
  65. package/dist/core/available-tools.js +43 -0
  66. package/dist/core/command-generation/adapters/amazon-q.d.ts +13 -0
  67. package/dist/core/command-generation/adapters/amazon-q.js +26 -0
  68. package/dist/core/command-generation/adapters/antigravity.d.ts +13 -0
  69. package/dist/core/command-generation/adapters/antigravity.js +26 -0
  70. package/dist/core/command-generation/adapters/auggie.d.ts +13 -0
  71. package/dist/core/command-generation/adapters/auggie.js +27 -0
  72. package/dist/core/command-generation/adapters/bob.d.ts +14 -0
  73. package/dist/core/command-generation/adapters/bob.js +45 -0
  74. package/dist/core/command-generation/adapters/claude.d.ts +13 -0
  75. package/dist/core/command-generation/adapters/claude.js +50 -0
  76. package/dist/core/command-generation/adapters/cline.d.ts +14 -0
  77. package/dist/core/command-generation/adapters/cline.js +27 -0
  78. package/dist/core/command-generation/adapters/codebuddy.d.ts +13 -0
  79. package/dist/core/command-generation/adapters/codebuddy.js +28 -0
  80. package/dist/core/command-generation/adapters/codex.d.ts +16 -0
  81. package/dist/core/command-generation/adapters/codex.js +39 -0
  82. package/dist/core/command-generation/adapters/continue.d.ts +13 -0
  83. package/dist/core/command-generation/adapters/continue.js +28 -0
  84. package/dist/core/command-generation/adapters/costrict.d.ts +13 -0
  85. package/dist/core/command-generation/adapters/costrict.js +27 -0
  86. package/dist/core/command-generation/adapters/crush.d.ts +13 -0
  87. package/dist/core/command-generation/adapters/crush.js +30 -0
  88. package/dist/core/command-generation/adapters/cursor.d.ts +14 -0
  89. package/dist/core/command-generation/adapters/cursor.js +44 -0
  90. package/dist/core/command-generation/adapters/factory.d.ts +13 -0
  91. package/dist/core/command-generation/adapters/factory.js +27 -0
  92. package/dist/core/command-generation/adapters/gemini.d.ts +13 -0
  93. package/dist/core/command-generation/adapters/gemini.js +26 -0
  94. package/dist/core/command-generation/adapters/github-copilot.d.ts +13 -0
  95. package/dist/core/command-generation/adapters/github-copilot.js +26 -0
  96. package/dist/core/command-generation/adapters/iflow.d.ts +13 -0
  97. package/dist/core/command-generation/adapters/iflow.js +29 -0
  98. package/dist/core/command-generation/adapters/index.d.ts +32 -0
  99. package/dist/core/command-generation/adapters/index.js +32 -0
  100. package/dist/core/command-generation/adapters/junie.d.ts +13 -0
  101. package/dist/core/command-generation/adapters/junie.js +26 -0
  102. package/dist/core/command-generation/adapters/kilocode.d.ts +14 -0
  103. package/dist/core/command-generation/adapters/kilocode.js +23 -0
  104. package/dist/core/command-generation/adapters/kiro.d.ts +13 -0
  105. package/dist/core/command-generation/adapters/kiro.js +26 -0
  106. package/dist/core/command-generation/adapters/lingma.d.ts +13 -0
  107. package/dist/core/command-generation/adapters/lingma.js +30 -0
  108. package/dist/core/command-generation/adapters/opencode.d.ts +13 -0
  109. package/dist/core/command-generation/adapters/opencode.js +27 -0
  110. package/dist/core/command-generation/adapters/pi.d.ts +18 -0
  111. package/dist/core/command-generation/adapters/pi.js +55 -0
  112. package/dist/core/command-generation/adapters/qoder.d.ts +13 -0
  113. package/dist/core/command-generation/adapters/qoder.js +30 -0
  114. package/dist/core/command-generation/adapters/qwen.d.ts +13 -0
  115. package/dist/core/command-generation/adapters/qwen.js +26 -0
  116. package/dist/core/command-generation/adapters/roocode.d.ts +14 -0
  117. package/dist/core/command-generation/adapters/roocode.js +27 -0
  118. package/dist/core/command-generation/adapters/windsurf.d.ts +14 -0
  119. package/dist/core/command-generation/adapters/windsurf.js +51 -0
  120. package/dist/core/command-generation/generator.d.ts +21 -0
  121. package/dist/core/command-generation/generator.js +27 -0
  122. package/dist/core/command-generation/index.d.ts +22 -0
  123. package/dist/core/command-generation/index.js +24 -0
  124. package/dist/core/command-generation/registry.d.ts +36 -0
  125. package/dist/core/command-generation/registry.js +98 -0
  126. package/dist/core/command-generation/types.d.ts +56 -0
  127. package/dist/core/command-generation/types.js +8 -0
  128. package/dist/core/completions/command-registry.d.ts +7 -0
  129. package/dist/core/completions/command-registry.js +596 -0
  130. package/dist/core/completions/completion-provider.d.ts +71 -0
  131. package/dist/core/completions/completion-provider.js +129 -0
  132. package/dist/core/completions/factory.d.ts +64 -0
  133. package/dist/core/completions/factory.js +75 -0
  134. package/dist/core/completions/generators/bash-generator.d.ts +35 -0
  135. package/dist/core/completions/generators/bash-generator.js +230 -0
  136. package/dist/core/completions/generators/fish-generator.d.ts +32 -0
  137. package/dist/core/completions/generators/fish-generator.js +160 -0
  138. package/dist/core/completions/generators/powershell-generator.d.ts +36 -0
  139. package/dist/core/completions/generators/powershell-generator.js +266 -0
  140. package/dist/core/completions/generators/zsh-generator.d.ts +47 -0
  141. package/dist/core/completions/generators/zsh-generator.js +274 -0
  142. package/dist/core/completions/installers/bash-installer.d.ts +87 -0
  143. package/dist/core/completions/installers/bash-installer.js +318 -0
  144. package/dist/core/completions/installers/fish-installer.d.ts +43 -0
  145. package/dist/core/completions/installers/fish-installer.js +143 -0
  146. package/dist/core/completions/installers/powershell-installer.d.ts +102 -0
  147. package/dist/core/completions/installers/powershell-installer.js +387 -0
  148. package/dist/core/completions/installers/zsh-installer.d.ts +117 -0
  149. package/dist/core/completions/installers/zsh-installer.js +421 -0
  150. package/dist/core/completions/templates/bash-templates.d.ts +6 -0
  151. package/dist/core/completions/templates/bash-templates.js +30 -0
  152. package/dist/core/completions/templates/fish-templates.d.ts +7 -0
  153. package/dist/core/completions/templates/fish-templates.js +45 -0
  154. package/dist/core/completions/templates/powershell-templates.d.ts +6 -0
  155. package/dist/core/completions/templates/powershell-templates.js +34 -0
  156. package/dist/core/completions/templates/zsh-templates.d.ts +6 -0
  157. package/dist/core/completions/templates/zsh-templates.js +45 -0
  158. package/dist/core/completions/types.d.ts +101 -0
  159. package/dist/core/completions/types.js +2 -0
  160. package/dist/core/config-prompts.d.ts +9 -0
  161. package/dist/core/config-prompts.js +34 -0
  162. package/dist/core/config-schema.d.ts +86 -0
  163. package/dist/core/config-schema.js +213 -0
  164. package/dist/core/config.d.ts +19 -0
  165. package/dist/core/config.js +38 -0
  166. package/dist/core/converters/json-converter.d.ts +6 -0
  167. package/dist/core/converters/json-converter.js +51 -0
  168. package/dist/core/global-config.d.ts +49 -0
  169. package/dist/core/global-config.js +124 -0
  170. package/dist/core/index.d.ts +3 -0
  171. package/dist/core/index.js +4 -0
  172. package/dist/core/init.d.ts +37 -0
  173. package/dist/core/init.js +585 -0
  174. package/dist/core/legacy-cleanup.d.ts +169 -0
  175. package/dist/core/legacy-cleanup.js +578 -0
  176. package/dist/core/list.d.ts +9 -0
  177. package/dist/core/list.js +172 -0
  178. package/dist/core/migration.d.ts +23 -0
  179. package/dist/core/migration.js +108 -0
  180. package/dist/core/parsers/change-parser.d.ts +13 -0
  181. package/dist/core/parsers/change-parser.js +197 -0
  182. package/dist/core/parsers/markdown-parser.d.ts +26 -0
  183. package/dist/core/parsers/markdown-parser.js +227 -0
  184. package/dist/core/parsers/requirement-blocks.d.ts +37 -0
  185. package/dist/core/parsers/requirement-blocks.js +201 -0
  186. package/dist/core/parsers/spec-structure.d.ts +9 -0
  187. package/dist/core/parsers/spec-structure.js +88 -0
  188. package/dist/core/profile-sync-drift.d.ts +38 -0
  189. package/dist/core/profile-sync-drift.js +197 -0
  190. package/dist/core/profiles.d.ts +26 -0
  191. package/dist/core/profiles.js +37 -0
  192. package/dist/core/project-config.d.ts +64 -0
  193. package/dist/core/project-config.js +224 -0
  194. package/dist/core/schemas/base.schema.d.ts +13 -0
  195. package/dist/core/schemas/base.schema.js +13 -0
  196. package/dist/core/schemas/change.schema.d.ts +73 -0
  197. package/dist/core/schemas/change.schema.js +31 -0
  198. package/dist/core/schemas/index.d.ts +4 -0
  199. package/dist/core/schemas/index.js +4 -0
  200. package/dist/core/schemas/spec.schema.d.ts +18 -0
  201. package/dist/core/schemas/spec.schema.js +15 -0
  202. package/dist/core/shared/index.d.ts +8 -0
  203. package/dist/core/shared/index.js +8 -0
  204. package/dist/core/shared/skill-generation.d.ts +49 -0
  205. package/dist/core/shared/skill-generation.js +90 -0
  206. package/dist/core/shared/tool-detection.d.ts +71 -0
  207. package/dist/core/shared/tool-detection.js +152 -0
  208. package/dist/core/specs-apply.d.ts +73 -0
  209. package/dist/core/specs-apply.js +393 -0
  210. package/dist/core/styles/palette.d.ts +7 -0
  211. package/dist/core/styles/palette.js +8 -0
  212. package/dist/core/templates/index.d.ts +8 -0
  213. package/dist/core/templates/index.js +9 -0
  214. package/dist/core/templates/skill-templates.d.ts +15 -0
  215. package/dist/core/templates/skill-templates.js +14 -0
  216. package/dist/core/templates/types.d.ts +19 -0
  217. package/dist/core/templates/types.js +5 -0
  218. package/dist/core/templates/workflows/analyze.d.ts +4 -0
  219. package/dist/core/templates/workflows/analyze.js +101 -0
  220. package/dist/core/templates/workflows/apply-change.d.ts +10 -0
  221. package/dist/core/templates/workflows/apply-change.js +308 -0
  222. package/dist/core/templates/workflows/archive-change.d.ts +10 -0
  223. package/dist/core/templates/workflows/archive-change.js +271 -0
  224. package/dist/core/templates/workflows/clarify.d.ts +4 -0
  225. package/dist/core/templates/workflows/clarify.js +108 -0
  226. package/dist/core/templates/workflows/debug.d.ts +4 -0
  227. package/dist/core/templates/workflows/debug.js +117 -0
  228. package/dist/core/templates/workflows/explore.d.ts +10 -0
  229. package/dist/core/templates/workflows/explore.js +479 -0
  230. package/dist/core/templates/workflows/propose.d.ts +10 -0
  231. package/dist/core/templates/workflows/propose.js +216 -0
  232. package/dist/core/templates/workflows/sync.d.ts +4 -0
  233. package/dist/core/templates/workflows/sync.js +108 -0
  234. package/dist/core/update.d.ts +82 -0
  235. package/dist/core/update.js +555 -0
  236. package/dist/core/validation/constants.d.ts +34 -0
  237. package/dist/core/validation/constants.js +40 -0
  238. package/dist/core/validation/types.d.ts +18 -0
  239. package/dist/core/validation/types.js +2 -0
  240. package/dist/core/validation/validator.d.ts +33 -0
  241. package/dist/core/validation/validator.js +418 -0
  242. package/dist/core/view.d.ts +8 -0
  243. package/dist/core/view.js +169 -0
  244. package/dist/core/workspace/foundation.d.ts +79 -0
  245. package/dist/core/workspace/foundation.js +367 -0
  246. package/dist/core/workspace/index.d.ts +5 -0
  247. package/dist/core/workspace/index.js +5 -0
  248. package/dist/core/workspace/link-input.d.ts +9 -0
  249. package/dist/core/workspace/link-input.js +32 -0
  250. package/dist/core/workspace/open-surface.d.ts +24 -0
  251. package/dist/core/workspace/open-surface.js +137 -0
  252. package/dist/core/workspace/openers.d.ts +21 -0
  253. package/dist/core/workspace/openers.js +119 -0
  254. package/dist/index.d.ts +3 -0
  255. package/dist/index.js +3 -0
  256. package/dist/prompts/searchable-multi-select.d.ts +28 -0
  257. package/dist/prompts/searchable-multi-select.js +159 -0
  258. package/dist/ui/ascii-patterns.d.ts +25 -0
  259. package/dist/ui/ascii-patterns.js +140 -0
  260. package/dist/ui/welcome-screen.d.ts +10 -0
  261. package/dist/ui/welcome-screen.js +144 -0
  262. package/dist/utils/change-metadata.d.ts +51 -0
  263. package/dist/utils/change-metadata.js +147 -0
  264. package/dist/utils/change-utils.d.ts +62 -0
  265. package/dist/utils/change-utils.js +122 -0
  266. package/dist/utils/command-references.d.ts +18 -0
  267. package/dist/utils/command-references.js +20 -0
  268. package/dist/utils/file-system.d.ts +41 -0
  269. package/dist/utils/file-system.js +301 -0
  270. package/dist/utils/index.d.ts +6 -0
  271. package/dist/utils/index.js +9 -0
  272. package/dist/utils/interactive.d.ts +18 -0
  273. package/dist/utils/interactive.js +21 -0
  274. package/dist/utils/item-discovery.d.ts +4 -0
  275. package/dist/utils/item-discovery.js +73 -0
  276. package/dist/utils/match.d.ts +3 -0
  277. package/dist/utils/match.js +22 -0
  278. package/dist/utils/shell-detection.d.ts +20 -0
  279. package/dist/utils/shell-detection.js +41 -0
  280. package/dist/utils/task-progress.d.ts +8 -0
  281. package/dist/utils/task-progress.js +36 -0
  282. package/package.json +76 -0
  283. package/schemas/synarcx/schema.yaml +153 -0
  284. package/schemas/synarcx/templates/design.md +19 -0
  285. package/schemas/synarcx/templates/proposal.md +23 -0
  286. package/schemas/synarcx/templates/spec.md +8 -0
  287. package/schemas/synarcx/templates/tasks.md +9 -0
  288. package/scripts/postinstall.js +83 -0
@@ -0,0 +1,319 @@
1
+ import { promises as fs } from 'fs';
2
+ import path from 'path';
3
+ import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progress.js';
4
+ import { Validator } from './validation/validator.js';
5
+ import chalk from 'chalk';
6
+ import { findSpecUpdates, buildUpdatedSpec, writeUpdatedSpec, } from './specs-apply.js';
7
+ import { SYNSPEC_DIR_NAME } from './config.js';
8
+ /**
9
+ * Recursively copy a directory. Used when fs.rename fails (e.g. EPERM on Windows).
10
+ */
11
+ async function copyDirRecursive(src, dest) {
12
+ await fs.mkdir(dest, { recursive: true });
13
+ const entries = await fs.readdir(src, { withFileTypes: true });
14
+ for (const entry of entries) {
15
+ const srcPath = path.join(src, entry.name);
16
+ const destPath = path.join(dest, entry.name);
17
+ if (entry.isDirectory()) {
18
+ await copyDirRecursive(srcPath, destPath);
19
+ }
20
+ else {
21
+ await fs.copyFile(srcPath, destPath);
22
+ }
23
+ }
24
+ }
25
+ /**
26
+ * Move a directory from src to dest. On Windows, fs.rename() often fails with
27
+ * EPERM when the directory is non-empty or another process has it open (IDE,
28
+ * file watcher, antivirus). Fall back to copy-then-remove when rename fails
29
+ * with EPERM or EXDEV.
30
+ */
31
+ async function moveDirectory(src, dest) {
32
+ try {
33
+ await fs.rename(src, dest);
34
+ }
35
+ catch (err) {
36
+ const code = err?.code;
37
+ if (code === 'EPERM' || code === 'EXDEV') {
38
+ await copyDirRecursive(src, dest);
39
+ await fs.rm(src, { recursive: true, force: true });
40
+ }
41
+ else {
42
+ throw err;
43
+ }
44
+ }
45
+ }
46
+ export class ArchiveCommand {
47
+ async execute(changeName, options = {}) {
48
+ const targetPath = '.';
49
+ const changesDir = path.join(targetPath, SYNSPEC_DIR_NAME, 'changes');
50
+ const archiveDir = path.join(changesDir, 'archive');
51
+ const mainSpecsDir = path.join(targetPath, SYNSPEC_DIR_NAME, 'specs');
52
+ // Check if changes directory exists
53
+ try {
54
+ await fs.access(changesDir);
55
+ }
56
+ catch {
57
+ throw new Error("No synarcx changes directory found. Run 'synarcx init' first.");
58
+ }
59
+ // Get change name interactively if not provided
60
+ if (!changeName) {
61
+ const selectedChange = await this.selectChange(changesDir);
62
+ if (!selectedChange) {
63
+ console.log('No change selected. Aborting.');
64
+ return;
65
+ }
66
+ changeName = selectedChange;
67
+ }
68
+ const changeDir = path.join(changesDir, changeName);
69
+ // Verify change exists
70
+ try {
71
+ const stat = await fs.stat(changeDir);
72
+ if (!stat.isDirectory()) {
73
+ throw new Error(`Change '${changeName}' not found.`);
74
+ }
75
+ }
76
+ catch {
77
+ throw new Error(`Change '${changeName}' not found.`);
78
+ }
79
+ const skipValidation = options.validate === false || options.noValidate === true;
80
+ // Validate specs and change before archiving
81
+ if (!skipValidation) {
82
+ const validator = new Validator();
83
+ let hasValidationErrors = false;
84
+ // Validate proposal.md (non-blocking unless strict mode desired in future)
85
+ const changeFile = path.join(changeDir, 'proposal.md');
86
+ try {
87
+ await fs.access(changeFile);
88
+ const changeReport = await validator.validateChange(changeFile);
89
+ // Proposal validation is informative only (do not block archive)
90
+ if (!changeReport.valid) {
91
+ console.log(chalk.yellow(`\nProposal warnings in proposal.md (non-blocking):`));
92
+ for (const issue of changeReport.issues) {
93
+ const symbol = issue.level === 'ERROR' ? '⚠' : (issue.level === 'WARNING' ? '⚠' : 'ℹ');
94
+ console.log(chalk.yellow(` ${symbol} ${issue.message}`));
95
+ }
96
+ }
97
+ }
98
+ catch {
99
+ // Change file doesn't exist, skip validation
100
+ }
101
+ // Validate delta-formatted spec files under the change directory if present
102
+ const changeSpecsDir = path.join(changeDir, 'specs');
103
+ let hasDeltaSpecs = false;
104
+ try {
105
+ const candidates = await fs.readdir(changeSpecsDir, { withFileTypes: true });
106
+ for (const c of candidates) {
107
+ if (c.isDirectory()) {
108
+ try {
109
+ const candidatePath = path.join(changeSpecsDir, c.name, 'spec.md');
110
+ await fs.access(candidatePath);
111
+ const content = await fs.readFile(candidatePath, 'utf-8');
112
+ if (/^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements/m.test(content)) {
113
+ hasDeltaSpecs = true;
114
+ break;
115
+ }
116
+ }
117
+ catch { }
118
+ }
119
+ }
120
+ }
121
+ catch { }
122
+ if (hasDeltaSpecs) {
123
+ const deltaReport = await validator.validateChangeDeltaSpecs(changeDir);
124
+ if (!deltaReport.valid) {
125
+ hasValidationErrors = true;
126
+ console.log(chalk.red(`\nValidation errors in change delta specs:`));
127
+ for (const issue of deltaReport.issues) {
128
+ if (issue.level === 'ERROR') {
129
+ console.log(chalk.red(` ✗ ${issue.message}`));
130
+ }
131
+ else if (issue.level === 'WARNING') {
132
+ console.log(chalk.yellow(` ⚠ ${issue.message}`));
133
+ }
134
+ }
135
+ }
136
+ }
137
+ if (hasValidationErrors) {
138
+ console.log(chalk.red('\nValidation failed. Please fix the errors before archiving.'));
139
+ console.log(chalk.yellow('To skip validation (not recommended), use --no-validate flag.'));
140
+ return;
141
+ }
142
+ }
143
+ else {
144
+ // Log warning when validation is skipped
145
+ const timestamp = new Date().toISOString();
146
+ if (!options.yes) {
147
+ const { confirm } = await import('@inquirer/prompts');
148
+ const proceed = await confirm({
149
+ message: chalk.yellow('⚠️ WARNING: Skipping validation may archive invalid specs. Continue? (y/N)'),
150
+ default: false
151
+ });
152
+ if (!proceed) {
153
+ console.log('Archive cancelled.');
154
+ return;
155
+ }
156
+ }
157
+ else {
158
+ console.log(chalk.yellow(`\n⚠️ WARNING: Skipping validation may archive invalid specs.`));
159
+ }
160
+ console.log(chalk.yellow(`[${timestamp}] Validation skipped for change: ${changeName}`));
161
+ console.log(chalk.yellow(`Affected files: ${changeDir}`));
162
+ }
163
+ // Show progress and check for incomplete tasks
164
+ const progress = await getTaskProgressForChange(changesDir, changeName);
165
+ const status = formatTaskStatus(progress);
166
+ console.log(`Task status: ${status}`);
167
+ const incompleteTasks = Math.max(progress.total - progress.completed, 0);
168
+ if (incompleteTasks > 0) {
169
+ if (!options.yes) {
170
+ const { confirm } = await import('@inquirer/prompts');
171
+ const proceed = await confirm({
172
+ message: `Warning: ${incompleteTasks} incomplete task(s) found. Continue?`,
173
+ default: false
174
+ });
175
+ if (!proceed) {
176
+ console.log('Archive cancelled.');
177
+ return;
178
+ }
179
+ }
180
+ else {
181
+ console.log(`Warning: ${incompleteTasks} incomplete task(s) found. Continuing due to --yes flag.`);
182
+ }
183
+ }
184
+ // Handle spec updates unless skipSpecs flag is set
185
+ if (options.skipSpecs) {
186
+ console.log('Skipping spec updates (--skip-specs flag provided).');
187
+ }
188
+ else {
189
+ // Find specs to update
190
+ const specUpdates = await findSpecUpdates(changeDir, mainSpecsDir);
191
+ if (specUpdates.length > 0) {
192
+ console.log('\nSpecs to update:');
193
+ for (const update of specUpdates) {
194
+ const status = update.exists ? 'update' : 'create';
195
+ const capability = path.basename(path.dirname(update.target));
196
+ console.log(` ${capability}: ${status}`);
197
+ }
198
+ let shouldUpdateSpecs = true;
199
+ if (!options.yes) {
200
+ const { confirm } = await import('@inquirer/prompts');
201
+ shouldUpdateSpecs = await confirm({
202
+ message: 'Proceed with spec updates?',
203
+ default: true
204
+ });
205
+ if (!shouldUpdateSpecs) {
206
+ console.log('Skipping spec updates. Proceeding with archive.');
207
+ }
208
+ }
209
+ if (shouldUpdateSpecs) {
210
+ // Prepare all updates first (validation pass, no writes)
211
+ const prepared = [];
212
+ try {
213
+ for (const update of specUpdates) {
214
+ const built = await buildUpdatedSpec(update, changeName);
215
+ prepared.push({ update, rebuilt: built.rebuilt, counts: built.counts });
216
+ }
217
+ }
218
+ catch (err) {
219
+ console.log(String(err.message || err));
220
+ console.log('Aborted. No files were changed.');
221
+ return;
222
+ }
223
+ // All validations passed; pre-validate rebuilt full spec and then write files and display counts
224
+ let totals = { added: 0, modified: 0, removed: 0, renamed: 0 };
225
+ for (const p of prepared) {
226
+ const specName = path.basename(path.dirname(p.update.target));
227
+ if (!skipValidation) {
228
+ const report = await new Validator().validateSpecContent(specName, p.rebuilt);
229
+ if (!report.valid) {
230
+ console.log(chalk.red(`\nValidation errors in rebuilt spec for ${specName} (will not write changes):`));
231
+ for (const issue of report.issues) {
232
+ if (issue.level === 'ERROR')
233
+ console.log(chalk.red(` ✗ ${issue.message}`));
234
+ else if (issue.level === 'WARNING')
235
+ console.log(chalk.yellow(` ⚠ ${issue.message}`));
236
+ }
237
+ console.log('Aborted. No files were changed.');
238
+ return;
239
+ }
240
+ }
241
+ await writeUpdatedSpec(p.update, p.rebuilt, p.counts);
242
+ totals.added += p.counts.added;
243
+ totals.modified += p.counts.modified;
244
+ totals.removed += p.counts.removed;
245
+ totals.renamed += p.counts.renamed;
246
+ }
247
+ console.log(`Totals: + ${totals.added}, ~ ${totals.modified}, - ${totals.removed}, → ${totals.renamed}`);
248
+ console.log('Specs updated successfully.');
249
+ }
250
+ }
251
+ }
252
+ // Create archive directory with date prefix
253
+ const archiveName = `${this.getArchiveDate()}-${changeName}`;
254
+ const archivePath = path.join(archiveDir, archiveName);
255
+ // Check if archive already exists
256
+ try {
257
+ await fs.access(archivePath);
258
+ throw new Error(`Archive '${archiveName}' already exists.`);
259
+ }
260
+ catch (error) {
261
+ if (error.code !== 'ENOENT') {
262
+ throw error;
263
+ }
264
+ }
265
+ // Create archive directory if needed
266
+ await fs.mkdir(archiveDir, { recursive: true });
267
+ // Move change to archive (uses copy+remove on EPERM/EXDEV, e.g. Windows)
268
+ await moveDirectory(changeDir, archivePath);
269
+ console.log(`Change '${changeName}' archived as '${archiveName}'.`);
270
+ }
271
+ async selectChange(changesDir) {
272
+ const { select } = await import('@inquirer/prompts');
273
+ // Get all directories in changes (excluding archive)
274
+ const entries = await fs.readdir(changesDir, { withFileTypes: true });
275
+ const changeDirs = entries
276
+ .filter(entry => entry.isDirectory() && entry.name !== 'archive')
277
+ .map(entry => entry.name)
278
+ .sort();
279
+ if (changeDirs.length === 0) {
280
+ console.log('No active changes found.');
281
+ return null;
282
+ }
283
+ // Build choices with progress inline to avoid duplicate lists
284
+ let choices = changeDirs.map(name => ({ name, value: name }));
285
+ try {
286
+ const progressList = [];
287
+ for (const id of changeDirs) {
288
+ const progress = await getTaskProgressForChange(changesDir, id);
289
+ const status = formatTaskStatus(progress);
290
+ progressList.push({ id, status });
291
+ }
292
+ const nameWidth = Math.max(...progressList.map(p => p.id.length));
293
+ choices = progressList.map(p => ({
294
+ name: `${p.id.padEnd(nameWidth)} ${p.status}`,
295
+ value: p.id
296
+ }));
297
+ }
298
+ catch {
299
+ // If anything fails, fall back to simple names
300
+ choices = changeDirs.map(name => ({ name, value: name }));
301
+ }
302
+ try {
303
+ const answer = await select({
304
+ message: 'Select a change to archive',
305
+ choices
306
+ });
307
+ return answer;
308
+ }
309
+ catch (error) {
310
+ // User cancelled (Ctrl+C)
311
+ return null;
312
+ }
313
+ }
314
+ getArchiveDate() {
315
+ // Returns date in YYYY-MM-DD format
316
+ return new Date().toISOString().split('T')[0];
317
+ }
318
+ }
319
+ //# sourceMappingURL=archive.js.map
@@ -0,0 +1,56 @@
1
+ import type { Artifact, SchemaYaml, CompletedSet, BlockedArtifacts } from './types.js';
2
+ /**
3
+ * Represents an artifact dependency graph.
4
+ * Provides methods for querying build order, ready artifacts, and completion status.
5
+ */
6
+ export declare class ArtifactGraph {
7
+ private artifacts;
8
+ private schema;
9
+ private constructor();
10
+ /**
11
+ * Creates an ArtifactGraph from a YAML file path.
12
+ */
13
+ static fromYaml(filePath: string): ArtifactGraph;
14
+ /**
15
+ * Creates an ArtifactGraph from YAML content string.
16
+ */
17
+ static fromYamlContent(yamlContent: string): ArtifactGraph;
18
+ /**
19
+ * Creates an ArtifactGraph from a pre-validated schema object.
20
+ */
21
+ static fromSchema(schema: SchemaYaml): ArtifactGraph;
22
+ /**
23
+ * Gets a single artifact by ID.
24
+ */
25
+ getArtifact(id: string): Artifact | undefined;
26
+ /**
27
+ * Gets all artifacts in the graph.
28
+ */
29
+ getAllArtifacts(): Artifact[];
30
+ /**
31
+ * Gets the schema name.
32
+ */
33
+ getName(): string;
34
+ /**
35
+ * Gets the schema version.
36
+ */
37
+ getVersion(): number;
38
+ /**
39
+ * Computes the topological build order using Kahn's algorithm.
40
+ * Returns artifact IDs in the order they should be built.
41
+ */
42
+ getBuildOrder(): string[];
43
+ /**
44
+ * Gets artifacts that are ready to be created (all dependencies completed).
45
+ */
46
+ getNextArtifacts(completed: CompletedSet): string[];
47
+ /**
48
+ * Checks if all artifacts in the graph are completed.
49
+ */
50
+ isComplete(completed: CompletedSet): boolean;
51
+ /**
52
+ * Gets blocked artifacts and their unmet dependencies.
53
+ */
54
+ getBlocked(completed: CompletedSet): BlockedArtifacts;
55
+ }
56
+ //# sourceMappingURL=graph.d.ts.map
@@ -0,0 +1,141 @@
1
+ import { loadSchema, parseSchema } from './schema.js';
2
+ /**
3
+ * Represents an artifact dependency graph.
4
+ * Provides methods for querying build order, ready artifacts, and completion status.
5
+ */
6
+ export class ArtifactGraph {
7
+ artifacts;
8
+ schema;
9
+ constructor(schema) {
10
+ this.schema = schema;
11
+ this.artifacts = new Map(schema.artifacts.map(a => [a.id, a]));
12
+ }
13
+ /**
14
+ * Creates an ArtifactGraph from a YAML file path.
15
+ */
16
+ static fromYaml(filePath) {
17
+ const schema = loadSchema(filePath);
18
+ return new ArtifactGraph(schema);
19
+ }
20
+ /**
21
+ * Creates an ArtifactGraph from YAML content string.
22
+ */
23
+ static fromYamlContent(yamlContent) {
24
+ const schema = parseSchema(yamlContent);
25
+ return new ArtifactGraph(schema);
26
+ }
27
+ /**
28
+ * Creates an ArtifactGraph from a pre-validated schema object.
29
+ */
30
+ static fromSchema(schema) {
31
+ return new ArtifactGraph(schema);
32
+ }
33
+ /**
34
+ * Gets a single artifact by ID.
35
+ */
36
+ getArtifact(id) {
37
+ return this.artifacts.get(id);
38
+ }
39
+ /**
40
+ * Gets all artifacts in the graph.
41
+ */
42
+ getAllArtifacts() {
43
+ return Array.from(this.artifacts.values());
44
+ }
45
+ /**
46
+ * Gets the schema name.
47
+ */
48
+ getName() {
49
+ return this.schema.name;
50
+ }
51
+ /**
52
+ * Gets the schema version.
53
+ */
54
+ getVersion() {
55
+ return this.schema.version;
56
+ }
57
+ /**
58
+ * Computes the topological build order using Kahn's algorithm.
59
+ * Returns artifact IDs in the order they should be built.
60
+ */
61
+ getBuildOrder() {
62
+ const inDegree = new Map();
63
+ const dependents = new Map();
64
+ // Initialize all artifacts
65
+ for (const artifact of this.artifacts.values()) {
66
+ inDegree.set(artifact.id, artifact.requires.length);
67
+ dependents.set(artifact.id, []);
68
+ }
69
+ // Build reverse adjacency (who depends on whom)
70
+ for (const artifact of this.artifacts.values()) {
71
+ for (const req of artifact.requires) {
72
+ dependents.get(req).push(artifact.id);
73
+ }
74
+ }
75
+ // Start with roots (in-degree 0), sorted for determinism
76
+ const queue = [...this.artifacts.keys()]
77
+ .filter(id => inDegree.get(id) === 0)
78
+ .sort();
79
+ const result = [];
80
+ while (queue.length > 0) {
81
+ const current = queue.shift();
82
+ result.push(current);
83
+ // Collect newly ready artifacts, then sort before adding
84
+ const newlyReady = [];
85
+ for (const dep of dependents.get(current)) {
86
+ const newDegree = inDegree.get(dep) - 1;
87
+ inDegree.set(dep, newDegree);
88
+ if (newDegree === 0) {
89
+ newlyReady.push(dep);
90
+ }
91
+ }
92
+ queue.push(...newlyReady.sort());
93
+ }
94
+ return result;
95
+ }
96
+ /**
97
+ * Gets artifacts that are ready to be created (all dependencies completed).
98
+ */
99
+ getNextArtifacts(completed) {
100
+ const ready = [];
101
+ for (const artifact of this.artifacts.values()) {
102
+ if (completed.has(artifact.id)) {
103
+ continue; // Already completed
104
+ }
105
+ const allDepsCompleted = artifact.requires.every(req => completed.has(req));
106
+ if (allDepsCompleted) {
107
+ ready.push(artifact.id);
108
+ }
109
+ }
110
+ // Sort for deterministic ordering
111
+ return ready.sort();
112
+ }
113
+ /**
114
+ * Checks if all artifacts in the graph are completed.
115
+ */
116
+ isComplete(completed) {
117
+ for (const artifact of this.artifacts.values()) {
118
+ if (!completed.has(artifact.id)) {
119
+ return false;
120
+ }
121
+ }
122
+ return true;
123
+ }
124
+ /**
125
+ * Gets blocked artifacts and their unmet dependencies.
126
+ */
127
+ getBlocked(completed) {
128
+ const blocked = {};
129
+ for (const artifact of this.artifacts.values()) {
130
+ if (completed.has(artifact.id)) {
131
+ continue; // Already completed
132
+ }
133
+ const unmetDeps = artifact.requires.filter(req => !completed.has(req));
134
+ if (unmetDeps.length > 0) {
135
+ blocked[artifact.id] = unmetDeps.sort();
136
+ }
137
+ }
138
+ return blocked;
139
+ }
140
+ }
141
+ //# sourceMappingURL=graph.js.map
@@ -0,0 +1,8 @@
1
+ export { ArtifactSchema, SchemaYamlSchema, type Artifact, type SchemaYaml, type CompletedSet, type BlockedArtifacts, } from './types.js';
2
+ export { loadSchema, parseSchema, SchemaValidationError } from './schema.js';
3
+ export { ArtifactGraph } from './graph.js';
4
+ export { detectCompleted } from './state.js';
5
+ export { artifactOutputExists, isGlobPattern, resolveArtifactOutputs } from './outputs.js';
6
+ export { resolveSchema, listSchemas, listSchemasWithInfo, getSchemaDir, getPackageSchemasDir, getUserSchemasDir, SchemaLoadError, type SchemaInfo, } from './resolver.js';
7
+ export { loadTemplate, loadChangeContext, generateInstructions, formatChangeStatus, TemplateLoadError, type ChangeContext, type ArtifactInstructions, type DependencyInfo, type ArtifactStatus, type ChangeStatus, } from './instruction-loader.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,14 @@
1
+ // Types
2
+ export { ArtifactSchema, SchemaYamlSchema, } from './types.js';
3
+ // Schema loading and validation
4
+ export { loadSchema, parseSchema, SchemaValidationError } from './schema.js';
5
+ // Graph operations
6
+ export { ArtifactGraph } from './graph.js';
7
+ // State detection
8
+ export { detectCompleted } from './state.js';
9
+ export { artifactOutputExists, isGlobPattern, resolveArtifactOutputs } from './outputs.js';
10
+ // Schema resolution
11
+ export { resolveSchema, listSchemas, listSchemasWithInfo, getSchemaDir, getPackageSchemasDir, getUserSchemasDir, SchemaLoadError, } from './resolver.js';
12
+ // Instruction loading
13
+ export { loadTemplate, loadChangeContext, generateInstructions, formatChangeStatus, TemplateLoadError, } from './instruction-loader.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,143 @@
1
+ import { ArtifactGraph } from './graph.js';
2
+ import type { CompletedSet } from './types.js';
3
+ /**
4
+ * Error thrown when loading a template fails.
5
+ */
6
+ export declare class TemplateLoadError extends Error {
7
+ readonly templatePath: string;
8
+ constructor(message: string, templatePath: string);
9
+ }
10
+ /**
11
+ * Change context containing graph, completion state, and metadata.
12
+ */
13
+ export interface ChangeContext {
14
+ /** The artifact dependency graph */
15
+ graph: ArtifactGraph;
16
+ /** Set of completed artifact IDs */
17
+ completed: CompletedSet;
18
+ /** Schema name being used */
19
+ schemaName: string;
20
+ /** Change name */
21
+ changeName: string;
22
+ /** Path to the change directory */
23
+ changeDir: string;
24
+ /** Project root directory */
25
+ projectRoot: string;
26
+ }
27
+ /**
28
+ * Enriched instructions for creating an artifact.
29
+ */
30
+ export interface ArtifactInstructions {
31
+ /** Change name */
32
+ changeName: string;
33
+ /** Artifact ID */
34
+ artifactId: string;
35
+ /** Schema name */
36
+ schemaName: string;
37
+ /** Full path to change directory */
38
+ changeDir: string;
39
+ /** Output path pattern (e.g., "proposal.md") */
40
+ outputPath: string;
41
+ /** Artifact description */
42
+ description: string;
43
+ /** Guidance on how to create this artifact (from schema instruction field) */
44
+ instruction: string | undefined;
45
+ /** Project context from config (constraints/background for AI, not to be included in output) */
46
+ context: string | undefined;
47
+ /** Artifact-specific rules from config (constraints for AI, not to be included in output) */
48
+ rules: string[] | undefined;
49
+ /** Template content (structure to follow - this IS the output format) */
50
+ template: string;
51
+ /** Dependencies with completion status and paths */
52
+ dependencies: DependencyInfo[];
53
+ /** Artifacts that become available after completing this one */
54
+ unlocks: string[];
55
+ }
56
+ /**
57
+ * Dependency information including path and description.
58
+ */
59
+ export interface DependencyInfo {
60
+ /** Artifact ID */
61
+ id: string;
62
+ /** Whether the dependency is completed */
63
+ done: boolean;
64
+ /** Relative output path of the dependency (e.g., "proposal.md") */
65
+ path: string;
66
+ /** Description of the dependency artifact */
67
+ description: string;
68
+ }
69
+ /**
70
+ * Status of a single artifact in the workflow.
71
+ */
72
+ export interface ArtifactStatus {
73
+ /** Artifact ID */
74
+ id: string;
75
+ /** Output path pattern */
76
+ outputPath: string;
77
+ /** Status: done, ready, or blocked */
78
+ status: 'done' | 'ready' | 'blocked';
79
+ /** Missing dependencies (only for blocked) */
80
+ missingDeps?: string[];
81
+ }
82
+ /**
83
+ * Formatted change status.
84
+ */
85
+ export interface ChangeStatus {
86
+ /** Change name */
87
+ changeName: string;
88
+ /** Schema name */
89
+ schemaName: string;
90
+ /** Whether all artifacts are complete */
91
+ isComplete: boolean;
92
+ /** Artifact IDs required before apply phase (from schema's apply.requires) */
93
+ applyRequires: string[];
94
+ /** Status of each artifact */
95
+ artifacts: ArtifactStatus[];
96
+ }
97
+ /**
98
+ * Loads a template from a schema's templates directory.
99
+ *
100
+ * @param schemaName - Schema name (e.g., "synarcx")
101
+ * @param templatePath - Relative path within the templates directory (e.g., "proposal.md")
102
+ * @param projectRoot - Optional project root for project-local schema resolution
103
+ * @returns The template content
104
+ * @throws TemplateLoadError if the template cannot be loaded
105
+ */
106
+ export declare function loadTemplate(schemaName: string, templatePath: string, projectRoot?: string): string;
107
+ /**
108
+ * Loads change context combining graph and completion state.
109
+ *
110
+ * Schema resolution order:
111
+ * 1. Explicit schemaName parameter (if provided)
112
+ * 2. Schema from .synspec.yaml metadata (if exists in change directory)
113
+ * 3. Default 'synarcx'
114
+ *
115
+ * @param projectRoot - Project root directory
116
+ * @param changeName - Change name
117
+ * @param schemaName - Optional schema name override. If not provided, auto-detected from metadata.
118
+ * @returns Change context with graph, completed set, and metadata
119
+ */
120
+ export declare function loadChangeContext(projectRoot: string, changeName: string, schemaName?: string): ChangeContext;
121
+ /**
122
+ * Generates enriched instructions for creating an artifact.
123
+ *
124
+ * Instruction injection order:
125
+ * 1. <context> - Project context from config (if present)
126
+ * 2. <rules> - Artifact-specific rules from config (if present)
127
+ * 3. <template> - Schema's template content
128
+ *
129
+ * @param context - Change context
130
+ * @param artifactId - Artifact ID to generate instructions for
131
+ * @param projectRoot - Project root directory (for reading config)
132
+ * @returns Enriched artifact instructions
133
+ * @throws Error if artifact not found
134
+ */
135
+ export declare function generateInstructions(context: ChangeContext, artifactId: string, projectRoot?: string): ArtifactInstructions;
136
+ /**
137
+ * Formats the status of all artifacts in a change.
138
+ *
139
+ * @param context - Change context
140
+ * @returns Formatted change status
141
+ */
142
+ export declare function formatChangeStatus(context: ChangeContext): ChangeStatus;
143
+ //# sourceMappingURL=instruction-loader.d.ts.map