phasegate 0.335.0 → 0.340.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.ja.md +22 -9
  3. package/README.md +28 -17
  4. package/docs/ADR/038-config-state-operation-permission-policy.md +5 -2
  5. package/docs/ADR/041-non-excludable-agent-trust-roots.md +68 -0
  6. package/docs/guide/antigravity-integration.md +26 -0
  7. package/docs/guide/cli-reference.md +4 -4
  8. package/docs/guide/codex-integration.md +44 -107
  9. package/docs/guide/grok-integration.md +26 -0
  10. package/docs/guide/hooks-integration.md +27 -4
  11. package/docs/guide/installation.md +4 -2
  12. package/docs/guide/setup-artifacts.md +4 -4
  13. package/docs/guide/skills-overview.md +1 -1
  14. package/package.json +1 -1
  15. package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +1 -1
  16. package/scripts/harness/agent-integration/application/dto/normalized-pre-tool-use-request.ts +32 -0
  17. package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +2 -2
  18. package/scripts/harness/agent-integration/domain/ports/full-mode-requirement-query-port.ts +3 -1
  19. package/scripts/harness/agent-integration/domain/services/apply-patch-write-target-extractor.ts +98 -0
  20. package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +6 -52
  21. package/scripts/harness/agent-integration/domain/value-objects/hook-translation-result.ts +1 -1
  22. package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +35 -18
  23. package/scripts/harness/agent-integration/presentation/phasegate-status-context.ts +1 -1
  24. package/scripts/harness/agent-integration/presentation/post-tool-use-hook.ts +1 -0
  25. package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +123 -70
  26. package/scripts/harness/agent-integration/presentation/pre-tool-use-payload-normalizer.ts +228 -0
  27. package/scripts/harness/agent-integration/presentation/pre-tool-use-response-renderer.ts +38 -0
  28. package/scripts/harness/ci-governance/domain/value-objects/integrity-target.ts +2 -0
  29. package/scripts/harness/installation/application/checks/antigravity-hook-missing-check.ts +79 -0
  30. package/scripts/harness/installation/application/checks/codex-hook-missing-check.ts +70 -6
  31. package/scripts/harness/installation/application/checks/grok-hook-missing-check.ts +79 -0
  32. package/scripts/harness/installation/application/checks/husky-runtime-inactive-check.ts +33 -0
  33. package/scripts/harness/installation/application/named-hook-json.ts +17 -0
  34. package/scripts/harness/installation/application/operator-notice.ts +42 -0
  35. package/scripts/harness/installation/application/usecases/run-doctor-diagnostics.ts +18 -7
  36. package/scripts/harness/installation/application/usecases/run-install.ts +152 -52
  37. package/scripts/harness/installation/application/usecases/run-reconcile.ts +86 -10
  38. package/scripts/harness/installation/application/usecases/run-uninstall.ts +149 -25
  39. package/scripts/harness/installation/composition-root.ts +10 -0
  40. package/scripts/harness/installation/domain/agent-target.ts +39 -0
  41. package/scripts/harness/installation/domain/check-id.ts +5 -0
  42. package/scripts/harness/installation/domain/husky-runtime-state.ts +34 -0
  43. package/scripts/harness/installation/domain/ports/git-hooks-runtime-probe.ts +9 -0
  44. package/scripts/harness/installation/domain/repair-table.ts +5 -0
  45. package/scripts/harness/installation/infrastructure/adapters/git-hooks-runtime-probe-adapter.ts +62 -0
  46. package/scripts/harness/installation/presentation/cli/install-handler.ts +23 -4
  47. package/scripts/harness/installation/presentation/cli/reconcile-handler.ts +7 -1
  48. package/scripts/harness/installation/presentation/formatters/diagnostic-report-formatter.ts +27 -0
  49. package/scripts/harness/main.ts +100 -39
  50. package/scripts/harness/quick-mode/application/dto/change-category-classification-contract.ts +1 -1
  51. package/scripts/harness/quick-mode/application/dto/quick-mode-eligibility-contract.ts +1 -1
  52. package/scripts/harness/quick-mode/application/usecases/classify-change-category-usecase.ts +3 -1
  53. package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +13 -2
  54. package/scripts/harness/quick-mode/domain/types/rejection-rule.ts +1 -1
  55. package/scripts/harness/regression-suite/infrastructure/adapters/vitest-test-runner-adapter.ts +13 -3
  56. package/templates/.agents/hooks.json +16 -0
  57. package/templates/.claude/scripts/analyze-errors-hook.sh +2 -2
  58. package/templates/.claude/settings.json +5 -3
  59. package/templates/.codex/hooks.json +2 -2
@@ -6,7 +6,15 @@
6
6
  // @work-item-id WI-180
7
7
  // @work-item-id WI-208
8
8
  // @work-item-id WI-330
9
+ // @work-item-id WI-384
10
+ // @work-item-id WI-385
9
11
 
12
+ import {
13
+ ANTIGRAVITY_CLI_ONLY_NOTICE,
14
+ CODEX_HOOK_TRUST_UNVERIFIABLE_NOTICE,
15
+ GROK_HOOK_TRUST_UNVERIFIABLE_NOTICE,
16
+ type OperatorNotice,
17
+ } from "../../application/operator-notice.js";
10
18
  import type {
11
19
  DoctorAgentScope,
12
20
  ScopedOutDiagnosticFinding,
@@ -27,6 +35,7 @@ export interface DiagnosticReportFormatterInput {
27
35
 
28
36
  export class DiagnosticReportFormatter {
29
37
  formatJson(input: DiagnosticReportFormatterInput): string {
38
+ const operatorNotices = noticesFor(input.agent);
30
39
  return JSON.stringify(
31
40
  {
32
41
  schemaVersion: "1.0",
@@ -59,6 +68,7 @@ export class DiagnosticReportFormatter {
59
68
  scopeReason,
60
69
  };
61
70
  }),
71
+ operatorNotices,
62
72
  exitCode: input.exitCode,
63
73
  },
64
74
  null,
@@ -74,6 +84,9 @@ export class DiagnosticReportFormatter {
74
84
  `Config: ${input.configStatus}`,
75
85
  "",
76
86
  ];
87
+ for (const notice of noticesFor(input.agent)) {
88
+ lines.push(`Notice [${notice.code}]: ${notice.message}`, "");
89
+ }
77
90
  for (const finding of input.report.findings) {
78
91
  lines.push(`[${finding.severity}] ${finding.checkId}: ${finding.message}`);
79
92
  lines.push(` target: ${finding.target}`);
@@ -107,9 +120,23 @@ function scopeDescription(agent: DoctorAgentScope, installationMode: "project" |
107
120
  return "Personal Claude Code sandbox; team/project Husky, CI, package, and Codex-only findings are not repair targets.";
108
121
  if (agent === "codex")
109
122
  return "Personal Codex sandbox; team/project Husky, CI, package, and Claude-only findings are not repair targets.";
123
+ if (agent === "grok") return "Personal Grok-compatible hook diagnostics; external trust remains operator-verified.";
124
+ if (agent === "antigravity") return "Personal Antigravity CLI hook diagnostics; IDE/desktop uses the L2 backstop.";
125
+ if (agent === "all") return "Personal diagnostics for all supported runtime targets.";
110
126
  return "Personal sandbox diagnostics; team/project Husky, CI, and package findings are not repair targets.";
111
127
  }
112
128
  if (agent === "claude") return "Claude Code and shared setup targets; Codex-only findings are not applicable.";
113
129
  if (agent === "codex") return "Codex and shared setup targets; Claude-only findings are not applicable.";
130
+ if (agent === "grok") return "Grok-compatible Claude hook diagnostics; other runtime findings are not applicable.";
131
+ if (agent === "antigravity") return "Antigravity CLI hook diagnostics; other runtime findings are not applicable.";
132
+ if (agent === "all") return "Full setup diagnostics for Claude, Codex, Grok, Antigravity, and shared targets.";
114
133
  return "Full setup diagnostics for Claude, Codex, and shared targets.";
115
134
  }
135
+
136
+ function noticesFor(agent: DoctorAgentScope): readonly OperatorNotice[] {
137
+ return [
138
+ ...(agent === "codex" || agent === "both" || agent === "all" ? [CODEX_HOOK_TRUST_UNVERIFIABLE_NOTICE] : []),
139
+ ...(agent === "grok" || agent === "all" ? [GROK_HOOK_TRUST_UNVERIFIABLE_NOTICE] : []),
140
+ ...(agent === "antigravity" || agent === "all" ? [ANTIGRAVITY_CLI_ONLY_NOTICE] : []),
141
+ ];
142
+ }
@@ -25,6 +25,8 @@
25
25
  * @work-item-id WI-300
26
26
  * @work-item-id WI-306
27
27
  * @work-item-id WI-308
28
+ * @work-item-id WI-384
29
+ * @work-item-id WI-385
28
30
  *
29
31
  * Phasegate CLI エントリポイント。
30
32
  * 各Unitの Composition Root からハンドラーを取得し、コマンドに応じてディスパッチする。
@@ -63,6 +65,7 @@ import {
63
65
  SkillDeployerManifestBuilder,
64
66
  } from "./installation/application/wrappers/skill-deployer-manifest-builder.js";
65
67
  import { createInstallationModule } from "./installation/composition-root.js";
68
+ import { type AgentTarget, isAgentTarget } from "./installation/domain/agent-target.js";
66
69
  import { NodeCryptoHashAdapter } from "./installation/infrastructure/adapters/node-crypto-hash-adapter.js";
67
70
  import { CheckStoryReflectionUseCase } from "./phase-dependency-model/application/usecases/check-story-reflection-usecase.js";
68
71
  import { createPhaseDependencyModelModule } from "./phase-dependency-model/composition-root.js";
@@ -207,14 +210,14 @@ Usage: phasegate <command> [options]
207
210
  Setup:
208
211
  init Initialize project: deploy skills + design docs + phasegate.config.json
209
212
  (--name <project-name>, --preset <full|standard|minimal|custom>,
210
- --skills <core|all>, --agent <claude|codex|both>, --workflow <standard|strict>,
213
+ --skills <core|all>, --agent <claude|codex|both|grok|antigravity|all>, --workflow <standard|strict>,
211
214
  --language <language>, --with-husky, --with-ci, --yes)
212
215
  update-skills Alias for reconcile (kept for compatibility)
213
- doctor Diagnose silent installation failures (--json, --strict, --personal, --agent <claude|codex|both>, --report-out <path>)
216
+ doctor Diagnose silent installation failures (--json, --strict, --personal, --agent <claude|codex|both|grok|antigravity|all>, --report-out <path>)
214
217
  scaffold-wi <unit|_cross> <story|issue|fix|refactor|chore>
215
218
  Create docs/inception/{unit}/WI-XXX/description.md
216
219
  emit-agent-rules Print AGENTS.md / CLAUDE.md WI workflow rules block
217
- install Install phasegate managed files (--dry-run|--apply, --force, --personal, --agent <claude|codex|both>)
220
+ install Install phasegate managed files (--dry-run|--apply, --force, --personal, --agent <claude|codex|both|grok|antigravity|all>)
218
221
  uninstall Uninstall phasegate managed files (--dry-run|--apply, --force)
219
222
  reconcile Reconcile phasegate managed files (--dry-run|--apply, --force)
220
223
  setup:agent Diagnose repo setup and produce/apply an agent-readable setup plan
@@ -271,7 +274,7 @@ Gate semantics:
271
274
  ci:auto-refresh-agent-context Refresh AGENTS.md / CLAUDE.md (--dry-run, --apply, --json)
272
275
  refresh-claude-md Refresh CLAUDE.md standard sections (--dry-run, --apply, --json)
273
276
  p2:check-agent-context Check AGENTS.md / CLAUDE.md freshness (--threshold-days <n>, --json)
274
- setup:agent Plan agent-driven setup (--intent <minimal|recommended|strict|ci-only|agent-hooks|retrofit>, --agent <claude|codex|both>, --dry-run|--apply, --json)
277
+ setup:agent Plan agent-driven setup (--intent <minimal|recommended|strict|ci-only|agent-hooks|retrofit>, --agent <claude|codex|both|grok|antigravity|all>, --dry-run|--apply, --json)
275
278
  config:plan Plan safe config changes (--intent <l4-strict|codex-hooks|ci-fail-on-warning|baseline-reset|quick-mode-strict|quick-mode-relax|retrofit-bootstrap|planning-mode-relax>, --dry-run|--apply, --json)
276
279
  ci:check-repetition Check error repetition (--code <errorCode>, --reset, --json)
277
280
  baseline Create retrofit baseline snapshot (--dry-run, --force, --paths <glob,glob,...>, --json)
@@ -705,7 +708,7 @@ Options:
705
708
  --name <project-name> Project name (default: "my-project")
706
709
  --preset <full|standard|minimal|custom> Phase dependency preset (default: "standard")
707
710
  --skills <core|all> Skill set to deploy (default: "all")
708
- --agent <claude|codex|both> Agent integration target (default: "claude")
711
+ --agent <claude|codex|both|grok|antigravity|all> Agent integration target (default: "claude")
709
712
  --workflow <standard|strict> Workflow enforcement defaults (default: "standard")
710
713
  --language <language> Project language declaration (default: "typescript")
711
714
  --with-husky Install Husky pre-commit hooks
@@ -731,7 +734,7 @@ Options:
731
734
  --dry-run Preview target actions without writing (default)
732
735
  --apply Write merge results and manifest
733
736
  --force Force ai-assisted/manual targets after backing up existing files
734
- --agent <claude|codex|both> Agent context and hook targets (default: both)
737
+ --agent <claude|codex|both|grok|antigravity|all> Agent context and hook targets (default: both)
735
738
  --skills <core|all> Rendered agent context skill mode (default: all)
736
739
  --workflow <standard|strict> Rendered agent context workflow mode (default: standard)
737
740
  --with-husky Include Husky hook targets (opt-in; omitted by default)
@@ -740,13 +743,24 @@ Options:
740
743
  With --agent claude, initializes .phasegate-local config/settings/skills and ignored .claude shims.
741
744
  --json Output machine-readable JSON
742
745
  --help, -h Show this help`,
746
+ doctor: `Usage: phasegate doctor [options]
747
+
748
+ Diagnose silent installation failures for the selected runtime scope.
749
+
750
+ Options:
751
+ --json Output machine-readable JSON
752
+ --strict Treat warnings as failures
753
+ --personal Diagnose a personal installation
754
+ --agent <claude|codex|both|grok|antigravity|all> Diagnostic scope (default: both)
755
+ --report-out <path> Write the JSON report to a file
756
+ --help, -h Show this help`,
743
757
  "setup:agent": `Usage: phasegate setup:agent [options]
744
758
 
745
759
  Diagnose repository setup and produce an agent-readable setup plan.
746
760
 
747
761
  Options:
748
762
  --intent <minimal|recommended|strict|ci-only|agent-hooks|retrofit>
749
- --agent <claude|codex|both>
763
+ --agent <claude|codex|both|grok|antigravity|all>
750
764
  --workflow <standard|strict>
751
765
  --with-husky
752
766
  --with-ci
@@ -1075,7 +1089,7 @@ function parseCoverageThreshold(raw: string | undefined): number {
1075
1089
  }
1076
1090
 
1077
1091
  type InitPhasePreset = "full" | "standard" | "minimal" | "custom";
1078
- type AgentTarget = "claude" | "codex" | "both";
1092
+ const AGENT_TARGET_USAGE = "claude|codex|both|grok|antigravity|all";
1079
1093
  type SetupIntent = "minimal" | "recommended" | "strict" | "ci-only" | "agent-hooks" | "retrofit";
1080
1094
  type ConfigChangeIntent =
1081
1095
  | "l4-strict"
@@ -1097,7 +1111,7 @@ interface SetupCompletenessEntry {
1097
1111
  }
1098
1112
 
1099
1113
  interface AgentReadinessEntry {
1100
- readonly agent: "claude" | "codex" | "shared";
1114
+ readonly agent: "claude" | "codex" | "grok" | "antigravity" | "shared";
1101
1115
  readonly status: SetupCompletenessStatus;
1102
1116
  readonly evidence: readonly string[];
1103
1117
  readonly nextAction: string | null;
@@ -1134,14 +1148,9 @@ function parseInitPhasePreset(value: string | undefined): InitPhasePreset | unde
1134
1148
  return undefined;
1135
1149
  }
1136
1150
 
1137
- function parseAgentTarget(value: string | undefined, fallback: AgentTarget = "both"): AgentTarget {
1138
- if (value === "claude" || value === "codex" || value === "both") return value;
1139
- return fallback;
1140
- }
1141
-
1142
1151
  function parseRequiredAgentTarget(value: string | undefined, fallback: AgentTarget = "both"): AgentTarget | null {
1143
1152
  if (value === undefined) return fallback;
1144
- if (value === "claude" || value === "codex" || value === "both") return value;
1153
+ if (isAgentTarget(value)) return value;
1145
1154
  return null;
1146
1155
  }
1147
1156
 
@@ -1234,6 +1243,7 @@ function buildSetupCompleteness(input: {
1234
1243
  readonly phasegateConfig: boolean;
1235
1244
  readonly claudeSettings: boolean;
1236
1245
  readonly codexHooks: boolean;
1246
+ readonly antigravityHooks: boolean;
1237
1247
  readonly agentsMd: boolean;
1238
1248
  readonly claudeMd: boolean;
1239
1249
  readonly huskyPreCommit: boolean;
@@ -1241,12 +1251,19 @@ function buildSetupCompleteness(input: {
1241
1251
  readonly skillsVersion: boolean;
1242
1252
  };
1243
1253
  }): readonly SetupCompletenessEntry[] {
1244
- const includeClaude = input.agent === "claude" || input.agent === "both";
1245
- const includeCodex = input.agent === "codex" || input.agent === "both";
1254
+ const includeClaude = input.agent === "claude" || input.agent === "both" || input.agent === "all";
1255
+ const includeCodex = input.agent === "codex" || input.agent === "both" || input.agent === "all";
1256
+ const includeGrok = input.agent === "grok" || input.agent === "all";
1257
+ const includeAntigravity = input.agent === "antigravity" || input.agent === "all";
1258
+ const includeClaudeHook = includeClaude || includeGrok;
1259
+ const includeAgentsContext = includeCodex || includeGrok || includeAntigravity;
1246
1260
  const doctorCommand = doctorValidationCommand(input.agent);
1247
1261
  const agentHooksConfigured =
1248
- (!includeClaude || input.checks.claudeSettings) && (!includeCodex || input.checks.codexHooks);
1249
- const agentContextConfigured = (!includeClaude || input.checks.claudeMd) && (!includeCodex || input.checks.agentsMd);
1262
+ (!includeClaudeHook || input.checks.claudeSettings) &&
1263
+ (!includeCodex || input.checks.codexHooks) &&
1264
+ (!includeAntigravity || input.checks.antigravityHooks);
1265
+ const agentContextConfigured =
1266
+ (!includeClaude || input.checks.claudeMd) && (!includeAgentsContext || input.checks.agentsMd);
1250
1267
  const entries: SetupCompletenessEntry[] = [
1251
1268
  setupCompletenessEntry({
1252
1269
  area: "local-config",
@@ -1258,16 +1275,22 @@ function buildSetupCompleteness(input: {
1258
1275
  }),
1259
1276
  setupCompletenessEntry({
1260
1277
  area: "agent-hooks",
1261
- included: includeClaude || includeCodex,
1278
+ included: includeClaudeHook || includeCodex || includeAntigravity,
1262
1279
  configured: agentHooksConfigured,
1263
1280
  configuredEvidence: "Selected agent hook settings are present.",
1264
1281
  plannedEvidence: "setup:agent will create or refresh selected agent hook settings.",
1265
1282
  nextAction: "Run setup:agent --apply for selected agents.",
1266
- risk: includeCodex ? "Codex user-level hook feature enablement remains a manual external check." : undefined,
1283
+ risk: includeCodex
1284
+ ? "Codex user-level hook feature enablement remains a manual external check."
1285
+ : includeGrok
1286
+ ? "Grok project-hook trust remains a manual external check."
1287
+ : includeAntigravity
1288
+ ? "Antigravity hard blocking is CLI-only; IDE/desktop relies on L2 pre-commit."
1289
+ : undefined,
1267
1290
  }),
1268
1291
  setupCompletenessEntry({
1269
1292
  area: "agent-context",
1270
- included: includeClaude || includeCodex,
1293
+ included: includeClaude || includeAgentsContext,
1271
1294
  configured: agentContextConfigured,
1272
1295
  configuredEvidence: "Selected AGENTS.md / CLAUDE.md managed context files are present.",
1273
1296
  plannedEvidence: "setup:agent will create or refresh selected managed agent context sections.",
@@ -1371,6 +1394,7 @@ function buildAgentReadiness(input: {
1371
1394
  readonly phasegateConfig: boolean;
1372
1395
  readonly claudeSettings: boolean;
1373
1396
  readonly codexHooks: boolean;
1397
+ readonly antigravityHooks: boolean;
1374
1398
  readonly agentsMd: boolean;
1375
1399
  readonly claudeMd: boolean;
1376
1400
  readonly huskyPreCommit: boolean;
@@ -1378,8 +1402,10 @@ function buildAgentReadiness(input: {
1378
1402
  readonly skillsVersion: boolean;
1379
1403
  };
1380
1404
  }): readonly AgentReadinessEntry[] {
1381
- const includeClaude = input.agent === "claude" || input.agent === "both";
1382
- const includeCodex = input.agent === "codex" || input.agent === "both";
1405
+ const includeClaude = input.agent === "claude" || input.agent === "both" || input.agent === "all";
1406
+ const includeCodex = input.agent === "codex" || input.agent === "both" || input.agent === "all";
1407
+ const includeGrok = input.agent === "grok" || input.agent === "all";
1408
+ const includeAntigravity = input.agent === "antigravity" || input.agent === "all";
1383
1409
  const doctorCommand = doctorValidationCommand(input.agent);
1384
1410
  const sharedConfigured =
1385
1411
  input.checks.packageJson &&
@@ -1423,6 +1449,24 @@ function buildAgentReadiness(input: {
1423
1449
  nextAction: "Run setup:agent --agent codex --apply, then enable hooks at user level if needed.",
1424
1450
  risk: "Codex user-level hook feature enablement remains a manual external check.",
1425
1451
  }),
1452
+ setupReadinessEntry({
1453
+ agent: "grok",
1454
+ included: includeGrok,
1455
+ configured: input.checks.claudeSettings && input.checks.agentsMd,
1456
+ configuredEvidence: [".claude/settings.json and AGENTS.md are present for Grok compatibility."],
1457
+ plannedEvidence: ["setup:agent will deploy the Claude-compatible hook and AGENTS.md without .grok duplication."],
1458
+ nextAction: "Run setup:agent --agent grok --apply, then verify grok inspect and /hooks trust.",
1459
+ risk: "Grok project-hook trust cannot be verified by Phasegate.",
1460
+ }),
1461
+ setupReadinessEntry({
1462
+ agent: "antigravity",
1463
+ included: includeAntigravity,
1464
+ configured: input.checks.antigravityHooks && input.checks.agentsMd,
1465
+ configuredEvidence: [".agents/hooks.json and AGENTS.md are present for Antigravity CLI."],
1466
+ plannedEvidence: ["setup:agent will deploy the Antigravity named hook map and AGENTS.md."],
1467
+ nextAction: "Run setup:agent --agent antigravity --apply, then inspect /hooks in agy.",
1468
+ risk: "IDE/desktop hook execution is not guaranteed; keep the L2 pre-commit backstop.",
1469
+ }),
1426
1470
  setupReadinessEntry({
1427
1471
  agent: "shared",
1428
1472
  included: true,
@@ -1452,14 +1496,17 @@ async function buildAgentSetupPlan(
1452
1496
  phasegateConfig: await projectFileExists(rootDir, "phasegate.config.json"),
1453
1497
  claudeSettings: await projectFileExists(rootDir, ".claude/settings.json"),
1454
1498
  codexHooks: await projectFileExists(rootDir, ".codex/hooks.json"),
1499
+ antigravityHooks: await projectFileExists(rootDir, ".agents/hooks.json"),
1455
1500
  agentsMd: await projectFileExists(rootDir, "AGENTS.md"),
1456
1501
  claudeMd: await projectFileExists(rootDir, "CLAUDE.md"),
1457
1502
  huskyPreCommit: await projectFileExists(rootDir, ".husky/pre-commit"),
1458
1503
  ciWorkflow: await projectFileExists(rootDir, ".github/workflows/phasegate-aidlc-gate.yml"),
1459
1504
  skillsVersion: await projectFileExists(rootDir, "skills/.harness-version"),
1460
1505
  };
1461
- const includeClaude = input.agent === "claude" || input.agent === "both";
1462
- const includeCodex = input.agent === "codex" || input.agent === "both";
1506
+ const includeClaude = input.agent === "claude" || input.agent === "both" || input.agent === "all";
1507
+ const includeCodex = input.agent === "codex" || input.agent === "both" || input.agent === "all";
1508
+ const includeGrok = input.agent === "grok" || input.agent === "all";
1509
+ const includeAntigravity = input.agent === "antigravity" || input.agent === "all";
1463
1510
  const changes = [
1464
1511
  !checks.packageJson
1465
1512
  ? "Add phasegate devDependency and phasegate scripts to package.json."
@@ -1469,6 +1516,8 @@ async function buildAgentSetupPlan(
1469
1516
  : "Keep existing phasegate.config.json; review config:plan before changing policy.",
1470
1517
  includeClaude ? "Create or refresh .claude/settings.json and CLAUDE.md managed section." : null,
1471
1518
  includeCodex ? "Create or refresh .codex/hooks.json and AGENTS.md managed section." : null,
1519
+ includeGrok ? "Create or refresh the Claude-compatible hook and AGENTS.md without a .grok hook duplicate." : null,
1520
+ includeAntigravity ? "Create or refresh .agents/hooks.json named hook and AGENTS.md managed section." : null,
1472
1521
  input.withHusky
1473
1522
  ? "Create or refresh Husky pre-commit, commit-msg, and pre-push backstops."
1474
1523
  : "Leave Husky hooks unmanaged in this setup run.",
@@ -2100,8 +2149,8 @@ async function main(): Promise<void> {
2100
2149
  }
2101
2150
  const skillSet: SkillSet = skillSetRaw;
2102
2151
  const agentRaw = parseFlag(args, "--agent") ?? "claude";
2103
- if (agentRaw !== "claude" && agentRaw !== "codex" && agentRaw !== "both") {
2104
- console.error(`Invalid --agent value: "${agentRaw}". Use "claude", "codex", or "both".`);
2152
+ if (!isAgentTarget(agentRaw)) {
2153
+ console.error(`Invalid --agent value: "${agentRaw}". Use <${AGENT_TARGET_USAGE}>.`);
2105
2154
  process.exit(2);
2106
2155
  }
2107
2156
  const agent = agentRaw;
@@ -2116,8 +2165,8 @@ async function main(): Promise<void> {
2116
2165
  console.error("Invalid --language value: value must not be empty.");
2117
2166
  process.exit(2);
2118
2167
  }
2119
- const deployClaude = agent === "claude" || agent === "both";
2120
- const deployCodex = agent === "codex" || agent === "both";
2168
+ const deployClaude = agent === "claude" || agent === "both" || agent === "grok" || agent === "all";
2169
+ const deployCodex = agent === "codex" || agent === "both" || agent === "all";
2121
2170
  const result = await deploySkills(harnessRoot, rootDir, skillSet);
2122
2171
  const packageResult = await ensurePhasegatePackageDependency(rootDir, result.version);
2123
2172
  const skillLinkResult = await deployAgentSkillLinks(rootDir, {
@@ -2312,11 +2361,15 @@ async function main(): Promise<void> {
2312
2361
  console.log(" 3. Edit .claude/scripts/hook-config.json to set target directories");
2313
2362
  }
2314
2363
  if (deployCodex) {
2315
- console.log(` ${deployClaude ? "4" : "3"}. Enable Codex hooks: codex features enable hooks`);
2364
+ console.log(
2365
+ ` ${deployClaude ? "4" : "3"}. Use Codex CLI >= 0.124.0, then open /hooks and trust the current hook definition hash`,
2366
+ );
2316
2367
  console.log(
2317
2368
  ` ${deployClaude ? "5" : "4"}. (Recommended) Install pre-commit backstop: rerun with --with-husky or set up husky manually`,
2318
2369
  );
2319
- console.log(` See docs/guide/codex-integration.md for the native apply_patch limitation.`);
2370
+ console.log(
2371
+ ` See docs/guide/codex-integration.md for native apply_patch coverage and trust troubleshooting.`,
2372
+ );
2320
2373
  }
2321
2374
  if (skillSet !== "core") {
2322
2375
  console.log("");
@@ -2363,7 +2416,7 @@ async function main(): Promise<void> {
2363
2416
  }
2364
2417
  const agent = parseRequiredAgentTarget(parseFlag(args, "--agent"), "both");
2365
2418
  if (agent === null) {
2366
- console.error(`Invalid --agent value: "${parseFlag(args, "--agent")}". Use "claude", "codex", or "both".`);
2419
+ console.error(`Invalid --agent value: "${parseFlag(args, "--agent")}". Use <${AGENT_TARGET_USAGE}>.`);
2367
2420
  process.exit(2);
2368
2421
  }
2369
2422
  const mod = createInstallationModule();
@@ -2434,7 +2487,11 @@ async function main(): Promise<void> {
2434
2487
  }
2435
2488
  const apply = hasFlag(args, "--apply");
2436
2489
  const dryRun = hasFlag(args, "--dry-run") || !apply;
2437
- const agent = parseAgentTarget(parseFlag(args, "--agent"), "both");
2490
+ const agent = parseRequiredAgentTarget(parseFlag(args, "--agent"), "both");
2491
+ if (agent === null) {
2492
+ console.error(`Invalid --agent value: "${parseFlag(args, "--agent")}". Use <${AGENT_TARGET_USAGE}>.`);
2493
+ process.exit(2);
2494
+ }
2438
2495
  const skillSetRaw = parseFlag(args, "--skills") ?? "all";
2439
2496
  if (skillSetRaw !== "core" && skillSetRaw !== "all") {
2440
2497
  console.error(`Invalid --skills value: "${skillSetRaw}". Use "core" or "all".`);
@@ -2445,8 +2502,8 @@ async function main(): Promise<void> {
2445
2502
  console.error(`Invalid --workflow value: "${workflowRaw}". Use "standard" or "strict".`);
2446
2503
  process.exit(2);
2447
2504
  }
2448
- const includeClaude = agent === "claude" || agent === "both";
2449
- const includeCodex = agent === "codex" || agent === "both";
2505
+ const includeClaude = agent === "claude" || agent === "both" || agent === "grok" || agent === "all";
2506
+ const includeCodex = agent === "codex" || agent === "both" || agent === "all";
2450
2507
  const personal = hasFlag(args, "--personal");
2451
2508
  const mod = createInstallationModule();
2452
2509
  const phasegateVersion = await getHarnessVersion(harnessRoot);
@@ -2490,7 +2547,11 @@ async function main(): Promise<void> {
2490
2547
  process.exit(2);
2491
2548
  }
2492
2549
  const intent = parseSetupIntent(parseFlag(args, "--intent"));
2493
- const agent = parseAgentTarget(parseFlag(args, "--agent"), "both");
2550
+ const agent = parseRequiredAgentTarget(parseFlag(args, "--agent"), "both");
2551
+ if (agent === null) {
2552
+ console.error(`Invalid --agent value: "${parseFlag(args, "--agent")}". Use <${AGENT_TARGET_USAGE}>.`);
2553
+ process.exit(2);
2554
+ }
2494
2555
  const workflow = parseWorkflowMode(
2495
2556
  parseFlag(args, "--workflow") ?? (intent === "strict" ? "strict" : "standard"),
2496
2557
  );
@@ -2526,8 +2587,8 @@ async function main(): Promise<void> {
2526
2587
  dryRun: false,
2527
2588
  apply: true,
2528
2589
  force: hasFlag(args, "--force"),
2529
- includeClaude: agent === "claude" || agent === "both",
2530
- includeCodex: agent === "codex" || agent === "both",
2590
+ includeClaude: agent === "claude" || agent === "both" || agent === "grok" || agent === "all",
2591
+ includeCodex: agent === "codex" || agent === "both" || agent === "all",
2531
2592
  includeHusky: withHusky,
2532
2593
  includeCi: withCi,
2533
2594
  skillSet: "all",
@@ -11,6 +11,6 @@ export interface ChangeCategoryClassificationContract {
11
11
  readonly dominantCategory: string | null;
12
12
  readonly perFile: readonly ChangeCategoryPerFile[];
13
13
  readonly fullModeRequired: boolean;
14
- readonly rejectionRule?: 'MIXED_CHANGES' | 'NEW_DOMAIN' | 'API_CONTRACT';
14
+ readonly rejectionRule?: 'CATEGORY_NOT_ALLOWED' | 'MIXED_CHANGES' | 'NEW_DOMAIN' | 'API_CONTRACT';
15
15
  readonly rejectionReason?: string;
16
16
  }
@@ -8,6 +8,6 @@
8
8
  export interface QuickModeEligibilityContract {
9
9
  readonly eligible: boolean;
10
10
  readonly reason: string;
11
- readonly rejectionRule?: 'MIXED_CHANGES' | 'NEW_DOMAIN' | 'API_CONTRACT';
11
+ readonly rejectionRule?: 'CATEGORY_NOT_ALLOWED' | 'MIXED_CHANGES' | 'NEW_DOMAIN' | 'API_CONTRACT';
12
12
  readonly rejectedFiles?: readonly { filePath: string; changeKind: string }[];
13
13
  }
@@ -2,6 +2,7 @@
2
2
  * @layer application
3
3
  * @unit quick-mode
4
4
  * @story H10-05
5
+ * @work-item-id WI-384
5
6
  *
6
7
  * paths から変更カテゴリを分類し fullModeRequired 判定と理由を返す UseCase
7
8
  */
@@ -20,6 +21,7 @@ export interface ClassifyChangeCategoryUseCaseInput {
20
21
  readonly paths: readonly string[];
21
22
  readonly targetChanges?: readonly {
22
23
  readonly filePath: string;
24
+ readonly changeKind?: ChangeKind;
23
25
  readonly beforeContent?: string | null;
24
26
  readonly afterContent?: string | null;
25
27
  }[];
@@ -94,7 +96,7 @@ export class ClassifyChangeCategoryUseCase {
94
96
  // 変更前の内容が無く変更後の内容がある場合は新規作成 (CREATE) とみなす。
95
97
  // 以前は無条件で MODIFY 固定だったため、新規 domain/ ファイルが
96
98
  // NEW_DOMAIN 判定を回避して quick mode をすり抜けていた。
97
- changeKind: beforeContent === null && afterContent !== null ? "CREATE" : "MODIFY",
99
+ changeKind: targetChange?.changeKind ?? (beforeContent === null && afterContent !== null ? "CREATE" : "MODIFY"),
98
100
  beforeContent,
99
101
  afterContent,
100
102
  });
@@ -5,6 +5,7 @@
5
5
  * @work-item-id WI-349
6
6
  * @work-item-id WI-352
7
7
  * @work-item-id WI-372
8
+ * @work-item-id WI-390
8
9
  *
9
10
  * ChangedFile[]をChangeClassificationに変換し、3拒否ルールを評価してQuickModeEligibilityを返すドメインサービス
10
11
  */
@@ -92,6 +93,15 @@ function categorizeFileByBuiltInRules(file: ChangedFile): ChangeCategory {
92
93
  return ChangeCategory.fromString("test");
93
94
  }
94
95
 
96
+ // Markdown は配置場所にかかわらず文書として扱う。config / API / test の
97
+ // 明示ルールは優先しつつ、domain ディレクトリや CREATE フォールバックより
98
+ // 前に評価して README.md / *.mdx の新規作成を feature/domain に誤分類しない。
99
+ // 信頼境界となる root agent instructions は protected-file 側で別途保護する。
100
+ // @work-item-id WI-390
101
+ if (filePath.endsWith(".md") || filePath.endsWith(".mdx")) {
102
+ return ChangeCategory.fromString("docs");
103
+ }
104
+
95
105
  // domain: domain/ 配下
96
106
  if (filePath.includes("/domain/") || filePath.startsWith("domain/")) {
97
107
  return ChangeCategory.fromString("domain");
@@ -202,7 +212,7 @@ export class QuickModeJudgmentEngine {
202
212
  judge(changedFiles: readonly ChangedFile[], config: QuickModeConfig): QuickModeEligibility {
203
213
  const classification = this.classify(changedFiles, config);
204
214
 
205
- // 1. MIXED_CHANGES評価: allowedCategories 外のカテゴリが含まれる場合
215
+ // 1. カテゴリ評価: 不許可カテゴリを含む単一カテゴリ変更と、複数カテゴリ変更を区別する。
206
216
  if (config.isFullModeRequiredFor("mixedCategories")) {
207
217
  const notAllowedFiles: ChangedFile[] = [];
208
218
  const notAllowedDescriptions: string[] = [];
@@ -216,8 +226,9 @@ export class QuickModeJudgmentEngine {
216
226
  });
217
227
 
218
228
  if (notAllowedFiles.length > 0) {
229
+ const rejectionRule = classification.categorizedFiles.size >= 2 ? "MIXED_CHANGES" : "CATEGORY_NOT_ALLOWED";
219
230
  return QuickModeEligibility.rejected(
220
- "MIXED_CHANGES",
231
+ rejectionRule,
221
232
  notAllowedFiles,
222
233
  `allowedCategories外のファイルが含まれています: ${notAllowedDescriptions.join(", ")}`,
223
234
  );
@@ -5,4 +5,4 @@
5
5
  * Quick Mode拒否ルール識別子
6
6
  */
7
7
 
8
- export type RejectionRule = 'MIXED_CHANGES' | 'NEW_DOMAIN' | 'API_CONTRACT';
8
+ export type RejectionRule = 'CATEGORY_NOT_ALLOWED' | 'MIXED_CHANGES' | 'NEW_DOMAIN' | 'API_CONTRACT';
@@ -1,7 +1,10 @@
1
1
  // @layer infrastructure
2
- import { execSync } from 'node:child_process';
2
+ // @unit regression-suite
3
+ // @work-item-id WI-384
4
+ import { execFileSync } from 'node:child_process';
3
5
  import { existsSync } from 'node:fs';
4
6
  import { join } from 'node:path';
7
+ import { execPath } from 'node:process';
5
8
  import { KRequirementTest } from '../../domain/value-objects/k-requirement-test.js';
6
9
  import { GngConditionTest } from '../../domain/value-objects/gng-condition-test.js';
7
10
  import { AgentIndependenceTest } from '../../domain/value-objects/agent-independence-test.js';
@@ -65,8 +68,15 @@ export class VitestTestRunnerAdapter implements TestRunnerPort {
65
68
 
66
69
  const testPattern = hasUnitTests ? unitTestDir : unitIntegrationDir;
67
70
  try {
68
- execSync(
69
- `npx tsx node_modules/vitest/vitest.mjs run --config scripts/harness/__tests__/vitest.config.ts "${testPattern}"`,
71
+ execFileSync(
72
+ execPath,
73
+ [
74
+ 'node_modules/vitest/vitest.mjs',
75
+ 'run',
76
+ '--config',
77
+ 'scripts/harness/__tests__/vitest.config.ts',
78
+ testPattern,
79
+ ],
70
80
  { cwd: this.rootDir, stdio: 'pipe', timeout: 60_000 },
71
81
  );
72
82
  unitResults.set(unit, true);
@@ -0,0 +1,16 @@
1
+ {
2
+ "phasegate-gate": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "^(write_to_file|replace_file_content|multi_replace_file_content|run_command)$",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "npx phasegate hook pre-tool-use",
10
+ "timeout": 30
11
+ }
12
+ ]
13
+ }
14
+ ]
15
+ }
16
+ }
@@ -96,10 +96,10 @@ if command -v npx >/dev/null 2>&1; then
96
96
  fi
97
97
  fi
98
98
 
99
- # Biome lint check
99
+ # PhaseGate lint check (uses the target project's PhaseGate/Biome configuration)
100
100
  LINT_OUTPUT=""
101
101
  if command -v npx >/dev/null 2>&1; then
102
- LINT_RAW=$(npx @biomejs/biome lint "$LOCAL_PATH" 2>&1)
102
+ LINT_RAW=$(npx phasegate lint --json --skip-eslint-removal-check 2>&1)
103
103
  if [ $? -ne 0 ] && [ -n "$LINT_RAW" ]; then
104
104
  LINT_OUTPUT=$(echo "$LINT_RAW" | head -5)
105
105
  fi
@@ -2,7 +2,7 @@
2
2
  "hooks": {
3
3
  "PreToolUse": [
4
4
  {
5
- "matcher": "Bash",
5
+ "matcher": "Bash|apply_patch",
6
6
  "hooks": [
7
7
  {
8
8
  "type": "command",
@@ -10,7 +10,8 @@
10
10
  },
11
11
  {
12
12
  "type": "command",
13
- "command": "npx phasegate hook pre-tool-use"
13
+ "command": "npx phasegate hook pre-tool-use",
14
+ "timeout": 30
14
15
  }
15
16
  ]
16
17
  },
@@ -19,7 +20,8 @@
19
20
  "hooks": [
20
21
  {
21
22
  "type": "command",
22
- "command": "npx phasegate hook pre-tool-use"
23
+ "command": "npx phasegate hook pre-tool-use",
24
+ "timeout": 30
23
25
  }
24
26
  ]
25
27
  }
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "PreToolUse": [
27
27
  {
28
- "matcher": "Bash",
28
+ "matcher": "Bash|apply_patch",
29
29
  "hooks": [
30
30
  {
31
31
  "type": "command",
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "PostToolUse": [
39
39
  {
40
- "matcher": "Bash",
40
+ "matcher": "Bash|apply_patch",
41
41
  "hooks": [
42
42
  {
43
43
  "type": "command",