@zhuan-ai/zhuanspec 2.15.6 → 2.15.7

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/dist/cli/index.js CHANGED
@@ -272,7 +272,7 @@ progressCmd
272
272
  // Progress set-phase subcommand
273
273
  progressCmd
274
274
  .command('set-phase <change-id> <phase>')
275
- .description('Set phase for a change (e.g., propose, apply, review, archive)')
275
+ .description('Set phase for a change (idle, techDesign, propose, apply, review, archive). Workflow-entry phases (idle/techDesign/propose) auto-create the change directory if missing.')
276
276
  .action(async (changeId, phase) => {
277
277
  try {
278
278
  const progressCommand = new ProgressCommand();
@@ -32,6 +32,10 @@ export declare class ProgressCommand {
32
32
  /**
33
33
  * Set phase for a change
34
34
  * Usage: zhuanspec progress set-phase <change-id> <phase>
35
+ *
36
+ * Workflow-entry phases (idle/techDesign/propose) auto-create the change
37
+ * directory if it does not exist; later phases (apply/review/archive)
38
+ * require the directory to already exist to avoid typo-creating ghost dirs.
35
39
  */
36
40
  setPhase(changeId: string, phase: string): Promise<void>;
37
41
  /**
@@ -350,19 +350,30 @@ export class ProgressCommand {
350
350
  /**
351
351
  * Set phase for a change
352
352
  * Usage: zhuanspec progress set-phase <change-id> <phase>
353
+ *
354
+ * Workflow-entry phases (idle/techDesign/propose) auto-create the change
355
+ * directory if it does not exist; later phases (apply/review/archive)
356
+ * require the directory to already exist to avoid typo-creating ghost dirs.
353
357
  */
354
358
  async setPhase(changeId, phase) {
359
+ // Validate phase first (fail fast on user typos so we don't surface
360
+ // misleading "Change not found" errors when the real issue is the phase name).
361
+ // Match case-insensitively but keep the canonical casing from PHASE_ORDER
362
+ // (e.g. user input 'techdesign' / 'TECHDESIGN' / 'techDesign' all map to 'techDesign').
363
+ const normalizedPhase = PHASE_ORDER.find(p => p.toLowerCase() === phase.toLowerCase());
364
+ if (!normalizedPhase) {
365
+ throw new Error(`Invalid phase: ${phase}. Valid phases: ${PHASE_ORDER.join(', ')}`);
366
+ }
355
367
  const cwd = resolveZhuanSpecRoot();
356
368
  const changeDir = path.join(cwd, 'zhuanspec', 'changes', changeId);
357
- if (!await FileSystemUtils.directoryExists(changeDir)) {
369
+ const dirExists = await FileSystemUtils.directoryExists(changeDir);
370
+ // Workflow-entry phases may create the change directory; later phases must not.
371
+ const ENTRY_PHASES = ['idle', 'techDesign', 'propose'];
372
+ if (!dirExists && !ENTRY_PHASES.includes(normalizedPhase)) {
358
373
  throw new Error(`Change '${changeId}' not found`);
359
374
  }
360
- // Validate phase
361
- const normalizedPhase = phase.toLowerCase();
362
- if (!PHASE_ORDER.includes(normalizedPhase)) {
363
- throw new Error(`Invalid phase: ${phase}. Valid phases: ${PHASE_ORDER.join(', ')}`);
364
- }
365
- // Update progress.json
375
+ // Update progress.json (initializeProgress creates metrics/ recursively,
376
+ // which also creates the change directory itself when missing).
366
377
  await initializeProgress(changeId, normalizedPhase);
367
378
  // Read and display result
368
379
  const progressPath = path.join(changeDir, 'metrics', 'progress.json');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhuan-ai/zhuanspec",
3
- "version": "2.15.6",
3
+ "version": "2.15.7",
4
4
  "description": "AI-native system for spec-driven development",
5
5
  "keywords": [
6
6
  "zhuanspec",