teamspec 3.2.0 ā 4.0.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.
- package/README.md +24 -12
- package/bin/teamspec-init.js +2 -2
- package/lib/cli.js +653 -99
- package/lib/linter.js +823 -1076
- package/lib/prompt-generator.js +312 -330
- package/lib/structure-loader.js +400 -0
- package/package.json +14 -6
- package/teamspec-core/FOLDER_STRUCTURE.yml +131 -0
- package/teamspec-core/agents/AGENT_BA.md +188 -293
- package/teamspec-core/agents/AGENT_BOOTSTRAP.md +197 -102
- package/teamspec-core/agents/AGENT_DES.md +9 -8
- package/teamspec-core/agents/AGENT_DEV.md +68 -67
- package/teamspec-core/agents/AGENT_FA.md +437 -245
- package/teamspec-core/agents/AGENT_FIX.md +344 -74
- package/teamspec-core/agents/AGENT_PO.md +487 -0
- package/teamspec-core/agents/AGENT_QA.md +124 -98
- package/teamspec-core/agents/AGENT_SA.md +143 -84
- package/teamspec-core/agents/AGENT_SM.md +106 -83
- package/teamspec-core/agents/README.md +143 -93
- package/teamspec-core/copilot-instructions.md +281 -205
- package/teamspec-core/definitions/definition-of-done.md +47 -84
- package/teamspec-core/definitions/definition-of-ready.md +35 -60
- package/teamspec-core/registry.yml +898 -0
- package/teamspec-core/teamspec.yml +44 -28
- package/teamspec-core/templates/README.md +5 -5
- package/teamspec-core/templates/adr-template.md +19 -17
- package/teamspec-core/templates/bai-template.md +125 -0
- package/teamspec-core/templates/bug-report-template.md +21 -15
- package/teamspec-core/templates/business-analysis-template.md +16 -13
- package/teamspec-core/templates/decision-log-template.md +26 -22
- package/teamspec-core/templates/dev-plan-template.md +168 -0
- package/teamspec-core/templates/epic-template.md +204 -0
- package/teamspec-core/templates/feature-increment-template.md +84 -0
- package/teamspec-core/templates/feature-template.md +45 -32
- package/teamspec-core/templates/increments-index-template.md +53 -0
- package/teamspec-core/templates/product-template.yml +44 -0
- package/teamspec-core/templates/products-index-template.md +46 -0
- package/teamspec-core/templates/project-template.yml +70 -0
- package/teamspec-core/templates/ri-template.md +225 -0
- package/teamspec-core/templates/rt-template.md +104 -0
- package/teamspec-core/templates/sd-template.md +132 -0
- package/teamspec-core/templates/sdi-template.md +119 -0
- package/teamspec-core/templates/sprint-template.md +17 -15
- package/teamspec-core/templates/story-template-v4.md +202 -0
- package/teamspec-core/templates/story-template.md +48 -90
- package/teamspec-core/templates/ta-template.md +198 -0
- package/teamspec-core/templates/tai-template.md +131 -0
- package/teamspec-core/templates/tc-template.md +145 -0
- package/teamspec-core/templates/testcases-template.md +20 -17
- package/extensions/teamspec-0.1.0.vsix +0 -0
- package/lib/extension-installer.js +0 -236
package/lib/prompt-generator.js
CHANGED
|
@@ -1,321 +1,174 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TeamSpec Copilot
|
|
2
|
+
* TeamSpec Copilot Prompt Generator
|
|
3
3
|
*
|
|
4
4
|
* Generates GitHub Copilot prompt files for all TeamSpec commands.
|
|
5
|
+
* Commands and roles are loaded from registry.yml to ensure consistency.
|
|
6
|
+
*
|
|
7
|
+
* Prompts are kept minimal and reference agent files for detailed guidance.
|
|
8
|
+
*
|
|
5
9
|
* Usage: teamspec generate-prompts
|
|
10
|
+
* Version: 4.0
|
|
6
11
|
*/
|
|
7
12
|
|
|
8
13
|
const fs = require('fs');
|
|
9
14
|
const path = require('path');
|
|
15
|
+
const { loadRegistry, getCommandsFromRegistry } = require('./structure-loader');
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// ROLE EXTRACTION FROM REGISTRY
|
|
19
|
+
// =============================================================================
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
description: 'Define an epic',
|
|
31
|
-
prompt: `Execute epic creation workflow:
|
|
32
|
-
1. Identify epic scope and goal
|
|
33
|
-
2. Link to project
|
|
34
|
-
3. Break down into candidate features
|
|
35
|
-
4. Create epic file in epics/ folder
|
|
36
|
-
Wait for user confirmation before creating files.`
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: 'feature',
|
|
40
|
-
description: 'Create feature file',
|
|
41
|
-
prompt: `Execute feature creation workflow:
|
|
42
|
-
1. Gather feature requirements (purpose, value, scope)
|
|
43
|
-
2. Define personas/actors
|
|
44
|
-
3. Create feature file in features/ folder using F-XXX-name.md format
|
|
45
|
-
4. Update features-index.md
|
|
46
|
-
Ensure feature is implementation-agnostic.
|
|
47
|
-
Wait for user confirmation before creating files.`
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: 'decision',
|
|
51
|
-
description: 'Log business decision',
|
|
52
|
-
prompt: `Log a business decision:
|
|
53
|
-
1. Capture decision context
|
|
54
|
-
2. Document options considered
|
|
55
|
-
3. Record rationale
|
|
56
|
-
4. Link to affected features
|
|
57
|
-
5. Create decision file in decisions/ folder using DECISION-XXX-name.md format`
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: 'analysis',
|
|
61
|
-
description: 'Create business analysis document',
|
|
62
|
-
prompt: `Create a business analysis document to describe business processes:
|
|
63
|
-
|
|
64
|
-
1. **Understand the business domain** - Ask about:
|
|
65
|
-
- What business problem needs to be analyzed?
|
|
66
|
-
- Who are the stakeholders?
|
|
67
|
-
- What business processes are involved?
|
|
68
|
-
|
|
69
|
-
2. **Document Current State (As-Is)**
|
|
70
|
-
- How do users solve the problem today?
|
|
71
|
-
- What are the pain points?
|
|
72
|
-
- What processes exist?
|
|
73
|
-
|
|
74
|
-
3. **Define Future State (To-Be)**
|
|
75
|
-
- What will the improved workflow look like?
|
|
76
|
-
- What business rules apply?
|
|
77
|
-
- What are the success metrics?
|
|
78
|
-
|
|
79
|
-
4. **Create business analysis document** using templates/business-analysis-template.md
|
|
80
|
-
- Place in analysis/ folder (create if needed)
|
|
81
|
-
- Name format: BA-XXX-description.md
|
|
82
|
-
|
|
83
|
-
5. **Link to related artifacts**
|
|
84
|
-
- Identify candidate features that will come from this analysis
|
|
85
|
-
- Note decisions that need to be made
|
|
86
|
-
- Identify risks and constraints
|
|
87
|
-
|
|
88
|
-
ā ļø This is a PLANNING artifact - Feature Canon becomes Source of Truth after features are created.`
|
|
89
|
-
}
|
|
90
|
-
]
|
|
91
|
-
},
|
|
92
|
-
fa: {
|
|
93
|
-
name: 'Functional Analyst',
|
|
94
|
-
commands: [
|
|
95
|
-
{
|
|
96
|
-
name: 'story',
|
|
97
|
-
description: 'Create a new story',
|
|
98
|
-
prompt: `Create a story as a DELTA to the Feature Canon:
|
|
99
|
-
1. Identify the linked feature (REQUIRED)
|
|
100
|
-
2. Document BEFORE state (reference Canon)
|
|
101
|
-
3. Document AFTER state (the delta)
|
|
102
|
-
4. Write testable Acceptance Criteria
|
|
103
|
-
5. Mark impact type (Adds/Changes/Fixes/Removes)
|
|
104
|
-
6. Create story in stories/backlog/ using S-XXX-name.md format
|
|
105
|
-
NEVER create a story without a feature link.`
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
name: 'slice',
|
|
109
|
-
description: 'Slice feature into stories',
|
|
110
|
-
prompt: `Slice a feature into implementable stories:
|
|
111
|
-
1. Read the Feature Canon entry
|
|
112
|
-
2. Identify discrete behavior changes
|
|
113
|
-
3. Create story deltas for each change
|
|
114
|
-
4. Ensure each story is independently deliverable
|
|
115
|
-
5. Link all stories to the feature
|
|
116
|
-
Create files in stories/backlog/`
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
name: 'refine',
|
|
120
|
-
description: 'Move story to ready-to-refine',
|
|
121
|
-
prompt: `Refine a story for development:
|
|
122
|
-
1. Verify feature link exists
|
|
123
|
-
2. Check Before/After delta is clear
|
|
124
|
-
3. Validate ACs are testable
|
|
125
|
-
4. Move file from stories/backlog/ to stories/ready-to-refine/`
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
name: 'sync',
|
|
129
|
-
description: 'Update Feature Canon after story completion',
|
|
130
|
-
prompt: `CRITICAL: Canon sync workflow:
|
|
131
|
-
1. Identify completed story
|
|
132
|
-
2. Check impact type (Adds/Changes Behavior?)
|
|
133
|
-
3. Update Feature Canon sections in features/
|
|
134
|
-
4. Add Change Log entry with story reference
|
|
135
|
-
5. Update story-ledger.md
|
|
136
|
-
6. Verify DoD checkbox is checked
|
|
137
|
-
A story CANNOT be Done until Canon is synchronized.`
|
|
138
|
-
}
|
|
139
|
-
]
|
|
140
|
-
},
|
|
141
|
-
arch: {
|
|
142
|
-
name: 'Solution Architect',
|
|
143
|
-
commands: [
|
|
144
|
-
{
|
|
145
|
-
name: 'adr',
|
|
146
|
-
description: 'Create Architecture Decision Record',
|
|
147
|
-
prompt: `Create an ADR:
|
|
148
|
-
1. Identify technical decision
|
|
149
|
-
2. Document context and options
|
|
150
|
-
3. Record decision and rationale
|
|
151
|
-
4. Link to affected features
|
|
152
|
-
5. Create ADR file in adr/ folder using ADR-XXX-name.md format`
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
},
|
|
156
|
-
dev: {
|
|
157
|
-
name: 'Developer',
|
|
158
|
-
commands: [
|
|
159
|
-
{
|
|
160
|
-
name: 'plan',
|
|
161
|
-
description: 'Create development plan',
|
|
162
|
-
prompt: `Create a development plan:
|
|
163
|
-
1. Read the story and linked feature
|
|
164
|
-
2. Break down into implementation tasks
|
|
165
|
-
3. Estimate effort for each task
|
|
166
|
-
4. Identify dependencies and risks
|
|
167
|
-
5. Create dev plan in dev-plans/ using story-XXX-tasks.md format`
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
name: 'implement',
|
|
171
|
-
description: 'Execute implementation',
|
|
172
|
-
prompt: `Execute implementation:
|
|
173
|
-
1. Load dev plan
|
|
174
|
-
2. Work through tasks sequentially
|
|
175
|
-
3. Update task completion status
|
|
176
|
-
4. Create/modify code files
|
|
177
|
-
5. Track actual vs estimated effort`
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
name: 'ready',
|
|
181
|
-
description: 'Move story to ready-for-development',
|
|
182
|
-
prompt: `Move story to ready-for-development:
|
|
183
|
-
1. Verify dev plan exists
|
|
184
|
-
2. Check DoR criteria
|
|
185
|
-
3. Move file from stories/ready-to-refine/ to stories/ready-for-development/`
|
|
186
|
-
}
|
|
187
|
-
]
|
|
188
|
-
},
|
|
189
|
-
qa: {
|
|
190
|
-
name: 'QA Engineer',
|
|
191
|
-
commands: [
|
|
192
|
-
{
|
|
193
|
-
name: 'test',
|
|
194
|
-
description: 'Design test cases',
|
|
195
|
-
prompt: `Design test cases:
|
|
196
|
-
1. Read feature specification
|
|
197
|
-
2. Identify test scenarios
|
|
198
|
-
3. Write test cases with steps and expected results
|
|
199
|
-
4. Create test file in qa/test-cases/ using F-XXX-test-cases.md format`
|
|
200
|
-
},
|
|
201
|
-
{
|
|
202
|
-
name: 'bug',
|
|
203
|
-
description: 'File bug report',
|
|
204
|
-
prompt: `File a bug report:
|
|
205
|
-
1. Capture bug details
|
|
206
|
-
2. Document reproduction steps
|
|
207
|
-
3. Classify severity
|
|
208
|
-
4. Link to affected feature
|
|
209
|
-
5. Create bug file in bugs/ folder`
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
name: 'dor-check',
|
|
213
|
-
description: 'Validate Definition of Ready',
|
|
214
|
-
prompt: `Check Definition of Ready:
|
|
215
|
-
1. Verify feature link exists
|
|
216
|
-
2. Check Before/After delta is clear
|
|
217
|
-
3. Validate ACs are testable
|
|
218
|
-
4. Confirm no TBD/placeholder content
|
|
219
|
-
5. Check estimate is assigned
|
|
220
|
-
Report any gaps.`
|
|
221
|
-
}
|
|
222
|
-
]
|
|
223
|
-
},
|
|
224
|
-
sm: {
|
|
225
|
-
name: 'Scrum Master',
|
|
226
|
-
commands: [
|
|
227
|
-
{
|
|
228
|
-
name: 'sprint-create',
|
|
229
|
-
description: 'Create new sprint',
|
|
230
|
-
prompt: `Create new sprint:
|
|
231
|
-
1. Determine sprint number
|
|
232
|
-
2. Create sprint folder: sprints/sprint-N/
|
|
233
|
-
3. Create sprint-goal.md
|
|
234
|
-
4. Update active-sprint.md`
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
name: 'sprint-plan',
|
|
238
|
-
description: 'Plan sprint backlog',
|
|
239
|
-
prompt: `Plan sprint backlog:
|
|
240
|
-
1. Review ready-for-development stories
|
|
241
|
-
2. Calculate team capacity
|
|
242
|
-
3. Select stories for sprint
|
|
243
|
-
4. Move story files to sprints/sprint-N/
|
|
244
|
-
5. Update sprint-goal.md`
|
|
245
|
-
},
|
|
246
|
-
{
|
|
247
|
-
name: 'sprint-status',
|
|
248
|
-
description: 'Sprint status report',
|
|
249
|
-
prompt: `Generate sprint status:
|
|
250
|
-
1. Count stories by status
|
|
251
|
-
2. Calculate burndown
|
|
252
|
-
3. Identify blockers
|
|
253
|
-
4. Report health metrics`
|
|
254
|
-
}
|
|
255
|
-
]
|
|
256
|
-
},
|
|
257
|
-
utility: {
|
|
258
|
-
name: 'Utility',
|
|
259
|
-
commands: [
|
|
260
|
-
{
|
|
261
|
-
name: 'fix',
|
|
262
|
-
description: 'Auto-fix linter errors',
|
|
263
|
-
prompt: `Auto-fix TeamSpec linter errors:
|
|
264
|
-
1. Run \`teamspec lint\` to identify errors
|
|
265
|
-
2. Review the linter output
|
|
266
|
-
3. Apply fixes for each error category
|
|
267
|
-
4. Re-run linter to verify fixes
|
|
268
|
-
|
|
269
|
-
Supports: TS-PROJ, TS-FEAT, TS-STORY, TS-ADR, TS-DEVPLAN, TS-DOD, TS-NAMING rules.`
|
|
270
|
-
}
|
|
271
|
-
]
|
|
21
|
+
/**
|
|
22
|
+
* Extract roles from registry.yml
|
|
23
|
+
* @param {Object} registry - Parsed registry object
|
|
24
|
+
* @returns {Object} - Roles keyed by role ID
|
|
25
|
+
*/
|
|
26
|
+
function extractRoles(registry) {
|
|
27
|
+
const roles = {};
|
|
28
|
+
|
|
29
|
+
if (registry?.roles) {
|
|
30
|
+
for (const [roleId, roleData] of Object.entries(registry.roles)) {
|
|
31
|
+
roles[roleId] = {
|
|
32
|
+
id: roleId,
|
|
33
|
+
name: roleData.name || roleId,
|
|
34
|
+
agent: `AGENT_${roleId}`,
|
|
35
|
+
owns: roleData.owns || [],
|
|
36
|
+
creates: roleData.creates || [],
|
|
37
|
+
commands: roleData.commands || []
|
|
38
|
+
};
|
|
39
|
+
}
|
|
272
40
|
}
|
|
273
|
-
|
|
41
|
+
|
|
42
|
+
return roles;
|
|
43
|
+
}
|
|
274
44
|
|
|
275
45
|
/**
|
|
276
|
-
*
|
|
46
|
+
* Get role info from registry roles
|
|
47
|
+
* @param {Object} roles - Extracted roles
|
|
48
|
+
* @param {string} roleKey - Role key (e.g., 'PO', 'BA')
|
|
49
|
+
* @returns {Object} - Role info with defaults
|
|
277
50
|
*/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
51
|
+
function getRoleInfo(roles, roleKey) {
|
|
52
|
+
const key = roleKey?.toUpperCase();
|
|
53
|
+
return roles[key] || {
|
|
54
|
+
id: key || 'ANY',
|
|
55
|
+
name: 'TeamSpec',
|
|
56
|
+
agent: 'AGENT_BOOTSTRAP',
|
|
57
|
+
owns: [],
|
|
58
|
+
creates: [],
|
|
59
|
+
commands: []
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// =============================================================================
|
|
64
|
+
// COMMAND EXTRACTION FROM REGISTRY
|
|
65
|
+
// =============================================================================
|
|
287
66
|
|
|
288
67
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
68
|
+
* Group commands by role
|
|
69
|
+
* @param {Array} commands - Commands from registry
|
|
70
|
+
* @returns {Object} - Commands grouped by role
|
|
292
71
|
*/
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const
|
|
297
|
-
|
|
72
|
+
function groupCommandsByRole(commands) {
|
|
73
|
+
const byRole = {};
|
|
74
|
+
|
|
75
|
+
for (const cmd of commands) {
|
|
76
|
+
if (cmd.status === 'REMOVED') continue;
|
|
77
|
+
|
|
78
|
+
const role = cmd.role || 'Any';
|
|
79
|
+
if (!byRole[role]) byRole[role] = [];
|
|
80
|
+
byRole[role].push(cmd);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return byRole;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// =============================================================================
|
|
87
|
+
// PROMPT FILE GENERATION
|
|
88
|
+
// =============================================================================
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Sanitize a string for use in filenames
|
|
92
|
+
*/
|
|
93
|
+
function sanitizeFilename(str) {
|
|
94
|
+
return str
|
|
95
|
+
.replace(/<[^>]+>/g, '') // Remove angle bracket patterns like <role>
|
|
96
|
+
.replace(/[\\/:*?"<>|]/g, '') // Remove Windows-invalid chars
|
|
97
|
+
.replace(/\s+/g, '-') // Replace spaces with dashes
|
|
98
|
+
.replace(/-+/g, '-') // Collapse multiple dashes
|
|
99
|
+
.replace(/^-|-$/g, ''); // Remove leading/trailing dashes
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse command invocation to extract role and action
|
|
104
|
+
* @param {string} invocation - Command invocation (e.g., "ts:po product")
|
|
105
|
+
* @returns {Object|null} - Parsed command parts or null if invalid
|
|
106
|
+
*/
|
|
107
|
+
function parseInvocation(invocation) {
|
|
108
|
+
const match = invocation.match(/^ts:(\w+)(?:\s+(.+))?$/);
|
|
109
|
+
if (!match) return null;
|
|
110
|
+
|
|
111
|
+
const [, roleOrCmd, action] = match;
|
|
112
|
+
|
|
113
|
+
// Skip commands with placeholders
|
|
114
|
+
if (action && action.includes('<')) return null;
|
|
115
|
+
|
|
116
|
+
return { roleOrCmd, action };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Generate a clean, minimal GitHub Copilot prompt file
|
|
121
|
+
* The prompt references the agent file for detailed guidance.
|
|
122
|
+
*/
|
|
123
|
+
function generatePromptFile(command, outputDir, roles) {
|
|
124
|
+
const parsed = parseInvocation(command.invocation);
|
|
125
|
+
if (!parsed) return null;
|
|
126
|
+
|
|
127
|
+
const { roleOrCmd, action } = parsed;
|
|
128
|
+
const role = getRoleInfo(roles, roleOrCmd);
|
|
129
|
+
|
|
130
|
+
// Determine filename
|
|
131
|
+
let filename, commandName;
|
|
132
|
+
if (action) {
|
|
133
|
+
const sanitizedAction = sanitizeFilename(action);
|
|
134
|
+
filename = `${roleOrCmd}-${sanitizedAction}.prompt.md`;
|
|
135
|
+
commandName = `ts:${roleOrCmd} ${action}`;
|
|
136
|
+
} else {
|
|
137
|
+
filename = `${roleOrCmd}.prompt.md`;
|
|
138
|
+
commandName = `ts:${roleOrCmd}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
298
141
|
const filepath = path.join(outputDir, filename);
|
|
299
|
-
const commandName = isUtility ? `ts:${command.name}` : `ts:${role}-${command.name}`;
|
|
300
|
-
const agentFile = ROLE_TO_AGENT[role] || `AGENT_${role.toUpperCase()}`;
|
|
301
142
|
|
|
302
|
-
//
|
|
303
|
-
// VS Code will include the linked file as context automatically
|
|
143
|
+
// Build clean, minimal prompt content
|
|
304
144
|
const content = `---
|
|
305
|
-
name: "
|
|
306
|
-
description: "
|
|
307
|
-
agent: "agent"
|
|
145
|
+
name: "ts:${filename.replace('.prompt.md', '').replace(/-/g, ' ')}"
|
|
146
|
+
description: "${command.purpose || `${role.name} command`}"
|
|
308
147
|
---
|
|
309
148
|
|
|
310
|
-
# ${command.
|
|
149
|
+
# ${command.purpose || `${role.name} - ${action || roleOrCmd}`}
|
|
311
150
|
|
|
312
|
-
|
|
151
|
+
You are acting as **${role.name}**.
|
|
313
152
|
|
|
314
|
-
|
|
153
|
+
## Command
|
|
315
154
|
|
|
316
|
-
|
|
155
|
+
\`${commandName}\`
|
|
317
156
|
|
|
318
|
-
|
|
157
|
+
## Agent Instructions
|
|
158
|
+
|
|
159
|
+
See full role instructions and command details in:
|
|
160
|
+
- [${role.agent}.md](../../.teamspec/agents/${role.agent}.md)
|
|
161
|
+
|
|
162
|
+
${command.output ? `## Output
|
|
163
|
+
|
|
164
|
+
${command.output}
|
|
165
|
+
` : ''}
|
|
166
|
+
${command.precondition ? `## Precondition
|
|
167
|
+
|
|
168
|
+
${command.precondition}
|
|
169
|
+
` : ''}
|
|
170
|
+
---
|
|
171
|
+
*Command from registry.yml - TeamSpec 4.0*
|
|
319
172
|
`;
|
|
320
173
|
|
|
321
174
|
fs.writeFileSync(filepath, content, 'utf-8');
|
|
@@ -323,32 +176,86 @@ ${command.prompt}
|
|
|
323
176
|
}
|
|
324
177
|
|
|
325
178
|
/**
|
|
326
|
-
* Generate all prompt files
|
|
179
|
+
* Generate all prompt files from registry
|
|
327
180
|
*/
|
|
328
181
|
function generateAllPrompts(targetDir = process.cwd()) {
|
|
329
182
|
const outputDir = path.join(targetDir, '.github', 'prompts');
|
|
330
183
|
|
|
331
184
|
// Create output directory
|
|
332
|
-
|
|
333
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
334
|
-
}
|
|
185
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
335
186
|
|
|
336
187
|
console.log('š Generating GitHub Copilot prompt files...\n');
|
|
337
188
|
|
|
189
|
+
// Load registry and extract data
|
|
190
|
+
const registry = loadRegistry(targetDir);
|
|
191
|
+
const roles = extractRoles(registry);
|
|
192
|
+
const commands = getCommandsFromRegistry(registry);
|
|
193
|
+
const byRole = groupCommandsByRole(commands);
|
|
194
|
+
|
|
338
195
|
const generated = [];
|
|
196
|
+
const generatedByRole = {};
|
|
197
|
+
|
|
198
|
+
// Generate prompts for each command
|
|
199
|
+
for (const command of commands) {
|
|
200
|
+
if (command.status === 'REMOVED') continue;
|
|
339
201
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
console.log(`š ${config.name}:`);
|
|
343
|
-
for (const command of config.commands) {
|
|
344
|
-
const filename = generatePromptFile(role, command, outputDir);
|
|
202
|
+
const filename = generatePromptFile(command, outputDir, roles);
|
|
203
|
+
if (filename) {
|
|
345
204
|
generated.push(filename);
|
|
205
|
+
|
|
206
|
+
// Track by role for summary
|
|
207
|
+
const role = command.role || 'Any';
|
|
208
|
+
if (!generatedByRole[role]) generatedByRole[role] = [];
|
|
209
|
+
generatedByRole[role].push({ filename, command });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Print summary by role
|
|
214
|
+
for (const [roleKey, cmds] of Object.entries(generatedByRole)) {
|
|
215
|
+
const role = getRoleInfo(roles, roleKey);
|
|
216
|
+
console.log(`š ${role.name}:`);
|
|
217
|
+
for (const { filename } of cmds) {
|
|
346
218
|
console.log(` ā ${filename}`);
|
|
347
219
|
}
|
|
348
220
|
}
|
|
349
221
|
|
|
350
|
-
// Generate index
|
|
351
|
-
|
|
222
|
+
// Generate README index
|
|
223
|
+
generateReadme(outputDir, generatedByRole, roles, registry);
|
|
224
|
+
generated.push('README.md');
|
|
225
|
+
|
|
226
|
+
console.log(`\nā
Generated ${generated.length} files in ${outputDir}`);
|
|
227
|
+
console.log('\nš See .github/prompts/README.md for usage instructions');
|
|
228
|
+
console.log('š” In Copilot Chat, type "/" to see available prompts');
|
|
229
|
+
|
|
230
|
+
return generated;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Generate README index file
|
|
235
|
+
*/
|
|
236
|
+
function generateReadme(outputDir, byRole, roles, registry) {
|
|
237
|
+
let pkgVersion = '4.0';
|
|
238
|
+
try {
|
|
239
|
+
const pkg = require('../package.json');
|
|
240
|
+
pkgVersion = pkg.version;
|
|
241
|
+
} catch (e) {
|
|
242
|
+
// Use default
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const rolesSections = Object.entries(byRole).map(([roleKey, cmds]) => {
|
|
246
|
+
const role = getRoleInfo(roles, roleKey);
|
|
247
|
+
const cmdList = cmds.map(({ filename, command }) => {
|
|
248
|
+
return `- \`/${filename.replace('.prompt.md', '')}\` - ${command.purpose || 'Command'}`;
|
|
249
|
+
}).join('\n');
|
|
250
|
+
|
|
251
|
+
return `
|
|
252
|
+
### ${role.name}
|
|
253
|
+
|
|
254
|
+
${cmdList}
|
|
255
|
+
`;
|
|
256
|
+
}).join('\n');
|
|
257
|
+
|
|
258
|
+
const content = `# TeamSpec Copilot Prompts
|
|
352
259
|
|
|
353
260
|
These prompt files provide structured guidance for TeamSpec commands in GitHub Copilot Chat.
|
|
354
261
|
|
|
@@ -356,50 +263,125 @@ These prompt files provide structured guidance for TeamSpec commands in GitHub C
|
|
|
356
263
|
|
|
357
264
|
In VS Code, type \`/\` in Copilot Chat to see available prompts. Look for prompts starting with \`ts:\`.
|
|
358
265
|
|
|
359
|
-
Alternatively, run any prompt directly:
|
|
360
|
-
- Press \`Ctrl+Shift+P\` (or \`Cmd+Shift+P\` on Mac)
|
|
361
|
-
- Type "Chat: Run Prompt"
|
|
362
|
-
- Select a TeamSpec prompt
|
|
363
|
-
|
|
364
266
|
## Available Commands
|
|
365
267
|
|
|
366
|
-
${
|
|
367
|
-
const isUtility = role === 'utility';
|
|
368
|
-
return `
|
|
369
|
-
### ${config.name}
|
|
370
|
-
|
|
371
|
-
${config.commands.map(cmd => {
|
|
372
|
-
const cmdName = isUtility ? `ts:${cmd.name}` : `ts:${role}-${cmd.name}`;
|
|
373
|
-
return `- \`/${cmdName}\` - ${cmd.description}`;
|
|
374
|
-
}).join('\n')}
|
|
375
|
-
`;
|
|
376
|
-
}).join('\n')}
|
|
268
|
+
${rolesSections}
|
|
377
269
|
|
|
378
270
|
## How It Works
|
|
379
271
|
|
|
380
272
|
1. Type \`/\` in Copilot Chat to see prompts
|
|
381
|
-
2. Select a
|
|
382
|
-
3. Copilot
|
|
273
|
+
2. Select a TeamSpec prompt
|
|
274
|
+
3. Copilot loads role-specific agent instructions
|
|
383
275
|
4. Follow the workflow to create artifacts
|
|
384
276
|
|
|
277
|
+
## Command Reference
|
|
278
|
+
|
|
279
|
+
| Command | Role | Purpose |
|
|
280
|
+
|---------|------|---------|
|
|
281
|
+
${Object.values(byRole).flat().map(({ command }) => {
|
|
282
|
+
const role = getRoleInfo(roles, command.role);
|
|
283
|
+
return `| \`${command.invocation}\` | ${role.name} | ${command.purpose || '-'} |`;
|
|
284
|
+
}).join('\n')}
|
|
285
|
+
|
|
385
286
|
---
|
|
386
287
|
|
|
387
|
-
*Generated by TeamSpec CLI v${
|
|
288
|
+
*Generated by TeamSpec CLI v${pkgVersion}*
|
|
289
|
+
*Source: registry.yml - TeamSpec ${registry?.version || '4.0'}*
|
|
388
290
|
`;
|
|
389
291
|
|
|
390
|
-
fs.writeFileSync(path.join(outputDir, 'README.md'),
|
|
391
|
-
|
|
292
|
+
fs.writeFileSync(path.join(outputDir, 'README.md'), content, 'utf-8');
|
|
293
|
+
}
|
|
392
294
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
295
|
+
// =============================================================================
|
|
296
|
+
// BACKWARDS COMPATIBILITY EXPORTS
|
|
297
|
+
// =============================================================================
|
|
396
298
|
|
|
397
|
-
|
|
299
|
+
/**
|
|
300
|
+
* Get all commands for external use
|
|
301
|
+
*/
|
|
302
|
+
function getCommands(targetDir = process.cwd()) {
|
|
303
|
+
const registry = loadRegistry(targetDir);
|
|
304
|
+
return getCommandsFromRegistry(registry);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get ROLES object (extracted from registry)
|
|
309
|
+
* For backwards compatibility with tests
|
|
310
|
+
*/
|
|
311
|
+
function getRolesFromRegistry(targetDir = process.cwd()) {
|
|
312
|
+
const registry = loadRegistry(targetDir);
|
|
313
|
+
return extractRoles(registry);
|
|
398
314
|
}
|
|
399
315
|
|
|
316
|
+
// Legacy ROLES export - now dynamically loaded
|
|
317
|
+
// Kept for backwards compatibility with tests
|
|
318
|
+
const ROLES = (() => {
|
|
319
|
+
try {
|
|
320
|
+
const registry = loadRegistry(process.cwd());
|
|
321
|
+
return extractRoles(registry);
|
|
322
|
+
} catch (e) {
|
|
323
|
+
// Fallback for when registry not available
|
|
324
|
+
return {
|
|
325
|
+
PO: { id: 'PO', name: 'Product Owner', agent: 'AGENT_PO', owns: [], creates: [], commands: [] },
|
|
326
|
+
BA: { id: 'BA', name: 'Business Analyst', agent: 'AGENT_BA', owns: [], creates: [], commands: [] },
|
|
327
|
+
FA: { id: 'FA', name: 'Functional Analyst', agent: 'AGENT_FA', owns: [], creates: [], commands: [] },
|
|
328
|
+
SA: { id: 'SA', name: 'Solution Architect', agent: 'AGENT_SA', owns: [], creates: [], commands: [] },
|
|
329
|
+
DEV: { id: 'DEV', name: 'Developer', agent: 'AGENT_DEV', owns: [], creates: [], commands: [] },
|
|
330
|
+
QA: { id: 'QA', name: 'QA Engineer', agent: 'AGENT_QA', owns: [], creates: [], commands: [] },
|
|
331
|
+
SM: { id: 'SM', name: 'Scrum Master', agent: 'AGENT_SM', owns: [], creates: [], commands: [] },
|
|
332
|
+
DES: { id: 'DES', name: 'Designer', agent: 'AGENT_DES', owns: [], creates: [], commands: [] }
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
})();
|
|
336
|
+
|
|
337
|
+
// Legacy COMMANDS export - now dynamically built from registry
|
|
338
|
+
// Kept for backwards compatibility with tests
|
|
339
|
+
const COMMANDS = (() => {
|
|
340
|
+
try {
|
|
341
|
+
const registry = loadRegistry(process.cwd());
|
|
342
|
+
const commands = getCommandsFromRegistry(registry);
|
|
343
|
+
const roles = extractRoles(registry);
|
|
344
|
+
const byRole = groupCommandsByRole(commands);
|
|
345
|
+
|
|
346
|
+
const result = {};
|
|
347
|
+
for (const [roleKey, cmds] of Object.entries(byRole)) {
|
|
348
|
+
const role = getRoleInfo(roles, roleKey);
|
|
349
|
+
const normalizedKey = roleKey.toLowerCase() === 'any' ? 'utility' : roleKey.toLowerCase();
|
|
350
|
+
|
|
351
|
+
result[normalizedKey] = {
|
|
352
|
+
name: role.name,
|
|
353
|
+
commands: cmds.map(cmd => ({
|
|
354
|
+
name: cmd.invocation.replace(/^ts:\w+\s*/, '').trim() || cmd.id.split('.').pop(),
|
|
355
|
+
description: cmd.purpose || '',
|
|
356
|
+
prompt: '' // No longer embedding workflow in prompt
|
|
357
|
+
}))
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return result;
|
|
362
|
+
} catch (e) {
|
|
363
|
+
// Fallback for when registry not available
|
|
364
|
+
return {
|
|
365
|
+
ba: { name: 'Business Analyst', commands: [] },
|
|
366
|
+
fa: { name: 'Functional Analyst', commands: [] },
|
|
367
|
+
po: { name: 'Product Owner', commands: [] },
|
|
368
|
+
sa: { name: 'Solution Architect', commands: [] },
|
|
369
|
+
dev: { name: 'Developer', commands: [] },
|
|
370
|
+
qa: { name: 'QA Engineer', commands: [] },
|
|
371
|
+
sm: { name: 'Scrum Master', commands: [] },
|
|
372
|
+
utility: { name: 'Utility', commands: [] }
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
})();
|
|
376
|
+
|
|
400
377
|
module.exports = {
|
|
401
378
|
generateAllPrompts,
|
|
402
|
-
|
|
379
|
+
getCommands,
|
|
380
|
+
getRolesFromRegistry,
|
|
381
|
+
extractRoles,
|
|
382
|
+
groupCommandsByRole,
|
|
383
|
+
COMMANDS,
|
|
384
|
+
ROLES
|
|
403
385
|
};
|
|
404
386
|
|
|
405
387
|
// CLI execution
|