@bradygaster/squad-sdk 0.8.0 → 0.8.2-1.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.
Files changed (240) hide show
  1. package/README.md +296 -296
  2. package/dist/adapter/client.d.ts +25 -4
  3. package/dist/adapter/client.d.ts.map +1 -1
  4. package/dist/adapter/client.js +377 -75
  5. package/dist/adapter/client.js.map +1 -1
  6. package/dist/adapter/errors.js.map +1 -1
  7. package/dist/adapter/types.d.ts +38 -0
  8. package/dist/adapter/types.d.ts.map +1 -1
  9. package/dist/agents/charter-compiler.d.ts +4 -0
  10. package/dist/agents/charter-compiler.d.ts.map +1 -1
  11. package/dist/agents/charter-compiler.js +8 -0
  12. package/dist/agents/charter-compiler.js.map +1 -1
  13. package/dist/agents/history-shadow.js +30 -30
  14. package/dist/agents/history-shadow.js.map +1 -1
  15. package/dist/agents/index.d.ts +6 -3
  16. package/dist/agents/index.d.ts.map +1 -1
  17. package/dist/agents/index.js +132 -23
  18. package/dist/agents/index.js.map +1 -1
  19. package/dist/agents/lifecycle.d.ts.map +1 -1
  20. package/dist/agents/lifecycle.js +28 -2
  21. package/dist/agents/lifecycle.js.map +1 -1
  22. package/dist/agents/model-selector.d.ts.map +1 -1
  23. package/dist/agents/model-selector.js +10 -37
  24. package/dist/agents/model-selector.js.map +1 -1
  25. package/dist/agents/onboarding.js +12 -12
  26. package/dist/agents/onboarding.js.map +1 -1
  27. package/dist/build/ci-pipeline.js.map +1 -1
  28. package/dist/build/github-dist.d.ts +2 -2
  29. package/dist/build/github-dist.js +45 -45
  30. package/dist/build/github-dist.js.map +1 -1
  31. package/dist/build/install-migration.js +2 -2
  32. package/dist/build/install-migration.js.map +1 -1
  33. package/dist/build/release.js.map +1 -1
  34. package/dist/build/versioning.js.map +1 -1
  35. package/dist/builders/index.d.ts +156 -0
  36. package/dist/builders/index.d.ts.map +1 -0
  37. package/dist/builders/index.js +404 -0
  38. package/dist/builders/index.js.map +1 -0
  39. package/dist/builders/types.d.ts +187 -0
  40. package/dist/builders/types.d.ts.map +1 -0
  41. package/dist/builders/types.js +12 -0
  42. package/dist/builders/types.js.map +1 -0
  43. package/dist/casting/index.d.ts.map +1 -1
  44. package/dist/casting/index.js +10 -2
  45. package/dist/casting/index.js.map +1 -1
  46. package/dist/client/event-bus.d.ts.map +1 -1
  47. package/dist/client/event-bus.js +0 -1
  48. package/dist/client/event-bus.js.map +1 -1
  49. package/dist/client/index.d.ts +7 -4
  50. package/dist/client/index.d.ts.map +1 -1
  51. package/dist/client/index.js +22 -10
  52. package/dist/client/index.js.map +1 -1
  53. package/dist/config/agent-doc.js.map +1 -1
  54. package/dist/config/agent-source.js.map +1 -1
  55. package/dist/config/doc-sync.js.map +1 -1
  56. package/dist/config/init.d.ts +38 -13
  57. package/dist/config/init.d.ts.map +1 -1
  58. package/dist/config/init.js +615 -177
  59. package/dist/config/init.js.map +1 -1
  60. package/dist/config/legacy-fallback.js.map +1 -1
  61. package/dist/config/markdown-migration.js.map +1 -1
  62. package/dist/config/migration.js.map +1 -1
  63. package/dist/config/routing.js.map +1 -1
  64. package/dist/coordinator/coordinator.d.ts.map +1 -1
  65. package/dist/coordinator/coordinator.js +73 -55
  66. package/dist/coordinator/coordinator.js.map +1 -1
  67. package/dist/coordinator/fan-out.js.map +1 -1
  68. package/dist/coordinator/index.d.ts +19 -4
  69. package/dist/coordinator/index.d.ts.map +1 -1
  70. package/dist/coordinator/index.js +144 -21
  71. package/dist/coordinator/index.js.map +1 -1
  72. package/dist/index.d.ts +15 -2
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +16 -2
  75. package/dist/index.js.map +1 -1
  76. package/dist/multi-squad.d.ts +89 -0
  77. package/dist/multi-squad.d.ts.map +1 -0
  78. package/dist/multi-squad.js +233 -0
  79. package/dist/multi-squad.js.map +1 -0
  80. package/dist/ralph/index.d.ts +3 -1
  81. package/dist/ralph/index.d.ts.map +1 -1
  82. package/dist/ralph/index.js +83 -16
  83. package/dist/ralph/index.js.map +1 -1
  84. package/dist/ralph/triage.d.ts +48 -0
  85. package/dist/ralph/triage.d.ts.map +1 -0
  86. package/dist/ralph/triage.js +310 -0
  87. package/dist/ralph/triage.js.map +1 -0
  88. package/dist/remote/bridge.d.ts +79 -0
  89. package/dist/remote/bridge.d.ts.map +1 -0
  90. package/dist/remote/bridge.js +583 -0
  91. package/dist/remote/bridge.js.map +1 -0
  92. package/dist/remote/index.d.ts +7 -0
  93. package/dist/remote/index.d.ts.map +1 -0
  94. package/dist/remote/index.js +6 -0
  95. package/dist/remote/index.js.map +1 -0
  96. package/dist/remote/protocol.d.ts +130 -0
  97. package/dist/remote/protocol.d.ts.map +1 -0
  98. package/dist/remote/protocol.js +25 -0
  99. package/dist/remote/protocol.js.map +1 -0
  100. package/dist/remote/types.d.ts +34 -0
  101. package/dist/remote/types.d.ts.map +1 -0
  102. package/dist/remote/types.js +5 -0
  103. package/dist/remote/types.js.map +1 -0
  104. package/dist/resolution.d.ts +72 -0
  105. package/dist/resolution.d.ts.map +1 -1
  106. package/dist/resolution.js +153 -0
  107. package/dist/resolution.js.map +1 -1
  108. package/dist/runtime/benchmarks.js.map +1 -1
  109. package/dist/runtime/config.d.ts +3 -2
  110. package/dist/runtime/config.d.ts.map +1 -1
  111. package/dist/runtime/config.js +7 -6
  112. package/dist/runtime/config.js.map +1 -1
  113. package/dist/runtime/constants.d.ts +37 -0
  114. package/dist/runtime/constants.d.ts.map +1 -0
  115. package/dist/runtime/constants.js +60 -0
  116. package/dist/runtime/constants.js.map +1 -0
  117. package/dist/runtime/event-bus-ws-bridge.d.ts +35 -0
  118. package/dist/runtime/event-bus-ws-bridge.d.ts.map +1 -0
  119. package/dist/runtime/event-bus-ws-bridge.js +55 -0
  120. package/dist/runtime/event-bus-ws-bridge.js.map +1 -0
  121. package/dist/runtime/event-payloads.d.ts +108 -0
  122. package/dist/runtime/event-payloads.d.ts.map +1 -0
  123. package/dist/runtime/event-payloads.js +28 -0
  124. package/dist/runtime/event-payloads.js.map +1 -0
  125. package/dist/runtime/health.d.ts.map +1 -1
  126. package/dist/runtime/health.js +2 -1
  127. package/dist/runtime/health.js.map +1 -1
  128. package/dist/runtime/otel-api.d.ts +38 -0
  129. package/dist/runtime/otel-api.d.ts.map +1 -0
  130. package/dist/runtime/otel-api.js +94 -0
  131. package/dist/runtime/otel-api.js.map +1 -0
  132. package/dist/runtime/otel-bridge.d.ts +52 -0
  133. package/dist/runtime/otel-bridge.d.ts.map +1 -0
  134. package/dist/runtime/otel-bridge.js +132 -0
  135. package/dist/runtime/otel-bridge.js.map +1 -0
  136. package/dist/runtime/otel-init.d.ts +84 -0
  137. package/dist/runtime/otel-init.d.ts.map +1 -0
  138. package/dist/runtime/otel-init.js +86 -0
  139. package/dist/runtime/otel-init.js.map +1 -0
  140. package/dist/runtime/otel-metrics.d.ts +42 -0
  141. package/dist/runtime/otel-metrics.d.ts.map +1 -0
  142. package/dist/runtime/otel-metrics.js +196 -0
  143. package/dist/runtime/otel-metrics.js.map +1 -0
  144. package/dist/runtime/otel.d.ts +55 -0
  145. package/dist/runtime/otel.d.ts.map +1 -0
  146. package/dist/runtime/otel.js +165 -0
  147. package/dist/runtime/otel.js.map +1 -0
  148. package/dist/runtime/squad-observer.d.ts +75 -0
  149. package/dist/runtime/squad-observer.d.ts.map +1 -0
  150. package/dist/runtime/squad-observer.js +190 -0
  151. package/dist/runtime/squad-observer.js.map +1 -0
  152. package/dist/runtime/streaming.d.ts +9 -0
  153. package/dist/runtime/streaming.d.ts.map +1 -1
  154. package/dist/runtime/streaming.js +37 -1
  155. package/dist/runtime/streaming.js.map +1 -1
  156. package/dist/sharing/conflicts.js +7 -5
  157. package/dist/sharing/conflicts.js.map +1 -1
  158. package/dist/sharing/consult.d.ts +226 -0
  159. package/dist/sharing/consult.d.ts.map +1 -0
  160. package/dist/sharing/consult.js +818 -0
  161. package/dist/sharing/consult.js.map +1 -0
  162. package/dist/sharing/export.js.map +1 -1
  163. package/dist/sharing/import.js.map +1 -1
  164. package/dist/sharing/index.d.ts +2 -1
  165. package/dist/sharing/index.d.ts.map +1 -1
  166. package/dist/sharing/index.js +2 -1
  167. package/dist/sharing/index.js.map +1 -1
  168. package/dist/skills/skill-loader.js.map +1 -1
  169. package/dist/streams/filter.d.ts +33 -0
  170. package/dist/streams/filter.d.ts.map +1 -0
  171. package/dist/streams/filter.js +29 -0
  172. package/dist/streams/filter.js.map +1 -0
  173. package/dist/streams/index.d.ts +9 -0
  174. package/dist/streams/index.d.ts.map +1 -0
  175. package/dist/streams/index.js +9 -0
  176. package/dist/streams/index.js.map +1 -0
  177. package/dist/streams/resolver.d.ts +40 -0
  178. package/dist/streams/resolver.d.ts.map +1 -0
  179. package/dist/streams/resolver.js +162 -0
  180. package/dist/streams/resolver.js.map +1 -0
  181. package/dist/streams/types.d.ts +44 -0
  182. package/dist/streams/types.d.ts.map +1 -0
  183. package/dist/streams/types.js +10 -0
  184. package/dist/streams/types.js.map +1 -0
  185. package/dist/tools/index.d.ts +8 -0
  186. package/dist/tools/index.d.ts.map +1 -1
  187. package/dist/tools/index.js +57 -1
  188. package/dist/tools/index.js.map +1 -1
  189. package/dist/types.d.ts +23 -0
  190. package/dist/types.d.ts.map +1 -1
  191. package/dist/upstream/index.d.ts +8 -0
  192. package/dist/upstream/index.d.ts.map +1 -0
  193. package/dist/upstream/index.js +7 -0
  194. package/dist/upstream/index.js.map +1 -0
  195. package/dist/upstream/resolver.d.ts +37 -0
  196. package/dist/upstream/resolver.d.ts.map +1 -0
  197. package/dist/upstream/resolver.js +234 -0
  198. package/dist/upstream/resolver.js.map +1 -0
  199. package/dist/upstream/types.d.ts +55 -0
  200. package/dist/upstream/types.d.ts.map +1 -0
  201. package/dist/upstream/types.js +11 -0
  202. package/dist/upstream/types.js.map +1 -0
  203. package/dist/utils/safe-timestamp.d.ts +6 -0
  204. package/dist/utils/safe-timestamp.d.ts.map +1 -0
  205. package/dist/utils/safe-timestamp.js +8 -0
  206. package/dist/utils/safe-timestamp.js.map +1 -0
  207. package/package.json +208 -115
  208. package/templates/casting-history.json +4 -0
  209. package/templates/casting-policy.json +35 -0
  210. package/templates/casting-registry.json +3 -0
  211. package/templates/ceremonies.md +41 -0
  212. package/templates/charter.md +53 -0
  213. package/templates/constraint-tracking.md +38 -0
  214. package/templates/copilot-instructions.md +46 -0
  215. package/templates/history.md +10 -0
  216. package/templates/identity/now.md +9 -0
  217. package/templates/identity/wisdom.md +15 -0
  218. package/templates/mcp-config.md +98 -0
  219. package/templates/multi-agent-format.md +28 -0
  220. package/templates/orchestration-log.md +27 -0
  221. package/templates/plugin-marketplace.md +49 -0
  222. package/templates/raw-agent-output.md +37 -0
  223. package/templates/roster.md +60 -0
  224. package/templates/routing.md +54 -0
  225. package/templates/run-output.md +50 -0
  226. package/templates/scribe-charter.md +119 -0
  227. package/templates/skill.md +24 -0
  228. package/templates/skills/project-conventions/SKILL.md +56 -0
  229. package/templates/squad.agent.md +1146 -0
  230. package/templates/workflows/squad-ci.yml +24 -0
  231. package/templates/workflows/squad-docs.yml +50 -0
  232. package/templates/workflows/squad-heartbeat.yml +316 -0
  233. package/templates/workflows/squad-insider-release.yml +61 -0
  234. package/templates/workflows/squad-issue-assign.yml +161 -0
  235. package/templates/workflows/squad-label-enforce.yml +181 -0
  236. package/templates/workflows/squad-preview.yml +55 -0
  237. package/templates/workflows/squad-promote.yml +120 -0
  238. package/templates/workflows/squad-release.yml +77 -0
  239. package/templates/workflows/squad-triage.yml +260 -0
  240. package/templates/workflows/sync-squad-labels.yml +169 -0
@@ -3,12 +3,54 @@
3
3
  *
4
4
  * Creates new Squad projects with typed configuration.
5
5
  * Generates squad.config.ts or squad.config.json with agent definitions.
6
+ * Scaffolds directory structure, templates, workflows, and agent files.
6
7
  *
7
8
  * @module config/init
8
9
  */
9
- import { mkdir, writeFile } from 'fs/promises';
10
- import { join } from 'path';
11
- import { existsSync } from 'fs';
10
+ import { mkdir, writeFile, appendFile, unlink } from 'fs/promises';
11
+ import { join, dirname } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+ import { existsSync, cpSync, statSync, mkdirSync, readFileSync, readdirSync } from 'fs';
14
+ import { MODELS } from '../runtime/constants.js';
15
+ // ============================================================================
16
+ // Template Resolution
17
+ // ============================================================================
18
+ /**
19
+ * Get the SDK templates directory path.
20
+ */
21
+ export function getSDKTemplatesDir() {
22
+ // Use fileURLToPath for cross-platform compatibility (handles Windows drive letters, URL encoding)
23
+ const currentDir = dirname(fileURLToPath(import.meta.url));
24
+ // Try relative to this file (in dist/)
25
+ const distPath = join(currentDir, '../../templates');
26
+ if (existsSync(distPath)) {
27
+ return distPath;
28
+ }
29
+ // Try relative to package root (for dev)
30
+ const pkgPath = join(currentDir, '../../../templates');
31
+ if (existsSync(pkgPath)) {
32
+ return pkgPath;
33
+ }
34
+ return null;
35
+ }
36
+ /**
37
+ * Copy a directory recursively.
38
+ */
39
+ function copyRecursiveSync(src, dest) {
40
+ if (!existsSync(dest)) {
41
+ mkdirSync(dest, { recursive: true });
42
+ }
43
+ for (const entry of statSync(src).isDirectory() ? readdirSync(src) : []) {
44
+ const srcPath = join(src, entry);
45
+ const destPath = join(dest, entry);
46
+ if (statSync(srcPath).isDirectory()) {
47
+ copyRecursiveSync(srcPath, destPath);
48
+ }
49
+ else {
50
+ cpSync(srcPath, destPath);
51
+ }
52
+ }
53
+ }
12
54
  // ============================================================================
13
55
  // Default Agent Templates
14
56
  // ============================================================================
@@ -40,87 +82,93 @@ const AGENT_TEMPLATES = {
40
82
  // ============================================================================
41
83
  // Configuration Templates
42
84
  // ============================================================================
85
+ /**
86
+ * Format a readonly string array as a single-quoted TypeScript array literal.
87
+ */
88
+ function formatModelArray(chain) {
89
+ return `[${chain.map(m => `'${m}'`).join(', ')}]`;
90
+ }
43
91
  /**
44
92
  * Generate TypeScript config file content.
45
93
  */
46
94
  function generateTypeScriptConfig(options) {
47
95
  const { projectName, projectDescription, agents } = options;
48
- return `import type { SquadConfig } from '@bradygaster/squad';
49
-
50
- /**
51
- * Squad Configuration for ${projectName}
52
- * ${projectDescription ? `\n * ${projectDescription}` : ''}
53
- */
54
- const config: SquadConfig = {
55
- version: '1.0.0',
56
-
57
- models: {
58
- defaultModel: 'claude-sonnet-4.5',
59
- defaultTier: 'standard',
60
- fallbackChains: {
61
- premium: ['claude-opus-4.6', 'claude-opus-4.5', 'gpt-5.2'],
62
- standard: ['claude-sonnet-4.5', 'gpt-5.1-codex', 'claude-sonnet-4'],
63
- fast: ['claude-haiku-4.5', 'gpt-5-mini', 'gpt-4.1']
64
- },
65
- preferSameProvider: true,
66
- respectTierCeiling: true,
67
- nuclearFallback: {
68
- enabled: false,
69
- model: 'claude-haiku-4.5',
70
- maxRetriesBeforeNuclear: 3
71
- }
72
- },
73
-
74
- routing: {
75
- rules: [
76
- {
77
- workType: 'feature-dev',
78
- agents: ['@${agents[0]?.name || 'coordinator'}'],
79
- confidence: 'high'
80
- },
81
- {
82
- workType: 'bug-fix',
83
- agents: ['@${agents.find(a => a.role === 'developer')?.name || agents[0]?.name || 'coordinator'}'],
84
- confidence: 'high'
85
- },
86
- {
87
- workType: 'testing',
88
- agents: ['@${agents.find(a => a.role === 'tester')?.name || agents[0]?.name || 'coordinator'}'],
89
- confidence: 'high'
90
- },
91
- {
92
- workType: 'documentation',
93
- agents: ['@${agents.find(a => a.role === 'scribe')?.name || agents[0]?.name || 'coordinator'}'],
94
- confidence: 'high'
95
- }
96
- ],
97
- governance: {
98
- eagerByDefault: true,
99
- scribeAutoRuns: false,
100
- allowRecursiveSpawn: false
101
- }
102
- },
103
-
104
- casting: {
105
- allowlistUniverses: [
106
- 'The Usual Suspects',
107
- 'Breaking Bad',
108
- 'The Wire',
109
- 'Firefly'
110
- ],
111
- overflowStrategy: 'generic',
112
- universeCapacity: {}
113
- },
114
-
115
- platforms: {
116
- vscode: {
117
- disableModelSelection: false,
118
- scribeMode: 'sync'
119
- }
120
- }
121
- };
122
-
123
- export default config;
96
+ return `import type { SquadConfig } from '@bradygaster/squad';
97
+
98
+ /**
99
+ * Squad Configuration for ${projectName}
100
+ * ${projectDescription ? `\n * ${projectDescription}` : ''}
101
+ */
102
+ const config: SquadConfig = {
103
+ version: '1.0.0',
104
+
105
+ models: {
106
+ defaultModel: '${MODELS.DEFAULT}',
107
+ defaultTier: 'standard',
108
+ fallbackChains: {
109
+ premium: ${formatModelArray(MODELS.FALLBACK_CHAINS.premium)},
110
+ standard: ${formatModelArray(MODELS.FALLBACK_CHAINS.standard)},
111
+ fast: ${formatModelArray(MODELS.FALLBACK_CHAINS.fast)}
112
+ },
113
+ preferSameProvider: true,
114
+ respectTierCeiling: true,
115
+ nuclearFallback: {
116
+ enabled: false,
117
+ model: '${MODELS.NUCLEAR_FALLBACK}',
118
+ maxRetriesBeforeNuclear: ${MODELS.NUCLEAR_MAX_RETRIES}
119
+ }
120
+ },
121
+
122
+ routing: {
123
+ rules: [
124
+ {
125
+ workType: 'feature-dev',
126
+ agents: ['@${agents[0]?.name || 'coordinator'}'],
127
+ confidence: 'high'
128
+ },
129
+ {
130
+ workType: 'bug-fix',
131
+ agents: ['@${agents.find(a => a.role === 'developer')?.name || agents[0]?.name || 'coordinator'}'],
132
+ confidence: 'high'
133
+ },
134
+ {
135
+ workType: 'testing',
136
+ agents: ['@${agents.find(a => a.role === 'tester')?.name || agents[0]?.name || 'coordinator'}'],
137
+ confidence: 'high'
138
+ },
139
+ {
140
+ workType: 'documentation',
141
+ agents: ['@${agents.find(a => a.role === 'scribe')?.name || agents[0]?.name || 'coordinator'}'],
142
+ confidence: 'high'
143
+ }
144
+ ],
145
+ governance: {
146
+ eagerByDefault: true,
147
+ scribeAutoRuns: false,
148
+ allowRecursiveSpawn: false
149
+ }
150
+ },
151
+
152
+ casting: {
153
+ allowlistUniverses: [
154
+ 'The Usual Suspects',
155
+ 'Breaking Bad',
156
+ 'The Wire',
157
+ 'Firefly'
158
+ ],
159
+ overflowStrategy: 'generic',
160
+ universeCapacity: {}
161
+ },
162
+
163
+ platforms: {
164
+ vscode: {
165
+ disableModelSelection: false,
166
+ scribeMode: 'sync'
167
+ }
168
+ }
169
+ };
170
+
171
+ export default config;
124
172
  `;
125
173
  }
126
174
  /**
@@ -131,19 +179,19 @@ function generateJsonConfig(options) {
131
179
  const config = {
132
180
  version: '1.0.0',
133
181
  models: {
134
- defaultModel: 'claude-sonnet-4.5',
182
+ defaultModel: MODELS.DEFAULT,
135
183
  defaultTier: 'standard',
136
184
  fallbackChains: {
137
- premium: ['claude-opus-4.6', 'claude-opus-4.5', 'gpt-5.2'],
138
- standard: ['claude-sonnet-4.5', 'gpt-5.1-codex', 'claude-sonnet-4'],
139
- fast: ['claude-haiku-4.5', 'gpt-5-mini', 'gpt-4.1']
185
+ premium: [...MODELS.FALLBACK_CHAINS.premium],
186
+ standard: [...MODELS.FALLBACK_CHAINS.standard],
187
+ fast: [...MODELS.FALLBACK_CHAINS.fast]
140
188
  },
141
189
  preferSameProvider: true,
142
190
  respectTierCeiling: true,
143
191
  nuclearFallback: {
144
192
  enabled: false,
145
- model: 'claude-haiku-4.5',
146
- maxRetriesBeforeNuclear: 3
193
+ model: MODELS.NUCLEAR_FALLBACK,
194
+ maxRetriesBeforeNuclear: MODELS.NUCLEAR_MAX_RETRIES
147
195
  }
148
196
  },
149
197
  routing: {
@@ -194,6 +242,42 @@ function generateJsonConfig(options) {
194
242
  };
195
243
  return JSON.stringify(config, null, 2);
196
244
  }
245
+ /**
246
+ * Generate SDK builder config file content (new defineSquad() format).
247
+ */
248
+ function generateSDKBuilderConfig(options) {
249
+ const { projectName, projectDescription, agents } = options;
250
+ // Generate imports
251
+ let code = `import {\n defineSquad,\n defineTeam,\n defineAgent,\n} from '@bradygaster/squad-sdk';\n\n`;
252
+ code += `/**\n * Squad Configuration — ${projectName}\n`;
253
+ if (projectDescription) {
254
+ code += ` *\n * ${projectDescription}\n`;
255
+ }
256
+ code += ` */\n`;
257
+ // Generate agent definitions
258
+ for (const agent of agents) {
259
+ const displayName = agent.displayName || titleCase(agent.name);
260
+ code += `const ${agent.name} = defineAgent({\n`;
261
+ code += ` name: '${agent.name}',\n`;
262
+ code += ` role: '${agent.role}',\n`;
263
+ code += ` description: '${displayName}',\n`;
264
+ code += ` status: 'active',\n`;
265
+ code += `});\n\n`;
266
+ }
267
+ // Generate squad config
268
+ code += `export default defineSquad({\n`;
269
+ code += ` version: '1.0.0',\n\n`;
270
+ code += ` team: defineTeam({\n`;
271
+ code += ` name: '${projectName}',\n`;
272
+ if (projectDescription) {
273
+ code += ` description: '${projectDescription.replace(/'/g, "\\'")}',\n`;
274
+ }
275
+ code += ` members: [${agents.map(a => `'${a.name}'`).join(', ')}],\n`;
276
+ code += ` }),\n\n`;
277
+ code += ` agents: [${agents.map(a => a.name).join(', ')}],\n`;
278
+ code += `});\n`;
279
+ return code;
280
+ }
197
281
  // ============================================================================
198
282
  // Agent Template Generation
199
283
  // ============================================================================
@@ -204,26 +288,26 @@ function generateCharter(agent, projectName, projectDescription) {
204
288
  const template = AGENT_TEMPLATES[agent.role];
205
289
  const displayName = agent.displayName || template?.displayName || titleCase(agent.name);
206
290
  const description = template?.description || 'Team member focused on their assigned responsibilities.';
207
- return `# ${displayName} — ${titleCase(agent.role)}
208
-
209
- ${description}
210
-
211
- ## Project Context
212
-
213
- **Project:** ${projectName}
214
- ${projectDescription ? `**Description:** ${projectDescription}\n` : ''}
215
-
216
- ## Responsibilities
217
-
218
- - Collaborate with team members on assigned work
219
- - Maintain code quality and project standards
220
- - Document decisions and progress in history
221
-
222
- ## Work Style
223
-
224
- - Read project context and team decisions before starting work
225
- - Communicate clearly with team members
226
- - Follow established patterns and conventions
291
+ return `# ${displayName} — ${titleCase(agent.role)}
292
+
293
+ ${description}
294
+
295
+ ## Project Context
296
+
297
+ **Project:** ${projectName}
298
+ ${projectDescription ? `**Description:** ${projectDescription}\n` : ''}
299
+
300
+ ## Responsibilities
301
+
302
+ - Collaborate with team members on assigned work
303
+ - Maintain code quality and project standards
304
+ - Document decisions and progress in history
305
+
306
+ ## Work Style
307
+
308
+ - Read project context and team decisions before starting work
309
+ - Communicate clearly with team members
310
+ - Follow established patterns and conventions
227
311
  `;
228
312
  }
229
313
  /**
@@ -232,22 +316,22 @@ ${projectDescription ? `**Description:** ${projectDescription}\n` : ''}
232
316
  function generateInitialHistory(agent, projectName, projectDescription, userName) {
233
317
  const displayName = agent.displayName || AGENT_TEMPLATES[agent.role]?.displayName || titleCase(agent.name);
234
318
  const now = new Date().toISOString().split('T')[0];
235
- return `# Project Context
236
-
237
- ${userName ? `- **Owner:** ${userName}\n` : ''}- **Project:** ${projectName}
238
- ${projectDescription ? `- **Description:** ${projectDescription}\n` : ''}- **Created:** ${now}
239
-
240
- ## Core Context
241
-
242
- Agent ${displayName} initialized and ready for work.
243
-
244
- ## Recent Updates
245
-
246
- 📌 Team initialized on ${now}
247
-
248
- ## Learnings
249
-
250
- Initial setup complete.
319
+ return `# Project Context
320
+
321
+ ${userName ? `- **Owner:** ${userName}\n` : ''}- **Project:** ${projectName}
322
+ ${projectDescription ? `- **Description:** ${projectDescription}\n` : ''}- **Created:** ${now}
323
+
324
+ ## Core Context
325
+
326
+ Agent ${displayName} initialized and ready for work.
327
+
328
+ ## Recent Updates
329
+
330
+ 📌 Team initialized on ${now}
331
+
332
+ ## Learnings
333
+
334
+ Initial setup complete.
251
335
  `;
252
336
  }
253
337
  /**
@@ -262,21 +346,45 @@ function titleCase(str) {
262
346
  // ============================================================================
263
347
  // Initialization Functions
264
348
  // ============================================================================
349
+ /**
350
+ * Stamp version into squad.agent.md content.
351
+ */
352
+ function stampVersionInContent(content, version) {
353
+ return content.replace(/<!-- version: [^>]* -->/, `<!-- version: ${version} -->`);
354
+ }
265
355
  /**
266
356
  * Initialize a new Squad project.
267
357
  *
268
358
  * Creates:
269
- * - .squad/ directory structure
359
+ * - .squad/ directory structure (agents, casting, decisions, skills, identity, etc.)
270
360
  * - squad.config.ts or squad.config.json
271
361
  * - Agent directories with charter.md and history.md
272
362
  * - .gitattributes for merge drivers
363
+ * - .gitignore entries for logs
364
+ * - .github/agents/squad.agent.md
365
+ * - .github/workflows/ (optional)
366
+ * - .squad/templates/ (optional)
367
+ * - .copilot/mcp-config.json (optional)
368
+ * - Identity files (now.md, wisdom.md)
369
+ * - ceremonies.md
273
370
  *
274
371
  * @param options - Initialization options
275
372
  * @returns Result with created file paths
276
373
  */
374
+ /**
375
+ * Workflow files that are part of the Squad framework and should always be installed.
376
+ * Other workflows in templates/workflows/ are generic CI/CD scaffolding and are opt-in.
377
+ */
378
+ const FRAMEWORK_WORKFLOWS = [
379
+ 'squad-heartbeat.yml',
380
+ 'squad-issue-assign.yml',
381
+ 'squad-triage.yml',
382
+ 'sync-squad-labels.yml',
383
+ ];
277
384
  export async function initSquad(options) {
278
- const { teamRoot, projectName, projectDescription, agents, configFormat = 'typescript', userName } = options;
385
+ const { teamRoot, projectName, projectDescription, agents, configFormat = 'typescript', userName, skipExisting = true, includeWorkflows = true, includeTemplates = true, includeMcpConfig = true, projectType = 'unknown', version = '0.0.0', } = options;
279
386
  const createdFiles = [];
387
+ const skippedFiles = [];
280
388
  const agentDirs = [];
281
389
  // Validate inputs
282
390
  if (!teamRoot) {
@@ -288,40 +396,100 @@ export async function initSquad(options) {
288
396
  if (!agents || agents.length === 0) {
289
397
  throw new Error('At least one agent is required');
290
398
  }
291
- // Create .squad directory
399
+ // Get templates directory
400
+ const templatesDir = getSDKTemplatesDir();
401
+ // Helper to convert absolute path to relative
402
+ const toRelativePath = (absolutePath) => {
403
+ // Use path separator-agnostic approach
404
+ if (absolutePath.startsWith(teamRoot)) {
405
+ const relative = absolutePath.slice(teamRoot.length);
406
+ // Remove leading separator if present
407
+ return relative.startsWith('/') || relative.startsWith('\\')
408
+ ? relative.slice(1)
409
+ : relative;
410
+ }
411
+ return absolutePath;
412
+ };
413
+ // Helper to write file (respects skipExisting)
414
+ const writeIfNotExists = async (filePath, content) => {
415
+ if (existsSync(filePath) && skipExisting) {
416
+ skippedFiles.push(toRelativePath(filePath));
417
+ return false;
418
+ }
419
+ await mkdir(dirname(filePath), { recursive: true });
420
+ await writeFile(filePath, content, 'utf-8');
421
+ createdFiles.push(toRelativePath(filePath));
422
+ return true;
423
+ };
424
+ // Helper to copy file (respects skipExisting)
425
+ const copyIfNotExists = async (src, dest) => {
426
+ if (existsSync(dest) && skipExisting) {
427
+ skippedFiles.push(toRelativePath(dest));
428
+ return false;
429
+ }
430
+ await mkdir(dirname(dest), { recursive: true });
431
+ cpSync(src, dest);
432
+ createdFiles.push(toRelativePath(dest));
433
+ return true;
434
+ };
435
+ // -------------------------------------------------------------------------
436
+ // Create .squad/ directory structure
437
+ // -------------------------------------------------------------------------
292
438
  const squadDir = join(teamRoot, '.squad');
293
- if (!existsSync(squadDir)) {
294
- await mkdir(squadDir, { recursive: true });
439
+ const directories = [
440
+ join(squadDir, 'agents'),
441
+ join(squadDir, 'casting'),
442
+ join(squadDir, 'decisions'),
443
+ join(squadDir, 'decisions', 'inbox'),
444
+ join(squadDir, 'skills'),
445
+ join(squadDir, 'plugins'),
446
+ join(squadDir, 'identity'),
447
+ join(squadDir, 'orchestration-log'),
448
+ join(squadDir, 'log'),
449
+ ];
450
+ for (const dir of directories) {
451
+ if (!existsSync(dir)) {
452
+ await mkdir(dir, { recursive: true });
453
+ }
295
454
  }
296
- // Create agents directory
297
- const agentsDir = join(squadDir, 'agents');
298
- if (!existsSync(agentsDir)) {
299
- await mkdir(agentsDir, { recursive: true });
455
+ // -------------------------------------------------------------------------
456
+ // Create .squad/config.json for squad settings
457
+ // -------------------------------------------------------------------------
458
+ const squadConfigPath = join(squadDir, 'config.json');
459
+ if (!existsSync(squadConfigPath)) {
460
+ const squadConfig = {
461
+ version: 1,
462
+ teamRoot: teamRoot,
463
+ };
464
+ // Only include extractionDisabled if explicitly set
465
+ if (options.extractionDisabled) {
466
+ squadConfig.extractionDisabled = true;
467
+ }
468
+ await writeFile(squadConfigPath, JSON.stringify(squadConfig, null, 2), 'utf-8');
469
+ createdFiles.push(toRelativePath(squadConfigPath));
300
470
  }
301
- // Create casting directory (for future casting system)
302
- const castingDir = join(squadDir, 'casting');
303
- if (!existsSync(castingDir)) {
304
- await mkdir(castingDir, { recursive: true });
471
+ // -------------------------------------------------------------------------
472
+ // Create configuration file
473
+ // -------------------------------------------------------------------------
474
+ // When configFormat is 'markdown', skip config file generation entirely
475
+ let configPath;
476
+ if (configFormat !== 'markdown') {
477
+ const configFileName = configFormat === 'sdk' ? 'squad.config.ts' :
478
+ configFormat === 'typescript' ? 'squad.config.ts' : 'squad.config.json';
479
+ configPath = join(teamRoot, configFileName);
480
+ const configContent = configFormat === 'sdk' ? generateSDKBuilderConfig(options) :
481
+ configFormat === 'typescript' ? generateTypeScriptConfig(options) :
482
+ generateJsonConfig(options);
483
+ await writeIfNotExists(configPath, configContent);
305
484
  }
306
- // Create decisions directory
307
- const decisionsDir = join(squadDir, 'decisions');
308
- if (!existsSync(decisionsDir)) {
309
- await mkdir(decisionsDir, { recursive: true });
485
+ else {
486
+ // No config file for markdown-only mode
487
+ configPath = '';
310
488
  }
311
- // Create skills directory
312
- const skillsDir = join(squadDir, 'skills');
313
- if (!existsSync(skillsDir)) {
314
- await mkdir(skillsDir, { recursive: true });
315
- }
316
- // Generate configuration file
317
- const configFileName = configFormat === 'typescript' ? 'squad.config.ts' : 'squad.config.json';
318
- const configPath = join(teamRoot, configFileName);
319
- const configContent = configFormat === 'typescript'
320
- ? generateTypeScriptConfig(options)
321
- : generateJsonConfig(options);
322
- await writeFile(configPath, configContent, 'utf-8');
323
- createdFiles.push(configPath);
489
+ // -------------------------------------------------------------------------
324
490
  // Create agent directories and files
491
+ // -------------------------------------------------------------------------
492
+ const agentsDir = join(squadDir, 'agents');
325
493
  for (const agent of agents) {
326
494
  const agentDir = join(agentsDir, agent.name);
327
495
  await mkdir(agentDir, { recursive: true });
@@ -329,41 +497,311 @@ export async function initSquad(options) {
329
497
  // Create charter.md
330
498
  const charterPath = join(agentDir, 'charter.md');
331
499
  const charterContent = generateCharter(agent, projectName, projectDescription);
332
- await writeFile(charterPath, charterContent, 'utf-8');
333
- createdFiles.push(charterPath);
500
+ await writeIfNotExists(charterPath, charterContent);
334
501
  // Create history.md
335
502
  const historyPath = join(agentDir, 'history.md');
336
503
  const historyContent = generateInitialHistory(agent, projectName, projectDescription, userName);
337
- await writeFile(historyPath, historyContent, 'utf-8');
338
- createdFiles.push(historyPath);
504
+ await writeIfNotExists(historyPath, historyContent);
339
505
  }
340
- // Create .gitattributes for merge drivers
341
- const gitattributesPath = join(teamRoot, '.gitattributes');
342
- const gitattributesContent = `.squad/agents/*/history.md merge=union
343
- .squad/decisions/*.md merge=union
506
+ // -------------------------------------------------------------------------
507
+ // Create identity files (now.md, wisdom.md)
508
+ // -------------------------------------------------------------------------
509
+ const identityDir = join(squadDir, 'identity');
510
+ const nowMdPath = join(identityDir, 'now.md');
511
+ const wisdomMdPath = join(identityDir, 'wisdom.md');
512
+ const nowContent = `---
513
+ updated_at: ${new Date().toISOString()}
514
+ focus_area: Initial setup
515
+ active_issues: []
516
+ ---
517
+
518
+ # What We're Focused On
519
+
520
+ Getting started. Updated by coordinator at session start.
521
+ `;
522
+ const wisdomContent = `---
523
+ last_updated: ${new Date().toISOString()}
524
+ ---
525
+
526
+ # Team Wisdom
527
+
528
+ Reusable patterns and heuristics learned through work. NOT transcripts — each entry is a distilled, actionable insight.
529
+
530
+ ## Patterns
531
+
532
+ <!-- Append entries below. Format: **Pattern:** description. **Context:** when it applies. -->
533
+ `;
534
+ await writeIfNotExists(nowMdPath, nowContent);
535
+ await writeIfNotExists(wisdomMdPath, wisdomContent);
536
+ // -------------------------------------------------------------------------
537
+ // Create ceremonies.md
538
+ // -------------------------------------------------------------------------
539
+ const ceremoniesDest = join(squadDir, 'ceremonies.md');
540
+ if (templatesDir && existsSync(join(templatesDir, 'ceremonies.md'))) {
541
+ await copyIfNotExists(join(templatesDir, 'ceremonies.md'), ceremoniesDest);
542
+ }
543
+ // -------------------------------------------------------------------------
544
+ // Create decisions.md (canonical location at squad root)
545
+ // -------------------------------------------------------------------------
546
+ const decisionsPath = join(squadDir, 'decisions.md');
547
+ const decisionsContent = `# Squad Decisions
548
+
549
+ ## Active Decisions
550
+
551
+ No decisions recorded yet.
552
+
553
+ ## Governance
554
+
555
+ - All meaningful changes require team consensus
556
+ - Document architectural decisions here
557
+ - Keep history focused on work, decisions focused on direction
558
+ `;
559
+ await writeIfNotExists(decisionsPath, decisionsContent);
560
+ // -------------------------------------------------------------------------
561
+ // Create team.md (required by shell lifecycle)
562
+ // -------------------------------------------------------------------------
563
+ const teamPath = join(squadDir, 'team.md');
564
+ const teamContent = `# Squad Team
565
+
566
+ > ${projectDescription || projectName}
567
+
568
+ ## Coordinator
569
+
570
+ | Name | Role | Notes |
571
+ |------|------|-------|
572
+ | Squad | Coordinator | Routes work, enforces handoffs and reviewer gates. |
573
+
574
+ ## Members
575
+
576
+ | Name | Role | Charter | Status |
577
+ |------|------|---------|--------|
578
+
579
+ ## Project Context
580
+
581
+ - **Project:** ${projectName}
582
+ ${projectDescription ? `- **Description:** ${projectDescription}\n` : ''}- **Created:** ${new Date().toISOString().split('T')[0]}
344
583
  `;
345
- await writeFile(gitattributesPath, gitattributesContent, 'utf-8');
346
- createdFiles.push(gitattributesPath);
347
- // Create initial decisions.md
348
- const decisionsPath = join(decisionsDir, 'decisions.md');
349
- const decisionsContent = `# Squad Decisions
350
-
351
- ## Active Decisions
352
-
353
- No decisions recorded yet.
354
-
355
- ## Governance
356
-
357
- - All meaningful changes require team consensus
358
- - Document architectural decisions here
359
- - Keep history focused on work, decisions focused on direction
584
+ await writeIfNotExists(teamPath, teamContent);
585
+ // -------------------------------------------------------------------------
586
+ // Create routing.md
587
+ // -------------------------------------------------------------------------
588
+ const routingPath = join(squadDir, 'routing.md');
589
+ if (templatesDir && existsSync(join(templatesDir, 'routing.md'))) {
590
+ await copyIfNotExists(join(templatesDir, 'routing.md'), routingPath);
591
+ }
592
+ else {
593
+ const routingContent = `# Squad Routing
594
+
595
+ ## Work Type Rules
596
+
597
+ | Work Type | Primary Agent | Fallback |
598
+ |-----------|---------------|----------|
599
+
600
+ ## Governance
601
+
602
+ - Route based on work type and agent expertise
603
+ - Update this file as team capabilities evolve
360
604
  `;
361
- await writeFile(decisionsPath, decisionsContent, 'utf-8');
362
- createdFiles.push(decisionsPath);
605
+ await writeIfNotExists(routingPath, routingContent);
606
+ }
607
+ // -------------------------------------------------------------------------
608
+ // Copy starter skills
609
+ // -------------------------------------------------------------------------
610
+ const skillsDir = join(squadDir, 'skills');
611
+ if (templatesDir && existsSync(join(templatesDir, 'skills'))) {
612
+ const skillsSrc = join(templatesDir, 'skills');
613
+ const existingSkills = existsSync(skillsDir) ? readdirSync(skillsDir) : [];
614
+ if (existingSkills.length === 0) {
615
+ cpSync(skillsSrc, skillsDir, { recursive: true });
616
+ createdFiles.push('.squad/skills');
617
+ }
618
+ }
619
+ // -------------------------------------------------------------------------
620
+ // Create .gitattributes for merge drivers
621
+ // -------------------------------------------------------------------------
622
+ const gitattributesPath = join(teamRoot, '.gitattributes');
623
+ const unionRules = [
624
+ '.squad/decisions.md merge=union',
625
+ '.squad/agents/*/history.md merge=union',
626
+ '.squad/log/** merge=union',
627
+ '.squad/orchestration-log/** merge=union',
628
+ ];
629
+ let existingAttrs = '';
630
+ if (existsSync(gitattributesPath)) {
631
+ existingAttrs = readFileSync(gitattributesPath, 'utf-8');
632
+ }
633
+ const missingRules = unionRules.filter(rule => !existingAttrs.includes(rule));
634
+ if (missingRules.length > 0) {
635
+ const block = (existingAttrs && !existingAttrs.endsWith('\n') ? '\n' : '')
636
+ + '# Squad: union merge for append-only team state files\n'
637
+ + missingRules.join('\n') + '\n';
638
+ await appendFile(gitattributesPath, block);
639
+ createdFiles.push(toRelativePath(gitattributesPath));
640
+ }
641
+ // -------------------------------------------------------------------------
642
+ // Create .gitignore entries for runtime state (logs, inbox, sessions)
643
+ // These paths are written during normal squad operation but should not be
644
+ // committed to version control (they are runtime state).
645
+ // -------------------------------------------------------------------------
646
+ const gitignorePath = join(teamRoot, '.gitignore');
647
+ const ignoreEntries = [
648
+ '.squad/orchestration-log/',
649
+ '.squad/log/',
650
+ '.squad/decisions/inbox/',
651
+ '.squad/sessions/',
652
+ ];
653
+ let existingIgnore = '';
654
+ if (existsSync(gitignorePath)) {
655
+ existingIgnore = readFileSync(gitignorePath, 'utf-8');
656
+ }
657
+ const missingIgnore = ignoreEntries.filter(entry => !existingIgnore.includes(entry));
658
+ if (missingIgnore.length > 0) {
659
+ const block = (existingIgnore && !existingIgnore.endsWith('\n') ? '\n' : '')
660
+ + '# Squad: ignore runtime state (logs, inbox, sessions)\n'
661
+ + missingIgnore.join('\n') + '\n';
662
+ await appendFile(gitignorePath, block);
663
+ createdFiles.push(toRelativePath(gitignorePath));
664
+ }
665
+ // -------------------------------------------------------------------------
666
+ // Create .github/agents/squad.agent.md
667
+ // -------------------------------------------------------------------------
668
+ const agentFile = join(teamRoot, '.github', 'agents', 'squad.agent.md');
669
+ if (!existsSync(agentFile) || !skipExisting) {
670
+ if (templatesDir && existsSync(join(templatesDir, 'squad.agent.md'))) {
671
+ let agentContent = readFileSync(join(templatesDir, 'squad.agent.md'), 'utf-8');
672
+ agentContent = stampVersionInContent(agentContent, version);
673
+ await mkdir(dirname(agentFile), { recursive: true });
674
+ await writeFile(agentFile, agentContent, 'utf-8');
675
+ createdFiles.push(toRelativePath(agentFile));
676
+ }
677
+ }
678
+ else {
679
+ skippedFiles.push(toRelativePath(agentFile));
680
+ }
681
+ // -------------------------------------------------------------------------
682
+ // Copy .squad/templates/ (optional)
683
+ // -------------------------------------------------------------------------
684
+ if (includeTemplates && templatesDir) {
685
+ const templatesDest = join(teamRoot, '.squad', 'templates');
686
+ if (!existsSync(templatesDest)) {
687
+ cpSync(templatesDir, templatesDest, { recursive: true });
688
+ createdFiles.push(toRelativePath(templatesDest));
689
+ }
690
+ else {
691
+ skippedFiles.push(toRelativePath(templatesDest));
692
+ }
693
+ }
694
+ // -------------------------------------------------------------------------
695
+ // Copy workflows (optional)
696
+ // -------------------------------------------------------------------------
697
+ if (includeWorkflows && templatesDir && existsSync(join(templatesDir, 'workflows'))) {
698
+ const workflowsSrc = join(templatesDir, 'workflows');
699
+ const workflowsDest = join(teamRoot, '.github', 'workflows');
700
+ if (statSync(workflowsSrc).isDirectory()) {
701
+ const allWorkflowFiles = readdirSync(workflowsSrc).filter(f => f.endsWith('.yml'));
702
+ const workflowFiles = allWorkflowFiles.filter(f => FRAMEWORK_WORKFLOWS.includes(f));
703
+ await mkdir(workflowsDest, { recursive: true });
704
+ for (const file of workflowFiles) {
705
+ const destFile = join(workflowsDest, file);
706
+ if (!existsSync(destFile) || !skipExisting) {
707
+ cpSync(join(workflowsSrc, file), destFile);
708
+ createdFiles.push(toRelativePath(destFile));
709
+ }
710
+ else {
711
+ skippedFiles.push(toRelativePath(destFile));
712
+ }
713
+ }
714
+ }
715
+ }
716
+ // -------------------------------------------------------------------------
717
+ // Create sample MCP config (optional)
718
+ // -------------------------------------------------------------------------
719
+ if (includeMcpConfig) {
720
+ const mcpConfigPath = join(teamRoot, '.copilot', 'mcp-config.json');
721
+ if (!existsSync(mcpConfigPath)) {
722
+ const mcpSample = {
723
+ mcpServers: {
724
+ "EXAMPLE-trello": {
725
+ command: "npx",
726
+ args: ["-y", "@trello/mcp-server"],
727
+ env: {
728
+ TRELLO_API_KEY: "${TRELLO_API_KEY}",
729
+ TRELLO_TOKEN: "${TRELLO_TOKEN}"
730
+ }
731
+ }
732
+ }
733
+ };
734
+ await mkdir(dirname(mcpConfigPath), { recursive: true });
735
+ await writeFile(mcpConfigPath, JSON.stringify(mcpSample, null, 2) + '\n', 'utf-8');
736
+ createdFiles.push(toRelativePath(mcpConfigPath));
737
+ }
738
+ else {
739
+ skippedFiles.push(toRelativePath(mcpConfigPath));
740
+ }
741
+ }
742
+ // -------------------------------------------------------------------------
743
+ // Generate .squad/workstreams.json (when streams provided)
744
+ // -------------------------------------------------------------------------
745
+ if (options.streams && options.streams.length > 0) {
746
+ const workstreamsConfig = {
747
+ workstreams: options.streams,
748
+ defaultWorkflow: 'branch-per-issue',
749
+ };
750
+ const workstreamsPath = join(squadDir, 'workstreams.json');
751
+ await writeIfNotExists(workstreamsPath, JSON.stringify(workstreamsConfig, null, 2) + '\n');
752
+ }
753
+ // -------------------------------------------------------------------------
754
+ // Add .squad-workstream to .gitignore
755
+ // -------------------------------------------------------------------------
756
+ {
757
+ const workstreamIgnoreEntry = '.squad-workstream';
758
+ let currentIgnore = '';
759
+ if (existsSync(gitignorePath)) {
760
+ currentIgnore = readFileSync(gitignorePath, 'utf-8');
761
+ }
762
+ if (!currentIgnore.includes(workstreamIgnoreEntry)) {
763
+ const block = (currentIgnore && !currentIgnore.endsWith('\n') ? '\n' : '')
764
+ + '# Squad: workstream activation file (local to this machine)\n'
765
+ + workstreamIgnoreEntry + '\n';
766
+ await appendFile(gitignorePath, block);
767
+ createdFiles.push(toRelativePath(gitignorePath));
768
+ }
769
+ }
770
+ // -------------------------------------------------------------------------
771
+ // Create .first-run marker
772
+ // -------------------------------------------------------------------------
773
+ const firstRunMarker = join(squadDir, '.first-run');
774
+ if (!existsSync(firstRunMarker)) {
775
+ await writeFile(firstRunMarker, new Date().toISOString() + '\n', 'utf-8');
776
+ createdFiles.push(toRelativePath(firstRunMarker));
777
+ }
778
+ // -------------------------------------------------------------------------
779
+ // Store init prompt for REPL auto-casting
780
+ // -------------------------------------------------------------------------
781
+ if (options.prompt) {
782
+ const promptFile = join(squadDir, '.init-prompt');
783
+ await writeFile(promptFile, options.prompt, 'utf-8');
784
+ createdFiles.push(toRelativePath(promptFile));
785
+ }
363
786
  return {
364
787
  createdFiles,
788
+ skippedFiles,
365
789
  configPath,
366
- agentDirs
790
+ agentDirs,
791
+ agentFile,
792
+ squadDir,
367
793
  };
368
794
  }
795
+ /**
796
+ * Clean up orphan .init-prompt file.
797
+ * Called by CLI on Ctrl+C abort to remove partial state.
798
+ *
799
+ * @param squadDir - Path to the .squad directory
800
+ */
801
+ export async function cleanupOrphanInitPrompt(squadDir) {
802
+ const promptFile = join(squadDir, '.init-prompt');
803
+ if (existsSync(promptFile)) {
804
+ await unlink(promptFile);
805
+ }
806
+ }
369
807
  //# sourceMappingURL=init.js.map