synarcx 0.3.2 → 0.3.4
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.
- package/README.md +45 -35
- package/dist/core/config.js +24 -24
- package/dist/core/init.js +6 -3
- package/dist/core/profiles.d.ts +10 -2
- package/dist/core/profiles.js +20 -1
- package/dist/core/shared/artifact-cleanup.js +12 -0
- package/dist/core/specs-apply.js +8 -3
- package/dist/core/templates/workflows/review.js +80 -29
- package/dist/core/templates/workflows/sync.js +62 -18
- package/dist/core/update.d.ts +2 -3
- package/dist/core/update.js +19 -22
- package/package.json +87 -83
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ Then in your AI coding tool:
|
|
|
92
92
|
3. `/syn:propose "my-feature"` — create proposal, specs, design, and tasks in one step
|
|
93
93
|
4. `/syn:clarify` — sharpen artifacts with targeted questions + auto consistency check
|
|
94
94
|
5. `/syn:apply` — implement the tasks
|
|
95
|
-
6. `/syn:review` — verify implementation, run sanity checks,
|
|
95
|
+
6. `/syn:review` — verify implementation, run sanity checks, then choose: archive (auto spec sync), add more work (scope-gated), or start a new change
|
|
96
96
|
|
|
97
97
|
For specific cases, use these instead of `/syn:explore`:
|
|
98
98
|
|
|
@@ -126,22 +126,32 @@ Session 1 ──► constitution.md ──► Session 2 ──► constitu
|
|
|
126
126
|
**The workflow:**
|
|
127
127
|
|
|
128
128
|
```
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
explore ──┐
|
|
132
|
-
debug ──┤
|
|
133
|
-
|
|
134
|
-
│
|
|
135
|
-
refactor ──┘
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
quick ─────────────────────────────────────┐
|
|
130
|
+
│
|
|
131
|
+
explore ──┐ │
|
|
132
|
+
debug ──┤ ▼
|
|
133
|
+
├───► propose ──► clarify ──► apply ──► review
|
|
134
|
+
│ ▲ ▲ │
|
|
135
|
+
refactor ──┘ │ │ │
|
|
136
|
+
│ │ │
|
|
137
|
+
│ │ ┌───────────────┼───────────────┐
|
|
138
|
+
│ │ ▼ ▼ ▼
|
|
139
|
+
│ add more work new change archive
|
|
140
|
+
│ │ │
|
|
141
|
+
└─────────────────────────────────┘ ▼
|
|
142
|
+
sync ──────────────────────────────────────────────────────────► constitution
|
|
138
143
|
```
|
|
139
144
|
|
|
140
145
|
Each step suggests the next — you decide when to advance. Works in Claude Code, Cursor, Cline, and any AI coding tool that supports slash commands.
|
|
141
146
|
|
|
142
|
-
- `sync` generates the `constitution.md` — run once, re-run when the project shifts. Also runs a daily version check
|
|
147
|
+
- `sync` generates the `constitution.md` — run once, re-run when the project shifts. Also runs a daily version check (prompts to auto-update) and checks for pending spec syncs from recently archived changes.
|
|
143
148
|
- `explore`, `debug`, and `refactor` are entry points that hand off to `propose`
|
|
144
149
|
- `quick` skips the pipeline for small, low-risk changes
|
|
150
|
+
- Run `synarcx update` in your terminal to refresh all skill and command files after installing a new version
|
|
151
|
+
- `review` is a three-way fork:
|
|
152
|
+
- **Archive now**: auto-syncs delta specs to main spec via `buildUpdatedSpec()`, writes `.pending-sync.json` marker, moves to archive. If spec sync fails, the change is already safely in archive; retry on next sync run.
|
|
153
|
+
- **Add more work**: scope gate reads proposal capabilities + design goals/non-goals. In-scope → update artifacts, MUST `/syn:clarify` then `/syn:apply` then `/syn:review` again (loop). Out-of-scope → offer archive then route to `/syn:propose`.
|
|
154
|
+
- **Start a new change**: routes to `/syn:propose`.
|
|
145
155
|
|
|
146
156
|
Each change gets its own folder under `synspec/changes/` with:
|
|
147
157
|
|
|
@@ -183,35 +193,35 @@ A typical `constitution.md` includes:
|
|
|
183
193
|
|
|
184
194
|
Used inside your AI coding tool (Claude Code, Cursor, Cline, etc.):
|
|
185
195
|
|
|
186
|
-
| Command | Description
|
|
187
|
-
| ----------------- |
|
|
188
|
-
| `/syn:sync` | Scan project, run guardrail Q&A, generate/update `constitution.md`. Auto-checks for synarcx updates once daily. |
|
|
189
|
-
| `/syn:explore` | Think through ideas, investigate problems, clarify requirements
|
|
190
|
-
| `/syn:debug` | Diagnose a known error (3-phase analysis), then prompts `/syn:propose`
|
|
191
|
-
| `/syn:refactor` | Map current vs target structure, then prompts `/syn:propose`
|
|
192
|
-
| `/syn:quick` | Fast-path for small low-risk changes — inline preview, confirm, apply
|
|
193
|
-
| `/syn:propose` | Create a new change with proposal, specs, design, and tasks
|
|
194
|
-
| `/syn:clarify` | Targeted Q&A (adaptive limit) + auto consistency checks in one command
|
|
195
|
-
| `/syn:analyze` | Standalone cross-artifact consistency check (auto-run by clarify)
|
|
196
|
-
| `/syn:apply` | Implement tasks from a change's task list
|
|
197
|
-
| `/syn:review` | Verify implementation, run sanity checks, and archive
|
|
196
|
+
| Command | Description |
|
|
197
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
198
|
+
| `/syn:sync` | Scan project, run guardrail Q&A, generate/update `constitution.md`. Checks pending spec syncs from archived changes first. Auto-checks for synarcx updates once daily. |
|
|
199
|
+
| `/syn:explore` | Think through ideas, investigate problems, clarify requirements |
|
|
200
|
+
| `/syn:debug` | Diagnose a known error (3-phase analysis), then prompts `/syn:propose` |
|
|
201
|
+
| `/syn:refactor` | Map current vs target structure, then prompts `/syn:propose` |
|
|
202
|
+
| `/syn:quick` | Fast-path for small low-risk changes — inline preview, confirm, apply |
|
|
203
|
+
| `/syn:propose` | Create a new change with proposal, specs, design, and tasks |
|
|
204
|
+
| `/syn:clarify` | Targeted Q&A (adaptive limit) + auto consistency checks in one command |
|
|
205
|
+
| `/syn:analyze` | Standalone cross-artifact consistency check (auto-run by clarify) |
|
|
206
|
+
| `/syn:apply` | Implement tasks from a change's task list |
|
|
207
|
+
| `/syn:review` | Verify implementation, run sanity checks, and three-way fork: archive (auto spec sync), add more work (scope gate), or start new change |
|
|
198
208
|
|
|
199
209
|
### CLI Commands
|
|
200
210
|
|
|
201
211
|
Used in your terminal:
|
|
202
212
|
|
|
203
|
-
| Command | Description
|
|
204
|
-
| ------------------- |
|
|
205
|
-
| `synarcx init` | Set up SynArcX workflow structure in your repository
|
|
206
|
-
| `synarcx update` | Refresh skill and command files for all configured tools
|
|
207
|
-
| `synarcx sync` | Regenerate `constitution.md`
|
|
208
|
-
| `synarcx explore` | Open explore session
|
|
209
|
-
| `synarcx propose` | Create a structured change proposal
|
|
210
|
-
| `synarcx clarify` | Targeted Q&A (adaptive limit) + auto consistency checks
|
|
211
|
-
| `synarcx analyze` | Cross-artifact consistency check (standalone)
|
|
212
|
-
| `synarcx apply` | Execute implementation tasks
|
|
213
|
+
| Command | Description |
|
|
214
|
+
| ------------------- | ------------------------------------------------------------ |
|
|
215
|
+
| `synarcx init` | Set up SynArcX workflow structure in your repository |
|
|
216
|
+
| `synarcx update` | Refresh skill and command files for all configured tools |
|
|
217
|
+
| `synarcx sync` | Regenerate `constitution.md` |
|
|
218
|
+
| `synarcx explore` | Open explore session |
|
|
219
|
+
| `synarcx propose` | Create a structured change proposal |
|
|
220
|
+
| `synarcx clarify` | Targeted Q&A (adaptive limit) + auto consistency checks |
|
|
221
|
+
| `synarcx analyze` | Cross-artifact consistency check (standalone) |
|
|
222
|
+
| `synarcx apply` | Execute implementation tasks |
|
|
213
223
|
| `synarcx review` | Verify implementation, run sanity checks, archive when clean |
|
|
214
|
-
| `synarcx quick` | Fast-path execution for small changes
|
|
224
|
+
| `synarcx quick` | Fast-path execution for small changes |
|
|
215
225
|
|
|
216
226
|
---
|
|
217
227
|
|
|
@@ -266,7 +276,7 @@ SynArcX is evolving toward an architecture-aware workflow system for long-runnin
|
|
|
266
276
|
|
|
267
277
|
## Status
|
|
268
278
|
|
|
269
|
-
**v0.3.x** — `syn:review`
|
|
279
|
+
**v0.3.x** — `syn:review` three-way fork (archive with auto spec sync, scope-gated add-more-work, or start new change); `syn:sync` extended with pending spec sync backstop for recently archived changes; archive writes `.pending-sync.json` marker consumed by sync; atomic spec writes prevent half-written corruption; retroactive fix for broken main spec format; seamless upgrade from v0.2.x (auto-migrates to `core` profile)
|
|
270
280
|
|
|
271
281
|
Active development roadmap:
|
|
272
282
|
|
package/dist/core/config.js
CHANGED
|
@@ -4,32 +4,32 @@ export const SYNSPEC_MARKERS = {
|
|
|
4
4
|
end: '<!-- SYNSPEC:END -->'
|
|
5
5
|
};
|
|
6
6
|
export const AI_TOOLS = [
|
|
7
|
-
{ name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer', skillsDir: '.amazonq' },
|
|
8
|
-
{ name: 'Antigravity', value: 'antigravity', available: true, successLabel: 'Antigravity', skillsDir: '.agent' },
|
|
9
|
-
{ name: 'Auggie (Augment CLI)', value: 'auggie', available: true, successLabel: 'Auggie', skillsDir: '.augment' },
|
|
10
|
-
{ name: 'Bob Shell', value: 'bob', available: true, successLabel: 'Bob Shell', skillsDir: '.bob' },
|
|
7
|
+
{ name: 'Amazon Q Developer', value: 'amazon-q', available: true, successLabel: 'Amazon Q Developer', skillsDir: '.amazonq', hasCommands: true },
|
|
8
|
+
{ name: 'Antigravity', value: 'antigravity', available: true, successLabel: 'Antigravity', skillsDir: '.agent', hasCommands: true },
|
|
9
|
+
{ name: 'Auggie (Augment CLI)', value: 'auggie', available: true, successLabel: 'Auggie', skillsDir: '.augment', hasCommands: true },
|
|
10
|
+
{ name: 'Bob Shell', value: 'bob', available: true, successLabel: 'Bob Shell', skillsDir: '.bob', hasCommands: true },
|
|
11
11
|
{ name: 'Claude Code', value: 'claude', available: true, successLabel: 'Claude Code', skillsDir: '.claude', hasCommands: true },
|
|
12
|
-
{ name: 'Cline', value: 'cline', available: true, successLabel: 'Cline', skillsDir: '.cline' },
|
|
13
|
-
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex', skillsDir: '.codex' },
|
|
14
|
-
{ name: 'CodeBuddy Code (CLI)', value: 'codebuddy', available: true, successLabel: 'CodeBuddy Code', skillsDir: '.codebuddy' },
|
|
15
|
-
{ name: 'Continue', value: 'continue', available: true, successLabel: 'Continue (VS Code / JetBrains / Cli)', skillsDir: '.continue' },
|
|
16
|
-
{ name: 'CoStrict', value: 'costrict', available: true, successLabel: 'CoStrict', skillsDir: '.cospec' },
|
|
17
|
-
{ name: 'Crush', value: 'crush', available: true, successLabel: 'Crush', skillsDir: '.crush' },
|
|
18
|
-
{ name: 'Cursor', value: 'cursor', available: true, successLabel: 'Cursor', skillsDir: '.cursor' },
|
|
19
|
-
{ name: 'Factory Droid', value: 'factory', available: true, successLabel: 'Factory Droid', skillsDir: '.factory' },
|
|
20
|
-
{ name: 'Gemini CLI', value: 'gemini', available: true, successLabel: 'Gemini CLI', skillsDir: '.gemini' },
|
|
21
|
-
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot', skillsDir: '.github', detectionPaths: ['.github/copilot-instructions.md', '.github/instructions', '.github/workflows/copilot-setup-steps.yml', '.github/prompts', '.github/agents', '.github/skills', '.github/.mcp.json'] },
|
|
22
|
-
{ name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow', skillsDir: '.iflow' },
|
|
23
|
-
{ name: 'Junie', value: 'junie', available: true, successLabel: 'Junie', skillsDir: '.junie' },
|
|
24
|
-
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code', skillsDir: '.kilocode' },
|
|
25
|
-
{ name: 'Kiro', value: 'kiro', available: true, successLabel: 'Kiro', skillsDir: '.kiro' },
|
|
12
|
+
{ name: 'Cline', value: 'cline', available: true, successLabel: 'Cline', skillsDir: '.cline', hasCommands: true },
|
|
13
|
+
{ name: 'Codex', value: 'codex', available: true, successLabel: 'Codex', skillsDir: '.codex', hasCommands: true },
|
|
14
|
+
{ name: 'CodeBuddy Code (CLI)', value: 'codebuddy', available: true, successLabel: 'CodeBuddy Code', skillsDir: '.codebuddy', hasCommands: true },
|
|
15
|
+
{ name: 'Continue', value: 'continue', available: true, successLabel: 'Continue (VS Code / JetBrains / Cli)', skillsDir: '.continue', hasCommands: true },
|
|
16
|
+
{ name: 'CoStrict', value: 'costrict', available: true, successLabel: 'CoStrict', skillsDir: '.cospec', hasCommands: true },
|
|
17
|
+
{ name: 'Crush', value: 'crush', available: true, successLabel: 'Crush', skillsDir: '.crush', hasCommands: true },
|
|
18
|
+
{ name: 'Cursor', value: 'cursor', available: true, successLabel: 'Cursor', skillsDir: '.cursor', hasCommands: true },
|
|
19
|
+
{ name: 'Factory Droid', value: 'factory', available: true, successLabel: 'Factory Droid', skillsDir: '.factory', hasCommands: true },
|
|
20
|
+
{ name: 'Gemini CLI', value: 'gemini', available: true, successLabel: 'Gemini CLI', skillsDir: '.gemini', hasCommands: true },
|
|
21
|
+
{ name: 'GitHub Copilot', value: 'github-copilot', available: true, successLabel: 'GitHub Copilot', skillsDir: '.github', detectionPaths: ['.github/copilot-instructions.md', '.github/instructions', '.github/workflows/copilot-setup-steps.yml', '.github/prompts', '.github/agents', '.github/skills', '.github/.mcp.json'], hasCommands: true },
|
|
22
|
+
{ name: 'iFlow', value: 'iflow', available: true, successLabel: 'iFlow', skillsDir: '.iflow', hasCommands: true },
|
|
23
|
+
{ name: 'Junie', value: 'junie', available: true, successLabel: 'Junie', skillsDir: '.junie', hasCommands: true },
|
|
24
|
+
{ name: 'Kilo Code', value: 'kilocode', available: true, successLabel: 'Kilo Code', skillsDir: '.kilocode', hasCommands: true },
|
|
25
|
+
{ name: 'Kiro', value: 'kiro', available: true, successLabel: 'Kiro', skillsDir: '.kiro', hasCommands: true },
|
|
26
26
|
{ name: 'OpenCode', value: 'opencode', available: true, successLabel: 'OpenCode', skillsDir: '.opencode', hasCommands: true },
|
|
27
|
-
{ name: 'Pi', value: 'pi', available: true, successLabel: 'Pi', skillsDir: '.pi' },
|
|
28
|
-
{ name: 'Qoder', value: 'qoder', available: true, successLabel: 'Qoder', skillsDir: '.qoder' },
|
|
29
|
-
{ name: 'Lingma', value: 'lingma', available: true, successLabel: 'Lingma', skillsDir: '.lingma' },
|
|
30
|
-
{ name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code', skillsDir: '.qwen' },
|
|
31
|
-
{ name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode', skillsDir: '.roo' },
|
|
32
|
-
{ name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf', skillsDir: '.windsurf' },
|
|
27
|
+
{ name: 'Pi', value: 'pi', available: true, successLabel: 'Pi', skillsDir: '.pi', hasCommands: true },
|
|
28
|
+
{ name: 'Qoder', value: 'qoder', available: true, successLabel: 'Qoder', skillsDir: '.qoder', hasCommands: true },
|
|
29
|
+
{ name: 'Lingma', value: 'lingma', available: true, successLabel: 'Lingma', skillsDir: '.lingma', hasCommands: true },
|
|
30
|
+
{ name: 'Qwen Code', value: 'qwen', available: true, successLabel: 'Qwen Code', skillsDir: '.qwen', hasCommands: true },
|
|
31
|
+
{ name: 'RooCode', value: 'roocode', available: true, successLabel: 'RooCode', skillsDir: '.roo', hasCommands: true },
|
|
32
|
+
{ name: 'Windsurf', value: 'windsurf', available: true, successLabel: 'Windsurf', skillsDir: '.windsurf', hasCommands: true },
|
|
33
33
|
{ name: 'AGENTS.md (works with Amp, VS Code, …)', value: 'agents', available: false, successLabel: 'your AGENTS.md-compatible assistant' }
|
|
34
34
|
];
|
|
35
35
|
//# sourceMappingURL=config.js.map
|
package/dist/core/init.js
CHANGED
|
@@ -19,7 +19,7 @@ import { generateCommands, CommandAdapterRegistry, } from './command-generation/
|
|
|
19
19
|
import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, } from './legacy-cleanup.js';
|
|
20
20
|
import { getToolsWithSkillsDir, getToolStates, getSkillTemplates, getCommandContents, generateSkillContent, } from './shared/index.js';
|
|
21
21
|
import { getGlobalConfig } from './global-config.js';
|
|
22
|
-
import { getProfileWorkflows } from './profiles.js';
|
|
22
|
+
import { getProfileWorkflows, syncNewCoreWorkflowsToCustomProfile } from './profiles.js';
|
|
23
23
|
import { DEFAULT_SCHEMA } from './shared/workflow-registry.js';
|
|
24
24
|
import { removeSkillDirs, removeCommandFiles } from './shared/artifact-cleanup.js';
|
|
25
25
|
import { getAvailableTools } from './available-tools.js';
|
|
@@ -60,6 +60,9 @@ export class InitCommand {
|
|
|
60
60
|
// Migration check: migrate existing projects to profile system (task 7.3)
|
|
61
61
|
if (extendMode) {
|
|
62
62
|
migrateIfNeeded(projectPath, detectedTools);
|
|
63
|
+
// Ensure custom-profile users receive any new commands added since their last init
|
|
64
|
+
// (e.g. syn:review added in 0.3.x). Runs after migration so the profile is settled.
|
|
65
|
+
syncNewCoreWorkflowsToCustomProfile(getGlobalConfig());
|
|
63
66
|
}
|
|
64
67
|
// Show animated welcome screen (interactive mode only)
|
|
65
68
|
const canPrompt = this.canPromptInteractively();
|
|
@@ -381,8 +384,8 @@ export class InitCommand {
|
|
|
381
384
|
const skillsDir = path.join(projectPath, tool.skillsDir, 'skills');
|
|
382
385
|
removedSkillCount += await removeSkillDirs(skillsDir);
|
|
383
386
|
}
|
|
384
|
-
// Generate commands if delivery includes commands
|
|
385
|
-
if (shouldGenerateCommands) {
|
|
387
|
+
// Generate commands if delivery includes commands and tool supports them
|
|
388
|
+
if (shouldGenerateCommands && hasCommands) {
|
|
386
389
|
const adapter = CommandAdapterRegistry.get(tool.value);
|
|
387
390
|
if (adapter) {
|
|
388
391
|
const generatedCommands = generateCommands(commandContents, adapter);
|
package/dist/core/profiles.d.ts
CHANGED
|
@@ -4,12 +4,20 @@
|
|
|
4
4
|
* Defines workflow profiles that control which workflows are installed.
|
|
5
5
|
* Profiles determine WHICH workflows; delivery (in global config) determines HOW.
|
|
6
6
|
*/
|
|
7
|
-
import type { Profile } from './global-config.js';
|
|
7
|
+
import type { Profile, GlobalConfig } from './global-config.js';
|
|
8
8
|
/**
|
|
9
9
|
* Resolves which workflows should be active for a given profile configuration.
|
|
10
10
|
*
|
|
11
11
|
* - 'core' profile always returns CORE_WORKFLOWS
|
|
12
12
|
* - 'custom' profile returns the provided customWorkflows, or empty array if not provided
|
|
13
13
|
*/
|
|
14
|
-
export declare function getProfileWorkflows(profile: Profile, customWorkflows?: string[]): readonly string[];
|
|
14
|
+
export declare function getProfileWorkflows(profile: Profile, customWorkflows?: readonly string[]): readonly string[];
|
|
15
|
+
/**
|
|
16
|
+
* Ensures a 'custom' profile always contains all current ALL_WORKFLOWS entries.
|
|
17
|
+
* Called by both `synarcx init` and `synarcx update` so new commands (e.g. syn:review)
|
|
18
|
+
* are never silently dropped for users on a stale custom profile, regardless of which
|
|
19
|
+
* command they run after upgrading.
|
|
20
|
+
* No-op for 'core' profile (it derives its list from ALL_WORKFLOWS directly).
|
|
21
|
+
*/
|
|
22
|
+
export declare function syncNewCoreWorkflowsToCustomProfile(config: GlobalConfig): void;
|
|
15
23
|
//# sourceMappingURL=profiles.d.ts.map
|
package/dist/core/profiles.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Defines workflow profiles that control which workflows are installed.
|
|
5
5
|
* Profiles determine WHICH workflows; delivery (in global config) determines HOW.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { saveGlobalConfig } from './global-config.js';
|
|
8
|
+
import { ALL_WORKFLOWS, CORE_WORKFLOWS } from './shared/workflow-registry.js';
|
|
8
9
|
/**
|
|
9
10
|
* Resolves which workflows should be active for a given profile configuration.
|
|
10
11
|
*
|
|
@@ -17,4 +18,22 @@ export function getProfileWorkflows(profile, customWorkflows) {
|
|
|
17
18
|
}
|
|
18
19
|
return CORE_WORKFLOWS;
|
|
19
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Ensures a 'custom' profile always contains all current ALL_WORKFLOWS entries.
|
|
23
|
+
* Called by both `synarcx init` and `synarcx update` so new commands (e.g. syn:review)
|
|
24
|
+
* are never silently dropped for users on a stale custom profile, regardless of which
|
|
25
|
+
* command they run after upgrading.
|
|
26
|
+
* No-op for 'core' profile (it derives its list from ALL_WORKFLOWS directly).
|
|
27
|
+
*/
|
|
28
|
+
export function syncNewCoreWorkflowsToCustomProfile(config) {
|
|
29
|
+
if (config.profile !== 'custom')
|
|
30
|
+
return;
|
|
31
|
+
const current = config.workflows ?? [];
|
|
32
|
+
const currentSet = new Set(current);
|
|
33
|
+
const missing = ALL_WORKFLOWS.filter(w => !currentSet.has(w));
|
|
34
|
+
if (missing.length === 0)
|
|
35
|
+
return;
|
|
36
|
+
config.workflows = [...current, ...missing];
|
|
37
|
+
saveGlobalConfig(config);
|
|
38
|
+
}
|
|
20
39
|
//# sourceMappingURL=profiles.js.map
|
|
@@ -19,6 +19,18 @@ export async function removeSkillDirs(skillsDir) {
|
|
|
19
19
|
// Ignore errors
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
+
// Remove empty parent skills directory
|
|
23
|
+
try {
|
|
24
|
+
if (fs.existsSync(skillsDir)) {
|
|
25
|
+
const remaining = await fs.promises.readdir(skillsDir);
|
|
26
|
+
if (remaining.length === 0) {
|
|
27
|
+
await fs.promises.rmdir(skillsDir);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Ignore errors
|
|
33
|
+
}
|
|
22
34
|
return removed;
|
|
23
35
|
}
|
|
24
36
|
export async function removeUnselectedSkillDirs(skillsDir, desiredWorkflows) {
|
package/dist/core/specs-apply.js
CHANGED
|
@@ -270,7 +270,10 @@ export async function writeUpdatedSpec(update, rebuilt, counts) {
|
|
|
270
270
|
// Create target directory if needed
|
|
271
271
|
const targetDir = path.dirname(update.target);
|
|
272
272
|
await fs.mkdir(targetDir, { recursive: true });
|
|
273
|
-
|
|
273
|
+
// Atomic write: write to .tmp then rename (prevents half-written files on crash)
|
|
274
|
+
const tmpPath = update.target + '.tmp';
|
|
275
|
+
await fs.writeFile(tmpPath, rebuilt);
|
|
276
|
+
await fs.rename(tmpPath, update.target);
|
|
274
277
|
const specName = path.basename(path.dirname(update.target));
|
|
275
278
|
console.log(`Applying changes to synspec/specs/${specName}/spec.md:`);
|
|
276
279
|
if (counts.added)
|
|
@@ -347,10 +350,12 @@ export async function applySpecs(projectRoot, changeName, options = {}) {
|
|
|
347
350
|
for (const p of prepared) {
|
|
348
351
|
const capability = path.basename(path.dirname(p.update.target));
|
|
349
352
|
if (!options.dryRun) {
|
|
350
|
-
// Write the updated spec
|
|
353
|
+
// Write the updated spec (atomic: .tmp + rename)
|
|
351
354
|
const targetDir = path.dirname(p.update.target);
|
|
352
355
|
await fs.mkdir(targetDir, { recursive: true });
|
|
353
|
-
|
|
356
|
+
const tmpPath = p.update.target + '.tmp';
|
|
357
|
+
await fs.writeFile(tmpPath, p.rebuilt);
|
|
358
|
+
await fs.rename(tmpPath, p.update.target);
|
|
354
359
|
if (!options.silent) {
|
|
355
360
|
console.log(`Applying changes to synspec/specs/${capability}/spec.md:`);
|
|
356
361
|
if (p.counts.added)
|
|
@@ -77,15 +77,28 @@ export function getSynReviewSkillTemplate() {
|
|
|
77
77
|
|
|
78
78
|
**If ALL checks pass** (clean):
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
**For option B (add more work)**:
|
|
80
|
+
Present three options:
|
|
81
|
+
|
|
82
|
+
| Option | Label | What happens | Next |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| A | Archive now | AI moves change to archive/ with auto spec sync | Done (specs merged to main) |
|
|
85
|
+
| B | Add more work | AI reads proposal+design for scope boundary, checks scope gate | Scope check → update or route |
|
|
86
|
+
| C | Start a new change | AI suggests creating a fresh change | /syn:propose |
|
|
87
|
+
|
|
88
|
+
**For option B (add more work)**: Use the scope gate protocol:
|
|
89
|
+
|
|
90
|
+
1. Read \`proposal.md\` capabilities section and \`design.md\` goals/non-goals to establish scope boundary
|
|
91
|
+
2. Ask the user what additional work is needed
|
|
92
|
+
3. **IN SCOPE** (same capabilities, same concern, no non-goal violation):
|
|
93
|
+
- Update \`proposal.md\` if scope description needs widening
|
|
94
|
+
- Update spec with ADDED requirements
|
|
95
|
+
- Update \`design.md\` if new technical decisions
|
|
96
|
+
- Append unchecked tasks to \`tasks.md\`
|
|
97
|
+
- MUST run \`/syn:clarify\` then \`/syn:apply\` — no skipping refinement, no escape hatch for trivial changes
|
|
98
|
+
4. **OUT OF SCOPE** (new capability, different concern, violates non-goal):
|
|
99
|
+
- Inform user: "This is outside the current change's scope."
|
|
100
|
+
- Offer: "Archive current change first?" — YES archives with spec sync, NO leaves active
|
|
101
|
+
- Route: "Start a new change with \`/syn:propose\`"
|
|
89
102
|
|
|
90
103
|
**If any checks failed** (dirty):
|
|
91
104
|
|
|
@@ -103,13 +116,31 @@ export function getSynReviewSkillTemplate() {
|
|
|
103
116
|
|
|
104
117
|
**Note**: /syn:quick bypasses review entirely. Quick is the low-risk fast path.
|
|
105
118
|
|
|
106
|
-
8. **Archive inline** (when user picks option A)
|
|
119
|
+
8. **Archive inline** (when user picks option A, or in Option B out-of-scope with "yes" to archive)
|
|
120
|
+
|
|
121
|
+
a. **Check for delta specs**: Look for any \`synspec/changes/<name>/specs/<capability>/spec.md\` files. If any exist, proceed with spec sync. If none exist (infrastructure, doc-only change), skip the marker and just do the directory move.
|
|
122
|
+
|
|
123
|
+
b. **Write marker** (only if delta specs exist):
|
|
124
|
+
- Create \`synspec/.pending-sync.json\` if it doesn't exist
|
|
125
|
+
- Add entry: \`{ change: "YYYY-MM-DD-<change-name>", archivedAt: "<ISO timestamp>", syncedAt: null }\`
|
|
126
|
+
- Use the full archive directory name (with date prefix) in the \`change\` field
|
|
127
|
+
|
|
128
|
+
c. **Move to archive**:
|
|
129
|
+
- Create \`synspec/changes/archive/\` if missing
|
|
130
|
+
- Target: \`YYYY-MM-DD-<change-name>\`
|
|
131
|
+
- If target already exists: fail with error, suggest renaming
|
|
132
|
+
- Move: \`mv synspec/changes/<name> synspec/changes/archive/YYYY-MM-DD-<name>\`
|
|
107
133
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
134
|
+
d. **Sync specs** (only if delta specs existed):
|
|
135
|
+
- Call \`findSpecUpdates(archivePath, synspec/specs/)\` to discover delta specs
|
|
136
|
+
- For each delta: call \`buildUpdatedSpec()\`, write atomically (\`.tmp\` + rename)
|
|
137
|
+
- Show per-capability progress: "Syncing specs for <capability>: +N added, ~M modified"
|
|
138
|
+
- Update marker entry with \`syncedAt\` timestamp
|
|
139
|
+
- On failure: change is already safely archived, show error, marker stays \`null\` for backstop retry
|
|
140
|
+
|
|
141
|
+
e. **Confirm**:
|
|
142
|
+
- "Archived <change-name> to synspec/changes/archive/YYYY-MM-DD-<name>/"
|
|
143
|
+
- If specs were synced: "Specs synced: <capability>: +N ~M"
|
|
113
144
|
|
|
114
145
|
---
|
|
115
146
|
|
|
@@ -156,7 +187,7 @@ This change still has 3 tasks remaining.
|
|
|
156
187
|
→ Use /syn:apply to finish the remaining tasks, then run /syn:review again.
|
|
157
188
|
\`\`\`
|
|
158
189
|
|
|
159
|
-
## Output (Archived)
|
|
190
|
+
## Output (Archived with Spec Sync)
|
|
160
191
|
|
|
161
192
|
\`\`\`
|
|
162
193
|
## Archive Complete
|
|
@@ -164,16 +195,32 @@ This change still has 3 tasks remaining.
|
|
|
164
195
|
**Change:** <change-name>
|
|
165
196
|
**Archived to:** synspec/changes/archive/YYYY-MM-DD-<name>/
|
|
166
197
|
|
|
198
|
+
Specs synced:
|
|
199
|
+
review-command: +2 added, ~1 modified
|
|
200
|
+
|
|
167
201
|
All tasks complete. All checks passed. Change archived.
|
|
168
202
|
\`\`\`
|
|
169
203
|
|
|
204
|
+
## Output (Archived — No Delta Specs)
|
|
205
|
+
|
|
206
|
+
\`\`\`
|
|
207
|
+
## Archive Complete
|
|
208
|
+
|
|
209
|
+
**Change:** <change-name>
|
|
210
|
+
**Archived to:** synspec/changes/archive/YYYY-MM-DD-<name>/
|
|
211
|
+
|
|
212
|
+
No delta specs to sync. Change archived.
|
|
213
|
+
\`\`\`
|
|
214
|
+
|
|
170
215
|
## Guardrails
|
|
171
216
|
- Do NOT run checks if tasks are incomplete — gate at step 3
|
|
172
217
|
- Checks are read-only — do not modify project files during checks
|
|
173
|
-
- Archive
|
|
218
|
+
- Archive happens BEFORE spec sync — if sync fails, change is safely archived
|
|
174
219
|
- Always list ALL findings at once, don't ask "fix one at a time"
|
|
175
220
|
- For failed checks, show only summary counts unless user asks for details
|
|
176
|
-
- If user picks "add more work,"
|
|
221
|
+
- If user picks "add more work," use scope gate protocol: read proposal capabilities + design goals/non-goals before acting
|
|
222
|
+
- MUST run clarify after any in-scope expansion — no escape hatch for trivial changes
|
|
223
|
+
- Out-of-scope work: offer to archive first, then route to \`/syn:propose\`
|
|
177
224
|
- Quick bypasses review — do not suggest review to users who used /syn:quick`,
|
|
178
225
|
license: 'MIT',
|
|
179
226
|
compatibility: 'Requires synarcx CLI.',
|
|
@@ -236,24 +283,25 @@ export function getSynReviewCommandTemplate() {
|
|
|
236
283
|
|
|
237
284
|
Then narrative paragraph.
|
|
238
285
|
|
|
239
|
-
7. **Present the fork**
|
|
286
|
+
7. **Present the fork**
|
|
240
287
|
|
|
241
288
|
**If all checks pass:**
|
|
242
|
-
- Archive now →
|
|
243
|
-
- Add more work →
|
|
289
|
+
- Archive now → write marker, move to archive, sync specs, update marker
|
|
290
|
+
- Add more work → scope gate: read proposal capabilities + design goals/non-goals. If in scope: update artifacts, MUST run \`/syn:clarify\` then \`/syn:apply\`. If out of scope: offer to archive first (with spec sync), route to \`/syn:propose\`.
|
|
244
291
|
- Start a new change → \`/syn:propose\`
|
|
245
292
|
|
|
246
293
|
**If issues found:**
|
|
247
294
|
- Show each finding with route: \`/syn:apply\`, \`/syn:clarify\`, \`/syn:analyze\`
|
|
248
295
|
- List all findings at once
|
|
249
296
|
|
|
250
|
-
8. **Archive inline** (when user picks archive)
|
|
297
|
+
8. **Archive inline** (when user picks archive)
|
|
251
298
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
299
|
+
a. Check for delta specs. If none, skip marker and just move.
|
|
300
|
+
b. Write \`synspec/.pending-sync.json\` with \`syncedAt: null\` using full \`YYYY-MM-DD-<name>\` as change field
|
|
301
|
+
c. Move: \`mv synspec/changes/<name> synspec/changes/archive/YYYY-MM-DD-<name>\`
|
|
302
|
+
d. Sync specs: \`findSpecUpdates(archivePath)\` → \`buildUpdatedSpec()\` → atomic write (\`.tmp\` + rename) → per-capability output
|
|
303
|
+
e. Update marker with \`syncedAt\` timestamp
|
|
304
|
+
f. On failure: archive is already moved, marker stays \`null\`, backstop retries on next sync
|
|
257
305
|
|
|
258
306
|
---
|
|
259
307
|
|
|
@@ -267,7 +315,7 @@ Lint: 0 errors ✓
|
|
|
267
315
|
Typecheck: clean ✓
|
|
268
316
|
|
|
269
317
|
All checks pass. What would you like to do?
|
|
270
|
-
[A] Archive now [B] Add more work [C] Start a new change
|
|
318
|
+
[A] Archive now (with spec sync) [B] Add more work (scope-gated) [C] Start a new change
|
|
271
319
|
\`\`\`
|
|
272
320
|
|
|
273
321
|
## Output (Dirty)
|
|
@@ -286,7 +334,10 @@ Findings:
|
|
|
286
334
|
## Guardrails
|
|
287
335
|
- Gate on task completion — no checks until all tasks are done
|
|
288
336
|
- Summary only for failed checks (not full logs)
|
|
289
|
-
- Scope-
|
|
337
|
+
- Scope gate for Option B: read proposal capabilities + design goals/non-goals before acting
|
|
338
|
+
- In-scope expansion MUST run clarify then apply — no escape hatch
|
|
339
|
+
- Out-of-scope work: offer archive first, then route to /syn:propose
|
|
340
|
+
- Archive happens before spec sync — if sync fails, change is safely archived
|
|
290
341
|
- Quick bypasses review — do not suggest review after /syn:quick`
|
|
291
342
|
});
|
|
292
343
|
}
|
|
@@ -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:
|
|
6
|
+
instructions: `## Step 0: Pending Spec Sync Check (runs before version check)
|
|
7
7
|
|
|
8
|
-
|
|
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
|
|
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
|
-
###
|
|
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
|
|
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
|
-
###
|
|
61
|
+
### 1.3 Get installed version
|
|
26
62
|
|
|
27
|
-
Run \`synarcx --version\`. On failure: silently skip to
|
|
63
|
+
Run \`synarcx --version\`. On failure: silently skip to 1.5.
|
|
28
64
|
|
|
29
|
-
###
|
|
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
|
-
###
|
|
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:
|
|
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
|
-
|
|
199
|
+
## Step 1: SynArcX Version Check
|
|
156
200
|
|
|
157
|
-
###
|
|
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
|
-
###
|
|
205
|
+
### 1.2 Fetch latest from npm
|
|
162
206
|
|
|
163
|
-
Run \`npm view synarcx version\`. On failure, silently skip to
|
|
207
|
+
Run \`npm view synarcx version\`. On failure, silently skip to 1.5, write cache with \`latestVersion: null\`.
|
|
164
208
|
|
|
165
|
-
###
|
|
209
|
+
### 1.3 Get installed version
|
|
166
210
|
|
|
167
|
-
Run \`synarcx --version\`. On failure, silently skip to
|
|
211
|
+
Run \`synarcx --version\`. On failure, silently skip to 1.5.
|
|
168
212
|
|
|
169
|
-
###
|
|
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
|
-
###
|
|
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
|
|
package/dist/core/update.d.ts
CHANGED
|
@@ -39,12 +39,11 @@ export declare class UpdateCommand {
|
|
|
39
39
|
*/
|
|
40
40
|
private displayExtraWorkflowsNote;
|
|
41
41
|
/**
|
|
42
|
-
* Ensures a 'custom' profile always contains all current ALL_WORKFLOWS entries.
|
|
43
42
|
* Runs on every `synarcx update` so any newly introduced workflow (e.g. syn:review)
|
|
44
43
|
* is automatically added to the saved custom workflow list rather than silently dropped.
|
|
45
|
-
*
|
|
44
|
+
* Also prints a dim notice listing what was added.
|
|
46
45
|
*/
|
|
47
|
-
private
|
|
46
|
+
private runSyncNewCoreWorkflowsToCustomProfile;
|
|
48
47
|
/**
|
|
49
48
|
* Suggest opting back into core when a custom profile still matches the old
|
|
50
49
|
* pre-sync core set. Keep custom profiles user-owned; do not mutate them.
|
package/dist/core/update.js
CHANGED
|
@@ -15,8 +15,8 @@ import { generateCommands, CommandAdapterRegistry, } from './command-generation/
|
|
|
15
15
|
import { getToolVersionStatus, getSkillTemplates, getCommandContents, generateSkillContent, getToolsWithSkillsDir, } from './shared/index.js';
|
|
16
16
|
import { detectLegacyArtifacts, cleanupLegacyArtifacts, formatCleanupSummary, formatDetectionSummary, getToolsFromLegacyArtifacts, } from './legacy-cleanup.js';
|
|
17
17
|
import { isInteractive } from '../utils/interactive.js';
|
|
18
|
-
import { getGlobalConfig
|
|
19
|
-
import { getProfileWorkflows } from './profiles.js';
|
|
18
|
+
import { getGlobalConfig } from './global-config.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';
|
|
@@ -60,7 +60,7 @@ export class UpdateCommand {
|
|
|
60
60
|
// 3b. For custom profiles, auto-merge any new ALL_WORKFLOWS entries that aren't
|
|
61
61
|
// listed yet (e.g. 'syn:review' added in a new release). This prevents future
|
|
62
62
|
// versions from silently dropping new commands for existing custom-profile users.
|
|
63
|
-
this.
|
|
63
|
+
this.runSyncNewCoreWorkflowsToCustomProfile(globalConfig);
|
|
64
64
|
// Re-read after potential mutation so desiredWorkflows is always current.
|
|
65
65
|
const effectiveConfig = getGlobalConfig();
|
|
66
66
|
const profileWorkflows = getProfileWorkflows(profile, effectiveConfig.workflows);
|
|
@@ -144,12 +144,12 @@ export class UpdateCommand {
|
|
|
144
144
|
}
|
|
145
145
|
removedDeselectedSkillCount += await removeUnselectedSkillDirs(skillsDir, desiredWorkflows);
|
|
146
146
|
}
|
|
147
|
-
// Delete skill directories if delivery is commands-only
|
|
148
|
-
if (!shouldGenerateSkills) {
|
|
147
|
+
// Delete skill directories if delivery is commands-only or tool uses commands
|
|
148
|
+
if (!shouldGenerateSkills || tool.hasCommands) {
|
|
149
149
|
removedSkillCount += await removeSkillDirs(skillsDir);
|
|
150
150
|
}
|
|
151
|
-
// Generate commands if delivery includes commands
|
|
152
|
-
if (shouldGenerateCommands) {
|
|
151
|
+
// Generate commands if delivery includes commands and tool supports them
|
|
152
|
+
if (shouldGenerateCommands && tool.hasCommands) {
|
|
153
153
|
const adapter = CommandAdapterRegistry.get(tool.value);
|
|
154
154
|
if (adapter) {
|
|
155
155
|
const generatedCommands = generateCommands(commandContents, adapter);
|
|
@@ -273,24 +273,21 @@ export class UpdateCommand {
|
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
275
|
/**
|
|
276
|
-
* Ensures a 'custom' profile always contains all current ALL_WORKFLOWS entries.
|
|
277
276
|
* Runs on every `synarcx update` so any newly introduced workflow (e.g. syn:review)
|
|
278
277
|
* is automatically added to the saved custom workflow list rather than silently dropped.
|
|
279
|
-
*
|
|
278
|
+
* Also prints a dim notice listing what was added.
|
|
280
279
|
*/
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
285
|
-
const
|
|
286
|
-
const
|
|
287
|
-
if (
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
console.log(chalk.dim(`Auto-added new workflow(s) to your profile: ${listed}`));
|
|
293
|
-
console.log();
|
|
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
|
+
}
|
|
294
291
|
}
|
|
295
292
|
/**
|
|
296
293
|
* Suggest opting back into core when a custom profile still matches the old
|
package/package.json
CHANGED
|
@@ -1,83 +1,87 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "synarcx",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Structured engineering workflows for AI coding assistants — persistent project memory, spec-driven development, and architecture drift prevention across every session.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"ai-workflow",
|
|
7
|
-
"claude-code",
|
|
8
|
-
"cursor",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"ai-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
"
|
|
40
|
-
"dist"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"test
|
|
57
|
-
"test:
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
},
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "synarcx",
|
|
3
|
+
"version": "0.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
|
+
"keywords": [
|
|
6
|
+
"ai-workflow",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"cursor",
|
|
9
|
+
"opencode",
|
|
10
|
+
"codex",
|
|
11
|
+
"gemini",
|
|
12
|
+
"spec-driven-development",
|
|
13
|
+
"ai-coding-assistant",
|
|
14
|
+
"architecture-drift",
|
|
15
|
+
"persistent-context",
|
|
16
|
+
"engineering-workflow",
|
|
17
|
+
"ai-coding-workflow",
|
|
18
|
+
"specification",
|
|
19
|
+
"claude-code-workflow",
|
|
20
|
+
"ai-context-management"
|
|
21
|
+
],
|
|
22
|
+
"homepage": "https://github.com/funara/synarcx",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/funara/synarcx.git"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"author": "Adhi Rahmadian",
|
|
29
|
+
"type": "module",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"default": "./dist/index.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"bin": {
|
|
40
|
+
"synarcx": "./dist/index.js"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"bin",
|
|
45
|
+
"schemas",
|
|
46
|
+
"scripts/postinstall.js",
|
|
47
|
+
"!dist/**/*.test.js",
|
|
48
|
+
"!dist/**/__tests__",
|
|
49
|
+
"!dist/**/*.map"
|
|
50
|
+
],
|
|
51
|
+
"scripts": {
|
|
52
|
+
"lint": "eslint src/",
|
|
53
|
+
"build": "node build.js",
|
|
54
|
+
"dev": "tsc --watch",
|
|
55
|
+
"dev:cli": "pnpm build && node bin/synarcx.js",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"test:ui": "vitest --ui",
|
|
59
|
+
"test:coverage": "vitest --coverage",
|
|
60
|
+
"test:postinstall": "node scripts/postinstall.js",
|
|
61
|
+
"prepare": "pnpm run build",
|
|
62
|
+
"postinstall": "node scripts/postinstall.js"
|
|
63
|
+
},
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=20.19.0"
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@types/node": "^24.2.0",
|
|
69
|
+
"@vitest/ui": "^3.2.4",
|
|
70
|
+
"eslint": "^9.39.2",
|
|
71
|
+
"typescript": "^5.9.3",
|
|
72
|
+
"typescript-eslint": "^8.50.1",
|
|
73
|
+
"vitest": "^3.2.4"
|
|
74
|
+
},
|
|
75
|
+
"dependencies": {
|
|
76
|
+
"@inquirer/core": "^10.2.2",
|
|
77
|
+
"@inquirer/prompts": "^7.8.0",
|
|
78
|
+
"chalk": "^5.5.0",
|
|
79
|
+
"commander": "^14.0.0",
|
|
80
|
+
"cross-spawn": "7.0.6",
|
|
81
|
+
"fast-glob": "^3.3.3",
|
|
82
|
+
"ora": "^8.2.0",
|
|
83
|
+
"synarcx": "link:",
|
|
84
|
+
"yaml": "^2.8.2",
|
|
85
|
+
"zod": "^4.0.17"
|
|
86
|
+
}
|
|
87
|
+
}
|