@polymorphism-tech/morph-spec 4.8.14 → 4.8.16
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 +2 -2
- package/bin/morph-spec.js +23 -2
- package/bin/task-manager.js +202 -14
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents.json +113 -116
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +48 -2
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +151 -0
- package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +6 -0
- package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +6 -0
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +27 -0
- package/framework/hooks/claude-code/stop/validate-completion.js +17 -2
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +87 -0
- package/framework/hooks/claude-code/user-prompt/set-terminal-title.js +58 -0
- package/framework/hooks/shared/phase-utils.js +1 -1
- package/framework/hooks/shared/state-reader.js +1 -0
- package/framework/skills/README.md +1 -0
- package/framework/skills/level-0-meta/brainstorming/SKILL.md +2 -0
- package/framework/skills/level-0-meta/code-review/SKILL.md +16 -0
- package/framework/skills/level-0-meta/code-review/references/review-guidelines.md +100 -0
- package/framework/skills/level-0-meta/code-review/scripts/scan-csharp.mjs +36 -6
- package/framework/skills/level-0-meta/code-review-nextjs/SKILL.md +16 -0
- package/framework/skills/level-0-meta/code-review-nextjs/scripts/scan-nextjs.mjs +189 -0
- package/framework/skills/level-0-meta/frontend-review/SKILL.md +359 -0
- package/framework/skills/level-0-meta/frontend-review/scripts/scan-accessibility.mjs +376 -0
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +3 -2
- package/framework/skills/level-0-meta/morph-replicate/SKILL.md +10 -8
- package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +70 -0
- package/framework/skills/level-0-meta/post-implementation/SKILL.md +315 -0
- package/framework/skills/level-0-meta/post-implementation/scripts/detect-dev-server.mjs +153 -0
- package/framework/skills/level-0-meta/post-implementation/scripts/detect-stack.mjs +234 -0
- package/framework/skills/level-0-meta/terminal-title/SKILL.md +61 -0
- package/framework/skills/level-0-meta/terminal-title/scripts/set_title.sh +65 -0
- package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +13 -206
- package/framework/skills/level-0-meta/tool-usage-guide/references/tools-per-phase.md +213 -0
- package/framework/skills/level-0-meta/verification-before-completion/SKILL.md +2 -0
- package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +4 -7
- package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/phase-design/SKILL.md +16 -110
- package/framework/skills/level-1-workflows/phase-design/references/architecture-analysis-guide.md +89 -0
- package/framework/skills/level-1-workflows/phase-design/references/spec-authoring-guide.md +55 -0
- package/framework/skills/level-1-workflows/phase-implement/SKILL.md +153 -118
- package/framework/skills/level-1-workflows/phase-implement/references/vsa-implementation-guide.md +92 -0
- package/framework/skills/level-1-workflows/phase-setup/SKILL.md +1 -2
- package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +11 -158
- package/framework/skills/level-1-workflows/phase-tasks/references/task-planning-patterns.md +172 -0
- package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +42 -3
- package/framework/squad-templates/backend-only.json +14 -1
- package/framework/squad-templates/frontend-only.json +14 -1
- package/framework/squad-templates/full-stack.json +25 -8
- package/framework/standards/STANDARDS.json +631 -86
- package/framework/standards/frontend/design-system/aesthetic-direction.md +213 -0
- package/framework/templates/project/validate.js +122 -0
- package/framework/workflows/configs/zero-touch.json +7 -0
- package/package.json +1 -1
- package/src/commands/agents/dispatch-agents.js +53 -10
- package/src/commands/state/advance-phase.js +56 -0
- package/src/commands/state/index.js +2 -1
- package/src/commands/state/phase-runner.js +215 -0
- package/src/commands/tasks/task.js +23 -2
- package/src/core/paths/output-schema.js +1 -1
- package/src/lib/generators/recap-generator.js +16 -0
- package/src/lib/orchestration/team-orchestrator.js +171 -89
- package/src/lib/phase-chain/eligibility-checker.js +243 -0
- package/src/lib/standards/digest-builder.js +231 -0
- package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +39 -0
- package/src/lib/validators/nextjs/next-component-validator.js +2 -0
- package/src/lib/validators/validation-runner.js +2 -2
- package/src/utils/file-copier.js +2 -0
- package/src/utils/hooks-installer.js +31 -7
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Spec-driven development framework for multi-stack projects. Turns feature requests into implementation-ready code through structured, AI-orchestrated phases.
|
|
4
4
|
|
|
5
5
|
**Package:** `@polymorphism-tech/morph-spec`
|
|
6
|
-
**Version:** 4.8.
|
|
6
|
+
**Version:** 4.8.16
|
|
7
7
|
**Requires:** Node.js 18+, Claude Code
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -376,4 +376,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
|
|
|
376
376
|
|
|
377
377
|
---
|
|
378
378
|
|
|
379
|
-
*morph-spec v4.8.
|
|
379
|
+
*morph-spec v4.8.16 by [Polymorphism Tech](https://polymorphism.tech)*
|
package/bin/morph-spec.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
@@ -22,12 +22,13 @@ import { stateCommand } from '../src/commands/state/state.js';
|
|
|
22
22
|
import { validatePhaseCommand } from '../src/commands/state/validate-phase.js';
|
|
23
23
|
import { advancePhaseCommand } from '../src/commands/state/advance-phase.js';
|
|
24
24
|
import { approveCommand, approvalStatusCommand, unapproveCommand } from '../src/commands/state/approve.js';
|
|
25
|
+
import { phaseRunCommand } from '../src/commands/state/phase-runner.js';
|
|
25
26
|
|
|
26
27
|
// Agent commands
|
|
27
28
|
import { dispatchAgentsCommand } from '../src/commands/agents/dispatch-agents.js';
|
|
28
29
|
|
|
29
30
|
// Task commands
|
|
30
|
-
import { taskDoneCommand, taskStartCommand, taskNextCommand } from '../src/commands/tasks/task.js';
|
|
31
|
+
import { taskDoneCommand, taskStartCommand, taskNextCommand, taskBulkDoneCommand } from '../src/commands/tasks/task.js';
|
|
31
32
|
|
|
32
33
|
// Validation commands
|
|
33
34
|
import { validateCommand } from './validate.js';
|
|
@@ -129,8 +130,19 @@ taskCommand
|
|
|
129
130
|
.command('done <feature> <task-ids...>')
|
|
130
131
|
.description('Mark tasks as completed (runs validation first)')
|
|
131
132
|
.option('--skip-validation', 'Skip code validation (not recommended)')
|
|
133
|
+
.option('--dry-run', 'Show validation results without marking tasks as complete')
|
|
132
134
|
.action((feature, taskIds, options) => taskDoneCommand(feature, taskIds, options));
|
|
133
135
|
|
|
136
|
+
taskCommand
|
|
137
|
+
.command('bulk-done <feature> [range]')
|
|
138
|
+
.description('Bulk-complete tasks (--all | --from T001 --to T053 | T001..T082)')
|
|
139
|
+
.option('--all', 'Complete all pending tasks')
|
|
140
|
+
.option('--from <id>', 'Start task ID for range')
|
|
141
|
+
.option('--to <id>', 'End task ID for range')
|
|
142
|
+
.option('--skip-validation', 'Skip code validation (not recommended)')
|
|
143
|
+
.option('--dry-run', 'Show validation results without marking tasks as complete')
|
|
144
|
+
.action((feature, range, options) => taskBulkDoneCommand(feature, range, options));
|
|
145
|
+
|
|
134
146
|
taskCommand
|
|
135
147
|
.command('start <feature> <task-id>')
|
|
136
148
|
.description('Start a task (mark as in_progress)')
|
|
@@ -191,6 +203,14 @@ phaseCommand
|
|
|
191
203
|
.option('--skip-optional', 'Skip optional phases (uiux, sync)')
|
|
192
204
|
.action((feature, options) => advancePhaseCommand(feature, options));
|
|
193
205
|
|
|
206
|
+
phaseCommand
|
|
207
|
+
.command('run <feature>')
|
|
208
|
+
.description('Automated phase chain runner — advances phases until blocker or max reached')
|
|
209
|
+
.option('--dry-run', 'Show decision tree without executing')
|
|
210
|
+
.option('--max-phases <n>', 'Max phases to auto-advance (default: 6)', parseInt)
|
|
211
|
+
.option('--skip-approval', 'Skip approval gate checks')
|
|
212
|
+
.action((feature, options) => phaseRunCommand(feature, options));
|
|
213
|
+
|
|
194
214
|
// Phase validation command (also available as standalone)
|
|
195
215
|
program
|
|
196
216
|
.command('validate-phase <feature> <phase>')
|
|
@@ -294,6 +314,7 @@ program
|
|
|
294
314
|
.command('dispatch-agents <feature> <phase>')
|
|
295
315
|
.description('Build dispatch config for parallel agent orchestration (design | tasks | implement)')
|
|
296
316
|
.option('--table', 'Human-readable table output instead of JSON')
|
|
317
|
+
.option('--mode <mode>', 'Dispatch mode: validate (includes Tier-4 validators as read-only)')
|
|
297
318
|
.option('-v, --verbose', 'Show stack trace on error')
|
|
298
319
|
.action((feature, phase, options) => dispatchAgentsCommand(feature, phase, options));
|
|
299
320
|
|
package/bin/task-manager.js
CHANGED
|
@@ -144,13 +144,60 @@ class TaskManager {
|
|
|
144
144
|
|
|
145
145
|
// Run validation BEFORE marking tasks as complete
|
|
146
146
|
if (tasksToComplete.length > 0 && !options.skipValidation) {
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
149
|
-
console.
|
|
150
|
-
|
|
147
|
+
const validationResult = await this.runValidation(featureName);
|
|
148
|
+
if (options.dryRun) {
|
|
149
|
+
console.log(chalk.cyan('\n ℹ️ Dry-run — tasks NOT marked as complete'));
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
if (!validationResult.passed) {
|
|
153
|
+
// Reload state to ensure we have the latest (may have been modified by validation)
|
|
154
|
+
const currentState = loadState();
|
|
155
|
+
const currentFeature = currentState.features[featureName];
|
|
156
|
+
if (currentFeature) {
|
|
157
|
+
for (const task of tasksToComplete) {
|
|
158
|
+
this.persistValidationHistory(currentFeature, task.id, validationResult);
|
|
159
|
+
}
|
|
160
|
+
saveState(currentState);
|
|
161
|
+
|
|
162
|
+
// Check escalation status
|
|
163
|
+
const blockedTasks = tasksToComplete.filter(t => {
|
|
164
|
+
const hist = currentFeature.validationHistory?.[t.id];
|
|
165
|
+
return hist?.status === 'blocked';
|
|
166
|
+
});
|
|
167
|
+
if (blockedTasks.length > 0) {
|
|
168
|
+
console.error(chalk.red(`\n⛔ ESCALATION — Task(s) ${blockedTasks.map(t => t.id).join(', ')} have failed 3 times`));
|
|
169
|
+
console.error(chalk.red(' Human review required. Mark resolved with: morph-spec state set <feature> validationHistory.<taskId>.status passed'));
|
|
170
|
+
} else {
|
|
171
|
+
const attempt = currentFeature.validationHistory?.[tasksToComplete[0]?.id]?.attempt || 1;
|
|
172
|
+
console.error(chalk.red(`\n❌ Validation failed (attempt ${attempt}/3) — tasks NOT marked as complete`));
|
|
173
|
+
console.log(chalk.gray(' Fix the issues above, then run task done again'));
|
|
174
|
+
if (attempt >= 2) {
|
|
175
|
+
console.log(chalk.yellow(` ⚠️ Next failure will escalate to human review`));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
console.error(chalk.red('\n❌ Validation failed — tasks NOT marked as complete'));
|
|
180
|
+
console.log(chalk.gray(' Fix the issues above, then run task done again'));
|
|
181
|
+
}
|
|
151
182
|
console.log(chalk.gray(' Or use --skip-validation to bypass (not recommended)\n'));
|
|
152
183
|
process.exit(1);
|
|
153
184
|
}
|
|
185
|
+
|
|
186
|
+
// Validation passed — mark any pending history as passed
|
|
187
|
+
const successState = loadState();
|
|
188
|
+
const successFeature = successState.features[featureName];
|
|
189
|
+
if (successFeature) {
|
|
190
|
+
for (const task of tasksToComplete) {
|
|
191
|
+
if (successFeature.validationHistory?.[task.id]) {
|
|
192
|
+
successFeature.validationHistory[task.id].status = 'passed';
|
|
193
|
+
successFeature.validationHistory[task.id].updatedAt = new Date().toISOString();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
saveState(successState);
|
|
197
|
+
}
|
|
198
|
+
} else if (options.dryRun) {
|
|
199
|
+
console.log(chalk.cyan('\n ℹ️ Dry-run — tasks NOT marked as complete (validation skipped)'));
|
|
200
|
+
return [];
|
|
154
201
|
}
|
|
155
202
|
|
|
156
203
|
// Breaking change detection (non-blocking warning)
|
|
@@ -242,9 +289,11 @@ class TaskManager {
|
|
|
242
289
|
}
|
|
243
290
|
|
|
244
291
|
/**
|
|
245
|
-
* Run validation for a feature using the ValidationRunner (ESM dynamic import)
|
|
292
|
+
* Run validation for a feature using the ValidationRunner (ESM dynamic import).
|
|
293
|
+
* Returns a structured result with per-validator breakdown for validationHistory.
|
|
294
|
+
*
|
|
246
295
|
* @param {string} featureName - Feature name
|
|
247
|
-
* @returns {boolean
|
|
296
|
+
* @returns {{ passed: boolean, validators: Object, passRate: number }}
|
|
248
297
|
*/
|
|
249
298
|
async runValidation(featureName) {
|
|
250
299
|
try {
|
|
@@ -254,15 +303,74 @@ class TaskManager {
|
|
|
254
303
|
const result = await runValidation('.', featureName, { verbose: true });
|
|
255
304
|
|
|
256
305
|
formatValidationResults(result);
|
|
257
|
-
|
|
306
|
+
|
|
307
|
+
// Build structured validators map from result
|
|
308
|
+
const validators = {};
|
|
309
|
+
if (result.results && typeof result.results === 'object') {
|
|
310
|
+
for (const [name, vResult] of Object.entries(result.results)) {
|
|
311
|
+
const issues = (vResult.errors || []).map(e => ({
|
|
312
|
+
message: typeof e === 'string' ? e : (e.message || String(e)),
|
|
313
|
+
file: e.file,
|
|
314
|
+
line: e.line,
|
|
315
|
+
rule: e.rule,
|
|
316
|
+
}));
|
|
317
|
+
validators[name] = {
|
|
318
|
+
passed: vResult.passed ?? (issues.length === 0),
|
|
319
|
+
issues,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
} else if (!result.passed && result.errors?.length > 0) {
|
|
323
|
+
// Flat format fallback — wrap as single 'validation' validator
|
|
324
|
+
validators['validation'] = {
|
|
325
|
+
passed: false,
|
|
326
|
+
issues: result.errors.map(e => ({
|
|
327
|
+
message: typeof e === 'string' ? e : (e.message || String(e)),
|
|
328
|
+
file: e.file,
|
|
329
|
+
line: e.line,
|
|
330
|
+
rule: e.rule || 'validation',
|
|
331
|
+
})),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const validatorEntries = Object.values(validators);
|
|
336
|
+
const passRate = validatorEntries.length > 0
|
|
337
|
+
? validatorEntries.filter(v => v.passed).length / validatorEntries.length
|
|
338
|
+
: (result.passed ? 1.0 : 0.0);
|
|
339
|
+
|
|
340
|
+
return { passed: result.passed, validators, passRate };
|
|
258
341
|
} catch (error) {
|
|
259
342
|
// If validation runner fails to load, warn but don't block task completion.
|
|
260
343
|
// This is fail-open by design: a broken validator shouldn't block commits.
|
|
261
344
|
// Common cause: missing optional deps.
|
|
262
345
|
console.log(chalk.yellow(`\n⚠️ Validation skipped (${error.message})`));
|
|
263
346
|
console.log(chalk.gray(' Run manually: npx morph-spec validate --verbose'));
|
|
264
|
-
return true;
|
|
347
|
+
return { passed: true, validators: {}, passRate: 1.0 };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Persist a validation result to feature.validationHistory[taskId].
|
|
353
|
+
* Increments attempt counter and sets status to 'failed' or 'blocked' (attempt >= 3).
|
|
354
|
+
*
|
|
355
|
+
* @param {Object} feature - Mutable feature state object
|
|
356
|
+
* @param {string} taskId - Task ID
|
|
357
|
+
* @param {{ passed: boolean, validators: Object, passRate: number }} validationResult
|
|
358
|
+
*/
|
|
359
|
+
persistValidationHistory(feature, taskId, validationResult) {
|
|
360
|
+
if (!feature.validationHistory) {
|
|
361
|
+
feature.validationHistory = {};
|
|
265
362
|
}
|
|
363
|
+
const existing = feature.validationHistory[taskId] || { attempt: 0 };
|
|
364
|
+
const attempt = (existing.attempt || 0) + 1;
|
|
365
|
+
const status = attempt >= 3 ? 'blocked' : 'failed';
|
|
366
|
+
|
|
367
|
+
feature.validationHistory[taskId] = {
|
|
368
|
+
attempt,
|
|
369
|
+
validators: validationResult.validators || {},
|
|
370
|
+
passRate: validationResult.passRate || 0,
|
|
371
|
+
status,
|
|
372
|
+
updatedAt: new Date().toISOString(),
|
|
373
|
+
};
|
|
266
374
|
}
|
|
267
375
|
|
|
268
376
|
/**
|
|
@@ -511,6 +619,58 @@ class TaskManager {
|
|
|
511
619
|
console.log(chalk.blue(`▶️ Task ${taskId} started: ${task.title}`));
|
|
512
620
|
}
|
|
513
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Bulk-complete tasks with a single validation pass.
|
|
624
|
+
* @param {string} featureName
|
|
625
|
+
* @param {Object} opts
|
|
626
|
+
* @param {boolean} [opts.all] - Complete all pending tasks
|
|
627
|
+
* @param {string} [opts.from] - Start of range (e.g. T001)
|
|
628
|
+
* @param {string} [opts.to] - End of range (e.g. T053)
|
|
629
|
+
* @param {string} [opts.range] - Compact range string (e.g. T001..T082)
|
|
630
|
+
* @param {boolean} [opts.skipValidation]
|
|
631
|
+
* @param {boolean} [opts.dryRun]
|
|
632
|
+
*/
|
|
633
|
+
async bulkCompleteTasks(featureName, opts = {}) {
|
|
634
|
+
const state = loadState();
|
|
635
|
+
const feature = state.features[featureName];
|
|
636
|
+
if (!feature) throw new Error(`Feature '${featureName}' not found in state.json`);
|
|
637
|
+
|
|
638
|
+
const taskList = await ensureTaskList(feature, featureName);
|
|
639
|
+
if (taskList.length === 0) throw new Error(`No tasks found for '${featureName}'`);
|
|
640
|
+
|
|
641
|
+
// Resolve task IDs from range options
|
|
642
|
+
let targetIds;
|
|
643
|
+
if (opts.all) {
|
|
644
|
+
targetIds = taskList.filter(t => t.status !== 'completed').map(t => t.id);
|
|
645
|
+
} else if (opts.from && opts.to) {
|
|
646
|
+
targetIds = this.expandRange(taskList, opts.from, opts.to);
|
|
647
|
+
} else if (opts.range && opts.range.includes('..')) {
|
|
648
|
+
const [from, to] = opts.range.split('..');
|
|
649
|
+
targetIds = this.expandRange(taskList, from.trim(), to.trim());
|
|
650
|
+
} else {
|
|
651
|
+
throw new Error('bulk-done requires --all, --from/--to, or a T001..T082 range argument');
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
console.log(chalk.cyan(`\n📦 Bulk-done: ${targetIds.length} task(s) targeted`));
|
|
655
|
+
await this.completeTasks(featureName, targetIds, { skipValidation: opts.skipValidation, dryRun: opts.dryRun });
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Expand a task range like T001..T053 using zero-padded numeric IDs.
|
|
660
|
+
*/
|
|
661
|
+
expandRange(taskList, from, to) {
|
|
662
|
+
const numOf = id => parseInt(id.replace(/\D/g, ''), 10);
|
|
663
|
+
const prefix = from.replace(/\d+$/, '');
|
|
664
|
+
const padLen = from.replace(prefix, '').length;
|
|
665
|
+
const fromNum = numOf(from);
|
|
666
|
+
const toNum = numOf(to);
|
|
667
|
+
const ids = [];
|
|
668
|
+
for (let n = fromNum; n <= toNum; n++) {
|
|
669
|
+
ids.push(prefix + String(n).padStart(padLen, '0'));
|
|
670
|
+
}
|
|
671
|
+
return ids.filter(id => taskList.some(t => t.id === id));
|
|
672
|
+
}
|
|
673
|
+
|
|
514
674
|
/**
|
|
515
675
|
* Get next task suggestion
|
|
516
676
|
*/
|
|
@@ -555,16 +715,40 @@ async function main() {
|
|
|
555
715
|
case 'done':
|
|
556
716
|
case 'complete': {
|
|
557
717
|
const skipValidation = args.includes('--skip-validation');
|
|
558
|
-
const
|
|
718
|
+
const dryRun = args.includes('--dry-run');
|
|
719
|
+
const filteredArgs = args.filter(a => !['--skip-validation', '--dry-run'].includes(a));
|
|
559
720
|
const featureName = filteredArgs[1];
|
|
560
721
|
const taskIds = filteredArgs.slice(2);
|
|
561
722
|
|
|
562
723
|
if (!featureName || taskIds.length === 0) {
|
|
563
|
-
console.error(chalk.red('Usage: npx morph-spec task done <feature> <task-id> [task-id...] [--skip-validation]'));
|
|
724
|
+
console.error(chalk.red('Usage: npx morph-spec task done <feature> <task-id> [task-id...] [--skip-validation] [--dry-run]'));
|
|
725
|
+
process.exit(1);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
await manager.completeTasks(featureName, taskIds, { skipValidation, dryRun });
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
case 'bulk-done': {
|
|
733
|
+
const skipValidation = args.includes('--skip-validation');
|
|
734
|
+
const dryRun = args.includes('--dry-run');
|
|
735
|
+
const allFlag = args.includes('--all');
|
|
736
|
+
const fromIdx = args.indexOf('--from');
|
|
737
|
+
const toIdx = args.indexOf('--to');
|
|
738
|
+
const filteredArgs = args.filter(a => !['--skip-validation', '--dry-run', '--all'].includes(a)
|
|
739
|
+
&& !a.startsWith('--from') && !a.startsWith('--to'));
|
|
740
|
+
const featureName = filteredArgs[1];
|
|
741
|
+
const rangeArg = filteredArgs[2]; // e.g. T001..T082
|
|
742
|
+
|
|
743
|
+
if (!featureName) {
|
|
744
|
+
console.error(chalk.red('Usage: npx morph-spec task bulk-done <feature> [--all | --from T001 --to T053 | T001..T082] [--skip-validation] [--dry-run]'));
|
|
564
745
|
process.exit(1);
|
|
565
746
|
}
|
|
566
747
|
|
|
567
|
-
|
|
748
|
+
const fromId = fromIdx !== -1 ? args[fromIdx + 1] : null;
|
|
749
|
+
const toId = toIdx !== -1 ? args[toIdx + 1] : null;
|
|
750
|
+
|
|
751
|
+
await manager.bulkCompleteTasks(featureName, { all: allFlag, from: fromId, to: toId, range: rangeArg, skipValidation, dryRun });
|
|
568
752
|
break;
|
|
569
753
|
}
|
|
570
754
|
|
|
@@ -596,9 +780,13 @@ async function main() {
|
|
|
596
780
|
default:
|
|
597
781
|
console.error(chalk.red(`Unknown command: ${command}`));
|
|
598
782
|
console.log(chalk.gray('\nAvailable commands:'));
|
|
599
|
-
console.log(chalk.gray(' done
|
|
600
|
-
console.log(chalk.gray('
|
|
601
|
-
console.log(chalk.gray('
|
|
783
|
+
console.log(chalk.gray(' done <feature> <task-id...> - Mark tasks as completed'));
|
|
784
|
+
console.log(chalk.gray(' bulk-done <feature> --all - Mark all pending tasks as completed'));
|
|
785
|
+
console.log(chalk.gray(' bulk-done <feature> --from T001 --to T053 - Mark range as completed'));
|
|
786
|
+
console.log(chalk.gray(' bulk-done <feature> T001..T082 - Mark range as completed'));
|
|
787
|
+
console.log(chalk.gray(' start <feature> <task-id> - Start a task (mark as in_progress)'));
|
|
788
|
+
console.log(chalk.gray(' next <feature> - Show next suggested task'));
|
|
789
|
+
console.log(chalk.gray('\nFlags: --skip-validation, --dry-run'));
|
|
602
790
|
process.exit(1);
|
|
603
791
|
}
|
|
604
792
|
} catch (error) {
|
package/claude-plugin.json
CHANGED
package/docs/CHEATSHEET.md
CHANGED
package/docs/QUICKSTART.md
CHANGED