phasegate 0.73.0 → 0.74.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/CHANGELOG.md CHANGED
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.74.0] - 2026-04-22
11
+
12
+ ### Fixed
13
+
14
+ - ISSUE-007 Wave 9 — phase-gate ブロック時に出力される `scaffold:` 行に含まれる `<unit-id>` プレースホルダを、実 unit ID に置換するようにした。従来は L2-001 registry (`l2-error-definitions.ts`) の `defaultScaffoldCommand` が静的文字列 `npx phasegate scaffold-design --unit <unit-id> --phase logical` のまま出力されていたため、ユーザーが手で unit 名に書き換える必要があった。PHASE_GATE の場合は `metadata.unitId`、FULL_MODE_REQUIRED の場合は `targetFilePaths` から `WriteTargetScope.fromPath` で導出した unit ID を使って置換する。
15
+ - IT-AI-GUIDE-UID-001 / IT-AI-GUIDE-UID-002 を追加(PHASE_GATE と FULL_MODE_REQUIRED の両経路で `<unit-id>` が実 unit に置換されることを検証)。
16
+
10
17
  ## [0.73.0] - 2026-04-22
11
18
 
12
19
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.73.0",
3
+ "version": "0.74.0",
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": "Apache-2.0",
@@ -103,7 +103,14 @@ export class HandlePreToolUseUseCase {
103
103
  // fallthrough: continue to full-mode / story-reflection checks (which may also grandfather)
104
104
  } else {
105
105
  const guidance = await this.resolveGuidance('L2-001');
106
- return HandlePreToolUseUseCase.buildPhaseGateBlockOutput(blockedFilePath, metadata, guidance);
106
+ const unitIdForGuidance =
107
+ metadata?.unitId ?? this.deriveUnitIdFromPaths(input.targetFilePaths);
108
+ return HandlePreToolUseUseCase.buildPhaseGateBlockOutput(
109
+ blockedFilePath,
110
+ metadata,
111
+ guidance,
112
+ unitIdForGuidance,
113
+ );
107
114
  }
108
115
  } else {
109
116
  return {
@@ -125,10 +132,12 @@ export class HandlePreToolUseUseCase {
125
132
  const fullModeResult = await this.fullModeRequirementQueryPort.check(input.targetFilePaths);
126
133
  if (fullModeResult.requiresFullMode) {
127
134
  const guidance = await this.resolveGuidance('L2-001');
135
+ const unitIdForGuidance = this.deriveUnitIdFromPaths(input.targetFilePaths);
128
136
  return HandlePreToolUseUseCase.buildFullModeRequiredBlockOutput(
129
137
  input.targetFilePaths[0],
130
138
  fullModeResult,
131
139
  guidance,
140
+ unitIdForGuidance,
132
141
  );
133
142
  }
134
143
  }
@@ -198,6 +207,7 @@ export class HandlePreToolUseUseCase {
198
207
  dominantCategory?: string;
199
208
  },
200
209
  guidance: ErrorGuidance | null,
210
+ unitId: string | undefined,
201
211
  ): HandlePreToolUseOutput {
202
212
  const fp = blockedFilePath ?? '不明なファイル';
203
213
  const lines: string[] = [
@@ -214,7 +224,7 @@ export class HandlePreToolUseUseCase {
214
224
  }
215
225
  const suggestedSkill = guidance?.suggestedSkill ?? '/story-implementor';
216
226
  lines.push(`次のアクション: ${suggestedSkill} スキルを使用して設計フェーズから開始してください。`);
217
- HandlePreToolUseUseCase.appendGuidanceLines(lines, guidance);
227
+ HandlePreToolUseUseCase.appendGuidanceLines(lines, guidance, unitId);
218
228
 
219
229
  return {
220
230
  shouldBlock: true,
@@ -227,16 +237,34 @@ export class HandlePreToolUseUseCase {
227
237
  };
228
238
  }
229
239
 
230
- private static appendGuidanceLines(lines: string[], guidance: ErrorGuidance | null): void {
240
+ private static appendGuidanceLines(
241
+ lines: string[],
242
+ guidance: ErrorGuidance | null,
243
+ unitId?: string,
244
+ ): void {
231
245
  if (guidance === null) return;
232
246
  if (guidance.scaffoldCommand !== null) {
233
- lines.push(` scaffold: ${guidance.scaffoldCommand}`);
247
+ const command = unitId !== undefined && unitId !== ''
248
+ ? guidance.scaffoldCommand.replaceAll('<unit-id>', unitId)
249
+ : guidance.scaffoldCommand;
250
+ lines.push(` scaffold: ${command}`);
234
251
  }
235
252
  if (guidance.templatePath !== null) {
236
253
  lines.push(` テンプレ: ${guidance.templatePath}`);
237
254
  }
238
255
  }
239
256
 
257
+ private deriveUnitIdFromPaths(targetFilePaths: readonly string[]): string | undefined {
258
+ const projectPaths = this.configQueryPort.getProjectPaths();
259
+ for (const targetFilePath of targetFilePaths) {
260
+ const scope = WriteTargetScope.fromPath(targetFilePath, projectPaths);
261
+ if (scope?.unitId !== undefined) {
262
+ return scope.unitId;
263
+ }
264
+ }
265
+ return undefined;
266
+ }
267
+
240
268
  private resolveStoryReflectionScope(input: HandlePreToolUseInput): WriteTargetScope | null {
241
269
  if (!HandlePreToolUseUseCase.WRITE_TOOLS.has(input.toolName)) {
242
270
  return null;
@@ -264,6 +292,7 @@ export class HandlePreToolUseUseCase {
264
292
  blockedFilePath: string | undefined,
265
293
  metadata: BlockMetadata,
266
294
  guidance: ErrorGuidance | null,
295
+ unitId: string | undefined,
267
296
  ): HandlePreToolUseOutput {
268
297
  const levelLabel = metadata.scopeLevel
269
298
  ? HandlePreToolUseUseCase.LEVEL_LABELS[metadata.scopeLevel] ?? `Level ${metadata.scopeLevel}`
@@ -286,7 +315,7 @@ export class HandlePreToolUseUseCase {
286
315
  if (metadata.unitId) {
287
316
  lines.push(` 実行例: ${suggestedSkill} --unit ${metadata.unitId}`);
288
317
  }
289
- HandlePreToolUseUseCase.appendGuidanceLines(lines, guidance);
318
+ HandlePreToolUseUseCase.appendGuidanceLines(lines, guidance, unitId);
290
319
 
291
320
  return {
292
321
  shouldBlock: true,