phasegate 0.160.18 → 0.160.19

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/README.md CHANGED
@@ -109,7 +109,7 @@ npx phasegate install --personal --agent claude --dry-run
109
109
  npx phasegate install --personal --agent claude --apply
110
110
  ```
111
111
 
112
- `--personal` does not plan or write `package.json`, `AGENTS.md`, `CLAUDE.md`, `.husky/*`, `.github/workflows/*`, `.gitignore`, GitHub CLI config, repo secrets, or CI settings. It creates `.phasegate-local/phasegate.config.json`, local agent context (`.claude/CLAUDE.local.md` and/or `.codex/AGENTS.local.md`), real local-only agent runtime artifacts for the selected agent (`.claude/settings.json` + `.claude/skills/` and/or `.codex/hooks.json` + `.codex/skills/`), local git hooks under `.git/hooks/`, local reference docs under `.phasegate-local/docs/`, a managed local-only block in `.git/info/exclude`, and `.phasegate/manifest.json`. Codex user-level hook feature enablement remains a manual action. <!-- @work-item-id WI-207 --> <!-- @work-item-id WI-208 --> <!-- @work-item-id WI-209 --> <!-- @work-item-id WI-213 -->
112
+ `--personal` does not plan or write `package.json`, `CLAUDE.md`, `.husky/*`, `.github/workflows/*`, `.gitignore`, GitHub CLI config, repo secrets, or CI settings. It creates `.phasegate-local/phasegate.config.json`, runtime-visible local agent context (`.claude/CLAUDE.md` for Claude, and `AGENTS.md` for Codex only when that root file is absent or already PhaseGate-managed), real local-only agent runtime artifacts for the selected agent (`.claude/settings.json` + `.claude/skills/` and/or `.codex/hooks.json` + `.codex/skills/`), local git hooks under `.git/hooks/`, local reference docs under `.phasegate-local/docs/`, a managed local-only block in `.git/info/exclude`, and `.phasegate/manifest.json`. If a team `AGENTS.md` already exists, personal Codex install leaves it unchanged and `doctor --personal --agent codex` reports the remaining context step instead of hiding it behind `AGENTS.override.md`. Codex user-level hook feature enablement remains a manual action. <!-- @work-item-id WI-207 --> <!-- @work-item-id WI-208 --> <!-- @work-item-id WI-209 --> <!-- @work-item-id WI-213 --> <!-- @work-item-id WI-215 -->
113
113
 
114
114
  For agent-driven setup planning, use:
115
115
 
@@ -65,7 +65,7 @@ npx phasegate install --personal --agent claude --dry-run
65
65
  npx phasegate install --personal --agent claude --apply
66
66
  ```
67
67
 
68
- Personal install keeps team-owned files out of both the plan and apply path: `package.json`, `AGENTS.md`, `CLAUDE.md`, `.husky/*`, `.github/workflows/*`, `.gitignore`, GitHub CLI config, repo secrets, and CI settings are not touched. PhaseGate writes `.phasegate-local/phasegate.config.json`, creates local agent context (`.claude/CLAUDE.local.md` and/or `.codex/AGENTS.local.md`), creates real local-only agent runtime artifacts for the selected agent (`.claude/settings.json` + `.claude/skills/` and/or `.codex/hooks.json` + `.codex/skills/`), deploys local git hooks under `.git/hooks/`, copies reference docs under `.phasegate-local/docs/`, records `.phasegate/manifest.json`, and manages a local exclude block in `.git/info/exclude`. Codex user-level hook feature enablement is still reported as a manual action. <!-- @work-item-id WI-207 --> <!-- @work-item-id WI-208 --> <!-- @work-item-id WI-209 --> <!-- @work-item-id WI-213 -->
68
+ Personal install keeps team-owned files out of the apply path: `package.json`, `CLAUDE.md`, `.husky/*`, `.github/workflows/*`, `.gitignore`, GitHub CLI config, repo secrets, and CI settings are not touched. PhaseGate writes `.phasegate-local/phasegate.config.json`, creates runtime-visible local agent context (`.claude/CLAUDE.md` for Claude, and `AGENTS.md` for Codex only when that root file is absent or already PhaseGate-managed), creates real local-only agent runtime artifacts for the selected agent (`.claude/settings.json` + `.claude/skills/` and/or `.codex/hooks.json` + `.codex/skills/`), deploys local git hooks under `.git/hooks/`, copies reference docs under `.phasegate-local/docs/`, records `.phasegate/manifest.json`, and manages a local exclude block in `.git/info/exclude`. If a team `AGENTS.md` already exists, personal Codex install leaves it unchanged and `doctor --personal --agent codex` reports the remaining context step instead of hiding it behind `AGENTS.override.md`. Codex user-level hook feature enablement is still reported as a manual action. <!-- @work-item-id WI-207 --> <!-- @work-item-id WI-208 --> <!-- @work-item-id WI-209 --> <!-- @work-item-id WI-213 --> <!-- @work-item-id WI-215 -->
69
69
 
70
70
  If an existing repository keeps design or governance docs outside `docs/`, set `paths.designDocs`, `paths.inceptionDocs`, `paths.principlesDocs`, and `paths.folderRulesDoc` in `phasegate.config.json` before setup/reconcile. PhaseGate deploy and hook protection use those mappings instead of forcing the default `docs/` layout. <!-- @work-item-id WI-214 -->
71
71
 
@@ -7,6 +7,7 @@ PhaseGate setup is more than `phasegate.config.json`. A healthy installation is
7
7
  <!-- @work-item-id WI-169 -->
8
8
  <!-- @work-item-id WI-208 -->
9
9
  <!-- @work-item-id WI-209 -->
10
+ <!-- @work-item-id WI-215 -->
10
11
 
11
12
  ## Artifact Classes
12
13
 
@@ -16,7 +17,7 @@ PhaseGate setup is more than `phasegate.config.json`. A healthy installation is
16
17
  | Configuration | `phasegate.config.json`, `package.json` | User owned, PhaseGate assisted | Created by `init` or project install when absent; `install` may merge scripts/devDependency into `package.json` |
17
18
  | Generated artifact | `.phasegate/manifest.json`, `.phasegate/backups/*`, `.phasegate/uninstalled-*.json`, `.phasegate/baseline.json` | PhaseGate | Written by lifecycle commands and validators; safe to regenerate only through the owning command |
18
19
  | Runtime state/report | `.phasegate/hook-skip-events.jsonl`, explicit `doctor --report-out <path>` output, `reports/regression/*`, resolved `reporting.outputDir` reports | PhaseGate command output | Produced while hooks, doctor, and validation commands run |
19
- | Personal install artifact | `.phasegate-local/phasegate.config.json`, ignored real `.claude/settings.json` + `.claude/skills/`, ignored real `.codex/hooks.json` + `.codex/skills/`, `.git/info/exclude` PhaseGate block | One developer on one machine | Created by `phasegate install --personal --agent <agent>`; team `.gitignore`, team-owned files, GitHub CLI config, repo secrets, and CI settings are not modified |
20
+ | Personal install artifact | `.phasegate-local/phasegate.config.json`, ignored real `.claude/CLAUDE.md` + `.claude/settings.json` + `.claude/skills/`, ignored real `.codex/hooks.json` + `.codex/skills/`, optional ignored root `AGENTS.md`, `.git/info/exclude` PhaseGate block | One developer on one machine | Created by `phasegate install --personal --agent <agent>`; team `.gitignore`, team-owned files, GitHub CLI config, repo secrets, and CI settings are not modified |
20
21
  | Legacy artifact | `.harness-hooks.yml`, old Fuse hook files, `.harness/session-state.json`, `.harness/context-priority.json`, `.harness/reports` fallback | Compatibility only | Not required for current install lifecycle unless a project intentionally keeps an archived integration |
21
22
  | User-level setting | Codex CLI `hooks` feature flag | User machine | Must be enabled manually with `codex features enable hooks`; project commands do not modify it |
22
23
 
@@ -44,6 +45,8 @@ PhaseGate setup is more than `phasegate.config.json`. A healthy installation is
44
45
 
45
46
  If `.claude/*` or `.codex/*` already exists and is not a PhaseGate-managed personal artifact, personal install reports manual review and preserves the existing path. <!-- @work-item-id WI-209 -->
46
47
 
48
+ Personal agent context is also placed only where the runtime will read it. Claude Code uses `.claude/CLAUDE.md`. Codex uses root `AGENTS.md` when PhaseGate can create or manage it locally; if a team `AGENTS.md` already exists, PhaseGate leaves it unchanged and doctor reports `codex-context-missing` instead of creating `AGENTS.override.md`. <!-- @work-item-id WI-215 -->
49
+
47
50
  Project install uses a different topology: selected bundled skills are deployed once to root `skills/`, and `.claude/skills` / `.codex/skills` point to that shared target. If an older project install has the links but an empty `skills/` target, `phasegate doctor` reports the selected agent skill check and `phasegate reconcile --apply` repairs the shared skill bodies. <!-- @work-item-id WI-210 -->
48
51
 
49
52
  ## Doctor Findings
@@ -4,3 +4,4 @@
4
4
  .claude/
5
5
  skills/
6
6
  phasegate.config.json
7
+ AGENTS.md
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.160.18",
3
+ "version": "0.160.19",
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",
@@ -0,0 +1,35 @@
1
+ // @unit installation
2
+ // @layer application
3
+ // @work-item-id WI-215
4
+
5
+ import type { FileInspectorPort } from "../ports/file-inspector-port.js";
6
+ import type { HeuristicCheck } from "../../domain/ports/heuristic-check.js";
7
+ import type { DiagnosticFinding } from "../../domain/diagnostic-finding.js";
8
+ import { createFinding, projectPath } from "./check-utils.js";
9
+
10
+ const MARKDOWN_BEGIN = "<!-- phasegate:managed-section:start -->";
11
+
12
+ export class ClaudeContextMissingCheck implements HeuristicCheck {
13
+ readonly checkId = "claude-context-missing" as const;
14
+ private readonly target = ".claude/CLAUDE.md";
15
+ private readonly projectTarget = "CLAUDE.md";
16
+ private readonly legacyTarget = ".claude/CLAUDE.local.md";
17
+
18
+ async run(projectRoot: string, inspector: FileInspectorPort): Promise<DiagnosticFinding | null> {
19
+ const content = await inspector.readText(projectPath(projectRoot, this.target));
20
+ if (content?.includes(MARKDOWN_BEGIN) === true && content.includes("PhaseGate")) return null;
21
+ const projectContent = await inspector.readText(projectPath(projectRoot, this.projectTarget));
22
+ if (projectContent?.includes(MARKDOWN_BEGIN) === true && projectContent.includes("PhaseGate")) return null;
23
+
24
+ const legacyContent = await inspector.readText(projectPath(projectRoot, this.legacyTarget));
25
+ return createFinding({
26
+ checkId: this.checkId,
27
+ severity: "red",
28
+ target: this.target,
29
+ message: legacyContent?.includes("PhaseGate") === true
30
+ ? ".claude/CLAUDE.local.md は Claude Code の documented discovery path ではありません"
31
+ : ".claude/CLAUDE.md に runtime-visible PhaseGate context がありません",
32
+ repairMode: "mechanical",
33
+ });
34
+ }
35
+ }
@@ -0,0 +1,32 @@
1
+ // @unit installation
2
+ // @layer application
3
+ // @work-item-id WI-215
4
+
5
+ import type { FileInspectorPort } from "../ports/file-inspector-port.js";
6
+ import type { HeuristicCheck } from "../../domain/ports/heuristic-check.js";
7
+ import type { DiagnosticFinding } from "../../domain/diagnostic-finding.js";
8
+ import { createFinding, projectPath } from "./check-utils.js";
9
+
10
+ const MARKDOWN_BEGIN = "<!-- phasegate:managed-section:start -->";
11
+
12
+ export class CodexContextMissingCheck implements HeuristicCheck {
13
+ readonly checkId = "codex-context-missing" as const;
14
+ private readonly target = "AGENTS.md";
15
+ private readonly legacyTarget = ".codex/AGENTS.local.md";
16
+
17
+ async run(projectRoot: string, inspector: FileInspectorPort): Promise<DiagnosticFinding | null> {
18
+ const content = await inspector.readText(projectPath(projectRoot, this.target));
19
+ if (content?.includes(MARKDOWN_BEGIN) === true && content.includes("PhaseGate")) return null;
20
+
21
+ const legacyContent = await inspector.readText(projectPath(projectRoot, this.legacyTarget));
22
+ return createFinding({
23
+ checkId: this.checkId,
24
+ severity: "red",
25
+ target: this.target,
26
+ message: legacyContent?.includes("PhaseGate") === true
27
+ ? ".codex/AGENTS.local.md は Codex の default discovery path ではありません"
28
+ : "AGENTS.md に runtime-visible PhaseGate context がありません",
29
+ repairMode: content === null ? "mechanical" : "manual",
30
+ });
31
+ }
32
+ }
@@ -3,6 +3,7 @@
3
3
  // @work-item-id WI-145
4
4
  // @work-item-id WI-178
5
5
  // @work-item-id WI-208
6
+ // @work-item-id WI-215
6
7
 
7
8
  import { DiagnosticReport } from "../../domain/diagnostic-report.js";
8
9
  import type { CheckId } from "../../domain/check-id.js";
@@ -32,8 +33,8 @@ export interface RunDoctorDiagnosticsOutput {
32
33
  readonly exitCode: number;
33
34
  }
34
35
 
35
- const CLAUDE_ONLY_CHECKS = new Set<CheckId>(["claude-hook-missing", "claude-skills-symlink"]);
36
- const CODEX_ONLY_CHECKS = new Set<CheckId>(["codex-hook-missing", "codex-skills-symlink"]);
36
+ const CLAUDE_ONLY_CHECKS = new Set<CheckId>(["claude-hook-missing", "claude-context-missing", "claude-skills-symlink"]);
37
+ const CODEX_ONLY_CHECKS = new Set<CheckId>(["codex-hook-missing", "codex-context-missing", "codex-skills-symlink"]);
37
38
  const PERSONAL_SCOPED_OUT_CHECKS = new Set<CheckId>([
38
39
  "husky-pre-commit-missing",
39
40
  "husky-commit-msg-missing",
@@ -13,6 +13,7 @@
13
13
  // @work-item-id WI-210
14
14
  // @work-item-id WI-213
15
15
  // @work-item-id WI-214
16
+ // @work-item-id WI-215
16
17
 
17
18
  import { mkdir, readFile, writeFile, copyFile, chmod, access, lstat, readlink, symlink, readdir, rm } from "node:fs/promises";
18
19
  import { dirname, join } from "node:path";
@@ -78,6 +79,7 @@ interface InstallTarget {
78
79
  readonly templatePath: string;
79
80
  readonly executable?: boolean;
80
81
  readonly block?: ManagedBlockInput;
82
+ readonly personalManualIfUnmanaged?: boolean;
81
83
  }
82
84
 
83
85
  const SKILL_HINT = "invoke /phasegate-config-doctor";
@@ -89,7 +91,7 @@ const MARKDOWN_BEGIN = "<!-- phasegate:managed-section:start -->";
89
91
  const MARKDOWN_END = "<!-- phasegate:managed-section:end -->";
90
92
  const TEXT_BEGIN = "# phasegate personal install exclude (BEGIN)";
91
93
  const TEXT_END = "# phasegate personal install exclude (END)";
92
- const PERSONAL_AGENT_RUNTIME_FILES = new Set([".claude/settings.json", ".codex/hooks.json"]);
94
+ const PERSONAL_AGENT_RUNTIME_FILES = new Set([".claude/CLAUDE.md", ".claude/settings.json", "AGENTS.md", ".codex/hooks.json"]);
93
95
  const SHARED_SKILLS_VERSION_PATH = "skills/.harness-version";
94
96
  const PERSONAL_PRINCIPLES_DOCS = ".phasegate-local/docs/principles";
95
97
  const PERSONAL_FOLDER_RULES_DOC = ".phasegate-local/docs/folder_management_rules.md";
@@ -350,8 +352,9 @@ export class RunInstallUseCase {
350
352
  const existingEntry = baseManifest.findEntry(target.path);
351
353
  const beforeHash = before === null ? null : this.hashCalculator.compute(before);
352
354
  const unmanagedPersonalRuntimeFile = input.personal
353
- && PERSONAL_AGENT_RUNTIME_FILES.has(target.path)
355
+ && (PERSONAL_AGENT_RUNTIME_FILES.has(target.path) || target.personalManualIfUnmanaged === true)
354
356
  && before !== null
357
+ && !before.includes(MARKDOWN_BEGIN)
355
358
  && (existingEntry === null || beforeHash === null || !beforeHash.equals(existingEntry.hash));
356
359
  const repairMode = unmanagedPersonalRuntimeFile ? "manual" : this.repairMode(target, before);
357
360
  const next = unmanagedPersonalRuntimeFile ? before : this.merge(target, before, template, input.phasegateVersion);
@@ -364,7 +367,7 @@ export class RunInstallUseCase {
364
367
  strategy: target.strategy,
365
368
  changed: didChange,
366
369
  summary: unmanagedPersonalRuntimeFile
367
- ? `${target.path}: existing non-phasegate path requires manual review`
370
+ ? `${target.path}: existing non-phasegate runtime path requires manual review`
368
371
  : didChange ? `${target.path}: ${action}` : `${target.path}: already up to date`,
369
372
  diff: unmanagedPersonalRuntimeFile ? "manual review required" : this.diffSummary(before, next),
370
373
  skillHint: repairMode === "ai-assisted" ? SKILL_HINT : null,
@@ -629,10 +632,11 @@ export class RunInstallUseCase {
629
632
  ...(options.includeClaude
630
633
  ? [
631
634
  {
632
- path: ".claude/CLAUDE.local.md",
635
+ path: ".claude/CLAUDE.md",
633
636
  strategy: "markdown-managed" as const,
634
637
  templatePath: "docs/templates/agent-context/CLAUDE.md.template.md",
635
- block: { start: MARKDOWN_BEGIN, end: MARKDOWN_END, content: "phasegate personal CLAUDE.local.md managed section" },
638
+ block: { start: MARKDOWN_BEGIN, end: MARKDOWN_END, content: "phasegate personal .claude/CLAUDE.md managed section" },
639
+ personalManualIfUnmanaged: true,
636
640
  },
637
641
  {
638
642
  path: ".claude/settings.json",
@@ -644,10 +648,11 @@ export class RunInstallUseCase {
644
648
  ...(options.includeCodex
645
649
  ? [
646
650
  {
647
- path: ".codex/AGENTS.local.md",
651
+ path: "AGENTS.md",
648
652
  strategy: "markdown-managed" as const,
649
653
  templatePath: "docs/templates/agent-context/AGENTS.md.template.md",
650
- block: { start: MARKDOWN_BEGIN, end: MARKDOWN_END, content: "phasegate personal AGENTS.local.md managed section" },
654
+ block: { start: MARKDOWN_BEGIN, end: MARKDOWN_END, content: "phasegate personal AGENTS.md managed section" },
655
+ personalManualIfUnmanaged: true,
651
656
  },
652
657
  {
653
658
  path: ".codex/hooks.json",
@@ -365,7 +365,9 @@ export class RunUninstallUseCase {
365
365
  if (mode === "symlink") return "symlink";
366
366
  if (mode === "created") return path.endsWith(".yml") || path.endsWith(".yaml") ? "yaml-add" : "created";
367
367
  if (path === "package.json") return "package-json";
368
- if (path === "AGENTS.md" || path === "CLAUDE.md") return "markdown-managed";
368
+ if (path === "AGENTS.md" || path === "CLAUDE.md" || path === ".claude/CLAUDE.md" || path === ".claude/CLAUDE.local.md" || path === ".codex/AGENTS.local.md") {
369
+ return "markdown-managed";
370
+ }
369
371
  if (path === ".git/info/exclude") return "text-managed";
370
372
  if (path.endsWith(".json")) return "json";
371
373
  if (path.startsWith(".husky/")) return "shell";
@@ -376,7 +378,9 @@ export class RunUninstallUseCase {
376
378
  if (path === ".claude/settings.json") return "templates/.claude/settings.json";
377
379
  if (path === ".codex/hooks.json") return "templates/.codex/hooks.json";
378
380
  if (path === "CLAUDE.md") return "docs/templates/agent-context/CLAUDE.md.template.md";
381
+ if (path === ".claude/CLAUDE.md" || path === ".claude/CLAUDE.local.md") return "docs/templates/agent-context/CLAUDE.md.template.md";
379
382
  if (path === "AGENTS.md") return "docs/templates/agent-context/AGENTS.md.template.md";
383
+ if (path === ".codex/AGENTS.local.md") return "docs/templates/agent-context/AGENTS.md.template.md";
380
384
  throw new Error(`No template for ${path}`);
381
385
  }
382
386
 
@@ -2,9 +2,12 @@
2
2
  // @layer presentation
3
3
  // @work-item-id WI-145
4
4
  // @work-item-id WI-148
5
+ // @work-item-id WI-215
5
6
 
7
+ import { ClaudeContextMissingCheck } from "./application/checks/claude-context-missing-check.js";
6
8
  import { ClaudeHookMissingCheck } from "./application/checks/claude-hook-missing-check.js";
7
9
  import { ClaudeSkillsSymlinkCheck } from "./application/checks/claude-skills-symlink-check.js";
10
+ import { CodexContextMissingCheck } from "./application/checks/codex-context-missing-check.js";
8
11
  import { CodexHookMissingCheck } from "./application/checks/codex-hook-missing-check.js";
9
12
  import { CodexSkillsSymlinkCheck } from "./application/checks/codex-skills-symlink-check.js";
10
13
  import { CiWorkflowMissingCheck } from "./application/checks/ci-workflow-missing-check.js";
@@ -42,7 +45,9 @@ export function createInstallationModule() {
42
45
  const hashCalculator = new NodeCryptoHashAdapter();
43
46
  const checks = [
44
47
  new ClaudeHookMissingCheck(),
48
+ new ClaudeContextMissingCheck(),
45
49
  new CodexHookMissingCheck(),
50
+ new CodexContextMissingCheck(),
46
51
  new HuskyPreCommitMissingCheck(),
47
52
  new HuskyCommitMsgMissingCheck(),
48
53
  new HuskyPrePushMissingCheck(),
@@ -1,10 +1,13 @@
1
1
  // @unit installation
2
2
  // @layer domain
3
3
  // @work-item-id WI-145
4
+ // @work-item-id WI-215
4
5
 
5
6
  export const CHECK_IDS = [
6
7
  "claude-hook-missing",
8
+ "claude-context-missing",
7
9
  "codex-hook-missing",
10
+ "codex-context-missing",
8
11
  "husky-pre-commit-missing",
9
12
  "husky-commit-msg-missing",
10
13
  "husky-pre-push-missing",
@@ -1,6 +1,7 @@
1
1
  // @unit installation
2
2
  // @layer domain
3
3
  // @work-item-id WI-145
4
+ // @work-item-id WI-215
4
5
 
5
6
  import type { CheckId } from "./check-id.js";
6
7
  import { SuggestedSkill } from "./suggested-skill.js";
@@ -23,7 +24,9 @@ export class RepairTable {
23
24
  constructor() {
24
25
  this.table = new Map<CheckId, SuggestedSkill | null>([
25
26
  ["claude-hook-missing", CONFIG_DOCTOR],
27
+ ["claude-context-missing", CONFIG_DOCTOR],
26
28
  ["codex-hook-missing", CONFIG_DOCTOR],
29
+ ["codex-context-missing", CONFIG_DOCTOR],
27
30
  ["husky-pre-commit-missing", CONFIG_DOCTOR],
28
31
  ["husky-commit-msg-missing", CONFIG_DOCTOR],
29
32
  ["husky-pre-push-missing", null],