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,461 @@
1
+ import * as nodeFs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { getManagedWorkspaceRoot, getWorkspaceChangesDir, isWorkspaceRoot, parseWorkspaceSetupLinkInput, readOptionalWorkspaceLocalState, readWorkspaceRegistryState, readWorkspaceSharedState, syncWorkspaceOpenSurface, validateWorkspaceLinkName, validateWorkspaceName, writeWorkspaceLocalState, writeWorkspaceRegistryState, writeWorkspaceSharedState, } from '../../core/workspace/index.js';
4
+ import { FileSystemUtils } from '../../utils/file-system.js';
5
+ import { SYNSPEC_DIR_NAME } from '../../core/config.js';
6
+ import { WorkspaceCliError, asErrorMessage, makeStatus, } from './types.js';
7
+ const fs = nodeFs.promises;
8
+ function emptyRegistry() {
9
+ return { version: 1, workspaces: {} };
10
+ }
11
+ function emptyLocalState() {
12
+ return { version: 1, paths: {} };
13
+ }
14
+ export async function readRegistry() {
15
+ return (await readWorkspaceRegistryState()) ?? emptyRegistry();
16
+ }
17
+ async function recordWorkspaceInRegistry(name, workspaceRoot) {
18
+ const registry = await readRegistry();
19
+ const recordedWorkspaceRoot = normalizeExistingPathForStorage(workspaceRoot);
20
+ await writeWorkspaceRegistryState({
21
+ version: 1,
22
+ workspaces: {
23
+ ...registry.workspaces,
24
+ [name]: recordedWorkspaceRoot,
25
+ },
26
+ });
27
+ }
28
+ export async function directoryExists(dirPath) {
29
+ try {
30
+ return (await fs.stat(dirPath)).isDirectory();
31
+ }
32
+ catch {
33
+ return false;
34
+ }
35
+ }
36
+ function normalizeExistingPathForStorage(existingPath) {
37
+ return process.platform === 'win32'
38
+ ? FileSystemUtils.canonicalizeExistingPath(existingPath)
39
+ : existingPath;
40
+ }
41
+ export async function resolveExistingDirectory(inputPath, cwd = process.cwd()) {
42
+ if (inputPath.length === 0) {
43
+ throw new WorkspaceCliError('Repo or folder path must not be empty.', 'linked_path_empty', {
44
+ target: 'link.path',
45
+ fix: 'Choose an existing repo or folder path.',
46
+ });
47
+ }
48
+ const resolvedPath = path.isAbsolute(inputPath)
49
+ ? path.resolve(inputPath)
50
+ : path.resolve(cwd, inputPath);
51
+ if (!(await directoryExists(resolvedPath))) {
52
+ throw new WorkspaceCliError(`Path '${inputPath}' is not an existing folder.`, 'linked_path_missing', {
53
+ target: 'link.path',
54
+ fix: 'Choose an existing repo or folder path.',
55
+ });
56
+ }
57
+ return normalizeExistingPathForStorage(resolvedPath);
58
+ }
59
+ export function inferLinkName(absolutePath) {
60
+ return path.basename(absolutePath);
61
+ }
62
+ function normalizeLinksForOutput(sharedState, localState) {
63
+ return Object.keys(sharedState.links)
64
+ .sort((a, b) => a.localeCompare(b))
65
+ .map((name) => ({
66
+ name,
67
+ path: localState?.paths[name] ?? null,
68
+ status: [],
69
+ }));
70
+ }
71
+ function formatDuplicateLinkMessage(linkName, existingPath, replacementPath) {
72
+ return [
73
+ `Cannot use link name '${linkName}' because another link already uses that name.`,
74
+ 'Existing link:',
75
+ ` ${linkName} -> ${existingPath ?? '(no local path recorded)'}`,
76
+ '',
77
+ 'Choose a different link name:',
78
+ ` synarcx workspace link archived-${linkName} ${replacementPath}`,
79
+ '',
80
+ 'If you meant to change the existing link path:',
81
+ ` synarcx workspace relink ${linkName} ${replacementPath}`,
82
+ ].join('\n');
83
+ }
84
+ function duplicateLinkError(linkName, existingPath, replacementPath) {
85
+ return new WorkspaceCliError(formatDuplicateLinkMessage(linkName, existingPath, replacementPath), 'duplicate_link_name', {
86
+ target: `links.${linkName}`,
87
+ fix: `Choose a different link name or run 'synarcx workspace relink ${linkName} ${replacementPath}'.`,
88
+ });
89
+ }
90
+ function duplicateSetupLinkError(linkName, existingPath, replacementPath) {
91
+ return new WorkspaceCliError([
92
+ `Cannot use link name '${linkName}' because another setup link already uses that name.`,
93
+ 'Existing link:',
94
+ ` ${linkName} -> ${existingPath}`,
95
+ '',
96
+ 'Use explicit --link <name>=<path> values with different names.',
97
+ ].join('\n'), 'duplicate_link_name', {
98
+ target: `links.${linkName}`,
99
+ fix: `Use explicit --link ${linkName}-alt=${replacementPath} with a different link name.`,
100
+ });
101
+ }
102
+ export function validateWorkspaceNameForSetup(name) {
103
+ try {
104
+ return validateWorkspaceName(name);
105
+ }
106
+ catch {
107
+ throw new WorkspaceCliError('Workspace name must be kebab-case with lowercase letters, numbers, and single hyphen separators.', 'invalid_workspace_name', {
108
+ target: 'workspace.name',
109
+ });
110
+ }
111
+ }
112
+ export function validateLinkNameForCommand(name) {
113
+ try {
114
+ return validateWorkspaceLinkName(name);
115
+ }
116
+ catch (error) {
117
+ throw new WorkspaceCliError(asErrorMessage(error), 'invalid_link_name', {
118
+ target: 'link.name',
119
+ });
120
+ }
121
+ }
122
+ function localStateInvalidStatus(error) {
123
+ return makeStatus('error', 'workspace_local_state_invalid', `Machine-local paths could not be read: ${asErrorMessage(error)}`, {
124
+ target: 'workspace.local_state',
125
+ fix: 'Repair or remove .synarcx-workspace/local.yaml, then run synarcx workspace relink <name> <path> for affected links.',
126
+ });
127
+ }
128
+ async function readLocalStateForMutation(workspaceRoot) {
129
+ try {
130
+ return (await readOptionalWorkspaceLocalState(workspaceRoot)) ?? emptyLocalState();
131
+ }
132
+ catch (error) {
133
+ const status = localStateInvalidStatus(error);
134
+ throw new WorkspaceCliError(status.message, status.code, {
135
+ target: status.target,
136
+ fix: status.fix,
137
+ });
138
+ }
139
+ }
140
+ export async function createManagedWorkspace(name, links, preferredOpener) {
141
+ const workspaceName = validateWorkspaceNameForSetup(name);
142
+ const workspaceRoot = getManagedWorkspaceRoot(workspaceName);
143
+ const registry = await readRegistry();
144
+ if (registry.workspaces[workspaceName]) {
145
+ throw new WorkspaceCliError(`Workspace '${workspaceName}' is already recorded in the local workspace registry at ${registry.workspaces[workspaceName]}.`, 'workspace_already_exists', {
146
+ target: 'workspace.name',
147
+ });
148
+ }
149
+ if (await directoryExists(workspaceRoot)) {
150
+ throw new WorkspaceCliError(`Workspace '${workspaceName}' already exists at ${workspaceRoot}.`, 'workspace_already_exists', {
151
+ target: 'workspace.root',
152
+ });
153
+ }
154
+ let createdWorkspaceRoot = false;
155
+ try {
156
+ await FileSystemUtils.createDirectory(path.dirname(workspaceRoot));
157
+ await fs.mkdir(workspaceRoot);
158
+ createdWorkspaceRoot = true;
159
+ await FileSystemUtils.createDirectory(getWorkspaceChangesDir(workspaceRoot));
160
+ const sharedState = {
161
+ version: 1,
162
+ name: workspaceName,
163
+ links: Object.fromEntries(Object.keys(links).map((linkName) => [linkName, {}])),
164
+ };
165
+ const localState = {
166
+ version: 1,
167
+ paths: links,
168
+ ...(preferredOpener ? { preferred_opener: preferredOpener } : {}),
169
+ };
170
+ await writeWorkspaceSharedState(workspaceRoot, sharedState);
171
+ await writeWorkspaceLocalState(workspaceRoot, localState);
172
+ await syncWorkspaceOpenSurface(workspaceRoot, sharedState, localState);
173
+ await recordWorkspaceInRegistry(workspaceName, workspaceRoot);
174
+ }
175
+ catch (error) {
176
+ if (createdWorkspaceRoot) {
177
+ try {
178
+ await fs.rm(workspaceRoot, { recursive: true, force: true });
179
+ }
180
+ catch {
181
+ // Preserve the original creation failure; callers can retry or inspect the path.
182
+ }
183
+ }
184
+ throw new WorkspaceCliError(`Could not create workspace '${workspaceName}': ${asErrorMessage(error)}`, 'workspace_create_failed', {
185
+ target: 'workspace.root',
186
+ });
187
+ }
188
+ return {
189
+ name: workspaceName,
190
+ root: workspaceRoot,
191
+ planning_path: getWorkspaceChangesDir(workspaceRoot),
192
+ links: Object.entries(links)
193
+ .sort(([a], [b]) => a.localeCompare(b))
194
+ .map(([linkName, linkPath]) => ({
195
+ name: linkName,
196
+ path: linkPath,
197
+ status: [],
198
+ })),
199
+ status: [],
200
+ };
201
+ }
202
+ export async function parseSetupLinks(linkInputs) {
203
+ const links = {};
204
+ for (const rawLink of linkInputs ?? []) {
205
+ const parsed = await parseWorkspaceSetupLinkInput(rawLink);
206
+ const resolvedPath = await resolveExistingDirectory(parsed.pathInput);
207
+ const linkName = validateLinkNameForCommand(parsed.name ?? inferLinkName(resolvedPath));
208
+ if (links[linkName]) {
209
+ throw duplicateSetupLinkError(linkName, links[linkName], resolvedPath);
210
+ }
211
+ links[linkName] = resolvedPath;
212
+ }
213
+ return links;
214
+ }
215
+ export async function loadWorkspaceForList(entry) {
216
+ const workspaceStatus = [];
217
+ if (!(await directoryExists(entry.workspaceRoot)) || !(await isWorkspaceRoot(entry.workspaceRoot))) {
218
+ return {
219
+ name: entry.name,
220
+ root: entry.workspaceRoot,
221
+ links: [],
222
+ status: [
223
+ makeStatus('error', 'workspace_root_missing', 'Workspace location does not exist.', {
224
+ target: 'workspace.root',
225
+ fix: 'Remove or repair the local registry record.',
226
+ }),
227
+ ],
228
+ };
229
+ }
230
+ let sharedState;
231
+ let localState = null;
232
+ try {
233
+ sharedState = await readWorkspaceSharedState(entry.workspaceRoot);
234
+ }
235
+ catch (error) {
236
+ return {
237
+ name: entry.name,
238
+ root: entry.workspaceRoot,
239
+ links: [],
240
+ status: [
241
+ makeStatus('error', 'workspace_state_invalid', `Workspace state could not be read: ${asErrorMessage(error)}`, {
242
+ target: 'workspace.root',
243
+ fix: 'Repair the workspace state files before using this workspace.',
244
+ }),
245
+ ],
246
+ };
247
+ }
248
+ try {
249
+ localState = await readOptionalWorkspaceLocalState(entry.workspaceRoot);
250
+ }
251
+ catch (error) {
252
+ workspaceStatus.push(localStateInvalidStatus(error));
253
+ }
254
+ return {
255
+ name: sharedState.name,
256
+ root: entry.workspaceRoot,
257
+ links: normalizeLinksForOutput(sharedState, localState),
258
+ status: workspaceStatus,
259
+ };
260
+ }
261
+ export async function loadWorkspaceForDoctor(selected) {
262
+ const commandStatus = [...selected.status];
263
+ const workspaceStatus = [];
264
+ const planningPath = getWorkspaceChangesDir(selected.root);
265
+ if (!(await directoryExists(selected.root)) || !(await isWorkspaceRoot(selected.root))) {
266
+ return {
267
+ workspace: {
268
+ name: selected.name,
269
+ root: selected.root,
270
+ planning_path: planningPath,
271
+ links: [],
272
+ status: [
273
+ makeStatus('error', 'selected_workspace_root_missing', 'Selected workspace location does not exist or is not a valid workspace.', {
274
+ target: 'workspace.root',
275
+ fix: 'Repair the local workspace registry record or choose another workspace.',
276
+ }),
277
+ ],
278
+ },
279
+ status: commandStatus,
280
+ };
281
+ }
282
+ let sharedState;
283
+ let localState;
284
+ let localStateInvalid = false;
285
+ try {
286
+ sharedState = await readWorkspaceSharedState(selected.root);
287
+ }
288
+ catch (error) {
289
+ return {
290
+ workspace: {
291
+ name: selected.name,
292
+ root: selected.root,
293
+ planning_path: planningPath,
294
+ links: [],
295
+ status: [
296
+ makeStatus('error', 'workspace_state_invalid', `Workspace state could not be read: ${asErrorMessage(error)}`, {
297
+ target: 'workspace.root',
298
+ fix: 'Repair .synarcx-workspace/workspace.yaml before using this workspace.',
299
+ }),
300
+ ],
301
+ },
302
+ status: commandStatus,
303
+ };
304
+ }
305
+ try {
306
+ const optionalLocalState = await readOptionalWorkspaceLocalState(selected.root);
307
+ localState = optionalLocalState ?? emptyLocalState();
308
+ if (!optionalLocalState) {
309
+ workspaceStatus.push(makeStatus('warning', 'workspace_local_state_missing', 'Machine-local paths are not recorded yet.', {
310
+ target: 'workspace.local_state',
311
+ fix: 'Run synarcx workspace relink <name> <path> for each linked repo or folder on this machine.',
312
+ }));
313
+ }
314
+ }
315
+ catch (error) {
316
+ localState = emptyLocalState();
317
+ localStateInvalid = true;
318
+ workspaceStatus.push(localStateInvalidStatus(error));
319
+ }
320
+ if (!(await directoryExists(planningPath))) {
321
+ workspaceStatus.push(makeStatus('error', 'workspace_planning_path_missing', 'Workspace planning path does not exist.', {
322
+ target: 'workspace.planning_path',
323
+ fix: `Create ${planningPath} or recreate the workspace with synarcx workspace setup.`,
324
+ }));
325
+ }
326
+ const sharedNames = new Set(Object.keys(sharedState.links));
327
+ const localNames = new Set(Object.keys(localState.paths));
328
+ const linkNames = [...new Set([...sharedNames, ...localNames])].sort((a, b) => a.localeCompare(b));
329
+ const links = [];
330
+ for (const linkName of linkNames) {
331
+ const linkStatus = [];
332
+ const localPath = localState.paths[linkName] ?? null;
333
+ let repoSpecsPath = null;
334
+ if (!sharedNames.has(linkName)) {
335
+ linkStatus.push(makeStatus('warning', 'local_path_without_shared_link', 'Local path is recorded without a shared workspace link.', {
336
+ target: `links.${linkName}`,
337
+ fix: `Add a shared link with synarcx workspace link ${linkName} ${localPath ?? '/path/to/folder'} or remove the local-only path from .synarcx-workspace/local.yaml.`,
338
+ }));
339
+ }
340
+ if (sharedNames.has(linkName) && !localPath && !localStateInvalid) {
341
+ linkStatus.push(makeStatus('error', 'linked_path_missing_from_local_state', 'Shared link does not have a local path on this machine.', {
342
+ target: `links.${linkName}.path`,
343
+ fix: `synarcx workspace relink ${linkName} /path/to/${linkName}`,
344
+ }));
345
+ }
346
+ if (localPath) {
347
+ if (await directoryExists(localPath)) {
348
+ const candidateSpecsPath = path.join(localPath, SYNSPEC_DIR_NAME, 'specs');
349
+ repoSpecsPath = (await directoryExists(candidateSpecsPath)) ? candidateSpecsPath : null;
350
+ }
351
+ else {
352
+ linkStatus.push(makeStatus('error', 'linked_path_missing', 'Linked path does not exist.', {
353
+ target: `links.${linkName}.path`,
354
+ fix: `synarcx workspace relink ${linkName} /path/to/${linkName}`,
355
+ }));
356
+ }
357
+ }
358
+ links.push({
359
+ name: linkName,
360
+ path: localPath,
361
+ repo_specs_path: repoSpecsPath,
362
+ status: linkStatus,
363
+ });
364
+ }
365
+ return {
366
+ workspace: {
367
+ name: sharedState.name,
368
+ root: selected.root,
369
+ planning_path: planningPath,
370
+ links,
371
+ status: workspaceStatus,
372
+ },
373
+ status: commandStatus,
374
+ };
375
+ }
376
+ async function readWorkspaceForMutation(selected) {
377
+ if (!(await directoryExists(selected.root)) || !(await isWorkspaceRoot(selected.root))) {
378
+ throw new WorkspaceCliError(`Workspace location does not exist for '${selected.name}': ${selected.root}`, 'selected_workspace_root_missing', {
379
+ target: 'workspace.root',
380
+ fix: 'Run synarcx workspace list to inspect known workspaces.',
381
+ });
382
+ }
383
+ return {
384
+ sharedState: await readWorkspaceSharedState(selected.root),
385
+ localState: await readLocalStateForMutation(selected.root),
386
+ };
387
+ }
388
+ async function recordSelectedWorkspaceAfterMutation(selected) {
389
+ if (selected.unregisteredCurrentWorkspace) {
390
+ await recordWorkspaceInRegistry(selected.name, selected.root);
391
+ }
392
+ }
393
+ function buildLinkMutationPayload(selected, sharedState, localState, linkName, linkPath) {
394
+ return {
395
+ workspace: {
396
+ name: sharedState.name,
397
+ root: selected.root,
398
+ planning_path: getWorkspaceChangesDir(selected.root),
399
+ links: normalizeLinksForOutput(sharedState, localState),
400
+ status: [],
401
+ },
402
+ link: {
403
+ name: linkName,
404
+ path: linkPath,
405
+ status: [],
406
+ },
407
+ status: selected.status,
408
+ };
409
+ }
410
+ export async function addWorkspaceLink(selected, nameOrPath, linkPath) {
411
+ const explicitName = linkPath ? nameOrPath : undefined;
412
+ const pathInput = linkPath ?? nameOrPath;
413
+ const resolvedPath = await resolveExistingDirectory(pathInput);
414
+ const linkName = validateLinkNameForCommand(explicitName ?? inferLinkName(resolvedPath));
415
+ const { sharedState, localState } = await readWorkspaceForMutation(selected);
416
+ if (sharedState.links[linkName]) {
417
+ throw duplicateLinkError(linkName, localState.paths[linkName] ?? null, resolvedPath);
418
+ }
419
+ const updatedSharedState = {
420
+ ...sharedState,
421
+ links: {
422
+ ...sharedState.links,
423
+ [linkName]: {},
424
+ },
425
+ };
426
+ const updatedLocalState = {
427
+ ...localState,
428
+ paths: {
429
+ ...localState.paths,
430
+ [linkName]: resolvedPath,
431
+ },
432
+ };
433
+ await writeWorkspaceSharedState(selected.root, updatedSharedState);
434
+ await writeWorkspaceLocalState(selected.root, updatedLocalState);
435
+ await syncWorkspaceOpenSurface(selected.root, updatedSharedState, updatedLocalState);
436
+ await recordSelectedWorkspaceAfterMutation(selected);
437
+ return buildLinkMutationPayload(selected, updatedSharedState, updatedLocalState, linkName, resolvedPath);
438
+ }
439
+ export async function updateWorkspaceLink(selected, linkNameInput, linkPath) {
440
+ const linkName = validateLinkNameForCommand(linkNameInput);
441
+ const resolvedPath = await resolveExistingDirectory(linkPath);
442
+ const { sharedState, localState } = await readWorkspaceForMutation(selected);
443
+ if (!sharedState.links[linkName]) {
444
+ throw new WorkspaceCliError(`Unknown workspace link '${linkName}'.`, 'unknown_link_name', {
445
+ target: `links.${linkName}`,
446
+ fix: 'Run synarcx workspace doctor to see linked repos or folders.',
447
+ });
448
+ }
449
+ const updatedLocalState = {
450
+ ...localState,
451
+ paths: {
452
+ ...localState.paths,
453
+ [linkName]: resolvedPath,
454
+ },
455
+ };
456
+ await writeWorkspaceLocalState(selected.root, updatedLocalState);
457
+ await syncWorkspaceOpenSurface(selected.root, sharedState, updatedLocalState);
458
+ await recordSelectedWorkspaceAfterMutation(selected);
459
+ return buildLinkMutationPayload(selected, sharedState, updatedLocalState, linkName, resolvedPath);
460
+ }
461
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1,5 @@
1
+ import { SelectedWorkspace, WorkspaceSelectionOptions } from './types.js';
2
+ export declare function selectWorkspaceForCommand(options: WorkspaceSelectionOptions, commandName: string, selectionOptions?: {
3
+ preferPositionalName?: boolean;
4
+ }): Promise<SelectedWorkspace>;
5
+ //# sourceMappingURL=selection.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { findWorkspaceRoot, listWorkspaceRegistryEntries, readWorkspaceSharedState, } from '../../core/workspace/index.js';
2
+ import { FileSystemUtils } from '../../utils/file-system.js';
3
+ import { isInteractive, resolveNoInteractive } from '../../utils/interactive.js';
4
+ import { readRegistry, validateWorkspaceNameForSetup } from './operations.js';
5
+ import { WorkspaceCliError, makeStatus, } from './types.js';
6
+ function normalizeRegistryRootForComparison(workspaceRoot) {
7
+ return process.platform === 'win32'
8
+ ? FileSystemUtils.canonicalizeExistingPath(workspaceRoot)
9
+ : workspaceRoot;
10
+ }
11
+ export async function selectWorkspaceForCommand(options, commandName, selectionOptions = {}) {
12
+ const registry = await readRegistry();
13
+ if (options.workspace) {
14
+ const workspaceName = validateWorkspaceNameForSetup(options.workspace);
15
+ const registryRoot = registry.workspaces[workspaceName];
16
+ if (!registryRoot) {
17
+ throw new WorkspaceCliError(`Unknown synarcx workspace '${workspaceName}'.`, 'workspace_not_found', {
18
+ target: 'workspace.name',
19
+ fix: 'Run synarcx workspace list to see known workspaces.',
20
+ });
21
+ }
22
+ return {
23
+ name: workspaceName,
24
+ root: registryRoot,
25
+ status: [],
26
+ unregisteredCurrentWorkspace: false,
27
+ };
28
+ }
29
+ const currentWorkspaceRoot = await findWorkspaceRoot(process.cwd());
30
+ if (currentWorkspaceRoot) {
31
+ const sharedState = await readWorkspaceSharedState(currentWorkspaceRoot);
32
+ const registeredRoot = registry.workspaces[sharedState.name];
33
+ const isRegistered = registeredRoot !== undefined &&
34
+ normalizeRegistryRootForComparison(registeredRoot) === currentWorkspaceRoot;
35
+ const warning = makeStatus('warning', 'workspace_not_in_local_registry', 'This workspace is not recorded in the local workspace registry.', {
36
+ target: 'workspace.root',
37
+ fix: 'Run a mutating workspace command from this workspace, such as workspace link or workspace relink, to record it locally.',
38
+ });
39
+ return {
40
+ name: sharedState.name,
41
+ root: currentWorkspaceRoot,
42
+ status: isRegistered ? [] : [warning],
43
+ unregisteredCurrentWorkspace: !isRegistered,
44
+ };
45
+ }
46
+ const entries = listWorkspaceRegistryEntries(registry);
47
+ if (entries.length === 0) {
48
+ throw new WorkspaceCliError("No known synarcx workspaces. Run 'synarcx workspace setup' first.\nAfter at least one workspace is known locally, you can also pass --workspace <name>.", 'no_known_workspaces', {
49
+ target: 'workspace.name',
50
+ fix: 'synarcx workspace setup',
51
+ });
52
+ }
53
+ if (entries.length === 1) {
54
+ const [entry] = entries;
55
+ return {
56
+ name: entry.name,
57
+ root: entry.workspaceRoot,
58
+ status: [],
59
+ unregisteredCurrentWorkspace: false,
60
+ };
61
+ }
62
+ if (options.json || resolveNoInteractive(options) || !isInteractive(options)) {
63
+ const knownNames = entries.map((entry) => entry.name).join(', ');
64
+ const usesPositionalName = selectionOptions.preferPositionalName;
65
+ const fix = usesPositionalName
66
+ ? `synarcx workspace ${commandName} <name>`
67
+ : `synarcx workspace ${commandName} --workspace <name>`;
68
+ throw new WorkspaceCliError(usesPositionalName
69
+ ? `Multiple synarcx workspaces are known. Known workspaces: ${knownNames}. Pass a workspace name.`
70
+ : `Multiple synarcx workspaces are known. Known workspaces: ${knownNames}. Pass --workspace <name>.`, 'workspace_selection_ambiguous', {
71
+ target: 'workspace.name',
72
+ fix,
73
+ });
74
+ }
75
+ const { select } = await import('@inquirer/prompts');
76
+ const selectedName = await select({
77
+ message: 'Select workspace:',
78
+ choices: entries.map((entry) => ({
79
+ name: `${entry.name} (${entry.workspaceRoot})`,
80
+ value: entry.name,
81
+ })),
82
+ });
83
+ return {
84
+ name: selectedName,
85
+ root: registry.workspaces[selectedName],
86
+ status: [],
87
+ unregisteredCurrentWorkspace: false,
88
+ };
89
+ }
90
+ //# sourceMappingURL=selection.js.map
@@ -0,0 +1,83 @@
1
+ export type StatusSeverity = 'error' | 'warning';
2
+ export interface WorkspaceStatus {
3
+ severity: StatusSeverity;
4
+ code: string;
5
+ message: string;
6
+ target?: string;
7
+ fix?: string;
8
+ }
9
+ export interface WorkspaceLinkOutput {
10
+ name: string;
11
+ path: string | null;
12
+ repo_specs_path?: string | null;
13
+ status: WorkspaceStatus[];
14
+ }
15
+ export interface WorkspaceOutput {
16
+ name: string;
17
+ root: string;
18
+ planning_path: string;
19
+ links: WorkspaceLinkOutput[];
20
+ status: WorkspaceStatus[];
21
+ }
22
+ export interface WorkspaceListOutput {
23
+ name: string;
24
+ root: string;
25
+ links: WorkspaceLinkOutput[];
26
+ status: WorkspaceStatus[];
27
+ }
28
+ export interface WorkspaceSetupOptions {
29
+ name?: string;
30
+ link?: string[];
31
+ opener?: string;
32
+ json?: boolean;
33
+ noInteractive?: boolean;
34
+ interactive?: boolean;
35
+ }
36
+ export interface WorkspaceSelectionOptions {
37
+ workspace?: string;
38
+ json?: boolean;
39
+ noInteractive?: boolean;
40
+ interactive?: boolean;
41
+ }
42
+ export type WorkspaceLinkOptions = WorkspaceSelectionOptions;
43
+ export interface WorkspaceOpenOptions extends WorkspaceSelectionOptions {
44
+ agent?: string;
45
+ editor?: boolean;
46
+ prepareOnly?: boolean;
47
+ change?: string;
48
+ }
49
+ export interface WorkspaceListOptions {
50
+ json?: boolean;
51
+ }
52
+ export interface SelectedWorkspace {
53
+ name: string;
54
+ root: string;
55
+ status: WorkspaceStatus[];
56
+ unregisteredCurrentWorkspace: boolean;
57
+ }
58
+ export interface WorkspaceLinkMutationPayload {
59
+ workspace: WorkspaceOutput;
60
+ link: {
61
+ name: string;
62
+ path: string;
63
+ status: WorkspaceStatus[];
64
+ };
65
+ status: WorkspaceStatus[];
66
+ }
67
+ export declare class WorkspaceCliError extends Error {
68
+ readonly status: WorkspaceStatus;
69
+ constructor(message: string, code: string, options?: {
70
+ target?: string;
71
+ fix?: string;
72
+ });
73
+ }
74
+ export declare function makeStatus(severity: StatusSeverity, code: string, message: string, options?: {
75
+ target?: string;
76
+ fix?: string;
77
+ }): WorkspaceStatus;
78
+ export declare function asErrorMessage(error: unknown): string;
79
+ export declare function asStatus(error: unknown): WorkspaceStatus;
80
+ export declare function appendStatus<T extends {
81
+ status: WorkspaceStatus[];
82
+ }>(payload: T, status: WorkspaceStatus): T;
83
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,36 @@
1
+ export class WorkspaceCliError extends Error {
2
+ status;
3
+ constructor(message, code, options = {}) {
4
+ super(message);
5
+ this.status = {
6
+ severity: 'error',
7
+ code,
8
+ message,
9
+ ...options,
10
+ };
11
+ }
12
+ }
13
+ export function makeStatus(severity, code, message, options = {}) {
14
+ return {
15
+ severity,
16
+ code,
17
+ message,
18
+ ...options,
19
+ };
20
+ }
21
+ export function asErrorMessage(error) {
22
+ return error instanceof Error ? error.message : String(error);
23
+ }
24
+ export function asStatus(error) {
25
+ if (error instanceof WorkspaceCliError) {
26
+ return error.status;
27
+ }
28
+ return makeStatus('error', 'workspace_error', asErrorMessage(error));
29
+ }
30
+ export function appendStatus(payload, status) {
31
+ return {
32
+ ...payload,
33
+ status: [...payload.status, status],
34
+ };
35
+ }
36
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerWorkspaceCommand(program: Command): void;
3
+ //# sourceMappingURL=workspace.d.ts.map