synarcx 0.2.0 → 0.2.1

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 (46) hide show
  1. package/README.md +233 -39
  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/tool-detection.d.ts +4 -10
  26. package/dist/core/shared/tool-detection.js +3 -31
  27. package/dist/core/shared/workflow-registry.d.ts +40 -0
  28. package/dist/core/shared/workflow-registry.js +19 -0
  29. package/dist/core/templates/types.d.ts +7 -0
  30. package/dist/core/templates/types.js +9 -1
  31. package/dist/core/templates/workflows/analyze.js +84 -84
  32. package/dist/core/templates/workflows/apply-change.js +291 -291
  33. package/dist/core/templates/workflows/archive-change.js +254 -254
  34. package/dist/core/templates/workflows/clarify.js +91 -91
  35. package/dist/core/templates/workflows/debug.js +100 -100
  36. package/dist/core/templates/workflows/explore.js +462 -462
  37. package/dist/core/templates/workflows/propose.js +199 -199
  38. package/dist/core/templates/workflows/quick.js +112 -112
  39. package/dist/core/templates/workflows/refactor.js +109 -109
  40. package/dist/core/templates/workflows/sync.js +148 -148
  41. package/dist/core/update.d.ts +1 -21
  42. package/dist/core/update.js +18 -117
  43. package/dist/core/view.js +8 -8
  44. package/dist/core/workspace/open-surface.d.ts +2 -2
  45. package/dist/core/workspace/open-surface.js +13 -13
  46. package/package.json +84 -76
package/README.md CHANGED
@@ -1,20 +1,82 @@
1
- # SynArcX — Synapse Architecture Code Extension
1
+ # SynArcX
2
2
 
3
- Spec-driven development workflow for AI coding agents.
3
+ ![npm version](https://img.shields.io/npm/v/synarcx) ![license](https://img.shields.io/npm/l/synarcx) ![node](https://img.shields.io/node/v/synarcx)
4
+
5
+ > Structured engineering workflows for AI coding assistants — persistent project memory, spec-driven development, and architecture drift prevention across every session.
6
+
7
+ Works with **Claude Code, Cursor, GitHub Copilot, Cline, Windsurf, Codex**, and more AI coding tools.
8
+
9
+ ---
10
+
11
+ ## The Problem: Architecture Drift in AI-Assisted Development
12
+
13
+ AI coding assistants like Claude Code and Cursor lose context fast. Requirements live in chat history. Architecture decisions vanish between sessions. Generated code drifts from design intent.
14
+
15
+ Without an explicit workflow, AI-generated code gradually drifts from your architecture — each session introduces small misalignments that compound into structural debt. This is architecture drift, and it gets worse the longer the project runs.
16
+
17
+ SynArcX fixes this by adding a lightweight spec layer between you and your AI — so both you and the assistant agree on what to build before any code is written.
18
+
19
+ ---
20
+
21
+ ## Why Not Just Prompt Better?
22
+
23
+ Better prompts help, but they don't survive session resets, tool switches, or team handoffs. Every new session starts cold. Every new contributor re-explains the same constraints.
24
+
25
+ SynArcX makes your engineering decisions durable. The `constitution.md` is always there. The specs don't live in someone's chat history.
26
+
27
+ ---
28
+
29
+ ## Why Not Just Use Another Spec Format?
30
+
31
+ Spec formats describe *what* to build.
32
+
33
+ SynArcX structures *how you get there* from exploration to proposal to implementation, and helps keep AI-generated changes aligned with the evolving codebase at every stage, not just at planning time.
34
+
35
+ If you already have markdown specs in your repo, `/syn:sync` will incorporate them into the `constitution.md`.
36
+
37
+ ---
38
+
39
+ ## What Happens If You Ignore SynArcX?
40
+
41
+ Nothing breaks immediately. That's the problem.
42
+
43
+ ```
44
+ Week 1 AI reads the codebase, builds the feature correctly.
45
+
46
+ Week 3 New session. AI re-derives context from code alone.
47
+ Small assumptions diverge from your actual design.
48
+
49
+ Week 6 Three sessions in. The auth module now does things
50
+ no spec ever said it should. The AI was "helpful."
51
+
52
+ Week 10 You're untangling AI-introduced architecture violations
53
+ instead of shipping features. The specs live in a chat
54
+ log nobody can find.
55
+ ```
56
+
57
+ SynArcX makes the spec the source of truth, not the chat history.
58
+
59
+ ---
4
60
 
5
61
  ## Install
6
62
 
63
+ Requires **Node.js 20+**
64
+
7
65
  ```bash
8
66
  npm install -g synarcx
9
67
  ```
10
68
 
11
- Or with pnpm:
12
-
13
69
  ```bash
14
70
  pnpm add -g synarcx
15
71
  ```
16
72
 
17
- Run `synarcx --help` to verify.
73
+ Verify:
74
+
75
+ ```bash
76
+ synarcx --help
77
+ ```
78
+
79
+ ---
18
80
 
19
81
  ## Quick Start
20
82
 
@@ -23,66 +85,196 @@ cd your-project
23
85
  synarcx init
24
86
  ```
25
87
 
26
- Then in your AI tool:
88
+ Then in your AI coding tool:
27
89
 
28
- 1. `/syn:sync` — validate README and generate project constitution with guardrail Q&A
29
- 2. `/syn:explore "your idea"` — think through the problem, then follow the suggestion to `/syn:propose`
30
- 3. `/syn:propose my-feature` — create proposal, specs, design, and tasks in one step
31
- 4. `/syn:clarify` — sharpen the artifacts with targeted questions
90
+ 1. `/syn:sync` — scan the project and generate the `constitution.md` (persistent project memory)
91
+ 2. `/syn:explore "your idea"` — think through the problem with your AI
92
+ 3. `/syn:propose "my-feature"` — create proposal, specs, design, and tasks in one step
93
+ 4. `/syn:clarify` — sharpen artifacts with targeted questions
32
94
  5. `/syn:analyze` — cross-artifact consistency check
33
95
  6. `/syn:apply` — implement the tasks
34
96
  7. `/syn:archive` — archive when done
35
97
 
36
- For bugs, use `/syn:debug` instead of `/syn:explore` — same flow, starting from a known error.
37
- For structural improvements, use `/syn:refactor`.
38
- For small, low-risk changes (typos, config tweaks), use `/syn:quick` — no artifacts, inline preview, then apply.
98
+ For specific cases, use these instead of `/syn:explore`:
39
99
 
40
- ## Commands
100
+ * For bugs: use `/syn:debug`.
101
+ * For structural improvements: use `/syn:refactor`.
102
+ * For small low-risk changes (typos, config tweaks): use `/syn:quick` with no artifacts, just apply.
103
+
104
+ ---
105
+
106
+ ## How It Works: Spec-Driven AI Coding Workflow
107
+
108
+ **Without SynArcX, development drift compounds silently:**
109
+
110
+ ```
111
+ Session 1 ──► Session 2 ──► Session 3 ──► Session N
112
+ ✓ correct ~ close ✗ diverged ✗✗ structural debt
113
+ (nobody noticed)
114
+ ```
115
+
116
+ **With SynArcX — alignment is maintained explicitly:**
41
117
 
42
- | Command | Description |
43
- | ----------------- | ------------------------------------------------------------------------------------------------ |
44
- | `/syn:sync` | Validate README, scan project files, run guardrail Q&A, generate constitution |
45
- | `/syn:explore` | Thinking partner — explore ideas, investigate problems, clarify requirements |
46
- | `/syn:debug` | Diagnose a known error (3-phase analysis), then prompts `/syn:propose` explicitly |
47
- | `/syn:refactor` | Investigate structural refactoring — map current vs target shape, then prompts `/syn:propose` |
48
- | `/syn:quick` | Fast-path for small low-risk changes — inline preview, confirm, apply — no artifacts |
49
- | `/syn:propose` | Create a new change with proposal, specs, design, and tasks in one step |
50
- | `/syn:clarify` | Ask up to 5 targeted questions to sharpen artifacts before implementation |
51
- | `/syn:analyze` | Cross-artifact consistency check across proposal, specs, design, and tasks |
52
- | `/syn:apply` | Implement tasks from a change's task list |
53
- | `/syn:archive` | Archive a completed change and sync specs |
118
+ ```
119
+ Session 1 ──► constitution.md ──► Session 2 ──► constitution.md ──► Session N
120
+ correct (updated) ✓ correct (updated) ✓ correct
121
+
122
+ specs · architecture · intent preserved across every reset
123
+ ```
54
124
 
55
- ## How It Works
125
+ ---
56
126
 
57
- AI coding assistants are powerful but lose context fast when requirements live only in chat history. SynArcX adds a lightweight spec layer so you and your AI agree on what to build before any code is written.
127
+ **The workflow:**
58
128
 
59
129
  ```
60
- sync ─────────────────────────────────────────► constitution
130
+ sync ──────────────────────────────────────────────────► constitution
61
131
 
62
132
  explore ──┐
63
133
  debug ──┤
64
134
  ├──► propose ──► clarify ──► analyze ──► apply ──► archive
65
135
  refactor ──┘
66
136
 
67
- quick ────────────────────────────────────────────► apply
137
+ quick ────────────────────────────────────────────────────────► apply
68
138
  ```
69
139
 
70
- Each step suggests the next — you decide when to advance.
140
+ 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.
71
141
 
72
- - `sync` generates/updates the constitution — run once, re-run when the project shifts
142
+ - `sync` generates the `constitution.md` — run once, re-run when the project shifts
73
143
  - `explore`, `debug`, and `refactor` are entry points that hand off to `propose`
74
- - `quick` is a fast-path that skips the pipeline entirely — for small, low-risk changes
144
+ - `quick` skips the pipeline for small, low-risk changes
75
145
 
76
146
  Each change gets its own folder under `synspec/changes/` with:
77
147
 
78
- - `proposal.md` — what and why
79
- - `specs/` — what the system shall do
80
- - `design.md` how to build it
81
- - `tasks.md` implementation checklist
148
+ ```
149
+ synspec/changes/my-feature/
150
+ ├── proposal.md # what and why
151
+ ├── design.md # how to build it
152
+ ├── tasks.md # implementation checklist
153
+ └── specs/
154
+ └── *.md # what the system shall do
155
+ ```
156
+
157
+ ### Persistent Project Memory
158
+
159
+ `constitution.md` is the core of SynArcX. Generated by `/syn:sync`, it preserves architectural intent, conventions, constraints, and engineering decisions across AI sessions — keeping specifications, architecture, and implementation in sync.
160
+
161
+ Unlike documentation, the constitution is optimized for AI operational context — not human reading.
162
+
163
+ A typical `constitution.md` includes:
164
+
165
+ ```markdown
166
+ ## Architecture Principles
167
+ - All API routes are RESTful, no GraphQL
168
+ - Business logic lives in /services, not controllers
169
+
170
+ ## Module Boundaries
171
+ - auth/ owns all token lifecycle — nothing else touches JWTs
172
+
173
+ ## Known Pitfalls
174
+ - DB migrations must be backwards-compatible (rolling deploys)
175
+
176
+ ## Coding Patterns
177
+ - Use Result<T, E> for fallible operations, never throw in services
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Commands
183
+
184
+ Used inside your AI coding tool (Claude Code, Cursor, Cline, etc.):
185
+
186
+ | Command | Description |
187
+ | ----------------- | ------------------------------------------------------------------------ |
188
+ | `/syn:sync` | Scan project, run guardrail Q&A, generate/update `constitution.md` |
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` | Ask up to 5 targeted questions to sharpen artifacts |
195
+ | `/syn:analyze` | Cross-artifact consistency check across all change artifacts |
196
+ | `/syn:apply` | Implement tasks from a change's task list |
197
+ | `/syn:archive` | Archive a completed change and sync specs |
198
+
199
+ ### CLI Commands
200
+
201
+ Used in your terminal:
202
+
203
+ | Command | Description |
204
+ | ------------------- | ---------------------------------------------------- |
205
+ | `synarcx init` | Set up SynArcX workflow structure in your repository |
206
+ | `synarcx sync` | Regenerate `constitution.md` |
207
+ | `synarcx explore` | Open explore session |
208
+ | `synarcx propose` | Create a structured change proposal |
209
+ | `synarcx clarify` | Refine requirements into explicit specifications |
210
+ | `synarcx analyze` | Evaluate architecture impact and tradeoffs |
211
+ | `synarcx apply` | Execute implementation tasks |
212
+ | `synarcx quick` | Fast-path execution for small changes |
82
213
 
83
- ## Supported Tools
214
+ ---
84
215
 
85
- Works with 25+ AI coding assistants: Claude Code, OpenCode, Cursor, Gemini, GitHub Copilot, Cline, Windsurf, Codex, and more. Slash commands are generated per tool on `synarcx init`.
216
+ ## Artifacts
217
+
218
+ Each workflow stage produces explicit, reviewable files:
219
+
220
+ | Artifact | Purpose |
221
+ | ------------------- | -------------------------------------------------- |
222
+ | `proposal.md` | Problem framing : what, why, and scope |
223
+ | `specs/*.md` | Clarified requirements : what the system shall do |
224
+ | `design.md` | Architectural reasoning : how to build it |
225
+ | `tasks.md` | Implementation checklist |
226
+ | `constitution.md` | Persistent project memory for AI agents |
227
+
228
+ Artifacts create a traceable chain from requirements → reasoning → implementation → architecture decisions.
229
+
230
+ ---
231
+
232
+ ## SynArcX vs. Unstructured AI Coding
233
+
234
+ | Capability | AI Coding Without SynArcX | With SynArcX |
235
+ | ------------------------------------- | ------------------------- | ------------------------------------ |
236
+ | Persistent engineering memory | Lost between sessions | Preserved in `constitution.md` |
237
+ | Structured specification flow | Informal, chat-based | Explicit staged workflow |
238
+ | Architecture-aware changes | Inconsistent | Built into every step |
239
+ | Artifact traceability | None | Proposal → spec → design → tasks |
240
+ | Session continuity | Weak | Persistent across tools and sessions |
241
+ | Structured, traceable workflow stages | Rare | Core design |
242
+ | Low-risk fast path | Manual | `/syn:quick` |
243
+
244
+ ---
245
+
246
+ ## Supported AI Coding Tools
247
+
248
+ SynArcX works with Claude Code, Cursor, GitHub Copilot, Cline, Windsurf, Codex, Gemini, OpenCode, and more. Slash commands are generated per tool on `synarcx init` — each tool gets its own command syntax automatically.
249
+
250
+ ---
251
+
252
+ ## Built for AI-Assisted Software Engineering Teams
253
+
254
+ SynArcX is evolving toward an architecture-aware workflow system for long-running AI-assisted software engineering. It is designed for:
255
+
256
+ - long-running projects with evolving requirements
257
+ - architecture-sensitive systems where drift is costly
258
+ - AI-assisted engineering teams
259
+ - spec-driven development practices
260
+ - multi-session and multi-tool workflows
261
+ - controlled implementation pipelines
262
+
263
+ ---
264
+
265
+ ## Status
266
+
267
+ **v0.2.x** — core workflow stable (init, sync, propose, clarify, analyze, apply, archive, quick)
268
+
269
+ Active development roadmap:
270
+
271
+ - stronger repository cognition
272
+ - architecture-aware execution validation
273
+ - workflow guardrails
274
+ - context continuity across tool switches
275
+ - structured, traceable AI engineering pipelines
276
+
277
+ ---
86
278
 
87
279
  ## Development
88
280
 
@@ -97,6 +289,8 @@ pnpm link --global
97
289
  pnpm build
98
290
  ```
99
291
 
292
+ ---
293
+
100
294
  ## License
101
295
 
102
296
  MIT
@@ -3,7 +3,8 @@ import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
4
  import { getGlobalConfigPath, getGlobalConfig, saveGlobalConfig, } from '../core/global-config.js';
5
5
  import { getNestedValue, setNestedValue, deleteNestedValue, coerceValue, formatValueYaml, validateConfigKeyPath, validateConfig, DEFAULT_CONFIG, } from '../core/config-schema.js';
6
- import { CORE_WORKFLOWS, ALL_WORKFLOWS, getProfileWorkflows } from '../core/profiles.js';
6
+ import { getProfileWorkflows } from '../core/profiles.js';
7
+ import { CORE_WORKFLOWS, ALL_WORKFLOWS } from '../core/shared/workflow-registry.js';
7
8
  import { SYNSPEC_DIR_NAME } from '../core/config.js';
8
9
  import { hasProjectConfigDrift } from '../core/profile-sync-drift.js';
9
10
  const WORKFLOW_PROMPT_META = {
@@ -133,8 +134,8 @@ export function diffProfileState(before, after) {
133
134
  };
134
135
  }
135
136
  function maybeWarnConfigDrift(projectDir, state, colorize) {
136
- const openspecDir = path.join(projectDir, SYNSPEC_DIR_NAME);
137
- if (!fs.existsSync(openspecDir)) {
137
+ const synspecDir = path.join(projectDir, SYNSPEC_DIR_NAME);
138
+ if (!fs.existsSync(synspecDir)) {
138
139
  return;
139
140
  }
140
141
  if (!hasProjectConfigDrift(projectDir, state.workflows, state.delivery)) {
@@ -517,10 +518,10 @@ export function registerConfigCommand(program) {
517
518
  config.delivery = nextState.delivery;
518
519
  config.workflows = nextState.workflows;
519
520
  saveGlobalConfig(config);
520
- // Check if inside an OpenSpec project
521
+ // Check if inside a synarcx project
521
522
  const projectDir = process.cwd();
522
- const openspecDir = path.join(projectDir, SYNSPEC_DIR_NAME);
523
- if (fs.existsSync(openspecDir)) {
523
+ const synspecDir = path.join(projectDir, SYNSPEC_DIR_NAME);
524
+ if (fs.existsSync(synspecDir)) {
524
525
  const applyNow = await confirm({
525
526
  message: 'Apply changes to this project now?',
526
527
  default: true,
@@ -6,20 +6,7 @@
6
6
  */
7
7
  import path from 'path';
8
8
  import { transformToHyphenCommands } from '../../../utils/command-references.js';
9
- /**
10
- * Escapes a string value for safe YAML output.
11
- * Quotes the string if it contains special YAML characters.
12
- */
13
- function escapeYamlValue(value) {
14
- // Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
15
- const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
16
- if (needsQuoting) {
17
- // Use double quotes and escape internal double quotes and backslashes
18
- const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
19
- return `"${escaped}"`;
20
- }
21
- return value;
22
- }
9
+ import { escapeYamlValue } from '../yaml-utils.js';
23
10
  /**
24
11
  * Bob Shell adapter for command generation.
25
12
  * File path: .bob/commands/syn-<id>.md
@@ -33,12 +20,12 @@ export const bobAdapter = {
33
20
  formatFile(content) {
34
21
  // Transform command references from colon to hyphen format for Bob
35
22
  const transformedBody = transformToHyphenCommands(content.body);
36
- return `---
37
- description: ${escapeYamlValue(content.description)}
38
- argument-hint: command arguments
39
- ---
40
-
41
- ${transformedBody}
23
+ return `---
24
+ description: ${escapeYamlValue(content.description)}
25
+ argument-hint: command arguments
26
+ ---
27
+
28
+ ${transformedBody}
42
29
  `;
43
30
  },
44
31
  };
@@ -4,27 +4,7 @@
4
4
  * Formats commands for Claude Code following its frontmatter specification.
5
5
  */
6
6
  import path from 'path';
7
- /**
8
- * Escapes a string value for safe YAML output.
9
- * Quotes the string if it contains special YAML characters.
10
- */
11
- function escapeYamlValue(value) {
12
- // Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
13
- const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
14
- if (needsQuoting) {
15
- // Use double quotes and escape internal double quotes and backslashes
16
- const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
17
- return `"${escaped}"`;
18
- }
19
- return value;
20
- }
21
- /**
22
- * Formats a tags array as a YAML array with proper escaping.
23
- */
24
- function formatTagsArray(tags) {
25
- const escapedTags = tags.map((tag) => escapeYamlValue(tag));
26
- return `[${escapedTags.join(', ')}]`;
27
- }
7
+ import { escapeYamlValue, formatTagsArray } from '../yaml-utils.js';
28
8
  /**
29
9
  * Claude Code adapter for command generation.
30
10
  * File path: .claude/commands/syn/<id>.md
@@ -36,14 +16,14 @@ export const claudeAdapter = {
36
16
  return path.join('.claude', 'commands', 'syn', `${commandId}.md`);
37
17
  },
38
18
  formatFile(content) {
39
- return `---
40
- name: ${escapeYamlValue(content.name)}
41
- description: ${escapeYamlValue(content.description)}
42
- category: ${escapeYamlValue(content.category)}
43
- tags: ${formatTagsArray(content.tags)}
44
- ---
45
-
46
- ${content.body}
19
+ return `---
20
+ name: ${escapeYamlValue(content.name)}
21
+ description: ${escapeYamlValue(content.description)}
22
+ category: ${escapeYamlValue(content.category)}
23
+ tags: ${formatTagsArray(content.tags)}
24
+ ---
25
+
26
+ ${content.body}
47
27
  `;
48
28
  },
49
29
  };
@@ -5,20 +5,7 @@
5
5
  * Cursor uses a different frontmatter format and file naming convention.
6
6
  */
7
7
  import path from 'path';
8
- /**
9
- * Escapes a string value for safe YAML output.
10
- * Quotes the string if it contains special YAML characters.
11
- */
12
- function escapeYamlValue(value) {
13
- // Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
14
- const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
15
- if (needsQuoting) {
16
- // Use double quotes and escape internal double quotes and backslashes
17
- const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
18
- return `"${escaped}"`;
19
- }
20
- return value;
21
- }
8
+ import { escapeYamlValue } from '../yaml-utils.js';
22
9
  /**
23
10
  * Cursor adapter for command generation.
24
11
  * File path: .cursor/commands/syn-<id>.md
@@ -30,14 +17,14 @@ export const cursorAdapter = {
30
17
  return path.join('.cursor', 'commands', `syn-${commandId}.md`);
31
18
  },
32
19
  formatFile(content) {
33
- return `---
34
- name: /syn-${content.id}
35
- id: syn-${content.id}
36
- category: ${escapeYamlValue(content.category)}
37
- description: ${escapeYamlValue(content.description)}
38
- ---
39
-
40
- ${content.body}
20
+ return `---
21
+ name: /syn-${content.id}
22
+ id: syn-${content.id}
23
+ category: ${escapeYamlValue(content.category)}
24
+ description: ${escapeYamlValue(content.description)}
25
+ ---
26
+
27
+ ${content.body}
41
28
  `;
42
29
  },
43
30
  };
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import path from 'path';
8
8
  import { transformToHyphenCommands } from '../../../utils/command-references.js';
9
+ import { escapeYamlValue } from '../yaml-utils.js';
9
10
  const PI_INPUT_HEADING = /^\*\*Input\*\*:[^\n]*$/m;
10
11
  function injectPiArgs(body) {
11
12
  if (body.includes('$@') || body.includes('$ARGUMENTS')) {
@@ -13,20 +14,6 @@ function injectPiArgs(body) {
13
14
  }
14
15
  return body.replace(PI_INPUT_HEADING, (heading) => `${heading}\n**Provided arguments**: $@`);
15
16
  }
16
- /**
17
- * Escapes a string value for safe YAML output.
18
- * Quotes the string if it contains special YAML characters.
19
- */
20
- function escapeYamlValue(value) {
21
- // Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
22
- const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
23
- if (needsQuoting) {
24
- // Use double quotes and escape internal double quotes and backslashes
25
- const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
26
- return `"${escaped}"`;
27
- }
28
- return value;
29
- }
30
17
  /**
31
18
  * Pi adapter for prompt template generation.
32
19
  * File path: .pi/prompts/syn-<id>.md
@@ -44,11 +31,11 @@ export const piAdapter = {
44
31
  formatFile(content) {
45
32
  // Transform /syn: references to /syn- and inject $@ for template args
46
33
  const transformedBody = transformToHyphenCommands(content.body);
47
- return `---
48
- description: ${escapeYamlValue(content.description)}
49
- ---
50
-
51
- ${injectPiArgs(transformedBody)}
34
+ return `---
35
+ description: ${escapeYamlValue(content.description)}
36
+ ---
37
+
38
+ ${injectPiArgs(transformedBody)}
52
39
  `;
53
40
  },
54
41
  };
@@ -5,27 +5,7 @@
5
5
  * Windsurf uses a similar format to Claude but may have different conventions.
6
6
  */
7
7
  import path from 'path';
8
- /**
9
- * Escapes a string value for safe YAML output.
10
- * Quotes the string if it contains special YAML characters.
11
- */
12
- function escapeYamlValue(value) {
13
- // Check if value needs quoting (contains special YAML characters or starts/ends with whitespace)
14
- const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
15
- if (needsQuoting) {
16
- // Use double quotes and escape internal double quotes and backslashes
17
- const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
18
- return `"${escaped}"`;
19
- }
20
- return value;
21
- }
22
- /**
23
- * Formats a tags array as a YAML array with proper escaping.
24
- */
25
- function formatTagsArray(tags) {
26
- const escapedTags = tags.map((tag) => escapeYamlValue(tag));
27
- return `[${escapedTags.join(', ')}]`;
28
- }
8
+ import { escapeYamlValue, formatTagsArray } from '../yaml-utils.js';
29
9
  /**
30
10
  * Windsurf adapter for command generation.
31
11
  * File path: .windsurf/workflows/syn-<id>.md
@@ -37,14 +17,14 @@ export const windsurfAdapter = {
37
17
  return path.join('.windsurf', 'workflows', `syn-${commandId}.md`);
38
18
  },
39
19
  formatFile(content) {
40
- return `---
41
- name: ${escapeYamlValue(content.name)}
42
- description: ${escapeYamlValue(content.description)}
43
- category: ${escapeYamlValue(content.category)}
44
- tags: ${formatTagsArray(content.tags)}
45
- ---
46
-
47
- ${content.body}
20
+ return `---
21
+ name: ${escapeYamlValue(content.name)}
22
+ description: ${escapeYamlValue(content.description)}
23
+ category: ${escapeYamlValue(content.category)}
24
+ tags: ${formatTagsArray(content.tags)}
25
+ ---
26
+
27
+ ${content.body}
48
28
  `;
49
29
  },
50
30
  };
@@ -0,0 +1,3 @@
1
+ export declare function escapeYamlValue(value: string): string;
2
+ export declare function formatTagsArray(tags: string[]): string;
3
+ //# sourceMappingURL=yaml-utils.d.ts.map
@@ -0,0 +1,12 @@
1
+ export function escapeYamlValue(value) {
2
+ const needsQuoting = /[:\n\r#{}[\],&*!|>'"%@`]|^\s|\s$/.test(value);
3
+ if (needsQuoting) {
4
+ const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
5
+ return `"${escaped}"`;
6
+ }
7
+ return value;
8
+ }
9
+ export function formatTagsArray(tags) {
10
+ return `[${tags.map(escapeYamlValue).join(', ')}]`;
11
+ }
12
+ //# sourceMappingURL=yaml-utils.js.map
@@ -9,6 +9,7 @@ export declare class BashInstaller {
9
9
  * Markers for .bashrc configuration management
10
10
  */
11
11
  private readonly BASHRC_MARKERS;
12
+ private readonly LEGACY_MARKERS;
12
13
  constructor(homeDir?: string);
13
14
  /**
14
15
  * Check if bash-completion is installed
@@ -12,6 +12,10 @@ export class BashInstaller {
12
12
  * Markers for .bashrc configuration management
13
13
  */
14
14
  BASHRC_MARKERS = {
15
+ start: '# SYNARCX:START',
16
+ end: '# SYNARCX:END',
17
+ };
18
+ LEGACY_MARKERS = {
15
19
  start: '# OPENSPEC:START',
16
20
  end: '# OPENSPEC:END',
17
21
  };
@@ -147,15 +151,22 @@ export class BashInstaller {
147
151
  }
148
152
  // Read file content
149
153
  const content = await fs.readFile(bashrcPath, 'utf-8');
154
+ // Determine which markers to use (check new first, fall back to legacy)
155
+ const startMarker = content.includes(this.BASHRC_MARKERS.start)
156
+ ? this.BASHRC_MARKERS.start
157
+ : this.LEGACY_MARKERS.start;
158
+ const endMarker = content.includes(this.BASHRC_MARKERS.end)
159
+ ? this.BASHRC_MARKERS.end
160
+ : this.LEGACY_MARKERS.end;
150
161
  // Check if markers exist
151
- if (!content.includes(this.BASHRC_MARKERS.start) || !content.includes(this.BASHRC_MARKERS.end)) {
162
+ if (!content.includes(startMarker) || !content.includes(endMarker)) {
152
163
  // Markers don't exist, nothing to remove
153
164
  return true;
154
165
  }
155
166
  // Remove content between markers (including markers)
156
167
  const lines = content.split('\n');
157
- const startIndex = lines.findIndex((line) => line.trim() === this.BASHRC_MARKERS.start);
158
- const endIndex = lines.findIndex((line) => line.trim() === this.BASHRC_MARKERS.end);
168
+ const startIndex = lines.findIndex((line) => line.trim() === startMarker);
169
+ const endIndex = lines.findIndex((line) => line.trim() === endMarker);
159
170
  if (startIndex === -1 || endIndex === -1 || endIndex < startIndex) {
160
171
  // Invalid marker placement
161
172
  return false;