phasegate 0.283.0 → 0.335.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 +16 -0
- package/docs/ADR/017-warning-severity-aggregation.md +17 -0
- package/docs/ADR/038-config-state-operation-permission-policy.md +78 -0
- package/docs/ADR/039-hook-observable-state-as-authorization-unit.md +74 -0
- package/docs/ADR/040-quick-mode-config-via-preset-resolution.md +66 -0
- package/docs/guide/cli-reference.md +89 -7
- package/docs/guide/configuration.md +43 -2
- package/docs/guide/layer-model.md +2 -0
- package/docs/guide/quick-vs-full-mode.md +63 -3
- package/docs/guide/troubleshooting.md +37 -0
- package/docs/templates/agent-context/CLAUDE.md.template.md +6 -6
- package/package.json +2 -2
- package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +5 -1
- package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +108 -20
- package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +215 -5
- package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +5 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/file-system-full-mode-session-query-adapter.ts +75 -23
- package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +18 -3
- package/scripts/harness/agent-integration/infrastructure/adapters/quick-mode-full-mode-requirement-adapter.ts +15 -1
- package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +57 -9
- package/scripts/harness/ci-governance/application/dto/scaffold-inception-input.ts +9 -0
- package/scripts/harness/ci-governance/application/dto/scaffold-inception-output.ts +13 -0
- package/scripts/harness/ci-governance/application/usecases/list-templates-usecase.ts +30 -0
- package/scripts/harness/ci-governance/application/usecases/scaffold-inception-usecase.ts +63 -0
- package/scripts/harness/ci-governance/application/usecases/show-template-usecase.ts +39 -0
- package/scripts/harness/ci-governance/composition-root.ts +57 -5
- package/scripts/harness/ci-governance/domain/ports/inception-doc-writer-port.ts +19 -0
- package/scripts/harness/ci-governance/domain/ports/inception-template-repository-port.ts +16 -0
- package/scripts/harness/ci-governance/domain/ports/template-catalog-port.ts +23 -0
- package/scripts/harness/ci-governance/domain/services/claude-md-composer.ts +20 -11
- package/scripts/harness/ci-governance/domain/value-objects/inception-doc-kind.ts +109 -0
- package/scripts/harness/ci-governance/domain/value-objects/template-catalog-entry.ts +53 -0
- package/scripts/harness/ci-governance/domain/value-objects/template-name.ts +50 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/file-system-inception-doc-writer-adapter.ts +42 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/file-system-inception-template-repository-adapter.ts +32 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/file-system-template-catalog-adapter.ts +60 -0
- package/scripts/harness/ci-governance/presentation/handlers/check-repetition-handler.ts +10 -2
- package/scripts/harness/ci-governance/presentation/handlers/scaffold-inception-handler.ts +101 -0
- package/scripts/harness/ci-governance/presentation/handlers/templates-handler.ts +103 -0
- package/scripts/harness/config-foundation/domain/harness-config.ts +12 -0
- package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +3 -3
- package/scripts/harness/config-foundation/infrastructure/presets/standard.json +3 -3
- package/scripts/harness/config-foundation/infrastructure/presets/strict.json +3 -3
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +44 -1
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +42 -1
- package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +12 -3
- package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +2 -22
- package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +3 -1
- package/scripts/harness/harness-error/application/dto/create-harness-error-input.ts +3 -1
- package/scripts/harness/harness-error/application/dto/harness-error-contract.ts +3 -1
- package/scripts/harness/harness-error/application/mappers/harness-error-contract-mapper.ts +9 -17
- package/scripts/harness/harness-error/application/usecases/create-harness-error-use-case.ts +7 -7
- package/scripts/harness/harness-error/domain/services/harness-error-factory.ts +30 -33
- package/scripts/harness/harness-error/domain/value-objects/error-definition.ts +28 -17
- package/scripts/harness/harness-error/domain/value-objects/harness-error.ts +30 -9
- package/scripts/harness/harness-error/domain/value-objects/remediation-type.ts +30 -0
- package/scripts/harness/harness-error/infrastructure/registry/l2-error-definitions.ts +44 -29
- package/scripts/harness/harness-error/infrastructure/registry/l3-error-definitions.ts +44 -27
- package/scripts/harness/harness-error/infrastructure/registry/l4-error-definitions.ts +47 -32
- package/scripts/harness/installation/application/checks/claude-context-missing-check.ts +13 -7
- package/scripts/harness/installation/application/checks/config-status-check.ts +52 -0
- package/scripts/harness/installation/application/checks/husky-pre-commit-missing-check.ts +6 -0
- package/scripts/harness/installation/application/ports/config-status-probe-port.ts +9 -0
- package/scripts/harness/installation/application/usecases/run-doctor-diagnostics.ts +30 -8
- package/scripts/harness/installation/application/usecases/run-install.ts +44 -11
- package/scripts/harness/installation/application/usecases/run-reconcile.ts +44 -11
- package/scripts/harness/installation/composition-root.ts +13 -3
- package/scripts/harness/installation/domain/check-id.ts +2 -0
- package/scripts/harness/installation/domain/config-status.ts +17 -0
- package/scripts/harness/installation/domain/ports/heuristic-check.ts +10 -1
- package/scripts/harness/installation/infrastructure/adapters/config-status-probe-adapter.ts +79 -0
- package/scripts/harness/installation/presentation/cli/doctor-handler.ts +6 -1
- package/scripts/harness/installation/presentation/formatters/diagnostic-report-formatter.ts +19 -5
- package/scripts/harness/integrations/pre-commit.ts +17 -3
- package/scripts/harness/main.ts +159 -21
- package/scripts/harness/phase-dependency-model/application/services/evidence-bundle-assembler.ts +2 -1
- package/scripts/harness/phase-dependency-model/domain/ports/plan-document-reader-port.ts +12 -0
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/markdown-plan-document-reader.ts +64 -33
- package/scripts/harness/phase2-extensions/presentation/handlers/check-freshness-handler.ts +17 -9
- package/scripts/harness/quick-mode/application/ports/file-existence-port.ts +15 -0
- package/scripts/harness/quick-mode/application/usecases/classify-change-category-usecase.ts +60 -22
- package/scripts/harness/quick-mode/composition-root.ts +25 -15
- package/scripts/harness/quick-mode/domain/errors/quick-mode-config-error.ts +19 -0
- package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +116 -18
- package/scripts/harness/quick-mode/domain/value-objects/category-override-rules.ts +147 -0
- package/scripts/harness/quick-mode/domain/value-objects/change-category.ts +33 -0
- package/scripts/harness/quick-mode/domain/value-objects/quick-mode-config.ts +31 -10
- package/scripts/harness/quick-mode/index.ts +7 -1
- package/scripts/harness/quick-mode/infrastructure/adapters/fs-file-existence-adapter.ts +38 -0
- package/scripts/harness/quick-mode/infrastructure/adapters/harness-config-quick-mode-config-adapter.ts +83 -32
- package/scripts/harness/skill-quality/infrastructure/adapters/file-system-requirement-test-matrix-adapter.ts +51 -8
- package/scripts/harness/skill-quality/presentation/handlers/check-coverage-handler.ts +13 -6
- package/scripts/harness/traceability-model/domain/value-objects/work-item-frontmatter.ts +5 -1
- package/scripts/harness/traceability-model/infrastructure/gateways/file-system-work-item-identity-gateway.ts +6 -1
- package/scripts/harness/traceability-model/infrastructure/gateways/file-system-work-item-status-gateway.ts +15 -2
- package/scripts/harness/validator-system/application/use-cases/aggregate-validation-results-usecase.ts +11 -14
- package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +8 -0
- package/scripts/harness/validator-system/domain/services/effective-severity-policy.ts +39 -0
- package/scripts/harness/validator-system/domain/value-objects/consistency-report.ts +6 -4
- package/scripts/harness/validator-system/domain/value-objects/drift-report.ts +12 -7
- package/scripts/harness/validator-system/domain/value-objects/validation-result.ts +11 -3
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-security-pattern-scanner-adapter.ts +20 -17
- package/scripts/harness/validator-system/infrastructure/adapters/phase-dependency-phase-gate-policy-adapter.ts +35 -14
- package/scripts/harness/validator-system/infrastructure/adapters/traceability-metadata-policy-adapter.ts +13 -8
- package/scripts/harness/validator-system/presentation/formatters/agent-validation-result-formatter.ts +19 -10
- package/skills/product-architect/SKILL.md +19 -0
- package/skills/quick-implementor/SKILL.md +19 -0
- package/skills/story-mapper/SKILL.md +11 -0
- package/skills/story-writer/SKILL.md +11 -0
- package/skills/unit-designer/SKILL.md +11 -0
- package/templates/product_overview.template.md +85 -0
- package/templates/product_overview_plan.template.md +55 -0
- package/templates/story_mapping_plan.template.md +61 -0
- package/templates/story_writer_plan.template.md +61 -0
- package/templates/unit_design_plan.template.md +63 -0
|
@@ -27,6 +27,43 @@ Each finding has a severity, a repair mode, and optional next-step fields.
|
|
|
27
27
|
| `ci-workflow-missing` | `npx phasegate setup:agent --intent ci-only --with-ci --dry-run --json` |
|
|
28
28
|
| agent context drift | `npx phasegate reconcile --dry-run` |
|
|
29
29
|
|
|
30
|
+
## Phase Gate Blocked a Write and I Do Not Know What the Document Should Contain
|
|
31
|
+
|
|
32
|
+
<!-- @work-item-id WI-356 -->
|
|
33
|
+
|
|
34
|
+
Phase Gate blocks (`L2-001`, `FULL_MODE_REQUIRED`) mean a required inception or product design document is missing. Two things are needed: the file skeleton, and the section structure to fill in.
|
|
35
|
+
|
|
36
|
+
**1. Generate the skeleton.** `scaffold-design` writes a placeholder document for the missing phase:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx phasegate scaffold-design --unit <unit-id> --phase logical --dry-run
|
|
40
|
+
npx phasegate scaffold-design --unit <unit-id> --phase logical --apply
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Accepted `--phase` values: `logical`, `domain`, `uiux`, `unit-test`, `it-test`.
|
|
44
|
+
|
|
45
|
+
**2. Read the section structure from the skill definition.** PhaseGate does not ship fill-in templates into your repo; the authoritative structure of every plan and inception artifact lives in the skill markdown that produces it. `install` / `init` copy these into `skills/` in your repository:
|
|
46
|
+
|
|
47
|
+
| Document | Skill definition |
|
|
48
|
+
|---|---|
|
|
49
|
+
| product architecture / unit decomposition | `skills/product-architect/SKILL.md` |
|
|
50
|
+
| story map | `skills/story-mapper/SKILL.md` |
|
|
51
|
+
| story descriptions / acceptance criteria | `skills/story-writer/SKILL.md` |
|
|
52
|
+
| unit plan | `skills/unit-designer/SKILL.md` |
|
|
53
|
+
| `logical_design.md` | `skills/logical-designer/SKILL.md` |
|
|
54
|
+
| `domain_model.md` | `skills/domain-designer/SKILL.md` |
|
|
55
|
+
| UI/UX design | `skills/uiux-designer/SKILL.md` |
|
|
56
|
+
| unit / IT / scenario test plans | `skills/unit-test-designer/SKILL.md`, `skills/it-test-designer/SKILL.md`, `skills/scenario-test-designer/SKILL.md` |
|
|
57
|
+
|
|
58
|
+
If `skills/` is not present in your repository — the personal install path does not copy it — read the same content from PhaseGate's own package instead:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx phasegate skills list
|
|
62
|
+
npx phasegate skills info logical-designer
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`skills info` prints the full SKILL.md to stdout, so it works from a sandboxed agent that cannot read `node_modules/`.
|
|
66
|
+
|
|
30
67
|
## Refused Managed Target
|
|
31
68
|
|
|
32
69
|
If `install`, `reconcile`, or `uninstall` refuses a target, do not immediately force it. Inspect the diff and ask the agent to explain:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLAUDE.md
|
|
2
2
|
|
|
3
|
-
<!-- @work-item-id WI-174, WI-176, WI-177 -->
|
|
3
|
+
<!-- @work-item-id WI-174, WI-176, WI-177, WI-331 -->
|
|
4
4
|
|
|
5
5
|
<!-- phasegate:managed-section:start -->
|
|
6
6
|
## 必読ドキュメント
|
|
@@ -38,13 +38,13 @@ If `setup:agent --apply --json` or `install --apply --json` fails with a structu
|
|
|
38
38
|
|
|
39
39
|
{{PHASEGATE_SKILLS}}
|
|
40
40
|
|
|
41
|
+
## Agent Context Refresh
|
|
42
|
+
|
|
43
|
+
Run `phasegate ci:auto-refresh-agent-context --dry-run` to preview updates and `phasegate ci:auto-refresh-agent-context --apply` to write AGENTS.md / CLAUDE.md.
|
|
44
|
+
<!-- phasegate:managed-section:end -->
|
|
45
|
+
|
|
41
46
|
## User Section
|
|
42
47
|
|
|
43
48
|
<!-- phasegate:user-section:start -->
|
|
44
49
|
{{PHASEGATE_USER_SECTION}}
|
|
45
50
|
<!-- phasegate:user-section:end -->
|
|
46
|
-
|
|
47
|
-
## Agent Context Refresh
|
|
48
|
-
|
|
49
|
-
Run `phasegate ci:auto-refresh-agent-context --dry-run` to preview updates and `phasegate ci:auto-refresh-agent-context --apply` to write AGENTS.md / CLAUDE.md.
|
|
50
|
-
<!-- phasegate:managed-section:end -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phasegate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.335.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": "MIT",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"harness:check-phase": "pnpm phasegate:check-phase",
|
|
61
61
|
"harness:check-ready": "pnpm phasegate:check-ready",
|
|
62
62
|
"test": "vitest run --config scripts/harness/__tests__/vitest.config.forks.ts && vitest run --config scripts/harness/__tests__/vitest.config.ts",
|
|
63
|
-
"coverage": "node -e \"require('node:fs').rmSync('coverage',{recursive:true,force:true})\" && vitest run --config scripts/harness/__tests__/vitest.config.coverage.forks.ts --reporter=blob --outputFile=coverage/.blob/forks.json && vitest run --config scripts/harness/__tests__/vitest.config.coverage.ts --reporter=blob --outputFile=coverage/.blob/threads.json && vitest run --config scripts/harness/__tests__/vitest.config.coverage.ts --merge-reports=coverage/.blob",
|
|
63
|
+
"coverage": "node -e \"require('node:fs').rmSync('coverage',{recursive:true,force:true})\" && vitest run --config scripts/harness/__tests__/vitest.config.coverage.forks.ts --reporter=blob --reporter=default --outputFile.blob=coverage/.blob/forks.json && vitest run --config scripts/harness/__tests__/vitest.config.coverage.ts --reporter=blob --reporter=default --outputFile.blob=coverage/.blob/threads.json && vitest run --config scripts/harness/__tests__/vitest.config.coverage.ts --merge-reports=coverage/.blob",
|
|
64
64
|
"ci": "npm run coverage && npm run test"
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
* @unit agent-integration
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* WI-376 (ADR-039): 呼び出し元 skill 名を受け取るフィールドは持たない。
|
|
8
|
+
* 判定・案内の入力は hook が自ら観測・検証できる state(対象パス、変更カテゴリ、
|
|
9
|
+
* session marker、設計文書の存在、解決済み config 等)に限る。
|
|
10
|
+
*/
|
|
6
11
|
export interface HandlePreToolUseInput {
|
|
7
12
|
toolName: string;
|
|
8
13
|
targetFilePaths: string[];
|
|
9
|
-
callerSkill?: string;
|
|
10
14
|
targetChanges?: {
|
|
11
15
|
filePath: string;
|
|
12
16
|
beforeContent?: string | null;
|
package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* @work-item-id WI-202 / WI-204
|
|
7
7
|
* @work-item-id WI-206
|
|
8
8
|
* @work-item-id WI-214
|
|
9
|
+
* @work-item-id WI-349
|
|
10
|
+
* @work-item-id WI-354
|
|
11
|
+
* @work-item-id WI-376
|
|
9
12
|
*
|
|
10
13
|
* HandlePreToolUseUseCase
|
|
11
14
|
* PreToolUse Hook処理のオーケストレーション
|
|
@@ -17,11 +20,11 @@ import type {
|
|
|
17
20
|
} from "../../domain/ports/baseline-grandfather-query-port.js";
|
|
18
21
|
import type { ConfigQueryPort } from "../../domain/ports/config-query-port.js";
|
|
19
22
|
import type { ErrorGuidance, ErrorGuidanceQueryPort } from "../../domain/ports/error-guidance-query-port.js";
|
|
23
|
+
import type { FullModeRequirementQueryPort } from "../../domain/ports/full-mode-requirement-query-port.js";
|
|
20
24
|
import type {
|
|
21
25
|
FullModeSessionQueryPort,
|
|
22
26
|
FullModeSessionQueryResult,
|
|
23
27
|
} from "../../domain/ports/full-mode-session-query-port.js";
|
|
24
|
-
import type { FullModeRequirementQueryPort } from "../../domain/ports/full-mode-requirement-query-port.js";
|
|
25
28
|
import type { PhaseGateQueryPort } from "../../domain/ports/phase-gate-query-port.js";
|
|
26
29
|
import type { StoryReflectionQueryPort } from "../../domain/ports/story-reflection-query-port.js";
|
|
27
30
|
import { AsyncHookToCliTranslator } from "../../domain/services/hook-to-cli-translator.js";
|
|
@@ -185,7 +188,7 @@ export class HandlePreToolUseUseCase {
|
|
|
185
188
|
fullModeResult,
|
|
186
189
|
guidance,
|
|
187
190
|
unitIdForGuidance,
|
|
188
|
-
|
|
191
|
+
sessionResult,
|
|
189
192
|
);
|
|
190
193
|
}
|
|
191
194
|
} else {
|
|
@@ -281,22 +284,20 @@ export class HandlePreToolUseUseCase {
|
|
|
281
284
|
},
|
|
282
285
|
guidance: ErrorGuidance | null,
|
|
283
286
|
unitId: string | undefined,
|
|
284
|
-
|
|
287
|
+
sessionResult?: FullModeSessionQueryResult,
|
|
285
288
|
): HandlePreToolUseOutput {
|
|
286
289
|
const fp = blockedFilePath ?? "不明なファイル";
|
|
287
290
|
if (result.dominantCategory === "config" && /(?:^|\/)phasegate\.config\.json$/.test(fp)) {
|
|
288
291
|
const dryRunCommand = "phasegate config:plan --intent quick-mode-relax --dry-run --json";
|
|
289
292
|
const applyCommand = "phasegate config:plan --intent quick-mode-relax --apply --json";
|
|
290
|
-
const lines = [
|
|
291
|
-
`Full mode 必須変更が検出されました: ${fp}`,
|
|
292
|
-
"カテゴリ: config",
|
|
293
|
-
];
|
|
293
|
+
const lines = [`Full mode 必須変更が検出されました: ${fp}`, "カテゴリ: config"];
|
|
294
294
|
if (result.rejectionRule) {
|
|
295
295
|
lines.push(`判定ルール: ${result.rejectionRule}`);
|
|
296
296
|
}
|
|
297
297
|
if (result.rejectionReason) {
|
|
298
298
|
lines.push(`理由: ${result.rejectionReason}`);
|
|
299
299
|
}
|
|
300
|
+
HandlePreToolUseUseCase.appendJudgmentContextLines(lines, sessionResult);
|
|
300
301
|
lines.push(`次のアクション: ${dryRunCommand} で差分を確認し、承認後に ${applyCommand} を実行してください。`);
|
|
301
302
|
|
|
302
303
|
return {
|
|
@@ -309,22 +310,24 @@ export class HandlePreToolUseUseCase {
|
|
|
309
310
|
nextAction: `${dryRunCommand} && ${applyCommand}`,
|
|
310
311
|
};
|
|
311
312
|
}
|
|
312
|
-
if (
|
|
313
|
-
callerSkill === "quick-implementor" &&
|
|
314
|
-
result.dominantCategory !== undefined &&
|
|
315
|
-
["bugfix", "docs", "test", "config"].includes(result.dominantCategory)
|
|
316
|
-
) {
|
|
313
|
+
if (HandlePreToolUseUseCase.shouldGuideQuickModeRelax(result.dominantCategory)) {
|
|
317
314
|
const dryRunCommand = "phasegate config:plan --intent quick-mode-relax --dry-run --json";
|
|
318
315
|
const applyCommand = "phasegate config:plan --intent quick-mode-relax --apply --json";
|
|
319
316
|
const lines: string[] = [`Full mode 必須変更が検出されました: ${fp}`];
|
|
320
|
-
|
|
317
|
+
if (result.dominantCategory) {
|
|
318
|
+
lines.push(`カテゴリ: ${result.dominantCategory}`);
|
|
319
|
+
}
|
|
321
320
|
if (result.rejectionRule) {
|
|
322
321
|
lines.push(`判定ルール: ${result.rejectionRule}`);
|
|
323
322
|
}
|
|
324
323
|
if (result.rejectionReason) {
|
|
325
324
|
lines.push(`理由: ${result.rejectionReason}`);
|
|
326
325
|
}
|
|
327
|
-
|
|
326
|
+
HandlePreToolUseUseCase.appendJudgmentContextLines(lines, sessionResult);
|
|
327
|
+
lines.push(
|
|
328
|
+
`次のアクション: Quick Mode の許可カテゴリを確認してください。緩和する場合は ${dryRunCommand} で差分を確認し、承認後に ${applyCommand} を実行してください。`,
|
|
329
|
+
);
|
|
330
|
+
lines.push(` 分類の確認: npx phasegate check-change-category --paths ${fp}`);
|
|
328
331
|
|
|
329
332
|
return {
|
|
330
333
|
shouldBlock: true,
|
|
@@ -346,9 +349,15 @@ export class HandlePreToolUseUseCase {
|
|
|
346
349
|
if (result.rejectionReason) {
|
|
347
350
|
lines.push(`理由: ${result.rejectionReason}`);
|
|
348
351
|
}
|
|
352
|
+
HandlePreToolUseUseCase.appendJudgmentContextLines(lines, sessionResult);
|
|
349
353
|
const suggestedSkill = guidance?.suggestedSkill ?? "/story-implementor";
|
|
350
354
|
lines.push(`次のアクション: ${suggestedSkill} スキルを使用して設計フェーズから開始してください。`);
|
|
351
355
|
if (unitId !== undefined && unitId !== "") {
|
|
356
|
+
if (HandlePreToolUseUseCase.isSessionActiveButRejected(sessionResult)) {
|
|
357
|
+
lines.push(
|
|
358
|
+
` 既存 session は上記理由で今回の書き込みに使えません。張り直す場合: phasegate session end --work-item ${sessionResult?.workItemId ?? "<WI-XXX>"} を実行してから下記を実行してください。`,
|
|
359
|
+
);
|
|
360
|
+
}
|
|
352
361
|
lines.push(
|
|
353
362
|
` 実装フェーズ開始時: phasegate session begin --mode full --unit ${unitId} --work-item <WI-XXX> --reason "<reason>" --duration 1h`,
|
|
354
363
|
);
|
|
@@ -367,6 +376,59 @@ export class HandlePreToolUseUseCase {
|
|
|
367
376
|
};
|
|
368
377
|
}
|
|
369
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Quick Mode スコープのカテゴリ(bugfix / docs / test / config)は quick-implementor で
|
|
381
|
+
* 完遂できる変更であり、遮断の実体は「allowedCategories が絞られている」ことである。
|
|
382
|
+
* ここで /story-implementor を案内すると、設計フェーズからやり直せという誤った指示になる。
|
|
383
|
+
*
|
|
384
|
+
* WI-354: 従来は callerSkill === "quick-implementor" を条件にしていたが、
|
|
385
|
+
* callerSkill を供給する producer(hook input の caller_skill / PHASEGATE_CALLER_SKILL)は
|
|
386
|
+
* 実運用で設定されず、この分岐は到達不能だった。カテゴリを一次条件にして
|
|
387
|
+
* skill context なしでも実用的な復旧手順を出す。
|
|
388
|
+
* feature / domain / api は従来どおり /story-implementor 誘導を維持する。
|
|
389
|
+
*
|
|
390
|
+
* WI-376 (ADR-039): 判定入力は hook が自ら観測できる state のみとする方針に従い、
|
|
391
|
+
* カテゴリ未確定時の callerSkill フォールバックを削除した。カテゴリが取れない場合は
|
|
392
|
+
* 一律 /story-implementor 誘導(従来のフォールバックも実運用では常にこの経路だった)。
|
|
393
|
+
*/
|
|
394
|
+
private static readonly QUICK_MODE_SCOPE_CATEGORIES: readonly string[] = ["bugfix", "docs", "test", "config"];
|
|
395
|
+
|
|
396
|
+
private static shouldGuideQuickModeRelax(dominantCategory: string | undefined): boolean {
|
|
397
|
+
if (dominantCategory === undefined) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
return HandlePreToolUseUseCase.QUICK_MODE_SCOPE_CATEGORIES.includes(dominantCategory);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* WI-349: ブロック理由に判定根拠を明示する。
|
|
405
|
+
*
|
|
406
|
+
* - 判定対象は「今回の書き込み対象パス」だけであり、ワークツリー上の未コミット変更は
|
|
407
|
+
* 一切含まれない。この誤解が「無関係な変更のせいでブロックされている」という
|
|
408
|
+
* 誤った原因究明を招いていた(issue #41 症状②)。
|
|
409
|
+
* - アクティブな Full Mode session がありながら不許可だった場合は、その理由
|
|
410
|
+
* (unit 不一致 / category 不一致 / 期限切れ 等)を提示する。これがないと
|
|
411
|
+
* 「session begin せよ」と案内されながら session は既に有効、という最悪の混乱になる。
|
|
412
|
+
*/
|
|
413
|
+
private static appendJudgmentContextLines(
|
|
414
|
+
lines: string[],
|
|
415
|
+
sessionResult: FullModeSessionQueryResult | undefined,
|
|
416
|
+
): void {
|
|
417
|
+
lines.push("判定対象: 今回の書き込み対象パスのみです(ワークツリー上の他の未コミット変更は判定に含まれません)。");
|
|
418
|
+
if (!HandlePreToolUseUseCase.isSessionActiveButRejected(sessionResult)) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const workItemId = sessionResult?.workItemId ?? "<unknown>";
|
|
422
|
+
const unit = sessionResult?.unit ?? "<unknown>";
|
|
423
|
+
const expiresAt = sessionResult?.expiresAt ?? "<unknown>";
|
|
424
|
+
lines.push(`アクティブな Full Mode session: ${workItemId} (unit=${unit}, expiresAt=${expiresAt})`);
|
|
425
|
+
lines.push(`session が書き込みを許可しなかった理由: ${sessionResult?.reason ?? "<unknown>"}`);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
private static isSessionActiveButRejected(sessionResult: FullModeSessionQueryResult | undefined): boolean {
|
|
429
|
+
return sessionResult !== undefined && sessionResult.active && !sessionResult.allowed;
|
|
430
|
+
}
|
|
431
|
+
|
|
370
432
|
private static appendGuidanceLines(lines: string[], guidance: ErrorGuidance | null, unitId?: string): void {
|
|
371
433
|
if (guidance === null) return;
|
|
372
434
|
if (guidance.scaffoldCommand !== null) {
|
|
@@ -377,10 +439,25 @@ export class HandlePreToolUseUseCase {
|
|
|
377
439
|
lines.push(` scaffold: ${command}`);
|
|
378
440
|
}
|
|
379
441
|
if (guidance.templatePath !== null) {
|
|
380
|
-
lines.push(`
|
|
442
|
+
lines.push(` 構成リファレンス: ${HandlePreToolUseUseCase.describeStructureReference(guidance.templatePath)}`);
|
|
381
443
|
}
|
|
382
444
|
}
|
|
383
445
|
|
|
446
|
+
/**
|
|
447
|
+
* WI-356 (issue #29): 参照先が skill 定義(skills/<name>/SKILL.md)の場合、
|
|
448
|
+
* skills/ をユーザー repo に配置しない personal install 経路でも読めるよう
|
|
449
|
+
* stdout 経路(phasegate skills info)を併記する。
|
|
450
|
+
*/
|
|
451
|
+
private static readonly SKILL_REFERENCE_PATTERN = /^skills\/([^/]+)\/SKILL\.md$/;
|
|
452
|
+
|
|
453
|
+
private static describeStructureReference(templatePath: string): string {
|
|
454
|
+
const skillMatch = HandlePreToolUseUseCase.SKILL_REFERENCE_PATTERN.exec(templatePath);
|
|
455
|
+
if (skillMatch === null) {
|
|
456
|
+
return templatePath;
|
|
457
|
+
}
|
|
458
|
+
return `${templatePath}(未配置なら: npx phasegate skills info ${skillMatch[1]})`;
|
|
459
|
+
}
|
|
460
|
+
|
|
384
461
|
private deriveUnitIdFromPaths(targetFilePaths: readonly string[]): string | undefined {
|
|
385
462
|
const projectPaths = this.configQueryPort.getProjectPaths();
|
|
386
463
|
for (const targetFilePath of targetFilePaths) {
|
|
@@ -419,8 +496,15 @@ export class HandlePreToolUseUseCase {
|
|
|
419
496
|
}
|
|
420
497
|
|
|
421
498
|
private static isUnderInception(targetFilePath: string, inceptionPath: string): boolean {
|
|
422
|
-
const normalizedTarget = targetFilePath
|
|
423
|
-
|
|
499
|
+
const normalizedTarget = targetFilePath
|
|
500
|
+
.replaceAll("\\", "/")
|
|
501
|
+
.replace(/^\.\/+/, "")
|
|
502
|
+
.replace(/\/+/g, "/");
|
|
503
|
+
const normalizedBase = inceptionPath
|
|
504
|
+
.replaceAll("\\", "/")
|
|
505
|
+
.replace(/^\.\/+/, "")
|
|
506
|
+
.replace(/\/+/g, "/")
|
|
507
|
+
.replace(/\/$/, "");
|
|
424
508
|
return normalizedTarget === normalizedBase || normalizedTarget.startsWith(`${normalizedBase}/`);
|
|
425
509
|
}
|
|
426
510
|
|
|
@@ -493,6 +577,12 @@ export class HandlePreToolUseUseCase {
|
|
|
493
577
|
message: (fp) =>
|
|
494
578
|
`保護ファイルへの書き込みがブロックされました: ${fp}\nClaude Code の設定変更は /update-config スキルを使用してください。`,
|
|
495
579
|
},
|
|
580
|
+
{
|
|
581
|
+
// WI-363: .husky/ 配下は L0 runtime の実施点。Quick Mode でも書き換えさせない。
|
|
582
|
+
pattern: /(?:^|\/)\.husky\//,
|
|
583
|
+
message: (fp) =>
|
|
584
|
+
`保護ファイルへの書き込みがブロックされました: ${fp}\n.husky/ 配下は L0 runtime(pre-commit / commit-msg / pre-push)の実施点であり、Quick Mode でも変更できません。\n未導入 hook の配置は npx phasegate setup:agent --apply --with-husky を使用してください。\n意図的に手編集する場合は phasegate.config.json の protectedFiles.exclude に対象パターンを明示してください。`,
|
|
585
|
+
},
|
|
496
586
|
];
|
|
497
587
|
|
|
498
588
|
private async buildProtectedFileBlockOutput(blockedFilePath: string | undefined): Promise<HandlePreToolUseOutput> {
|
|
@@ -577,9 +667,7 @@ export class HandlePreToolUseUseCase {
|
|
|
577
667
|
lines.push("");
|
|
578
668
|
lines.push("修正方法:");
|
|
579
669
|
lines.push(" 1. cascade-updater を実行して product 文書を更新");
|
|
580
|
-
lines.push(
|
|
581
|
-
` 2. または手動で該当 product 文書に @${annotationKey} ${details?.storyId ?? "<WORK-ITEM-ID>"} を追加`,
|
|
582
|
-
);
|
|
670
|
+
lines.push(` 2. または手動で該当 product 文書に @${annotationKey} ${details?.storyId ?? "<WORK-ITEM-ID>"} を追加`);
|
|
583
671
|
lines.push("");
|
|
584
672
|
lines.push("参照: ADR-XXX");
|
|
585
673
|
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* 副作用なし・純粋関数相当。対応パターンは以下:
|
|
11
11
|
* - リダイレクト `>` / `>>` / `>|` (clobber)
|
|
12
|
-
* - heredoc (`<<EOF > path`)
|
|
12
|
+
* - heredoc (`<<EOF > path`) — ヒアドキュメント**本文**はシェル構文解析の対象外
|
|
13
|
+
* (本文中の `<...>` をリダイレクトと誤認しないため。WI-362)
|
|
13
14
|
* - `tee` / `tee -a`
|
|
14
15
|
* - `sed -i` / `sed -i ''` (BSD)
|
|
15
16
|
* - `cp` / `mv` (宛先のみ)
|
|
@@ -111,6 +112,191 @@ function tokenize(input: string): Token[] {
|
|
|
111
112
|
return tokens;
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
/** ヒアドキュメント開始演算子で宣言された終端デリミタ */
|
|
116
|
+
type HeredocDelimiter = {
|
|
117
|
+
/** クォート・エスケープを解除した終端語 */
|
|
118
|
+
readonly word: string;
|
|
119
|
+
/** `<<-` 形式か (終端行の先頭タブを無視する) */
|
|
120
|
+
readonly allowIndent: boolean;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/** デリミタ語の終端となる文字 (空白およびシェル演算子) */
|
|
124
|
+
const DELIMITER_TERMINATORS = new Set([' ', '\t', ';', '&', '|', '<', '>', '(', ')']);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 1 行から ヒアドキュメント開始演算子 (`<<WORD` / `<<-WORD` / `<<'WORD'`) を検出し、
|
|
128
|
+
* 宣言順に終端デリミタを返す。
|
|
129
|
+
*
|
|
130
|
+
* クォート状態を追跡し、クォート内の `<<` は演算子として扱わない
|
|
131
|
+
* (例: `echo "a << b" > out.txt` の `<<` はヒアドキュメントではない)。
|
|
132
|
+
* `$( ... )` コマンド置換の内側ではクォート文脈がリセットされるため、
|
|
133
|
+
* `git commit -m "$(cat <<'EOF'` のような入れ子も検出できる。
|
|
134
|
+
* ヒアストリング `<<<` は本文を持たないため対象外。
|
|
135
|
+
*/
|
|
136
|
+
function findHeredocDelimiters(line: string): HeredocDelimiter[] {
|
|
137
|
+
const found: HeredocDelimiter[] = [];
|
|
138
|
+
const substitutionStack: ('none' | 'single' | 'double')[] = [];
|
|
139
|
+
let quote: 'none' | 'single' | 'double' = 'none';
|
|
140
|
+
let i = 0;
|
|
141
|
+
|
|
142
|
+
while (i < line.length) {
|
|
143
|
+
const ch = line[i];
|
|
144
|
+
|
|
145
|
+
if (quote === 'single') {
|
|
146
|
+
if (ch === "'") quote = 'none';
|
|
147
|
+
i += 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (ch === '\\') {
|
|
152
|
+
i += 2;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (ch === '$' && line[i + 1] === '(') {
|
|
157
|
+
substitutionStack.push(quote);
|
|
158
|
+
quote = 'none';
|
|
159
|
+
i += 2;
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (quote === 'double') {
|
|
164
|
+
if (ch === '"') quote = 'none';
|
|
165
|
+
i += 1;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (ch === "'") {
|
|
170
|
+
quote = 'single';
|
|
171
|
+
i += 1;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (ch === '"') {
|
|
176
|
+
quote = 'double';
|
|
177
|
+
i += 1;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (ch === ')' && substitutionStack.length > 0) {
|
|
182
|
+
quote = substitutionStack.pop() ?? 'none';
|
|
183
|
+
i += 1;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (ch === '<' && line[i + 1] === '<') {
|
|
188
|
+
if (line[i + 2] === '<') {
|
|
189
|
+
// ヒアストリング `<<<` は本文行を持たない
|
|
190
|
+
i += 3;
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
i += 2;
|
|
194
|
+
let allowIndent = false;
|
|
195
|
+
if (line[i] === '-') {
|
|
196
|
+
allowIndent = true;
|
|
197
|
+
i += 1;
|
|
198
|
+
}
|
|
199
|
+
while (i < line.length && (line[i] === ' ' || line[i] === '\t')) i += 1;
|
|
200
|
+
const word = readHeredocDelimiterWord(line, i);
|
|
201
|
+
i = word.nextIndex;
|
|
202
|
+
if (word.value.length > 0) {
|
|
203
|
+
found.push({ word: word.value, allowIndent });
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
i += 1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return found;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** `<<` 直後のデリミタ語を読む。クォート/バックスラッシュを解除した値を返す。 */
|
|
215
|
+
function readHeredocDelimiterWord(line: string, start: number): { value: string; nextIndex: number } {
|
|
216
|
+
let value = '';
|
|
217
|
+
let i = start;
|
|
218
|
+
|
|
219
|
+
while (i < line.length) {
|
|
220
|
+
const ch = line[i];
|
|
221
|
+
|
|
222
|
+
if (ch === "'") {
|
|
223
|
+
i += 1;
|
|
224
|
+
while (i < line.length && line[i] !== "'") {
|
|
225
|
+
value += line[i];
|
|
226
|
+
i += 1;
|
|
227
|
+
}
|
|
228
|
+
i += 1;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (ch === '"') {
|
|
233
|
+
i += 1;
|
|
234
|
+
while (i < line.length && line[i] !== '"') {
|
|
235
|
+
if (line[i] === '\\' && i + 1 < line.length) {
|
|
236
|
+
value += line[i + 1];
|
|
237
|
+
i += 2;
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
value += line[i];
|
|
241
|
+
i += 1;
|
|
242
|
+
}
|
|
243
|
+
i += 1;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (ch === '\\' && i + 1 < line.length) {
|
|
248
|
+
value += line[i + 1];
|
|
249
|
+
i += 2;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (DELIMITER_TERMINATORS.has(ch)) break;
|
|
254
|
+
|
|
255
|
+
value += ch;
|
|
256
|
+
i += 1;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return { value, nextIndex: i };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** 終端行判定 (`<<-` は先頭タブを無視し、行末の空白・CR は許容する) */
|
|
263
|
+
function isHeredocTerminator(line: string, delimiter: HeredocDelimiter): boolean {
|
|
264
|
+
const candidate = delimiter.allowIndent ? line.replace(/^\t+/, '') : line;
|
|
265
|
+
return candidate.trimEnd() === delimiter.word;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* ヒアドキュメント本文行を除去する。
|
|
270
|
+
*
|
|
271
|
+
* 本文はシェル構文ではなくデータであり、`Co-Authored-By: Claude <noreply@example.com>`
|
|
272
|
+
* のような `<...>` をリダイレクトと誤解釈すると無関係なコマンドが誤ブロックされる (WI-362)。
|
|
273
|
+
* 除去対象は**本文行と終端行のみ**で、開始演算子のある行はそのまま残すため
|
|
274
|
+
* `cat <<EOF > path` の `> path` は従来どおり検出される (fail-closed 維持)。
|
|
275
|
+
*/
|
|
276
|
+
function stripHeredocBodies(command: string): string {
|
|
277
|
+
if (!command.includes('<<')) return command;
|
|
278
|
+
|
|
279
|
+
const lines = command.split('\n');
|
|
280
|
+
const kept: string[] = [];
|
|
281
|
+
const pending: HeredocDelimiter[] = [];
|
|
282
|
+
|
|
283
|
+
for (const line of lines) {
|
|
284
|
+
if (pending.length > 0) {
|
|
285
|
+
// 本文行 / 終端行はいずれも構文解析対象から外す
|
|
286
|
+
if (isHeredocTerminator(line, pending[0])) {
|
|
287
|
+
pending.shift();
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
kept.push(line);
|
|
292
|
+
for (const delimiter of findHeredocDelimiters(line)) {
|
|
293
|
+
pending.push(delimiter);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return kept.join('\n');
|
|
298
|
+
}
|
|
299
|
+
|
|
114
300
|
const OPERATOR_SEPARATORS = new Set(['&&', '||', ';', '|']);
|
|
115
301
|
|
|
116
302
|
/** トークン列を複合・パイプ境界で分割 */
|
|
@@ -140,6 +326,28 @@ function getCommandName(tokens: Token[]): string | undefined {
|
|
|
140
326
|
return undefined;
|
|
141
327
|
}
|
|
142
328
|
|
|
329
|
+
/** リダイレクト演算子の右辺を解釈し、fd 複製の場合は書き込み先なしとして扱う。 */
|
|
330
|
+
function getRedirectTarget(tokens: Token[], redirectIndex: number): string | undefined {
|
|
331
|
+
const next = tokens[redirectIndex + 1];
|
|
332
|
+
if (next === undefined) return undefined;
|
|
333
|
+
|
|
334
|
+
if (next.quoted === 'none' && /^&\d+$/.test(next.value)) {
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (next.quoted === 'none' && next.value === '&') {
|
|
339
|
+
const afterAmpersand = tokens[redirectIndex + 2];
|
|
340
|
+
if (afterAmpersand === undefined) return undefined;
|
|
341
|
+
if (afterAmpersand.quoted === 'none' && /^\d+$/.test(afterAmpersand.value)) {
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
// `>& file` は csh 形式の実ファイル書き込みとして安全側で抽出する。
|
|
345
|
+
return afterAmpersand.value;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return next.value;
|
|
349
|
+
}
|
|
350
|
+
|
|
143
351
|
/**
|
|
144
352
|
* 1 コマンド分のトークン列から書き込み先を抽出する。
|
|
145
353
|
* リダイレクト先はコマンド種別によらず検出する (全コマンド共通)。
|
|
@@ -151,9 +359,9 @@ function extractFromSingleCommand(tokens: Token[]): string[] {
|
|
|
151
359
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
152
360
|
const t = tokens[i];
|
|
153
361
|
if (t.quoted === 'none' && (t.value === '>' || t.value === '>>' || t.value === '>|')) {
|
|
154
|
-
const
|
|
155
|
-
if (
|
|
156
|
-
results.push(
|
|
362
|
+
const target = getRedirectTarget(tokens, i);
|
|
363
|
+
if (target !== undefined) {
|
|
364
|
+
results.push(target);
|
|
157
365
|
}
|
|
158
366
|
}
|
|
159
367
|
}
|
|
@@ -289,7 +497,9 @@ function collectPositionals(tokens: Token[]): Token[] {
|
|
|
289
497
|
|
|
290
498
|
/** コマンド文字列を token 化 → 演算子分割 → 各コマンドから書き込み先を抽出する内部関数。 */
|
|
291
499
|
function extractFromCommandString(command: string): string[] {
|
|
292
|
-
|
|
500
|
+
// ヒアドキュメント本文はデータなのでシェル構文解析の対象外にする (WI-362)。
|
|
501
|
+
// apply_patch 抽出は元の command 文字列を直接走査するため影響を受けない。
|
|
502
|
+
const tokens = tokenize(stripHeredocBodies(command));
|
|
293
503
|
const groups = splitByOperators(tokens);
|
|
294
504
|
const collected: string[] = [];
|
|
295
505
|
for (const group of groups) {
|
|
@@ -25,6 +25,11 @@ const DEFAULT_PATTERNS = [
|
|
|
25
25
|
// grandfather bypass を防ぐため、書き込み自体を保護対象とする。
|
|
26
26
|
'.phasegate/baseline.json',
|
|
27
27
|
'**/.phasegate/baseline.json',
|
|
28
|
+
// WI-363: .husky/ 配下は L0 runtime の実施点(pre-commit / commit-msg / pre-push)。
|
|
29
|
+
// WI-352 で config カテゴリに分類されるようになり Quick Mode の書き込み許可対象に
|
|
30
|
+
// 入ったため、防御機構そのものの書き換えを protected file として明示的に止める。
|
|
31
|
+
'.husky/**',
|
|
32
|
+
'**/.husky/**',
|
|
28
33
|
];
|
|
29
34
|
|
|
30
35
|
/**
|