teamspec 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/bin/teamspec-init.js +10 -0
  4. package/extensions/teamspec-0.1.0.vsix +0 -0
  5. package/lib/cli.js +1174 -0
  6. package/lib/extension-installer.js +236 -0
  7. package/lib/linter.js +1184 -0
  8. package/lib/prompt-generator.js +409 -0
  9. package/package.json +51 -0
  10. package/teamspec-core/agents/AGENT_BA.md +486 -0
  11. package/teamspec-core/agents/AGENT_BOOTSTRAP.md +447 -0
  12. package/teamspec-core/agents/AGENT_DES.md +623 -0
  13. package/teamspec-core/agents/AGENT_DEV.md +611 -0
  14. package/teamspec-core/agents/AGENT_FA.md +736 -0
  15. package/teamspec-core/agents/AGENT_FEEDBACK.md +202 -0
  16. package/teamspec-core/agents/AGENT_FIX.md +380 -0
  17. package/teamspec-core/agents/AGENT_QA.md +756 -0
  18. package/teamspec-core/agents/AGENT_SA.md +581 -0
  19. package/teamspec-core/agents/AGENT_SM.md +771 -0
  20. package/teamspec-core/agents/README.md +383 -0
  21. package/teamspec-core/context/_schema.yml +222 -0
  22. package/teamspec-core/copilot-instructions.md +356 -0
  23. package/teamspec-core/definitions/definition-of-done.md +129 -0
  24. package/teamspec-core/definitions/definition-of-ready.md +104 -0
  25. package/teamspec-core/profiles/enterprise.yml +127 -0
  26. package/teamspec-core/profiles/platform-team.yml +104 -0
  27. package/teamspec-core/profiles/regulated.yml +97 -0
  28. package/teamspec-core/profiles/startup.yml +85 -0
  29. package/teamspec-core/teamspec.yml +69 -0
  30. package/teamspec-core/templates/README.md +211 -0
  31. package/teamspec-core/templates/active-sprint-template.md +98 -0
  32. package/teamspec-core/templates/adr-template.md +194 -0
  33. package/teamspec-core/templates/bug-report-template.md +188 -0
  34. package/teamspec-core/templates/business-analysis-template.md +164 -0
  35. package/teamspec-core/templates/decision-log-template.md +216 -0
  36. package/teamspec-core/templates/feature-template.md +269 -0
  37. package/teamspec-core/templates/functional-spec-template.md +161 -0
  38. package/teamspec-core/templates/refinement-notes-template.md +133 -0
  39. package/teamspec-core/templates/sprint-goal-template.md +129 -0
  40. package/teamspec-core/templates/sprint-template.md +175 -0
  41. package/teamspec-core/templates/sprints-index-template.md +67 -0
  42. package/teamspec-core/templates/story-template.md +244 -0
  43. package/teamspec-core/templates/storymap-template.md +204 -0
  44. package/teamspec-core/templates/testcases-template.md +147 -0
  45. package/teamspec-core/templates/uat-pack-template.md +161 -0
package/lib/cli.js ADDED
@@ -0,0 +1,1174 @@
1
+ /**
2
+ * TeamSpec Init CLI - Pure JavaScript implementation
3
+ * Version 3.0.0 - Feature Canon Operating Model
4
+ *
5
+ * This CLI bootstraps TeamSpec 2.0 in any repository by:
6
+ * 1. Asking team setup questions
7
+ * 2. Deploying .teamspec/ folder with core files
8
+ * 3. Creating project structure
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const readline = require('readline');
14
+
15
+ // =============================================================================
16
+ // ANSI Color Helpers
17
+ // =============================================================================
18
+
19
+ const colors = {
20
+ reset: '\x1b[0m',
21
+ bold: '\x1b[1m',
22
+ red: '\x1b[91m',
23
+ green: '\x1b[92m',
24
+ yellow: '\x1b[93m',
25
+ blue: '\x1b[94m',
26
+ cyan: '\x1b[96m',
27
+ };
28
+
29
+ function colored(text, color) {
30
+ if (process.stdout.isTTY) {
31
+ return `${color}${text}${colors.reset}`;
32
+ }
33
+ return text;
34
+ }
35
+
36
+ // =============================================================================
37
+ // Banner
38
+ // =============================================================================
39
+
40
+ function printBanner() {
41
+ const banner = `
42
+ ╔══════════════════════════════════════════════════════════════════════╗
43
+ ║ ║
44
+ ║ ████████╗███████╗ █████╗ ███╗ ███╗███████╗██████╗ ███████╗ ██████╗ ║
45
+ ║ ╚══██╔══╝██╔════╝██╔══██╗████╗ ████║██╔════╝██╔══██╗██╔════╝██╔════╝ ║
46
+ ║ ██║ █████╗ ███████║██╔████╔██║███████╗██████╔╝█████╗ ██║ ║
47
+ ║ ██║ ██╔══╝ ██╔══██║██║╚██╔╝██║╚════██║██╔═══╝ ██╔══╝ ██║ ║
48
+ ║ ██║ ███████╗██║ ██║██║ ╚═╝ ██║███████║██║ ███████╗╚██████╗ ║
49
+ ║ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝ ╚═════╝ ║
50
+ ║ ║
51
+ ║ ║
52
+ ║ Feature Canon Operating Model v2.0 ║
53
+ ╚══════════════════════════════════════════════════════════════════════╝
54
+ `;
55
+ console.log(colored(banner, colors.cyan));
56
+ }
57
+
58
+ // =============================================================================
59
+ // Configuration Options
60
+ // =============================================================================
61
+
62
+ const PROFILE_OPTIONS = {
63
+ none: 'No specific profile - vanilla TeamSpec',
64
+ regulated: 'Regulated industry (banking, healthcare, government) - strict compliance',
65
+ startup: 'Startup/MVP mode - lean documentation, speed focus',
66
+ 'platform-team': 'Platform/infrastructure team - API-first, SLA focus',
67
+ enterprise: 'Enterprise - governance, audit trails, multi-team coordination',
68
+ };
69
+
70
+ const INDUSTRY_OPTIONS = {
71
+ technology: 'Technology / Software',
72
+ finance: 'Financial Services / Banking',
73
+ healthcare: 'Healthcare / Life Sciences',
74
+ retail: 'Retail / E-commerce',
75
+ manufacturing: 'Manufacturing / Industrial',
76
+ government: 'Government / Public Sector',
77
+ other: 'Other',
78
+ };
79
+
80
+ const CADENCE_OPTIONS = {
81
+ scrum: 'Scrum (time-boxed sprints)',
82
+ kanban: 'Kanban (continuous flow)',
83
+ scrumban: 'Scrumban (hybrid)',
84
+ };
85
+
86
+ const IDE_OPTIONS = {
87
+ none: 'No IDE integration - just files',
88
+ vscode: 'VS Code with @teamspec chat participant',
89
+ cursor: 'Cursor (VS Code settings only, no chat participant)',
90
+ other: 'Other IDE (manual setup)',
91
+ };
92
+
93
+ const DEFAULT_PROJECT_ID = 'main-project';
94
+
95
+ // =============================================================================
96
+ // Argument Parsing
97
+ // =============================================================================
98
+
99
+ function parseArgs(args) {
100
+ const options = {
101
+ command: 'init',
102
+ target: process.cwd(),
103
+ profile: null,
104
+ org: null,
105
+ team: null,
106
+ project: null,
107
+ ide: null,
108
+ copilot: null,
109
+ nonInteractive: false,
110
+ help: false,
111
+ version: false,
112
+ force: false,
113
+ };
114
+
115
+ let i = 0;
116
+
117
+ if (args.length > 0 && !args[0].startsWith('-')) {
118
+ const cmd = args[0].toLowerCase();
119
+ if (['init', 'update', 'lint', 'generate-prompts'].includes(cmd)) {
120
+ options.command = cmd;
121
+ i = 1;
122
+ }
123
+ }
124
+
125
+ for (; i < args.length; i++) {
126
+ const arg = args[i];
127
+ switch (arg) {
128
+ case '--help':
129
+ case '-h':
130
+ options.help = true;
131
+ break;
132
+ case '--version':
133
+ case '-v':
134
+ options.version = true;
135
+ break;
136
+ case '--target':
137
+ case '-t':
138
+ options.target = args[++i];
139
+ break;
140
+ case '--profile':
141
+ case '-p':
142
+ options.profile = args[++i];
143
+ break;
144
+ case '--org':
145
+ case '-o':
146
+ options.org = args[++i];
147
+ break;
148
+ case '--team':
149
+ options.team = args[++i];
150
+ break;
151
+ case '--project':
152
+ options.project = args[++i];
153
+ break;
154
+ case '--ide':
155
+ options.ide = args[++i];
156
+ break;
157
+ case '--copilot':
158
+ const copilotArg = args[++i];
159
+ options.copilot = copilotArg === 'true' || copilotArg === 'yes';
160
+ break;
161
+ case '--non-interactive':
162
+ case '-y':
163
+ options.nonInteractive = true;
164
+ break;
165
+ case '--force':
166
+ case '-f':
167
+ options.force = true;
168
+ break;
169
+ }
170
+ }
171
+
172
+ return options;
173
+ }
174
+
175
+ // =============================================================================
176
+ // Help and Version
177
+ // =============================================================================
178
+
179
+ function printHelp() {
180
+ console.log(`
181
+ ${colored('TeamSpec Init', colors.bold)} - Bootstrap TeamSpec 2.0 Feature Canon Operating Model
182
+
183
+ ${colored('USAGE:', colors.bold)}
184
+ teamspec [command] [options]
185
+
186
+ ${colored('COMMANDS:', colors.bold)}
187
+ init [options] Initialize TeamSpec in a repository (default)
188
+ update [options] Update TeamSpec core files (keeps team context)
189
+ lint [options] Lint project artifacts against TeamSpec rules
190
+
191
+ ${colored('OPTIONS:', colors.bold)}
192
+ -h, --help Show this help message
193
+ -v, --version Show version number
194
+ -t, --target <dir> Target directory (default: current directory)
195
+ -p, --profile <profile> Team profile to use
196
+ -o, --org <name> Organization name
197
+ --team <name> Team name
198
+ --project <id> Project ID for folder structure (default: main-project)
199
+ --ide <ide> IDE integration (vscode, cursor, other, none)
200
+ --copilot <yes|no> Install GitHub Copilot instructions file (default: yes)
201
+ -y, --non-interactive Run without prompts (use defaults)
202
+ -f, --force Force update without confirmation
203
+
204
+ ${colored('PROFILES:', colors.bold)}
205
+ none Vanilla TeamSpec
206
+ regulated Banking, healthcare, government (strict compliance)
207
+ startup Lean documentation, speed focus
208
+ platform-team API-first, SLA focus
209
+ enterprise Full governance, audit trails
210
+
211
+ ${colored('EXAMPLES:', colors.bold)}
212
+ teamspec # Interactive setup
213
+ teamspec --profile startup -y # Quick setup with startup profile
214
+ teamspec update # Update core files, keep context
215
+ teamspec update --force # Update without confirmation
216
+ teamspec lint # Lint all projects
217
+ teamspec lint --project my-project # Lint specific project
218
+ teamspec generate-prompts # Generate GitHub Copilot prompt files
219
+
220
+ ${colored('WHAT GETS CREATED:', colors.bold)}
221
+ .teamspec/ Core framework
222
+ ├── templates/ Document templates
223
+ ├── definitions/ DoR/DoD checklists
224
+ ├── profiles/ Profile overlays
225
+ └── context/team.yml Team configuration
226
+ projects/<project-id>/ Project artifacts
227
+ ├── features/ Feature Canon (source of truth)
228
+ ├── stories/ User stories (workflow folders)
229
+ ├── adr/ Architecture decisions
230
+ └── ...
231
+ `);
232
+ }
233
+
234
+ function printVersion() {
235
+ const pkg = require('../package.json');
236
+ console.log(`teamspec ${pkg.version}`);
237
+ }
238
+
239
+ // =============================================================================
240
+ // File System Utilities
241
+ // =============================================================================
242
+
243
+ function getTeamspecCoreDir() {
244
+ return path.join(__dirname, '..', 'teamspec-core');
245
+ }
246
+
247
+ function copyDirRecursive(src, dest) {
248
+ fs.mkdirSync(dest, { recursive: true });
249
+ const entries = fs.readdirSync(src, { withFileTypes: true });
250
+
251
+ for (const entry of entries) {
252
+ const srcPath = path.join(src, entry.name);
253
+ const destPath = path.join(dest, entry.name);
254
+
255
+ if (entry.isDirectory()) {
256
+ copyDirRecursive(srcPath, destPath);
257
+ } else {
258
+ fs.copyFileSync(srcPath, destPath);
259
+ }
260
+ }
261
+ }
262
+
263
+ // =============================================================================
264
+ // Interactive Prompts
265
+ // =============================================================================
266
+
267
+ function createReadlineInterface() {
268
+ return readline.createInterface({
269
+ input: process.stdin,
270
+ output: process.stdout,
271
+ });
272
+ }
273
+
274
+ function prompt(rl, question, defaultValue = '') {
275
+ return new Promise((resolve) => {
276
+ const defaultHint = defaultValue ? ` [${defaultValue}]` : '';
277
+ rl.question(`${question}${defaultHint}: `, (answer) => {
278
+ resolve(answer.trim() || defaultValue);
279
+ });
280
+ });
281
+ }
282
+
283
+ function promptYesNo(rl, question, defaultValue = true) {
284
+ return new Promise((resolve) => {
285
+ const choices = defaultValue ? '[Y/n]' : '[y/N]';
286
+ rl.question(`${question} ${choices}: `, (answer) => {
287
+ const normalized = answer.trim().toLowerCase();
288
+ if (!normalized) {
289
+ resolve(defaultValue);
290
+ } else if (normalized === 'y' || normalized === 'yes') {
291
+ resolve(true);
292
+ } else {
293
+ resolve(false);
294
+ }
295
+ });
296
+ });
297
+ }
298
+
299
+ async function promptChoice(rl, question, options, defaultValue = null) {
300
+ console.log(`\n${colored(question, colors.bold)}`);
301
+ const keys = Object.keys(options);
302
+ for (const key of keys) {
303
+ const marker = key === defaultValue ? ' (default)' : '';
304
+ console.log(` ${colored(key, colors.cyan)}: ${options[key]}${marker}`);
305
+ }
306
+
307
+ while (true) {
308
+ const answer = await prompt(rl, `\nYour choice`, defaultValue || '');
309
+ if (keys.includes(answer.toLowerCase())) {
310
+ return answer.toLowerCase();
311
+ }
312
+ console.log(`Invalid choice. Please choose from: ${keys.join(', ')}`);
313
+ }
314
+ }
315
+
316
+ // =============================================================================
317
+ // Team Setup Questions
318
+ // =============================================================================
319
+
320
+ async function runInteractive(options) {
321
+ const rl = createReadlineInterface();
322
+
323
+ try {
324
+ // Profile selection
325
+ if (!options.profile) {
326
+ options.profile = await promptChoice(
327
+ rl,
328
+ 'What type of team/environment?',
329
+ PROFILE_OPTIONS,
330
+ 'none'
331
+ );
332
+ }
333
+
334
+ // Organization name
335
+ if (!options.org) {
336
+ options.org = await prompt(
337
+ rl,
338
+ `\n${colored('Organization name', colors.bold)}`,
339
+ 'My Organization'
340
+ );
341
+ }
342
+
343
+ // Team name
344
+ if (!options.team) {
345
+ options.team = await prompt(
346
+ rl,
347
+ `${colored('Team name', colors.bold)}`,
348
+ 'My Team'
349
+ );
350
+ }
351
+
352
+ // Industry
353
+ options.industry = await promptChoice(
354
+ rl,
355
+ 'What industry is your organization in?',
356
+ INDUSTRY_OPTIONS,
357
+ 'technology'
358
+ );
359
+
360
+ // Cadence
361
+ options.cadence = await promptChoice(
362
+ rl,
363
+ 'What development cadence does your team use?',
364
+ CADENCE_OPTIONS,
365
+ 'scrum'
366
+ );
367
+
368
+ // Sprint length (if scrum/scrumban)
369
+ if (options.cadence !== 'kanban') {
370
+ const sprintLength = await prompt(
371
+ rl,
372
+ `\n${colored('Sprint length (days)', colors.bold)}`,
373
+ '14'
374
+ );
375
+ options.sprintLengthDays = parseInt(sprintLength, 10) || 14;
376
+ } else {
377
+ options.sprintLengthDays = null;
378
+ }
379
+
380
+ // Project ID
381
+ if (!options.project) {
382
+ console.log(`\n${colored('Project Structure', colors.bold)}`);
383
+ console.log(' TeamSpec organizes artifacts in project folders: projects/<project-id>/');
384
+ options.project = await prompt(
385
+ rl,
386
+ `${colored('Initial project ID', colors.bold)} (lowercase, hyphenated)`,
387
+ DEFAULT_PROJECT_ID
388
+ );
389
+ options.project = normalizeProjectId(options.project);
390
+ }
391
+
392
+ // IDE Integration
393
+ if (!options.ide) {
394
+ options.ide = await promptChoice(
395
+ rl,
396
+ 'Which IDE are you using?',
397
+ IDE_OPTIONS,
398
+ 'vscode'
399
+ );
400
+ }
401
+
402
+ // GitHub Copilot Instructions
403
+ if (options.copilot === null) {
404
+ options.copilot = await promptYesNo(
405
+ rl,
406
+ `\n${colored('Install GitHub Copilot instructions file?', colors.bold)}\n (Adds .github/copilot-instructions.md with TeamSpec guidance)`,
407
+ true
408
+ );
409
+ }
410
+
411
+ // Confirmation
412
+ console.log(`\n${colored('Configuration:', colors.bold)}`);
413
+ console.log(` Profile: ${options.profile}`);
414
+ console.log(` Organization: ${options.org}`);
415
+ console.log(` Team: ${options.team}`);
416
+ console.log(` Industry: ${options.industry}`);
417
+ console.log(` Cadence: ${options.cadence}`);
418
+ if (options.sprintLengthDays) {
419
+ console.log(` Sprint Length: ${options.sprintLengthDays} days`);
420
+ }
421
+ console.log(` Project ID: ${options.project}`);
422
+ console.log(` IDE: ${options.ide}`);
423
+ console.log(` Copilot: ${options.copilot ? 'Yes' : 'No'}`);
424
+
425
+ const proceed = await promptYesNo(
426
+ rl,
427
+ `\n${colored('Proceed with initialization?', colors.bold)}`,
428
+ true
429
+ );
430
+ if (!proceed) {
431
+ console.log('Aborted.');
432
+ process.exit(0);
433
+ }
434
+ } finally {
435
+ rl.close();
436
+ }
437
+
438
+ return options;
439
+ }
440
+
441
+ function normalizeProjectId(projectId) {
442
+ const normalized = projectId.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
443
+ return normalized || DEFAULT_PROJECT_ID;
444
+ }
445
+
446
+ // =============================================================================
447
+ // Core File Deployment
448
+ // =============================================================================
449
+
450
+ function copyTeamspecCore(targetDir, sourceDir) {
451
+ const targetTeamspec = path.join(targetDir, '.teamspec');
452
+
453
+ const dirsToCopy = [
454
+ 'agents',
455
+ 'definitions',
456
+ 'profiles',
457
+ 'templates',
458
+ ];
459
+
460
+ const filesToCopy = ['teamspec.yml'];
461
+
462
+ console.log(`\n${colored('Copying TeamSpec core files...', colors.blue)}`);
463
+
464
+ fs.mkdirSync(targetTeamspec, { recursive: true });
465
+
466
+ for (const dirName of dirsToCopy) {
467
+ const src = path.join(sourceDir, dirName);
468
+ const dest = path.join(targetTeamspec, dirName);
469
+ if (fs.existsSync(src)) {
470
+ if (fs.existsSync(dest)) {
471
+ fs.rmSync(dest, { recursive: true });
472
+ }
473
+ copyDirRecursive(src, dest);
474
+ console.log(` ✓ Copied ${dirName}/`);
475
+ }
476
+ }
477
+
478
+ for (const fileName of filesToCopy) {
479
+ const src = path.join(sourceDir, fileName);
480
+ const dest = path.join(targetTeamspec, fileName);
481
+ if (fs.existsSync(src)) {
482
+ fs.copyFileSync(src, dest);
483
+ console.log(` ✓ Copied ${fileName}`);
484
+ }
485
+ }
486
+
487
+ const contextDir = path.join(targetTeamspec, 'context');
488
+ fs.mkdirSync(contextDir, { recursive: true });
489
+
490
+ const schemaSrc = path.join(sourceDir, 'context', '_schema.yml');
491
+ if (fs.existsSync(schemaSrc)) {
492
+ fs.copyFileSync(schemaSrc, path.join(contextDir, '_schema.yml'));
493
+ console.log(' ✓ Copied context/_schema.yml');
494
+ }
495
+ }
496
+
497
+ // =============================================================================
498
+ // Copilot Instructions Deployment
499
+ // =============================================================================
500
+
501
+ function copyCopilotInstructions(targetDir, sourceDir) {
502
+ const githubDir = path.join(targetDir, '.github');
503
+ fs.mkdirSync(githubDir, { recursive: true });
504
+
505
+ const copilotSrc = path.join(sourceDir, 'copilot-instructions.md');
506
+ const copilotDest = path.join(githubDir, 'copilot-instructions.md');
507
+
508
+ if (fs.existsSync(copilotSrc)) {
509
+ fs.copyFileSync(copilotSrc, copilotDest);
510
+ console.log(`\n${colored('Copying GitHub Copilot instructions...', colors.blue)}`);
511
+ console.log(' ✓ Copied .github/copilot-instructions.md');
512
+ return true;
513
+ } else {
514
+ console.log(` ⚠ Copilot instructions not found in source`);
515
+ return false;
516
+ }
517
+ }
518
+
519
+ // =============================================================================
520
+ // Team Context Creation
521
+ // =============================================================================
522
+
523
+ function createTeamContext(targetDir, options) {
524
+ const contextDir = path.join(targetDir, '.teamspec', 'context');
525
+ fs.mkdirSync(contextDir, { recursive: true });
526
+
527
+ const teamYml = `# Team Context Configuration
528
+ # This file parameterizes the TeamSpec Core for your specific team.
529
+ # See _schema.yml for full schema documentation.
530
+
531
+ # ==============================================================================
532
+ # Organization
533
+ # ==============================================================================
534
+ org:
535
+ name: "${options.org}"
536
+ department: "Engineering"
537
+ industry: ${options.industry}
538
+ profile: ${options.profile} # Options: regulated, startup, platform-team, enterprise, none
539
+ compliance: [] # Options: SOX, PCI-DSS, HIPAA, GDPR, SOC2, ISO27001, FedRAMP, POPIA
540
+
541
+ # ==============================================================================
542
+ # Team
543
+ # ==============================================================================
544
+ team:
545
+ name: "${options.team}"
546
+ roles:
547
+ - BA # Business Analyst
548
+ - FA # Functional Analyst
549
+ - SA # Solution Architect
550
+ - DEV # Developer
551
+ - QA # Quality Assurance
552
+ - SM # Scrum Master
553
+
554
+ cadence:
555
+ type: ${options.cadence} # Options: scrum, kanban, scrumban
556
+ ${options.sprintLengthDays ? ` sprint_length_days: ${options.sprintLengthDays}` : ' # sprint_length_days: N/A for kanban'}
557
+
558
+ # ==============================================================================
559
+ # Technology Stack
560
+ # ==============================================================================
561
+ tech:
562
+ stack: []
563
+ # Add your technologies, e.g.:
564
+ # - Python
565
+ # - React
566
+ # - PostgreSQL
567
+
568
+ architecture:
569
+ type: monolith # Options: monolith, microservices, modular-monolith, serverless
570
+
571
+ # ==============================================================================
572
+ # Governance
573
+ # ==============================================================================
574
+ governance:
575
+ sign_off_required: ${options.profile === 'regulated' || options.profile === 'enterprise'}
576
+ audit_trail: ${options.profile === 'regulated' || options.profile === 'enterprise'}
577
+ change_control_board: ${options.profile === 'regulated'}
578
+
579
+ # ==============================================================================
580
+ # Feature Canon Configuration
581
+ # ==============================================================================
582
+ feature_canon:
583
+ ownership:
584
+ purpose_and_scope: BA
585
+ behavior: FA
586
+ change_log: FA
587
+ require_feature_links: true
588
+ require_delta_format: true
589
+ `;
590
+
591
+ fs.writeFileSync(path.join(contextDir, 'team.yml'), teamYml, 'utf-8');
592
+ console.log(' ✓ Created context/team.yml');
593
+ }
594
+
595
+ // =============================================================================
596
+ // Project Structure Creation
597
+ // =============================================================================
598
+
599
+ function createProjectStructure(targetDir, projectId) {
600
+ const projectDir = path.join(targetDir, 'projects', projectId);
601
+ fs.mkdirSync(projectDir, { recursive: true });
602
+
603
+ console.log(`\n${colored(`Creating project structure: projects/${projectId}/...`, colors.blue)}`);
604
+
605
+ // project.yml
606
+ const projectYml = `# Project Configuration: ${projectId}
607
+ project:
608
+ id: "${projectId}"
609
+ name: "${projectId.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')}"
610
+ description: |
611
+ # TODO: Add project description
612
+ status: active # active, paused, completed, archived
613
+ `;
614
+ fs.writeFileSync(path.join(projectDir, 'project.yml'), projectYml, 'utf-8');
615
+ console.log(` ✓ Created projects/${projectId}/project.yml`);
616
+
617
+ // features/
618
+ const featuresDir = path.join(projectDir, 'features');
619
+ fs.mkdirSync(featuresDir, { recursive: true });
620
+
621
+ fs.writeFileSync(path.join(featuresDir, 'features-index.md'), `# Features Index (Feature Canon)
622
+
623
+ This is the master index of all features in this project.
624
+ The Feature Canon is the **source of truth** for system behavior.
625
+
626
+ ## Feature Registry
627
+
628
+ | ID | Name | Status | Owner |
629
+ |----|------|--------|-------|
630
+ | _(none yet)_ | | | |
631
+
632
+ ## Next Available ID: **F-001**
633
+
634
+ ---
635
+
636
+ > **To add features:** Run \`ts:ba feature\` — features are never created implicitly.
637
+ `, 'utf-8');
638
+
639
+ fs.writeFileSync(path.join(featuresDir, 'story-ledger.md'), `# Story Ledger
640
+
641
+ Tracks completed stories and their impact on the Feature Canon.
642
+
643
+ | Story ID | Title | Sprint | Features Modified | Merged Date |
644
+ |----------|-------|--------|-------------------|-------------|
645
+ | _(none yet)_ | | | | |
646
+ `, 'utf-8');
647
+ console.log(` ✓ Created projects/${projectId}/features/`);
648
+
649
+ // stories/ with workflow folders
650
+ const storiesDir = path.join(projectDir, 'stories');
651
+ for (const folder of ['backlog', 'ready-to-refine', 'ready-for-development']) {
652
+ fs.mkdirSync(path.join(storiesDir, folder), { recursive: true });
653
+ }
654
+
655
+ fs.writeFileSync(path.join(storiesDir, 'README.md'), `# Stories
656
+
657
+ ## Workflow Folders
658
+
659
+ | Folder | Status | Owner |
660
+ |--------|--------|-------|
661
+ | \`backlog/\` | New stories | FA |
662
+ | \`ready-to-refine/\` | Ready for dev refinement | FA |
663
+ | \`ready-for-development/\` | Refined, ready for sprint | DEV |
664
+
665
+ ## Rules
666
+
667
+ - Every story must link to features in the Feature Canon
668
+ - Stories describe DELTAS (changes), not full behavior
669
+ - Use \`ts:fa story\` to create properly formatted stories
670
+ `, 'utf-8');
671
+ console.log(` ✓ Created projects/${projectId}/stories/`);
672
+
673
+ // adr/
674
+ fs.mkdirSync(path.join(projectDir, 'adr'), { recursive: true });
675
+ fs.writeFileSync(path.join(projectDir, 'adr', 'README.md'), `# Architecture Decision Records
676
+
677
+ Naming: \`ADR-NNN-<slug>.md\`
678
+
679
+ Use template: \`/.teamspec/templates/adr-template.md\`
680
+ `, 'utf-8');
681
+ console.log(` ✓ Created projects/${projectId}/adr/`);
682
+
683
+ // decisions/
684
+ fs.mkdirSync(path.join(projectDir, 'decisions'), { recursive: true });
685
+ fs.writeFileSync(path.join(projectDir, 'decisions', 'README.md'), `# Business Decisions
686
+
687
+ Naming: \`DECISION-NNN-<slug>.md\`
688
+
689
+ Use template: \`/.teamspec/templates/decision-log-template.md\`
690
+ `, 'utf-8');
691
+ console.log(` ✓ Created projects/${projectId}/decisions/`);
692
+
693
+ // dev-plans/
694
+ fs.mkdirSync(path.join(projectDir, 'dev-plans'), { recursive: true });
695
+ fs.writeFileSync(path.join(projectDir, 'dev-plans', 'README.md'), `# Development Plans
696
+
697
+ Naming: \`story-NNN-tasks.md\`
698
+
699
+ Rules:
700
+ - NEVER start coding without a dev plan
701
+ - No TBD/TODO items before implementation
702
+ - Mark tasks ✅ as completed
703
+ `, 'utf-8');
704
+ console.log(` ✓ Created projects/${projectId}/dev-plans/`);
705
+
706
+ // qa/
707
+ fs.mkdirSync(path.join(projectDir, 'qa', 'test-cases'), { recursive: true });
708
+ fs.writeFileSync(path.join(projectDir, 'qa', 'README.md'), `# Quality Assurance
709
+
710
+ Tests validate **Feature Canon** behavior, not individual stories.
711
+
712
+ Use template: \`/.teamspec/templates/testcases-template.md\`
713
+ `, 'utf-8');
714
+ console.log(` ✓ Created projects/${projectId}/qa/`);
715
+
716
+ // sprints/
717
+ fs.mkdirSync(path.join(projectDir, 'sprints'), { recursive: true });
718
+ fs.writeFileSync(path.join(projectDir, 'sprints', 'sprints-index.md'), `# Sprints Index
719
+
720
+ ## Current Sprint: _None active_
721
+
722
+ ## Sprint History
723
+
724
+ | Sprint | Goal | Status | Stories | Canon Synced |
725
+ |--------|------|--------|---------|--------------|
726
+ | _(none yet)_ | | | | |
727
+ `, 'utf-8');
728
+ console.log(` ✓ Created projects/${projectId}/sprints/`);
729
+
730
+ // epics/
731
+ fs.mkdirSync(path.join(projectDir, 'epics'), { recursive: true });
732
+ fs.writeFileSync(path.join(projectDir, 'epics', 'epics-index.md'), `# Epics Index
733
+
734
+ | ID | Name | Status | Features | Target |
735
+ |----|------|--------|----------|--------|
736
+ | _(none yet)_ | | | | |
737
+
738
+ ## Next Available ID: **EPIC-001**
739
+
740
+ > **To add epics:** Run \`ts:ba epic\` — epics are never created implicitly.
741
+ `, 'utf-8');
742
+ console.log(` ✓ Created projects/${projectId}/epics/`);
743
+ }
744
+
745
+ // =============================================================================
746
+ // Update Command
747
+ // =============================================================================
748
+
749
+ function updateTeamspecCore(targetDir, sourceDir) {
750
+ const targetTeamspec = path.join(targetDir, '.teamspec');
751
+
752
+ const dirsToUpdate = ['agents', 'definitions', 'profiles', 'templates'];
753
+ const filesToUpdate = ['teamspec.yml'];
754
+ const contextFilesToUpdate = ['_schema.yml'];
755
+
756
+ console.log(`\n${colored('Updating TeamSpec core files...', colors.blue)}`);
757
+
758
+ const updated = [];
759
+ const skipped = [];
760
+
761
+ for (const dirName of dirsToUpdate) {
762
+ const src = path.join(sourceDir, dirName);
763
+ const dest = path.join(targetTeamspec, dirName);
764
+ if (fs.existsSync(src)) {
765
+ if (fs.existsSync(dest)) {
766
+ fs.rmSync(dest, { recursive: true });
767
+ }
768
+ copyDirRecursive(src, dest);
769
+ updated.push(`${dirName}/`);
770
+ console.log(` ✓ Updated ${dirName}/`);
771
+ }
772
+ }
773
+
774
+ for (const fileName of filesToUpdate) {
775
+ const src = path.join(sourceDir, fileName);
776
+ const dest = path.join(targetTeamspec, fileName);
777
+ if (fs.existsSync(src)) {
778
+ fs.copyFileSync(src, dest);
779
+ updated.push(fileName);
780
+ console.log(` ✓ Updated ${fileName}`);
781
+ }
782
+ }
783
+
784
+ const contextDir = path.join(targetTeamspec, 'context');
785
+ if (fs.existsSync(contextDir)) {
786
+ for (const fileName of contextFilesToUpdate) {
787
+ const src = path.join(sourceDir, 'context', fileName);
788
+ const dest = path.join(contextDir, fileName);
789
+ if (fs.existsSync(src)) {
790
+ fs.copyFileSync(src, dest);
791
+ updated.push(`context/${fileName}`);
792
+ console.log(` ✓ Updated context/${fileName}`);
793
+ }
794
+ }
795
+ if (fs.existsSync(path.join(contextDir, 'team.yml'))) {
796
+ skipped.push('context/team.yml');
797
+ console.log(` ⏭ Preserved context/team.yml`);
798
+ }
799
+ }
800
+
801
+ return { updated, skipped };
802
+ }
803
+
804
+ // =============================================================================
805
+ // IDE Integration
806
+ // =============================================================================
807
+
808
+ async function setupIDEIntegration(targetDir, options) {
809
+ if (!options.ide || options.ide === 'none') {
810
+ return { skipped: true };
811
+ }
812
+
813
+ const { installExtension, copyExtensionToWorkspace, isVSCodeAvailable } = require('./extension-installer');
814
+ const { generateAllPrompts } = require('./prompt-generator');
815
+
816
+ console.log(`\n${colored('Setting up IDE integration...', colors.blue)}`);
817
+
818
+ // Generate GitHub Copilot prompt files for VS Code and Cursor
819
+ if (options.ide === 'vscode' || options.ide === 'cursor') {
820
+ try {
821
+ console.log(' → Generating GitHub Copilot prompt files...');
822
+ const generatedFiles = generateAllPrompts(targetDir);
823
+ console.log(` ✓ Generated ${generatedFiles.length} prompt files in .github/prompts/`);
824
+ } catch (error) {
825
+ console.log(` ⚠ Failed to generate prompts: ${error.message}`);
826
+ }
827
+ }
828
+
829
+ // Create .vscode folder with settings
830
+ const vscodeDir = path.join(targetDir, '.vscode');
831
+ fs.mkdirSync(vscodeDir, { recursive: true });
832
+
833
+ // Create or update settings.json
834
+ const settingsPath = path.join(vscodeDir, 'settings.json');
835
+ let settings = {};
836
+
837
+ if (fs.existsSync(settingsPath)) {
838
+ try {
839
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
840
+ } catch {
841
+ // Ignore parse errors
842
+ }
843
+ }
844
+
845
+ // Add TeamSpec-specific settings
846
+ settings['teamspec.enforceGates'] = true;
847
+ settings['files.associations'] = settings['files.associations'] || {};
848
+ settings['files.associations']['*.teamspec'] = 'yaml';
849
+ settings['editor.formatOnSave'] = settings['editor.formatOnSave'] ?? true;
850
+
851
+ // Add markdown settings for better feature/story editing
852
+ settings['[markdown]'] = settings['[markdown]'] || {};
853
+ settings['[markdown]']['editor.wordWrap'] = 'on';
854
+
855
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
856
+ console.log(' ✓ Created .vscode/settings.json');
857
+
858
+ // Create extensions.json to recommend the extension
859
+ const extensionsPath = path.join(vscodeDir, 'extensions.json');
860
+ const extensionsJson = {
861
+ recommendations: ['teamspec.teamspec']
862
+ };
863
+
864
+ if (fs.existsSync(extensionsPath)) {
865
+ try {
866
+ const existing = JSON.parse(fs.readFileSync(extensionsPath, 'utf-8'));
867
+ if (existing.recommendations) {
868
+ const recs = new Set(existing.recommendations);
869
+ recs.add('teamspec.teamspec');
870
+ extensionsJson.recommendations = Array.from(recs);
871
+ }
872
+ } catch {
873
+ // Ignore parse errors
874
+ }
875
+ }
876
+
877
+ fs.writeFileSync(extensionsPath, JSON.stringify(extensionsJson, null, 2));
878
+ console.log(' ✓ Created .vscode/extensions.json');
879
+
880
+ return { success: true };
881
+ }
882
+
883
+ // =============================================================================
884
+ // Summary Output
885
+ // =============================================================================
886
+
887
+ function printNextSteps(targetDir, profile, projectId, ide, ideResult, copilot) {
888
+ console.log(`\n${colored('='.repeat(70), colors.green)}`);
889
+ console.log(colored(' ✅ TeamSpec 2.0 initialized successfully!', colors.green + colors.bold));
890
+ console.log(colored('='.repeat(70), colors.green));
891
+
892
+ const copilotSection = copilot !== false ? `
893
+ .github/
894
+ └── copilot-instructions.md - GitHub Copilot guidance` : '';
895
+
896
+ console.log(`
897
+ ${colored('📁 Created Structure:', colors.bold)}
898
+ .teamspec/ - Core framework
899
+ ├── templates/ - Document templates
900
+ ├── definitions/ - DoR/DoD checklists
901
+ ├── profiles/ - Profile overlays
902
+ └── context/team.yml - Team configuration${copilotSection}
903
+ projects/${projectId}/
904
+ ├── features/ - Feature Canon (source of truth)
905
+ ├── stories/ - User stories (workflow folders)
906
+ ├── adr/ - Architecture decisions
907
+ ├── decisions/ - Business decisions
908
+ ├── dev-plans/ - Development task breakdowns
909
+ ├── qa/ - Test cases
910
+ ├── sprints/ - Sprint management
911
+ └── epics/ - Epic specifications
912
+ `);
913
+
914
+ // IDE Integration Info
915
+ if (ide === 'vscode') {
916
+ console.log(`${colored('🖥️ VS Code Integration:', colors.bold)}`);
917
+ console.log(` ✓ Prompt templates generated in .github/prompts/`);
918
+ console.log(` ℹ Use with GitHub Copilot, Cursor, or your preferred AI assistant`);
919
+ console.log('');
920
+ } else if (ide === 'cursor') {
921
+ console.log(`${colored('🖥️ Cursor Integration:', colors.bold)}`);
922
+ console.log(` ✓ Prompt templates generated in .github/prompts/`);
923
+ console.log(` ℹ VS Code settings configured for Cursor compatibility`);
924
+ console.log('');
925
+ }
926
+
927
+ console.log(`${colored('🚀 Next Steps:', colors.bold + colors.yellow)}
928
+
929
+ ${colored('1. Configure Your Team', colors.cyan)}
930
+ Edit ${colored('.teamspec/context/team.yml', colors.bold)} to set your tech stack.
931
+
932
+ ${colored('2. Create Your First Feature', colors.cyan)}
933
+ Features are NEVER created implicitly. Use:
934
+ ${colored('ts:ba feature', colors.bold)} - Create feature
935
+
936
+ ${colored('3. Start Using TeamSpec Commands', colors.cyan)}
937
+ ${colored('ts:ba create', colors.bold)} - Create business analysis
938
+ ${colored('ts:fa story', colors.bold)} - Create user story
939
+ ${colored('ts:dev plan', colors.bold)} - Create development plan
940
+ ${colored('ts:qa test', colors.bold)} - Design test cases
941
+
942
+ Or use templates in .github/prompts/ with GitHub Copilot, Cursor, or Claude
943
+
944
+ ${colored('📚 Documentation:', colors.bold)}
945
+ - Templates: .teamspec/templates/
946
+ - Definitions: .teamspec/definitions/
947
+ `);
948
+ }
949
+
950
+ function printUpdateSummary(coreResult) {
951
+ const pkg = require('../package.json');
952
+
953
+ console.log(`\n${colored('='.repeat(70), colors.green)} `);
954
+ console.log(colored(' ✅ TeamSpec updated successfully!', colors.green + colors.bold));
955
+ console.log(colored('='.repeat(70), colors.green));
956
+
957
+ console.log(`\n${colored('Version:', colors.bold)} ${pkg.version} `);
958
+
959
+ console.log(`\n${colored('Updated:', colors.bold)} `);
960
+ for (const item of coreResult.updated) {
961
+ console.log(` ✓ ${item} `);
962
+ }
963
+
964
+ if (coreResult.skipped.length > 0) {
965
+ console.log(`\n${colored('Preserved:', colors.bold)} `);
966
+ for (const item of coreResult.skipped) {
967
+ console.log(` ⏭ ${item} `);
968
+ }
969
+ }
970
+ }
971
+
972
+ // =============================================================================
973
+ // Main Entry Point
974
+ // =============================================================================
975
+
976
+ async function run(args) {
977
+ const options = parseArgs(args);
978
+
979
+ if (options.help) {
980
+ printHelp();
981
+ return;
982
+ }
983
+
984
+ if (options.version) {
985
+ printVersion();
986
+ return;
987
+ }
988
+
989
+ // Handle lint command
990
+ if (options.command === 'lint') {
991
+ const { Linter, SEVERITY } = require('./linter');
992
+ const targetDir = path.resolve(options.target);
993
+
994
+ console.log(`\n${colored('TeamSpec Linter', colors.bold + colors.cyan)} `);
995
+ console.log(`${colored('Scanning:', colors.bold)} ${targetDir} `);
996
+
997
+ if (options.project) {
998
+ console.log(`${colored('Project:', colors.bold)} ${options.project} `);
999
+ }
1000
+
1001
+ const linter = new Linter(targetDir);
1002
+ const results = await linter.run({ project: options.project });
1003
+
1004
+ console.log(linter.formatResults(results));
1005
+
1006
+ // Exit with error code if there are errors or blockers
1007
+ const hasErrors = results.some(r => r.severity === SEVERITY.ERROR || r.severity === SEVERITY.BLOCKER);
1008
+ if (hasErrors) {
1009
+ process.exit(1);
1010
+ }
1011
+ return;
1012
+ }
1013
+
1014
+ // Handle generate-prompts command
1015
+ if (options.command === 'generate-prompts') {
1016
+ const { generateAllPrompts } = require('./prompt-generator');
1017
+ const targetDir = path.resolve(options.target);
1018
+
1019
+ console.log(`\n${colored('TeamSpec Copilot Prompt Generator', colors.bold + colors.cyan)} `);
1020
+ console.log(`${colored('Target:', colors.bold)} ${targetDir} \n`);
1021
+
1022
+ try {
1023
+ generateAllPrompts(targetDir);
1024
+ console.log(`\n${colored('✨ Done!', colors.green + colors.bold)} `);
1025
+ console.log(`\nYou can now use ${colored('ts:role-command', colors.cyan)} in GitHub Copilot Chat.`);
1026
+ console.log(`Example: ${colored('ts:ba-project', colors.cyan)} or ${colored('ts:fa-story', colors.cyan)} `);
1027
+ } catch (error) {
1028
+ console.error(colored(`❌ Error: ${error.message} `, colors.red));
1029
+ process.exit(1);
1030
+ }
1031
+ return;
1032
+ }
1033
+
1034
+ // Handle update command
1035
+ if (options.command === 'update') {
1036
+ const targetDir = path.resolve(options.target);
1037
+ const teamspecDir = path.join(targetDir, '.teamspec');
1038
+
1039
+ if (!fs.existsSync(teamspecDir)) {
1040
+ console.error(colored(`❌ TeamSpec not found in: ${targetDir} `, colors.red));
1041
+ console.error('Run `teamspec init` first.');
1042
+ process.exit(1);
1043
+ }
1044
+
1045
+ const pkg = require('../package.json');
1046
+ console.log(`\n${colored('TeamSpec Update', colors.bold + colors.cyan)} v${pkg.version} `);
1047
+
1048
+ if (!options.force && !options.nonInteractive) {
1049
+ const rl = createReadlineInterface();
1050
+ const proceed = await promptYesNo(
1051
+ rl,
1052
+ `${colored('Update core files (preserves team.yml)?', colors.bold)} `,
1053
+ true
1054
+ );
1055
+ rl.close();
1056
+ if (!proceed) {
1057
+ console.log('Cancelled.');
1058
+ process.exit(0);
1059
+ }
1060
+ }
1061
+
1062
+ const sourceDir = getTeamspecCoreDir();
1063
+ if (!fs.existsSync(sourceDir)) {
1064
+ console.error(colored(`Error: Core files not found at: ${sourceDir} `, colors.red));
1065
+ process.exit(1);
1066
+ }
1067
+
1068
+ const coreResult = updateTeamspecCore(targetDir, sourceDir);
1069
+
1070
+ // Update copilot instructions if they exist
1071
+ const copilotPath = path.join(targetDir, '.github', 'copilot-instructions.md');
1072
+ if (fs.existsSync(copilotPath)) {
1073
+ console.log(`\n${colored('Updating GitHub Copilot instructions...', colors.blue)}`);
1074
+ copyCopilotInstructions(targetDir, sourceDir);
1075
+ }
1076
+
1077
+ // Regenerate prompt files if prompts folder exists
1078
+ const promptsPath = path.join(targetDir, '.github', 'prompts');
1079
+ if (fs.existsSync(promptsPath)) {
1080
+ console.log(`\n${colored('Regenerating prompt files...', colors.blue)}`);
1081
+ // Delete old prompts folder to remove obsolete files
1082
+ fs.rmSync(promptsPath, { recursive: true });
1083
+ const { generateAllPrompts } = require('./prompt-generator');
1084
+ try {
1085
+ const generatedFiles = generateAllPrompts(targetDir);
1086
+ console.log(` ✓ Regenerated ${generatedFiles.length} prompt files`);
1087
+ coreResult.updated.push('.github/prompts/');
1088
+ } catch (error) {
1089
+ console.log(` ⚠ Failed to regenerate prompts: ${error.message}`);
1090
+ }
1091
+ }
1092
+
1093
+ printUpdateSummary(coreResult);
1094
+ return;
1095
+ }
1096
+
1097
+ // Default: init command
1098
+ printBanner();
1099
+
1100
+ const targetDir = path.resolve(options.target);
1101
+
1102
+ if (!fs.existsSync(targetDir)) {
1103
+ console.error(colored(`Error: Directory does not exist: ${targetDir} `, colors.red));
1104
+ process.exit(1);
1105
+ }
1106
+
1107
+ const teamspecDir = path.join(targetDir, '.teamspec');
1108
+ if (fs.existsSync(teamspecDir)) {
1109
+ if (options.nonInteractive) {
1110
+ console.log(colored('TeamSpec exists. Overwriting...', colors.yellow));
1111
+ } else {
1112
+ const rl = createReadlineInterface();
1113
+ const overwrite = await promptYesNo(
1114
+ rl,
1115
+ colored('⚠️ TeamSpec exists. Overwrite?', colors.yellow),
1116
+ false
1117
+ );
1118
+ rl.close();
1119
+ if (!overwrite) {
1120
+ console.log('Aborted.');
1121
+ process.exit(0);
1122
+ }
1123
+ }
1124
+ }
1125
+
1126
+ console.log(`\n${colored('Target:', colors.bold)} ${targetDir} `);
1127
+
1128
+ // Set defaults for non-interactive mode
1129
+ if (options.nonInteractive) {
1130
+ options.profile = options.profile || 'none';
1131
+ options.org = options.org || 'My Organization';
1132
+ options.team = options.team || 'My Team';
1133
+ options.project = options.project || DEFAULT_PROJECT_ID;
1134
+ options.ide = options.ide || 'none';
1135
+ options.copilot = options.copilot !== false; // Default to true unless explicitly set to false
1136
+ options.industry = 'technology';
1137
+ options.cadence = 'scrum';
1138
+ options.sprintLengthDays = 14;
1139
+ } else {
1140
+ await runInteractive(options);
1141
+ }
1142
+
1143
+ // Validate profile
1144
+ if (!PROFILE_OPTIONS[options.profile]) {
1145
+ console.error(colored(`Error: Unknown profile: ${options.profile} `, colors.red));
1146
+ process.exit(1);
1147
+ }
1148
+
1149
+ options.project = normalizeProjectId(options.project);
1150
+
1151
+ console.log(`\n${colored('Initializing TeamSpec...', colors.bold)} `);
1152
+
1153
+ const sourceDir = getTeamspecCoreDir();
1154
+ if (!fs.existsSync(sourceDir)) {
1155
+ console.error(colored(`Error: Core files not found at: ${sourceDir} `, colors.red));
1156
+ process.exit(1);
1157
+ }
1158
+
1159
+ copyTeamspecCore(targetDir, sourceDir);
1160
+ createTeamContext(targetDir, options);
1161
+ createProjectStructure(targetDir, options.project);
1162
+
1163
+ // Copy GitHub Copilot instructions if requested
1164
+ if (options.copilot !== false) {
1165
+ copyCopilotInstructions(targetDir, sourceDir);
1166
+ }
1167
+
1168
+ // Setup IDE integration
1169
+ const ideResult = await setupIDEIntegration(targetDir, options);
1170
+
1171
+ printNextSteps(targetDir, options.profile, options.project, options.ide, ideResult, options.copilot);
1172
+ }
1173
+
1174
+ module.exports = { run };