phasegate 0.160.4 → 0.160.5

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/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.160.5] - 2026-05-14
11
+
12
+ ### Fixed
13
+
14
+ - **WI-189 — umbrella CLI UX cleanup** — aligns `validate --format json` with the global JSON contract, makes `scaffold-design` default to dry-run with explicit `--apply`, fixes bypass audit empty-range wording, and synchronizes public help for scaffold, quick-mode, and delegate commands.
15
+
10
16
  ## [0.160.4] - 2026-05-14
11
17
 
12
18
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.160.4",
3
+ "version": "0.160.5",
4
4
  "packageManager": "pnpm@10.30.1",
5
5
  "description": "Phasegate — AI-agnostic quality defense toolkit. Enforces structural integrity between design intent and code.",
6
6
  "license": "MIT",
@@ -1,8 +1,10 @@
1
1
  // @unit ci-governance
2
2
  // @layer application
3
+ // @work-item-id WI-189
3
4
 
4
5
  export interface ScaffoldDesignInput {
5
6
  readonly unit: string;
6
7
  readonly phase: string;
8
+ readonly dryRun?: boolean;
7
9
  readonly force?: boolean;
8
10
  }
@@ -1,11 +1,13 @@
1
1
  // @unit ci-governance
2
2
  // @layer application
3
+ // @work-item-id WI-189
3
4
 
4
5
  export interface ScaffoldDesignOutput {
5
6
  readonly targetPath: string;
6
7
  readonly templatePath: string;
7
8
  readonly unit: string;
8
9
  readonly phase: string;
10
+ readonly dryRun: boolean;
9
11
  readonly written: boolean;
10
12
  readonly alreadyExists: boolean;
11
13
  readonly overwritten: boolean;
@@ -1,5 +1,6 @@
1
1
  // @unit ci-governance
2
2
  // @layer application
3
+ // @work-item-id WI-189
3
4
 
4
5
  import { DesignPhase } from '../../domain/value-objects/design-phase.js';
5
6
  import type { TemplateRepositoryPort } from '../../domain/ports/template-repository-port.js';
@@ -21,18 +22,33 @@ export class ScaffoldDesignUseCase {
21
22
  }
22
23
  const phase = DesignPhase.create(input.phase);
23
24
  const unit = input.unit.trim();
25
+ const dryRun = input.dryRun === true;
24
26
  const force = input.force === true;
25
27
 
26
28
  const templatePath = this.templates.resolvePath(phase);
27
29
  const targetPath = this.writer.resolvePath(unit, phase);
28
30
  const alreadyExists = await this.writer.exists(unit, phase);
29
31
 
32
+ if (dryRun) {
33
+ return {
34
+ targetPath,
35
+ templatePath,
36
+ unit,
37
+ phase: phase.value,
38
+ dryRun: true,
39
+ written: false,
40
+ alreadyExists,
41
+ overwritten: false,
42
+ };
43
+ }
44
+
30
45
  if (alreadyExists && !force) {
31
46
  return {
32
47
  targetPath,
33
48
  templatePath,
34
49
  unit,
35
50
  phase: phase.value,
51
+ dryRun: false,
36
52
  written: false,
37
53
  alreadyExists: true,
38
54
  overwritten: false,
@@ -48,6 +64,7 @@ export class ScaffoldDesignUseCase {
48
64
  templatePath,
49
65
  unit,
50
66
  phase: phase.value,
67
+ dryRun: false,
51
68
  written: true,
52
69
  alreadyExists,
53
70
  overwritten: alreadyExists && force,
@@ -1,5 +1,6 @@
1
1
  // @unit ci-governance
2
2
  // @layer presentation
3
+ // @work-item-id WI-189
3
4
 
4
5
  import { DesignPhase } from '../../domain/value-objects/design-phase.js';
5
6
  import type { ScaffoldDesignUseCase } from '../../application/usecases/scaffold-design-usecase.js';
@@ -7,6 +8,8 @@ import type { ScaffoldDesignUseCase } from '../../application/usecases/scaffold-
7
8
  export interface ScaffoldDesignHandlerArgs {
8
9
  readonly unit?: string;
9
10
  readonly phase?: string;
11
+ readonly dryRun?: boolean;
12
+ readonly apply?: boolean;
10
13
  readonly force?: boolean;
11
14
  readonly format?: 'human' | 'json';
12
15
  }
@@ -21,6 +24,12 @@ export class ScaffoldDesignHandler {
21
24
 
22
25
  async handle(args: ScaffoldDesignHandlerArgs): Promise<ScaffoldDesignHandlerResult> {
23
26
  const format = args.format ?? 'human';
27
+ const apply = args.apply === true;
28
+ const dryRun = args.dryRun === true || !apply;
29
+
30
+ if (args.dryRun === true && apply) {
31
+ return this.fail(format, '--dry-run と --apply は同時に指定できません', 2);
32
+ }
24
33
 
25
34
  if (!args.unit || args.unit.trim().length === 0) {
26
35
  return this.fail(format, '--unit <unit-id> は必須です', 2);
@@ -45,6 +54,7 @@ export class ScaffoldDesignHandler {
45
54
  result = await this.useCase.execute({
46
55
  unit: args.unit,
47
56
  phase: args.phase,
57
+ dryRun,
48
58
  force: args.force,
49
59
  });
50
60
  } catch (err) {
@@ -61,10 +71,22 @@ export class ScaffoldDesignHandler {
61
71
 
62
72
  if (result.alreadyExists && !result.written) {
63
73
  return {
64
- exitCode: 2,
74
+ exitCode: result.dryRun ? 0 : 2,
75
+ output: [
76
+ result.dryRun ? `dry-run: 既に存在します: ${result.targetPath}` : `既に存在します: ${result.targetPath}`,
77
+ result.dryRun ? '書き込むには --apply を指定してください。' : '上書きするには --force を指定してください。',
78
+ ].join('\n'),
79
+ };
80
+ }
81
+
82
+ if (result.dryRun) {
83
+ return {
84
+ exitCode: 0,
65
85
  output: [
66
- `既に存在します: ${result.targetPath}`,
67
- '上書きするには --force を指定してください。',
86
+ `dry-run: 設計文書を生成予定: ${result.targetPath}`,
87
+ `テンプレ: ${result.templatePath}`,
88
+ `Unit: ${result.unit} / phase: ${result.phase}`,
89
+ '書き込むには --apply を指定してください。',
68
90
  ].join('\n'),
69
91
  };
70
92
  }
@@ -3,6 +3,7 @@
3
3
  * @layer presentation
4
4
  * @work-item-id WI-141
5
5
  * @work-item-id WI-109
6
+ * @work-item-id WI-189
6
7
  *
7
8
  * Pre-commit CLI entry.
8
9
  * Runs L2 validators against staged TypeScript files AND design-document
@@ -533,6 +534,11 @@ function hasCompleteBypassTrailerSet(results: readonly BypassTrailerValidationRe
533
534
  return results.some((result) => result.hasAnyBypassTrailer && result.complete);
534
535
  }
535
536
 
537
+ function toBypassAuditStdout(stdout: string, changedFiles: readonly string[]): string {
538
+ if (changedFiles.length > 0) return stdout;
539
+ return stdout.replace("No staged files to check. Skipping.", "No changed files in range to check. Skipping.");
540
+ }
541
+
536
542
  export async function runBypassAudit(
537
543
  deps: PreCommitDeps,
538
544
  options: BypassAuditOptions = {},
@@ -554,7 +560,7 @@ export async function runBypassAudit(
554
560
 
555
561
  const sections = [
556
562
  `${BOLD}[phasegate]${RESET} Bypass audit (${baseRef}..${headRef})`,
557
- result.stdout,
563
+ toBypassAuditStdout(result.stdout, changedFiles),
558
564
  ];
559
565
  let exitCode = result.exitCode;
560
566
  if (result.exitCode !== 0 && !hasCompleteBypassTrailerSet(bypassResults)) {
@@ -7,6 +7,7 @@
7
7
  * @work-item-id WI-175
8
8
  * @work-item-id WI-176
9
9
  * @work-item-id WI-184
10
+ * @work-item-id WI-189
10
11
  *
11
12
  * Phasegate CLI エントリポイント。
12
13
  * 各Unitの Composition Root からハンドラーを取得し、コマンドに応じてディスパッチする。
@@ -156,7 +157,8 @@ Setup:
156
157
  --with-husky, --with-ci, --yes)
157
158
  update-skills Alias for reconcile (kept for compatibility)
158
159
  doctor Diagnose silent installation failures (--json, --strict, --agent <claude|codex|both>, --report-out <path>)
159
- scaffold-wi <unit> <type> Create docs/inception/{unit}/WI-XXX/description.md
160
+ scaffold-wi <unit|_cross> <story|issue|chore>
161
+ Create docs/inception/{unit}/WI-XXX/description.md
160
162
  emit-agent-rules Print AGENTS.md / CLAUDE.md WI workflow rules block
161
163
  install Install phasegate managed files (--dry-run|--apply, --force)
162
164
  uninstall Uninstall phasegate managed files (--dry-run|--apply, --force)
@@ -183,8 +185,9 @@ Commands:
183
185
 
184
186
  lint Run lint checks (--json, --target <path>)
185
187
 
186
- validate Run validators (--layer L2|L3|L4|all; L0 prints runtime hook info, --unit, --format human|agent|ci)
188
+ validate Run validators (--layer L2|L3|L4|all; L0 prints runtime hook info, --unit, --format human|agent|ci|json, --json)
187
189
  ci-check CI check (--quick for quick mode, --fail-on-reject, --dry-run, --files)
190
+ check-change-category Classify changed paths for quick mode (--paths <csv>, --format human|json)
188
191
 
189
192
  phasegate:check-ready Check ready status (--json)
190
193
  phasegate:check-phase Check phase gate (--unit <unitId>, --json)
@@ -211,7 +214,7 @@ Gate semantics:
211
214
  config:plan Plan safe config changes (--intent <l4-strict|codex-hooks|ci-fail-on-warning|baseline-reset|quick-mode-strict>, --dry-run, --json)
212
215
  ci:check-repetition Check error repetition (--code <errorCode>, --reset, --json)
213
216
  baseline Create retrofit baseline snapshot (--dry-run, --force, --paths <glob,glob,...>, --json)
214
- scaffold-design Scaffold a design doc (--unit <id>, --phase <logical|domain|uiux|unit-test|it-test>, --force, --json)
217
+ scaffold-design Scaffold a design doc (--unit <id>, --phase <logical|domain|uiux|unit-test|it-test>, --dry-run|--apply, --force, --json)
215
218
 
216
219
  skill:execute-tdd-cycle Execute TDD cycle (--unit, --story, --desc, --phase RED|GREEN|REFACTOR, --passed)
217
220
  skill:check-coverage Check coverage (--story <storyId>, --json)
@@ -409,7 +412,8 @@ function parseValidateFormat(args: readonly string[]): "human" | "agent" | "ci"
409
412
  const raw = parseFlag(args, "--format");
410
413
  if (raw === undefined) return undefined;
411
414
  if (raw === "human" || raw === "agent" || raw === "ci") return raw;
412
- throw new Error(`Invalid --format value for validate: '${raw}'. Supported values: human, agent, ci.`);
415
+ if (raw === "json") return "ci";
416
+ throw new Error(`Invalid --format value for validate: '${raw}'. Supported values: human, agent, ci, json.`);
413
417
  }
414
418
 
415
419
  function levenshtein(a: string, b: string): number {
@@ -546,7 +550,8 @@ Run validators against the project. Without --layer, runs all enabled validator
546
550
 
547
551
  Options:
548
552
  --layer <L0|L2|L3|L4> Run only the specified validator layer; L0 prints runtime hook info
549
- --json Output machine-readable JSON
553
+ --format <human|agent|ci|json> Output format; json is an alias for ci JSON
554
+ --json Output machine-readable JSON when --format is omitted
550
555
  --help, -h Show this help`,
551
556
  lint: `Usage: phasegate lint [options]
552
557
 
@@ -626,6 +631,28 @@ Options:
626
631
  Examples:
627
632
  phasegate check-change-category --paths src/foo.ts,src/bar.ts
628
633
  phasegate check-change-category --paths src/foo.ts --format json`,
634
+ "scaffold-wi": `Usage: phasegate scaffold-wi <unit|_cross> <story|issue|chore>
635
+
636
+ Create docs/inception/{unit}/WI-XXX/description.md.
637
+
638
+ Arguments:
639
+ <unit|_cross> Unit id or _cross for cross-cutting work items.
640
+ <story|issue|chore> Work item type.
641
+
642
+ Options:
643
+ --help, -h Show this help`,
644
+ "scaffold-design": `Usage: phasegate scaffold-design --unit <id> --phase <phase> [options]
645
+
646
+ Scaffold a product construction design document.
647
+
648
+ Options:
649
+ --unit <id> Unit id under docs/product/construction.
650
+ --phase <phase> logical, domain, uiux, unit-test, or it-test.
651
+ --dry-run Preview target and template without writing (default).
652
+ --apply Write the scaffold.
653
+ --force Overwrite an existing target when applying.
654
+ --json Output machine-readable JSON.
655
+ --help, -h Show this help`,
629
656
  "ci:generate-template": `Usage: phasegate ci:generate-template [options]
630
657
 
631
658
  Generates a CI template configuration.
@@ -643,6 +670,15 @@ Options:
643
670
  Examples:
644
671
  phasegate ci:generate-template --type aidlc-gate
645
672
  phasegate ci:generate-template --preset strict --type pre-commit --render`,
673
+ "delegate-sonnet": `Usage: phasegate delegate-sonnet [...args]
674
+
675
+ Delegate a task to Sonnet 4.6 by forwarding all args to scripts/delegate-sonnet.sh.
676
+
677
+ Arguments:
678
+ [...args] Task text and options consumed by the delegate script.
679
+
680
+ Options:
681
+ --help, -h Show this help`,
646
682
  };
647
683
 
648
684
  function printSubcommandHelp(command: string): void {
@@ -2191,7 +2227,7 @@ async function main(): Promise<void> {
2191
2227
  const layer = parseFlag(args, "--layer") as "L0" | "L2" | "L3" | "L4" | "all" | undefined;
2192
2228
  const unit = parseFlag(args, "--unit");
2193
2229
  const phase = parseFlag(args, "--phase");
2194
- const format = parseValidateFormat(args);
2230
+ const format = parseValidateFormat(args) ?? (json ? "ci" : undefined);
2195
2231
  // WI-094 / ADR-017: --fail-on-warning / --no-fail-on-warning / 未指定→config値
2196
2232
  const failOnWarning = parseTriStateFlag(args, "--fail-on-warning", "--no-fail-on-warning");
2197
2233
  const noL4 = hasFlag(args, "--no-l4");
@@ -2206,7 +2242,7 @@ async function main(): Promise<void> {
2206
2242
  targetPaths,
2207
2243
  });
2208
2244
  console.log(result.output);
2209
- if (layer === "L2" || layer === "all") {
2245
+ if ((layer === "L2" || layer === "all") && format !== "ci") {
2210
2246
  await printStoryReflectionValidationSummary(rootDir, unit);
2211
2247
  }
2212
2248
  process.exit(result.exitCode);
@@ -2492,11 +2528,15 @@ Examples:
2492
2528
  const mod = buildCiGovernance(rootDir, harnessRoot);
2493
2529
  const unit = parseFlag(args, "--unit") ?? "";
2494
2530
  const phase = parseFlag(args, "--phase") ?? "";
2531
+ const dryRun = hasFlag(args, "--dry-run");
2532
+ const apply = hasFlag(args, "--apply");
2495
2533
  const force = hasFlag(args, "--force");
2496
2534
  const format = json ? "json" : "human";
2497
2535
  const result = await mod.scaffoldDesignHandler.handle({
2498
2536
  unit,
2499
2537
  phase,
2538
+ dryRun,
2539
+ apply,
2500
2540
  force,
2501
2541
  format,
2502
2542
  });