synarcx 0.2.0 → 0.3.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 (51) hide show
  1. package/README.md +237 -43
  2. package/dist/commands/config.js +7 -6
  3. package/dist/core/command-generation/adapters/bob.js +7 -20
  4. package/dist/core/command-generation/adapters/claude.js +9 -29
  5. package/dist/core/command-generation/adapters/cursor.js +9 -22
  6. package/dist/core/command-generation/adapters/pi.js +6 -19
  7. package/dist/core/command-generation/adapters/windsurf.js +9 -29
  8. package/dist/core/command-generation/yaml-utils.d.ts +3 -0
  9. package/dist/core/command-generation/yaml-utils.js +12 -0
  10. package/dist/core/completions/installers/bash-installer.d.ts +1 -0
  11. package/dist/core/completions/installers/bash-installer.js +14 -3
  12. package/dist/core/completions/installers/powershell-installer.d.ts +1 -0
  13. package/dist/core/completions/installers/powershell-installer.js +18 -10
  14. package/dist/core/config.js +0 -3
  15. package/dist/core/index.js +1 -1
  16. package/dist/core/init.d.ts +0 -2
  17. package/dist/core/init.js +27 -77
  18. package/dist/core/migration.js +1 -2
  19. package/dist/core/profile-sync-drift.d.ts +0 -7
  20. package/dist/core/profile-sync-drift.js +2 -17
  21. package/dist/core/profiles.d.ts +0 -11
  22. package/dist/core/profiles.js +1 -20
  23. package/dist/core/shared/artifact-cleanup.d.ts +5 -0
  24. package/dist/core/shared/artifact-cleanup.js +89 -0
  25. package/dist/core/shared/skill-generation.js +3 -3
  26. package/dist/core/shared/tool-detection.d.ts +4 -10
  27. package/dist/core/shared/tool-detection.js +3 -31
  28. package/dist/core/shared/workflow-registry.d.ts +40 -0
  29. package/dist/core/shared/workflow-registry.js +19 -0
  30. package/dist/core/templates/skill-templates.d.ts +1 -0
  31. package/dist/core/templates/skill-templates.js +1 -0
  32. package/dist/core/templates/types.d.ts +7 -0
  33. package/dist/core/templates/types.js +9 -1
  34. package/dist/core/templates/workflows/analyze.js +84 -84
  35. package/dist/core/templates/workflows/apply-change.js +291 -291
  36. package/dist/core/templates/workflows/archive-change.js +254 -254
  37. package/dist/core/templates/workflows/clarify.js +115 -93
  38. package/dist/core/templates/workflows/debug.js +100 -100
  39. package/dist/core/templates/workflows/explore.js +462 -462
  40. package/dist/core/templates/workflows/propose.js +199 -199
  41. package/dist/core/templates/workflows/quick.js +112 -112
  42. package/dist/core/templates/workflows/refactor.js +109 -109
  43. package/dist/core/templates/workflows/review.d.ts +4 -0
  44. package/dist/core/templates/workflows/review.js +293 -0
  45. package/dist/core/templates/workflows/sync.js +225 -148
  46. package/dist/core/update.d.ts +1 -21
  47. package/dist/core/update.js +18 -117
  48. package/dist/core/view.js +8 -8
  49. package/dist/core/workspace/open-surface.d.ts +2 -2
  50. package/dist/core/workspace/open-surface.js +13 -13
  51. package/package.json +84 -76
@@ -1,101 +1,144 @@
1
+ import { commandFromSkill } from '../types.js';
1
2
  export function getSynSyncSkillTemplate() {
2
3
  return {
3
4
  name: 'syn-sync',
4
5
  description: 'Generate or update synspec/constitution.md with README validation, supporting file scan, guardrail Q&A, and structured constitution generation.',
5
- instructions: `Generate or update the project constitution a living document in \`synspec/constitution.md\` that captures validated project context.
6
-
7
- **Input**: The user can specify a focus area, or just run the command to proceed through the validation flow.
8
-
9
- ---
10
-
11
- ## README Gate (Required)
12
-
13
- Before any generation, the README MUST pass quality validation.
14
-
15
- 1. **Check that README.md exists and is not empty.**
16
- - If missing or blank → stop, output: "SYNC BLOCKED: README.md is missing or empty. Please write a README that describes what this project does and what problem it solves, then re-run \`/syn:sync\`."
17
- - Do NOT write any files. Do NOT proceed.
18
-
19
- 2. **Check that README meaningfully describes the project.**
20
- - A passing README must have both:
21
- - At least one sentence describing what the project does
22
- - At least one sentence describing the problem it solves or who it is for
23
- - AI judges quality at runtime. Examples of THIN content that should fail:
24
- - Only a title and install instructions
25
- - Auto-generated placeholder text ("# My Project", "## Getting Started")
26
- - Single-line descriptions with no substance
27
- - If README is too thin → stop, output: "SYNC BLOCKED: README is too thin. A passing README must describe (1) what the project does and (2) what problem it solves, in at least one sentence each. Please update README.md and re-run \`/syn:sync\`."
28
-
29
- 3. **If README passes**, proceed to the next phase.
30
-
31
- ---
32
-
33
- ## Supporting File Scan
34
-
35
- After README passes, read supporting project files for context:
36
- - \`AGENTS.md\` — AI agent conventions
37
- - \`package.json\` — dependencies, scripts, metadata
38
- - \`src/\` structure — code organization (top-level directories)
39
- - Any other notable config files (\`tsconfig.json\`, \`.eslintrc.*\`, etc.)
40
-
41
- Note what information is already well-covered by the README so guardrail questions avoid repeating it.
42
-
43
- ---
44
-
45
- ## Guardrail Q&A
46
-
47
- Generate up to 5 targeted questions. Each question should probe areas that CANNOT be reliably inferred from code or README alone:
48
-
49
- | Topic Area | Example Question |
50
- |------------|-----------------|
51
- | Off-limits areas | "Are there any parts of the codebase AI should never modify?" |
52
- | Hard constraints | "Are there compliance, security, or infrastructure constraints?" |
53
- | Coding rules | "Are there coding conventions not obvious from the code?" |
54
- | Out-of-scope | "What is explicitly out of scope for this project right now?" |
55
- | Dependencies | "Are there any planned or pending dependency changes?" |
56
-
57
- **Rules:**
58
- - Adapt questions to the project's stack, structure, and domain don't ask generic questions that don't apply
59
- - Skip questions already answered by the README, AGENTS.md, or other supporting files
60
- - Maximum 5 questions per session
61
- - Ask one at a time using the AskUserQuestion tool, wait for each answer
62
-
63
- ---
64
-
65
- ## Constitution Generation
66
-
67
- With README validated, supporting files scanned, and Q&A answers collected, generate \`synspec/constitution.md\` with these sections:
68
-
69
- 1. **\`# Constitution: <project-name>\`** derived from package.json name, with Version field
70
- 2. **\`## Purpose\`** — 2-3 sentences synthesized from README
71
- 3. **\`## Principles\`** key design principles inferred from codebase and Q&A
72
- 4. **\`## Tech Stack\`** — languages, frameworks, key dependencies from package.json
73
- 5. **\`## Constraints\`** — from Q&A answers: hard constraints, compliance, infra limits
74
- 6. **\`## Off-Limits\`** — from Q&A answers: areas AI must not touch, out-of-scope items
75
- 7. **\`## Conventions\`** — code style, naming, patterns observed from code and AGENTS.md
76
- 8. **\`## Architecture\`** — high-level structure overview from src/ scan
77
- 9. **\`## Decision Log\`** table with Date, Decision, Rationale (append-only)
78
-
79
- **The Constraints and Off-Limits sections MUST be written from Q&A answers, not inferred from docs.**
80
-
81
- ---
82
-
83
- ## Re-run (Update)
84
-
85
- When re-run and \`synspec/constitution.md\` already exists, still run the README gate — README may have degraded since last sync. Offer a semver bump choice:
86
- - **MAJOR** — constitution structure changed or reorganized
87
- - **MINOR** — new section added
88
- - **PATCH** — content update, typo fixes
89
-
90
- Append a Sync Impact Report as an HTML comment at the top:
91
- \`\`\`
92
- <!-- Sync Impact: MAJOR — constitution structure reorganized -->
93
- \`\`\`
94
-
95
- ---
96
-
97
- ## Output
98
-
6
+ instructions: `## Step 0: SynArcX Version Check (MUST run first)
7
+
8
+ Do NOT read any project files yet. Run this version check first.
9
+
10
+ ### 0.1 Read the daily cache
11
+
12
+ Read \`synspec/.version-cache.json\`. If \`lastCheck\` matches today's UTC date (YYYY-MM-DD), skip the version check entirely — proceed to "Main Sync Flow" below.
13
+
14
+ Expected cache format:
15
+ \`\`\`json
16
+ { "lastCheck": "2026-05-13", "latestVersion": "0.4.0" }
17
+ \`\`\`
18
+
19
+ If missing or malformed, treat as cache miss and continue.
20
+
21
+ ### 0.2 Fetch latest from npm
22
+
23
+ Run \`npm view synarcx version\`. On failure (no npm, no network, non-zero exit): silently skip to 0.5, write cache with \`latestVersion: null\`.
24
+
25
+ ### 0.3 Get installed version
26
+
27
+ Run \`synarcx --version\`. On failure: silently skip to 0.5.
28
+
29
+ ### 0.4 Compare and prompt
30
+
31
+ Parse both as semver: split on \`.\`, parse each as integer, compare major→minor→patch. If npm version > installed:
32
+
33
+ 1. Print update banner.
34
+ 2. Use AskUserQuestion tool: "Update now?" with \`["Yes", "No"]\`.
35
+ 3. **Yes**: Run \`npm install -g synarcx@latest\`. On success: print "✓ SynArcX updated" + "Run \`synarcx update\` to refresh skill files." On failure: print error + manual command.
36
+ 4. **No**: Print manual command.
37
+
38
+ If versions match, proceed silently.
39
+
40
+ ### 0.5 Write cache
41
+
42
+ Write \`synspec/.version-cache.json\` with today's UTC date and latest version (or \`null\` on failure). Use \`new Date().toISOString().split('T')[0]\`.
43
+
44
+ ---
45
+
46
+ ## Main Sync Flow
47
+
48
+ Generate or update the project constitution a living document in \`synspec/constitution.md\` that captures validated project context.
49
+
50
+ **Input**: The user can specify a focus area, or just run the command to proceed through the validation flow.
51
+
52
+ ---
53
+
54
+ ## README Gate (Required)
55
+
56
+ Before any generation, the README MUST pass quality validation.
57
+
58
+ 1. **Check that README.md exists and is not empty.**
59
+ - If missing or blank stop, output: "SYNC BLOCKED: README.md is missing or empty. Please write a README that describes what this project does and what problem it solves, then re-run \`/syn:sync\`."
60
+ - Do NOT write any files. Do NOT proceed.
61
+
62
+ 2. **Check that README meaningfully describes the project.**
63
+ - A passing README must have both:
64
+ - At least one sentence describing what the project does
65
+ - At least one sentence describing the problem it solves or who it is for
66
+ - AI judges quality at runtime. Examples of THIN content that should fail:
67
+ - Only a title and install instructions
68
+ - Auto-generated placeholder text ("# My Project", "## Getting Started")
69
+ - Single-line descriptions with no substance
70
+ - If README is too thin → stop, output: "SYNC BLOCKED: README is too thin. A passing README must describe (1) what the project does and (2) what problem it solves, in at least one sentence each. Please update README.md and re-run \`/syn:sync\`."
71
+
72
+ 3. **If README passes**, proceed to the next phase.
73
+
74
+ ---
75
+
76
+ ## Supporting File Scan
77
+
78
+ After README passes, read supporting project files for context:
79
+ - \`AGENTS.md\` — AI agent conventions
80
+ - \`package.json\` dependencies, scripts, metadata
81
+ - \`src/\` structure — code organization (top-level directories)
82
+ - Any other notable config files (\`tsconfig.json\`, \`.eslintrc.*\`, etc.)
83
+
84
+ Note what information is already well-covered by the README so guardrail questions avoid repeating it.
85
+
86
+ ---
87
+
88
+ ## Guardrail Q&A
89
+
90
+ Generate up to 5 targeted questions. Each question should probe areas that CANNOT be reliably inferred from code or README alone:
91
+
92
+ | Topic Area | Example Question |
93
+ |------------|-----------------|
94
+ | Off-limits areas | "Are there any parts of the codebase AI should never modify?" |
95
+ | Hard constraints | "Are there compliance, security, or infrastructure constraints?" |
96
+ | Coding rules | "Are there coding conventions not obvious from the code?" |
97
+ | Out-of-scope | "What is explicitly out of scope for this project right now?" |
98
+ | Dependencies | "Are there any planned or pending dependency changes?" |
99
+
100
+ **Rules:**
101
+ - Adapt questions to the project's stack, structure, and domain — don't ask generic questions that don't apply
102
+ - Skip questions already answered by the README, AGENTS.md, or other supporting files
103
+ - Maximum 5 questions per session
104
+ - Ask one at a time using the AskUserQuestion tool, wait for each answer
105
+
106
+ ---
107
+
108
+ ## Constitution Generation
109
+
110
+ With README validated, supporting files scanned, and Q&A answers collected, generate \`synspec/constitution.md\` with these sections:
111
+
112
+ 1. **\`# Constitution: <project-name>\`** — derived from package.json name, with Version field
113
+ 2. **\`## Purpose\`** — 2-3 sentences synthesized from README
114
+ 3. **\`## Principles\`** — key design principles inferred from codebase and Q&A
115
+ 4. **\`## Tech Stack\`** — languages, frameworks, key dependencies from package.json
116
+ 5. **\`## Constraints\`** — from Q&A answers: hard constraints, compliance, infra limits
117
+ 6. **\`## Off-Limits\`** — from Q&A answers: areas AI must not touch, out-of-scope items
118
+ 7. **\`## Conventions\`** — code style, naming, patterns observed from code and AGENTS.md
119
+ 8. **\`## Architecture\`** — high-level structure overview from src/ scan
120
+ 9. **\`## Decision Log\`** — table with Date, Decision, Rationale (append-only)
121
+
122
+ **The Constraints and Off-Limits sections MUST be written from Q&A answers, not inferred from docs.**
123
+
124
+ ---
125
+
126
+ ## Re-run (Update)
127
+
128
+ When re-run and \`synspec/constitution.md\` already exists, still run the README gate — README may have degraded since last sync. Offer a semver bump choice:
129
+ - **MAJOR** — constitution structure changed or reorganized
130
+ - **MINOR** — new section added
131
+ - **PATCH** — content update, typo fixes
132
+
133
+ Append a Sync Impact Report as an HTML comment at the top:
134
+ \`\`\`
135
+ <!-- Sync Impact: MAJOR — constitution structure reorganized -->
136
+ \`\`\`
137
+
138
+ ---
139
+
140
+ ## Output
141
+
99
142
  After completion, summarize what was created or updated, note the version, and list how many Q&A questions were answered.`,
100
143
  license: 'MIT',
101
144
  compatibility: 'Requires synarcx CLI.',
@@ -103,63 +146,97 @@ After completion, summarize what was created or updated, note the version, and l
103
146
  };
104
147
  }
105
148
  export function getSynSyncCommandTemplate() {
106
- return {
149
+ return commandFromSkill(getSynSyncSkillTemplate(), {
107
150
  name: 'syn:sync',
108
151
  description: 'Generate/update project constitution with README validation, guardrail Q&A, and constraint capture',
109
- category: 'Workflow',
110
152
  tags: ['workflow', 'sync', 'project'],
111
- content: `Generate or update the project constitution a living document in \`synspec/constitution.md\` that captures validated project context.
112
-
113
- **Input**: The user can specify a focus area, or just run the command to proceed through the validation flow.
114
-
115
- ---
116
-
117
- ## README Gate (Required)
118
-
119
- Before any generation, the README MUST pass quality validation.
120
-
121
- 1. **Check that README.md exists and is not empty.**
122
- - If missing or blank → stop with message explaining README must exist and describe the project.
123
- - Do NOT write any files.
124
-
125
- 2. **Check that README meaningfully describes the project.**
126
- - A passing README must have both: (1) what the project does, (2) what problem it solves.
127
- - AI judges quality at runtime.
128
- - If too thin → stop with message explaining what's needed.
129
-
130
- 3. **If README passes**, proceed.
131
-
132
- ---
133
-
134
- ## Supporting File Scan
135
-
136
- Read AGENTS.md, package.json, src/ structure, and other notable config files. Note what's already covered by README to avoid redundant questions.
137
-
138
- ---
139
-
140
- ## Guardrail Q&A
141
-
142
- Generate up to 5 targeted questions about off-limits areas, hard constraints, coding rules, out-of-scope items, and dependency plans. Adapt to the project's stack and domain. Skip questions already answered by README or supporting files.
143
-
144
- ---
145
-
146
- ## Constitution Generation
147
-
148
- Generate \`synspec/constitution.md\` with sections: Purpose, Principles, Tech Stack, Constraints (from Q&A), Off-Limits (from Q&A), Conventions, Architecture, Decision Log.
149
-
150
- **Constraints and Off-Limits MUST come from Q&A answers, not inferred.**
151
-
152
- ---
153
-
154
- ## Re-run (Update)
155
-
156
- Still run the README gate even when constitution exists README may have degraded. Offer semver bump. Append Sync Impact Report HTML comment.
157
-
158
- ---
159
-
160
- ## Output
161
-
153
+ content: `## Step 0: SynArcX Version Check (MUST run first)
154
+
155
+ Do NOT read any project files yet. Run this version check first.
156
+
157
+ ### 0.1 Read the daily cache
158
+
159
+ Read \`synspec/.version-cache.json\`. If \`lastCheck\` matches today's UTC date (YYYY-MM-DD), skip to "Main Sync Flow" below. Expected format: \`{ "lastCheck": "2026-05-13", "latestVersion": "0.4.0" }\`.
160
+
161
+ ### 0.2 Fetch latest from npm
162
+
163
+ Run \`npm view synarcx version\`. On failure, silently skip to 0.5, write cache with \`latestVersion: null\`.
164
+
165
+ ### 0.3 Get installed version
166
+
167
+ Run \`synarcx --version\`. On failure, silently skip to 0.5.
168
+
169
+ ### 0.4 Compare and prompt
170
+
171
+ Parse both as semver: split on \`.\`, parse each as integer, compare major→minor→patch. If npm version > installed:
172
+
173
+ 1. Print update banner.
174
+ 2. Use AskUserQuestion tool: "Update now?" with \`["Yes", "No"]\`.
175
+ 3. **Yes**: Run \`npm install -g synarcx@latest\`. On success: print success + "Run \`synarcx update\` to refresh skill files." On failure: print error + manual command.
176
+ 4. **No**: Print manual command.
177
+
178
+ If versions match, proceed silently.
179
+
180
+ ### 0.5 Write cache
181
+
182
+ Write \`synspec/.version-cache.json\` with today's UTC date and latest version (or \`null\` on failure). Use \`new Date().toISOString().split('T')[0]\`.
183
+
184
+ ---
185
+
186
+ ## Main Sync Flow
187
+
188
+ Generate or update the project constitution — a living document in \`synspec/constitution.md\` that captures validated project context.
189
+
190
+ **Input**: The user can specify a focus area, or just run the command to proceed through the validation flow.
191
+
192
+ ---
193
+
194
+ ## README Gate (Required)
195
+
196
+ Before any generation, the README MUST pass quality validation.
197
+
198
+ 1. **Check that README.md exists and is not empty.**
199
+ - If missing or blank → stop with message explaining README must exist and describe the project.
200
+ - Do NOT write any files.
201
+
202
+ 2. **Check that README meaningfully describes the project.**
203
+ - A passing README must have both: (1) what the project does, (2) what problem it solves.
204
+ - AI judges quality at runtime.
205
+ - If too thin → stop with message explaining what's needed.
206
+
207
+ 3. **If README passes**, proceed.
208
+
209
+ ---
210
+
211
+ ## Supporting File Scan
212
+
213
+ Read AGENTS.md, package.json, src/ structure, and other notable config files. Note what's already covered by README to avoid redundant questions.
214
+
215
+ ---
216
+
217
+ ## Guardrail Q&A
218
+
219
+ Generate up to 5 targeted questions about off-limits areas, hard constraints, coding rules, out-of-scope items, and dependency plans. Adapt to the project's stack and domain. Skip questions already answered by README or supporting files.
220
+
221
+ ---
222
+
223
+ ## Constitution Generation
224
+
225
+ Generate \`synspec/constitution.md\` with sections: Purpose, Principles, Tech Stack, Constraints (from Q&A), Off-Limits (from Q&A), Conventions, Architecture, Decision Log.
226
+
227
+ **Constraints and Off-Limits MUST come from Q&A answers, not inferred.**
228
+
229
+ ---
230
+
231
+ ## Re-run (Update)
232
+
233
+ Still run the README gate even when constitution exists — README may have degraded. Offer semver bump. Append Sync Impact Report HTML comment.
234
+
235
+ ---
236
+
237
+ ## Output
238
+
162
239
  Summarize what was created/updated, note the version, and show how many Q&A questions were answered.`
163
- };
240
+ });
164
241
  }
165
242
  //# sourceMappingURL=sync.js.map
@@ -44,27 +44,7 @@ export declare class UpdateCommand {
44
44
  */
45
45
  private displayOldCoreCustomProfileNote;
46
46
  /**
47
- * Removes skill directories for workflows when delivery changed to commands-only.
48
- * Returns the number of directories removed.
49
- */
50
- private removeSkillDirs;
51
- /**
52
- * Removes skill directories for workflows that are no longer selected in the active profile.
53
- * Returns the number of directories removed.
54
- */
55
- private removeUnselectedSkillDirs;
56
- /**
57
- * Removes command files for workflows when delivery changed to skills-only.
58
- * Returns the number of files removed.
59
- */
60
- private removeCommandFiles;
61
- /**
62
- * Removes command files for workflows that are no longer selected in the active profile.
63
- * Returns the number of files removed.
64
- */
65
- private removeUnselectedCommandFiles;
66
- /**
67
- * Detect and handle legacy OpenSpec artifacts.
47
+ * Detect and handle legacy artifacts.
68
48
  * Unlike init, update warns but continues if legacy files found in non-interactive mode.
69
49
  * Returns array of tool IDs that were newly configured during legacy upgrade.
70
50
  */
@@ -7,7 +7,6 @@
7
7
  import path from 'path';
8
8
  import chalk from 'chalk';
9
9
  import ora from 'ora';
10
- import * as fs from 'fs';
11
10
  import { createRequire } from 'module';
12
11
  import { FileSystemUtils } from '../utils/file-system.js';
13
12
  import { transformToHyphenCommands } from '../utils/command-references.js';
@@ -17,12 +16,14 @@ import { getToolVersionStatus, getSkillTemplates, getCommandContents, generateSk
17
16
  import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, getToolsFromLegacyArtifacts, } from './legacy-cleanup.js';
18
17
  import { isInteractive } from '../utils/interactive.js';
19
18
  import { getGlobalConfig } from './global-config.js';
20
- import { getProfileWorkflows, ALL_WORKFLOWS } from './profiles.js';
19
+ import { getProfileWorkflows } from './profiles.js';
20
+ import { ALL_WORKFLOWS } from './shared/workflow-registry.js';
21
+ import { removeSkillDirs, removeUnselectedSkillDirs, removeCommandFiles, removeUnselectedCommandFiles, } from './shared/artifact-cleanup.js';
21
22
  import { getAvailableTools } from './available-tools.js';
22
- import { WORKFLOW_TO_SKILL_DIR, getCommandConfiguredTools, getConfiguredToolsForProfileSync, getToolsNeedingProfileSync, } from './profile-sync-drift.js';
23
+ import { getCommandConfiguredTools, getConfiguredToolsForProfileSync, getToolsNeedingProfileSync, } from './profile-sync-drift.js';
23
24
  import { scanInstalledWorkflows as scanInstalledWorkflowsShared, migrateIfNeeded as migrateIfNeededShared, } from './migration.js';
24
25
  const require = createRequire(import.meta.url);
25
- const { version: OPENSPEC_VERSION } = require('../../package.json');
26
+ const { version: SYNARCX_VERSION } = require('../../package.json');
26
27
  const OLD_CORE_WORKFLOWS = ['propose', 'explore', 'apply', 'archive'];
27
28
  /**
28
29
  * Scans installed workflow artifacts (skills and managed commands) across all configured tools.
@@ -43,9 +44,9 @@ export class UpdateCommand {
43
44
  }
44
45
  async execute(projectPath) {
45
46
  const resolvedProjectPath = path.resolve(projectPath);
46
- const openspecPath = path.join(resolvedProjectPath, SYNSPEC_DIR_NAME);
47
+ const synspecPath = path.join(resolvedProjectPath, SYNSPEC_DIR_NAME);
47
48
  // 1. Check synspec directory exists
48
- if (!await FileSystemUtils.directoryExists(openspecPath)) {
49
+ if (!await FileSystemUtils.directoryExists(synspecPath)) {
49
50
  throw new Error(`No synarcx directory found. Run 'synarcx init' first.`);
50
51
  }
51
52
  // 2. Perform one-time migration if needed before any legacy upgrade generation.
@@ -73,7 +74,7 @@ export class UpdateCommand {
73
74
  const commandConfiguredTools = getCommandConfiguredTools(resolvedProjectPath);
74
75
  const commandConfiguredSet = new Set(commandConfiguredTools);
75
76
  const toolStatuses = configuredTools.map((toolId) => {
76
- const status = getToolVersionStatus(resolvedProjectPath, toolId, OPENSPEC_VERSION);
77
+ const status = getToolVersionStatus(resolvedProjectPath, toolId, SYNARCX_VERSION);
77
78
  if (!status.configured && commandConfiguredSet.has(toolId)) {
78
79
  return { ...status, configured: true };
79
80
  }
@@ -132,14 +133,14 @@ export class UpdateCommand {
132
133
  const skillFile = path.join(skillDir, 'SKILL.md');
133
134
  // Use hyphen-based command references for tools where filename = command name
134
135
  const transformer = tool.value === 'pi' ? transformToHyphenCommands : undefined;
135
- const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
136
+ const skillContent = generateSkillContent(template, SYNARCX_VERSION, transformer);
136
137
  await FileSystemUtils.writeFile(skillFile, skillContent);
137
138
  }
138
- removedDeselectedSkillCount += await this.removeUnselectedSkillDirs(skillsDir, desiredWorkflows);
139
+ removedDeselectedSkillCount += await removeUnselectedSkillDirs(skillsDir, desiredWorkflows);
139
140
  }
140
141
  // Delete skill directories if delivery is commands-only
141
142
  if (!shouldGenerateSkills) {
142
- removedSkillCount += await this.removeSkillDirs(skillsDir);
143
+ removedSkillCount += await removeSkillDirs(skillsDir);
143
144
  }
144
145
  // Generate commands if delivery includes commands
145
146
  if (shouldGenerateCommands) {
@@ -150,12 +151,12 @@ export class UpdateCommand {
150
151
  const commandFile = path.isAbsolute(cmd.path) ? cmd.path : path.join(resolvedProjectPath, cmd.path);
151
152
  await FileSystemUtils.writeFile(commandFile, cmd.fileContent);
152
153
  }
153
- removedDeselectedCommandCount += await this.removeUnselectedCommandFiles(resolvedProjectPath, toolId, desiredWorkflows);
154
+ removedDeselectedCommandCount += await removeUnselectedCommandFiles(resolvedProjectPath, toolId, desiredWorkflows);
154
155
  }
155
156
  }
156
157
  // Delete command files if delivery is skills-only
157
158
  if (!shouldGenerateCommands) {
158
- removedCommandCount += await this.removeCommandFiles(resolvedProjectPath, toolId);
159
+ removedCommandCount += await removeCommandFiles(resolvedProjectPath, toolId);
159
160
  }
160
161
  spinner.succeed(`Updated ${tool.name}`);
161
162
  updatedTools.push(tool.name);
@@ -171,7 +172,7 @@ export class UpdateCommand {
171
172
  // 11. Summary
172
173
  console.log();
173
174
  if (updatedTools.length > 0) {
174
- console.log(chalk.green(`✓ Updated: ${updatedTools.join(', ')} (v${OPENSPEC_VERSION})`));
175
+ console.log(chalk.green(`✓ Updated: ${updatedTools.join(', ')} (v${SYNARCX_VERSION})`));
175
176
  }
176
177
  if (failedTools.length > 0) {
177
178
  console.log(chalk.red(`✗ Failed: ${failedTools.map(f => `${f.name} (${f.error})`).join(', ')}`));
@@ -215,7 +216,7 @@ export class UpdateCommand {
215
216
  */
216
217
  displayUpToDateMessage(toolStatuses) {
217
218
  const toolNames = toolStatuses.map((s) => s.toolId);
218
- console.log(chalk.green(`✓ All ${toolStatuses.length} tool(s) up to date (v${OPENSPEC_VERSION})`));
219
+ console.log(chalk.green(`✓ All ${toolStatuses.length} tool(s) up to date (v${SYNARCX_VERSION})`));
219
220
  console.log(chalk.dim(` Tools: ${toolNames.join(', ')}`));
220
221
  console.log();
221
222
  console.log(chalk.dim('Use --force to refresh files anyway.'));
@@ -228,7 +229,7 @@ export class UpdateCommand {
228
229
  const status = statusByTool.get(toolId);
229
230
  if (status?.needsUpdate) {
230
231
  const fromVersion = status.generatedByVersion ?? 'unknown';
231
- return `${status.toolId} (${fromVersion} → ${OPENSPEC_VERSION})`;
232
+ return `${status.toolId} (${fromVersion} → ${SYNARCX_VERSION})`;
232
233
  }
233
234
  return `${toolId} (config sync)`;
234
235
  });
@@ -283,107 +284,7 @@ export class UpdateCommand {
283
284
  console.log(chalk.dim('Run `synarcx config profile core` and then `synarcx update` to add sync.'));
284
285
  }
285
286
  /**
286
- * Removes skill directories for workflows when delivery changed to commands-only.
287
- * Returns the number of directories removed.
288
- */
289
- async removeSkillDirs(skillsDir) {
290
- let removed = 0;
291
- for (const workflow of ALL_WORKFLOWS) {
292
- const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
293
- if (!dirName)
294
- continue;
295
- const skillDir = path.join(skillsDir, dirName);
296
- try {
297
- if (fs.existsSync(skillDir)) {
298
- await fs.promises.rm(skillDir, { recursive: true, force: true });
299
- removed++;
300
- }
301
- }
302
- catch {
303
- // Ignore errors
304
- }
305
- }
306
- return removed;
307
- }
308
- /**
309
- * Removes skill directories for workflows that are no longer selected in the active profile.
310
- * Returns the number of directories removed.
311
- */
312
- async removeUnselectedSkillDirs(skillsDir, desiredWorkflows) {
313
- const desiredSet = new Set(desiredWorkflows);
314
- let removed = 0;
315
- for (const workflow of ALL_WORKFLOWS) {
316
- if (desiredSet.has(workflow))
317
- continue;
318
- const dirName = WORKFLOW_TO_SKILL_DIR[workflow];
319
- if (!dirName)
320
- continue;
321
- const skillDir = path.join(skillsDir, dirName);
322
- try {
323
- if (fs.existsSync(skillDir)) {
324
- await fs.promises.rm(skillDir, { recursive: true, force: true });
325
- removed++;
326
- }
327
- }
328
- catch {
329
- // Ignore errors
330
- }
331
- }
332
- return removed;
333
- }
334
- /**
335
- * Removes command files for workflows when delivery changed to skills-only.
336
- * Returns the number of files removed.
337
- */
338
- async removeCommandFiles(projectPath, toolId) {
339
- let removed = 0;
340
- const adapter = CommandAdapterRegistry.get(toolId);
341
- if (!adapter)
342
- return 0;
343
- for (const workflow of ALL_WORKFLOWS) {
344
- const cmdPath = adapter.getFilePath(workflow);
345
- const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
346
- try {
347
- if (fs.existsSync(fullPath)) {
348
- await fs.promises.unlink(fullPath);
349
- removed++;
350
- }
351
- }
352
- catch {
353
- // Ignore errors
354
- }
355
- }
356
- return removed;
357
- }
358
- /**
359
- * Removes command files for workflows that are no longer selected in the active profile.
360
- * Returns the number of files removed.
361
- */
362
- async removeUnselectedCommandFiles(projectPath, toolId, desiredWorkflows) {
363
- let removed = 0;
364
- const adapter = CommandAdapterRegistry.get(toolId);
365
- if (!adapter)
366
- return 0;
367
- const desiredSet = new Set(desiredWorkflows);
368
- for (const workflow of ALL_WORKFLOWS) {
369
- if (desiredSet.has(workflow))
370
- continue;
371
- const cmdPath = adapter.getFilePath(workflow);
372
- const fullPath = path.isAbsolute(cmdPath) ? cmdPath : path.join(projectPath, cmdPath);
373
- try {
374
- if (fs.existsSync(fullPath)) {
375
- await fs.promises.unlink(fullPath);
376
- removed++;
377
- }
378
- }
379
- catch {
380
- // Ignore errors
381
- }
382
- }
383
- return removed;
384
- }
385
- /**
386
- * Detect and handle legacy OpenSpec artifacts.
287
+ * Detect and handle legacy artifacts.
387
288
  * Unlike init, update warns but continues if legacy files found in non-interactive mode.
388
289
  * Returns array of tool IDs that were newly configured during legacy upgrade.
389
290
  */
@@ -523,7 +424,7 @@ export class UpdateCommand {
523
424
  const skillFile = path.join(skillDir, 'SKILL.md');
524
425
  // Use hyphen-based command references for tools where filename = command name
525
426
  const transformer = tool.value === 'pi' ? transformToHyphenCommands : undefined;
526
- const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
427
+ const skillContent = generateSkillContent(template, SYNARCX_VERSION, transformer);
527
428
  await FileSystemUtils.writeFile(skillFile, skillContent);
528
429
  }
529
430
  }