synarcx 0.3.1 → 0.3.3

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.
@@ -3,11 +3,47 @@ export function getSynSyncSkillTemplate() {
3
3
  return {
4
4
  name: 'syn-sync',
5
5
  description: 'Generate or update synspec/constitution.md with README validation, supporting file scan, guardrail Q&A, and structured constitution generation.',
6
- instructions: `## Step 0: SynArcX Version Check (MUST run first)
6
+ instructions: `## Step 0: Pending Spec Sync Check (runs before version check)
7
7
 
8
- Do NOT read any project files yet. Run this version check first.
8
+ Check for pending spec syncs from recently archived changes. This runs before the version check because it's a local filesystem operation and should not be blocked by network issues.
9
9
 
10
- ### 0.1 Read the daily cache
10
+ ### 0.1 Read \`synspec/.pending-sync.json\`
11
+
12
+ If the file doesn't exist, skip this step entirely (no pending syncs).
13
+
14
+ Expected format:
15
+ \`\`\`json
16
+ { "pending": [{ "change": "YYYY-MM-DD-<name>", "archivedAt": "<ISO>", "syncedAt": null }] }
17
+ \`\`\`
18
+
19
+ ### 0.2 Process pending entries (FIFO order)
20
+
21
+ For each entry where \`syncedAt\` is \`null\`:
22
+ - Construct path: \`synspec/changes/archive/<change>/\`
23
+ - Call \`findSpecUpdates(archivePath, synspec/specs/)\` to discover delta specs in the archived change
24
+ - For each delta spec, call \`buildUpdatedSpec()\` to merge into main specs
25
+ - Write rebuilt specs atomically (\`.tmp\` + rename to \`spec.md\`)
26
+ - Show per-change output: "Synced specs from <change>: <capability>: +N ~M"
27
+ - After each change processed, update its \`syncedAt\` field
28
+ - If \`buildUpdatedSpec()\` throws: show error, leave \`syncedAt\` as \`null\` (retry next time)
29
+
30
+ ### 0.3 Clean up marker
31
+
32
+ After all pending entries are processed:
33
+ - If all entries have \`syncedAt\` set: delete \`synspec/.pending-sync.json\` entirely (clean slate)
34
+ - If some entries failed (\`syncedAt\` still \`null\`): keep the marker, show which changes still need attention, and why
35
+
36
+ ### 0.4 Proceed to version check
37
+
38
+ Once pending syncs are handled, proceed to Step 1.
39
+
40
+ ---
41
+
42
+ ## Step 1: SynArcX Version Check
43
+
44
+ Do NOT read any project files yet. Run this version check after pending spec syncs are handled.
45
+
46
+ ### 1.1 Read the daily cache
11
47
 
12
48
  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
49
 
@@ -18,15 +54,15 @@ Expected cache format:
18
54
 
19
55
  If missing or malformed, treat as cache miss and continue.
20
56
 
21
- ### 0.2 Fetch latest from npm
57
+ ### 1.2 Fetch latest from npm
22
58
 
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\`.
59
+ Run \`npm view synarcx version\`. On failure (no npm, no network, non-zero exit): silently skip to 1.5, write cache with \`latestVersion: null\`.
24
60
 
25
- ### 0.3 Get installed version
61
+ ### 1.3 Get installed version
26
62
 
27
- Run \`synarcx --version\`. On failure: silently skip to 0.5.
63
+ Run \`synarcx --version\`. On failure: silently skip to 1.5.
28
64
 
29
- ### 0.4 Compare and prompt
65
+ ### 1.4 Compare and prompt
30
66
 
31
67
  Parse both as semver: split on \`.\`, parse each as integer, compare major→minor→patch. If npm version > installed:
32
68
 
@@ -37,7 +73,7 @@ Parse both as semver: split on \`.\`, parse each as integer, compare major→min
37
73
 
38
74
  If versions match, proceed silently.
39
75
 
40
- ### 0.5 Write cache
76
+ ### 1.5 Write cache
41
77
 
42
78
  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
79
 
@@ -150,23 +186,31 @@ export function getSynSyncCommandTemplate() {
150
186
  name: 'syn:sync',
151
187
  description: 'Generate/update project constitution with README validation, guardrail Q&A, and constraint capture',
152
188
  tags: ['workflow', 'sync', 'project'],
153
- content: `## Step 0: SynArcX Version Check (MUST run first)
189
+ content: `## Step 0: Pending Spec Sync Check (runs before version check)
190
+
191
+ Check \`synspec/.pending-sync.json\`. If file missing, skip (no pending syncs). Expected format: \`{ "pending": [{ "change": "YYYY-MM-DD-<name>", "archivedAt": "<ISO>", "syncedAt": null }] }\`.
192
+
193
+ For each entry with \`syncedAt: null\`: construct path \`synspec/changes/archive/<change>/\`, call \`findSpecUpdates()\` + \`buildUpdatedSpec()\`, write atomically (\`.tmp\` + rename), update \`syncedAt\`. On error: leave \`syncedAt: null\` for retry.
194
+
195
+ After all processed: if all entries have \`syncedAt\`, delete marker file. If some still \`null\`, keep marker with error info. Then proceed.
196
+
197
+ ---
154
198
 
155
- Do NOT read any project files yet. Run this version check first.
199
+ ## Step 1: SynArcX Version Check
156
200
 
157
- ### 0.1 Read the daily cache
201
+ ### 1.1 Read the daily cache
158
202
 
159
203
  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
204
 
161
- ### 0.2 Fetch latest from npm
205
+ ### 1.2 Fetch latest from npm
162
206
 
163
- Run \`npm view synarcx version\`. On failure, silently skip to 0.5, write cache with \`latestVersion: null\`.
207
+ Run \`npm view synarcx version\`. On failure, silently skip to 1.5, write cache with \`latestVersion: null\`.
164
208
 
165
- ### 0.3 Get installed version
209
+ ### 1.3 Get installed version
166
210
 
167
- Run \`synarcx --version\`. On failure, silently skip to 0.5.
211
+ Run \`synarcx --version\`. On failure, silently skip to 1.5.
168
212
 
169
- ### 0.4 Compare and prompt
213
+ ### 1.4 Compare and prompt
170
214
 
171
215
  Parse both as semver: split on \`.\`, parse each as integer, compare major→minor→patch. If npm version > installed:
172
216
 
@@ -177,7 +221,7 @@ Parse both as semver: split on \`.\`, parse each as integer, compare major→min
177
221
 
178
222
  If versions match, proceed silently.
179
223
 
180
- ### 0.5 Write cache
224
+ ### 1.5 Write cache
181
225
 
182
226
  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
227
 
@@ -38,6 +38,12 @@ export declare class UpdateCommand {
38
38
  * Displays a note about extra workflows installed that aren't in the current profile.
39
39
  */
40
40
  private displayExtraWorkflowsNote;
41
+ /**
42
+ * Runs on every `synarcx update` so any newly introduced workflow (e.g. syn:review)
43
+ * is automatically added to the saved custom workflow list rather than silently dropped.
44
+ * Also prints a dim notice listing what was added.
45
+ */
46
+ private runSyncNewCoreWorkflowsToCustomProfile;
41
47
  /**
42
48
  * Suggest opting back into core when a custom profile still matches the old
43
49
  * pre-sync core set. Keep custom profiles user-owned; do not mutate them.
@@ -16,7 +16,7 @@ import { getToolVersionStatus, getSkillTemplates, getCommandContents, generateSk
16
16
  import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, getToolsFromLegacyArtifacts, } from './legacy-cleanup.js';
17
17
  import { isInteractive } from '../utils/interactive.js';
18
18
  import { getGlobalConfig } from './global-config.js';
19
- import { getProfileWorkflows } from './profiles.js';
19
+ import { getProfileWorkflows, syncNewCoreWorkflowsToCustomProfile } from './profiles.js';
20
20
  import { ALL_WORKFLOWS } from './shared/workflow-registry.js';
21
21
  import { removeSkillDirs, removeUnselectedSkillDirs, removeCommandFiles, removeUnselectedCommandFiles, } from './shared/artifact-cleanup.js';
22
22
  import { getAvailableTools } from './available-tools.js';
@@ -57,7 +57,13 @@ export class UpdateCommand {
57
57
  const globalConfig = getGlobalConfig();
58
58
  const profile = globalConfig.profile ?? 'core';
59
59
  const delivery = globalConfig.delivery ?? 'both';
60
- const profileWorkflows = getProfileWorkflows(profile, globalConfig.workflows);
60
+ // 3b. For custom profiles, auto-merge any new ALL_WORKFLOWS entries that aren't
61
+ // listed yet (e.g. 'syn:review' added in a new release). This prevents future
62
+ // versions from silently dropping new commands for existing custom-profile users.
63
+ this.runSyncNewCoreWorkflowsToCustomProfile(globalConfig);
64
+ // Re-read after potential mutation so desiredWorkflows is always current.
65
+ const effectiveConfig = getGlobalConfig();
66
+ const profileWorkflows = getProfileWorkflows(profile, effectiveConfig.workflows);
61
67
  const desiredWorkflows = profileWorkflows.filter((workflow) => ALL_WORKFLOWS.includes(workflow));
62
68
  const shouldGenerateSkills = delivery !== 'commands';
63
69
  const shouldGenerateCommands = delivery !== 'skills';
@@ -266,6 +272,23 @@ export class UpdateCommand {
266
272
  console.log(chalk.dim(`Note: ${extraWorkflows.length} extra workflows not in profile (use \`synarcx config profile\` to manage)`));
267
273
  }
268
274
  }
275
+ /**
276
+ * Runs on every `synarcx update` so any newly introduced workflow (e.g. syn:review)
277
+ * is automatically added to the saved custom workflow list rather than silently dropped.
278
+ * Also prints a dim notice listing what was added.
279
+ */
280
+ runSyncNewCoreWorkflowsToCustomProfile(config) {
281
+ const before = config.workflows ? [...config.workflows] : null;
282
+ syncNewCoreWorkflowsToCustomProfile(config);
283
+ const after = config.workflows ?? [];
284
+ const beforeSet = new Set(before ?? []);
285
+ const added = after.filter(w => !beforeSet.has(w));
286
+ if (added.length > 0) {
287
+ const listed = added.map(w => `/syn:${w}`).join(', ');
288
+ console.log(chalk.dim(`Auto-added new workflow(s) to your profile: ${listed}`));
289
+ console.log();
290
+ }
291
+ }
269
292
  /**
270
293
  * Suggest opting back into core when a custom profile still matches the old
271
294
  * pre-sync core set. Keep custom profiles user-owned; do not mutate them.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synarcx",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Structured engineering workflows for AI coding assistants — persistent project memory, spec-driven development, and architecture drift prevention across every session.",
5
5
  "keywords": [
6
6
  "ai-workflow",