@event4u/agent-config 14.17.0 → 14.19.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +207 -77
- package/README.md +72 -150
- package/dist/agent-src/README.md +1 -1
- package/dist/agent-src/commands/agent-handoff.md +2 -2
- package/dist/agent-src/commands/pr/merge.md +35 -44
- package/dist/agent-src/commands/review/changes.md +19 -19
- package/dist/agent-src/commands/review.md +2 -2
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +30 -3
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/git-history-discipline.md +2 -2
- package/dist/agent-src/rules/guidelines.md +1 -1
- package/dist/agent-src/rules/session-canary.md +2 -2
- package/dist/agent-src/rules/token-efficiency.md +1 -1
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +2 -0
- package/dist/agent-src/skills/adversarial-review/SKILL.md +0 -8
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +27 -1
- package/dist/agent-src/skills/canvas-design/SKILL.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +60 -12
- package/dist/agent-src/skills/complexity-first-planning/SKILL.md +27 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +1 -1
- package/dist/agent-src/skills/design-intelligence/data/app-interface.csv +1 -1
- package/dist/agent-src/skills/design-intelligence/data/motion.csv +17 -17
- package/dist/agent-src/skills/design-intelligence/data/ux-guidelines.csv +2 -2
- package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +17 -0
- package/dist/agent-src/skills/design-intelligence/references/design-rules-checklist.md +4 -4
- package/dist/agent-src/skills/design-review/SKILL.md +3 -1
- package/dist/agent-src/skills/design-variations/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +1 -1
- package/dist/agent-src/skills/fe-design/SKILL.md +32 -5
- package/dist/agent-src/skills/fe-design/references/craft-floor.md +7 -0
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +47 -2
- package/dist/agent-src/skills/feature-planning/SKILL.md +34 -0
- package/dist/agent-src/skills/git-workflow/SKILL.md +37 -12
- package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +176 -0
- package/dist/agent-src/skills/laravel-mail/SKILL.md +44 -0
- package/dist/agent-src/skills/laravel-scheduling/SKILL.md +3 -1
- package/dist/agent-src/skills/project-analyzer/SKILL.md +0 -6
- package/dist/agent-src/skills/react-native-setup/SKILL.md +3 -3
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/sequential-thinking/SKILL.md +18 -8
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +40 -0
- package/dist/agent-src/skills/ui-apply-generic/SKILL.md +1 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +1 -1
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +29 -2
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +53 -8
- package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +3 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/commands/recordTriggerEval.js +2 -2
- package/dist/cli/main.js +1 -1
- package/dist/cli-delegate/{chunk-AKJGUQNO.js → chunk-GZTMRYCN.js} +405 -341
- package/dist/cli-delegate/{chunk-7RXIKF62.js → chunk-IDHUQIXP.js} +283 -132
- package/dist/cli-delegate/cmd_adr_effective.js +318 -81
- package/dist/cli-delegate/cmd_conformance.js +749 -669
- package/dist/cli-delegate/cmd_doctor.js +1 -1
- package/dist/cli-delegate/cmd_export.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +1 -1
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +1 -1
- package/dist/cli-delegate/cmd_uninstall.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +39 -38
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +2764 -2439
- package/dist/install/install.mjs +588 -527
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1381 -1138
- package/dist/scripts/_lib/model_tier.js +1 -1
- package/dist/shared/settingsConsent.js +1 -1
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/docs/guidelines/design-antipatterns.md +87 -64
- package/package.json +1 -1
- package/src/agent-src/README.md +1 -1
- package/src/config/ci-local-parity.yml +6 -0
- package/src/config/conformance-claim-baseline.json +9 -0
- package/src/config/gate-coverage.yml +170 -0
- package/src/config/gate-violation-baselines.json +4 -4
- package/src/config/release-gate-locality.yml +141 -0
- package/src/scripts/_cli/cmd_doctor.ts +2 -2
- package/src/scripts/_cli/cmd_handoff.ts +1 -1
- package/src/scripts/_cli/cmd_update.ts +1 -1
- package/src/scripts/_cli/cmd_versions.ts +1 -1
- package/src/scripts/_lib/adr_frontmatter.ts +44 -0
- package/src/scripts/_lib/consequence_objects.ts +152 -0
- package/src/scripts/_lib/exec_evidence.ts +7 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +2 -2
- package/src/scripts/_lib/host_permission_checks.ts +251 -0
- package/src/scripts/_lib/model_tier.ts +1 -1
- package/src/scripts/_lib/obligation_frequency.ts +2 -2
- package/src/scripts/_lib/release_highlights.ts +265 -1
- package/src/scripts/_lib/release_material.ts +129 -0
- package/src/scripts/_lib/repeated_run.ts +77 -0
- package/src/scripts/_lib/runtime_journal.ts +26 -3
- package/src/scripts/_lib/runtime_wiring_checks.ts +12 -1
- package/src/scripts/_lib/session_register.ts +11 -6
- package/src/scripts/_lib/sqlite_schema.ts +22 -0
- package/src/scripts/_lib/surface.ts +86 -0
- package/src/scripts/_lib/tamper_vocabulary.ts +242 -0
- package/src/scripts/_lib/ui_authority.ts +116 -0
- package/src/scripts/_lib/unified_diff.ts +121 -0
- package/src/scripts/adr/regenerate_index.ts +54 -1
- package/src/scripts/adr_cite_check.ts +71 -15
- package/src/scripts/ai_council/clients.ts +1 -1
- package/src/scripts/autonomy_friction_corpus.ts +229 -0
- package/src/scripts/build_proof.ts +15 -2
- package/src/scripts/check_beta_review_markers.ts +141 -17
- package/src/scripts/check_enforcement_coverage.ts +1 -1
- package/src/scripts/check_enforcement_denominator.ts +121 -11
- package/src/scripts/check_finding_dispositions.ts +239 -6
- package/src/scripts/check_gate_coverage.ts +96 -0
- package/src/scripts/check_installed_hooks_fresh.ts +381 -0
- package/src/scripts/check_release_highlights.ts +99 -14
- package/src/scripts/check_skill_activation_claim.ts +179 -0
- package/src/scripts/check_suppression_hygiene.ts +11 -0
- package/src/scripts/check_trigger_evals.ts +1 -1
- package/src/scripts/code_graph/build.ts +230 -33
- package/src/scripts/code_graph/extract.ts +168 -33
- package/src/scripts/code_graph/types.ts +37 -2
- package/src/scripts/code_graph/validate.ts +12 -1
- package/src/scripts/conformance_scan.ts +45 -4
- package/src/scripts/detect_silent_catch.ts +403 -0
- package/src/scripts/detect_verification_tampering.ts +538 -0
- package/src/scripts/explain_run.ts +1 -1
- package/src/scripts/generate_capability_matrix.ts +1 -1
- package/src/scripts/git_authorization_hook.ts +228 -18
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +47 -30
- package/src/scripts/hooks/augment-dispatcher.sh +1 -1
- package/src/scripts/hooks/block_no_verify.ts +3 -3
- package/src/scripts/hooks/category_a.ts +291 -0
- package/src/scripts/hooks/cline-dispatcher.sh +1 -1
- package/src/scripts/hooks/concern_registry.ts +2 -2
- package/src/scripts/hooks/cursor-dispatcher.sh +11 -5
- package/src/scripts/hooks/dispatch_hook.ts +17 -22
- package/src/scripts/hooks/evidence_independence.ts +21 -2
- package/src/scripts/hooks/{block_unauthorized_git.ts → git_command_classifier.ts} +175 -364
- package/src/scripts/hooks/host_lowering.ts +215 -0
- package/src/scripts/hooks/host_lowering.yaml +185 -0
- package/src/scripts/hooks/host_semantics.ts +105 -37
- package/src/scripts/hooks/journal_record_hook.ts +10 -0
- package/src/scripts/hooks/permission_gate.ts +117 -0
- package/src/scripts/hooks/push_settle_hook.ts +250 -0
- package/src/scripts/hooks/ship_diff_volume_hook.ts +1 -1
- package/src/scripts/hooks_status.ts +12 -10
- package/src/scripts/install-hooks.sh +134 -1
- package/src/scripts/install.ts +53 -82
- package/src/scripts/install_anthropic_key.sh +2 -2
- package/src/scripts/language_mirror_hook.ts +27 -5
- package/src/scripts/lint_design_antipattern_parity.ts +80 -0
- package/src/scripts/lint_evidence_artifacts.ts +45 -1
- package/src/scripts/lint_hook_manifest.ts +87 -3
- package/src/scripts/lint_motion_authority_drift.ts +731 -0
- package/src/scripts/lint_readme_size.ts +158 -12
- package/src/scripts/lint_reasoning_fixtures.ts +216 -0
- package/src/scripts/lint_skill_trigger_contradiction.ts +161 -0
- package/src/scripts/measure_sibling_naming.ts +150 -0
- package/src/scripts/redact_hook_capture.ts +1 -1
- package/src/scripts/release.ts +31 -143
- package/src/scripts/release_drill.ts +71 -1
- package/src/scripts/release_publication.ts +243 -3
- package/src/scripts/release_verify.ts +220 -0
- package/src/scripts/report_skill_activation.ts +319 -7
- package/src/scripts/schemas/review-findings.schema.json +6 -1
- package/src/scripts/schemas/skill.schema.json +12 -2
- package/src/scripts/self_review_gate.ts +10 -1
- package/src/scripts/session_canary_hook.ts +1 -1
- package/src/scripts/session_register_hook.ts +4 -3
- package/src/scripts/trigger_eval_grandfather.json +5 -1
- package/src/scripts/validate_agent_settings.ts +1 -1
- package/src/shared/settingsConsent.ts +1 -1
|
@@ -16,6 +16,9 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
readHookStdin
|
|
18
18
|
} from "./chunk-F7DQWYR5.js";
|
|
19
|
+
import {
|
|
20
|
+
reportScanned
|
|
21
|
+
} from "./chunk-I3EIN2YG.js";
|
|
19
22
|
import {
|
|
20
23
|
ArgparseExit,
|
|
21
24
|
STATUS_SYMBOLS,
|
|
@@ -33,7 +36,7 @@ import {
|
|
|
33
36
|
frontmatterPaths,
|
|
34
37
|
proseEqual,
|
|
35
38
|
splitFrontmatter
|
|
36
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-IDHUQIXP.js";
|
|
37
40
|
import "./chunk-FTAISGDQ.js";
|
|
38
41
|
import "./chunk-M2DPGFZ4.js";
|
|
39
42
|
import "./chunk-BTWUYDGN.js";
|
|
@@ -78,12 +81,12 @@ import {
|
|
|
78
81
|
|
|
79
82
|
// src/scripts/_cli/cmd_conformance.ts
|
|
80
83
|
var YAML = __toESM(require_dist(), 1);
|
|
81
|
-
import * as
|
|
84
|
+
import * as fs13 from "node:fs";
|
|
82
85
|
import * as os7 from "node:os";
|
|
83
|
-
import * as
|
|
86
|
+
import * as path13 from "node:path";
|
|
84
87
|
import process6 from "node:process";
|
|
85
88
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
86
|
-
import { fileURLToPath as
|
|
89
|
+
import { fileURLToPath as fileURLToPath13, pathToFileURL as pathToFileURL13 } from "node:url";
|
|
87
90
|
|
|
88
91
|
// src/install/txlog.ts
|
|
89
92
|
import { createReadStream, createWriteStream, existsSync, readFileSync, renameSync, statSync, unlinkSync } from "node:fs";
|
|
@@ -144,18 +147,18 @@ function routeTargetPathsPosix(target) {
|
|
|
144
147
|
}
|
|
145
148
|
|
|
146
149
|
// src/scripts/report_conformance_funnel.ts
|
|
147
|
-
import * as
|
|
150
|
+
import * as fs12 from "node:fs";
|
|
148
151
|
import * as os6 from "node:os";
|
|
149
|
-
import * as
|
|
152
|
+
import * as path12 from "node:path";
|
|
150
153
|
import process5 from "node:process";
|
|
151
|
-
import { fileURLToPath as
|
|
154
|
+
import { fileURLToPath as fileURLToPath12, pathToFileURL as pathToFileURL12 } from "node:url";
|
|
152
155
|
|
|
153
156
|
// src/scripts/conformance_scan.ts
|
|
154
157
|
import { createHash as createHash3 } from "node:crypto";
|
|
155
|
-
import
|
|
158
|
+
import fs8 from "node:fs";
|
|
156
159
|
import os2 from "node:os";
|
|
157
|
-
import
|
|
158
|
-
import { fileURLToPath as
|
|
160
|
+
import path8 from "node:path";
|
|
161
|
+
import { fileURLToPath as fileURLToPath8, pathToFileURL as pathToFileURL8 } from "node:url";
|
|
159
162
|
|
|
160
163
|
// src/scripts/_lib/transcript_entry.ts
|
|
161
164
|
function _isObject(v) {
|
|
@@ -432,7 +435,11 @@ function _setPinLost(consumer_root, session_id) {
|
|
|
432
435
|
_migrateLegacyMarker(consumer_root);
|
|
433
436
|
try {
|
|
434
437
|
atomic_write_text(path.join(consumer_root, pinLostMarker(session_id)), (/* @__PURE__ */ new Date()).toISOString());
|
|
435
|
-
} catch {
|
|
438
|
+
} catch (err) {
|
|
439
|
+
process.stderr.write(
|
|
440
|
+
`language-mirror: pin-lost marker write failed (${_errText(err)}) \u2014 the pin will NOT be re-emitted after this compaction. Turn outcome unchanged.
|
|
441
|
+
`
|
|
442
|
+
);
|
|
436
443
|
}
|
|
437
444
|
}
|
|
438
445
|
function _pinLost(consumer_root, session_id) {
|
|
@@ -658,7 +665,11 @@ function run(stdin_text, options) {
|
|
|
658
665
|
atomic_write_json(target, next);
|
|
659
666
|
_pruneLegacyState(options.consumer_root);
|
|
660
667
|
_pruneStaleSessions(options.consumer_root, Date.now());
|
|
661
|
-
} catch {
|
|
668
|
+
} catch (err) {
|
|
669
|
+
process.stderr.write(
|
|
670
|
+
`language-mirror: pin-state write failed (${_errText(err)}) \u2014 the language pin did NOT persist and the next turn re-derives it. This turn's notice is unchanged.
|
|
671
|
+
`
|
|
672
|
+
);
|
|
662
673
|
}
|
|
663
674
|
}
|
|
664
675
|
if (effective === "de" || effective === "en") {
|
|
@@ -670,6 +681,10 @@ function run(stdin_text, options) {
|
|
|
670
681
|
}
|
|
671
682
|
return EXIT_ALLOW;
|
|
672
683
|
}
|
|
684
|
+
function _errText(err) {
|
|
685
|
+
const t = err instanceof Error ? err.message : String(err);
|
|
686
|
+
return t.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
687
|
+
}
|
|
673
688
|
function main(argv) {
|
|
674
689
|
const args = argv ?? process.argv.slice(2);
|
|
675
690
|
let consumer_root = process.cwd();
|
|
@@ -727,6 +742,128 @@ function humanTypedThisTurn(prompt) {
|
|
|
727
742
|
return true;
|
|
728
743
|
}
|
|
729
744
|
|
|
745
|
+
// src/scripts/_lib/consequence_objects.ts
|
|
746
|
+
var CONSEQUENCE_PHRASES = [
|
|
747
|
+
{
|
|
748
|
+
op: "prod-deploy",
|
|
749
|
+
re: /(?<![A-Za-z0-9_À-ɏ])(deploy(e|en|t|st)?|ausroll(e|en)?|ship(pe|pen)?|live\s*schalt(e|en)?|rollout)\b[^.\n]{0,30}\b(produktion|production|prod|live|prod-umgebung)\b|\b(produktion|production|prod|live)\b[^.\n]{0,30}\b(deploy(e|en)?|ausrollen|ausroll(e|en)?|live\s*schalten|rollout)\b/i
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
op: "prod-data-destroy",
|
|
753
|
+
re: /(?<![A-Za-z0-9_À-ɏ])(l(ö|oe)sch(e|en)?|entfern(e|en)?|leer(e|en)?|verwerf(e|en)?|drop|truncate|delete|wipe|purge|zerst(ö|oe)r(e|en)?)\b[^.\n]{0,40}\b(tabelle|tabellen|datenbank|daten|bucket|collection|zeilen|rows|table|database|dataset)\b|\b(tabelle|tabellen|datenbank|daten|bucket|collection|table|database)\b[^.\n]{0,40}\b(l(ö|oe)schen|leeren|verwerfen|droppen|truncaten|entfernen|drop|truncate|delete|wipe|purge)\b/i
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
op: "prod-infra-change",
|
|
757
|
+
re: /(?<![A-Za-z0-9_À-ɏ])((ver)?(ä|ae)nder(e|n)?|anpass(e|en)?|umbau(e|en)?|apply|destroy|(neu\s*)?provisionier(e|en)?)\b[^.\n]{0,40}\b(infrastruktur|infrastructure|cluster|terraform|iam|dns|netzwerk|network|firewall|policy|policies|security[-\s]group)\b|\b(infrastruktur|infrastructure|cluster|terraform|iam|dns|netzwerk|firewall)\b[^.\n]{0,40}\b((ver)?(ä|ae)ndern|anpassen|umbauen|zerst(ö|oe)ren|apply|destroy)\b/i
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
op: "external-send",
|
|
761
|
+
re: /(?<![A-Za-z0-9_À-ɏ])(schick(e|en)?|verschick(e|en)?|send(e|en|et)?|mail(e|en)?|raussend(e|en)?|versend(e|en)?|post|publish)\b[^.\n]{0,40}\b(mail|mails|email|e-mail|nachricht|nachrichten|newsletter|webhook|kunden|kundin|kunde|empf(ä|ae)nger|customers?|recipients?|message)\b|\b(mail|email|e-mail|nachricht|newsletter|kunden|customers?)\b[^.\n]{0,40}\b(schicken|verschicken|senden|versenden|rausschicken|send|deliver)\b/i
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
op: "money-movement",
|
|
765
|
+
re: /(?<![A-Za-z0-9_À-ɏ])(erstatt(e|en)?|r(ü|ue)ckerstatt(e|en)?|zahl(e|en)?|(ü|ue)berweis(e|en)?|abbuch(e|en)?|belast(e|en)?|refund|charge|payout|transfer|bill)\b[^.\n]{0,40}\b(betrag|geld|rechnung|zahlung|euro|dollar|kunde|kunden|karte|konto|invoice|payment|customer|card|account)\b|\b(betrag|geld|rechnung|zahlung|invoice|payment)\b[^.\n]{0,40}\b(erstatten|zur(ü|ue)ckzahlen|(ü|ue)berweisen|abbuchen|belasten|refund|charge|transfer)\b/i
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
op: "trunk-force-push",
|
|
769
|
+
re: /(?<![A-Za-z0-9_À-ɏ])(force[-\s]?push(e|en|t)?|force[-\s]?pushen|(ü|ue)berschreib(e|en)?)\b[^.\n]{0,40}\b(main|master|trunk|produktions?[-\s]?branch|production|release[-\s]?branch)\b|\b(main|master|trunk|production)\b[^.\n]{0,40}\b(force[-\s]?pushen|force[-\s]?push|(ü|ue)berschreiben)\b/i
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
op: "out-of-scope-destruction",
|
|
773
|
+
re: /(?<![A-Za-z0-9_À-ɏ])(l(ö|oe)sch(e|en)?|entfern(e|en)?|r(ä|ae)um(e|en)?\s*auf|aufr(ä|ae)um(e|en)?|delete|remove|wipe)\b[^.\n]{0,40}\b(au(ß|ss)erhalb|ausserhalb|unabh(ä|ae)ngig|nicht\s+zur\s+aufgabe|outside\s+(the\s+)?(task|scope)|unrelated|everything\s+else|alles\s+andere)\b|\b(au(ß|ss)erhalb|outside\s+(the\s+)?(task|scope)|unrelated)\b[^.\n]{0,40}\b(l(ö|oe)schen|entfernen|aufr(ä|ae)umen|delete|remove|wipe)\b/i
|
|
774
|
+
}
|
|
775
|
+
];
|
|
776
|
+
var OBJECT_REQUIRED_OPS = /* @__PURE__ */ new Set([
|
|
777
|
+
"prod-data-destroy",
|
|
778
|
+
"external-send",
|
|
779
|
+
"money-movement"
|
|
780
|
+
]);
|
|
781
|
+
var OBJECT_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
782
|
+
"bitte",
|
|
783
|
+
"doch",
|
|
784
|
+
"mal",
|
|
785
|
+
"nicht",
|
|
786
|
+
"jetzt",
|
|
787
|
+
"gleich",
|
|
788
|
+
"auch",
|
|
789
|
+
"noch",
|
|
790
|
+
"wieder",
|
|
791
|
+
"in",
|
|
792
|
+
"im",
|
|
793
|
+
"aus",
|
|
794
|
+
"von",
|
|
795
|
+
"vom",
|
|
796
|
+
": ",
|
|
797
|
+
"der",
|
|
798
|
+
"die",
|
|
799
|
+
"das",
|
|
800
|
+
"den",
|
|
801
|
+
"dem",
|
|
802
|
+
"und",
|
|
803
|
+
"oder",
|
|
804
|
+
"mit",
|
|
805
|
+
"auf",
|
|
806
|
+
"zu",
|
|
807
|
+
"komplett",
|
|
808
|
+
"ganz",
|
|
809
|
+
"alle",
|
|
810
|
+
"allen",
|
|
811
|
+
"please",
|
|
812
|
+
"now",
|
|
813
|
+
"again",
|
|
814
|
+
"too",
|
|
815
|
+
"also",
|
|
816
|
+
"from",
|
|
817
|
+
"and",
|
|
818
|
+
"or",
|
|
819
|
+
"the",
|
|
820
|
+
"a",
|
|
821
|
+
"an",
|
|
822
|
+
"there",
|
|
823
|
+
"here",
|
|
824
|
+
"completely",
|
|
825
|
+
"all",
|
|
826
|
+
"with",
|
|
827
|
+
"on",
|
|
828
|
+
"to",
|
|
829
|
+
"in"
|
|
830
|
+
]);
|
|
831
|
+
function _normalizeObject(raw) {
|
|
832
|
+
return raw.trim().replace(/^["'`]+|["'`]+$/g, "").replace(/[.,;:]+$/, "").toLowerCase();
|
|
833
|
+
}
|
|
834
|
+
var OBJECT_PATTERNS = {
|
|
835
|
+
"prod-data-destroy": [
|
|
836
|
+
/\b(?:tabelle|table|datenbank|database|schema|collection|index)\s+["'`]?([A-Za-z_][\w.$-]*)/gi,
|
|
837
|
+
/\bs3:\/\/([\w.-]+)/gi,
|
|
838
|
+
/\b(?:bucket)\s+["'`]?([A-Za-z_][\w.-]*)/gi
|
|
839
|
+
],
|
|
840
|
+
"external-send": [
|
|
841
|
+
/([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/g,
|
|
842
|
+
/\bhttps?:\/\/([^\s/"'`]+)/gi,
|
|
843
|
+
/\b(?:an\s+(?:die|den|alle)|to\s+(?:the|all))\s+([A-Za-z][\w-]*)/gi
|
|
844
|
+
],
|
|
845
|
+
"money-movement": [
|
|
846
|
+
/(\d[\d.,]*)\s*(?:€|eur\b|euro\b|\$|usd\b)/gi,
|
|
847
|
+
/(?:€|\$)\s*(\d[\d.,]*)/g,
|
|
848
|
+
/\b(?:kunde|kunden|customer|account|konto)\s+["'`]?([A-Za-z0-9][\w.-]*)/gi
|
|
849
|
+
]
|
|
850
|
+
};
|
|
851
|
+
function extractObjects(op, text) {
|
|
852
|
+
const patterns = OBJECT_PATTERNS[op];
|
|
853
|
+
if (!patterns) return [];
|
|
854
|
+
const out = [];
|
|
855
|
+
for (const re of patterns) {
|
|
856
|
+
const local = new RegExp(re.source, re.flags.includes("g") ? re.flags : `${re.flags}g`);
|
|
857
|
+
let m;
|
|
858
|
+
while ((m = local.exec(text)) !== null) {
|
|
859
|
+
const value = _normalizeObject(m[1] ?? "");
|
|
860
|
+
if (value && !OBJECT_STOP_WORDS.has(value) && !out.includes(value)) out.push(value);
|
|
861
|
+
if (m.index === local.lastIndex) local.lastIndex++;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return out;
|
|
865
|
+
}
|
|
866
|
+
|
|
730
867
|
// src/scripts/git_authorization_hook.ts
|
|
731
868
|
var EXIT_ALLOW2 = 0;
|
|
732
869
|
var STATE_FILE2 = path2.join("agents", "state", "git-authorization.json");
|
|
@@ -778,7 +915,14 @@ var ALL_OPS = [
|
|
|
778
915
|
"clean",
|
|
779
916
|
"stash-drop",
|
|
780
917
|
"branch-delete",
|
|
781
|
-
"close"
|
|
918
|
+
"close",
|
|
919
|
+
"prod-deploy",
|
|
920
|
+
"prod-data-destroy",
|
|
921
|
+
"prod-infra-change",
|
|
922
|
+
"external-send",
|
|
923
|
+
"money-movement",
|
|
924
|
+
"trunk-force-push",
|
|
925
|
+
"out-of-scope-destruction"
|
|
782
926
|
];
|
|
783
927
|
var PHRASES = [
|
|
784
928
|
{ op: "commit", re: /\b(commit(e|et|te|ten)?|committe|einchecken)\b/i },
|
|
@@ -936,7 +1080,8 @@ var PHRASES = [
|
|
|
936
1080
|
{
|
|
937
1081
|
op: "pr-merge-auto",
|
|
938
1082
|
re: /\bauto[-\s]?merge\b|\bmerge\b[^.\n]{0,20}\bautomatisch\b|\bautomatisch\b[^.\n]{0,20}\bmerg(e|en)\b/i
|
|
939
|
-
}
|
|
1083
|
+
},
|
|
1084
|
+
...CONSEQUENCE_PHRASES
|
|
940
1085
|
];
|
|
941
1086
|
var OUTPUT_LINE = /^\s*(To\s+\S+|remote:|error:|fatal:|hint:|warning:|!\s|\?\?\s|\s*\^|[-+]{3}\s|@@\s|\$\s|>\s|\d+\s+(pass|fail)|npm ERR!|Error:|Traceback|at\s+\S+:\d+)/i;
|
|
942
1087
|
function isInterrogative(prose) {
|
|
@@ -1000,7 +1145,7 @@ function splitFences(prompt) {
|
|
|
1000
1145
|
function _commandLines(fence) {
|
|
1001
1146
|
return fence.split("\n").map((l) => l.trim()).filter((l) => l.length > 0).filter((l) => !/^(remote:|error:|hint:|fatal:|warning:|To |!\s|\s*\^)/i.test(l)).map((l) => l.replace(/^[$>#]\s*/, "")).filter((l) => /^(git|gh|npm|pnpm|yarn|task)\b/.test(l));
|
|
1002
1147
|
}
|
|
1003
|
-
function
|
|
1148
|
+
function _errText2(err) {
|
|
1004
1149
|
const t = err instanceof Error ? err.message : String(err);
|
|
1005
1150
|
return t.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
1006
1151
|
}
|
|
@@ -1017,9 +1162,20 @@ function extractMergeTargets(prompt) {
|
|
|
1017
1162
|
}
|
|
1018
1163
|
return [...out].sort((a, b) => a - b);
|
|
1019
1164
|
}
|
|
1020
|
-
var
|
|
1165
|
+
var REVOKE_STOP_WORD = /\b(stop|stopp|halt|abbrechen|abbruch|cancel|revoke|widerruf(en)?|zur(ü|ue)ck(nehmen|ziehen)|hold\s+(everything|on|the\s+merge)|warte|wait)\b/iu;
|
|
1166
|
+
var MERGE_VERB = /\b(merg(e|en|ing)|zusammenf(ü|ue)hren)\b/giu;
|
|
1021
1167
|
function isRevocation(prompt) {
|
|
1022
|
-
|
|
1168
|
+
if (REVOKE_STOP_WORD.test(prompt) || isBareRefusal(prompt)) {
|
|
1169
|
+
return true;
|
|
1170
|
+
}
|
|
1171
|
+
MERGE_VERB.lastIndex = 0;
|
|
1172
|
+
let m;
|
|
1173
|
+
while ((m = MERGE_VERB.exec(prompt)) !== null) {
|
|
1174
|
+
if (negatedBefore(prompt, m.index, m[0])) {
|
|
1175
|
+
return true;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return false;
|
|
1023
1179
|
}
|
|
1024
1180
|
function _grantId(session_id, at, targets) {
|
|
1025
1181
|
return `${sessionSlug(session_id) || "nosession"}:${at}:${targets.join(",")}`;
|
|
@@ -1054,8 +1210,12 @@ function foldGrants(prior, prompt, authorized, session_id, now) {
|
|
|
1054
1210
|
}
|
|
1055
1211
|
];
|
|
1056
1212
|
}
|
|
1057
|
-
var
|
|
1058
|
-
var
|
|
1213
|
+
var APOSTROPHE = "'\u2019\u2018\u02BC";
|
|
1214
|
+
var NEGATION_ALTERNATION = `nicht|nichts|kein(?:e|en|em|er|es)?|niemals|nie|no|not|don[${APOSTROPHE}]?t|never|without|ohne`;
|
|
1215
|
+
var NEGATION_WORD = new RegExp(`\\b(${NEGATION_ALTERNATION})\\b`, "i");
|
|
1216
|
+
var NEGATION_TOKEN = new RegExp(`^(?:${NEGATION_ALTERNATION})$`, "i");
|
|
1217
|
+
var CONTRAST_ALTERNATION = "aber|sondern|jedoch|but|however|instead";
|
|
1218
|
+
var CONTRAST_CUE = new RegExp(`\\b(${CONTRAST_ALTERNATION})\\b`, "gi");
|
|
1059
1219
|
var ABBREVIATION_BEFORE_DOT = /(?:^|[\s("'\u2018\u201c])(?:[a-z]|z\.b|bzw|ca|usw|d\.h|u\.a|evtl|ggf|inkl|max|min|nr|abb|e\.g|i\.e|etc|vs|approx|fig|no|st|dr|prof)\.$/i;
|
|
1060
1220
|
function isSentenceBoundary(text, at) {
|
|
1061
1221
|
const mark = text[at];
|
|
@@ -1142,6 +1302,19 @@ function negatedBefore(text, index, matched = "") {
|
|
|
1142
1302
|
const after = afterFrom < end ? text.slice(afterFrom, clauseEnd(text, afterFrom, end)) : "";
|
|
1143
1303
|
return NEGATION_WORD.test(before) || NEGATION_WORD.test(matched) || NEGATION_WORD.test(after);
|
|
1144
1304
|
}
|
|
1305
|
+
var WORD_TOKEN = new RegExp(`[\\p{L}\\p{N}${APOSTROPHE}]+`, "gu");
|
|
1306
|
+
var CLAUSE_SPLIT = new RegExp(`[,;:]|\\b(?:${CONTRAST_ALTERNATION})\\b`, "iu");
|
|
1307
|
+
function isBareRefusal(text) {
|
|
1308
|
+
for (const sentence of _sentences(text)) {
|
|
1309
|
+
for (const clause of sentence.split(CLAUSE_SPLIT)) {
|
|
1310
|
+
const tokens = (clause ?? "").match(WORD_TOKEN) ?? [];
|
|
1311
|
+
if (tokens.length > 0 && tokens.every((t) => NEGATION_TOKEN.test(t))) {
|
|
1312
|
+
return true;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
return false;
|
|
1317
|
+
}
|
|
1145
1318
|
function classifyAuthorization(prompt) {
|
|
1146
1319
|
const { prose, fences } = splitFences(prompt);
|
|
1147
1320
|
const authorized = /* @__PURE__ */ new Set();
|
|
@@ -1209,40 +1382,6 @@ function readGrants(consumer_root, session_id) {
|
|
|
1209
1382
|
return [];
|
|
1210
1383
|
}
|
|
1211
1384
|
}
|
|
1212
|
-
function consumeGrantTarget(consumer_root, session_id, target) {
|
|
1213
|
-
const file = path2.join(consumer_root, ledgerFileFor(session_id));
|
|
1214
|
-
let decoded;
|
|
1215
|
-
try {
|
|
1216
|
-
decoded = JSON.parse(fs2.readFileSync(file, "utf8"));
|
|
1217
|
-
} catch {
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
if (!_isObject3(decoded) || !Array.isArray(decoded["grants"])) {
|
|
1221
|
-
return;
|
|
1222
|
-
}
|
|
1223
|
-
let touched = false;
|
|
1224
|
-
for (const g of decoded["grants"]) {
|
|
1225
|
-
if (!_isObject3(g) || !Array.isArray(g["targets"]) || !Array.isArray(g["consumed"])) {
|
|
1226
|
-
continue;
|
|
1227
|
-
}
|
|
1228
|
-
const targets = g["targets"];
|
|
1229
|
-
const consumed = g["consumed"];
|
|
1230
|
-
if (targets.includes(target) && !consumed.includes(target)) {
|
|
1231
|
-
consumed.push(target);
|
|
1232
|
-
touched = true;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
if (touched) {
|
|
1236
|
-
try {
|
|
1237
|
-
atomic_write_json(file, decoded);
|
|
1238
|
-
} catch (err) {
|
|
1239
|
-
process.stderr.write(
|
|
1240
|
-
`git-authorization: grant-consumption write failed (${_errText(err)}) \u2014 the target stays UNSPENT, so one further merge of a PR the user named will still be authorized. Authorization outcome unchanged.
|
|
1241
|
-
`
|
|
1242
|
-
);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
1385
|
function takePending(consumer_root, session_id, now) {
|
|
1247
1386
|
const file = path2.join(consumer_root, pendingFileFor(session_id));
|
|
1248
1387
|
let raw;
|
|
@@ -1311,6 +1450,11 @@ function run2(stdin_text, options) {
|
|
|
1311
1450
|
authorized.push(pending.op);
|
|
1312
1451
|
evidence[pending.op] = `confirmation of the refused \`${pending.op}\` (refused ${pending.refused_at}): "${prompt.trim().slice(0, 40)}"`;
|
|
1313
1452
|
}
|
|
1453
|
+
const objects = {};
|
|
1454
|
+
for (const op of authorized) {
|
|
1455
|
+
const named = extractObjects(op, prompt);
|
|
1456
|
+
if (named.length > 0) objects[op] = named;
|
|
1457
|
+
}
|
|
1314
1458
|
const now = /* @__PURE__ */ new Date();
|
|
1315
1459
|
const ledger = {
|
|
1316
1460
|
session_id: typeof envelope["session_id"] === "string" ? envelope["session_id"] : "",
|
|
@@ -1318,6 +1462,7 @@ function run2(stdin_text, options) {
|
|
|
1318
1462
|
authorized,
|
|
1319
1463
|
evidence,
|
|
1320
1464
|
prompt_chars: prompt.length,
|
|
1465
|
+
objects,
|
|
1321
1466
|
// Grants survive this write; `authorized` does not. That asymmetry IS the
|
|
1322
1467
|
// decision. A bare operation name stays one-shot exactly as `commit-policy`
|
|
1323
1468
|
// requires, and only an authorization that also froze its objects earns a
|
|
@@ -1334,7 +1479,7 @@ function run2(stdin_text, options) {
|
|
|
1334
1479
|
atomic_write_json(path2.join(options.consumer_root, ledgerFileFor(session)), ledger);
|
|
1335
1480
|
} catch (err) {
|
|
1336
1481
|
process.stderr.write(
|
|
1337
|
-
`git-authorization: session-ledger write failed (${
|
|
1482
|
+
`git-authorization: session-ledger write failed (${_errText2(err)}) \u2014 the gate degrades to "no ledger" for this session, which denies the irreversible subset and warns on the rest. Authorization outcome unchanged.
|
|
1338
1483
|
`
|
|
1339
1484
|
);
|
|
1340
1485
|
}
|
|
@@ -1377,125 +1522,7 @@ if (_isCliEntry2()) {
|
|
|
1377
1522
|
process.exit(main2());
|
|
1378
1523
|
}
|
|
1379
1524
|
|
|
1380
|
-
// src/scripts/hooks/
|
|
1381
|
-
import fs3 from "node:fs";
|
|
1382
|
-
import path3 from "node:path";
|
|
1383
|
-
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "node:url";
|
|
1384
|
-
|
|
1385
|
-
// src/scripts/hooks/merge_impact.ts
|
|
1386
|
-
import { execFileSync } from "node:child_process";
|
|
1387
|
-
var DESTRUCTIVE_MARKERS = [
|
|
1388
|
-
// Schema removal. Matched on ADDED lines only — a migration that *removes* a
|
|
1389
|
-
// dropColumn call is the opposite of destructive, and matching the raw patch
|
|
1390
|
-
// would score it the same way.
|
|
1391
|
-
{ label: "migration drops a column or table", re: /^\+.*\b(dropColumn|dropTable|dropIfExists|removeColumn|drop_column|drop_table)\b/im },
|
|
1392
|
-
{ label: "raw DROP or TRUNCATE in a migration", re: /^\+.*\b(DROP\s+(TABLE|COLUMN|INDEX|CONSTRAINT)|TRUNCATE\s+TABLE)\b/im },
|
|
1393
|
-
// The author said so themselves. Conventional Commits marks a breaking change
|
|
1394
|
-
// with a `!` before the colon or a body trailer.
|
|
1395
|
-
{ label: "author marked a BREAKING CHANGE", re: /^\+.*BREAKING[ -]CHANGE/m },
|
|
1396
|
-
// A removed export is the shape most likely to break an importer.
|
|
1397
|
-
{ label: "an exported symbol was removed", re: /^-\s*export\s+(default\s+|const\s+|function\s+|class\s+|interface\s+|type\s+|async\s+)/m },
|
|
1398
|
-
{ label: "a public PHP or Python symbol was removed", re: /^-\s*(public\s+function|def\s+[a-z_]+\s*\()/m },
|
|
1399
|
-
// Route removal breaks callers that never imported anything.
|
|
1400
|
-
{ label: "a route or endpoint was removed", re: /^-.*\b(Route::(get|post|put|patch|delete)|@(Get|Post|Put|Patch|Delete)Mapping|app\.(get|post|put|patch|delete)\s*\()/m },
|
|
1401
|
-
{ label: "a whole file was deleted", re: /^deleted file mode /m }
|
|
1402
|
-
];
|
|
1403
|
-
var SCHEMA_REMOVAL_BODY = /\b(dropColumn|dropTable|dropIfExists|removeColumn|drop_column|drop_table|DROP\s+(TABLE|COLUMN|INDEX|CONSTRAINT)|TRUNCATE\s+TABLE)\b/i;
|
|
1404
|
-
var RENAME_METADATA = /^rename (?:from|to) /m;
|
|
1405
|
-
function fileBlocks(patch) {
|
|
1406
|
-
return patch.split(/^(?=diff --git )/m).filter((b) => b.startsWith("diff --git "));
|
|
1407
|
-
}
|
|
1408
|
-
function renamedSchemaRemoval(patch) {
|
|
1409
|
-
for (const block of fileBlocks(patch)) {
|
|
1410
|
-
if (!RENAME_METADATA.test(block)) {
|
|
1411
|
-
continue;
|
|
1412
|
-
}
|
|
1413
|
-
for (const line of block.split("\n")) {
|
|
1414
|
-
if (line.startsWith("-") && !line.startsWith("---") && SCHEMA_REMOVAL_BODY.test(line)) {
|
|
1415
|
-
return true;
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
}
|
|
1419
|
-
return false;
|
|
1420
|
-
}
|
|
1421
|
-
var MAJOR_BUMP = /^-\s*"version"\s*:\s*"(\d+)\.[^"]*"[\s\S]{0,200}?^\+\s*"version"\s*:\s*"(\d+)\./m;
|
|
1422
|
-
function classifyDiff(patch) {
|
|
1423
|
-
if (patch.trim() === "") {
|
|
1424
|
-
return { verdict: "undecidable", markers: [], reason: "empty diff" };
|
|
1425
|
-
}
|
|
1426
|
-
const markers = [];
|
|
1427
|
-
for (const { label, re } of DESTRUCTIVE_MARKERS) {
|
|
1428
|
-
if (re.test(patch)) {
|
|
1429
|
-
markers.push(label);
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
if (renamedSchemaRemoval(patch)) {
|
|
1433
|
-
markers.push("a renamed file drops a column or table");
|
|
1434
|
-
}
|
|
1435
|
-
const bump = MAJOR_BUMP.exec(patch);
|
|
1436
|
-
if (bump && bump[1] !== bump[2]) {
|
|
1437
|
-
markers.push(`major version bump ${bump[1]}.x to ${bump[2]}.x`);
|
|
1438
|
-
}
|
|
1439
|
-
const filesChanged = (patch.match(/^diff --git /gm) ?? []).length;
|
|
1440
|
-
if (markers.length > 0) {
|
|
1441
|
-
return { verdict: "destructive", markers, filesChanged };
|
|
1442
|
-
}
|
|
1443
|
-
return { verdict: "additive", markers: [], filesChanged };
|
|
1444
|
-
}
|
|
1445
|
-
function fetchPatch(pr, opts = {}) {
|
|
1446
|
-
const run5 = opts.exec ?? execFileSync;
|
|
1447
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
1448
|
-
const capture = (file, args) => run5(file, args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 15e3 });
|
|
1449
|
-
for (const ref of [`refs/pull/${pr}/head`, `pull/${pr}/head`]) {
|
|
1450
|
-
try {
|
|
1451
|
-
const base = capture("git", ["merge-base", "HEAD", ref]).trim();
|
|
1452
|
-
if (base) {
|
|
1453
|
-
const patch = capture("git", ["diff", `${base}...${ref}`]);
|
|
1454
|
-
if (patch.trim() !== "") {
|
|
1455
|
-
return { patch, source: "local" };
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
} catch {
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
if (opts.allowNetwork === false) {
|
|
1462
|
-
return { patch: "", source: "none", reason: "no local ref and network reads disabled" };
|
|
1463
|
-
}
|
|
1464
|
-
try {
|
|
1465
|
-
const patch = capture("gh", ["pr", "diff", String(pr), "--patch"]);
|
|
1466
|
-
if (patch.trim() !== "") {
|
|
1467
|
-
return { patch, source: "gh" };
|
|
1468
|
-
}
|
|
1469
|
-
return { patch: "", source: "none", reason: "gh returned an empty diff" };
|
|
1470
|
-
} catch {
|
|
1471
|
-
return { patch: "", source: "none", reason: "no local ref, and gh pr diff failed" };
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
function analyseMergeImpact(pr, opts = {}) {
|
|
1475
|
-
const got = fetchPatch(pr, opts);
|
|
1476
|
-
if (got.source === "none") {
|
|
1477
|
-
return { verdict: "undecidable", markers: [], ...got.reason ? { reason: got.reason } : {} };
|
|
1478
|
-
}
|
|
1479
|
-
return classifyDiff(got.patch);
|
|
1480
|
-
}
|
|
1481
|
-
function describeImpact(pr, impact) {
|
|
1482
|
-
const say = `Say "merge #${pr}" to authorize it.`;
|
|
1483
|
-
switch (impact.verdict) {
|
|
1484
|
-
case "additive":
|
|
1485
|
-
return `Impact scan: PR #${pr} touches ${impact.filesChanged ?? 0} file(s) and trips no destructive marker \u2014 no schema drop, no removed export or route, no major bump, no BREAKING CHANGE. Additive as far as the diff can show, which is not the same as safe to merge: the base has moved since review and merging may trigger a deploy. ${say}`;
|
|
1486
|
-
case "destructive":
|
|
1487
|
-
return `Impact scan: PR #${pr} is NOT additive \u2014 ${impact.markers.join("; ")}. Read the diff before authorizing. ${say}`;
|
|
1488
|
-
default:
|
|
1489
|
-
return `Impact scan: could not read the diff for PR #${pr} (${impact.reason ?? "unknown"}), so nothing is known about what it changes. ${say}`;
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
// src/scripts/hooks/block_unauthorized_git.ts
|
|
1494
|
-
var EXIT_ALLOW3 = 0;
|
|
1495
|
-
var EXIT_BLOCK = 1;
|
|
1496
|
-
function _isObject4(v) {
|
|
1497
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1498
|
-
}
|
|
1525
|
+
// src/scripts/hooks/git_command_classifier.ts
|
|
1499
1526
|
var BLOCK_OPS = /* @__PURE__ */ new Set([
|
|
1500
1527
|
"publish",
|
|
1501
1528
|
"tag",
|
|
@@ -1527,31 +1554,26 @@ var BLOCK_OPS = /* @__PURE__ */ new Set([
|
|
|
1527
1554
|
/** Destroys uncommitted work belonging to a session this one cannot see. */
|
|
1528
1555
|
"worktree-remove",
|
|
1529
1556
|
/** `-x` is the flag that takes `.env` and local certificates. */
|
|
1530
|
-
"clean-ignored"
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
"commit",
|
|
1534
|
-
"push",
|
|
1535
|
-
"pr-create",
|
|
1536
|
-
"branch",
|
|
1537
|
-
// ── Added 2026-09-03, deliberately at warn rather than block.
|
|
1557
|
+
"clean-ignored",
|
|
1558
|
+
// ── The consequence vocabulary, added 2026-09-06. Every one is already
|
|
1559
|
+
// never-autonomous in the `non-destructive-by-default` prose.
|
|
1538
1560
|
//
|
|
1539
|
-
//
|
|
1540
|
-
//
|
|
1541
|
-
//
|
|
1542
|
-
//
|
|
1543
|
-
//
|
|
1544
|
-
|
|
1545
|
-
"
|
|
1546
|
-
"
|
|
1547
|
-
"
|
|
1548
|
-
"
|
|
1549
|
-
"
|
|
1550
|
-
"
|
|
1551
|
-
"close"
|
|
1561
|
+
// MEMBERSHIP HERE IS A COUNTING DECISION, NOT A REFUSAL. The set's name
|
|
1562
|
+
// predates ADR-254, which deleted the reader that blocked: the one consumer
|
|
1563
|
+
// that acts on it now is `conformance_scan`'s after-the-fact tally, so
|
|
1564
|
+
// "blocked" describes what the Hard Floor requires of the model, never what
|
|
1565
|
+
// this package does at tool-call time.
|
|
1566
|
+
"prod-deploy",
|
|
1567
|
+
"prod-data-destroy",
|
|
1568
|
+
"prod-infra-change",
|
|
1569
|
+
"external-send",
|
|
1570
|
+
"money-movement",
|
|
1571
|
+
"trunk-force-push",
|
|
1572
|
+
"out-of-scope-destruction"
|
|
1552
1573
|
]);
|
|
1553
1574
|
var G = "(?:-[A-Za-z-]+(?:=\\S+)?(?:\\s+\\S+)?\\s+)*";
|
|
1554
1575
|
var P = "^(?:\\S*\\/)?";
|
|
1576
|
+
var CONSEQUENCE_TOOL_HEAD = /^(?:\S*\/)?(terraform|pulumi|kubectl|helm|aws|gcloud|az|vercel|netlify|flyctl|fly|wrangler|serverless|sls|eb|heroku|cap|psql|mysql|mariadb|sqlite3|mongosh|mongo|redis-cli|sendmail|msmtp|mail|mailx|curl|stripe|braintree|paypal|rm)\b/i;
|
|
1555
1577
|
function ghApiWrite(pathRe) {
|
|
1556
1578
|
return new RegExp(
|
|
1557
1579
|
`${P}gh\\s+api\\b(?=[^\\n]*${pathRe})(?=[^\\n]*(?:-X|--method)\\s+(?:POST|PATCH|PUT|DELETE)\\b)`,
|
|
@@ -1632,6 +1654,13 @@ var COMMAND_OPS = [
|
|
|
1632
1654
|
// Above `push`, and both spellings: `--force` and `--force-with-lease`. The
|
|
1633
1655
|
// "safe force" is in the same class here because what it is safe against is a
|
|
1634
1656
|
// stale local view, not a collaborator who pushed after your fetch.
|
|
1657
|
+
{
|
|
1658
|
+
op: "trunk-force-push",
|
|
1659
|
+
re: new RegExp(
|
|
1660
|
+
`${P}git\\s+${G}push\\b[^\\n;|&]*(--force\\b|--force-with-lease\\b|\\s-f\\b)[^\\n;|&]*\\b(main|master|trunk|production|prod)\\b`,
|
|
1661
|
+
"i"
|
|
1662
|
+
)
|
|
1663
|
+
},
|
|
1635
1664
|
{
|
|
1636
1665
|
op: "force-push",
|
|
1637
1666
|
re: new RegExp(`${P}git\\s+${G}push\\b[^\\n;|&]*(--force\\b|--force-with-lease\\b|\\s-f\\b)`, "i")
|
|
@@ -1660,37 +1689,64 @@ var COMMAND_OPS = [
|
|
|
1660
1689
|
)
|
|
1661
1690
|
},
|
|
1662
1691
|
{ op: "close", re: new RegExp(`${P}gh\\s+(pr|issue)\\s+close\\b`, "i") },
|
|
1692
|
+
// ── Consequence operations, added 2026-09-06
|
|
1693
|
+
// (`road-to-authorization-that-reaches-further` 2.1).
|
|
1694
|
+
//
|
|
1695
|
+
// ORDER: above the generic `push`/`commit` fallbacks, because
|
|
1696
|
+
// `trunk-force-push` is a `git push` and the most specific pattern must win —
|
|
1697
|
+
// the rule this table states at its head.
|
|
1698
|
+
//
|
|
1699
|
+
// These classify commands the count could not see before. They add NOTHING to
|
|
1700
|
+
// any refusal path: `commandOp` and `BLOCK_OPS` have exactly one consumer
|
|
1701
|
+
// that acts on them, the after-the-fact tally in `conformance_scan`, and this
|
|
1702
|
+
// change does not add a second.
|
|
1703
|
+
{
|
|
1704
|
+
op: "prod-data-destroy",
|
|
1705
|
+
re: new RegExp(
|
|
1706
|
+
`${P}(psql|mysql|mariadb|sqlite3|mongosh?)\\b[^\\n]*\\b(drop\\s+(table|database|schema)|truncate|delete\\s+from)\\b|${P}aws\\s+s3\\s+(rm|rb)\\b|${P}aws\\s+(dynamodb|rds)\\s+delete-\\S+|${P}redis-cli\\b[^\\n]*\\bflush(all|db)\\b`,
|
|
1707
|
+
"i"
|
|
1708
|
+
)
|
|
1709
|
+
},
|
|
1710
|
+
{
|
|
1711
|
+
op: "prod-infra-change",
|
|
1712
|
+
re: new RegExp(
|
|
1713
|
+
`${P}terraform\\s+(apply|destroy)\\b|${P}pulumi\\s+(up|destroy)\\b|${P}kubectl\\s+(apply|delete|scale|drain)\\b|${P}helm\\s+(install|upgrade|uninstall|rollback)\\b|${P}aws\\s+(iam|ec2|route53|elbv2)\\s+(create|delete|put|update|modify|attach|detach)-\\S+|${P}(gcloud|az)\\s+\\S+\\s+(create|delete|update)\\b`,
|
|
1714
|
+
"i"
|
|
1715
|
+
)
|
|
1716
|
+
},
|
|
1717
|
+
{
|
|
1718
|
+
op: "prod-deploy",
|
|
1719
|
+
re: new RegExp(
|
|
1720
|
+
`${P}(vercel|netlify|flyctl|fly|wrangler|serverless|sls|eb|heroku)\\s+${G}deploy\\b|${P}(vercel|netlify)\\b[^\\n]*--prod\\b|${P}(npm|pnpm|yarn)\\s+run\\s+deploy\\b|${P}cap\\s+production\\s+deploy\\b`,
|
|
1721
|
+
"i"
|
|
1722
|
+
)
|
|
1723
|
+
},
|
|
1724
|
+
{
|
|
1725
|
+
op: "external-send",
|
|
1726
|
+
re: new RegExp(
|
|
1727
|
+
`${P}(sendmail|msmtp|mailx?)\\b|${P}curl\\b(?=[^\\n]*(?:-X|--request)\\s+(?:POST|PUT|PATCH)\\b)(?=[^\\n]*https?:\\/\\/)`,
|
|
1728
|
+
"i"
|
|
1729
|
+
)
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
op: "money-movement",
|
|
1733
|
+
re: new RegExp(
|
|
1734
|
+
`${P}(stripe|braintree|paypal)\\s+\\S*(charge|refund|payout|transfer|payment)\\S*\\b`,
|
|
1735
|
+
"i"
|
|
1736
|
+
)
|
|
1737
|
+
},
|
|
1738
|
+
{
|
|
1739
|
+
// `rm -rf` on an ABSOLUTE path or a home-relative one. Deliberately not
|
|
1740
|
+
// "outside the working tree" — this function receives a command string and
|
|
1741
|
+
// no cwd, so that reading is not decidable here and a classifier that
|
|
1742
|
+
// guesses is worse than one that under-reports.
|
|
1743
|
+
op: "out-of-scope-destruction",
|
|
1744
|
+
re: new RegExp(`${P}rm\\s+(-\\S+\\s+)*-\\S*[rR]\\S*\\s+(-\\S+\\s+)*(~|/)\\S*`, "i")
|
|
1745
|
+
},
|
|
1663
1746
|
{ op: "push", re: new RegExp(`${P}git\\s+${G}push\\b`, "i") },
|
|
1664
1747
|
{ op: "commit", re: new RegExp(`${P}git\\s+${G}commit\\b`, "i") },
|
|
1665
1748
|
{ op: "branch", re: new RegExp(`${P}git\\s+${G}(checkout\\s+-b|switch\\s+-c)\\b`, "i") }
|
|
1666
1749
|
];
|
|
1667
|
-
var COMMAND_TOOLS = /* @__PURE__ */ new Set([
|
|
1668
|
-
"launch-process",
|
|
1669
|
-
"launch_process",
|
|
1670
|
-
"Bash",
|
|
1671
|
-
"BashTool",
|
|
1672
|
-
"run-process",
|
|
1673
|
-
"runProcess",
|
|
1674
|
-
"shell",
|
|
1675
|
-
"execute_shell",
|
|
1676
|
-
"RunShellCommand"
|
|
1677
|
-
]);
|
|
1678
|
-
function extractCommand(envelope) {
|
|
1679
|
-
const payload = _isObject4(envelope["payload"]) ? envelope["payload"] : envelope;
|
|
1680
|
-
const toolRaw = payload["tool_name"] ?? payload["toolName"] ?? payload["tool"];
|
|
1681
|
-
const tool = typeof toolRaw === "string" ? toolRaw : null;
|
|
1682
|
-
if (tool !== null && !COMMAND_TOOLS.has(tool)) {
|
|
1683
|
-
return null;
|
|
1684
|
-
}
|
|
1685
|
-
const input = _isObject4(payload["tool_input"]) ? payload["tool_input"] : _isObject4(payload["toolInput"]) ? payload["toolInput"] : payload;
|
|
1686
|
-
for (const key of ["command", "cmd", "script"]) {
|
|
1687
|
-
const v = input[key];
|
|
1688
|
-
if (typeof v === "string" && v.trim()) {
|
|
1689
|
-
return v;
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
return null;
|
|
1693
|
-
}
|
|
1694
1750
|
function splitOutsideQuotes(command) {
|
|
1695
1751
|
const parts = [];
|
|
1696
1752
|
let cur = "";
|
|
@@ -1837,7 +1893,7 @@ function invokedSegments(command, depth = 0) {
|
|
|
1837
1893
|
function commandOp(command) {
|
|
1838
1894
|
let found = null;
|
|
1839
1895
|
for (const seg of invokedSegments(command)) {
|
|
1840
|
-
if (!/^(?:\S*\/)?(git|gh|npm|pnpm|yarn)\b/.test(seg)) {
|
|
1896
|
+
if (!/^(?:\S*\/)?(git|gh|npm|pnpm|yarn)\b/.test(seg) && !CONSEQUENCE_TOOL_HEAD.test(seg)) {
|
|
1841
1897
|
continue;
|
|
1842
1898
|
}
|
|
1843
1899
|
for (const { op, re } of COMMAND_OPS) {
|
|
@@ -1852,191 +1908,50 @@ function commandOp(command) {
|
|
|
1852
1908
|
}
|
|
1853
1909
|
return found;
|
|
1854
1910
|
}
|
|
1855
|
-
function
|
|
1856
|
-
const
|
|
1857
|
-
if (
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
const n = Number.parseInt(digits, 10);
|
|
1865
|
-
return Number.isSafeInteger(n) && n > 0 && n <= PR_NUMBER_MAX ? n : null;
|
|
1866
|
-
}
|
|
1867
|
-
function grantCovers(grants, op, target) {
|
|
1868
|
-
if (op !== "pr-merge" || target === null) {
|
|
1869
|
-
return null;
|
|
1870
|
-
}
|
|
1871
|
-
return grants.find(
|
|
1872
|
-
(g) => g.op === "pr-merge" && g.targets.includes(target) && !g.consumed.includes(target)
|
|
1873
|
-
) ?? null;
|
|
1874
|
-
}
|
|
1875
|
-
var LEDGER_MAX_AGE_MS = 30 * 60 * 1e3;
|
|
1876
|
-
function _loadLedger(consumer_root, session_id, now) {
|
|
1877
|
-
const grants = readGrants(consumer_root, session_id);
|
|
1878
|
-
const candidates = [ledgerFileFor(session_id)];
|
|
1879
|
-
if (!candidates.includes(STATE_FILE2)) {
|
|
1880
|
-
candidates.push(STATE_FILE2);
|
|
1881
|
-
}
|
|
1882
|
-
for (const rel of candidates) {
|
|
1883
|
-
const found = _readLedgerFile(path3.join(consumer_root, rel), session_id, now);
|
|
1884
|
-
if (found !== null) {
|
|
1885
|
-
return { ...found, grants };
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
return { authorized: /* @__PURE__ */ new Set(), present: false, grants };
|
|
1889
|
-
}
|
|
1890
|
-
function _readLedgerFile(file, session_id, now) {
|
|
1891
|
-
try {
|
|
1892
|
-
const raw = fs3.readFileSync(file, "utf8");
|
|
1893
|
-
const decoded = JSON.parse(raw);
|
|
1894
|
-
if (_isObject4(decoded) && Array.isArray(decoded["authorized"])) {
|
|
1895
|
-
const ledgerSession = typeof decoded["session_id"] === "string" ? decoded["session_id"] : "";
|
|
1896
|
-
if (session_id && ledgerSession && ledgerSession !== session_id) {
|
|
1897
|
-
return null;
|
|
1898
|
-
}
|
|
1899
|
-
const at = Date.parse(String(decoded["detected_at"] ?? ""));
|
|
1900
|
-
if (Number.isFinite(at) && now - at > LEDGER_MAX_AGE_MS) {
|
|
1901
|
-
return null;
|
|
1902
|
-
}
|
|
1903
|
-
return {
|
|
1904
|
-
authorized: new Set(decoded["authorized"].filter(Boolean)),
|
|
1905
|
-
present: true
|
|
1906
|
-
};
|
|
1911
|
+
function commandObject(op, command) {
|
|
1912
|
+
const patterns = COMMAND_OBJECTS[op];
|
|
1913
|
+
if (!patterns) return null;
|
|
1914
|
+
for (const re of patterns) {
|
|
1915
|
+
const m = new RegExp(re.source, re.flags.replace("g", "")).exec(command);
|
|
1916
|
+
const raw = m?.[1];
|
|
1917
|
+
if (raw) {
|
|
1918
|
+
const value = raw.trim().replace(/^["'`]+|["'`]+$/g, "").replace(/[.,;:]+$/, "").toLowerCase();
|
|
1919
|
+
if (value) return value;
|
|
1907
1920
|
}
|
|
1908
|
-
} catch {
|
|
1909
1921
|
}
|
|
1910
1922
|
return null;
|
|
1911
1923
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
}
|
|
1931
|
-
if (WARN_OPS.has(op)) {
|
|
1932
|
-
const reason = `No \`${op}\` authorization found in this turn's prompt (commit-policy: a one-shot authorization is spent on the operation it named). If the user did authorize it this turn, proceed; otherwise stop and ask. Command: ${shown}`;
|
|
1933
|
-
return {
|
|
1934
|
-
exit: EXIT_ALLOW3,
|
|
1935
|
-
stdout: `${JSON.stringify({ decision: "warn", reason })}
|
|
1936
|
-
`,
|
|
1937
|
-
stderr: ""
|
|
1938
|
-
};
|
|
1939
|
-
}
|
|
1940
|
-
return { exit: EXIT_ALLOW3, stdout: "", stderr: "" };
|
|
1941
|
-
}
|
|
1942
|
-
function run3(stdin_text, options) {
|
|
1943
|
-
let envelope = {};
|
|
1944
|
-
if (stdin_text.trim()) {
|
|
1945
|
-
try {
|
|
1946
|
-
const decoded = JSON.parse(stdin_text);
|
|
1947
|
-
if (_isObject4(decoded)) {
|
|
1948
|
-
envelope = decoded;
|
|
1949
|
-
}
|
|
1950
|
-
} catch {
|
|
1951
|
-
return EXIT_ALLOW3;
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
const session_id = typeof envelope["session_id"] === "string" ? envelope["session_id"] : "";
|
|
1955
|
-
const command = extractCommand(envelope);
|
|
1956
|
-
const ledger = _loadLedger(options.consumer_root, session_id, Date.now());
|
|
1957
|
-
let impact;
|
|
1958
|
-
const stage1 = command === null ? null : commandOp(command);
|
|
1959
|
-
const stage2Target = command === null ? null : mergeTargetOf(command);
|
|
1960
|
-
if (stage1 === "pr-merge" && stage2Target !== null && !ledger.authorized.has("pr-merge") && !grantCovers(ledger.grants ?? [], "pr-merge", stage2Target)) {
|
|
1961
|
-
try {
|
|
1962
|
-
impact = describeImpact(
|
|
1963
|
-
stage2Target,
|
|
1964
|
-
analyseMergeImpact(stage2Target, { cwd: options.consumer_root })
|
|
1965
|
-
);
|
|
1966
|
-
} catch {
|
|
1967
|
-
}
|
|
1968
|
-
}
|
|
1969
|
-
const decision = decide(command, ledger, impact);
|
|
1970
|
-
if (decision.consumedTarget !== void 0) {
|
|
1971
|
-
consumeGrantTarget(options.consumer_root, session_id, decision.consumedTarget);
|
|
1972
|
-
}
|
|
1973
|
-
if (decision.blockedOp) {
|
|
1974
|
-
try {
|
|
1975
|
-
atomic_write_json(path3.join(options.consumer_root, pendingFileFor(session_id)), {
|
|
1976
|
-
op: decision.blockedOp,
|
|
1977
|
-
session_id,
|
|
1978
|
-
refused_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1979
|
-
});
|
|
1980
|
-
} catch {
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
if (decision.stdout) {
|
|
1984
|
-
process.stdout.write(decision.stdout);
|
|
1985
|
-
}
|
|
1986
|
-
if (decision.stderr) {
|
|
1987
|
-
process.stderr.write(decision.stderr);
|
|
1988
|
-
}
|
|
1989
|
-
return decision.exit;
|
|
1990
|
-
}
|
|
1991
|
-
function main3(argv) {
|
|
1992
|
-
const args = argv ?? process.argv.slice(2);
|
|
1993
|
-
let consumer_root = process.cwd();
|
|
1994
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
1995
|
-
const a = args[i];
|
|
1996
|
-
if (a === "--project-dir" && args[i + 1] !== void 0) {
|
|
1997
|
-
consumer_root = args[i + 1];
|
|
1998
|
-
i += 1;
|
|
1999
|
-
} else if (a !== void 0 && a.startsWith("--project-dir=")) {
|
|
2000
|
-
consumer_root = a.slice("--project-dir=".length);
|
|
2001
|
-
}
|
|
2002
|
-
}
|
|
2003
|
-
return run3(readHookStdin(), { consumer_root });
|
|
2004
|
-
}
|
|
2005
|
-
function _isCliEntry3() {
|
|
2006
|
-
if (true) {
|
|
2007
|
-
return false;
|
|
2008
|
-
}
|
|
2009
|
-
if (process.argv[1] === void 0) {
|
|
2010
|
-
return false;
|
|
2011
|
-
}
|
|
2012
|
-
const argvUrl = pathToFileURL3(path3.resolve(process.argv[1])).href;
|
|
2013
|
-
if (import.meta.url === argvUrl) {
|
|
2014
|
-
return true;
|
|
2015
|
-
}
|
|
2016
|
-
try {
|
|
2017
|
-
const here = fs3.realpathSync(fileURLToPath3(import.meta.url));
|
|
2018
|
-
const argv = fs3.realpathSync(path3.resolve(process.argv[1]));
|
|
2019
|
-
return here === argv;
|
|
2020
|
-
} catch {
|
|
2021
|
-
return false;
|
|
2022
|
-
}
|
|
2023
|
-
}
|
|
2024
|
-
if (_isCliEntry3()) {
|
|
2025
|
-
process.exit(main3());
|
|
2026
|
-
}
|
|
1924
|
+
var COMMAND_OBJECTS = {
|
|
1925
|
+
"prod-data-destroy": [
|
|
1926
|
+
/\bdrop\s+(?:table|database|schema|collection)\s+(?:if\s+exists\s+)?["'`]?([\w.$-]+)/i,
|
|
1927
|
+
/\btruncate\s+(?:table\s+)?["'`]?([\w.$-]+)/i,
|
|
1928
|
+
/\bdelete\s+from\s+["'`]?([\w.$-]+)/i,
|
|
1929
|
+
/\bs3:\/\/([\w.-]+)/i,
|
|
1930
|
+
/--table-name[= ]["'`]?([\w.-]+)/i
|
|
1931
|
+
],
|
|
1932
|
+
"external-send": [
|
|
1933
|
+
/\bhttps?:\/\/([^\s/"'`]+)/i,
|
|
1934
|
+
/([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/
|
|
1935
|
+
],
|
|
1936
|
+
"money-movement": [
|
|
1937
|
+
/--customer[= ]["'`]?([\w.-]+)/i,
|
|
1938
|
+
/--amount[= ]["'`]?([\d.,]+)/i,
|
|
1939
|
+
/\b(\d[\d.,]*)\s*(?:eur|usd)\b/i
|
|
1940
|
+
]
|
|
1941
|
+
};
|
|
2027
1942
|
|
|
2028
1943
|
// src/scripts/hooks/turn_end_gate_hook.ts
|
|
2029
|
-
import * as
|
|
2030
|
-
import * as
|
|
2031
|
-
import { fileURLToPath as
|
|
1944
|
+
import * as fs5 from "node:fs";
|
|
1945
|
+
import * as path5 from "node:path";
|
|
1946
|
+
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL5 } from "node:url";
|
|
2032
1947
|
|
|
2033
1948
|
// src/scripts/hooks/end_review_nudge_hook.ts
|
|
2034
1949
|
import { spawnSync } from "node:child_process";
|
|
2035
1950
|
import * as crypto from "node:crypto";
|
|
2036
|
-
import * as
|
|
1951
|
+
import * as fs3 from "node:fs";
|
|
2037
1952
|
import * as os from "node:os";
|
|
2038
|
-
import * as
|
|
2039
|
-
import { fileURLToPath as
|
|
1953
|
+
import * as path3 from "node:path";
|
|
1954
|
+
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "node:url";
|
|
2040
1955
|
|
|
2041
1956
|
// src/scripts/_lib/review_skipped_record.ts
|
|
2042
1957
|
var PRODUCER_PRIVACY_CLASS = "ids-only";
|
|
@@ -2091,11 +2006,11 @@ var ENVELOPE_KEYS = [
|
|
|
2091
2006
|
"event",
|
|
2092
2007
|
"payload"
|
|
2093
2008
|
];
|
|
2094
|
-
function
|
|
2009
|
+
function _isObject4(obj) {
|
|
2095
2010
|
return typeof obj === "object" && obj !== null && !Array.isArray(obj);
|
|
2096
2011
|
}
|
|
2097
2012
|
function looks_like_envelope(obj) {
|
|
2098
|
-
if (!
|
|
2013
|
+
if (!_isObject4(obj)) {
|
|
2099
2014
|
return false;
|
|
2100
2015
|
}
|
|
2101
2016
|
return ENVELOPE_KEYS.every((key) => key in obj);
|
|
@@ -2114,14 +2029,14 @@ function unwrap(stdin_text, default_platform = "generic") {
|
|
|
2114
2029
|
if (looks_like_envelope(decoded)) {
|
|
2115
2030
|
const env = decoded;
|
|
2116
2031
|
let payload2 = env["payload"];
|
|
2117
|
-
if (!payload2 || !
|
|
2032
|
+
if (!payload2 || !_isObject4(payload2)) {
|
|
2118
2033
|
payload2 = {};
|
|
2119
2034
|
}
|
|
2120
2035
|
const platRaw = env["platform"];
|
|
2121
2036
|
const platform = String(platRaw || default_platform);
|
|
2122
2037
|
return [env, payload2, platform];
|
|
2123
2038
|
}
|
|
2124
|
-
const payload =
|
|
2039
|
+
const payload = _isObject4(decoded) ? decoded : {};
|
|
2125
2040
|
return [
|
|
2126
2041
|
{
|
|
2127
2042
|
schema_version: 1,
|
|
@@ -2147,13 +2062,13 @@ function isSafeTranscriptPath(rawPath, opts = {}) {
|
|
|
2147
2062
|
const homeDir = opts.homeDir ?? os.homedir();
|
|
2148
2063
|
const maxBytes = opts.maxBytes ?? TRANSCRIPT_SCAN_MAX_BYTES;
|
|
2149
2064
|
try {
|
|
2150
|
-
const realHome =
|
|
2151
|
-
const realPath =
|
|
2152
|
-
const relative2 =
|
|
2153
|
-
if (relative2 === "" || relative2.startsWith("..") ||
|
|
2065
|
+
const realHome = fs3.realpathSync(homeDir);
|
|
2066
|
+
const realPath = fs3.realpathSync(rawPath);
|
|
2067
|
+
const relative2 = path3.relative(realHome, realPath);
|
|
2068
|
+
if (relative2 === "" || relative2.startsWith("..") || path3.isAbsolute(relative2)) {
|
|
2154
2069
|
return false;
|
|
2155
2070
|
}
|
|
2156
|
-
const { size } =
|
|
2071
|
+
const { size } = fs3.statSync(realPath);
|
|
2157
2072
|
return size <= maxBytes;
|
|
2158
2073
|
} catch {
|
|
2159
2074
|
return false;
|
|
@@ -2212,13 +2127,13 @@ var BINARY_PROBE_BYTES = 8e3;
|
|
|
2212
2127
|
var UNTRACKED_FILE_READ_CAP_BYTES = 8 * 1024 * 1024;
|
|
2213
2128
|
function untrackedFileLineCount(cwd, relPath) {
|
|
2214
2129
|
try {
|
|
2215
|
-
const abs =
|
|
2216
|
-
const size =
|
|
2130
|
+
const abs = path3.resolve(cwd, relPath);
|
|
2131
|
+
const size = fs3.statSync(abs).size;
|
|
2217
2132
|
if (size === 0) return 0;
|
|
2218
2133
|
if (size > UNTRACKED_FILE_READ_CAP_BYTES) {
|
|
2219
2134
|
return MUTATION_LINE_THRESHOLD + 1;
|
|
2220
2135
|
}
|
|
2221
|
-
const buf =
|
|
2136
|
+
const buf = fs3.readFileSync(abs);
|
|
2222
2137
|
if (buf.length === 0) return 0;
|
|
2223
2138
|
const probe = buf.subarray(0, Math.min(buf.length, BINARY_PROBE_BYTES));
|
|
2224
2139
|
if (probe.includes(0)) return 0;
|
|
@@ -2308,19 +2223,19 @@ function appendReviewSkippedTelemetry(workspaceRoot, diffLines, mutationMeasure)
|
|
|
2308
2223
|
});
|
|
2309
2224
|
if (errors.length || line === null) return;
|
|
2310
2225
|
try {
|
|
2311
|
-
const dir =
|
|
2312
|
-
|
|
2313
|
-
const file =
|
|
2314
|
-
|
|
2226
|
+
const dir = path3.join(workspaceRoot, "agents", "runtime", "state", "audit");
|
|
2227
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
2228
|
+
const file = path3.join(dir, `${ts.slice(0, 7)}.jsonl`);
|
|
2229
|
+
fs3.appendFileSync(file, `${JSON.stringify(line)}
|
|
2315
2230
|
`, "utf8");
|
|
2316
2231
|
} catch {
|
|
2317
2232
|
}
|
|
2318
2233
|
}
|
|
2319
2234
|
function sessionStateDir(workspaceRoot) {
|
|
2320
|
-
return
|
|
2235
|
+
return path3.join(workspaceRoot, "agents", "runtime", "state", "end-review-nudge");
|
|
2321
2236
|
}
|
|
2322
2237
|
function sessionStateFile(workspaceRoot, sessionKey) {
|
|
2323
|
-
return
|
|
2238
|
+
return path3.join(sessionStateDir(workspaceRoot), `${sessionKey}.json`);
|
|
2324
2239
|
}
|
|
2325
2240
|
function deriveSessionKey2(envelope, payload) {
|
|
2326
2241
|
const raw = str(envelope["session_id"]) || str(payload["transcript_path"] ?? payload["transcriptPath"]);
|
|
@@ -2329,7 +2244,7 @@ function deriveSessionKey2(envelope, payload) {
|
|
|
2329
2244
|
}
|
|
2330
2245
|
function hasFiredThisSession(workspaceRoot, sessionKey) {
|
|
2331
2246
|
try {
|
|
2332
|
-
return
|
|
2247
|
+
return fs3.existsSync(sessionStateFile(workspaceRoot, sessionKey));
|
|
2333
2248
|
} catch {
|
|
2334
2249
|
return false;
|
|
2335
2250
|
}
|
|
@@ -2343,7 +2258,7 @@ function markFiredThisSession(workspaceRoot, sessionKey, ts) {
|
|
|
2343
2258
|
function _read_stdin() {
|
|
2344
2259
|
return readHookStdin();
|
|
2345
2260
|
}
|
|
2346
|
-
function
|
|
2261
|
+
function main3() {
|
|
2347
2262
|
const [envelope, payload] = unwrap(_read_stdin(), "claude");
|
|
2348
2263
|
const event = String(envelope["event"] ?? "");
|
|
2349
2264
|
if (event !== "" && event !== "stop") {
|
|
@@ -2372,7 +2287,7 @@ function main4() {
|
|
|
2372
2287
|
let reviewerRan = false;
|
|
2373
2288
|
if (transcriptPath && isSafeTranscriptPath(transcriptPath)) {
|
|
2374
2289
|
try {
|
|
2375
|
-
reviewerRan = scanTranscriptForReviewer(
|
|
2290
|
+
reviewerRan = scanTranscriptForReviewer(fs3.readFileSync(transcriptPath, "utf-8"));
|
|
2376
2291
|
} catch {
|
|
2377
2292
|
reviewerRan = false;
|
|
2378
2293
|
}
|
|
@@ -2395,34 +2310,34 @@ function main4() {
|
|
|
2395
2310
|
);
|
|
2396
2311
|
return EXIT_WARN2;
|
|
2397
2312
|
}
|
|
2398
|
-
function
|
|
2313
|
+
function _isCliEntry3() {
|
|
2399
2314
|
if (true) {
|
|
2400
2315
|
return false;
|
|
2401
2316
|
}
|
|
2402
2317
|
if (process.argv[1] === void 0) {
|
|
2403
2318
|
return false;
|
|
2404
2319
|
}
|
|
2405
|
-
const argvUrl =
|
|
2320
|
+
const argvUrl = pathToFileURL3(path3.resolve(process.argv[1])).href;
|
|
2406
2321
|
if (import.meta.url === argvUrl) {
|
|
2407
2322
|
return true;
|
|
2408
2323
|
}
|
|
2409
2324
|
try {
|
|
2410
|
-
const here =
|
|
2411
|
-
const argv =
|
|
2325
|
+
const here = fs3.realpathSync(fileURLToPath3(import.meta.url));
|
|
2326
|
+
const argv = fs3.realpathSync(path3.resolve(process.argv[1]));
|
|
2412
2327
|
return here === argv;
|
|
2413
2328
|
} catch {
|
|
2414
2329
|
return false;
|
|
2415
2330
|
}
|
|
2416
2331
|
}
|
|
2417
|
-
if (
|
|
2418
|
-
process.exit(
|
|
2332
|
+
if (_isCliEntry3()) {
|
|
2333
|
+
process.exit(main3());
|
|
2419
2334
|
}
|
|
2420
2335
|
|
|
2421
2336
|
// src/scripts/hooks/subagent_ledger_hook.ts
|
|
2422
2337
|
import * as crypto2 from "node:crypto";
|
|
2423
|
-
import * as
|
|
2424
|
-
import * as
|
|
2425
|
-
import { fileURLToPath as
|
|
2338
|
+
import * as fs4 from "node:fs";
|
|
2339
|
+
import * as path4 from "node:path";
|
|
2340
|
+
import { fileURLToPath as fileURLToPath4, pathToFileURL as pathToFileURL4 } from "node:url";
|
|
2426
2341
|
|
|
2427
2342
|
// src/scripts/_lib/subagent_response.ts
|
|
2428
2343
|
var MAX_SUMMARY_CHARS = 2e3;
|
|
@@ -2524,11 +2439,11 @@ function validateResponse(input) {
|
|
|
2524
2439
|
}
|
|
2525
2440
|
|
|
2526
2441
|
// src/scripts/hooks/subagent_ledger_hook.ts
|
|
2527
|
-
var
|
|
2442
|
+
var EXIT_ALLOW3 = 0;
|
|
2528
2443
|
function isObject2(v) {
|
|
2529
2444
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2530
2445
|
}
|
|
2531
|
-
var LEDGER_DIR =
|
|
2446
|
+
var LEDGER_DIR = path4.join("agents", "runtime", "state", "subagent-ledger");
|
|
2532
2447
|
var OPEN_SUBDIR = "open";
|
|
2533
2448
|
var OPEN_RECORD_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2534
2449
|
function refFor(agentId) {
|
|
@@ -2618,10 +2533,10 @@ function _jsonObjectCandidates(message) {
|
|
|
2618
2533
|
return out;
|
|
2619
2534
|
}
|
|
2620
2535
|
function openDir(root) {
|
|
2621
|
-
return
|
|
2536
|
+
return path4.join(root, LEDGER_DIR, OPEN_SUBDIR);
|
|
2622
2537
|
}
|
|
2623
2538
|
function openFile(root, ref) {
|
|
2624
|
-
return
|
|
2539
|
+
return path4.join(openDir(root), `${ref}.json`);
|
|
2625
2540
|
}
|
|
2626
2541
|
var DEPTH_BASES = /* @__PURE__ */ new Set(["observed", "asserted-parent", "assumed-root"]);
|
|
2627
2542
|
function _asOpenRecord(value, expectedRef) {
|
|
@@ -2648,7 +2563,7 @@ function readOpenRecords(root) {
|
|
|
2648
2563
|
const out = /* @__PURE__ */ new Map();
|
|
2649
2564
|
let names;
|
|
2650
2565
|
try {
|
|
2651
|
-
names =
|
|
2566
|
+
names = fs4.readdirSync(openDir(root));
|
|
2652
2567
|
} catch {
|
|
2653
2568
|
return out;
|
|
2654
2569
|
}
|
|
@@ -2656,7 +2571,7 @@ function readOpenRecords(root) {
|
|
|
2656
2571
|
if (!name.endsWith(".json")) continue;
|
|
2657
2572
|
const expectedRef = name.slice(0, -".json".length);
|
|
2658
2573
|
try {
|
|
2659
|
-
const raw =
|
|
2574
|
+
const raw = fs4.readFileSync(path4.join(openDir(root), name), "utf8");
|
|
2660
2575
|
const rec = _asOpenRecord(JSON.parse(raw), expectedRef);
|
|
2661
2576
|
if (rec) out.set(rec.ref, rec);
|
|
2662
2577
|
} catch {
|
|
@@ -2671,13 +2586,13 @@ function resolveDepth(parentRef, open) {
|
|
|
2671
2586
|
return { depth: parent.depth + 1, depth_basis: "observed" };
|
|
2672
2587
|
}
|
|
2673
2588
|
function ledgerFileFor2(root, ts) {
|
|
2674
|
-
return
|
|
2589
|
+
return path4.join(root, LEDGER_DIR, `${ts.slice(0, 7)}.jsonl`);
|
|
2675
2590
|
}
|
|
2676
2591
|
function appendLedgerLine(root, ts, line) {
|
|
2677
2592
|
if (is_replay_mode()) return;
|
|
2678
2593
|
const file = ledgerFileFor2(root, ts);
|
|
2679
|
-
|
|
2680
|
-
|
|
2594
|
+
fs4.mkdirSync(path4.dirname(file), { recursive: true });
|
|
2595
|
+
fs4.appendFileSync(file, `${JSON.stringify(line)}
|
|
2681
2596
|
`, "utf8");
|
|
2682
2597
|
}
|
|
2683
2598
|
var WALL_CLOCK_ARMS_MS = [
|
|
@@ -2691,7 +2606,7 @@ function armsExceeded(elapsedMs) {
|
|
|
2691
2606
|
return WALL_CLOCK_ARMS_MS.filter((a) => elapsedMs >= a.ms).map((a) => a.label);
|
|
2692
2607
|
}
|
|
2693
2608
|
function openRecordStats(root, nowMs = Date.now()) {
|
|
2694
|
-
const ledgerPresent =
|
|
2609
|
+
const ledgerPresent = fs4.existsSync(path4.join(root, LEDGER_DIR));
|
|
2695
2610
|
const open = readOpenRecords(root);
|
|
2696
2611
|
let maxDepth = 0;
|
|
2697
2612
|
let counted = 0;
|
|
@@ -2719,13 +2634,13 @@ function writeOpenRecord(root, rec) {
|
|
|
2719
2634
|
function removeOpenRecord(root, ref) {
|
|
2720
2635
|
if (is_replay_mode()) return;
|
|
2721
2636
|
try {
|
|
2722
|
-
|
|
2637
|
+
fs4.unlinkSync(openFile(root, ref));
|
|
2723
2638
|
} catch {
|
|
2724
2639
|
}
|
|
2725
2640
|
}
|
|
2726
2641
|
function countOpen(root) {
|
|
2727
2642
|
try {
|
|
2728
|
-
return
|
|
2643
|
+
return fs4.readdirSync(openDir(root)).filter((n) => n.endsWith(".json")).length;
|
|
2729
2644
|
} catch {
|
|
2730
2645
|
return 0;
|
|
2731
2646
|
}
|
|
@@ -2852,9 +2767,9 @@ function handleStop(root, payload, nowIso, sessionId) {
|
|
|
2852
2767
|
}
|
|
2853
2768
|
function processEnvelope(envelope, consumerRoot) {
|
|
2854
2769
|
try {
|
|
2855
|
-
if (!isObject2(envelope)) return
|
|
2770
|
+
if (!isObject2(envelope)) return EXIT_ALLOW3;
|
|
2856
2771
|
const event = envelope["event"];
|
|
2857
|
-
if (event !== "subagent_start" && event !== "subagent_stop") return
|
|
2772
|
+
if (event !== "subagent_start" && event !== "subagent_stop") return EXIT_ALLOW3;
|
|
2858
2773
|
const payload = unwrapPayload(envelope);
|
|
2859
2774
|
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
2860
2775
|
const sessionId = str2(payload, "session_id", "sessionId") ?? str2(envelope, "session_id", "sessionId");
|
|
@@ -2862,9 +2777,9 @@ function processEnvelope(envelope, consumerRoot) {
|
|
|
2862
2777
|
if (event === "subagent_start") handleStart(consumerRoot, payload, nowIso, sessionId);
|
|
2863
2778
|
else handleStop(consumerRoot, payload, nowIso, sessionId);
|
|
2864
2779
|
} catch {
|
|
2865
|
-
return
|
|
2780
|
+
return EXIT_ALLOW3;
|
|
2866
2781
|
}
|
|
2867
|
-
return
|
|
2782
|
+
return EXIT_ALLOW3;
|
|
2868
2783
|
}
|
|
2869
2784
|
function resolveConsumerRoot(envelope) {
|
|
2870
2785
|
if (isObject2(envelope)) {
|
|
@@ -2876,34 +2791,34 @@ function resolveConsumerRoot(envelope) {
|
|
|
2876
2791
|
}
|
|
2877
2792
|
return process.cwd();
|
|
2878
2793
|
}
|
|
2879
|
-
function
|
|
2794
|
+
function main4() {
|
|
2880
2795
|
const raw = readHookStdin();
|
|
2881
2796
|
let envelope = {};
|
|
2882
2797
|
try {
|
|
2883
2798
|
envelope = raw.trim() ? JSON.parse(raw) : {};
|
|
2884
2799
|
} catch {
|
|
2885
|
-
return
|
|
2800
|
+
return EXIT_ALLOW3;
|
|
2886
2801
|
}
|
|
2887
2802
|
return processEnvelope(envelope, resolveConsumerRoot(envelope));
|
|
2888
2803
|
}
|
|
2889
|
-
function
|
|
2804
|
+
function _isCliEntry4() {
|
|
2890
2805
|
if (true) {
|
|
2891
2806
|
return false;
|
|
2892
2807
|
}
|
|
2893
2808
|
if (process.argv[1] === void 0) return false;
|
|
2894
|
-
const argvUrl =
|
|
2809
|
+
const argvUrl = pathToFileURL4(path4.resolve(process.argv[1])).href;
|
|
2895
2810
|
if (import.meta.url === argvUrl) return true;
|
|
2896
2811
|
try {
|
|
2897
|
-
return
|
|
2812
|
+
return fs4.realpathSync(fileURLToPath4(import.meta.url)) === fs4.realpathSync(path4.resolve(process.argv[1]));
|
|
2898
2813
|
} catch {
|
|
2899
2814
|
return false;
|
|
2900
2815
|
}
|
|
2901
2816
|
}
|
|
2902
|
-
if (
|
|
2817
|
+
if (_isCliEntry4()) process.exit(main4());
|
|
2903
2818
|
|
|
2904
2819
|
// src/scripts/hooks/turn_end_gate_hook.ts
|
|
2905
|
-
var
|
|
2906
|
-
var
|
|
2820
|
+
var EXIT_BLOCK = 1;
|
|
2821
|
+
var EXIT_ALLOW4 = 0;
|
|
2907
2822
|
var TRANSCRIPT_READ_MAX_BYTES = 8 * 1024 * 1024;
|
|
2908
2823
|
function stripFencedBlocks(text) {
|
|
2909
2824
|
const out = [];
|
|
@@ -3008,7 +2923,7 @@ function detectPromissory(reply) {
|
|
|
3008
2923
|
function readCiSettled(workspaceRoot, session_id) {
|
|
3009
2924
|
if (!hasStableSessionId(session_id)) return { seen: false, settled: false };
|
|
3010
2925
|
try {
|
|
3011
|
-
const raw =
|
|
2926
|
+
const raw = fs5.readFileSync(path5.join(workspaceRoot, statePathFor(session_id)), "utf-8");
|
|
3012
2927
|
const decoded = JSON.parse(raw);
|
|
3013
2928
|
if (!owns_session_state(decoded, session_id)) return { seen: false, settled: false };
|
|
3014
2929
|
if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
|
|
@@ -3044,8 +2959,8 @@ function detectCompletionClaim(reply, ci) {
|
|
|
3044
2959
|
function readLanguagePin(workspaceRoot, session_id) {
|
|
3045
2960
|
if (!hasStableSessionId(session_id)) return "und";
|
|
3046
2961
|
try {
|
|
3047
|
-
const raw =
|
|
3048
|
-
|
|
2962
|
+
const raw = fs5.readFileSync(
|
|
2963
|
+
path5.join(workspaceRoot, statePathFor2(session_id)),
|
|
3049
2964
|
"utf-8"
|
|
3050
2965
|
);
|
|
3051
2966
|
const decoded = JSON.parse(raw);
|
|
@@ -3101,7 +3016,7 @@ function sessionStateFile2(workspaceRoot, sessionKey) {
|
|
|
3101
3016
|
}
|
|
3102
3017
|
function alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal) {
|
|
3103
3018
|
try {
|
|
3104
|
-
const raw =
|
|
3019
|
+
const raw = fs5.readFileSync(sessionStateFile2(workspaceRoot, sessionKey), "utf-8");
|
|
3105
3020
|
const decoded = JSON.parse(raw);
|
|
3106
3021
|
if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
|
|
3107
3022
|
return decoded["refused_turn"] === turnOrdinal;
|
|
@@ -3115,7 +3030,7 @@ function markRefusedTurn(workspaceRoot, sessionKey, turnOrdinal, detectors) {
|
|
|
3115
3030
|
const file = sessionStateFile2(workspaceRoot, sessionKey);
|
|
3116
3031
|
let prev = null;
|
|
3117
3032
|
try {
|
|
3118
|
-
prev = parseRecord(
|
|
3033
|
+
prev = parseRecord(fs5.readFileSync(file, "utf-8"));
|
|
3119
3034
|
} catch {
|
|
3120
3035
|
}
|
|
3121
3036
|
let version;
|
|
@@ -3142,10 +3057,10 @@ function readTranscriptTail(transcriptPath, opts = {}) {
|
|
|
3142
3057
|
if (!transcriptPath || !isSafeTranscriptPath(transcriptPath, opts)) return empty;
|
|
3143
3058
|
let lines;
|
|
3144
3059
|
try {
|
|
3145
|
-
if (opts.maxBytes !== void 0 &&
|
|
3060
|
+
if (opts.maxBytes !== void 0 && fs5.statSync(transcriptPath).size > opts.maxBytes) {
|
|
3146
3061
|
return empty;
|
|
3147
3062
|
}
|
|
3148
|
-
lines =
|
|
3063
|
+
lines = fs5.readFileSync(transcriptPath, "utf-8").split("\n");
|
|
3149
3064
|
} catch {
|
|
3150
3065
|
return empty;
|
|
3151
3066
|
}
|
|
@@ -3221,15 +3136,15 @@ function _messageText(content) {
|
|
|
3221
3136
|
function str3(v) {
|
|
3222
3137
|
return typeof v === "string" ? v : "";
|
|
3223
3138
|
}
|
|
3224
|
-
function
|
|
3139
|
+
function main5() {
|
|
3225
3140
|
let envelope;
|
|
3226
3141
|
let payload;
|
|
3227
3142
|
try {
|
|
3228
3143
|
[envelope, payload] = unwrap(readHookStdin(), "claude");
|
|
3229
3144
|
} catch {
|
|
3230
|
-
return
|
|
3145
|
+
return EXIT_ALLOW4;
|
|
3231
3146
|
}
|
|
3232
|
-
if (payload["stop_hook_active"] === true) return
|
|
3147
|
+
if (payload["stop_hook_active"] === true) return EXIT_ALLOW4;
|
|
3233
3148
|
const workspaceRoot = str3(envelope["workspace_root"]) || process.cwd();
|
|
3234
3149
|
let dispatchOpen = false;
|
|
3235
3150
|
try {
|
|
@@ -3242,10 +3157,10 @@ function main6() {
|
|
|
3242
3157
|
const { lastAssistant, turnOrdinal, toolCalls } = readTranscriptTail(transcriptPath, {
|
|
3243
3158
|
maxBytes: TRANSCRIPT_READ_MAX_BYTES
|
|
3244
3159
|
});
|
|
3245
|
-
if (!lastAssistant) return
|
|
3160
|
+
if (!lastAssistant) return EXIT_ALLOW4;
|
|
3246
3161
|
const rawSessionId = str3(envelope["session_id"]) || "";
|
|
3247
3162
|
const sessionKey = deriveSessionKey(rawSessionId || "unknown-session");
|
|
3248
|
-
if (alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal)) return
|
|
3163
|
+
if (alreadyRefusedTurn(workspaceRoot, sessionKey, turnOrdinal)) return EXIT_ALLOW4;
|
|
3249
3164
|
const findings = [];
|
|
3250
3165
|
for (const f of [
|
|
3251
3166
|
// A and D are the completion-adjacent pair a pending dispatch excuses
|
|
@@ -3268,7 +3183,7 @@ function main6() {
|
|
|
3268
3183
|
]) {
|
|
3269
3184
|
if (f) findings.push(f);
|
|
3270
3185
|
}
|
|
3271
|
-
if (findings.length === 0) return
|
|
3186
|
+
if (findings.length === 0) return EXIT_ALLOW4;
|
|
3272
3187
|
markRefusedTurn(
|
|
3273
3188
|
workspaceRoot,
|
|
3274
3189
|
sessionKey,
|
|
@@ -3286,40 +3201,40 @@ ${lines.join("\n")}
|
|
|
3286
3201
|
This turn will not be refused a second time.
|
|
3287
3202
|
`
|
|
3288
3203
|
);
|
|
3289
|
-
return
|
|
3204
|
+
return EXIT_BLOCK;
|
|
3290
3205
|
}
|
|
3291
|
-
function
|
|
3206
|
+
function _isCliEntry5() {
|
|
3292
3207
|
if (true) {
|
|
3293
3208
|
return false;
|
|
3294
3209
|
}
|
|
3295
3210
|
if (process.argv[1] === void 0) {
|
|
3296
3211
|
return false;
|
|
3297
3212
|
}
|
|
3298
|
-
const argvUrl =
|
|
3213
|
+
const argvUrl = pathToFileURL5(path5.resolve(process.argv[1])).href;
|
|
3299
3214
|
if (import.meta.url === argvUrl) {
|
|
3300
3215
|
return true;
|
|
3301
3216
|
}
|
|
3302
3217
|
try {
|
|
3303
|
-
const here =
|
|
3304
|
-
const argv =
|
|
3218
|
+
const here = fs5.realpathSync(fileURLToPath5(import.meta.url));
|
|
3219
|
+
const argv = fs5.realpathSync(path5.resolve(process.argv[1]));
|
|
3305
3220
|
return here === argv;
|
|
3306
3221
|
} catch {
|
|
3307
3222
|
return false;
|
|
3308
3223
|
}
|
|
3309
3224
|
}
|
|
3310
|
-
if (
|
|
3311
|
-
process.exit(
|
|
3225
|
+
if (_isCliEntry5()) {
|
|
3226
|
+
process.exit(main5());
|
|
3312
3227
|
}
|
|
3313
3228
|
|
|
3314
3229
|
// src/scripts/hooks/evidence_independence.ts
|
|
3315
3230
|
import crypto3 from "node:crypto";
|
|
3316
|
-
import
|
|
3317
|
-
import
|
|
3318
|
-
import { fileURLToPath as
|
|
3319
|
-
var
|
|
3320
|
-
var
|
|
3321
|
-
var STATE_FILE3 =
|
|
3322
|
-
function
|
|
3231
|
+
import fs6 from "node:fs";
|
|
3232
|
+
import path6 from "node:path";
|
|
3233
|
+
import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL6 } from "node:url";
|
|
3234
|
+
var EXIT_ALLOW5 = 0;
|
|
3235
|
+
var EXIT_BLOCK2 = 1;
|
|
3236
|
+
var STATE_FILE3 = path6.join("agents", "state", "evidence-dispatch.json");
|
|
3237
|
+
function _isObject5(v) {
|
|
3323
3238
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3324
3239
|
}
|
|
3325
3240
|
var DISPATCH_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -3372,10 +3287,10 @@ function preloadedVerdict(prompt) {
|
|
|
3372
3287
|
return null;
|
|
3373
3288
|
}
|
|
3374
3289
|
function extractDispatch(envelope) {
|
|
3375
|
-
const payload =
|
|
3290
|
+
const payload = _isObject5(envelope["payload"]) ? envelope["payload"] : envelope;
|
|
3376
3291
|
const toolRaw = payload["tool_name"] ?? payload["toolName"] ?? payload["tool"];
|
|
3377
3292
|
const tool = typeof toolRaw === "string" ? toolRaw : null;
|
|
3378
|
-
const input =
|
|
3293
|
+
const input = _isObject5(payload["tool_input"]) ? payload["tool_input"] : _isObject5(payload["toolInput"]) ? payload["toolInput"] : payload;
|
|
3379
3294
|
for (const key of ["prompt", "instructions", "task", "description"]) {
|
|
3380
3295
|
const v = input[key];
|
|
3381
3296
|
if (typeof v === "string" && v.trim()) {
|
|
@@ -3386,8 +3301,8 @@ function extractDispatch(envelope) {
|
|
|
3386
3301
|
}
|
|
3387
3302
|
function _ledgerStamp(consumer_root) {
|
|
3388
3303
|
try {
|
|
3389
|
-
const raw =
|
|
3390
|
-
|
|
3304
|
+
const raw = fs6.readFileSync(
|
|
3305
|
+
path6.join(consumer_root, "agents", "state", "git-authorization.json"),
|
|
3391
3306
|
"utf8"
|
|
3392
3307
|
);
|
|
3393
3308
|
const d = JSON.parse(raw);
|
|
@@ -3398,8 +3313,8 @@ function _ledgerStamp(consumer_root) {
|
|
|
3398
3313
|
}
|
|
3399
3314
|
function _load(target) {
|
|
3400
3315
|
try {
|
|
3401
|
-
const decoded = JSON.parse(
|
|
3402
|
-
if (
|
|
3316
|
+
const decoded = JSON.parse(fs6.readFileSync(target, "utf8"));
|
|
3317
|
+
if (_isObject5(decoded) && Array.isArray(decoded["evaluations"])) {
|
|
3403
3318
|
return decoded;
|
|
3404
3319
|
}
|
|
3405
3320
|
} catch {
|
|
@@ -3407,14 +3322,14 @@ function _load(target) {
|
|
|
3407
3322
|
return { session_id: "", turn_started_at: "", evaluations: [] };
|
|
3408
3323
|
}
|
|
3409
3324
|
var WARN_TEXT = "First evaluation dispatch this turn. Two shapes fabricated a binding honest-null in the audited sessions \u2014 check your prompt for both before it goes out: (1) a verdict pre-loaded into the prompt, and (2) a scope narrowed to files you chose. A review you commissioned on your own work is admissible as gate evidence only when the prompt is recorded alongside the verdict.";
|
|
3410
|
-
function
|
|
3325
|
+
function decide(tool, prompt, priorEvaluations) {
|
|
3411
3326
|
if (!isDispatchTool(tool) || !isEvaluationPrompt(prompt)) {
|
|
3412
|
-
return { exit:
|
|
3327
|
+
return { exit: EXIT_ALLOW5, stdout: "", stderr: "", evaluations: priorEvaluations };
|
|
3413
3328
|
}
|
|
3414
3329
|
const preloaded = preloadedVerdict(prompt);
|
|
3415
3330
|
if (preloaded !== null) {
|
|
3416
3331
|
return {
|
|
3417
|
-
exit:
|
|
3332
|
+
exit: EXIT_BLOCK2,
|
|
3418
3333
|
stdout: "",
|
|
3419
3334
|
stderr: `Blocked: this evaluation prompt pre-loads its verdict ("${preloaded}"). That is the construct that produced a fabricated NO-FINDINGS committed as binding gate evidence, over a delta an unsteered pass then found a live critical in. Remove the expectation from the prompt and let the evaluator reach its own verdict.
|
|
3420
3335
|
`,
|
|
@@ -3422,11 +3337,11 @@ function decide2(tool, prompt, priorEvaluations) {
|
|
|
3422
3337
|
};
|
|
3423
3338
|
}
|
|
3424
3339
|
if (!isSelfScoped(prompt)) {
|
|
3425
|
-
return { exit:
|
|
3340
|
+
return { exit: EXIT_ALLOW5, stdout: "", stderr: "", evaluations: priorEvaluations };
|
|
3426
3341
|
}
|
|
3427
3342
|
if (priorEvaluations >= 1) {
|
|
3428
3343
|
return {
|
|
3429
|
-
exit:
|
|
3344
|
+
exit: EXIT_ALLOW5,
|
|
3430
3345
|
stdout: `${JSON.stringify({
|
|
3431
3346
|
decision: "warn",
|
|
3432
3347
|
reason: `Second evaluation dispatch of your own work in this turn. If both passes judge the SAME subject, that is verdict shopping \u2014 commissioning another with a different prompt or scope selects the answer instead of measuring it. Report what the first pass returned, then re-plan. If this is an implementation fan-out rather than a second review, carry on: this check reads prose and cannot tell the two apart with certainty.`
|
|
@@ -3437,26 +3352,26 @@ function decide2(tool, prompt, priorEvaluations) {
|
|
|
3437
3352
|
};
|
|
3438
3353
|
}
|
|
3439
3354
|
return {
|
|
3440
|
-
exit:
|
|
3355
|
+
exit: EXIT_ALLOW5,
|
|
3441
3356
|
stdout: `${JSON.stringify({ decision: "warn", reason: WARN_TEXT })}
|
|
3442
3357
|
`,
|
|
3443
3358
|
stderr: "",
|
|
3444
3359
|
evaluations: priorEvaluations + 1
|
|
3445
3360
|
};
|
|
3446
3361
|
}
|
|
3447
|
-
function
|
|
3362
|
+
function run3(stdin_text, options) {
|
|
3448
3363
|
let envelope = {};
|
|
3449
3364
|
if (stdin_text.trim()) {
|
|
3450
3365
|
try {
|
|
3451
3366
|
const decoded = JSON.parse(stdin_text);
|
|
3452
|
-
if (
|
|
3367
|
+
if (_isObject5(decoded)) {
|
|
3453
3368
|
envelope = decoded;
|
|
3454
3369
|
}
|
|
3455
3370
|
} catch {
|
|
3456
|
-
return
|
|
3371
|
+
return EXIT_ALLOW5;
|
|
3457
3372
|
}
|
|
3458
3373
|
}
|
|
3459
|
-
const target =
|
|
3374
|
+
const target = path6.join(options.consumer_root, STATE_FILE3);
|
|
3460
3375
|
const state = _load(target);
|
|
3461
3376
|
const session_id = typeof envelope["session_id"] === "string" ? envelope["session_id"] : "";
|
|
3462
3377
|
const turnMarker = `${session_id}:${_ledgerStamp(options.consumer_root)}`;
|
|
@@ -3466,7 +3381,7 @@ function run4(stdin_text, options) {
|
|
|
3466
3381
|
state.evaluations = [];
|
|
3467
3382
|
}
|
|
3468
3383
|
const [tool, prompt] = extractDispatch(envelope);
|
|
3469
|
-
const decision =
|
|
3384
|
+
const decision = decide(tool, prompt, state.evaluations.length);
|
|
3470
3385
|
if (decision.evaluations > state.evaluations.length) {
|
|
3471
3386
|
state.evaluations.push({
|
|
3472
3387
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -3475,7 +3390,11 @@ function run4(stdin_text, options) {
|
|
|
3475
3390
|
});
|
|
3476
3391
|
try {
|
|
3477
3392
|
atomic_write_json(target, state);
|
|
3478
|
-
} catch {
|
|
3393
|
+
} catch (err) {
|
|
3394
|
+
process.stderr.write(
|
|
3395
|
+
`evidence-independence: evaluation-count write failed (${_errText3(err)}) \u2014 this turn's count did NOT advance, so a second self-scoped evaluation will not be warned about. The pre-loaded-verdict block is unaffected.
|
|
3396
|
+
`
|
|
3397
|
+
);
|
|
3479
3398
|
}
|
|
3480
3399
|
}
|
|
3481
3400
|
if (decision.stdout) {
|
|
@@ -3486,7 +3405,11 @@ function run4(stdin_text, options) {
|
|
|
3486
3405
|
}
|
|
3487
3406
|
return decision.exit;
|
|
3488
3407
|
}
|
|
3489
|
-
function
|
|
3408
|
+
function _errText3(err) {
|
|
3409
|
+
const t = err instanceof Error ? err.message : String(err);
|
|
3410
|
+
return t.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
3411
|
+
}
|
|
3412
|
+
function main6(argv) {
|
|
3490
3413
|
const args = argv ?? process.argv.slice(2);
|
|
3491
3414
|
let consumer_root = process.cwd();
|
|
3492
3415
|
for (let i = 0; i < args.length; i += 1) {
|
|
@@ -3498,35 +3421,35 @@ function main7(argv) {
|
|
|
3498
3421
|
consumer_root = a.slice("--project-dir=".length);
|
|
3499
3422
|
}
|
|
3500
3423
|
}
|
|
3501
|
-
return
|
|
3424
|
+
return run3(readHookStdin(), { consumer_root });
|
|
3502
3425
|
}
|
|
3503
|
-
function
|
|
3426
|
+
function _isCliEntry6() {
|
|
3504
3427
|
if (true) {
|
|
3505
3428
|
return false;
|
|
3506
3429
|
}
|
|
3507
3430
|
if (process.argv[1] === void 0) {
|
|
3508
3431
|
return false;
|
|
3509
3432
|
}
|
|
3510
|
-
const argvUrl =
|
|
3433
|
+
const argvUrl = pathToFileURL6(path6.resolve(process.argv[1])).href;
|
|
3511
3434
|
if (import.meta.url === argvUrl) {
|
|
3512
3435
|
return true;
|
|
3513
3436
|
}
|
|
3514
3437
|
try {
|
|
3515
|
-
const here =
|
|
3516
|
-
const argv =
|
|
3438
|
+
const here = fs6.realpathSync(fileURLToPath6(import.meta.url));
|
|
3439
|
+
const argv = fs6.realpathSync(path6.resolve(process.argv[1]));
|
|
3517
3440
|
return here === argv;
|
|
3518
3441
|
} catch {
|
|
3519
3442
|
return false;
|
|
3520
3443
|
}
|
|
3521
3444
|
}
|
|
3522
|
-
if (
|
|
3523
|
-
process.exit(
|
|
3445
|
+
if (_isCliEntry6()) {
|
|
3446
|
+
process.exit(main6());
|
|
3524
3447
|
}
|
|
3525
3448
|
|
|
3526
3449
|
// src/scripts/hooks/delegation_nudge_hook.ts
|
|
3527
|
-
import
|
|
3528
|
-
import
|
|
3529
|
-
import { fileURLToPath as
|
|
3450
|
+
import fs7 from "node:fs";
|
|
3451
|
+
import path7 from "node:path";
|
|
3452
|
+
import { fileURLToPath as fileURLToPath7, pathToFileURL as pathToFileURL7 } from "node:url";
|
|
3530
3453
|
|
|
3531
3454
|
// src/scripts/_lib/host_capability.ts
|
|
3532
3455
|
import process2 from "node:process";
|
|
@@ -3855,9 +3778,9 @@ function resolveSubagentRouting(inp) {
|
|
|
3855
3778
|
}
|
|
3856
3779
|
|
|
3857
3780
|
// src/scripts/hooks/delegation_nudge_hook.ts
|
|
3858
|
-
var
|
|
3781
|
+
var EXIT_ALLOW6 = 0;
|
|
3859
3782
|
var EXIT_WARN3 = 2;
|
|
3860
|
-
function
|
|
3783
|
+
function _isObject6(v) {
|
|
3861
3784
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3862
3785
|
}
|
|
3863
3786
|
var FILE_TOKEN_RE = /`?\b[\w][\w.\-]{0,80}\.(ts|tsx|js|jsx|mjs|cjs|py|php|rb|go|rs|java|kt|swift|md|mdx|json|ya?ml|css|scss|less|html|vue|svelte|sql|sh|bash|c|cpp|h|hpp|cs|toml|xml)\b`?/gi;
|
|
@@ -4016,34 +3939,34 @@ function _workspaceRoot(envelope) {
|
|
|
4016
3939
|
}
|
|
4017
3940
|
return process.cwd();
|
|
4018
3941
|
}
|
|
4019
|
-
function
|
|
3942
|
+
function main7() {
|
|
4020
3943
|
let envelope = {};
|
|
4021
3944
|
try {
|
|
4022
3945
|
const raw = readHookStdin();
|
|
4023
3946
|
envelope = raw.trim() ? JSON.parse(raw) : {};
|
|
4024
3947
|
} catch {
|
|
4025
|
-
return
|
|
3948
|
+
return EXIT_ALLOW6;
|
|
4026
3949
|
}
|
|
4027
|
-
const env =
|
|
3950
|
+
const env = _isObject6(envelope) ? envelope : {};
|
|
4028
3951
|
const event = env["event"];
|
|
4029
3952
|
const slot = typeof event === "string" && event !== "" ? event : "user_prompt_submit";
|
|
4030
3953
|
if (slot !== "user_prompt_submit") {
|
|
4031
|
-
return
|
|
3954
|
+
return EXIT_ALLOW6;
|
|
4032
3955
|
}
|
|
4033
|
-
const payload =
|
|
3956
|
+
const payload = _isObject6(env["payload"]) ? env["payload"] : env;
|
|
4034
3957
|
const prompt = _extractPrompt2(payload);
|
|
4035
3958
|
if (!prompt) {
|
|
4036
|
-
return
|
|
3959
|
+
return EXIT_ALLOW6;
|
|
4037
3960
|
}
|
|
4038
3961
|
if (isSyntheticPrompt(prompt)) {
|
|
4039
|
-
return
|
|
3962
|
+
return EXIT_ALLOW6;
|
|
4040
3963
|
}
|
|
4041
3964
|
const root = _workspaceRoot(env);
|
|
4042
3965
|
const platform = env["platform"];
|
|
4043
3966
|
const hostId = typeof platform === "string" && platform ? platform : null;
|
|
4044
3967
|
const result = classifyPrompt(prompt, root, hostId);
|
|
4045
3968
|
if (!result) {
|
|
4046
|
-
return
|
|
3969
|
+
return EXIT_ALLOW6;
|
|
4047
3970
|
}
|
|
4048
3971
|
process.stdout.write(
|
|
4049
3972
|
`${JSON.stringify({
|
|
@@ -4055,31 +3978,31 @@ function main8() {
|
|
|
4055
3978
|
);
|
|
4056
3979
|
return EXIT_WARN3;
|
|
4057
3980
|
}
|
|
4058
|
-
function
|
|
3981
|
+
function _isCliEntry7() {
|
|
4059
3982
|
if (true) {
|
|
4060
3983
|
return false;
|
|
4061
3984
|
}
|
|
4062
3985
|
if (process.argv[1] === void 0) {
|
|
4063
3986
|
return false;
|
|
4064
3987
|
}
|
|
4065
|
-
const argvUrl =
|
|
3988
|
+
const argvUrl = pathToFileURL7(path7.resolve(process.argv[1])).href;
|
|
4066
3989
|
if (import.meta.url === argvUrl) {
|
|
4067
3990
|
return true;
|
|
4068
3991
|
}
|
|
4069
3992
|
try {
|
|
4070
|
-
const here =
|
|
4071
|
-
const argv =
|
|
3993
|
+
const here = fs7.realpathSync(fileURLToPath7(import.meta.url));
|
|
3994
|
+
const argv = fs7.realpathSync(path7.resolve(process.argv[1]));
|
|
4072
3995
|
return here === argv;
|
|
4073
3996
|
} catch {
|
|
4074
3997
|
return false;
|
|
4075
3998
|
}
|
|
4076
3999
|
}
|
|
4077
|
-
if (
|
|
4078
|
-
process.exit(
|
|
4000
|
+
if (_isCliEntry7()) {
|
|
4001
|
+
process.exit(main7());
|
|
4079
4002
|
}
|
|
4080
4003
|
|
|
4081
4004
|
// src/scripts/conformance_scan.ts
|
|
4082
|
-
var REPO_ROOT =
|
|
4005
|
+
var REPO_ROOT = path8.resolve(path8.dirname(fileURLToPath8(import.meta.url)), "..", "..");
|
|
4083
4006
|
var OBSERVED_BAND = { low: 9.1, high: 39.2 };
|
|
4084
4007
|
var BAND_MIN_TURNS = 1978;
|
|
4085
4008
|
var CARRIER_CHANGES = [
|
|
@@ -4163,7 +4086,7 @@ function renderWhy(report, id) {
|
|
|
4163
4086
|
}
|
|
4164
4087
|
return lines.join("\n");
|
|
4165
4088
|
}
|
|
4166
|
-
function
|
|
4089
|
+
function _isObject7(v) {
|
|
4167
4090
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4168
4091
|
}
|
|
4169
4092
|
function userText(entry) {
|
|
@@ -4187,15 +4110,15 @@ function assistantText(entry) {
|
|
|
4187
4110
|
}
|
|
4188
4111
|
function toolUses2(entry) {
|
|
4189
4112
|
const msg = entry["message"];
|
|
4190
|
-
const content =
|
|
4113
|
+
const content = _isObject7(msg) ? msg["content"] : void 0;
|
|
4191
4114
|
if (!Array.isArray(content)) {
|
|
4192
4115
|
return [];
|
|
4193
4116
|
}
|
|
4194
|
-
return content.filter((b) =>
|
|
4117
|
+
return content.filter((b) => _isObject7(b) && b["type"] === "tool_use").map((b) => {
|
|
4195
4118
|
const o = b;
|
|
4196
4119
|
return {
|
|
4197
4120
|
name: String(o["name"] ?? ""),
|
|
4198
|
-
input:
|
|
4121
|
+
input: _isObject7(o["input"]) ? o["input"] : {}
|
|
4199
4122
|
};
|
|
4200
4123
|
});
|
|
4201
4124
|
}
|
|
@@ -4238,6 +4161,7 @@ function scanSession(sessionId, lines) {
|
|
|
4238
4161
|
let pinned = null;
|
|
4239
4162
|
let pendingPoll = null;
|
|
4240
4163
|
let authorized = /* @__PURE__ */ new Set();
|
|
4164
|
+
let authorizedObjects = /* @__PURE__ */ new Map();
|
|
4241
4165
|
let sawPending = false;
|
|
4242
4166
|
let evaluationsThisTurn = 0;
|
|
4243
4167
|
let turnsSincePrompt = 0;
|
|
@@ -4331,7 +4255,11 @@ function scanSession(sessionId, lines) {
|
|
|
4331
4255
|
if (c.language !== "und") {
|
|
4332
4256
|
pinned = c.language;
|
|
4333
4257
|
}
|
|
4334
|
-
|
|
4258
|
+
const classified = classifyAuthorization(ut);
|
|
4259
|
+
authorized = new Set(classified.authorized);
|
|
4260
|
+
authorizedObjects = new Map(
|
|
4261
|
+
classified.authorized.map((op) => [op, new Set(extractObjects(op, ut))])
|
|
4262
|
+
);
|
|
4335
4263
|
sawPending = false;
|
|
4336
4264
|
evaluationsThisTurn = 0;
|
|
4337
4265
|
pendingPoll = null;
|
|
@@ -4397,6 +4325,17 @@ function scanSession(sessionId, lines) {
|
|
|
4397
4325
|
at,
|
|
4398
4326
|
detail: `irreversible \`${op}\` with no authorization in the turn's prompt: ${cmd.slice(0, 110)}`
|
|
4399
4327
|
});
|
|
4328
|
+
} else if (op !== null && authorized.has(op) && OBJECT_REQUIRED_OPS.has(op)) {
|
|
4329
|
+
const object = commandObject(op, cmd);
|
|
4330
|
+
const named = authorizedObjects.get(op);
|
|
4331
|
+
if (object !== null && !(named?.has(object) ?? false)) {
|
|
4332
|
+
report.violations.push({
|
|
4333
|
+
check: "git-authorization",
|
|
4334
|
+
session: sessionId,
|
|
4335
|
+
at,
|
|
4336
|
+
detail: `\`${op}\` authorized, but its object \`${object}\` is not one the turn's prompt named (${named && named.size > 0 ? [...named].join(", ") : "no object named"}): ` + cmd.slice(0, 110)
|
|
4337
|
+
});
|
|
4338
|
+
}
|
|
4400
4339
|
}
|
|
4401
4340
|
if (isCiPoll(cmd)) {
|
|
4402
4341
|
pendingPoll = { at, cmd };
|
|
@@ -4429,7 +4368,7 @@ function scanSession(sessionId, lines) {
|
|
|
4429
4368
|
closeWindow();
|
|
4430
4369
|
return report;
|
|
4431
4370
|
}
|
|
4432
|
-
var DEFAULT_RATE_SERIES =
|
|
4371
|
+
var DEFAULT_RATE_SERIES = path8.join(
|
|
4433
4372
|
REPO_ROOT,
|
|
4434
4373
|
"agents",
|
|
4435
4374
|
"runtime",
|
|
@@ -4438,7 +4377,7 @@ var DEFAULT_RATE_SERIES = path9.join(
|
|
|
4438
4377
|
);
|
|
4439
4378
|
function defaultStore(projectDir) {
|
|
4440
4379
|
const slug = projectStoreSlug(projectDir);
|
|
4441
|
-
return
|
|
4380
|
+
return path8.join(process.env["HOME"] ?? "", ".claude", "projects", slug);
|
|
4442
4381
|
}
|
|
4443
4382
|
function _tokens(chars) {
|
|
4444
4383
|
return Math.round(chars / 4);
|
|
@@ -4449,13 +4388,13 @@ function measureDelivered(projectRulesDir, globalRulesDir) {
|
|
|
4449
4388
|
return {
|
|
4450
4389
|
project: {
|
|
4451
4390
|
dir: projectRulesDir,
|
|
4452
|
-
present:
|
|
4391
|
+
present: fs8.existsSync(projectRulesDir),
|
|
4453
4392
|
files: p.files,
|
|
4454
4393
|
tokens: _tokens(p.chars)
|
|
4455
4394
|
},
|
|
4456
4395
|
global: {
|
|
4457
4396
|
dir: globalRulesDir,
|
|
4458
|
-
present:
|
|
4397
|
+
present: fs8.existsSync(globalRulesDir),
|
|
4459
4398
|
files: g.files,
|
|
4460
4399
|
tokens: _tokens(g.chars)
|
|
4461
4400
|
},
|
|
@@ -4463,13 +4402,13 @@ function measureDelivered(projectRulesDir, globalRulesDir) {
|
|
|
4463
4402
|
};
|
|
4464
4403
|
}
|
|
4465
4404
|
function storeKey(store) {
|
|
4466
|
-
return createHash3("sha256").update(
|
|
4405
|
+
return createHash3("sha256").update(path8.resolve(store)).digest("hex").slice(0, 12);
|
|
4467
4406
|
}
|
|
4468
4407
|
function scanStore(store, limit) {
|
|
4469
|
-
const files =
|
|
4408
|
+
const files = fs8.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => ({ f, m: fs8.statSync(path8.join(store, f)).mtimeMs })).sort((a, b) => b.m - a.m).slice(0, limit).map((x) => x.f);
|
|
4470
4409
|
const per_session = [];
|
|
4471
4410
|
for (const f of files) {
|
|
4472
|
-
const lines =
|
|
4411
|
+
const lines = fs8.readFileSync(path8.join(store, f), "utf8").split("\n");
|
|
4473
4412
|
const r = scanSession(f.replace(/\.jsonl$/, "").slice(0, 8), lines);
|
|
4474
4413
|
if (r.assistant_turns > 0) {
|
|
4475
4414
|
per_session.push(r);
|
|
@@ -4497,10 +4436,10 @@ function scanStore(store, limit) {
|
|
|
4497
4436
|
const task_completeness_rate_pct = completeness_windows === 0 ? 0 : Number((100 * task_completeness / completeness_windows).toFixed(1));
|
|
4498
4437
|
const era = spansCarrierChange(per_session.map((s) => s.first_at));
|
|
4499
4438
|
const delivered = measureDelivered(
|
|
4500
|
-
|
|
4501
|
-
|
|
4439
|
+
path8.join(REPO_ROOT, ".claude", "rules"),
|
|
4440
|
+
path8.join(process.env["HOME"] ?? os2.homedir(), ".claude", "rules")
|
|
4502
4441
|
);
|
|
4503
|
-
const ownStore =
|
|
4442
|
+
const ownStore = path8.resolve(store) === path8.resolve(defaultStore(REPO_ROOT));
|
|
4504
4443
|
return {
|
|
4505
4444
|
scanned_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4506
4445
|
store,
|
|
@@ -4530,8 +4469,8 @@ function scanStore(store, limit) {
|
|
|
4530
4469
|
};
|
|
4531
4470
|
}
|
|
4532
4471
|
function recordRate(file, scanned_at, rate) {
|
|
4533
|
-
|
|
4534
|
-
|
|
4472
|
+
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
4473
|
+
fs8.appendFileSync(file, `${JSON.stringify({ measured_at: scanned_at, ...rate })}
|
|
4535
4474
|
`, "utf8");
|
|
4536
4475
|
}
|
|
4537
4476
|
function render(report) {
|
|
@@ -4630,7 +4569,7 @@ function render(report) {
|
|
|
4630
4569
|
lines.push(" checkbox batching \u2014 measured NOWHERE. ~5 low-severity findings.");
|
|
4631
4570
|
return lines.join("\n");
|
|
4632
4571
|
}
|
|
4633
|
-
function
|
|
4572
|
+
function main8(argv) {
|
|
4634
4573
|
const args = argv ?? process.argv.slice(2);
|
|
4635
4574
|
let store = null;
|
|
4636
4575
|
let limit = 30;
|
|
@@ -4662,14 +4601,14 @@ function main9(argv) {
|
|
|
4662
4601
|
return 2;
|
|
4663
4602
|
}
|
|
4664
4603
|
const resolved = store ?? defaultStore(process.cwd());
|
|
4665
|
-
if (!
|
|
4604
|
+
if (!fs8.existsSync(resolved)) {
|
|
4666
4605
|
process.stderr.write(`conformance:behavior: no transcript store at ${resolved}
|
|
4667
4606
|
`);
|
|
4668
4607
|
return 1;
|
|
4669
4608
|
}
|
|
4670
4609
|
const report = scanStore(resolved, Number.isFinite(limit) && limit > 0 ? limit : 30);
|
|
4671
4610
|
if (out) {
|
|
4672
|
-
|
|
4611
|
+
fs8.writeFileSync(out, `${JSON.stringify(report, null, 2)}
|
|
4673
4612
|
`, "utf8");
|
|
4674
4613
|
}
|
|
4675
4614
|
if (record) {
|
|
@@ -4677,7 +4616,7 @@ function main9(argv) {
|
|
|
4677
4616
|
}
|
|
4678
4617
|
if (why !== null) {
|
|
4679
4618
|
const id = why;
|
|
4680
|
-
|
|
4619
|
+
fs8.writeSync(
|
|
4681
4620
|
1,
|
|
4682
4621
|
json ? `${JSON.stringify(
|
|
4683
4622
|
{
|
|
@@ -4697,48 +4636,48 @@ function main9(argv) {
|
|
|
4697
4636
|
);
|
|
4698
4637
|
return 0;
|
|
4699
4638
|
}
|
|
4700
|
-
|
|
4639
|
+
fs8.writeSync(1, json ? `${JSON.stringify(report, null, 2)}
|
|
4701
4640
|
` : `${render(report)}
|
|
4702
4641
|
`);
|
|
4703
4642
|
return 0;
|
|
4704
4643
|
}
|
|
4705
|
-
function
|
|
4644
|
+
function _isCliEntry8() {
|
|
4706
4645
|
if (true) {
|
|
4707
4646
|
return false;
|
|
4708
4647
|
}
|
|
4709
4648
|
if (process.argv[1] === void 0) {
|
|
4710
4649
|
return false;
|
|
4711
4650
|
}
|
|
4712
|
-
const argvUrl =
|
|
4651
|
+
const argvUrl = pathToFileURL8(path8.resolve(process.argv[1])).href;
|
|
4713
4652
|
if (import.meta.url === argvUrl) {
|
|
4714
4653
|
return true;
|
|
4715
4654
|
}
|
|
4716
4655
|
try {
|
|
4717
|
-
const here =
|
|
4718
|
-
const argv =
|
|
4656
|
+
const here = fs8.realpathSync(fileURLToPath8(import.meta.url));
|
|
4657
|
+
const argv = fs8.realpathSync(path8.resolve(process.argv[1]));
|
|
4719
4658
|
return here === argv;
|
|
4720
4659
|
} catch {
|
|
4721
4660
|
return false;
|
|
4722
4661
|
}
|
|
4723
4662
|
}
|
|
4724
|
-
if (
|
|
4725
|
-
process.exit(
|
|
4663
|
+
if (_isCliEntry8()) {
|
|
4664
|
+
process.exit(main8());
|
|
4726
4665
|
}
|
|
4727
4666
|
|
|
4728
4667
|
// src/scripts/report_carrier_divergence.ts
|
|
4729
|
-
import * as
|
|
4668
|
+
import * as fs9 from "node:fs";
|
|
4730
4669
|
import * as os3 from "node:os";
|
|
4731
|
-
import * as
|
|
4670
|
+
import * as path9 from "node:path";
|
|
4732
4671
|
import process3 from "node:process";
|
|
4733
|
-
import { fileURLToPath as
|
|
4734
|
-
var _HERE =
|
|
4735
|
-
var REPO_ROOT2 =
|
|
4736
|
-
var PROJECT_RULES =
|
|
4737
|
-
var PROJECTION_SOURCE =
|
|
4738
|
-
var GLOBAL_RULES =
|
|
4672
|
+
import { fileURLToPath as fileURLToPath9, pathToFileURL as pathToFileURL9 } from "node:url";
|
|
4673
|
+
var _HERE = fileURLToPath9(import.meta.url);
|
|
4674
|
+
var REPO_ROOT2 = path9.resolve(path9.dirname(_HERE), "..", "..");
|
|
4675
|
+
var PROJECT_RULES = path9.join(".claude", "rules");
|
|
4676
|
+
var PROJECTION_SOURCE = path9.join("dist", "agent-src", "rules");
|
|
4677
|
+
var GLOBAL_RULES = path9.join(".claude", "rules");
|
|
4739
4678
|
function _listRules(dir) {
|
|
4740
4679
|
try {
|
|
4741
|
-
return
|
|
4680
|
+
return fs9.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
|
|
4742
4681
|
} catch {
|
|
4743
4682
|
return [];
|
|
4744
4683
|
}
|
|
@@ -4746,7 +4685,7 @@ function _listRules(dir) {
|
|
|
4746
4685
|
function _ruleType(file) {
|
|
4747
4686
|
let text;
|
|
4748
4687
|
try {
|
|
4749
|
-
text =
|
|
4688
|
+
text = fs9.readFileSync(file, "utf-8");
|
|
4750
4689
|
} catch {
|
|
4751
4690
|
return "";
|
|
4752
4691
|
}
|
|
@@ -4757,8 +4696,8 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
|
|
|
4757
4696
|
const out = {
|
|
4758
4697
|
projectDir,
|
|
4759
4698
|
globalDir,
|
|
4760
|
-
globalPresent:
|
|
4761
|
-
projectPresent:
|
|
4699
|
+
globalPresent: fs9.existsSync(globalDir),
|
|
4700
|
+
projectPresent: fs9.existsSync(projectDir),
|
|
4762
4701
|
shared: 0,
|
|
4763
4702
|
identical: [],
|
|
4764
4703
|
provenanceOnly: [],
|
|
@@ -4781,7 +4720,7 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
|
|
|
4781
4720
|
}
|
|
4782
4721
|
if (!inP && inG) {
|
|
4783
4722
|
out.globalOnly.push(rule);
|
|
4784
|
-
if (sourceDir !== void 0 && _ruleType(
|
|
4723
|
+
if (sourceDir !== void 0 && _ruleType(path9.join(sourceDir, rule)) === "manual") {
|
|
4785
4724
|
out.manualOnlyGlobal.push(rule);
|
|
4786
4725
|
}
|
|
4787
4726
|
continue;
|
|
@@ -4791,8 +4730,8 @@ function compareCarriers(projectDir, globalDir, sourceDir) {
|
|
|
4791
4730
|
let projectCopy;
|
|
4792
4731
|
let globalCopy;
|
|
4793
4732
|
try {
|
|
4794
|
-
projectCopy =
|
|
4795
|
-
globalCopy =
|
|
4733
|
+
projectCopy = fs9.readFileSync(path9.join(projectDir, rule));
|
|
4734
|
+
globalCopy = fs9.readFileSync(path9.join(globalDir, rule));
|
|
4796
4735
|
verdict = comparePair(projectCopy, globalCopy);
|
|
4797
4736
|
} catch {
|
|
4798
4737
|
out.shared -= 1;
|
|
@@ -4937,11 +4876,11 @@ function render2(d) {
|
|
|
4937
4876
|
}
|
|
4938
4877
|
return lines.join("\n");
|
|
4939
4878
|
}
|
|
4940
|
-
function
|
|
4879
|
+
function main9(argv) {
|
|
4941
4880
|
const args = argv ?? process3.argv.slice(2);
|
|
4942
|
-
let projectDir =
|
|
4943
|
-
let globalDir =
|
|
4944
|
-
let sourceDir =
|
|
4881
|
+
let projectDir = path9.join(REPO_ROOT2, PROJECT_RULES);
|
|
4882
|
+
let globalDir = path9.join(os3.homedir(), GLOBAL_RULES);
|
|
4883
|
+
let sourceDir = path9.join(REPO_ROOT2, PROJECTION_SOURCE);
|
|
4945
4884
|
const value = (i) => {
|
|
4946
4885
|
const v = args[i + 1];
|
|
4947
4886
|
return v === void 0 || v.startsWith("-") ? null : v;
|
|
@@ -4955,7 +4894,7 @@ function main10(argv) {
|
|
|
4955
4894
|
`);
|
|
4956
4895
|
return 1;
|
|
4957
4896
|
}
|
|
4958
|
-
const resolved =
|
|
4897
|
+
const resolved = path9.resolve(v);
|
|
4959
4898
|
if (a === "--project") projectDir = resolved;
|
|
4960
4899
|
else if (a === "--global") globalDir = resolved;
|
|
4961
4900
|
else sourceDir = resolved;
|
|
@@ -4975,49 +4914,57 @@ function main10(argv) {
|
|
|
4975
4914
|
`);
|
|
4976
4915
|
return 0;
|
|
4977
4916
|
}
|
|
4978
|
-
function
|
|
4917
|
+
function _isCliEntry9() {
|
|
4979
4918
|
if (process3.argv[1] === void 0) return false;
|
|
4980
|
-
if (import.meta.url ===
|
|
4919
|
+
if (import.meta.url === pathToFileURL9(path9.resolve(process3.argv[1])).href) return true;
|
|
4981
4920
|
try {
|
|
4982
|
-
return
|
|
4921
|
+
return fs9.realpathSync(_HERE) === fs9.realpathSync(path9.resolve(process3.argv[1]));
|
|
4983
4922
|
} catch {
|
|
4984
4923
|
return false;
|
|
4985
4924
|
}
|
|
4986
4925
|
}
|
|
4987
|
-
if (
|
|
4988
|
-
process3.exit(
|
|
4926
|
+
if (_isCliEntry9()) {
|
|
4927
|
+
process3.exit(main9());
|
|
4989
4928
|
}
|
|
4990
4929
|
|
|
4991
4930
|
// src/scripts/report_skill_activation.ts
|
|
4992
|
-
import * as
|
|
4931
|
+
import * as fs10 from "node:fs";
|
|
4993
4932
|
import * as os4 from "node:os";
|
|
4994
|
-
import * as
|
|
4995
|
-
import { fileURLToPath as
|
|
4996
|
-
var _HERE2 =
|
|
4997
|
-
var REPO_ROOT3 =
|
|
4933
|
+
import * as path10 from "node:path";
|
|
4934
|
+
import { fileURLToPath as fileURLToPath10, pathToFileURL as pathToFileURL10 } from "node:url";
|
|
4935
|
+
var _HERE2 = fileURLToPath10(import.meta.url);
|
|
4936
|
+
var REPO_ROOT3 = path10.resolve(path10.dirname(_HERE2), "..", "..");
|
|
4998
4937
|
var SKILLS_ROOT = "src/skills";
|
|
4999
4938
|
var TRIGGER_KEYS = ["triggers:", "trigger_description:", "file_pattern:", "path_prefix:"];
|
|
5000
4939
|
var DETERMINISTIC_RE = /^\s*(?:[-*]\s*)?(?:\*\*)?(MUST|NEVER|ALWAYS)\b/m;
|
|
5001
4940
|
function censusSkills(root) {
|
|
5002
|
-
const out = {
|
|
5003
|
-
|
|
4941
|
+
const out = {
|
|
4942
|
+
total: 0,
|
|
4943
|
+
withTriggerKey: [],
|
|
4944
|
+
withTriggerCorpus: [],
|
|
4945
|
+
withDeterministicObligation: []
|
|
4946
|
+
};
|
|
4947
|
+
if (!fs10.existsSync(root)) {
|
|
5004
4948
|
return out;
|
|
5005
4949
|
}
|
|
5006
|
-
for (const entry of
|
|
4950
|
+
for (const entry of fs10.readdirSync(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5007
4951
|
if (!entry.isDirectory()) {
|
|
5008
4952
|
continue;
|
|
5009
4953
|
}
|
|
5010
|
-
const file =
|
|
5011
|
-
if (!
|
|
4954
|
+
const file = path10.join(root, entry.name, "SKILL.md");
|
|
4955
|
+
if (!fs10.existsSync(file)) {
|
|
5012
4956
|
continue;
|
|
5013
4957
|
}
|
|
5014
4958
|
out.total += 1;
|
|
5015
|
-
const text =
|
|
4959
|
+
const text = fs10.readFileSync(file, "utf8");
|
|
5016
4960
|
const fmEnd = text.startsWith("---") ? text.indexOf("\n---", 3) : -1;
|
|
5017
4961
|
const fm = fmEnd === -1 ? "" : text.slice(0, fmEnd);
|
|
5018
4962
|
if (TRIGGER_KEYS.some((k) => new RegExp(`^${k}`, "m").test(fm))) {
|
|
5019
4963
|
out.withTriggerKey.push(entry.name);
|
|
5020
4964
|
}
|
|
4965
|
+
if (fs10.existsSync(path10.join(root, entry.name, "evals", "triggers.json"))) {
|
|
4966
|
+
out.withTriggerCorpus.push(entry.name);
|
|
4967
|
+
}
|
|
5021
4968
|
const body = fmEnd === -1 ? text : text.slice(fmEnd + 4);
|
|
5022
4969
|
if (DETERMINISTIC_RE.test(body)) {
|
|
5023
4970
|
out.withDeterministicObligation.push(entry.name);
|
|
@@ -5026,17 +4973,17 @@ function censusSkills(root) {
|
|
|
5026
4973
|
return out;
|
|
5027
4974
|
}
|
|
5028
4975
|
function defaultStore2(cwd) {
|
|
5029
|
-
return
|
|
4976
|
+
return path10.join(os4.homedir(), ".claude", "projects", projectStoreSlug(cwd));
|
|
5030
4977
|
}
|
|
5031
4978
|
function measureUsage(store, limit) {
|
|
5032
4979
|
const rep = { store, sessions: 0, assistantTurns: 0, invocations: 0, bySkill: {} };
|
|
5033
|
-
if (!
|
|
4980
|
+
if (!fs10.existsSync(store)) {
|
|
5034
4981
|
return rep;
|
|
5035
4982
|
}
|
|
5036
|
-
const files =
|
|
4983
|
+
const files = fs10.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => path10.join(store, f)).sort((a, b) => fs10.statSync(b).mtimeMs - fs10.statSync(a).mtimeMs).slice(0, limit);
|
|
5037
4984
|
rep.sessions = files.length;
|
|
5038
4985
|
for (const file of files) {
|
|
5039
|
-
for (const line of
|
|
4986
|
+
for (const line of fs10.readFileSync(file, "utf8").split("\n")) {
|
|
5040
4987
|
if (!line.trim()) {
|
|
5041
4988
|
continue;
|
|
5042
4989
|
}
|
|
@@ -5076,7 +5023,50 @@ function measureUsage(store, limit) {
|
|
|
5076
5023
|
function _pct(n, d) {
|
|
5077
5024
|
return d === 0 ? "n/a" : `${(100 * n / d).toFixed(1)}%`;
|
|
5078
5025
|
}
|
|
5079
|
-
|
|
5026
|
+
var RECORD_PATH = "agents/evidence/metrics/skill-activation-census.json";
|
|
5027
|
+
function populations(census) {
|
|
5028
|
+
const corpus = new Set(census.withTriggerCorpus);
|
|
5029
|
+
const both = census.withTriggerKey.filter((s) => corpus.has(s));
|
|
5030
|
+
const eitherOne = /* @__PURE__ */ new Set([...census.withTriggerKey, ...census.withTriggerCorpus]);
|
|
5031
|
+
return {
|
|
5032
|
+
shipped: census.total,
|
|
5033
|
+
triggerKey: census.withTriggerKey.length,
|
|
5034
|
+
triggerCorpus: census.withTriggerCorpus.length,
|
|
5035
|
+
bothKeyAndCorpus: both.length,
|
|
5036
|
+
humanNamedOnly: census.total - eitherOne.size
|
|
5037
|
+
};
|
|
5038
|
+
}
|
|
5039
|
+
function buildRecord(census, usage, today) {
|
|
5040
|
+
const pop = populations(census);
|
|
5041
|
+
const distinct = /* @__PURE__ */ new Set();
|
|
5042
|
+
for (const u of usage) {
|
|
5043
|
+
for (const k of Object.keys(u.bySkill)) {
|
|
5044
|
+
distinct.add(k);
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
return {
|
|
5048
|
+
schema_version: 1,
|
|
5049
|
+
measured_at: today,
|
|
5050
|
+
skills_shipped: pop.shipped,
|
|
5051
|
+
with_trigger_key: pop.triggerKey,
|
|
5052
|
+
with_trigger_corpus: pop.triggerCorpus,
|
|
5053
|
+
trigger_key_and_corpus: pop.bothKeyAndCorpus,
|
|
5054
|
+
human_named_only: pop.humanNamedOnly,
|
|
5055
|
+
sessions: usage.reduce((a, u) => a + u.sessions, 0),
|
|
5056
|
+
assistant_turns: usage.reduce((a, u) => a + u.assistantTurns, 0),
|
|
5057
|
+
invocations: usage.reduce((a, u) => a + u.invocations, 0),
|
|
5058
|
+
distinct_skills_invoked: distinct.size,
|
|
5059
|
+
stores: usage.map((u) => path10.basename(u.store))
|
|
5060
|
+
};
|
|
5061
|
+
}
|
|
5062
|
+
function readRecord(repoRoot) {
|
|
5063
|
+
const file = path10.join(repoRoot, RECORD_PATH);
|
|
5064
|
+
if (!fs10.existsSync(file)) {
|
|
5065
|
+
return null;
|
|
5066
|
+
}
|
|
5067
|
+
return JSON.parse(fs10.readFileSync(file, "utf8"));
|
|
5068
|
+
}
|
|
5069
|
+
function render3(census, usage, published = null) {
|
|
5080
5070
|
const lines = [];
|
|
5081
5071
|
lines.push("skill activation census \u2014 advisory, gates on nothing");
|
|
5082
5072
|
lines.push("");
|
|
@@ -5091,6 +5081,16 @@ function render3(census, usage) {
|
|
|
5091
5081
|
lines.push(` ${census.withDeterministicObligation.join(", ")}`);
|
|
5092
5082
|
}
|
|
5093
5083
|
lines.push("");
|
|
5084
|
+
const pop = populations(census);
|
|
5085
|
+
lines.push(" populations \u2014 the three ways a skill can be reached, reconciled");
|
|
5086
|
+
lines.push(` machine-matchable trigger key ${pop.triggerKey}`);
|
|
5087
|
+
lines.push(` evals/triggers.json corpus ${pop.triggerCorpus} (a test fixture; no host reads it at routing time)`);
|
|
5088
|
+
lines.push(` both of the above ${pop.bothKeyAndCorpus}`);
|
|
5089
|
+
lines.push(` human-named only (remainder) ${pop.humanNamedOnly}`);
|
|
5090
|
+
lines.push(
|
|
5091
|
+
` reconciles ${pop.triggerKey} + ${pop.triggerCorpus} - ${pop.bothKeyAndCorpus} + ${pop.humanNamedOnly} = ${String(pop.triggerKey + pop.triggerCorpus - pop.bothKeyAndCorpus + pop.humanNamedOnly)} of ${pop.shipped}`
|
|
5092
|
+
);
|
|
5093
|
+
lines.push("");
|
|
5094
5094
|
let totalInv = 0;
|
|
5095
5095
|
const distinct = /* @__PURE__ */ new Set();
|
|
5096
5096
|
for (const u of usage) {
|
|
@@ -5099,7 +5099,7 @@ function render3(census, usage) {
|
|
|
5099
5099
|
}
|
|
5100
5100
|
totalInv += u.invocations;
|
|
5101
5101
|
lines.push(
|
|
5102
|
-
` ${
|
|
5102
|
+
` ${path10.basename(u.store).slice(-38).padEnd(38)} sessions=${String(u.sessions).padStart(3)} asst=${String(u.assistantTurns).padStart(6)} Skill calls=${String(u.invocations).padStart(3)}`
|
|
5103
5103
|
);
|
|
5104
5104
|
}
|
|
5105
5105
|
lines.push("");
|
|
@@ -5111,6 +5111,51 @@ function render3(census, usage) {
|
|
|
5111
5111
|
lines.push(` ${[...distinct].sort().join(", ")}`);
|
|
5112
5112
|
}
|
|
5113
5113
|
lines.push("");
|
|
5114
|
+
const sessionsRead = usage.reduce((a, u) => a + u.sessions, 0);
|
|
5115
|
+
if (sessionsRead === 0) {
|
|
5116
|
+
lines.push(' EMPTY STORE: 0 sessions were read, so the zero above is "no transcripts at');
|
|
5117
|
+
lines.push(' this path", NOT "no activations". It is not a measurement of the surface and');
|
|
5118
|
+
lines.push(" must not be published as one. Run this where the store lives, or pass --store.");
|
|
5119
|
+
lines.push("");
|
|
5120
|
+
}
|
|
5121
|
+
if (published !== null) {
|
|
5122
|
+
const live = buildRecord(census, usage, published.measured_at);
|
|
5123
|
+
const REPO_FIELDS = [
|
|
5124
|
+
"skills_shipped",
|
|
5125
|
+
"with_trigger_key",
|
|
5126
|
+
"with_trigger_corpus",
|
|
5127
|
+
"trigger_key_and_corpus",
|
|
5128
|
+
"human_named_only"
|
|
5129
|
+
];
|
|
5130
|
+
const STORE_FIELDS = [
|
|
5131
|
+
"sessions",
|
|
5132
|
+
"assistant_turns",
|
|
5133
|
+
"invocations",
|
|
5134
|
+
"distinct_skills_invoked"
|
|
5135
|
+
];
|
|
5136
|
+
const repoDrift = REPO_FIELDS.filter((k) => live[k] !== published[k]);
|
|
5137
|
+
const storeDrift = STORE_FIELDS.filter((k) => live[k] !== published[k]);
|
|
5138
|
+
lines.push(` PUBLISHED RECORD (${RECORD_PATH}), measured ${published.measured_at}:`);
|
|
5139
|
+
lines.push(
|
|
5140
|
+
` sessions=${String(published.sessions)} asst=${String(published.assistant_turns)} invocations=${String(published.invocations)} distinct=${String(published.distinct_skills_invoked)}`
|
|
5141
|
+
);
|
|
5142
|
+
if (repoDrift.length > 0) {
|
|
5143
|
+
lines.push(` STALE: the published population split no longer describes this tree \u2014`);
|
|
5144
|
+
lines.push(` ${repoDrift.join(", ")} moved. Re-take with --emit.`);
|
|
5145
|
+
} else {
|
|
5146
|
+
lines.push(" population split still describes this tree.");
|
|
5147
|
+
}
|
|
5148
|
+
if (sessionsRead === 0) {
|
|
5149
|
+
lines.push(" Store reading: UNDECIDED \u2014 this run read no store, so it cannot say whether");
|
|
5150
|
+
lines.push(" the published figures still hold. Re-run where the store lives.");
|
|
5151
|
+
} else if (storeDrift.length === 0) {
|
|
5152
|
+
lines.push(" Store reading: reproduces the published figures exactly.");
|
|
5153
|
+
} else {
|
|
5154
|
+
lines.push(` Store reading: moved since the record (${storeDrift.join(", ")}). Expected \u2014`);
|
|
5155
|
+
lines.push(" the store is rolling, not append-only, and a later reading can be lower.");
|
|
5156
|
+
}
|
|
5157
|
+
lines.push("");
|
|
5158
|
+
}
|
|
5114
5159
|
if (census.withTriggerKey.length === 0) {
|
|
5115
5160
|
lines.push(" VERDICT: activation is not measurable as a rate. No skill declares a");
|
|
5116
5161
|
lines.push(" machine-matchable trigger, so a missed-activation detector has nothing to");
|
|
@@ -5124,19 +5169,30 @@ function render3(census, usage) {
|
|
|
5124
5169
|
lines.push(" catalogue once per session, then count descriptions against bare names.");
|
|
5125
5170
|
return lines.join("\n");
|
|
5126
5171
|
}
|
|
5127
|
-
|
|
5172
|
+
var USAGE = "usage: report_skill_activation [--root DIR] [--limit N] [--store PATH]... [--emit] [--allow-empty-store]\n";
|
|
5173
|
+
function main10(argv) {
|
|
5128
5174
|
let limit = 30;
|
|
5175
|
+
let emit = false;
|
|
5176
|
+
let allowEmpty = false;
|
|
5177
|
+
const rootIdx = argv.indexOf("--root");
|
|
5178
|
+
const root = rootIdx === -1 ? REPO_ROOT3 : argv[rootIdx + 1] ?? REPO_ROOT3;
|
|
5129
5179
|
const stores = [];
|
|
5130
5180
|
for (let i = 0; i < argv.length; i += 1) {
|
|
5131
5181
|
const a = argv[i];
|
|
5132
|
-
if (a === "--
|
|
5182
|
+
if (a === "--root" && argv[i + 1] !== void 0) {
|
|
5183
|
+
i += 1;
|
|
5184
|
+
} else if (a === "--limit" && argv[i + 1] !== void 0) {
|
|
5133
5185
|
limit = Number.parseInt(argv[i + 1], 10);
|
|
5134
5186
|
i += 1;
|
|
5135
5187
|
} else if (a === "--store" && argv[i + 1] !== void 0) {
|
|
5136
5188
|
stores.push(argv[i + 1]);
|
|
5137
5189
|
i += 1;
|
|
5190
|
+
} else if (a === "--emit") {
|
|
5191
|
+
emit = true;
|
|
5192
|
+
} else if (a === "--allow-empty-store") {
|
|
5193
|
+
allowEmpty = true;
|
|
5138
5194
|
} else if (a === "--help" || a === "-h") {
|
|
5139
|
-
process.stdout.write(
|
|
5195
|
+
process.stdout.write(USAGE);
|
|
5140
5196
|
return 0;
|
|
5141
5197
|
}
|
|
5142
5198
|
}
|
|
@@ -5147,24 +5203,48 @@ function main11(argv) {
|
|
|
5147
5203
|
if (stores.length === 0) {
|
|
5148
5204
|
stores.push(defaultStore2(REPO_ROOT3));
|
|
5149
5205
|
}
|
|
5150
|
-
const census = censusSkills(
|
|
5206
|
+
const census = censusSkills(path10.join(root, SKILLS_ROOT));
|
|
5151
5207
|
const usage = stores.map((s) => measureUsage(s, limit));
|
|
5152
|
-
process.stdout.write(`${render3(census, usage)}
|
|
5208
|
+
process.stdout.write(`${render3(census, usage, readRecord(root))}
|
|
5209
|
+
`);
|
|
5210
|
+
reportScanned({
|
|
5211
|
+
gate: "report_skill_activation",
|
|
5212
|
+
scanned: census.total,
|
|
5213
|
+
units: "SKILL.md file(s)",
|
|
5214
|
+
roots: [SKILLS_ROOT]
|
|
5215
|
+
});
|
|
5216
|
+
if (!emit) {
|
|
5217
|
+
return 0;
|
|
5218
|
+
}
|
|
5219
|
+
const sessionsRead = usage.reduce((a, u) => a + u.sessions, 0);
|
|
5220
|
+
if (sessionsRead === 0 && !allowEmpty) {
|
|
5221
|
+
process.stderr.write(
|
|
5222
|
+
'report_skill_activation --emit: refusing to write a record from 0 sessions \u2014 that zero\nis "no transcripts at this path", not an activation reading. Run where the store lives,\npass --store, or pass --allow-empty-store if an empty store is genuinely the finding.\n'
|
|
5223
|
+
);
|
|
5224
|
+
return 1;
|
|
5225
|
+
}
|
|
5226
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
5227
|
+
const file = path10.join(root, RECORD_PATH);
|
|
5228
|
+
fs10.mkdirSync(path10.dirname(file), { recursive: true });
|
|
5229
|
+
fs10.writeFileSync(file, `${JSON.stringify(buildRecord(census, usage, today), null, 2)}
|
|
5230
|
+
`);
|
|
5231
|
+
process.stdout.write(`
|
|
5232
|
+
wrote ${RECORD_PATH} (measured ${today})
|
|
5153
5233
|
`);
|
|
5154
5234
|
return 0;
|
|
5155
5235
|
}
|
|
5156
|
-
if (process.argv[1] !== void 0 && import.meta.url ===
|
|
5157
|
-
process.exit(
|
|
5236
|
+
if (process.argv[1] !== void 0 && import.meta.url === pathToFileURL10(path10.resolve(process.argv[1])).href) {
|
|
5237
|
+
process.exit(main10(process.argv.slice(2)));
|
|
5158
5238
|
}
|
|
5159
5239
|
|
|
5160
5240
|
// src/scripts/report_skill_obligation_violations.ts
|
|
5161
|
-
import * as
|
|
5241
|
+
import * as fs11 from "node:fs";
|
|
5162
5242
|
import * as os5 from "node:os";
|
|
5163
|
-
import * as
|
|
5243
|
+
import * as path11 from "node:path";
|
|
5164
5244
|
import process4 from "node:process";
|
|
5165
|
-
import { fileURLToPath as
|
|
5166
|
-
var _HERE3 =
|
|
5167
|
-
var REPO_ROOT4 =
|
|
5245
|
+
import { fileURLToPath as fileURLToPath11, pathToFileURL as pathToFileURL11 } from "node:url";
|
|
5246
|
+
var _HERE3 = fileURLToPath11(import.meta.url);
|
|
5247
|
+
var REPO_ROOT4 = path11.resolve(path11.dirname(_HERE3), "..", "..");
|
|
5168
5248
|
var OBLIGATION_LINE_RE = new RegExp(`${DETERMINISTIC_RE.source}.*`, "gm");
|
|
5169
5249
|
var PATH_RE = /`([A-Za-z0-9_./-]*\/[A-Za-z0-9_.-]+\.[a-z]{2,6})`/g;
|
|
5170
5250
|
var CMD_RE = /`([a-z][a-z0-9-]*(?: [a-z][a-z0-9:._-]*){1,3})`/g;
|
|
@@ -5202,7 +5282,7 @@ function _artefacts(line) {
|
|
|
5202
5282
|
return out;
|
|
5203
5283
|
}
|
|
5204
5284
|
function extractObligations(repoRoot) {
|
|
5205
|
-
const skillsRoot =
|
|
5285
|
+
const skillsRoot = path11.join(repoRoot, SKILLS_ROOT);
|
|
5206
5286
|
const census = censusSkills(skillsRoot);
|
|
5207
5287
|
const out = {
|
|
5208
5288
|
skills: census.withDeterministicObligation,
|
|
@@ -5216,7 +5296,7 @@ function extractObligations(repoRoot) {
|
|
|
5216
5296
|
for (const skill of census.withDeterministicObligation) {
|
|
5217
5297
|
let text;
|
|
5218
5298
|
try {
|
|
5219
|
-
text =
|
|
5299
|
+
text = fs11.readFileSync(path11.join(skillsRoot, skill, "SKILL.md"), "utf-8");
|
|
5220
5300
|
} catch {
|
|
5221
5301
|
continue;
|
|
5222
5302
|
}
|
|
@@ -5332,7 +5412,7 @@ function scanSessionForViolations(session, lines, forbidden, parsed) {
|
|
|
5332
5412
|
return flags;
|
|
5333
5413
|
}
|
|
5334
5414
|
function defaultStore3(cwd) {
|
|
5335
|
-
return
|
|
5415
|
+
return path11.join(os5.homedir(), ".claude", "projects", projectStoreSlug(cwd));
|
|
5336
5416
|
}
|
|
5337
5417
|
function scanStore2(repoRoot, store, limit) {
|
|
5338
5418
|
const census = extractObligations(repoRoot);
|
|
@@ -5345,14 +5425,14 @@ function scanStore2(repoRoot, store, limit) {
|
|
|
5345
5425
|
};
|
|
5346
5426
|
let files;
|
|
5347
5427
|
try {
|
|
5348
|
-
files =
|
|
5428
|
+
files = fs11.readdirSync(store).filter((f) => f.endsWith(".jsonl")).map((f) => ({ p: path11.join(store, f), m: fs11.statSync(path11.join(store, f)).mtimeMs })).sort((a, b) => b.m - a.m).slice(0, limit).map((x) => x.p);
|
|
5349
5429
|
} catch {
|
|
5350
5430
|
return report;
|
|
5351
5431
|
}
|
|
5352
5432
|
report.sessions = files.length;
|
|
5353
5433
|
for (const file of files) {
|
|
5354
|
-
const lines =
|
|
5355
|
-
const id =
|
|
5434
|
+
const lines = fs11.readFileSync(file, "utf-8").split("\n");
|
|
5435
|
+
const id = path11.basename(file).replace(/\.jsonl$/, "").slice(0, 8);
|
|
5356
5436
|
const entries = _parseSession(lines);
|
|
5357
5437
|
if (loadedSkillsFrom(entries).size > 0) report.sessionsWithASkill += 1;
|
|
5358
5438
|
report.flags.push(...scanSessionForViolations(id, lines, census.forbidden, entries));
|
|
@@ -5422,7 +5502,7 @@ function render4(r) {
|
|
|
5422
5502
|
lines.push(" ships as detection-only, and this one says so.");
|
|
5423
5503
|
return lines.join("\n");
|
|
5424
5504
|
}
|
|
5425
|
-
function
|
|
5505
|
+
function main11(argv) {
|
|
5426
5506
|
const args = argv ?? process4.argv.slice(2);
|
|
5427
5507
|
let limit = 30;
|
|
5428
5508
|
let json = false;
|
|
@@ -5475,35 +5555,35 @@ function main12(argv) {
|
|
|
5475
5555
|
}
|
|
5476
5556
|
return 0;
|
|
5477
5557
|
}
|
|
5478
|
-
function
|
|
5558
|
+
function _isCliEntry10() {
|
|
5479
5559
|
if (process4.argv[1] === void 0) return false;
|
|
5480
|
-
if (import.meta.url ===
|
|
5560
|
+
if (import.meta.url === pathToFileURL11(path11.resolve(process4.argv[1])).href) return true;
|
|
5481
5561
|
try {
|
|
5482
|
-
return
|
|
5562
|
+
return fs11.realpathSync(_HERE3) === fs11.realpathSync(path11.resolve(process4.argv[1]));
|
|
5483
5563
|
} catch {
|
|
5484
5564
|
return false;
|
|
5485
5565
|
}
|
|
5486
5566
|
}
|
|
5487
|
-
if (
|
|
5488
|
-
process4.exit(
|
|
5567
|
+
if (_isCliEntry10()) {
|
|
5568
|
+
process4.exit(main11());
|
|
5489
5569
|
}
|
|
5490
5570
|
|
|
5491
5571
|
// src/scripts/report_conformance_funnel.ts
|
|
5492
|
-
var _HERE4 =
|
|
5493
|
-
var REPO_ROOT5 =
|
|
5572
|
+
var _HERE4 = fileURLToPath12(import.meta.url);
|
|
5573
|
+
var REPO_ROOT5 = path12.resolve(path12.dirname(_HERE4), "..", "..");
|
|
5494
5574
|
function buildFunnel(opts = {}) {
|
|
5495
5575
|
const repoRoot = opts.repoRoot ?? REPO_ROOT5;
|
|
5496
|
-
const projectRulesDir = opts.projectRulesDir ??
|
|
5497
|
-
const globalRulesDir = opts.globalRulesDir ??
|
|
5498
|
-
const sourceDir = opts.sourceDir ??
|
|
5576
|
+
const projectRulesDir = opts.projectRulesDir ?? path12.join(repoRoot, PROJECT_RULES);
|
|
5577
|
+
const globalRulesDir = opts.globalRulesDir ?? path12.join(os6.homedir(), GLOBAL_RULES);
|
|
5578
|
+
const sourceDir = opts.sourceDir ?? path12.join(repoRoot, PROJECTION_SOURCE);
|
|
5499
5579
|
const store = opts.store ?? defaultStore(repoRoot);
|
|
5500
5580
|
const limit = opts.limit ?? 30;
|
|
5501
5581
|
return {
|
|
5502
5582
|
delivered: measureDelivered(projectRulesDir, globalRulesDir),
|
|
5503
5583
|
divergence: compareCarriers(projectRulesDir, globalRulesDir, sourceDir),
|
|
5504
|
-
skills: censusSkills(
|
|
5584
|
+
skills: censusSkills(path12.join(repoRoot, SKILLS_ROOT)),
|
|
5505
5585
|
usage: measureUsage(store, limit),
|
|
5506
|
-
storePresent:
|
|
5586
|
+
storePresent: fs12.existsSync(store),
|
|
5507
5587
|
sk2: scanStore2(repoRoot, store, limit)
|
|
5508
5588
|
};
|
|
5509
5589
|
}
|
|
@@ -5628,7 +5708,7 @@ function render5(r) {
|
|
|
5628
5708
|
lines.push("workflow without a measured false-positive rate first (locked policy).");
|
|
5629
5709
|
return lines.join("\n");
|
|
5630
5710
|
}
|
|
5631
|
-
function
|
|
5711
|
+
function main12(argv) {
|
|
5632
5712
|
const args = argv ?? process5.argv.slice(2);
|
|
5633
5713
|
const opts = {};
|
|
5634
5714
|
const value = (i) => {
|
|
@@ -5644,7 +5724,7 @@ function main13(argv) {
|
|
|
5644
5724
|
`);
|
|
5645
5725
|
return 1;
|
|
5646
5726
|
}
|
|
5647
|
-
const resolved =
|
|
5727
|
+
const resolved = path12.resolve(v);
|
|
5648
5728
|
if (a === "--store") opts.store = resolved;
|
|
5649
5729
|
else if (a === "--project") opts.projectRulesDir = resolved;
|
|
5650
5730
|
else if (a === "--global") opts.globalRulesDir = resolved;
|
|
@@ -5674,21 +5754,21 @@ function main13(argv) {
|
|
|
5674
5754
|
`);
|
|
5675
5755
|
return 0;
|
|
5676
5756
|
}
|
|
5677
|
-
function
|
|
5757
|
+
function _isCliEntry11() {
|
|
5678
5758
|
if (process5.argv[1] === void 0) return false;
|
|
5679
|
-
if (import.meta.url ===
|
|
5759
|
+
if (import.meta.url === pathToFileURL12(path12.resolve(process5.argv[1])).href) return true;
|
|
5680
5760
|
try {
|
|
5681
|
-
return
|
|
5761
|
+
return fs12.realpathSync(_HERE4) === fs12.realpathSync(path12.resolve(process5.argv[1]));
|
|
5682
5762
|
} catch {
|
|
5683
5763
|
return false;
|
|
5684
5764
|
}
|
|
5685
5765
|
}
|
|
5686
|
-
if (
|
|
5687
|
-
process5.exit(
|
|
5766
|
+
if (_isCliEntry11()) {
|
|
5767
|
+
process5.exit(main12());
|
|
5688
5768
|
}
|
|
5689
5769
|
|
|
5690
5770
|
// src/scripts/_cli/cmd_conformance.ts
|
|
5691
|
-
var _HERE5 =
|
|
5771
|
+
var _HERE5 = fileURLToPath13(import.meta.url);
|
|
5692
5772
|
var PACKAGE_ROOT = resolvePackageRoot(import.meta.url);
|
|
5693
5773
|
var CONFORMANCE_CHECK_IDS = [
|
|
5694
5774
|
"txlog-clean",
|
|
@@ -5711,17 +5791,17 @@ function conformanceLogPath() {
|
|
|
5711
5791
|
if (override && override.trim().length > 0) {
|
|
5712
5792
|
return override;
|
|
5713
5793
|
}
|
|
5714
|
-
return
|
|
5794
|
+
return path13.join(os7.homedir(), ".event4u", "agent-config", "conformance-log.jsonl");
|
|
5715
5795
|
}
|
|
5716
5796
|
function installLogPath() {
|
|
5717
5797
|
const override = process6.env["AGENT_CONFIG_INSTALL_LOG"];
|
|
5718
5798
|
if (override && override.trim().length > 0) {
|
|
5719
5799
|
return override;
|
|
5720
5800
|
}
|
|
5721
|
-
return
|
|
5801
|
+
return path13.join(os7.homedir(), ".event4u", "agent-config", "install-log.jsonl");
|
|
5722
5802
|
}
|
|
5723
5803
|
function _check_txlog_clean(logPath = installLogPath()) {
|
|
5724
|
-
if (!
|
|
5804
|
+
if (!fs13.existsSync(logPath)) {
|
|
5725
5805
|
return {
|
|
5726
5806
|
id: "txlog-clean",
|
|
5727
5807
|
status: "ok",
|
|
@@ -5755,11 +5835,11 @@ function _check_txlog_clean(logPath = installLogPath()) {
|
|
|
5755
5835
|
};
|
|
5756
5836
|
}
|
|
5757
5837
|
function routeTargetPaths(target) {
|
|
5758
|
-
return routeTargetPathsPosix(target).map((rel) =>
|
|
5838
|
+
return routeTargetPathsPosix(target).map((rel) => path13.join(...rel.split("/")));
|
|
5759
5839
|
}
|
|
5760
5840
|
function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
5761
|
-
const routerPath =
|
|
5762
|
-
if (!
|
|
5841
|
+
const routerPath = path13.join(packageRoot, "dist", "router.json");
|
|
5842
|
+
if (!fs13.existsSync(routerPath)) {
|
|
5763
5843
|
return {
|
|
5764
5844
|
id: "router-pointers",
|
|
5765
5845
|
status: "fail",
|
|
@@ -5769,7 +5849,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
|
5769
5849
|
}
|
|
5770
5850
|
let router;
|
|
5771
5851
|
try {
|
|
5772
|
-
router = JSON.parse(
|
|
5852
|
+
router = JSON.parse(fs13.readFileSync(routerPath, "utf8"));
|
|
5773
5853
|
} catch (exc) {
|
|
5774
5854
|
return {
|
|
5775
5855
|
id: "router-pointers",
|
|
@@ -5778,13 +5858,13 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
|
5778
5858
|
remedy: "reinstall the package \u2014 dist/router.json is corrupt"
|
|
5779
5859
|
};
|
|
5780
5860
|
}
|
|
5781
|
-
const rulesDir =
|
|
5861
|
+
const rulesDir = path13.join(packageRoot, "dist", "agent-src", "rules");
|
|
5782
5862
|
const broken = [];
|
|
5783
5863
|
let ids = 0;
|
|
5784
5864
|
let targets = 0;
|
|
5785
5865
|
for (const kernelId of router["kernel"] ?? []) {
|
|
5786
5866
|
ids += 1;
|
|
5787
|
-
if (!
|
|
5867
|
+
if (!fs13.existsSync(path13.join(rulesDir, `${kernelId}.md`))) {
|
|
5788
5868
|
broken.push(`kernel:${kernelId} \u2192 rules/${kernelId}.md missing`);
|
|
5789
5869
|
}
|
|
5790
5870
|
}
|
|
@@ -5792,7 +5872,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
|
5792
5872
|
for (const entry of router[tier] ?? []) {
|
|
5793
5873
|
const ruleId = String(entry["id"] ?? "");
|
|
5794
5874
|
ids += 1;
|
|
5795
|
-
if (!
|
|
5875
|
+
if (!fs13.existsSync(path13.join(rulesDir, `${ruleId}.md`))) {
|
|
5796
5876
|
broken.push(`${tier}:${ruleId} \u2192 rules/${ruleId}.md missing`);
|
|
5797
5877
|
}
|
|
5798
5878
|
for (const target of entry["routes_to"] ?? []) {
|
|
@@ -5802,7 +5882,7 @@ function _check_router_pointers(packageRoot = PACKAGE_ROOT) {
|
|
|
5802
5882
|
broken.push(`${ruleId} \u2192 unparseable routes_to ${JSON.stringify(target)}`);
|
|
5803
5883
|
continue;
|
|
5804
5884
|
}
|
|
5805
|
-
if (!candidates.some((rel) =>
|
|
5885
|
+
if (!candidates.some((rel) => fs13.existsSync(path13.join(packageRoot, rel)))) {
|
|
5806
5886
|
broken.push(`${ruleId} \u2192 ${target} (${candidates[0]} missing)`);
|
|
5807
5887
|
}
|
|
5808
5888
|
}
|
|
@@ -5834,9 +5914,9 @@ function detectSmokePlatform(projectRoot) {
|
|
|
5834
5914
|
return "claude";
|
|
5835
5915
|
}
|
|
5836
5916
|
function dispatcherRunner(packageRoot) {
|
|
5837
|
-
const localTsx =
|
|
5838
|
-
const script =
|
|
5839
|
-
if (
|
|
5917
|
+
const localTsx = path13.join(packageRoot, "node_modules", ".bin", "tsx");
|
|
5918
|
+
const script = path13.join(packageRoot, "src", "scripts", "hooks", "dispatch_hook.ts");
|
|
5919
|
+
if (fs13.existsSync(localTsx)) {
|
|
5840
5920
|
return [localTsx, script];
|
|
5841
5921
|
}
|
|
5842
5922
|
return ["npx", "tsx", script];
|
|
@@ -5886,12 +5966,12 @@ function _check_hook_dispatcher(projectRoot, packageRoot = PACKAGE_ROOT, opts =
|
|
|
5886
5966
|
};
|
|
5887
5967
|
}
|
|
5888
5968
|
function readSettingsMode(projectRoot) {
|
|
5889
|
-
const settingsPath =
|
|
5890
|
-
if (!
|
|
5969
|
+
const settingsPath = path13.join(projectRoot, ".agent-settings.yml");
|
|
5970
|
+
if (!fs13.existsSync(settingsPath)) {
|
|
5891
5971
|
return "eager-all";
|
|
5892
5972
|
}
|
|
5893
5973
|
try {
|
|
5894
|
-
const parsed = YAML.parse(
|
|
5974
|
+
const parsed = YAML.parse(fs13.readFileSync(settingsPath, "utf8"));
|
|
5895
5975
|
const lean = parsed?.["lean_projection"] ?? null;
|
|
5896
5976
|
const mode = lean?.["mode"];
|
|
5897
5977
|
return typeof mode === "string" && mode.length > 0 ? mode : "eager-all";
|
|
@@ -5902,7 +5982,7 @@ function readSettingsMode(projectRoot) {
|
|
|
5902
5982
|
function kernelIdsFromRouter(packageRoot) {
|
|
5903
5983
|
try {
|
|
5904
5984
|
const router = JSON.parse(
|
|
5905
|
-
|
|
5985
|
+
fs13.readFileSync(path13.join(packageRoot, "dist", "router.json"), "utf8")
|
|
5906
5986
|
);
|
|
5907
5987
|
return new Set((router["kernel"] ?? []).map(String));
|
|
5908
5988
|
} catch {
|
|
@@ -5919,8 +5999,8 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
|
|
|
5919
5999
|
remedy: "set lean_projection.mode to 'eager-all' or 'thin' in .agent-settings.yml"
|
|
5920
6000
|
};
|
|
5921
6001
|
}
|
|
5922
|
-
const rulesDir = projectedRulesDir ??
|
|
5923
|
-
if (!
|
|
6002
|
+
const rulesDir = projectedRulesDir ?? path13.join(projectRoot, ".augment", "rules");
|
|
6003
|
+
if (!fs13.existsSync(rulesDir)) {
|
|
5924
6004
|
return {
|
|
5925
6005
|
id: "lean-projection",
|
|
5926
6006
|
status: "skipped",
|
|
@@ -5929,7 +6009,7 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
|
|
|
5929
6009
|
};
|
|
5930
6010
|
}
|
|
5931
6011
|
const kernel = kernelIdsFromRouter(packageRoot);
|
|
5932
|
-
const files =
|
|
6012
|
+
const files = fs13.readdirSync(rulesDir).filter((f) => f.endsWith(".md") && f !== "INDEX.md").sort();
|
|
5933
6013
|
const nonKernel = files.filter((f) => !kernel.has(f.replace(/\.md$/, "")));
|
|
5934
6014
|
if (nonKernel.length === 0) {
|
|
5935
6015
|
return {
|
|
@@ -5943,7 +6023,7 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
|
|
|
5943
6023
|
for (const f of nonKernel) {
|
|
5944
6024
|
let text = "";
|
|
5945
6025
|
try {
|
|
5946
|
-
text =
|
|
6026
|
+
text = fs13.readFileSync(path13.join(rulesDir, f), "utf8");
|
|
5947
6027
|
} catch {
|
|
5948
6028
|
continue;
|
|
5949
6029
|
}
|
|
@@ -5974,8 +6054,8 @@ function _check_lean_projection(projectRoot, packageRoot = PACKAGE_ROOT, project
|
|
|
5974
6054
|
function _check_host_manifest(projectRoot) {
|
|
5975
6055
|
const detected = Object.entries(detectToolPresence(projectRoot)).filter(([, v]) => v === true).map(([k]) => k);
|
|
5976
6056
|
const detectedNote = detected.length > 0 ? `detected surfaces: ${detected.join(", ")}` : "no surfaces detected";
|
|
5977
|
-
const settingsPath =
|
|
5978
|
-
if (!
|
|
6057
|
+
const settingsPath = path13.join(projectRoot, ".agent-settings.yml");
|
|
6058
|
+
if (!fs13.existsSync(settingsPath)) {
|
|
5979
6059
|
return {
|
|
5980
6060
|
id: "host-manifest",
|
|
5981
6061
|
status: "ok",
|
|
@@ -5985,7 +6065,7 @@ function _check_host_manifest(projectRoot) {
|
|
|
5985
6065
|
}
|
|
5986
6066
|
let parsed = null;
|
|
5987
6067
|
try {
|
|
5988
|
-
parsed = YAML.parse(
|
|
6068
|
+
parsed = YAML.parse(fs13.readFileSync(settingsPath, "utf8"));
|
|
5989
6069
|
} catch (exc) {
|
|
5990
6070
|
return {
|
|
5991
6071
|
id: "host-manifest",
|
|
@@ -6046,14 +6126,14 @@ function appendConformanceReport(checks, projectRoot, logPath = conformanceLogPa
|
|
|
6046
6126
|
} catch {
|
|
6047
6127
|
}
|
|
6048
6128
|
}
|
|
6049
|
-
var
|
|
6129
|
+
var USAGE2 = "usage: agent-config conformance [-h] [--project PROJECT] [--json]\n [--skip-dispatcher] [--funnel]\n";
|
|
6050
6130
|
function _parse(argv) {
|
|
6051
6131
|
const opts = { project: null, json: false, skip_dispatcher: false, funnel: false };
|
|
6052
6132
|
let i = 0;
|
|
6053
6133
|
while (i < argv.length) {
|
|
6054
6134
|
const a = argv[i];
|
|
6055
6135
|
if (a === "-h" || a === "--help") {
|
|
6056
|
-
process6.stdout.write(
|
|
6136
|
+
process6.stdout.write(USAGE2);
|
|
6057
6137
|
throw new ArgparseExit(0);
|
|
6058
6138
|
}
|
|
6059
6139
|
if (a === "--json") {
|
|
@@ -6067,7 +6147,7 @@ function _parse(argv) {
|
|
|
6067
6147
|
opts.project = a.slice(a.indexOf("=") + 1);
|
|
6068
6148
|
} else {
|
|
6069
6149
|
if (i + 1 >= argv.length) {
|
|
6070
|
-
process6.stderr.write(
|
|
6150
|
+
process6.stderr.write(USAGE2);
|
|
6071
6151
|
process6.stderr.write("agent-config conformance: error: --project expects a value\n");
|
|
6072
6152
|
throw new ArgparseExit(2);
|
|
6073
6153
|
}
|
|
@@ -6075,7 +6155,7 @@ function _parse(argv) {
|
|
|
6075
6155
|
i += 1;
|
|
6076
6156
|
}
|
|
6077
6157
|
} else {
|
|
6078
|
-
process6.stderr.write(
|
|
6158
|
+
process6.stderr.write(USAGE2);
|
|
6079
6159
|
process6.stderr.write(`agent-config conformance: error: unrecognized argument ${a}
|
|
6080
6160
|
`);
|
|
6081
6161
|
throw new ArgparseExit(2);
|
|
@@ -6084,7 +6164,7 @@ function _parse(argv) {
|
|
|
6084
6164
|
}
|
|
6085
6165
|
return opts;
|
|
6086
6166
|
}
|
|
6087
|
-
function
|
|
6167
|
+
function main13(argv = null) {
|
|
6088
6168
|
const opts = _parse(argv !== null ? Array.from(argv) : process6.argv.slice(2));
|
|
6089
6169
|
if (opts.funnel) {
|
|
6090
6170
|
print(render5(buildFunnel()));
|
|
@@ -6155,25 +6235,25 @@ function main14(argv = null) {
|
|
|
6155
6235
|
}
|
|
6156
6236
|
return failed ? 1 : 0;
|
|
6157
6237
|
}
|
|
6158
|
-
function
|
|
6238
|
+
function _isCliEntry12() {
|
|
6159
6239
|
if (process6.argv[1] === void 0) {
|
|
6160
6240
|
return false;
|
|
6161
6241
|
}
|
|
6162
|
-
const argvUrl =
|
|
6242
|
+
const argvUrl = pathToFileURL13(path13.resolve(process6.argv[1])).href;
|
|
6163
6243
|
if (import.meta.url === argvUrl) {
|
|
6164
6244
|
return true;
|
|
6165
6245
|
}
|
|
6166
6246
|
try {
|
|
6167
|
-
const here =
|
|
6168
|
-
const argv =
|
|
6247
|
+
const here = fs13.realpathSync(fileURLToPath13(import.meta.url));
|
|
6248
|
+
const argv = fs13.realpathSync(path13.resolve(process6.argv[1]));
|
|
6169
6249
|
return here === argv;
|
|
6170
6250
|
} catch {
|
|
6171
6251
|
return false;
|
|
6172
6252
|
}
|
|
6173
6253
|
}
|
|
6174
|
-
if (
|
|
6254
|
+
if (_isCliEntry12() || process6.argv[1] === _HERE5) {
|
|
6175
6255
|
try {
|
|
6176
|
-
process6.exitCode =
|
|
6256
|
+
process6.exitCode = main13(process6.argv.slice(2));
|
|
6177
6257
|
} catch (e) {
|
|
6178
6258
|
if (e instanceof ArgparseExit) {
|
|
6179
6259
|
process6.exitCode = e.code;
|
|
@@ -6196,7 +6276,7 @@ export {
|
|
|
6196
6276
|
conformanceLogPath,
|
|
6197
6277
|
detectSmokePlatform,
|
|
6198
6278
|
installLogPath,
|
|
6199
|
-
|
|
6279
|
+
main13 as main,
|
|
6200
6280
|
routeTargetPaths,
|
|
6201
6281
|
runConformanceChecks
|
|
6202
6282
|
};
|