mewkit 1.10.2 → 1.11.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/README.md +56 -4
- package/dist/commands/budget.d.ts +13 -0
- package/dist/commands/budget.d.ts.map +1 -1
- package/dist/commands/budget.js +45 -0
- package/dist/commands/budget.js.map +1 -1
- package/dist/commands/doctor-checks.d.ts +1 -1
- package/dist/commands/doctor-checks.d.ts.map +1 -1
- package/dist/commands/doctor-checks.js +7 -1
- package/dist/commands/doctor-checks.js.map +1 -1
- package/dist/commands/doctor-hard-gates.d.ts +3 -0
- package/dist/commands/doctor-hard-gates.d.ts.map +1 -0
- package/dist/commands/doctor-hard-gates.js +138 -0
- package/dist/commands/doctor-hard-gates.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +77 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/evolve.d.ts +11 -0
- package/dist/commands/evolve.d.ts.map +1 -0
- package/dist/commands/evolve.js +63 -0
- package/dist/commands/evolve.js.map +1 -0
- package/dist/commands/health.d.ts +14 -0
- package/dist/commands/health.d.ts.map +1 -0
- package/dist/commands/health.js +193 -0
- package/dist/commands/health.js.map +1 -0
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +63 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/inventory.d.ts +17 -0
- package/dist/commands/inventory.d.ts.map +1 -0
- package/dist/commands/inventory.js +114 -0
- package/dist/commands/inventory.js.map +1 -0
- package/dist/commands/memory.d.ts +3 -0
- package/dist/commands/memory.d.ts.map +1 -1
- package/dist/commands/memory.js +67 -0
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/pack-helpers.d.ts +37 -0
- package/dist/commands/pack-helpers.d.ts.map +1 -0
- package/dist/commands/pack-helpers.js +105 -0
- package/dist/commands/pack-helpers.js.map +1 -0
- package/dist/commands/pack.d.ts +11 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +192 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/commands/policy.d.ts +8 -0
- package/dist/commands/policy.d.ts.map +1 -0
- package/dist/commands/policy.js +65 -0
- package/dist/commands/policy.js.map +1 -0
- package/dist/commands/portability.d.ts +7 -0
- package/dist/commands/portability.d.ts.map +1 -0
- package/dist/commands/portability.js +50 -0
- package/dist/commands/portability.js.map +1 -0
- package/dist/commands/reflect.d.ts +14 -0
- package/dist/commands/reflect.d.ts.map +1 -0
- package/dist/commands/reflect.js +135 -0
- package/dist/commands/reflect.js.map +1 -0
- package/dist/commands/simulate.d.ts +14 -0
- package/dist/commands/simulate.d.ts.map +1 -0
- package/dist/commands/simulate.js +86 -0
- package/dist/commands/simulate.js.map +1 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +32 -6
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/commands/validate.d.ts +28 -0
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +170 -94
- package/dist/commands/validate.js.map +1 -1
- package/dist/core/build-inventory.d.ts +71 -0
- package/dist/core/build-inventory.d.ts.map +1 -0
- package/dist/core/build-inventory.js +222 -0
- package/dist/core/build-inventory.js.map +1 -0
- package/dist/core/check-packs.d.ts +13 -0
- package/dist/core/check-packs.d.ts.map +1 -0
- package/dist/core/check-packs.js +131 -0
- package/dist/core/check-packs.js.map +1 -0
- package/dist/core/check-stale-index.d.ts +35 -0
- package/dist/core/check-stale-index.d.ts.map +1 -0
- package/dist/core/check-stale-index.js +132 -0
- package/dist/core/check-stale-index.js.map +1 -0
- package/dist/core/check-workflow-drift.d.ts +49 -0
- package/dist/core/check-workflow-drift.d.ts.map +1 -0
- package/dist/core/check-workflow-drift.js +226 -0
- package/dist/core/check-workflow-drift.js.map +1 -0
- package/dist/core/compute-checksums.d.ts +7 -0
- package/dist/core/compute-checksums.d.ts.map +1 -1
- package/dist/core/compute-checksums.js +12 -3
- package/dist/core/compute-checksums.js.map +1 -1
- package/dist/core/context-budget.d.ts +9 -0
- package/dist/core/context-budget.d.ts.map +1 -0
- package/dist/core/context-budget.js +46 -0
- package/dist/core/context-budget.js.map +1 -0
- package/dist/core/event-log.d.ts +44 -0
- package/dist/core/event-log.d.ts.map +1 -0
- package/dist/core/event-log.js +169 -0
- package/dist/core/event-log.js.map +1 -0
- package/dist/core/evolution/event-clusters.d.ts +4 -0
- package/dist/core/evolution/event-clusters.d.ts.map +1 -0
- package/dist/core/evolution/event-clusters.js +82 -0
- package/dist/core/evolution/event-clusters.js.map +1 -0
- package/dist/core/evolution/recommendation-renderer.d.ts +3 -0
- package/dist/core/evolution/recommendation-renderer.d.ts.map +1 -0
- package/dist/core/evolution/recommendation-renderer.js +24 -0
- package/dist/core/evolution/recommendation-renderer.js.map +1 -0
- package/dist/core/evolution/review-failure-clusters.d.ts +10 -0
- package/dist/core/evolution/review-failure-clusters.d.ts.map +1 -0
- package/dist/core/evolution/review-failure-clusters.js +30 -0
- package/dist/core/evolution/review-failure-clusters.js.map +1 -0
- package/dist/core/evolution/suggestions.d.ts +24 -0
- package/dist/core/evolution/suggestions.d.ts.map +1 -0
- package/dist/core/evolution/suggestions.js +11 -0
- package/dist/core/evolution/suggestions.js.map +1 -0
- package/dist/core/file-lock.d.ts +3 -0
- package/dist/core/file-lock.d.ts.map +1 -0
- package/dist/core/file-lock.js +84 -0
- package/dist/core/file-lock.js.map +1 -0
- package/dist/core/gate-policy.d.ts +20 -0
- package/dist/core/gate-policy.d.ts.map +1 -0
- package/dist/core/gate-policy.js +64 -0
- package/dist/core/gate-policy.js.map +1 -0
- package/dist/core/harness-scaffold.d.ts +29 -0
- package/dist/core/harness-scaffold.d.ts.map +1 -0
- package/dist/core/harness-scaffold.js +53 -0
- package/dist/core/harness-scaffold.js.map +1 -0
- package/dist/core/hook-runner.d.ts +69 -0
- package/dist/core/hook-runner.d.ts.map +1 -0
- package/dist/core/hook-runner.js +95 -0
- package/dist/core/hook-runner.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/install-metadata-backref.d.ts +14 -0
- package/dist/core/install-metadata-backref.d.ts.map +1 -0
- package/dist/core/install-metadata-backref.js +39 -0
- package/dist/core/install-metadata-backref.js.map +1 -0
- package/dist/core/install-metadata-writer.d.ts +4 -0
- package/dist/core/install-metadata-writer.d.ts.map +1 -0
- package/dist/core/install-metadata-writer.js +31 -0
- package/dist/core/install-metadata-writer.js.map +1 -0
- package/dist/core/install-metadata.d.ts +173 -0
- package/dist/core/install-metadata.d.ts.map +1 -0
- package/dist/core/install-metadata.js +191 -0
- package/dist/core/install-metadata.js.map +1 -0
- package/dist/core/is-hook-script.d.ts +2 -0
- package/dist/core/is-hook-script.d.ts.map +1 -0
- package/dist/core/is-hook-script.js +45 -0
- package/dist/core/is-hook-script.js.map +1 -0
- package/dist/core/pack-manifest.d.ts +48 -0
- package/dist/core/pack-manifest.d.ts.map +1 -0
- package/dist/core/pack-manifest.js +126 -0
- package/dist/core/pack-manifest.js.map +1 -0
- package/dist/core/pack-resolver.d.ts +18 -0
- package/dist/core/pack-resolver.d.ts.map +1 -0
- package/dist/core/pack-resolver.js +144 -0
- package/dist/core/pack-resolver.js.map +1 -0
- package/dist/core/portability-matrix.d.ts +17 -0
- package/dist/core/portability-matrix.d.ts.map +1 -0
- package/dist/core/portability-matrix.js +31 -0
- package/dist/core/portability-matrix.js.map +1 -0
- package/dist/core/release-metadata.d.ts +80 -0
- package/dist/core/release-metadata.d.ts.map +1 -0
- package/dist/core/release-metadata.js +70 -0
- package/dist/core/release-metadata.js.map +1 -0
- package/dist/core/simulation-runner.d.ts +93 -0
- package/dist/core/simulation-runner.d.ts.map +1 -0
- package/dist/core/simulation-runner.js +161 -0
- package/dist/core/simulation-runner.js.map +1 -0
- package/dist/core/smart-update.d.ts +32 -0
- package/dist/core/smart-update.d.ts.map +1 -1
- package/dist/core/smart-update.js +239 -47
- package/dist/core/smart-update.js.map +1 -1
- package/dist/core/usage/usage-analyzer.d.ts +18 -0
- package/dist/core/usage/usage-analyzer.d.ts.map +1 -0
- package/dist/core/usage/usage-analyzer.js +41 -0
- package/dist/core/usage/usage-analyzer.js.map +1 -0
- package/dist/core/validate-install.d.ts.map +1 -1
- package/dist/core/validate-install.js +3 -1
- package/dist/core/validate-install.js.map +1 -1
- package/dist/index.js +175 -4
- package/dist/index.js.map +1 -1
- package/dist/memory/archive.d.ts +9 -0
- package/dist/memory/archive.d.ts.map +1 -0
- package/dist/memory/archive.js +42 -0
- package/dist/memory/archive.js.map +1 -0
- package/dist/memory/compact.d.ts +9 -0
- package/dist/memory/compact.d.ts.map +1 -0
- package/dist/memory/compact.js +33 -0
- package/dist/memory/compact.js.map +1 -0
- package/dist/memory/conflicts.d.ts +8 -0
- package/dist/memory/conflicts.d.ts.map +1 -0
- package/dist/memory/conflicts.js +28 -0
- package/dist/memory/conflicts.js.map +1 -0
- package/dist/memory/promote.d.ts +8 -0
- package/dist/memory/promote.d.ts.map +1 -0
- package/dist/memory/promote.js +10 -0
- package/dist/memory/promote.js.map +1 -0
- package/dist/memory/store-utils.d.ts +14 -0
- package/dist/memory/store-utils.d.ts.map +1 -0
- package/dist/memory/store-utils.js +44 -0
- package/dist/memory/store-utils.js.map +1 -0
- package/dist/migrate/migrate-orchestrator.d.ts.map +1 -1
- package/dist/migrate/migrate-orchestrator.js +6 -2
- package/dist/migrate/migrate-orchestrator.js.map +1 -1
- package/dist/migrate/portable-installer.d.ts +3 -0
- package/dist/migrate/portable-installer.d.ts.map +1 -1
- package/dist/migrate/portable-installer.js +3 -0
- package/dist/migrate/portable-installer.js.map +1 -1
- package/dist/migrate/reconcile/portable-registry-lock.d.ts.map +1 -1
- package/dist/migrate/reconcile/portable-registry-lock.js +6 -78
- package/dist/migrate/reconcile/portable-registry-lock.js.map +1 -1
- package/dist/migrate/reconcile/portable-registry.d.ts +24 -6
- package/dist/migrate/reconcile/portable-registry.d.ts.map +1 -1
- package/dist/migrate/reconcile/portable-registry.js +7 -1
- package/dist/migrate/reconcile/portable-registry.js.map +1 -1
- package/dist/migrate/skill-directory-installer.d.ts +2 -0
- package/dist/migrate/skill-directory-installer.d.ts.map +1 -1
- package/dist/migrate/skill-directory-installer.js +3 -0
- package/dist/migrate/skill-directory-installer.js.map +1 -1
- package/dist/migrate/types.d.ts +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { clusterReviewFailures } from "./review-failure-clusters.js";
|
|
2
|
+
import { makeSuggestion } from "./suggestions.js";
|
|
3
|
+
function str(value, fallback = "unknown") {
|
|
4
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
|
|
5
|
+
}
|
|
6
|
+
function bump(map, key) {
|
|
7
|
+
map.set(key, (map.get(key) ?? 0) + 1);
|
|
8
|
+
}
|
|
9
|
+
function repeated(map, min = 2) {
|
|
10
|
+
return [...map.entries()].filter(([, count]) => count >= min).sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
11
|
+
}
|
|
12
|
+
export function suggestionsFromEvents(events, malformed) {
|
|
13
|
+
const suggestions = [];
|
|
14
|
+
const hookFailures = new Map();
|
|
15
|
+
const gateBlocks = new Map();
|
|
16
|
+
for (const event of events) {
|
|
17
|
+
if (event.event === "hook.failed") {
|
|
18
|
+
bump(hookFailures, `${str(event.data.hook)} exit ${str(event.data.exit_code, "?")}`);
|
|
19
|
+
}
|
|
20
|
+
if (event.event === "gate.blocked") {
|
|
21
|
+
bump(gateBlocks, `${str(event.data.gate)}: ${str(event.data.reason, "")}`);
|
|
22
|
+
}
|
|
23
|
+
if (event.event === "dead_weight_audit_needed") {
|
|
24
|
+
suggestions.push(makeSuggestion({
|
|
25
|
+
kind: "pack-pruning",
|
|
26
|
+
title: "Run a dead-weight audit",
|
|
27
|
+
evidence: [{ source: "event-log", detail: "dead_weight_audit_needed event observed", count: 1 }],
|
|
28
|
+
confidence: 0.72,
|
|
29
|
+
impact: "medium",
|
|
30
|
+
risk: "low",
|
|
31
|
+
proposedAction: "Run `mewkit evolve report` and review pack/skill pruning candidates.",
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const [label, count] of repeated(hookFailures)) {
|
|
36
|
+
suggestions.push(makeSuggestion({
|
|
37
|
+
kind: "hook-test",
|
|
38
|
+
title: `Add regression coverage for ${label}`,
|
|
39
|
+
evidence: [{ source: "event-log", detail: label, count }],
|
|
40
|
+
confidence: Math.min(0.95, 0.55 + count * 0.1),
|
|
41
|
+
impact: "high",
|
|
42
|
+
risk: "low",
|
|
43
|
+
proposedAction: "Add or extend a `mewkit simulate` scenario and a configured-hook regression test.",
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
for (const [label, count] of repeated(gateBlocks, 3)) {
|
|
47
|
+
suggestions.push(makeSuggestion({
|
|
48
|
+
kind: "policy",
|
|
49
|
+
title: `Review gate policy for repeated ${label}`,
|
|
50
|
+
evidence: [{ source: "event-log", detail: label, count }],
|
|
51
|
+
confidence: Math.min(0.9, 0.45 + count * 0.08),
|
|
52
|
+
impact: "medium",
|
|
53
|
+
risk: "medium",
|
|
54
|
+
proposedAction: "Run `mewkit policy explain`; consider a clearer policy profile or docs clarification.",
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
for (const cluster of clusterReviewFailures(events)) {
|
|
58
|
+
const diversity = cluster.tasks.length > 1 ? " across multiple tasks" : "";
|
|
59
|
+
suggestions.push(makeSuggestion({
|
|
60
|
+
kind: "review-promotion",
|
|
61
|
+
title: `Promote repeated review failure: ${cluster.key}`,
|
|
62
|
+
evidence: [{ source: "verdict_written", detail: `${cluster.count} failure(s)${diversity}`, count: cluster.count }],
|
|
63
|
+
confidence: Math.min(0.92, 0.5 + cluster.count * 0.12 + (cluster.tasks.length > 1 ? 0.1 : 0)),
|
|
64
|
+
impact: "high",
|
|
65
|
+
risk: "medium",
|
|
66
|
+
proposedAction: "Generate a candidate checklist/rule via `mewkit memory promote --review-failures`.",
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
if (malformed > 0) {
|
|
70
|
+
suggestions.push(makeSuggestion({
|
|
71
|
+
kind: "trace-hardening",
|
|
72
|
+
title: "Harden trace writer or reader against malformed rows",
|
|
73
|
+
evidence: [{ source: "event-log", detail: "malformed or oversized rows skipped", count: malformed }],
|
|
74
|
+
confidence: Math.min(0.9, 0.5 + malformed * 0.05),
|
|
75
|
+
impact: "medium",
|
|
76
|
+
risk: "low",
|
|
77
|
+
proposedAction: "Add an emitter-output JSON validity test and inspect trace append paths.",
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
return suggestions;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=event-clusters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-clusters.js","sourceRoot":"","sources":["../../../src/core/evolution/event-clusters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommendation-renderer.d.ts","sourceRoot":"","sources":["../../../src/core/evolution/recommendation-renderer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAM5D,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,mBAAmB,EAAE,GAAG,IAAI,CAkB1E"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
function pct(n) {
|
|
3
|
+
return `${Math.round(n * 100)}%`;
|
|
4
|
+
}
|
|
5
|
+
export function renderSuggestions(suggestions) {
|
|
6
|
+
console.log(pc.bold(pc.cyan("MeowKit evolve — proposal-only suggestions")));
|
|
7
|
+
console.log(pc.dim("No rules, skills, hooks, packs, or policies were changed."));
|
|
8
|
+
console.log();
|
|
9
|
+
if (suggestions.length === 0) {
|
|
10
|
+
console.log(pc.yellow("No suggestions met the evidence threshold."));
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
for (const [i, suggestion] of suggestions.entries()) {
|
|
14
|
+
console.log(`${pc.bold(`${i + 1}. ${suggestion.title}`)} ${pc.dim(`[${suggestion.kind}]`)}`);
|
|
15
|
+
console.log(` confidence ${pc.cyan(pct(suggestion.confidence))} · impact ${suggestion.impact} · risk ${suggestion.risk}`);
|
|
16
|
+
for (const evidence of suggestion.evidence) {
|
|
17
|
+
const count = evidence.count === undefined ? "" : ` (${evidence.count})`;
|
|
18
|
+
console.log(` - ${evidence.source}: ${evidence.detail}${count}`);
|
|
19
|
+
}
|
|
20
|
+
console.log(` proposed: ${suggestion.proposedAction}`);
|
|
21
|
+
console.log();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=recommendation-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommendation-renderer.js","sourceRoot":"","sources":["../../../src/core/evolution/recommendation-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAG5B,SAAS,GAAG,CAAC,CAAS;IACrB,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,WAAkC;IACnE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC,CAAC,CAAC;IAC5E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC,CAAC;IACjF,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,4CAA4C,CAAC,CAAC,CAAC;QACrE,OAAO;IACR,CAAC;IACD,KAAK,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC;QAC7F,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,aAAa,UAAU,CAAC,MAAM,WAAW,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5H,KAAK,MAAM,QAAQ,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,QAAQ,CAAC,KAAK,GAAG,CAAC;YACzE,OAAO,CAAC,GAAG,CAAC,QAAQ,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,gBAAgB,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,EAAE,CAAC;IACf,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { EventRecord } from "../event-log.js";
|
|
2
|
+
export interface ReviewFailureCluster {
|
|
3
|
+
key: string;
|
|
4
|
+
count: number;
|
|
5
|
+
tasks: string[];
|
|
6
|
+
slug: string;
|
|
7
|
+
dimension?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function clusterReviewFailures(events: EventRecord[], minCount?: number): ReviewFailureCluster[];
|
|
10
|
+
//# sourceMappingURL=review-failure-clusters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-failure-clusters.d.ts","sourceRoot":"","sources":["../../../src/core/evolution/review-failure-clusters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,WAAW,oBAAoB;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAgBD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,QAAQ,SAAI,GAAG,oBAAoB,EAAE,CAajG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
function str(value, fallback = "unknown") {
|
|
2
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
|
|
3
|
+
}
|
|
4
|
+
function clusterKey(event) {
|
|
5
|
+
const slug = str(event.data.slug);
|
|
6
|
+
const dimension = str(event.data.dimension ?? event.data.rubric, "");
|
|
7
|
+
return {
|
|
8
|
+
key: dimension ? `${slug}:${dimension}` : slug,
|
|
9
|
+
slug,
|
|
10
|
+
dimension: dimension || undefined,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function clusterReviewFailures(events, minCount = 2) {
|
|
14
|
+
const clusters = new Map();
|
|
15
|
+
for (const event of events) {
|
|
16
|
+
if (event.event !== "verdict_written")
|
|
17
|
+
continue;
|
|
18
|
+
if (str(event.data.overall).toUpperCase() !== "FAIL")
|
|
19
|
+
continue;
|
|
20
|
+
const { key, slug, dimension } = clusterKey(event);
|
|
21
|
+
const existing = clusters.get(key) ?? { key, count: 0, tasks: [], slug, dimension };
|
|
22
|
+
existing.count++;
|
|
23
|
+
const task = str(event.data.task, "");
|
|
24
|
+
if (task && !existing.tasks.includes(task))
|
|
25
|
+
existing.tasks.push(task);
|
|
26
|
+
clusters.set(key, existing);
|
|
27
|
+
}
|
|
28
|
+
return [...clusters.values()].filter((c) => c.count >= minCount).sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=review-failure-clusters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-failure-clusters.js","sourceRoot":"","sources":["../../../src/core/evolution/review-failure-clusters.ts"],"names":[],"mappings":"AAUA,SAAS,GAAG,CAAC,KAAc,EAAE,QAAQ,GAAG,SAAS;IAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACvF,CAAC;AAED,SAAS,UAAU,CAAC,KAAkB;IACrC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACrE,OAAO;QACN,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI;QAC9C,IAAI;QACJ,SAAS,EAAE,SAAS,IAAI,SAAS;KACjC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAqB,EAAE,QAAQ,GAAG,CAAC;IACxE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgC,CAAC;IACzD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,KAAK,KAAK,iBAAiB;YAAE,SAAS;QAChD,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM;YAAE,SAAS;QAC/D,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QACpF,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtC,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtE,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAClI,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type SuggestionKind = "hook-test" | "review-promotion" | "policy" | "trace-hardening" | "regression-scenario" | "pack-pruning" | "memory-conflict" | "memory-archive";
|
|
2
|
+
export type EvidenceStatus = "available" | "insufficient" | "na";
|
|
3
|
+
export interface SuggestionEvidence {
|
|
4
|
+
source: string;
|
|
5
|
+
detail: string;
|
|
6
|
+
count?: number;
|
|
7
|
+
status?: EvidenceStatus;
|
|
8
|
+
}
|
|
9
|
+
export interface EvolutionSuggestion {
|
|
10
|
+
kind: SuggestionKind;
|
|
11
|
+
title: string;
|
|
12
|
+
evidence: SuggestionEvidence[];
|
|
13
|
+
confidence: number;
|
|
14
|
+
impact: "low" | "medium" | "high";
|
|
15
|
+
risk: "low" | "medium" | "high";
|
|
16
|
+
proposedAction: string;
|
|
17
|
+
neverAutoApply: true;
|
|
18
|
+
}
|
|
19
|
+
export declare function makeSuggestion(args: Omit<EvolutionSuggestion, "neverAutoApply">): EvolutionSuggestion;
|
|
20
|
+
export declare function filterSuggestions(suggestions: EvolutionSuggestion[], opts: {
|
|
21
|
+
minConfidence?: number;
|
|
22
|
+
kind?: string;
|
|
23
|
+
}): EvolutionSuggestion[];
|
|
24
|
+
//# sourceMappingURL=suggestions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggestions.d.ts","sourceRoot":"","sources":["../../../src/core/evolution/suggestions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GACvB,WAAW,GACX,kBAAkB,GAClB,QAAQ,GACR,iBAAiB,GACjB,qBAAqB,GACrB,cAAc,GACd,iBAAiB,GACjB,gBAAgB,CAAC;AAEpB,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,cAAc,GAAG,IAAI,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClC,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,IAAI,CAAC;CACrB;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,GAAG,mBAAmB,CAErG;AAED,wBAAgB,iBAAiB,CAChC,WAAW,EAAE,mBAAmB,EAAE,EAClC,IAAI,EAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,mBAAmB,EAAE,CAMvB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function makeSuggestion(args) {
|
|
2
|
+
return { ...args, neverAutoApply: true };
|
|
3
|
+
}
|
|
4
|
+
export function filterSuggestions(suggestions, opts) {
|
|
5
|
+
const min = opts.minConfidence ?? 0;
|
|
6
|
+
return suggestions
|
|
7
|
+
.filter((s) => s.confidence >= min)
|
|
8
|
+
.filter((s) => !opts.kind || s.kind === opts.kind)
|
|
9
|
+
.sort((a, b) => b.confidence - a.confidence || a.kind.localeCompare(b.kind) || a.title.localeCompare(b.title));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=suggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggestions.js","sourceRoot":"","sources":["../../../src/core/evolution/suggestions.ts"],"names":[],"mappings":"AA8BA,MAAM,UAAU,cAAc,CAAC,IAAiD;IAC/E,OAAO,EAAE,GAAG,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,iBAAiB,CAChC,WAAkC,EAClC,IAA+C;IAE/C,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC;IACpC,OAAO,WAAW;SAChB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,GAAG,CAAC;SAClC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC;SACjD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACjH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.d.ts","sourceRoot":"","sources":["../../src/core/file-lock.ts"],"names":[],"mappings":"AA+EA,qEAAqE;AACrE,wBAAsB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO/F"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// PID-based advisory file lock generalized over an arbitrary lock path.
|
|
2
|
+
// Acquires by exclusively creating the lock file (`wx`) and writing the
|
|
3
|
+
// owner PID + timestamp; reclaims a lock whose owner PID is dead or whose
|
|
4
|
+
// timestamp is older than STALE_LOCK_MS; serializes concurrent callers via a
|
|
5
|
+
// bounded spin. Replaces a per-call proper-lockfile dependency.
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { mkdir, open, readFile, unlink } from "node:fs/promises";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
const STALE_LOCK_MS = 60 * 1000;
|
|
10
|
+
const ACQUIRE_TIMEOUT_MS = 10 * 1000;
|
|
11
|
+
const RETRY_INTERVAL_MS = 100;
|
|
12
|
+
function isErrnoCode(error, code) {
|
|
13
|
+
return (typeof error === "object" && error !== null && "code" in error && error.code === code);
|
|
14
|
+
}
|
|
15
|
+
async function acquireFileLock(lockPath) {
|
|
16
|
+
const lockDir = dirname(lockPath);
|
|
17
|
+
if (!existsSync(lockDir))
|
|
18
|
+
await mkdir(lockDir, { recursive: true });
|
|
19
|
+
const myPid = process.pid;
|
|
20
|
+
const startTime = Date.now();
|
|
21
|
+
while (true) {
|
|
22
|
+
try {
|
|
23
|
+
const handle = await open(lockPath, "wx");
|
|
24
|
+
await handle.writeFile(`${myPid}\n${Date.now()}\n`, "utf-8");
|
|
25
|
+
await handle.close();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
if (!isErrnoCode(err, "EEXIST"))
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const content = await readFile(lockPath, "utf-8");
|
|
34
|
+
const [pidStr, tsStr] = content.split("\n");
|
|
35
|
+
const pid = Number.parseInt(pidStr, 10);
|
|
36
|
+
const ts = Number.parseInt(tsStr, 10);
|
|
37
|
+
let alive = false;
|
|
38
|
+
try {
|
|
39
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
40
|
+
process.kill(pid, 0);
|
|
41
|
+
alive = true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
alive = false;
|
|
46
|
+
}
|
|
47
|
+
if (!alive || (Number.isFinite(ts) && Date.now() - ts > STALE_LOCK_MS)) {
|
|
48
|
+
try {
|
|
49
|
+
await unlink(lockPath);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
/* lost the reclaim race — another waiter cleaned it */
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Lock disappeared or unreadable between create and read — retry.
|
|
59
|
+
}
|
|
60
|
+
if (Date.now() - startTime > ACQUIRE_TIMEOUT_MS) {
|
|
61
|
+
throw new Error(`Could not acquire lock ${lockPath} within ${ACQUIRE_TIMEOUT_MS / 1000}s`);
|
|
62
|
+
}
|
|
63
|
+
await new Promise((r) => setTimeout(r, RETRY_INTERVAL_MS));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function releaseFileLock(lockPath) {
|
|
67
|
+
try {
|
|
68
|
+
await unlink(lockPath);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Best-effort: the lock may already be gone (reclaimed as stale).
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Run `operation` while holding an exclusive lock at `lockPath`. */
|
|
75
|
+
export async function withFileLock(lockPath, operation) {
|
|
76
|
+
await acquireFileLock(lockPath);
|
|
77
|
+
try {
|
|
78
|
+
return await operation();
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
await releaseFileLock(lockPath);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=file-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-lock.js","sourceRoot":"","sources":["../../src/core/file-lock.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,wEAAwE;AACxE,0EAA0E;AAC1E,6EAA6E;AAC7E,gEAAgE;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC;AAChC,MAAM,kBAAkB,GAAG,EAAE,GAAG,IAAI,CAAC;AACrC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B,SAAS,WAAW,CAAC,KAAc,EAAE,IAAY;IAChD,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAK,KAA+B,CAAC,IAAI,KAAK,IAAI,CAChH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC;IAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,OAAO,IAAI,EAAE,CAAC;QACb,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC1C,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,KAAK,KAAK,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YAC7D,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,OAAO;QACR,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC;gBAAE,MAAM,GAAG,CAAC;QAC5C,CAAC;QAED,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACxC,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAEtC,IAAI,KAAK,GAAG,KAAK,CAAC;YAClB,IAAI,CAAC;gBACJ,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;oBACrC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;oBACrB,KAAK,GAAG,IAAI,CAAC;gBACd,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,KAAK,GAAG,KAAK,CAAC;YACf,CAAC;YAED,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC,EAAE,CAAC;gBACxE,IAAI,CAAC;oBACJ,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACxB,CAAC;gBAAC,MAAM,CAAC;oBACR,uDAAuD;gBACxD,CAAC;gBACD,SAAS;YACV,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,kEAAkE;QACnE,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,kBAAkB,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,0BAA0B,QAAQ,WAAW,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC;QAC5F,CAAC;QACD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC5D,CAAC;AACF,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,QAAgB;IAC9C,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACR,kEAAkE;IACnE,CAAC;AACF,CAAC;AAED,qEAAqE;AACrE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,QAAgB,EAAE,SAA2B;IAClF,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;IAChC,IAAI,CAAC;QACJ,OAAO,MAAM,SAAS,EAAE,CAAC;IAC1B,CAAC;YAAS,CAAC;QACV,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type GatePolicyProfile = "strict" | "balanced" | "lightweight";
|
|
2
|
+
export interface GatePolicy {
|
|
3
|
+
profile: GatePolicyProfile;
|
|
4
|
+
planGate: "hard" | "advisory";
|
|
5
|
+
requireApprovedPlan: boolean;
|
|
6
|
+
contractGate: "hard" | "optional" | "off";
|
|
7
|
+
reviewGate: "hard" | "hard-on-fail" | "advisory";
|
|
8
|
+
buildVerify: "on";
|
|
9
|
+
humanApproval: "required" | "selected" | "optional";
|
|
10
|
+
}
|
|
11
|
+
export declare const POLICY_PRESETS: Record<GatePolicyProfile, GatePolicy>;
|
|
12
|
+
export declare function policyPath(claudeDir: string): string;
|
|
13
|
+
export declare function parsePolicyProfile(value: string | undefined): GatePolicyProfile | null;
|
|
14
|
+
export declare function readGatePolicy(claudeDir: string): {
|
|
15
|
+
policy: GatePolicy;
|
|
16
|
+
source: string;
|
|
17
|
+
error?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function writeGatePolicy(claudeDir: string, profile: GatePolicyProfile): void;
|
|
20
|
+
//# sourceMappingURL=gate-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-policy.d.ts","sourceRoot":"","sources":["../../src/core/gate-policy.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAC;AAEtE,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC;IAC9B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,UAAU,GAAG,KAAK,CAAC;IAC1C,UAAU,EAAE,MAAM,GAAG,cAAc,GAAG,UAAU,CAAC;IACjD,WAAW,EAAE,IAAI,CAAC;IAClB,aAAa,EAAE,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;CACpD;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,iBAAiB,EAAE,UAAU,CA4BhE,CAAC;AAEF,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,GAAG,IAAI,CAEtF;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAWxG;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAUnF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export const POLICY_PRESETS = {
|
|
4
|
+
strict: {
|
|
5
|
+
profile: "strict",
|
|
6
|
+
planGate: "hard",
|
|
7
|
+
requireApprovedPlan: true,
|
|
8
|
+
contractGate: "hard",
|
|
9
|
+
reviewGate: "hard",
|
|
10
|
+
buildVerify: "on",
|
|
11
|
+
humanApproval: "required",
|
|
12
|
+
},
|
|
13
|
+
balanced: {
|
|
14
|
+
profile: "balanced",
|
|
15
|
+
planGate: "hard",
|
|
16
|
+
requireApprovedPlan: false,
|
|
17
|
+
contractGate: "optional",
|
|
18
|
+
reviewGate: "hard-on-fail",
|
|
19
|
+
buildVerify: "on",
|
|
20
|
+
humanApproval: "selected",
|
|
21
|
+
},
|
|
22
|
+
lightweight: {
|
|
23
|
+
profile: "lightweight",
|
|
24
|
+
planGate: "advisory",
|
|
25
|
+
requireApprovedPlan: false,
|
|
26
|
+
contractGate: "off",
|
|
27
|
+
reviewGate: "advisory",
|
|
28
|
+
buildVerify: "on",
|
|
29
|
+
humanApproval: "optional",
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export function policyPath(claudeDir) {
|
|
33
|
+
return path.join(claudeDir, "policy.json");
|
|
34
|
+
}
|
|
35
|
+
export function parsePolicyProfile(value) {
|
|
36
|
+
return value === "strict" || value === "balanced" || value === "lightweight" ? value : null;
|
|
37
|
+
}
|
|
38
|
+
export function readGatePolicy(claudeDir) {
|
|
39
|
+
const p = policyPath(claudeDir);
|
|
40
|
+
if (!fs.existsSync(p))
|
|
41
|
+
return { policy: POLICY_PRESETS.balanced, source: "default" };
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
44
|
+
const profile = parsePolicyProfile(parsed.profile);
|
|
45
|
+
if (!profile)
|
|
46
|
+
return { policy: POLICY_PRESETS.strict, source: p, error: "unknown profile; failing safe as strict" };
|
|
47
|
+
return { policy: POLICY_PRESETS[profile], source: p };
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return { policy: POLICY_PRESETS.strict, source: p, error: "unreadable policy; failing safe as strict" };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function writeGatePolicy(claudeDir, profile) {
|
|
54
|
+
const p = policyPath(claudeDir);
|
|
55
|
+
const tmp = `${p}.tmp`;
|
|
56
|
+
const body = {
|
|
57
|
+
profile,
|
|
58
|
+
updatedAt: new Date().toISOString(),
|
|
59
|
+
note: "Privacy and prompt-injection blocks are never disabled by policy profiles.",
|
|
60
|
+
};
|
|
61
|
+
fs.writeFileSync(tmp, `${JSON.stringify(body, null, 2)}\n`, "utf-8");
|
|
62
|
+
fs.renameSync(tmp, p);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=gate-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate-policy.js","sourceRoot":"","sources":["../../src/core/gate-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAc7B,MAAM,CAAC,MAAM,cAAc,GAA0C;IACpE,MAAM,EAAE;QACP,OAAO,EAAE,QAAQ;QACjB,QAAQ,EAAE,MAAM;QAChB,mBAAmB,EAAE,IAAI;QACzB,YAAY,EAAE,MAAM;QACpB,UAAU,EAAE,MAAM;QAClB,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,UAAU;KACzB;IACD,QAAQ,EAAE;QACT,OAAO,EAAE,UAAU;QACnB,QAAQ,EAAE,MAAM;QAChB,mBAAmB,EAAE,KAAK;QAC1B,YAAY,EAAE,UAAU;QACxB,UAAU,EAAE,cAAc;QAC1B,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,UAAU;KACzB;IACD,WAAW,EAAE;QACZ,OAAO,EAAE,aAAa;QACtB,QAAQ,EAAE,UAAU;QACpB,mBAAmB,EAAE,KAAK;QAC1B,YAAY,EAAE,KAAK;QACnB,UAAU,EAAE,UAAU;QACtB,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,UAAU;KACzB;CACD,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC3C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAyB;IAC3D,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7F,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC/C,MAAM,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IACrF,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAyB,CAAC;QAC/E,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC;QACpH,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,2CAA2C,EAAE,CAAC;IACzG,CAAC;AACF,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,OAA0B;IAC5E,MAAM,CAAC,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IACvB,MAAM,IAAI,GAAG;QACZ,OAAO;QACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,4EAA4E;KAClF,CAAC;IACF,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACrE,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a throwaway project that mirrors a real MeowKit install closely enough to
|
|
3
|
+
* run its configured hooks: the entire `.claude/hooks/` tree (including `lib/`, so
|
|
4
|
+
* relative `source` paths resolve under CLAUDE_PROJECT_DIR) plus `.claude/settings.json`.
|
|
5
|
+
*
|
|
6
|
+
* Shared by the configured-hook integration tests and `doctor --hard-gates` so there
|
|
7
|
+
* is exactly one "mkdtemp + copy .claude" implementation. Tests/probes mutate plan and
|
|
8
|
+
* `.env` state inside the temp dir, never the source repo.
|
|
9
|
+
*/
|
|
10
|
+
export interface HarnessProject {
|
|
11
|
+
/** Absolute path to the temp project root (use as CLAUDE_PROJECT_DIR). */
|
|
12
|
+
dir: string;
|
|
13
|
+
/** Remove the temp project. Safe to call multiple times. */
|
|
14
|
+
cleanup: () => void;
|
|
15
|
+
/** Create a plan file so Gate 1 passes. Returns the plan path. */
|
|
16
|
+
addPlan: (opts?: {
|
|
17
|
+
nested?: boolean;
|
|
18
|
+
approved?: boolean;
|
|
19
|
+
slug?: string;
|
|
20
|
+
}) => string;
|
|
21
|
+
/** Remove all plan files (return Gate 1 to the no-plan state). */
|
|
22
|
+
clearPlans: () => void;
|
|
23
|
+
/** Create a sensitive file (default `.env`) in the project root. Returns its path. */
|
|
24
|
+
addEnvFile: (name?: string) => string;
|
|
25
|
+
}
|
|
26
|
+
/** True if `<srcRoot>/.claude/settings.json` and the hooks tree exist. */
|
|
27
|
+
export declare function hasHarness(srcRoot: string): boolean;
|
|
28
|
+
export declare function scaffoldHarnessProject(srcRoot: string): HarnessProject;
|
|
29
|
+
//# sourceMappingURL=harness-scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-scaffold.d.ts","sourceRoot":"","sources":["../../src/core/harness-scaffold.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,MAAM,WAAW,cAAc;IAC9B,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,kEAAkE;IAClE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,MAAM,CAAC;IACpF,kEAAkE;IAClE,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,sFAAsF;IACtF,UAAU,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CACtC;AAWD,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAKnD;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAsCtE"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
function copyClaudeHarness(srcRoot, destRoot) {
|
|
5
|
+
const claudeSrc = path.join(srcRoot, ".claude");
|
|
6
|
+
const claudeDst = path.join(destRoot, ".claude");
|
|
7
|
+
fs.mkdirSync(claudeDst, { recursive: true });
|
|
8
|
+
// Whole hooks tree (incl. lib/) so each hook's relative `source` resolves.
|
|
9
|
+
fs.cpSync(path.join(claudeSrc, "hooks"), path.join(claudeDst, "hooks"), { recursive: true });
|
|
10
|
+
fs.copyFileSync(path.join(claudeSrc, "settings.json"), path.join(claudeDst, "settings.json"));
|
|
11
|
+
}
|
|
12
|
+
/** True if `<srcRoot>/.claude/settings.json` and the hooks tree exist. */
|
|
13
|
+
export function hasHarness(srcRoot) {
|
|
14
|
+
return (fs.existsSync(path.join(srcRoot, ".claude", "settings.json")) &&
|
|
15
|
+
fs.existsSync(path.join(srcRoot, ".claude", "hooks")));
|
|
16
|
+
}
|
|
17
|
+
export function scaffoldHarnessProject(srcRoot) {
|
|
18
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "mewkit-hardgate-"));
|
|
19
|
+
copyClaudeHarness(srcRoot, dir);
|
|
20
|
+
const plansDir = path.join(dir, "tasks", "plans");
|
|
21
|
+
fs.mkdirSync(plansDir, { recursive: true });
|
|
22
|
+
fs.mkdirSync(path.join(dir, "tasks", "contracts"), { recursive: true });
|
|
23
|
+
fs.writeFileSync(path.join(dir, "CLAUDE.md"), "# test project\n");
|
|
24
|
+
const frontmatter = (approved) => `---\nstatus: ${approved ? "approved" : "pending"}\n---\n# plan\n`;
|
|
25
|
+
return {
|
|
26
|
+
dir,
|
|
27
|
+
cleanup: () => fs.rmSync(dir, { recursive: true, force: true }),
|
|
28
|
+
addPlan: (opts = {}) => {
|
|
29
|
+
const slug = opts.slug ?? "260531-demo";
|
|
30
|
+
let planPath;
|
|
31
|
+
if (opts.nested) {
|
|
32
|
+
const planDir = path.join(plansDir, slug);
|
|
33
|
+
fs.mkdirSync(planDir, { recursive: true });
|
|
34
|
+
planPath = path.join(planDir, "plan.md");
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
planPath = path.join(plansDir, `${slug}.md`);
|
|
38
|
+
}
|
|
39
|
+
fs.writeFileSync(planPath, frontmatter(opts.approved ?? false));
|
|
40
|
+
return planPath;
|
|
41
|
+
},
|
|
42
|
+
clearPlans: () => {
|
|
43
|
+
fs.rmSync(plansDir, { recursive: true, force: true });
|
|
44
|
+
fs.mkdirSync(plansDir, { recursive: true });
|
|
45
|
+
},
|
|
46
|
+
addEnvFile: (name = ".env") => {
|
|
47
|
+
const envPath = path.join(dir, name);
|
|
48
|
+
fs.writeFileSync(envPath, "SECRET=do-not-read\n");
|
|
49
|
+
return envPath;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=harness-scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-scaffold.js","sourceRoot":"","sources":["../../src/core/harness-scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAyB7B,SAAS,iBAAiB,CAAC,OAAe,EAAE,QAAgB;IAC3D,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACjD,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,2EAA2E;IAC3E,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7F,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,UAAU,CAAC,OAAe;IACzC,OAAO,CACN,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QAC7D,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CACrD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,OAAe;IACrD,MAAM,GAAG,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACvE,iBAAiB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAEhC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAClD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,kBAAkB,CAAC,CAAC;IAElE,MAAM,WAAW,GAAG,CAAC,QAAiB,EAAU,EAAE,CACjD,gBAAgB,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,iBAAiB,CAAC;IAEpE,OAAO;QACN,GAAG;QACH,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAC/D,OAAO,EAAE,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE;YACtB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,aAAa,CAAC;YACxC,IAAI,QAAgB,CAAC;YACrB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;gBAC1C,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC3C,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACP,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC;YAC9C,CAAC;YACD,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,CAAC,CAAC;YAChE,OAAO,QAAQ,CAAC;QACjB,CAAC;QACD,UAAU,EAAE,GAAG,EAAE;YAChB,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;QACD,UAAU,EAAE,CAAC,IAAI,GAAG,MAAM,EAAE,EAAE;YAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACrC,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;YAClD,OAAO,OAAO,CAAC;QAChB,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replays a Claude Code hook exactly as `.claude/settings.json` configures it:
|
|
3
|
+
* same interpreter, same script, JSON on stdin (NOT positional args). This is the
|
|
4
|
+
* single source of truth shared by the configured-hook integration tests and the
|
|
5
|
+
* `doctor --hard-gates` probes — neither hardcodes `bash`, so both reproduce the
|
|
6
|
+
* real invocation (and would catch an `sh`/dash runtime mismatch).
|
|
7
|
+
*/
|
|
8
|
+
export interface HookCommand {
|
|
9
|
+
/** The raw command string from settings.json. */
|
|
10
|
+
raw: string;
|
|
11
|
+
/** First token of the command — e.g. "bash", "sh", "node". */
|
|
12
|
+
interpreter: string;
|
|
13
|
+
/** Script path as written in settings.json (may contain `$CLAUDE_PROJECT_DIR`). */
|
|
14
|
+
scriptRef: string;
|
|
15
|
+
/** Basename of the script, for filtering/identification. */
|
|
16
|
+
script: string;
|
|
17
|
+
}
|
|
18
|
+
export interface HookRunResult {
|
|
19
|
+
script: string;
|
|
20
|
+
interpreter: string;
|
|
21
|
+
status: number | null;
|
|
22
|
+
stdout: string;
|
|
23
|
+
stderr: string;
|
|
24
|
+
}
|
|
25
|
+
export interface HookToolInput {
|
|
26
|
+
file_path?: string;
|
|
27
|
+
command?: string;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface RunConfiguredHookOptions {
|
|
31
|
+
projectDir: string;
|
|
32
|
+
event: string;
|
|
33
|
+
/** Concrete tool name (e.g. "Write") matched against each group's `matcher` regex. */
|
|
34
|
+
tool?: string;
|
|
35
|
+
/** tool_name field in the stdin JSON; defaults to `tool`. */
|
|
36
|
+
toolName?: string;
|
|
37
|
+
toolInput?: HookToolInput;
|
|
38
|
+
extraEnv?: Record<string, string>;
|
|
39
|
+
/** Skip non-shell (e.g. node) commands. Default true — shell hooks are the runtime-mismatch surface. */
|
|
40
|
+
shellOnly?: boolean;
|
|
41
|
+
/** If set, only run the command whose script basename equals this value. */
|
|
42
|
+
only?: string;
|
|
43
|
+
/** Script basenames to skip (e.g. a slow bootstrap hook irrelevant to the probe). */
|
|
44
|
+
exclude?: string[];
|
|
45
|
+
}
|
|
46
|
+
interface HookEntry {
|
|
47
|
+
type?: string;
|
|
48
|
+
command?: string;
|
|
49
|
+
}
|
|
50
|
+
interface HookGroup {
|
|
51
|
+
matcher?: string;
|
|
52
|
+
hooks?: HookEntry[];
|
|
53
|
+
}
|
|
54
|
+
interface SettingsShape {
|
|
55
|
+
hooks?: Record<string, HookGroup[]>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Walk settings.hooks[event], returning every command whose group matches `tool`.
|
|
59
|
+
* Pure — does not expand `$CLAUDE_PROJECT_DIR` (that happens at run time).
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseHookCommands(settings: SettingsShape, event: string, tool?: string): HookCommand[];
|
|
62
|
+
/**
|
|
63
|
+
* Reconstruct and run the configured hook commands for one event/tool against
|
|
64
|
+
* `projectDir`, feeding the canonical JSON-on-stdin payload. Returns one result
|
|
65
|
+
* per executed command (in settings order).
|
|
66
|
+
*/
|
|
67
|
+
export declare function runConfiguredHook(settings: SettingsShape, opts: RunConfiguredHookOptions): HookRunResult[];
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=hook-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-runner.d.ts","sourceRoot":"","sources":["../../src/core/hook-runner.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AAEH,MAAM,WAAW,WAAW;IAC3B,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,wGAAwG;IACxG,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,SAAS;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AACD,UAAU,SAAS;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;CACpB;AACD,UAAU,aAAa;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;CACpC;AA0BD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAatG;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,EAAE,wBAAwB,GAAG,aAAa,EAAE,CAoC1G"}
|