phasegate 0.154.0 → 0.156.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.
@@ -24,7 +24,7 @@ Command names in this document are split into three surfaces:
24
24
  |---|---|
25
25
  | `init --name <name>` | Legacy-compatible bootstrap for new projects: deploy skills, generate config, and optionally add hooks/CI. Options: `--preset <full\|standard\|minimal\|custom>`, `--skills <core\|all>`, `--agent <claude\|codex\|both>`, `--workflow <standard\|strict>`, `--with-husky`, `--with-ci`, `--yes`. |
26
26
  | `install --dry-run` / `--apply` | Idempotently merge PhaseGate into an existing project, preserve user content, add package scripts/devDependency, create selected `AGENTS.md` / `CLAUDE.md` managed sections, and write `.phasegate/manifest.json`. `--agent <claude\|codex\|both>`, `--skills <core\|all>`, and `--workflow <standard\|strict>` affect rendered agent context; `--force` replaces managed targets after backup. |
27
- | `doctor` | Diagnose silent or partial installations and report repair hints (`--json`, `--strict`, `--report-out <path>`). `--report-out` writes exactly to the supplied path, not to `reporting.outputDir`. |
27
+ | `doctor` | Diagnose silent or partial installations and report repair hints (`--json`, `--strict`, `--agent <claude\|codex\|both>`, `--report-out <path>`). `--agent` defaults to `both`; single-agent scopes keep shared targets applicable and mark the other agent's findings as not applicable. `--report-out` writes exactly to the supplied path, not to `reporting.outputDir`. |
28
28
  | `uninstall --dry-run` / `--apply` | Remove PhaseGate-managed files and managed blocks using `.phasegate/manifest.json`; `--force` handles managed conflict cases. |
29
29
  | `reconcile --dry-run` / `--apply` | Update PhaseGate-managed files to current package templates and refresh manifest hashes; `--force` allows managed-file replacement with backup. |
30
30
  | `setup:agent` | Agent-readable setup planner and optional apply path. Options: `--intent <minimal\|recommended\|strict\|ci-only\|agent-hooks\|retrofit>`, `--agent <claude\|codex\|both>`, `--workflow <standard\|strict>`, `--with-husky`, `--with-ci`, `--dry-run`, `--apply`, `--json`. <!-- @work-item-id WI-172 --> |
@@ -40,7 +40,7 @@ Command names in this document are split into three surfaces:
40
40
 
41
41
  <!-- @work-item-id WI-158 -->
42
42
 
43
- Setup lifecycle commands support JSON for automation where shown by help: `install --json`, `reconcile --json`, `uninstall --json`, and `doctor --json`. `doctor --report-out <path>` persists the doctor JSON payload to that exact path. Relative paths are resolved from the project root; absolute paths are used as-is.
43
+ Setup lifecycle commands support JSON for automation where shown by help: `install --json`, `reconcile --json`, `uninstall --json`, and `doctor --json`. `doctor --agent claude --json` and `doctor --agent codex --json` include `scope` and `scopedOutFindings` so agents can distinguish selected-agent readiness from full-install diagnostics. `doctor --report-out <path>` persists the doctor JSON payload to that exact path. Relative paths are resolved from the project root; absolute paths are used as-is. <!-- @work-item-id WI-178 -->
44
44
 
45
45
  This is separate from `reporting.outputDir`. The configured report directory is used by phase-dependency / phase-gate reporting, while regression-suite result files are fixed under `reports/regression/` and status/drift JSON is emitted to stdout.
46
46
 
@@ -1,6 +1,6 @@
1
1
  # Troubleshooting
2
2
 
3
- <!-- @work-item-id WI-171, WI-172, WI-173, WI-175, WI-176 -->
3
+ <!-- @work-item-id WI-171, WI-172, WI-173, WI-175, WI-176, WI-177 -->
4
4
 
5
5
  Start with:
6
6
 
@@ -63,6 +63,8 @@ The planner output is intentionally agent-readable: detected state, questions, p
63
63
 
64
64
  `doctor` green means the local managed setup is consistent for the inspected targets. It does not prove user-level Codex feature flags, hosted GitHub Actions execution, npm registry state, or team policy acceptance.
65
65
 
66
+ By default, `phasegate doctor` inspects the full Claude + Codex + shared setup. For a deliberate single-agent setup, use `phasegate doctor --agent claude --json` or `phasegate doctor --agent codex --json`. Single-agent reports keep shared targets applicable and place the unselected agent's findings under `scopedOutFindings` with `applicability: "not-applicable"`; those findings are explanatory, not repair targets for the selected agent. <!-- @work-item-id WI-178 -->
67
+
66
68
  Check `plan.completeness` in `setup:agent --json`:
67
69
 
68
70
  - `configured`: PhaseGate found local evidence.
@@ -72,6 +74,14 @@ Check `plan.completeness` in `setup:agent --json`:
72
74
 
73
75
  When `install` or `setup:agent --apply` returns a structured `error`, use its `target`, `operation`, `code`, `recovery`, and `partialChanges` fields before rerunning.
74
76
 
77
+ Common structured setup errors:
78
+
79
+ | Code | Usually means | First recovery step |
80
+ |---|---|---|
81
+ | `EPERM` / `EACCES` | The current sandbox, user, or filesystem denied the write. | Ask the user to rerun in a writable workspace or approve the write, then retry the same `--apply` command. |
82
+ | `EEXIST` / `ENOTDIR` | A parent path such as `.claude` or `.codex` exists as a file or incompatible path. | Inspect the path, decide whether it is user-owned, then move or rename it before rerunning. |
83
+ | hash mismatch / refused target | A PhaseGate managed target contains user edits outside the expected hash. | Use `invoke /phasegate-config-doctor` and explain backup, merge, and `--force` tradeoffs before applying. |
84
+
75
85
  ## Claude Code Setup Still Feels Unclear
76
86
 
77
87
  Run the Claude-specific planner path:
@@ -85,3 +95,5 @@ Check `plan.agentReadiness`:
85
95
  - `agent=claude`: local Claude Code targets (`.claude/settings.json`, `CLAUDE.md`, skills).
86
96
  - `agent=shared`: package/config/skills plus selected Husky and CI targets.
87
97
  - `agent=codex`: should be `not-applicable` unless you selected `--agent both` or `--agent codex`.
98
+
99
+ When `claude` and `shared` are `configured`, the next step is work planning, not more setup. Confirm or create the WI under `docs/inception/**/{WI-XXX}/description.md`, write the needed inception plan/design files, reflect accepted design into `docs/product/...` with `@work-item-id WI-XXX`, then run `npx phasegate phasegate:check-ready` or the relevant `validate --layer ...` command.
@@ -1,6 +1,6 @@
1
1
  # CLAUDE.md
2
2
 
3
- <!-- @work-item-id WI-174, WI-176 -->
3
+ <!-- @work-item-id WI-174, WI-176, WI-177 -->
4
4
 
5
5
  <!-- phasegate:managed-section:start -->
6
6
  ## 必読ドキュメント
@@ -21,6 +21,15 @@ Before planning implementation work in a new or upgraded repository, run `phaseg
21
21
  - `agent=shared` covers package scripts, `phasegate.config.json`, skills, and selected Husky/CI targets.
22
22
  - `manual` or `external-actions` entries require user or hosted-service confirmation; do not treat local readiness as proof that external CI or user-level agent settings are complete.
23
23
 
24
+ After the `claude` and `shared` rows are `configured`, move from setup to work planning:
25
+
26
+ 1. Confirm the target WI under `docs/inception/**/{WI-XXX}/description.md`, or create the next unused WI if the user asks for new work.
27
+ 2. For implementation work, prepare or update the WI plan/design files under `docs/inception/.../{WI-XXX}/` before editing source or tests.
28
+ 3. Reflect the accepted design into the relevant `docs/product/...` files with `@work-item-id WI-XXX`.
29
+ 4. Run `phasegate phasegate:check-ready` or the requested `phasegate validate --layer ...` command before committing.
30
+
31
+ If `setup:agent --apply --json` or `install --apply --json` fails with a structured `error`, explain `target`, `operation`, `code`, `likelyCause`, `recovery`, and `partialChanges` before retrying. Use `invoke /phasegate-config-doctor` for managed target conflicts and `invoke /phasegate-toolkit-guide` for read-only setup guidance.
32
+
24
33
  ## Phase Presets
25
34
 
26
35
  {{PHASEGATE_PRESETS}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.154.0",
3
+ "version": "0.156.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",
@@ -1,22 +1,38 @@
1
1
  // @unit installation
2
2
  // @layer application
3
3
  // @work-item-id WI-145
4
+ // @work-item-id WI-178
4
5
 
5
6
  import { DiagnosticReport } from "../../domain/diagnostic-report.js";
7
+ import type { CheckId } from "../../domain/check-id.js";
8
+ import type { DiagnosticFinding } from "../../domain/diagnostic-finding.js";
6
9
  import type { HeuristicCheck } from "../../domain/ports/heuristic-check.js";
7
10
  import type { FileInspectorPort } from "../ports/file-inspector-port.js";
8
11
  import type { ManifestRepositoryPort } from "../ports/manifest-repository-port.js";
9
12
 
13
+ export type DoctorAgentScope = "claude" | "codex" | "both";
14
+
15
+ export interface ScopedOutDiagnosticFinding {
16
+ readonly finding: DiagnosticFinding;
17
+ readonly scopeReason: string;
18
+ }
19
+
10
20
  export interface RunDoctorDiagnosticsInput {
11
21
  readonly projectRoot: string;
12
22
  readonly strict: boolean;
23
+ readonly agent?: DoctorAgentScope;
13
24
  }
14
25
 
15
26
  export interface RunDoctorDiagnosticsOutput {
16
27
  readonly report: DiagnosticReport;
28
+ readonly agent: DoctorAgentScope;
29
+ readonly scopedOutFindings: readonly ScopedOutDiagnosticFinding[];
17
30
  readonly exitCode: number;
18
31
  }
19
32
 
33
+ const CLAUDE_ONLY_CHECKS = new Set<CheckId>(["claude-hook-missing", "claude-skills-symlink"]);
34
+ const CODEX_ONLY_CHECKS = new Set<CheckId>(["codex-hook-missing", "codex-skills-symlink"]);
35
+
20
36
  export class RunDoctorDiagnosticsUseCase {
21
37
  constructor(
22
38
  private readonly checks: readonly HeuristicCheck[],
@@ -25,17 +41,42 @@ export class RunDoctorDiagnosticsUseCase {
25
41
  ) {}
26
42
 
27
43
  async execute(input: RunDoctorDiagnosticsInput): Promise<RunDoctorDiagnosticsOutput> {
44
+ const agent = input.agent ?? "both";
28
45
  await this.manifestRepository.load(input.projectRoot).catch(() => null);
29
- const findings = (await Promise.all(
46
+ const rawFindings = (await Promise.all(
30
47
  this.checks.map((check) => check.run(input.projectRoot, this.inspector)),
31
48
  )).filter((finding) => finding !== null);
49
+ const { findings, scopedOutFindings } = this.applyAgentScope(rawFindings, agent);
32
50
  const report = DiagnosticReport.create(findings);
33
51
  return {
34
52
  report,
53
+ agent,
54
+ scopedOutFindings,
35
55
  exitCode: this.decideExitCode(report, input.strict),
36
56
  };
37
57
  }
38
58
 
59
+ private applyAgentScope(
60
+ findings: readonly DiagnosticFinding[],
61
+ agent: DoctorAgentScope,
62
+ ): { readonly findings: readonly DiagnosticFinding[]; readonly scopedOutFindings: readonly ScopedOutDiagnosticFinding[] } {
63
+ if (agent === "both") return { findings, scopedOutFindings: [] };
64
+ const scopedOutChecks = agent === "claude" ? CODEX_ONLY_CHECKS : CLAUDE_ONLY_CHECKS;
65
+ const applicable: DiagnosticFinding[] = [];
66
+ const scopedOut: ScopedOutDiagnosticFinding[] = [];
67
+ for (const finding of findings) {
68
+ if (scopedOutChecks.has(finding.checkId)) {
69
+ scopedOut.push({
70
+ finding,
71
+ scopeReason: `${finding.checkId} belongs to an unselected agent for doctor --agent ${agent}.`,
72
+ });
73
+ } else {
74
+ applicable.push(finding);
75
+ }
76
+ }
77
+ return { findings: applicable, scopedOutFindings: scopedOut };
78
+ }
79
+
39
80
  private decideExitCode(report: DiagnosticReport, strict: boolean): number {
40
81
  if (report.hasRedFlag()) return 1;
41
82
  if (strict && report.hasWarning()) return 1;
@@ -3,6 +3,7 @@
3
3
  // @work-item-id WI-146
4
4
  // @work-item-id WI-174
5
5
  // @work-item-id WI-175
6
+ // @work-item-id WI-177
6
7
 
7
8
  import { mkdir, readFile, writeFile, copyFile, chmod, access, lstat, readlink, symlink } from "node:fs/promises";
8
9
  import { dirname, join } from "node:path";
@@ -236,14 +237,16 @@ function likelyCauseFor(code: string): string {
236
237
  if (code === "EPERM") return "The filesystem or sandbox denied this write operation.";
237
238
  if (code === "EACCES") return "The current user does not have permission to write this target.";
238
239
  if (code === "EROFS") return "The project is on a read-only filesystem.";
240
+ if (code === "EEXIST") return "A parent path already exists as a file or incompatible filesystem entry.";
239
241
  if (code === "ENOTDIR") return "A parent path exists but is not a directory.";
240
242
  return "The managed target could not be written.";
241
243
  }
242
244
 
243
245
  function recoveryFor(code: string, target: string): string {
244
- if (code === "EPERM") return `Review sandbox or filesystem permissions for ${target}, then rerun phasegate setup:agent --apply or phasegate install --apply.`;
246
+ if (code === "EPERM") return `Review sandbox or filesystem permissions for ${target}, ask the user for write access when needed, then rerun phasegate setup:agent --apply or phasegate install --apply.`;
245
247
  if (code === "EACCES") return `Fix ownership or permissions for ${target}, then rerun phasegate install --apply.`;
246
248
  if (code === "EROFS") return `Move the project to a writable filesystem or rerun in a writable workspace before applying ${target}.`;
249
+ if (code === "EEXIST" || code === "ENOTDIR") return `Inspect the parent path for ${target}; if it is user-owned, rename or move it before rerunning phasegate install --dry-run --json and then --apply.`;
247
250
  return `Inspect ${target}, run phasegate install --dry-run --json, then rerun with --apply after resolving the filesystem issue.`;
248
251
  }
249
252
 
@@ -1,10 +1,11 @@
1
1
  // @unit installation
2
2
  // @layer presentation
3
3
  // @work-item-id WI-145
4
+ // @work-item-id WI-178
4
5
 
5
6
  import { mkdir, writeFile } from "node:fs/promises";
6
7
  import { dirname, isAbsolute, join } from "node:path";
7
- import type { RunDoctorDiagnosticsUseCase } from "../../application/usecases/run-doctor-diagnostics.js";
8
+ import type { DoctorAgentScope, RunDoctorDiagnosticsUseCase } from "../../application/usecases/run-doctor-diagnostics.js";
8
9
  import { DiagnosticReportFormatter } from "../formatters/diagnostic-report-formatter.js";
9
10
 
10
11
  export interface DoctorHandlerInput {
@@ -13,6 +14,7 @@ export interface DoctorHandlerInput {
13
14
  readonly json: boolean;
14
15
  readonly reportOut: string | null;
15
16
  readonly phasegateVersion: string;
17
+ readonly agent?: DoctorAgentScope;
16
18
  }
17
19
 
18
20
  export interface DoctorHandlerOutput {
@@ -30,9 +32,12 @@ export class DoctorHandler {
30
32
  const result = await this.useCase.execute({
31
33
  projectRoot: input.projectRoot,
32
34
  strict: input.strict,
35
+ agent: input.agent,
33
36
  });
34
37
  const formatInput = {
35
38
  report: result.report,
39
+ agent: result.agent,
40
+ scopedOutFindings: result.scopedOutFindings,
36
41
  phasegateVersion: input.phasegateVersion,
37
42
  projectRoot: input.projectRoot,
38
43
  exitCode: result.exitCode,
@@ -1,11 +1,15 @@
1
1
  // @unit installation
2
2
  // @layer presentation
3
3
  // @work-item-id WI-145
4
+ // @work-item-id WI-178
4
5
 
6
+ import type { DoctorAgentScope, ScopedOutDiagnosticFinding } from "../../application/usecases/run-doctor-diagnostics.js";
5
7
  import type { DiagnosticReport } from "../../domain/diagnostic-report.js";
6
8
 
7
9
  export interface DiagnosticReportFormatterInput {
8
10
  readonly report: DiagnosticReport;
11
+ readonly agent: DoctorAgentScope;
12
+ readonly scopedOutFindings: readonly ScopedOutDiagnosticFinding[];
9
13
  readonly phasegateVersion: string;
10
14
  readonly projectRoot: string;
11
15
  readonly exitCode: number;
@@ -18,8 +22,20 @@ export class DiagnosticReportFormatter {
18
22
  schemaVersion: "1.0",
19
23
  phasegateVersion: input.phasegateVersion,
20
24
  projectRoot: input.projectRoot,
25
+ scope: {
26
+ agent: input.agent,
27
+ description: scopeDescription(input.agent),
28
+ },
21
29
  overallStatus: input.report.overallStatus,
22
- findings: input.report.findings.map((finding) => finding.toJSON()),
30
+ findings: input.report.findings.map((finding) => ({
31
+ ...finding.toJSON(),
32
+ applicability: "applicable",
33
+ })),
34
+ scopedOutFindings: input.scopedOutFindings.map(({ finding, scopeReason }) => ({
35
+ ...finding.toJSON(),
36
+ applicability: "not-applicable",
37
+ scopeReason,
38
+ })),
23
39
  exitCode: input.exitCode,
24
40
  },
25
41
  null,
@@ -31,6 +47,7 @@ export class DiagnosticReportFormatter {
31
47
  const lines = [
32
48
  `phasegate doctor v${input.phasegateVersion}`,
33
49
  `Project: ${input.projectRoot}`,
50
+ `Scope: ${input.agent} (${scopeDescription(input.agent)})`,
34
51
  "",
35
52
  ];
36
53
  for (const finding of input.report.findings) {
@@ -47,7 +64,16 @@ export class DiagnosticReportFormatter {
47
64
  const redCount = input.report.findings.filter((finding) => finding.severity === "red").length;
48
65
  const warnCount = input.report.findings.filter((finding) => finding.severity === "warn").length;
49
66
  lines.push(`Status: ${input.report.overallStatus.toUpperCase()} (${input.report.findings.length} findings: ${redCount} red, ${warnCount} warn)`);
67
+ if (input.scopedOutFindings.length > 0) {
68
+ lines.push(`Scoped out: ${input.scopedOutFindings.length} findings not applicable to --agent ${input.agent}`);
69
+ }
50
70
  lines.push(`Exit: ${input.exitCode}`);
51
71
  return lines.join("\n");
52
72
  }
53
73
  }
74
+
75
+ function scopeDescription(agent: DoctorAgentScope): string {
76
+ if (agent === "claude") return "Claude Code and shared setup targets; Codex-only findings are not applicable.";
77
+ if (agent === "codex") return "Codex and shared setup targets; Claude-only findings are not applicable.";
78
+ return "Full setup diagnostics for Claude, Codex, and shared targets.";
79
+ }
@@ -152,7 +152,7 @@ Setup:
152
152
  --skills <core|all>, --agent <claude|codex|both>, --workflow <standard|strict>,
153
153
  --with-husky, --with-ci, --yes)
154
154
  update-skills Alias for reconcile (kept for compatibility)
155
- doctor Diagnose silent installation failures (--json, --strict, --report-out <path>)
155
+ doctor Diagnose silent installation failures (--json, --strict, --agent <claude|codex|both>, --report-out <path>)
156
156
  scaffold-wi <unit> <type> Create docs/inception/{unit}/WI-XXX/description.md
157
157
  emit-agent-rules Print AGENTS.md / CLAUDE.md WI workflow rules block
158
158
  install Install phasegate managed files (--dry-run|--apply, --force)
@@ -772,6 +772,16 @@ function parseAgentTarget(value: string | undefined, fallback: AgentTarget = "bo
772
772
  return fallback;
773
773
  }
774
774
 
775
+ function parseRequiredAgentTarget(value: string | undefined, fallback: AgentTarget = "both"): AgentTarget | null {
776
+ if (value === undefined) return fallback;
777
+ if (value === "claude" || value === "codex" || value === "both") return value;
778
+ return null;
779
+ }
780
+
781
+ function doctorValidationCommand(agent: AgentTarget): string {
782
+ return agent === "both" ? "phasegate doctor" : `phasegate doctor --agent ${agent}`;
783
+ }
784
+
775
785
  function parseSetupIntent(value: string | undefined): SetupIntent {
776
786
  if (
777
787
  value === "minimal" ||
@@ -863,6 +873,7 @@ function buildSetupCompleteness(input: {
863
873
  }): readonly SetupCompletenessEntry[] {
864
874
  const includeClaude = input.agent === "claude" || input.agent === "both";
865
875
  const includeCodex = input.agent === "codex" || input.agent === "both";
876
+ const doctorCommand = doctorValidationCommand(input.agent);
866
877
  const agentHooksConfigured = (!includeClaude || input.checks.claudeSettings) && (!includeCodex || input.checks.codexHooks);
867
878
  const agentContextConfigured = (!includeClaude || input.checks.claudeMd) && (!includeCodex || input.checks.agentsMd);
868
879
  const entries: SetupCompletenessEntry[] = [
@@ -872,7 +883,7 @@ function buildSetupCompleteness(input: {
872
883
  configured: input.checks.packageJson && input.checks.phasegateConfig,
873
884
  configuredEvidence: "package.json and phasegate.config.json are present.",
874
885
  plannedEvidence: "setup:agent will create or merge package.json scripts and phasegate.config.json.",
875
- nextAction: "Run setup:agent --apply, then phasegate doctor.",
886
+ nextAction: `Run setup:agent --apply, then ${doctorCommand}.`,
876
887
  }),
877
888
  setupCompletenessEntry({
878
889
  area: "agent-hooks",
@@ -922,7 +933,7 @@ function buildSetupCompleteness(input: {
922
933
  configured: input.checks.packageJson && input.checks.phasegateConfig,
923
934
  configuredEvidence: "Local validation commands can be run against the configured project.",
924
935
  plannedEvidence: "Validation commands are planned after setup apply.",
925
- nextAction: "Run phasegate doctor, phasegate phasegate:check-ready, and phasegate validate --layer L2 --format human.",
936
+ nextAction: `Run ${doctorCommand}, phasegate phasegate:check-ready, and phasegate validate --layer L2 --format human.`,
926
937
  }),
927
938
  ];
928
939
 
@@ -994,6 +1005,7 @@ function buildAgentReadiness(input: {
994
1005
  }): readonly AgentReadinessEntry[] {
995
1006
  const includeClaude = input.agent === "claude" || input.agent === "both";
996
1007
  const includeCodex = input.agent === "codex" || input.agent === "both";
1008
+ const doctorCommand = doctorValidationCommand(input.agent);
997
1009
  const sharedConfigured =
998
1010
  input.checks.packageJson &&
999
1011
  input.checks.phasegateConfig &&
@@ -1045,7 +1057,7 @@ function buildAgentReadiness(input: {
1045
1057
  plannedEvidence: [
1046
1058
  "setup:agent will create or refresh package scripts, phasegate.config.json, skills, and selected Husky/CI targets.",
1047
1059
  ],
1048
- nextAction: "Run setup:agent --apply, then phasegate doctor, phasegate phasegate:check-ready, and phasegate validate --layer L2 --format human.",
1060
+ nextAction: `Run setup:agent --apply, then ${doctorCommand}, phasegate phasegate:check-ready, and phasegate validate --layer L2 --format human.`,
1049
1061
  risk: input.withCi ? "A hosted CI run remains an external manual check." : undefined,
1050
1062
  }),
1051
1063
  ];
@@ -1103,7 +1115,7 @@ async function buildAgentSetupPlan(rootDir: string, input: {
1103
1115
  "Backups are written under .phasegate/backups when force is used on changed managed files.",
1104
1116
  ],
1105
1117
  validation: [
1106
- "phasegate doctor",
1118
+ doctorValidationCommand(input.agent),
1107
1119
  "phasegate phasegate:check-ready",
1108
1120
  "phasegate validate --layer L2 --format human",
1109
1121
  ],
@@ -1728,6 +1740,17 @@ async function main(): Promise<void> {
1728
1740
  }
1729
1741
 
1730
1742
  case "doctor": {
1743
+ const KNOWN_DOCTOR_FLAGS = ["--json", "--strict", "--agent", "--report-out"];
1744
+ const flagError = validateKnownFlags(args, KNOWN_DOCTOR_FLAGS);
1745
+ if (flagError) {
1746
+ console.error(flagError);
1747
+ process.exit(2);
1748
+ }
1749
+ const agent = parseRequiredAgentTarget(parseFlag(args, "--agent"), "both");
1750
+ if (agent === null) {
1751
+ console.error(`Invalid --agent value: "${parseFlag(args, "--agent")}". Use "claude", "codex", or "both".`);
1752
+ process.exit(2);
1753
+ }
1731
1754
  const mod = createInstallationModule();
1732
1755
  const phasegateVersion = await getHarnessVersion(harnessRoot);
1733
1756
  const result = await mod.doctorHandler.execute({
@@ -1736,6 +1759,7 @@ async function main(): Promise<void> {
1736
1759
  json,
1737
1760
  reportOut: parseFlag(args, "--report-out") ?? null,
1738
1761
  phasegateVersion,
1762
+ agent,
1739
1763
  });
1740
1764
  console.log(result.stdout);
1741
1765
  process.exit(result.exitCode);
@@ -127,12 +127,14 @@ product-architect で Unit を作り、いくつかの logical_design を書い
127
127
  #### 観点 9: setup lifecycle と doctor finding
128
128
 
129
129
  - `phasegate doctor --json` の finding に `repairMode: "ai-assisted"` と `suggestedSkill.skillName = "phasegate-config-doctor"` がある → 本 skill が merge 方針、保持する user content、実行すべき `install --apply` / `--force` / `reconcile --apply` を提案する
130
+ - Claude-only / Codex-only 導入後は `phasegate doctor --agent claude --json` または `phasegate doctor --agent codex --json` を使って selected agent の readiness を読む。`scopedOutFindings` は未選択 agent の `not-applicable` 情報なので、ユーザーがその agent を導入したいと言っていない限り repair 提案にしない。<!-- @work-item-id WI-178 -->
130
131
  - `repairHint` がある mechanical finding → 原則として hint のコマンドを優先し、実行前に対象ファイルと manifest の差分を確認
131
132
  - manifest parse error → `.phasegate/manifest.json` を手で修復する前に backup / uninstall / reinstall の選択肢を提示
132
133
  - reconcile / uninstall が refuse → user modified managed target として扱い、`--force` のリスクと backup path を説明して承認を取る
133
134
  - Codex の `codex_hooks` feature flag は user-level setting。project-local `install` では変更されないため、必要なら `codex features enable codex_hooks` を案内
134
135
  - Codex native `apply_patch` bypass は hook で完全捕捉できない。`.husky/pre-commit` の `phasegate pre-commit` が backstop になるため、Husky 配線を診断対象に含める
135
136
  - 初回 setup / retrofit / CI-only / agent-hooks の判断が曖昧な場合は `npx phasegate setup:agent --dry-run --json` を先に使い、検出済み状態、質問、変更案、rollback、validation を根拠として提案する。<!-- @work-item-id WI-172 -->
137
+ - Claude Code setup 相談では `npx phasegate setup:agent --agent claude --dry-run --json` を先に使い、`plan.agentReadiness` の `claude` / `shared` が `configured` なら setup 修復ではなく WI 起票、inception 計画、product reflection、validation の順で作業開始を案内する。`setup:agent --apply --json` または `install --apply --json` の structured `error` がある場合は、`target`, `operation`, `code`, `likelyCause`, `recovery`, `partialChanges` を読み、`.claude` が file として存在する path conflict、sandbox/permission denial、PhaseGate managed target hash mismatch を区別して説明する。<!-- @work-item-id WI-177 -->
136
138
  - 「L4 を厳しめにして」「Codex hook を有効にして」「CI で warning を fail にして」などの自然言語依頼は、`npx phasegate config:plan --intent <intent> --dry-run --json` で対象ファイル、コマンド、リスク、検証を確認してから diff を提案する。<!-- @work-item-id WI-173 -->
137
139
  - `AGENTS.md` は標準運用ルールの managed section と lesson pointer section を分ける。`ci:auto-refresh-agent-context --apply` は lesson pointers 専用 section を更新し、標準運用ルールや user-owned content を置換してはいけない。<!-- @work-item-id WI-174 -->
138
140
 
@@ -132,8 +132,12 @@ docs/guide/ # phasegate リポジトリ自体 (dogfood)
132
132
 
133
133
  `setup-artifacts.md` は managed target / generated artifact / runtime state / legacy artifact / user-level setting の分類を持つ。`doctor --report-out` は明示 path への出力で、`.phasegate/last-doctor-report.json` は固定生成物ではない点もここを参照する。<!-- @work-item-id WI-153 -->
134
134
 
135
+ Claude-only / Codex-only setup の確認では、full `phasegate doctor` と scoped doctor を区別する。ユーザーが `setup:agent --agent claude` を選んだ場合は `phasegate doctor --agent claude --json` を優先し、`scopedOutFindings` の Codex finding は「未選択 agent の not-applicable 情報」であり修復対象ではないと説明する。full doctor は両 agent を導入したい場合の診断として扱う。<!-- @work-item-id WI-178 -->
136
+
135
137
  `setup:agent` は初回 setup / retrofit / CI-only / agent hook 有効化の agent-readable planner、`config:plan` は安全な設定変更 intent planner。質問が「次に何を実行するか」「この変更はどの file/validation に対応するか」に寄っている場合は、これらの guide と CLI を案内する。<!-- @work-item-id WI-171, WI-172, WI-173 -->
136
138
 
139
+ Claude Code readiness の質問では `npx phasegate setup:agent --agent claude --dry-run --json` を案内し、`plan.agentReadiness` の `claude` / `shared` が `configured` なら setup ではなく作業導線へ進める。最短ルートは、WI の確認または起票、`docs/inception/.../{WI-XXX}/` への計画/設計、`docs/product/...` への `@work-item-id WI-XXX` 反映、`phasegate phasegate:check-ready` または該当 `validate` の実行である。`setup:agent --apply --json` の structured `error` は troubleshooting に委譲し、`target` / `operation` / `code` / `recovery` を読ませる。<!-- @work-item-id WI-177 -->
140
+
137
141
  ### 8. skill 一覧と使い分け
138
142
 
139
143
  ユーザー質問例: