openplanr 0.8.0 → 0.9.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 (36) hide show
  1. package/README.md +11 -5
  2. package/dist/ai/prompts/prompt-builder.d.ts.map +1 -1
  3. package/dist/ai/prompts/prompt-builder.js +8 -1
  4. package/dist/ai/prompts/prompt-builder.js.map +1 -1
  5. package/dist/ai/prompts/system-prompts.d.ts +1 -1
  6. package/dist/ai/prompts/system-prompts.d.ts.map +1 -1
  7. package/dist/ai/prompts/system-prompts.js +2 -1
  8. package/dist/ai/prompts/system-prompts.js.map +1 -1
  9. package/dist/ai/types.d.ts +1 -1
  10. package/dist/ai/types.js +1 -1
  11. package/dist/cli/commands/epic.d.ts.map +1 -1
  12. package/dist/cli/commands/epic.js +16 -3
  13. package/dist/cli/commands/epic.js.map +1 -1
  14. package/dist/cli/commands/export.d.ts +8 -0
  15. package/dist/cli/commands/export.d.ts.map +1 -0
  16. package/dist/cli/commands/export.js +280 -0
  17. package/dist/cli/commands/export.js.map +1 -0
  18. package/dist/cli/commands/github.d.ts +8 -0
  19. package/dist/cli/commands/github.d.ts.map +1 -0
  20. package/dist/cli/commands/github.js +412 -0
  21. package/dist/cli/commands/github.js.map +1 -0
  22. package/dist/cli/commands/plan.d.ts.map +1 -1
  23. package/dist/cli/commands/plan.js +89 -20
  24. package/dist/cli/commands/plan.js.map +1 -1
  25. package/dist/cli/index.js +4 -0
  26. package/dist/cli/index.js.map +1 -1
  27. package/dist/services/github-service.d.ts +83 -0
  28. package/dist/services/github-service.d.ts.map +1 -0
  29. package/dist/services/github-service.js +435 -0
  30. package/dist/services/github-service.js.map +1 -0
  31. package/dist/services/template-service.d.ts.map +1 -1
  32. package/dist/services/template-service.js +1 -0
  33. package/dist/services/template-service.js.map +1 -1
  34. package/dist/templates/export/planning-report.html.hbs +230 -0
  35. package/dist/templates/export/planning-report.md.hbs +136 -0
  36. package/package.json +1 -1
@@ -0,0 +1,412 @@
1
+ /**
2
+ * `planr github` command.
3
+ *
4
+ * Push planning artifacts to GitHub Issues and sync status bi-directionally.
5
+ */
6
+ import chalk from 'chalk';
7
+ import { findArtifactTypeById, listArtifacts, readArtifact, readArtifactRaw, updateArtifact, } from '../../services/artifact-service.js';
8
+ import { loadConfig } from '../../services/config-service.js';
9
+ import { buildIssueBody, cleanTitle, createIssue, ensureLabel, ensureMilestone, getIssue, getLabelForType, issueStateToStatus, statusToIssueState, updateIssue, verifyGitHubRepo, } from '../../services/github-service.js';
10
+ import { promptSelect } from '../../services/prompt-service.js';
11
+ import { logger } from '../../utils/logger.js';
12
+ import { parseMarkdown, toMarkdownWithFrontmatter } from '../../utils/markdown.js';
13
+ // ---------------------------------------------------------------------------
14
+ // Helpers
15
+ // ---------------------------------------------------------------------------
16
+ /** Inject or update a `githubIssue` field in raw artifact frontmatter. */
17
+ function setFrontmatterField(raw, field, value) {
18
+ const escaped = field.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
19
+ const fieldRegex = new RegExp(`^${escaped}:.*\\n`, 'm');
20
+ if (fieldRegex.test(raw)) {
21
+ return raw.replace(fieldRegex, `${field}: ${value}\n`);
22
+ }
23
+ // Insert before closing ---
24
+ const closingIdx = raw.indexOf('\n---', raw.indexOf('---') + 3);
25
+ if (closingIdx !== -1) {
26
+ return `${raw.slice(0, closingIdx)}\n${field}: ${value}${raw.slice(closingIdx)}`;
27
+ }
28
+ return raw;
29
+ }
30
+ /** Push a single artifact to GitHub as an issue. */
31
+ async function pushSingleArtifact(projectDir, config, artifactId, opts) {
32
+ const type = findArtifactTypeById(artifactId);
33
+ if (!type) {
34
+ logger.error(`Cannot determine artifact type from ID: ${artifactId}`);
35
+ return null;
36
+ }
37
+ const artifact = await readArtifact(projectDir, config, type, artifactId);
38
+ if (!artifact) {
39
+ logger.error(`Artifact not found: ${artifactId}`);
40
+ return null;
41
+ }
42
+ const raw = await readArtifactRaw(projectDir, config, type, artifactId);
43
+ if (!raw)
44
+ return null;
45
+ const title = cleanTitle(artifactId, artifact.data.title);
46
+ const body = buildIssueBody(raw, artifactId, type, artifact.data);
47
+ // Ensure label exists
48
+ const label = getLabelForType(type);
49
+ if (label)
50
+ await ensureLabel(label);
51
+ const labels = label ? [label] : [];
52
+ const existingIssueNumber = artifact.data.githubIssue;
53
+ let issueNumber;
54
+ let issueUrl;
55
+ let action;
56
+ if (existingIssueNumber) {
57
+ // Try to update existing issue — fall back to create if deleted on GitHub
58
+ try {
59
+ const currentIssue = await getIssue(existingIssueNumber);
60
+ await updateIssue(existingIssueNumber, { title, body });
61
+ const status = artifact.data.status || 'pending';
62
+ const targetState = statusToIssueState(status);
63
+ if (currentIssue.state !== targetState) {
64
+ await updateIssue(existingIssueNumber, { state: targetState });
65
+ }
66
+ issueNumber = existingIssueNumber;
67
+ issueUrl = currentIssue.url;
68
+ action = 'updated';
69
+ }
70
+ catch {
71
+ // Issue was deleted on GitHub — create a fresh one
72
+ const result = await createIssue(title, body, labels, opts.milestone);
73
+ issueNumber = result.number;
74
+ issueUrl = result.url;
75
+ action = 'created';
76
+ const updatedRaw = setFrontmatterField(raw, 'githubIssue', issueNumber);
77
+ await updateArtifact(projectDir, config, type, artifactId, updatedRaw);
78
+ }
79
+ }
80
+ else {
81
+ // Create new issue
82
+ const result = await createIssue(title, body, labels, opts.milestone);
83
+ issueNumber = result.number;
84
+ issueUrl = result.url;
85
+ action = 'created';
86
+ // Store issue number in artifact frontmatter
87
+ const updatedRaw = setFrontmatterField(raw, 'githubIssue', issueNumber);
88
+ await updateArtifact(projectDir, config, type, artifactId, updatedRaw);
89
+ }
90
+ return { issueNumber, issueUrl, action };
91
+ }
92
+ /** Collect all artifact IDs under an epic. */
93
+ async function collectEpicArtifacts(projectDir, config, epicId) {
94
+ const ids = [epicId];
95
+ const features = await listArtifacts(projectDir, config, 'feature');
96
+ const stories = await listArtifacts(projectDir, config, 'story');
97
+ const tasks = await listArtifacts(projectDir, config, 'task');
98
+ const epicFeatureIds = new Set();
99
+ for (const f of features) {
100
+ const data = await readArtifact(projectDir, config, 'feature', f.id);
101
+ if (data?.data.epicId === epicId) {
102
+ epicFeatureIds.add(f.id);
103
+ ids.push(f.id);
104
+ }
105
+ }
106
+ const epicStoryIds = new Set();
107
+ for (const s of stories) {
108
+ const data = await readArtifact(projectDir, config, 'story', s.id);
109
+ if (data?.data.featureId && epicFeatureIds.has(data.data.featureId)) {
110
+ epicStoryIds.add(s.id);
111
+ ids.push(s.id);
112
+ }
113
+ }
114
+ for (const t of tasks) {
115
+ const data = await readArtifact(projectDir, config, 'task', t.id);
116
+ const parentStory = data?.data.storyId;
117
+ const parentFeature = data?.data.featureId;
118
+ if ((parentStory && epicStoryIds.has(parentStory)) ||
119
+ (parentFeature && epicFeatureIds.has(parentFeature))) {
120
+ ids.push(t.id);
121
+ }
122
+ }
123
+ return ids;
124
+ }
125
+ // ---------------------------------------------------------------------------
126
+ // Command registration
127
+ // ---------------------------------------------------------------------------
128
+ export function registerGitHubCommand(program) {
129
+ const github = program
130
+ .command('github')
131
+ .description('Sync planning artifacts with GitHub Issues');
132
+ // --- planr github push ---
133
+ github
134
+ .command('push')
135
+ .description('Push artifacts to GitHub Issues')
136
+ .argument('[artifactId]', 'artifact ID to push (e.g., TASK-001)')
137
+ .option('--epic <epicId>', 'push all artifacts under an epic')
138
+ .option('--all', 'push all artifacts across all types')
139
+ .action(async (artifactId, opts) => {
140
+ const projectDir = program.opts().projectDir;
141
+ const config = await loadConfig(projectDir);
142
+ // Verify GitHub repo
143
+ let repoInfo;
144
+ try {
145
+ repoInfo = await verifyGitHubRepo();
146
+ }
147
+ catch (e) {
148
+ logger.error(e.message);
149
+ process.exit(1);
150
+ }
151
+ logger.heading(`GitHub Push — ${repoInfo.owner}/${repoInfo.repo}`);
152
+ console.log('');
153
+ let artifactIds = [];
154
+ if (opts.all) {
155
+ // Push everything
156
+ const types = ['epic', 'feature', 'story', 'task', 'quick'];
157
+ for (const type of types) {
158
+ const artifacts = await listArtifacts(projectDir, config, type);
159
+ for (const a of artifacts)
160
+ artifactIds.push(a.id);
161
+ }
162
+ }
163
+ else if (opts.epic) {
164
+ // Push all under epic, create milestone
165
+ const epic = await readArtifact(projectDir, config, 'epic', opts.epic);
166
+ if (!epic) {
167
+ logger.error(`Epic not found: ${opts.epic}`);
168
+ process.exit(1);
169
+ }
170
+ const milestoneTitle = `${opts.epic}: ${epic.data.title}`;
171
+ try {
172
+ await ensureMilestone(milestoneTitle);
173
+ logger.success(`Milestone: ${milestoneTitle}`);
174
+ }
175
+ catch {
176
+ logger.warn('Could not create milestone, pushing without it');
177
+ }
178
+ artifactIds = await collectEpicArtifacts(projectDir, config, opts.epic);
179
+ }
180
+ else if (artifactId) {
181
+ artifactIds = [artifactId];
182
+ }
183
+ else {
184
+ logger.error('Provide an artifact ID, --epic, or --all');
185
+ logger.dim('Usage: planr github push TASK-001');
186
+ logger.dim(' planr github push --epic EPIC-001');
187
+ logger.dim(' planr github push --all');
188
+ process.exit(1);
189
+ }
190
+ if (artifactIds.length === 0) {
191
+ logger.warn('No artifacts found to push');
192
+ return;
193
+ }
194
+ logger.dim(`Pushing ${artifactIds.length} artifact${artifactIds.length !== 1 ? 's' : ''}...`);
195
+ console.log('');
196
+ let created = 0;
197
+ let updated = 0;
198
+ const milestoneTitle = opts.epic
199
+ ? `${opts.epic}: ${(await readArtifact(projectDir, config, 'epic', opts.epic))?.data.title}`
200
+ : undefined;
201
+ for (const id of artifactIds) {
202
+ try {
203
+ const result = await pushSingleArtifact(projectDir, config, id, {
204
+ milestone: milestoneTitle,
205
+ });
206
+ if (result) {
207
+ const icon = result.action === 'created' ? chalk.green('+') : chalk.yellow('~');
208
+ console.log(` ${icon} ${id} → #${result.issueNumber} (${result.action})`);
209
+ if (result.action === 'created')
210
+ created++;
211
+ else
212
+ updated++;
213
+ }
214
+ }
215
+ catch (e) {
216
+ logger.error(` Failed to push ${id}: ${e.message}`);
217
+ }
218
+ }
219
+ console.log('');
220
+ logger.success(`Done: ${created} created, ${updated} updated`);
221
+ });
222
+ // --- planr github sync ---
223
+ github
224
+ .command('sync')
225
+ .description('Sync artifact status with GitHub Issues (bi-directional)')
226
+ .option('--direction <dir>', 'sync direction: pull (GitHub→local), push (local→GitHub), or both', 'both')
227
+ .action(async (opts) => {
228
+ const projectDir = program.opts().projectDir;
229
+ const config = await loadConfig(projectDir);
230
+ let repoInfo;
231
+ try {
232
+ repoInfo = await verifyGitHubRepo();
233
+ }
234
+ catch (e) {
235
+ logger.error(e.message);
236
+ process.exit(1);
237
+ }
238
+ logger.heading(`GitHub Sync — ${repoInfo.owner}/${repoInfo.repo}`);
239
+ console.log('');
240
+ // Find all artifacts with githubIssue field
241
+ const types = ['epic', 'feature', 'story', 'task', 'quick'];
242
+ const linkedArtifacts = [];
243
+ for (const type of types) {
244
+ const artifacts = await listArtifacts(projectDir, config, type);
245
+ for (const a of artifacts) {
246
+ const data = await readArtifact(projectDir, config, type, a.id);
247
+ if (data?.data.githubIssue) {
248
+ linkedArtifacts.push({
249
+ id: a.id,
250
+ type,
251
+ issueNumber: data.data.githubIssue,
252
+ status: data.data.status || 'pending',
253
+ });
254
+ }
255
+ }
256
+ }
257
+ if (linkedArtifacts.length === 0) {
258
+ logger.warn('No artifacts linked to GitHub Issues. Run `planr github push` first.');
259
+ return;
260
+ }
261
+ logger.dim(`Found ${linkedArtifacts.length} linked artifact${linkedArtifacts.length !== 1 ? 's' : ''}`);
262
+ console.log('');
263
+ let synced = 0;
264
+ const conflicts = [];
265
+ for (const artifact of linkedArtifacts) {
266
+ try {
267
+ const issue = await getIssue(artifact.issueNumber);
268
+ const remoteStatus = issueStateToStatus(issue.state);
269
+ const localStatus = artifact.status;
270
+ if (remoteStatus === localStatus) {
271
+ // Already in sync
272
+ continue;
273
+ }
274
+ if (opts.direction === 'pull') {
275
+ // GitHub → local
276
+ await updateLocalStatus(projectDir, config, artifact.type, artifact.id, remoteStatus);
277
+ console.log(` ${chalk.blue('←')} ${artifact.id}: ${chalk.dim(localStatus)} → ${chalk.green(remoteStatus)} (from #${artifact.issueNumber})`);
278
+ synced++;
279
+ }
280
+ else if (opts.direction === 'push') {
281
+ // Local → GitHub
282
+ const targetState = statusToIssueState(localStatus);
283
+ if (issue.state !== targetState) {
284
+ await updateIssue(artifact.issueNumber, { state: targetState });
285
+ }
286
+ console.log(` ${chalk.yellow('→')} ${artifact.id}: #${artifact.issueNumber} ${chalk.dim(issue.state)} → ${chalk.green(targetState)}`);
287
+ synced++;
288
+ }
289
+ else {
290
+ // Both — detect conflicts
291
+ conflicts.push({
292
+ id: artifact.id,
293
+ localStatus,
294
+ remoteState: issue.state,
295
+ issueNumber: artifact.issueNumber,
296
+ });
297
+ }
298
+ }
299
+ catch (e) {
300
+ logger.error(` Failed to sync ${artifact.id}: ${e.message}`);
301
+ }
302
+ }
303
+ // Handle conflicts for bi-directional sync
304
+ if (conflicts.length > 0) {
305
+ console.log('');
306
+ logger.heading('Conflicts detected');
307
+ console.log('');
308
+ for (const conflict of conflicts) {
309
+ const remoteStatus = issueStateToStatus(conflict.remoteState);
310
+ console.log(` ${chalk.red('!')} ${conflict.id} — local: ${chalk.yellow(conflict.localStatus)}, GitHub #${conflict.issueNumber}: ${chalk.cyan(conflict.remoteState)} (${remoteStatus})`);
311
+ const action = await promptSelect(` Resolve ${conflict.id}:`, [
312
+ {
313
+ name: `Use GitHub status (${remoteStatus})`,
314
+ value: 'pull',
315
+ },
316
+ {
317
+ name: `Use local status (${conflict.localStatus})`,
318
+ value: 'push',
319
+ },
320
+ { name: 'Skip', value: 'skip' },
321
+ ]);
322
+ if (action === 'pull') {
323
+ await updateLocalStatus(projectDir, config, findArtifactTypeById(conflict.id) || 'task', conflict.id, remoteStatus);
324
+ console.log(` ${chalk.blue('←')} Updated local to ${remoteStatus}`);
325
+ synced++;
326
+ }
327
+ else if (action === 'push') {
328
+ const targetState = statusToIssueState(conflict.localStatus);
329
+ await updateIssue(conflict.issueNumber, { state: targetState });
330
+ console.log(` ${chalk.yellow('→')} Updated GitHub to ${targetState}`);
331
+ synced++;
332
+ }
333
+ else {
334
+ console.log(` ${chalk.dim('Skipped')}`);
335
+ }
336
+ }
337
+ }
338
+ console.log('');
339
+ if (synced > 0) {
340
+ logger.success(`Synced ${synced} artifact${synced !== 1 ? 's' : ''}`);
341
+ }
342
+ else {
343
+ logger.success('All artifacts are in sync');
344
+ }
345
+ });
346
+ // --- planr github status ---
347
+ github
348
+ .command('status')
349
+ .description('Show sync status of all linked artifacts')
350
+ .action(async () => {
351
+ const projectDir = program.opts().projectDir;
352
+ const config = await loadConfig(projectDir);
353
+ try {
354
+ await verifyGitHubRepo();
355
+ }
356
+ catch (e) {
357
+ logger.error(e.message);
358
+ process.exit(1);
359
+ }
360
+ logger.heading('GitHub Sync Status');
361
+ console.log('');
362
+ const types = ['epic', 'feature', 'story', 'task', 'quick'];
363
+ let linked = 0;
364
+ let unlinked = 0;
365
+ console.log(` ${chalk.dim('Artifact'.padEnd(14))} ${chalk.dim('Status'.padEnd(14))} ${chalk.dim('Issue'.padEnd(10))} ${chalk.dim('State')}`);
366
+ console.log(` ${'─'.repeat(50)}`);
367
+ for (const type of types) {
368
+ const artifacts = await listArtifacts(projectDir, config, type);
369
+ for (const a of artifacts) {
370
+ const data = await readArtifact(projectDir, config, type, a.id);
371
+ const status = data?.data.status || 'pending';
372
+ const issueNumber = data?.data.githubIssue;
373
+ if (issueNumber) {
374
+ let issueState = '';
375
+ try {
376
+ const issue = await getIssue(issueNumber);
377
+ issueState = issue.state;
378
+ }
379
+ catch {
380
+ issueState = 'error';
381
+ }
382
+ const inSync = issueStateToStatus(issueState) === status ||
383
+ statusToIssueState(status) === issueState;
384
+ const syncIcon = inSync ? chalk.green('✓') : chalk.red('✗');
385
+ console.log(` ${a.id.padEnd(14)} ${status.padEnd(14)} #${String(issueNumber).padEnd(8)} ${issueState} ${syncIcon}`);
386
+ linked++;
387
+ }
388
+ else {
389
+ console.log(` ${a.id.padEnd(14)} ${status.padEnd(14)} ${chalk.dim('—'.padEnd(10))} ${chalk.dim('not linked')}`);
390
+ unlinked++;
391
+ }
392
+ }
393
+ }
394
+ console.log(` ${'─'.repeat(50)}`);
395
+ console.log('');
396
+ console.log(` Linked: ${linked}, Unlinked: ${unlinked}`);
397
+ });
398
+ }
399
+ // ---------------------------------------------------------------------------
400
+ // Local status update
401
+ // ---------------------------------------------------------------------------
402
+ async function updateLocalStatus(projectDir, config, type, id, newStatus) {
403
+ const raw = await readArtifactRaw(projectDir, config, type, id);
404
+ if (!raw)
405
+ return;
406
+ const { data, content } = parseMarkdown(raw);
407
+ data.status = newStatus;
408
+ data.updated = new Date().toISOString().split('T')[0];
409
+ const updated = toMarkdownWithFrontmatter(data, content);
410
+ await updateArtifact(projectDir, config, type, id, updated);
411
+ }
412
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../../src/cli/commands/github.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QA2CnD"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8BzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QA2CnD"}
@@ -12,8 +12,8 @@
12
12
  */
13
13
  import path from 'node:path';
14
14
  import chalk from 'chalk';
15
- import { buildEpicPrompt, buildFeaturesPrompt, buildStoriesPrompt, buildTasksPrompt, } from '../../ai/prompts/prompt-builder.js';
16
- import { aiEpicResponseSchema, aiFeaturesResponseSchema, aiStoriesResponseSchema, aiTasksResponseSchema, } from '../../ai/schemas/ai-response-schemas.js';
15
+ import { buildEpicPrompt, buildFeaturesPrompt, buildStoriesPrompt, } from '../../ai/prompts/prompt-builder.js';
16
+ import { aiEpicResponseSchema, aiFeaturesResponseSchema, aiStoriesResponseSchema, } from '../../ai/schemas/ai-response-schemas.js';
17
17
  import { TOKEN_BUDGETS } from '../../ai/types.js';
18
18
  import { generateStreamingJSON, getAIProvider, isAIConfigured } from '../../services/ai-service.js';
19
19
  import { addChildReference, createArtifact, getArtifactDir, listArtifacts, readArtifact, readArtifactRaw, resolveArtifactFilename, } from '../../services/artifact-service.js';
@@ -39,8 +39,8 @@ export function registerPlanCommand(program) {
39
39
  const provider = await getAIProvider(config);
40
40
  try {
41
41
  if (opts.story) {
42
- // Start from story → generate tasks
43
- await generateTasksForStory(projectDir, config, provider, opts.story);
42
+ // Start from story → generate tasks (find parent feature and generate at feature level)
43
+ await generateTasksForSingleStory(projectDir, config, provider, opts.story);
44
44
  }
45
45
  else if (opts.feature) {
46
46
  // Start from feature → stories → tasks
@@ -205,26 +205,26 @@ async function planFromFeature(projectDir, config, provider, featureId) {
205
205
  await addChildReference(projectDir, config, 'feature', featureId, 'story', id, story.title);
206
206
  logger.success(`Created ${id}: ${story.title}`);
207
207
  }
208
- // Step 4: Generate tasks for each story
209
- for (const storyId of storyIds) {
210
- await generateTasksForStory(projectDir, config, provider, storyId);
211
- }
208
+ // Step 4: Generate one task list per feature (all stories + full context)
209
+ await generateTasksForFeature(projectDir, config, provider, featureId, storyIds);
212
210
  }
213
- async function generateTasksForStory(projectDir, config, provider, storyId) {
214
- const { gatherStoryArtifacts } = await import('../../services/artifact-gathering.js');
211
+ async function generateTasksForFeature(projectDir, config, provider, featureId, storyIds) {
212
+ const { gatherFeatureArtifacts } = await import('../../services/artifact-gathering.js');
213
+ const { buildTasksPrompt } = await import('../../ai/prompts/prompt-builder.js');
214
+ const { aiTasksResponseSchema } = await import('../../ai/schemas/ai-response-schemas.js');
215
215
  let ctx;
216
216
  try {
217
- ctx = await gatherStoryArtifacts(projectDir, config, storyId);
217
+ ctx = await gatherFeatureArtifacts(projectDir, config, featureId);
218
218
  }
219
219
  catch {
220
- logger.error(`Story ${storyId} not found.`);
220
+ logger.error(`Feature ${featureId} not found.`);
221
221
  return;
222
222
  }
223
- logger.dim(`\n[4/4] Generating tasks for ${storyId}...`);
224
- ctx.scope = { type: 'story', id: storyId };
223
+ logger.dim(`\n[4/4] Generating tasks for ${featureId} (${ctx.stories.length} stories)...`);
224
+ ctx.scope = { type: 'feature', id: featureId };
225
225
  const taskMessages = buildTasksPrompt(ctx);
226
226
  const { result } = await generateStreamingJSON(provider, taskMessages, aiTasksResponseSchema, {
227
- maxTokens: TOKEN_BUDGETS.plan,
227
+ maxTokens: TOKEN_BUDGETS.taskFeature,
228
228
  });
229
229
  const tasks = result.tasks.map((tg) => ({
230
230
  id: tg.id,
@@ -237,7 +237,7 @@ async function generateTasksForStory(projectDir, config, provider, storyId) {
237
237
  subtasks: [],
238
238
  })),
239
239
  }));
240
- const storyFilename = await resolveArtifactFilename(projectDir, config, 'story', storyId);
240
+ const featureFilename = await resolveArtifactFilename(projectDir, config, 'feature', featureId);
241
241
  // Build artifact sources for traceability
242
242
  const artifactSources = [];
243
243
  for (const s of ctx.stories) {
@@ -257,15 +257,84 @@ async function generateTasksForStory(projectDir, config, provider, storyId) {
257
257
  }
258
258
  const { id } = await createArtifact(projectDir, config, 'task', 'tasks/task-list.md.hbs', {
259
259
  title: result.title,
260
- storyId,
261
- storyFilename,
260
+ featureId,
261
+ featureFilename,
262
262
  tasks,
263
263
  artifactSources,
264
264
  acceptanceCriteriaMapping: result.acceptanceCriteriaMapping,
265
265
  relevantFiles: result.relevantFiles,
266
266
  });
267
267
  const total = tasks.reduce((sum, t) => sum + t.subtasks.length + 1, 0);
268
- await addChildReference(projectDir, config, 'story', storyId, 'task', id, result.title);
269
- logger.success(`Created ${id}: ${result.title} (${total} tasks)`);
268
+ // Link task list from each story
269
+ for (const sId of storyIds) {
270
+ await addChildReference(projectDir, config, 'story', sId, 'task', id, result.title);
271
+ }
272
+ logger.success(`Created ${id}: ${result.title} (${total} tasks from ${ctx.stories.length} stories)`);
273
+ }
274
+ async function generateTasksForSingleStory(projectDir, config, provider, storyId) {
275
+ // Find parent feature and generate tasks at feature level
276
+ const storyData = await readArtifact(projectDir, config, 'story', storyId);
277
+ if (!storyData) {
278
+ logger.error(`Story ${storyId} not found.`);
279
+ return;
280
+ }
281
+ const featureId = storyData.data.featureId;
282
+ if (featureId) {
283
+ logger.dim(`Story ${storyId} belongs to ${featureId} — generating tasks at feature level.`);
284
+ await generateTasksForFeature(projectDir, config, provider, featureId, [storyId]);
285
+ }
286
+ else {
287
+ // Orphan story — fall back to story-level gathering
288
+ const { gatherStoryArtifacts } = await import('../../services/artifact-gathering.js');
289
+ const { buildTasksPrompt } = await import('../../ai/prompts/prompt-builder.js');
290
+ const { aiTasksResponseSchema } = await import('../../ai/schemas/ai-response-schemas.js');
291
+ const ctx = await gatherStoryArtifacts(projectDir, config, storyId);
292
+ logger.dim(`\nGenerating tasks for ${storyId}...`);
293
+ ctx.scope = { type: 'story', id: storyId };
294
+ const taskMessages = buildTasksPrompt(ctx);
295
+ const { result } = await generateStreamingJSON(provider, taskMessages, aiTasksResponseSchema, {
296
+ maxTokens: TOKEN_BUDGETS.plan,
297
+ });
298
+ const tasks = result.tasks.map((tg) => ({
299
+ id: tg.id,
300
+ title: tg.title,
301
+ status: 'pending',
302
+ subtasks: (tg.subtasks || []).map((st) => ({
303
+ id: st.id,
304
+ title: st.title,
305
+ status: 'pending',
306
+ subtasks: [],
307
+ })),
308
+ }));
309
+ const storyFilename = await resolveArtifactFilename(projectDir, config, 'story', storyId);
310
+ const artifactSources = [];
311
+ for (const s of ctx.stories) {
312
+ artifactSources.push({
313
+ type: 'User Story',
314
+ path: `${config.outputPaths.agile}/stories/${s.id}`,
315
+ });
316
+ }
317
+ for (const g of ctx.gherkinScenarios) {
318
+ artifactSources.push({
319
+ type: 'Gherkin',
320
+ path: `${config.outputPaths.agile}/stories/${g.storyId}-gherkin.feature`,
321
+ });
322
+ }
323
+ for (const a of ctx.adrs) {
324
+ artifactSources.push({ type: 'ADR', path: `${config.outputPaths.agile}/adrs/${a.id}` });
325
+ }
326
+ const { id } = await createArtifact(projectDir, config, 'task', 'tasks/task-list.md.hbs', {
327
+ title: result.title,
328
+ storyId,
329
+ storyFilename,
330
+ tasks,
331
+ artifactSources,
332
+ acceptanceCriteriaMapping: result.acceptanceCriteriaMapping,
333
+ relevantFiles: result.relevantFiles,
334
+ });
335
+ const total = tasks.reduce((sum, t) => sum + t.subtasks.length + 1, 0);
336
+ await addChildReference(projectDir, config, 'story', storyId, 'task', id, result.title);
337
+ logger.success(`Created ${id}: ${result.title} (${total} tasks)`);
338
+ }
270
339
  }
271
340
  //# sourceMappingURL=plan.js.map