phasegate 0.335.0 → 0.341.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/README.ja.md +22 -9
- package/README.md +28 -17
- package/docs/ADR/038-config-state-operation-permission-policy.md +5 -2
- package/docs/ADR/041-non-excludable-agent-trust-roots.md +68 -0
- package/docs/folder_management_rules.md +10 -1
- package/docs/guide/antigravity-integration.md +26 -0
- package/docs/guide/cli-reference.md +27 -7
- package/docs/guide/codex-integration.md +44 -107
- package/docs/guide/configuration.md +28 -2
- package/docs/guide/grok-integration.md +26 -0
- package/docs/guide/hooks-integration.md +34 -5
- package/docs/guide/installation.md +4 -2
- package/docs/guide/setup-artifacts.md +4 -4
- package/docs/guide/skills-overview.md +4 -1
- package/package.json +2 -1
- package/scripts/harness/agent-integration/application/dto/handle-pre-tool-use-dto.ts +1 -1
- package/scripts/harness/agent-integration/application/dto/normalized-pre-tool-use-request.ts +32 -0
- package/scripts/harness/agent-integration/application/usecases/handle-post-tool-use-usecase.ts +4 -0
- package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +38 -3
- package/scripts/harness/agent-integration/application/usecases/handle-stop-usecase.ts +3 -9
- package/scripts/harness/agent-integration/domain/ports/full-mode-requirement-query-port.ts +3 -1
- package/scripts/harness/agent-integration/domain/ports/story-reflection-query-port.ts +2 -0
- package/scripts/harness/agent-integration/domain/services/apply-patch-write-target-extractor.ts +98 -0
- package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +6 -52
- package/scripts/harness/agent-integration/domain/services/hook-to-cli-translator.ts +8 -5
- package/scripts/harness/agent-integration/domain/value-objects/hook-translation-result.ts +2 -2
- package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +35 -18
- package/scripts/harness/agent-integration/domain/value-objects/story-reflection-query-result.ts +10 -2
- package/scripts/harness/agent-integration/domain/value-objects/write-target-scope.ts +12 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/child-process-cli-executor-adapter.ts +64 -13
- package/scripts/harness/agent-integration/infrastructure/adapters/file-system-story-reflection-query-adapter.ts +108 -2
- package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +8 -6
- package/scripts/harness/agent-integration/presentation/hook-skip-event-recorder.ts +3 -0
- package/scripts/harness/agent-integration/presentation/phasegate-status-context.ts +1 -1
- package/scripts/harness/agent-integration/presentation/post-tool-use-feedback.ts +39 -0
- package/scripts/harness/agent-integration/presentation/post-tool-use-hook.ts +21 -27
- package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +126 -70
- package/scripts/harness/agent-integration/presentation/pre-tool-use-payload-normalizer.ts +228 -0
- package/scripts/harness/agent-integration/presentation/pre-tool-use-response-renderer.ts +38 -0
- package/scripts/harness/agent-integration/presentation/stop-hook.ts +1 -3
- package/scripts/harness/biome-ast-engine/infrastructure/adapters/biome-cli-executor-adapter.ts +21 -1
- package/scripts/harness/biome-ast-engine/infrastructure/adapters/typescript-source-module-analyzer-adapter.ts +5 -0
- package/scripts/harness/ci-governance/domain/value-objects/integrity-target.ts +2 -0
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +15 -0
- package/scripts/harness/config-foundation/infrastructure/validators/ajv-config-schema-validator.ts +6 -3
- package/scripts/harness/harness-api/domain/ports/biome-lint-port.ts +2 -1
- package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +12 -1
- package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +10 -2
- package/scripts/harness/installation/application/bundled-skill-selection.ts +28 -2
- package/scripts/harness/installation/application/checks/antigravity-hook-missing-check.ts +79 -0
- package/scripts/harness/installation/application/checks/check-utils.ts +2 -2
- package/scripts/harness/installation/application/checks/codex-hook-missing-check.ts +70 -6
- package/scripts/harness/installation/application/checks/grok-hook-missing-check.ts +79 -0
- package/scripts/harness/installation/application/checks/husky-runtime-inactive-check.ts +33 -0
- package/scripts/harness/installation/application/named-hook-json.ts +17 -0
- package/scripts/harness/installation/application/operator-notice.ts +42 -0
- package/scripts/harness/installation/application/usecases/run-doctor-diagnostics.ts +18 -7
- package/scripts/harness/installation/application/usecases/run-install.ts +157 -57
- package/scripts/harness/installation/application/usecases/run-reconcile.ts +148 -31
- package/scripts/harness/installation/application/usecases/run-uninstall.ts +149 -25
- package/scripts/harness/installation/composition-root.ts +10 -0
- package/scripts/harness/installation/domain/agent-target.ts +39 -0
- package/scripts/harness/installation/domain/check-id.ts +5 -0
- package/scripts/harness/installation/domain/husky-runtime-state.ts +34 -0
- package/scripts/harness/installation/domain/ports/git-hooks-runtime-probe.ts +9 -0
- package/scripts/harness/installation/domain/repair-table.ts +5 -0
- package/scripts/harness/installation/infrastructure/adapters/git-hooks-runtime-probe-adapter.ts +62 -0
- package/scripts/harness/installation/presentation/cli/install-handler.ts +25 -5
- package/scripts/harness/installation/presentation/cli/reconcile-handler.ts +7 -1
- package/scripts/harness/installation/presentation/formatters/diagnostic-report-formatter.ts +27 -0
- package/scripts/harness/main.ts +227 -128
- package/scripts/harness/phase-dependency-model/domain/services/story-reflection-checker.ts +48 -5
- package/scripts/harness/phase-dependency-model/domain/services/work-item-reflection-scope-resolver.ts +64 -0
- package/scripts/harness/phase-dependency-model/domain/values/story-reflection-mapping.ts +8 -2
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +6 -1
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-work-item-dependency-catalog.ts +73 -0
- package/scripts/harness/quick-mode/application/dto/change-category-classification-contract.ts +5 -1
- package/scripts/harness/quick-mode/application/dto/quick-mode-eligibility-contract.ts +1 -1
- package/scripts/harness/quick-mode/application/ports/change-risk-advisory-port.ts +16 -0
- package/scripts/harness/quick-mode/application/usecases/classify-change-category-usecase.ts +3 -1
- package/scripts/harness/quick-mode/composition-root.ts +5 -1
- package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +13 -2
- package/scripts/harness/quick-mode/domain/types/rejection-rule.ts +1 -1
- package/scripts/harness/quick-mode/infrastructure/adapters/snapshot-risk-advisory-adapter.ts +118 -0
- package/scripts/harness/quick-mode/presentation/formatters/change-category-formatter.ts +7 -0
- package/scripts/harness/quick-mode/presentation/handlers/check-change-category-handler.ts +12 -1
- package/scripts/harness/regression-suite/infrastructure/adapters/vitest-test-runner-adapter.ts +13 -3
- package/scripts/harness/setup/skill-deployer.ts +3 -45
- package/scripts/harness/skill-quality/application/dto/run-plan-checker-loop-output.ts +1 -0
- package/scripts/harness/skill-quality/application/usecases/apply-cascade-update-usecase.ts +9 -4
- package/scripts/harness/skill-quality/application/usecases/execute-tdd-cycle-usecase.ts +2 -1
- package/scripts/harness/skill-quality/application/usecases/run-plan-checker-loop-usecase.ts +8 -0
- package/scripts/harness/skill-quality/composition-root.ts +14 -7
- package/scripts/harness/skill-quality/domain/ports/plan-check-executor-port.ts +2 -0
- package/scripts/harness/skill-quality/domain/value-objects/cascade-update-target.ts +15 -1
- package/scripts/harness/skill-quality/infrastructure/adapters/l1-biome-validator-adapter.ts +9 -1
- package/scripts/harness/skill-quality/infrastructure/adapters/l2-validator-system-adapter.ts +18 -2
- package/scripts/harness/skill-quality/presentation/handlers/apply-cascade-update-handler.ts +8 -2
- package/scripts/harness/skill-quality/presentation/handlers/execute-tdd-cycle-handler.ts +4 -3
- package/scripts/harness/skill-quality/presentation/handlers/run-plan-checker-loop-handler.ts +11 -4
- package/scripts/harness/traceability-model/infrastructure/parsers/work-item-frontmatter-parser.ts +41 -0
- package/skills/README.md +7 -0
- package/skills/cascade-updater/SKILL.md +37 -109
- package/skills/cascade-updater/references//345/261/244/345/210/245/345/210/244/345/256/232/343/202/254/343/202/244/343/203/211.md +5 -3
- package/skills/release-publisher/SKILL.md +3 -1
- package/skills/skill-creator/SKILL.md +8 -9
- package/templates/.agents/hooks.json +16 -0
- package/templates/.claude/scripts/analyze-errors-hook.sh +2 -2
- package/templates/.claude/settings.json +5 -3
- package/templates/.codex/hooks.json +2 -2
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
// @work-item-id WI-215
|
|
7
7
|
// @work-item-id WI-330
|
|
8
8
|
// @work-item-id WI-343
|
|
9
|
+
// @work-item-id WI-385
|
|
10
|
+
// @work-item-id WI-390
|
|
9
11
|
|
|
12
|
+
import type { AgentTarget } from "../../domain/agent-target.js";
|
|
10
13
|
import type { CheckId } from "../../domain/check-id.js";
|
|
11
14
|
import type { ConfigStatus } from "../../domain/config-status.js";
|
|
12
15
|
import type { DiagnosticFinding } from "../../domain/diagnostic-finding.js";
|
|
@@ -16,7 +19,7 @@ import type { ConfigStatusProbePort } from "../ports/config-status-probe-port.js
|
|
|
16
19
|
import type { FileInspectorPort } from "../ports/file-inspector-port.js";
|
|
17
20
|
import type { ManifestRepositoryPort } from "../ports/manifest-repository-port.js";
|
|
18
21
|
|
|
19
|
-
export type DoctorAgentScope =
|
|
22
|
+
export type DoctorAgentScope = AgentTarget;
|
|
20
23
|
|
|
21
24
|
export interface ScopedOutDiagnosticFinding {
|
|
22
25
|
readonly finding: DiagnosticFinding;
|
|
@@ -40,10 +43,13 @@ export interface RunDoctorDiagnosticsOutput {
|
|
|
40
43
|
|
|
41
44
|
const CLAUDE_ONLY_CHECKS = new Set<CheckId>(["claude-hook-missing", "claude-context-missing", "claude-skills-symlink"]);
|
|
42
45
|
const CODEX_ONLY_CHECKS = new Set<CheckId>(["codex-hook-missing", "codex-context-missing", "codex-skills-symlink"]);
|
|
46
|
+
const CLAUDE_COMPATIBLE_CHECKS = new Set<CheckId>(["grok-hook-missing"]);
|
|
47
|
+
const ANTIGRAVITY_ONLY_CHECKS = new Set<CheckId>(["antigravity-hook-missing"]);
|
|
43
48
|
const PERSONAL_SCOPED_OUT_CHECKS = new Set<CheckId>([
|
|
44
49
|
"husky-pre-commit-missing",
|
|
45
50
|
"husky-commit-msg-missing",
|
|
46
51
|
"husky-pre-push-missing",
|
|
52
|
+
"husky-runtime-inactive",
|
|
47
53
|
"ci-workflow-missing",
|
|
48
54
|
"package-json-devdep-missing",
|
|
49
55
|
]);
|
|
@@ -65,9 +71,7 @@ export class RunDoctorDiagnosticsUseCase {
|
|
|
65
71
|
: "project";
|
|
66
72
|
const configStatus = (await this.configStatusProbe.probe(input.projectRoot)).status;
|
|
67
73
|
const rawFindings = (
|
|
68
|
-
await Promise.all(
|
|
69
|
-
this.checks.map((check) => check.run(input.projectRoot, this.inspector, { installationMode })),
|
|
70
|
-
)
|
|
74
|
+
await Promise.all(this.checks.map((check) => check.run(input.projectRoot, this.inspector, { installationMode })))
|
|
71
75
|
).filter((finding) => finding !== null);
|
|
72
76
|
const { findings, scopedOutFindings } = this.applyPersonalScope(
|
|
73
77
|
this.applyAgentScope(rawFindings, agent),
|
|
@@ -91,12 +95,10 @@ export class RunDoctorDiagnosticsUseCase {
|
|
|
91
95
|
readonly findings: readonly DiagnosticFinding[];
|
|
92
96
|
readonly scopedOutFindings: readonly ScopedOutDiagnosticFinding[];
|
|
93
97
|
} {
|
|
94
|
-
if (agent === "both") return { findings, scopedOutFindings: [] };
|
|
95
|
-
const scopedOutChecks = agent === "claude" ? CODEX_ONLY_CHECKS : CLAUDE_ONLY_CHECKS;
|
|
96
98
|
const applicable: DiagnosticFinding[] = [];
|
|
97
99
|
const scopedOut: ScopedOutDiagnosticFinding[] = [];
|
|
98
100
|
for (const finding of findings) {
|
|
99
|
-
if (
|
|
101
|
+
if (!this.isApplicableAgentCheck(finding.checkId, agent)) {
|
|
100
102
|
scopedOut.push({
|
|
101
103
|
finding,
|
|
102
104
|
scopeReason: `${finding.checkId} belongs to an unselected agent for doctor --agent ${agent}.`,
|
|
@@ -108,6 +110,15 @@ export class RunDoctorDiagnosticsUseCase {
|
|
|
108
110
|
return { findings: applicable, scopedOutFindings: scopedOut };
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
private isApplicableAgentCheck(checkId: CheckId, agent: DoctorAgentScope): boolean {
|
|
114
|
+
if (CLAUDE_ONLY_CHECKS.has(checkId)) return agent === "claude" || agent === "both" || agent === "all";
|
|
115
|
+
if (CODEX_ONLY_CHECKS.has(checkId)) return agent === "codex" || agent === "both" || agent === "all";
|
|
116
|
+
if (CLAUDE_COMPATIBLE_CHECKS.has(checkId))
|
|
117
|
+
return agent === "claude" || agent === "both" || agent === "grok" || agent === "all";
|
|
118
|
+
if (ANTIGRAVITY_ONLY_CHECKS.has(checkId)) return agent === "antigravity" || agent === "all";
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
111
122
|
private applyPersonalScope(
|
|
112
123
|
scoped: {
|
|
113
124
|
readonly findings: readonly DiagnosticFinding[];
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
// @work-item-id WI-315
|
|
20
20
|
// @work-item-id WI-326
|
|
21
21
|
// @work-item-id WI-331
|
|
22
|
+
// @work-item-id WI-385
|
|
23
|
+
// @work-item-id WI-387
|
|
22
24
|
|
|
23
25
|
import {
|
|
24
26
|
access,
|
|
@@ -34,11 +36,13 @@ import {
|
|
|
34
36
|
writeFile,
|
|
35
37
|
} from "node:fs/promises";
|
|
36
38
|
import { dirname, join } from "node:path";
|
|
39
|
+
import { type AgentTarget, resolveAgentTarget } from "../../domain/agent-target.js";
|
|
37
40
|
import { DeploymentEntry } from "../../domain/deployment-entry.js";
|
|
38
41
|
import { DeploymentManifest, type InstallationFlags } from "../../domain/deployment-manifest.js";
|
|
39
42
|
import type { ManagedBlockInput } from "../../domain/managed-block.js";
|
|
40
43
|
import type { RepairMode } from "../../domain/repair-mode.js";
|
|
41
44
|
import { getBundledSkillsForSet, type SkillSet } from "../bundled-skill-selection.js";
|
|
45
|
+
import { mergeNamedHookJson } from "../named-hook-json.js";
|
|
42
46
|
import type { HashCalculatorPort } from "../ports/hash-calculator-port.js";
|
|
43
47
|
import type { ManifestRepositoryPort } from "../ports/manifest-repository-port.js";
|
|
44
48
|
import type { ModelDelegationPort } from "../ports/model-delegation-port.js";
|
|
@@ -46,6 +50,7 @@ import type { ModelDelegationPort } from "../ports/model-delegation-port.js";
|
|
|
46
50
|
type InstallAction = "missing" | "will-merge" | "will-skip" | "will-overwrite";
|
|
47
51
|
type StrategyType =
|
|
48
52
|
| "json"
|
|
53
|
+
| "json-named"
|
|
49
54
|
| "shell"
|
|
50
55
|
| "yaml-add"
|
|
51
56
|
| "package-json"
|
|
@@ -81,9 +86,9 @@ export interface RunInstallInput {
|
|
|
81
86
|
readonly includeCodex?: boolean;
|
|
82
87
|
readonly includeHusky?: boolean;
|
|
83
88
|
readonly includeCi?: boolean;
|
|
84
|
-
readonly skillSet?:
|
|
89
|
+
readonly skillSet?: SkillSet;
|
|
85
90
|
readonly workflow?: "standard" | "strict";
|
|
86
|
-
readonly agent?:
|
|
91
|
+
readonly agent?: AgentTarget;
|
|
87
92
|
readonly personal?: boolean;
|
|
88
93
|
}
|
|
89
94
|
|
|
@@ -130,6 +135,7 @@ const PERSONAL_AGENT_RUNTIME_FILES = new Set([
|
|
|
130
135
|
".claude/settings.json",
|
|
131
136
|
"AGENTS.md",
|
|
132
137
|
".codex/hooks.json",
|
|
138
|
+
".agents/hooks.json",
|
|
133
139
|
]);
|
|
134
140
|
const SHARED_SKILLS_VERSION_PATH = "skills/.harness-version";
|
|
135
141
|
const PERSONAL_PRINCIPLES_DOCS = ".phasegate-local/docs/principles";
|
|
@@ -361,8 +367,8 @@ function mergeManagedMarkdown(existing: string | null, incoming: string): string
|
|
|
361
367
|
function renderAgentContextTemplate(
|
|
362
368
|
template: string,
|
|
363
369
|
options: {
|
|
364
|
-
readonly agent:
|
|
365
|
-
readonly skillSet:
|
|
370
|
+
readonly agent: AgentTarget;
|
|
371
|
+
readonly skillSet: SkillSet;
|
|
366
372
|
readonly workflow: "standard" | "strict";
|
|
367
373
|
readonly includeHusky: boolean;
|
|
368
374
|
readonly includeCi: boolean;
|
|
@@ -382,7 +388,7 @@ function renderAgentContextTemplate(
|
|
|
382
388
|
.replaceAll("{{PHASEGATE_HUSKY_STATE}}", options.includeHusky ? "managed" : "not managed by this setup run")
|
|
383
389
|
.replaceAll("{{PHASEGATE_CI_STATE}}", options.includeCi ? "managed" : "not managed by this setup run")
|
|
384
390
|
.replaceAll("{{PHASEGATE_COMMANDS}}", commands)
|
|
385
|
-
.replaceAll("{{PHASEGATE_SKILLS}}", options.skillSet === "
|
|
391
|
+
.replaceAll("{{PHASEGATE_SKILLS}}", options.skillSet === "all" ? "- `all bundled skills`" : `- \`${options.skillSet} skills\``)
|
|
386
392
|
.replaceAll("{{PHASEGATE_PRESETS}}", "- `minimal`\n- `standard`\n- `full`\n- `custom`")
|
|
387
393
|
.replaceAll("{{PHASEGATE_USER_SECTION}}", USER_SECTION_PLACEHOLDER);
|
|
388
394
|
}
|
|
@@ -409,20 +415,6 @@ function mergePackageJson(existing: Record<string, unknown>, version: string): R
|
|
|
409
415
|
};
|
|
410
416
|
}
|
|
411
417
|
|
|
412
|
-
function hasCustomJson(content: string | null): boolean {
|
|
413
|
-
if (content === null) return false;
|
|
414
|
-
try {
|
|
415
|
-
const parsed = JSON.parse(content) as unknown;
|
|
416
|
-
if (!isRecord(parsed)) return true;
|
|
417
|
-
const withoutEmptyHooks = { ...parsed };
|
|
418
|
-
if (isRecord(withoutEmptyHooks.hooks) && Object.keys(withoutEmptyHooks.hooks).length === 0)
|
|
419
|
-
delete withoutEmptyHooks.hooks;
|
|
420
|
-
return Object.keys(withoutEmptyHooks).length > 0;
|
|
421
|
-
} catch {
|
|
422
|
-
return true;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
418
|
function errorCode(error: unknown): string {
|
|
427
419
|
if (isRecord(error) && typeof error.code === "string") return error.code;
|
|
428
420
|
return "UNKNOWN";
|
|
@@ -471,17 +463,19 @@ export class RunInstallUseCase {
|
|
|
471
463
|
) {}
|
|
472
464
|
|
|
473
465
|
async execute(input: RunInstallInput): Promise<RunInstallResult> {
|
|
474
|
-
const
|
|
475
|
-
const
|
|
466
|
+
const legacyIncludeClaude = input.includeClaude ?? true;
|
|
467
|
+
const legacyIncludeCodex = input.includeCodex ?? true;
|
|
476
468
|
const includeHusky = input.includeHusky ?? true;
|
|
477
469
|
const includeCi = input.includeCi ?? true;
|
|
478
470
|
const skillSet = input.skillSet ?? "all";
|
|
479
471
|
const workflow = input.workflow ?? "standard";
|
|
480
|
-
const agent =
|
|
472
|
+
const agent =
|
|
473
|
+
input.agent ?? (legacyIncludeClaude && legacyIncludeCodex ? "both" : legacyIncludeCodex ? "codex" : "claude");
|
|
474
|
+
const selection = resolveAgentTarget(agent);
|
|
481
475
|
const personal = input.personal ?? false;
|
|
482
476
|
const targets = personal
|
|
483
|
-
? this.createPersonalTargets(
|
|
484
|
-
: this.createTargets({
|
|
477
|
+
? this.createPersonalTargets(selection)
|
|
478
|
+
: this.createTargets({ ...selection, includeHusky, includeCi });
|
|
485
479
|
// Persist the effective opt-in state so a later reconcile can honor the
|
|
486
480
|
// original install options. Personal installs never deploy Husky/CI
|
|
487
481
|
// targets, so their effective state is recorded as false regardless of
|
|
@@ -584,7 +578,7 @@ export class RunInstallUseCase {
|
|
|
584
578
|
const mode = before === null ? "created" : "merged";
|
|
585
579
|
const hash = this.hashCalculator.compute(next);
|
|
586
580
|
const manifestEntry = baseManifest.findEntry(target.path);
|
|
587
|
-
if (manifestEntry
|
|
581
|
+
if (manifestEntry?.hash.equals(hash)) {
|
|
588
582
|
manifest = manifest.addEntry(manifestEntry);
|
|
589
583
|
} else {
|
|
590
584
|
manifest = manifest.addEntry(
|
|
@@ -600,7 +594,11 @@ export class RunInstallUseCase {
|
|
|
600
594
|
}
|
|
601
595
|
|
|
602
596
|
const personalSkillTargets = input.personal
|
|
603
|
-
? [
|
|
597
|
+
? [
|
|
598
|
+
...(selection.claudeSkills ? [".claude/skills"] : []),
|
|
599
|
+
...(selection.codexSkills ? [".codex/skills"] : []),
|
|
600
|
+
...(selection.antigravitySkills ? [".agents/skills"] : []),
|
|
601
|
+
]
|
|
604
602
|
: [];
|
|
605
603
|
const selectedPersonalSkills = input.personal ? await listSelectedBundledSkills(input.harnessRoot, skillSet) : [];
|
|
606
604
|
for (const skillPath of personalSkillTargets) {
|
|
@@ -644,7 +642,7 @@ export class RunInstallUseCase {
|
|
|
644
642
|
}
|
|
645
643
|
}
|
|
646
644
|
|
|
647
|
-
if (!input.personal && (
|
|
645
|
+
if (!input.personal && (selection.claudeSkills || selection.codexSkills || selection.antigravitySkills)) {
|
|
648
646
|
const sharedSkills = await listSelectedBundledSkills(input.harnessRoot, skillSet);
|
|
649
647
|
const item = await this.planSharedSkillDirectory(input, sharedSkills, baseManifest);
|
|
650
648
|
plan.push(item);
|
|
@@ -673,8 +671,9 @@ export class RunInstallUseCase {
|
|
|
673
671
|
const linkSpecs = input.personal
|
|
674
672
|
? []
|
|
675
673
|
: [
|
|
676
|
-
...(
|
|
677
|
-
...(
|
|
674
|
+
...(selection.claudeSkills ? [{ path: ".claude/skills", target: "../skills" }] : []),
|
|
675
|
+
...(selection.codexSkills ? [{ path: ".codex/skills", target: "../skills" }] : []),
|
|
676
|
+
...(selection.antigravitySkills ? [{ path: ".agents/skills", target: "../skills" }] : []),
|
|
678
677
|
];
|
|
679
678
|
for (const linkSpec of linkSpecs) {
|
|
680
679
|
const item = await this.planSkillLink(input.projectRoot, linkSpec.path, linkSpec.target);
|
|
@@ -703,7 +702,7 @@ export class RunInstallUseCase {
|
|
|
703
702
|
});
|
|
704
703
|
}
|
|
705
704
|
|
|
706
|
-
if (input.personal &&
|
|
705
|
+
if (input.personal && selection.codexHook) {
|
|
707
706
|
plan.push({
|
|
708
707
|
path: "~/.codex/config.toml",
|
|
709
708
|
action: "will-skip",
|
|
@@ -770,8 +769,11 @@ export class RunInstallUseCase {
|
|
|
770
769
|
}
|
|
771
770
|
|
|
772
771
|
private createTargets(options: {
|
|
773
|
-
readonly
|
|
774
|
-
readonly
|
|
772
|
+
readonly claudeHook: boolean;
|
|
773
|
+
readonly claudeContext: boolean;
|
|
774
|
+
readonly codexHook: boolean;
|
|
775
|
+
readonly agentsContext: boolean;
|
|
776
|
+
readonly antigravityHook: boolean;
|
|
775
777
|
readonly includeHusky: boolean;
|
|
776
778
|
readonly includeCi: boolean;
|
|
777
779
|
}): readonly InstallTarget[] {
|
|
@@ -781,7 +783,7 @@ export class RunInstallUseCase {
|
|
|
781
783
|
strategy: "copy" as const,
|
|
782
784
|
templatePath: "docs/templates/project/phasegate.config.json",
|
|
783
785
|
},
|
|
784
|
-
...(options.
|
|
786
|
+
...(options.claudeHook
|
|
785
787
|
? [
|
|
786
788
|
{
|
|
787
789
|
path: ".claude/settings.json",
|
|
@@ -789,16 +791,60 @@ export class RunInstallUseCase {
|
|
|
789
791
|
templatePath: "templates/.claude/settings.json",
|
|
790
792
|
},
|
|
791
793
|
{
|
|
792
|
-
path: "
|
|
793
|
-
strategy: "
|
|
794
|
-
templatePath: "
|
|
795
|
-
|
|
794
|
+
path: ".claude/scripts/deny-check.sh",
|
|
795
|
+
strategy: "shell" as const,
|
|
796
|
+
templatePath: "templates/.claude/scripts/deny-check.sh",
|
|
797
|
+
executable: true,
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
path: ".claude/scripts/format-settings-hook.sh",
|
|
801
|
+
strategy: "shell" as const,
|
|
802
|
+
templatePath: "templates/.claude/scripts/format-settings-hook.sh",
|
|
803
|
+
executable: true,
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
path: ".claude/scripts/format-typescript-hook.sh",
|
|
807
|
+
strategy: "shell" as const,
|
|
808
|
+
templatePath: "templates/.claude/scripts/format-typescript-hook.sh",
|
|
809
|
+
executable: true,
|
|
796
810
|
},
|
|
811
|
+
{
|
|
812
|
+
path: ".claude/scripts/analyze-errors-hook.sh",
|
|
813
|
+
strategy: "shell" as const,
|
|
814
|
+
templatePath: "templates/.claude/scripts/analyze-errors-hook.sh",
|
|
815
|
+
executable: true,
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
path: ".claude/scripts/hook-config.json",
|
|
819
|
+
strategy: "copy" as const,
|
|
820
|
+
templatePath: "templates/.claude/scripts/hook-config.json",
|
|
821
|
+
},
|
|
822
|
+
...(options.claudeContext
|
|
823
|
+
? [
|
|
824
|
+
{
|
|
825
|
+
path: "CLAUDE.md",
|
|
826
|
+
strategy: "markdown-managed" as const,
|
|
827
|
+
templatePath: "docs/templates/agent-context/CLAUDE.md.template.md",
|
|
828
|
+
block: { start: MARKDOWN_BEGIN, end: MARKDOWN_END, content: "phasegate CLAUDE.md managed section" },
|
|
829
|
+
},
|
|
830
|
+
]
|
|
831
|
+
: []),
|
|
797
832
|
]
|
|
798
833
|
: []),
|
|
799
|
-
...(options.
|
|
834
|
+
...(options.codexHook
|
|
835
|
+
? [{ path: ".codex/hooks.json", strategy: "json" as const, templatePath: "templates/.codex/hooks.json" }]
|
|
836
|
+
: []),
|
|
837
|
+
...(options.antigravityHook
|
|
838
|
+
? [
|
|
839
|
+
{
|
|
840
|
+
path: ".agents/hooks.json",
|
|
841
|
+
strategy: "json-named" as const,
|
|
842
|
+
templatePath: "templates/.agents/hooks.json",
|
|
843
|
+
},
|
|
844
|
+
]
|
|
845
|
+
: []),
|
|
846
|
+
...(options.agentsContext
|
|
800
847
|
? [
|
|
801
|
-
{ path: ".codex/hooks.json", strategy: "json" as const, templatePath: "templates/.codex/hooks.json" },
|
|
802
848
|
{
|
|
803
849
|
path: "AGENTS.md",
|
|
804
850
|
strategy: "markdown-managed" as const,
|
|
@@ -846,8 +892,11 @@ export class RunInstallUseCase {
|
|
|
846
892
|
}
|
|
847
893
|
|
|
848
894
|
private createPersonalTargets(options: {
|
|
849
|
-
readonly
|
|
850
|
-
readonly
|
|
895
|
+
readonly claudeHook: boolean;
|
|
896
|
+
readonly claudeContext: boolean;
|
|
897
|
+
readonly codexHook: boolean;
|
|
898
|
+
readonly agentsContext: boolean;
|
|
899
|
+
readonly antigravityHook: boolean;
|
|
851
900
|
}): readonly InstallTarget[] {
|
|
852
901
|
return [
|
|
853
902
|
{
|
|
@@ -855,27 +904,60 @@ export class RunInstallUseCase {
|
|
|
855
904
|
strategy: "copy" as const,
|
|
856
905
|
templatePath: "docs/templates/personal/phasegate-local-config.json",
|
|
857
906
|
},
|
|
858
|
-
...(options.
|
|
907
|
+
...(options.claudeHook
|
|
859
908
|
? [
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
909
|
+
...(options.claudeContext
|
|
910
|
+
? [
|
|
911
|
+
{
|
|
912
|
+
path: ".claude/CLAUDE.md",
|
|
913
|
+
strategy: "markdown-managed" as const,
|
|
914
|
+
templatePath: "docs/templates/agent-context/CLAUDE.md.template.md",
|
|
915
|
+
block: {
|
|
916
|
+
start: MARKDOWN_BEGIN,
|
|
917
|
+
end: MARKDOWN_END,
|
|
918
|
+
content: "phasegate personal .claude/CLAUDE.md managed section",
|
|
919
|
+
},
|
|
920
|
+
personalManualIfUnmanaged: true,
|
|
921
|
+
},
|
|
922
|
+
]
|
|
923
|
+
: []),
|
|
871
924
|
{
|
|
872
925
|
path: ".claude/settings.json",
|
|
873
926
|
strategy: "copy" as const,
|
|
874
927
|
templatePath: "templates/.claude/settings.json",
|
|
875
928
|
},
|
|
929
|
+
{
|
|
930
|
+
path: ".claude/scripts/deny-check.sh",
|
|
931
|
+
strategy: "shell" as const,
|
|
932
|
+
templatePath: "templates/.claude/scripts/deny-check.sh",
|
|
933
|
+
executable: true,
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
path: ".claude/scripts/format-settings-hook.sh",
|
|
937
|
+
strategy: "shell" as const,
|
|
938
|
+
templatePath: "templates/.claude/scripts/format-settings-hook.sh",
|
|
939
|
+
executable: true,
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
path: ".claude/scripts/format-typescript-hook.sh",
|
|
943
|
+
strategy: "shell" as const,
|
|
944
|
+
templatePath: "templates/.claude/scripts/format-typescript-hook.sh",
|
|
945
|
+
executable: true,
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
path: ".claude/scripts/analyze-errors-hook.sh",
|
|
949
|
+
strategy: "shell" as const,
|
|
950
|
+
templatePath: "templates/.claude/scripts/analyze-errors-hook.sh",
|
|
951
|
+
executable: true,
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
path: ".claude/scripts/hook-config.json",
|
|
955
|
+
strategy: "copy" as const,
|
|
956
|
+
templatePath: "templates/.claude/scripts/hook-config.json",
|
|
957
|
+
},
|
|
876
958
|
]
|
|
877
959
|
: []),
|
|
878
|
-
...(options.
|
|
960
|
+
...(options.agentsContext
|
|
879
961
|
? [
|
|
880
962
|
{
|
|
881
963
|
path: "AGENTS.md",
|
|
@@ -888,6 +970,10 @@ export class RunInstallUseCase {
|
|
|
888
970
|
},
|
|
889
971
|
personalManualIfUnmanaged: true,
|
|
890
972
|
},
|
|
973
|
+
]
|
|
974
|
+
: []),
|
|
975
|
+
...(options.codexHook
|
|
976
|
+
? [
|
|
891
977
|
{
|
|
892
978
|
path: ".codex/hooks.json",
|
|
893
979
|
strategy: "copy" as const,
|
|
@@ -895,6 +981,15 @@ export class RunInstallUseCase {
|
|
|
895
981
|
},
|
|
896
982
|
]
|
|
897
983
|
: []),
|
|
984
|
+
...(options.antigravityHook
|
|
985
|
+
? [
|
|
986
|
+
{
|
|
987
|
+
path: ".agents/hooks.json",
|
|
988
|
+
strategy: "json-named" as const,
|
|
989
|
+
templatePath: "templates/.agents/hooks.json",
|
|
990
|
+
},
|
|
991
|
+
]
|
|
992
|
+
: []),
|
|
898
993
|
{
|
|
899
994
|
path: ".git/hooks/pre-commit",
|
|
900
995
|
strategy: "copy" as const,
|
|
@@ -943,6 +1038,7 @@ export class RunInstallUseCase {
|
|
|
943
1038
|
if (target.strategy === "copy-dir") return "mechanical";
|
|
944
1039
|
if (target.strategy === "symlink") return "mechanical";
|
|
945
1040
|
if (target.strategy === "json") return jsonRepairMode(before);
|
|
1041
|
+
if (target.strategy === "json-named") return jsonRepairMode(before);
|
|
946
1042
|
if (target.strategy === "markdown-managed") return "mechanical";
|
|
947
1043
|
return "mechanical";
|
|
948
1044
|
}
|
|
@@ -966,6 +1062,10 @@ export class RunInstallUseCase {
|
|
|
966
1062
|
}
|
|
967
1063
|
const existing = before === null ? {} : (JSON.parse(before) as unknown);
|
|
968
1064
|
const incoming = JSON.parse(template) as unknown;
|
|
1065
|
+
if (target.strategy === "json-named") {
|
|
1066
|
+
const merged = mergeNamedHookJson(isRecord(existing) ? existing : {}, isRecord(incoming) ? incoming : {});
|
|
1067
|
+
return `${JSON.stringify(merged, null, 2)}\n`;
|
|
1068
|
+
}
|
|
969
1069
|
const merged = mergeJsonObject(isRecord(existing) ? existing : {}, isRecord(incoming) ? incoming : {});
|
|
970
1070
|
return `${JSON.stringify(merged, null, 2)}\n`;
|
|
971
1071
|
}
|
|
@@ -1046,13 +1146,13 @@ export class RunInstallUseCase {
|
|
|
1046
1146
|
private personalSkillsVersionHashInput(
|
|
1047
1147
|
path: string,
|
|
1048
1148
|
version: string,
|
|
1049
|
-
skillSet:
|
|
1149
|
+
skillSet: SkillSet,
|
|
1050
1150
|
skills: readonly string[],
|
|
1051
1151
|
): string {
|
|
1052
1152
|
return `personal-skills-version:${path}:${version}:${skillSet}:${skills.join(",")}`;
|
|
1053
1153
|
}
|
|
1054
1154
|
|
|
1055
|
-
private personalSkillHashInput(path: string, skill: string, version: string, skillSet:
|
|
1155
|
+
private personalSkillHashInput(path: string, skill: string, version: string, skillSet: SkillSet): string {
|
|
1056
1156
|
return `personal-skill:${path}:${skill}:${version}:${skillSet}`;
|
|
1057
1157
|
}
|
|
1058
1158
|
|
|
@@ -1124,7 +1224,7 @@ export class RunInstallUseCase {
|
|
|
1124
1224
|
): DeploymentManifest {
|
|
1125
1225
|
const hash = this.hashCalculator.compute(input.contentForHash);
|
|
1126
1226
|
const existingEntry = baseManifest.findEntry(input.path);
|
|
1127
|
-
if (existingEntry
|
|
1227
|
+
if (existingEntry?.hash.equals(hash)) {
|
|
1128
1228
|
return manifest.addEntry(existingEntry);
|
|
1129
1229
|
}
|
|
1130
1230
|
return manifest.addEntry(
|