opensquid 0.5.441 → 0.5.447
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/functions/arm_scope.d.ts +27 -0
- package/dist/functions/arm_scope.d.ts.map +1 -0
- package/dist/functions/arm_scope.js +52 -0
- package/dist/functions/arm_scope.js.map +1 -0
- package/dist/functions/index.d.ts +1 -0
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +1 -0
- package/dist/functions/index.js.map +1 -1
- package/dist/runtime/bootstrap.d.ts.map +1 -1
- package/dist/runtime/bootstrap.js +2 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/handoff/render.d.ts +5 -4
- package/dist/runtime/handoff/render.d.ts.map +1 -1
- package/dist/runtime/handoff/render.js +7 -7
- package/dist/runtime/handoff/render.js.map +1 -1
- package/dist/runtime/hooks/active_task_mirror.js +0 -0
- package/dist/runtime/hooks/apply_patch.js +0 -0
- package/dist/runtime/hooks/dispatch.js +0 -0
- package/dist/runtime/hooks/hook_output.js +0 -0
- package/dist/runtime/hooks/memory_reconcile.js +0 -0
- package/dist/runtime/hooks/new_project_detect.js +0 -0
- package/dist/runtime/hooks/profession_resolver.js +0 -0
- package/dist/runtime/hooks/scope_intent.js +0 -0
- package/dist/runtime/hooks/session_id.js +0 -0
- package/dist/runtime/hooks/session_liveness.js +0 -0
- package/dist/runtime/hooks/stop_drive.js +0 -0
- package/dist/runtime/hooks/stop_stream.js +0 -0
- package/dist/runtime/hooks/subagent_guard.js +0 -0
- package/dist/runtime/hooks/transcript.js +0 -0
- package/dist/runtime/hooks/transcript_tasks.js +0 -0
- package/dist/runtime/ralph/orchestrator.d.ts.map +1 -1
- package/dist/runtime/ralph/orchestrator.js +2 -1
- package/dist/runtime/ralph/orchestrator.js.map +1 -1
- package/dist/setup/cli/limits_state.d.ts.map +1 -1
- package/dist/setup/cli/limits_state.js +6 -40
- package/dist/setup/cli/limits_state.js.map +1 -1
- package/dist/setup/cli/pack_walk.d.ts +32 -0
- package/dist/setup/cli/pack_walk.d.ts.map +1 -0
- package/dist/setup/cli/pack_walk.js +76 -0
- package/dist/setup/cli/pack_walk.js.map +1 -0
- package/dist/setup/cli/permissions_state.d.ts.map +1 -1
- package/dist/setup/cli/permissions_state.js +6 -37
- package/dist/setup/cli/permissions_state.js.map +1 -1
- package/dist/setup/cli/triggers_state.d.ts.map +1 -1
- package/dist/setup/cli/triggers_state.js +3 -29
- package/dist/setup/cli/triggers_state.js.map +1 -1
- package/dist/workgraph/events.d.ts.map +1 -1
- package/dist/workgraph/events.js +10 -0
- package/dist/workgraph/events.js.map +1 -1
- package/dist/workgraph/store.d.ts.map +1 -1
- package/dist/workgraph/store.js +5 -0
- package/dist/workgraph/store.js.map +1 -1
- package/dist/workgraph/types.d.ts +2 -1
- package/dist/workgraph/types.d.ts.map +1 -1
- package/docs/ARCHITECTURE.md +268 -0
- package/package.json +5 -3
- package/packs/builtin/coding-flow/skills/entry-and-handoffs/skill.yaml +13 -17
- package/dist/anti-drift/evaluator.d.ts +0 -88
- package/dist/anti-drift/evaluator.d.ts.map +0 -1
- package/dist/anti-drift/evaluator.js +0 -417
- package/dist/anti-drift/evaluator.js.map +0 -1
- package/dist/anti-drift/evaluator.test.js +0 -78
- package/dist/anti-drift/rules.d.ts +0 -80
- package/dist/anti-drift/rules.d.ts.map +0 -1
- package/dist/anti-drift/rules.js +0 -368
- package/dist/anti-drift/rules.js.map +0 -1
- package/dist/anti-drift/rules.test.js +0 -213
- package/dist/anti-drift/state.d.ts +0 -107
- package/dist/anti-drift/state.d.ts.map +0 -1
- package/dist/anti-drift/state.js +0 -177
- package/dist/anti-drift/state.js.map +0 -1
- package/dist/anti-drift/state.test.js +0 -120
- package/dist/chat/adapters/discord.d.ts +0 -41
- package/dist/chat/adapters/discord.d.ts.map +0 -1
- package/dist/chat/adapters/discord.js +0 -176
- package/dist/chat/adapters/discord.js.map +0 -1
- package/dist/chat/adapters/discord.test.js +0 -25
- package/dist/chat/adapters/slack.d.ts +0 -43
- package/dist/chat/adapters/slack.d.ts.map +0 -1
- package/dist/chat/adapters/slack.js +0 -172
- package/dist/chat/adapters/slack.js.map +0 -1
- package/dist/chat/adapters/slack.test.js +0 -30
- package/dist/chat/adapters/telegram.d.ts +0 -148
- package/dist/chat/adapters/telegram.d.ts.map +0 -1
- package/dist/chat/adapters/telegram.js +0 -498
- package/dist/chat/adapters/telegram.js.map +0 -1
- package/dist/chat/adapters/telegram.test.js +0 -94
- package/dist/chat/config.d.ts +0 -98
- package/dist/chat/config.d.ts.map +0 -1
- package/dist/chat/config.js +0 -185
- package/dist/chat/config.js.map +0 -1
- package/dist/chat/daemon/active-project.d.ts +0 -17
- package/dist/chat/daemon/active-project.d.ts.map +0 -1
- package/dist/chat/daemon/active-project.js +0 -23
- package/dist/chat/daemon/active-project.js.map +0 -1
- package/dist/chat/daemon/autospawn.d.ts +0 -40
- package/dist/chat/daemon/autospawn.d.ts.map +0 -1
- package/dist/chat/daemon/autospawn.js +0 -129
- package/dist/chat/daemon/autospawn.js.map +0 -1
- package/dist/chat/daemon/autospawn.test.js +0 -112
- package/dist/chat/daemon/cli.d.ts +0 -18
- package/dist/chat/daemon/cli.d.ts.map +0 -1
- package/dist/chat/daemon/cli.js +0 -71
- package/dist/chat/daemon/cli.js.map +0 -1
- package/dist/chat/daemon/collisions.js +0 -384
- package/dist/chat/daemon/health-check.d.ts +0 -69
- package/dist/chat/daemon/health-check.d.ts.map +0 -1
- package/dist/chat/daemon/health-check.js +0 -112
- package/dist/chat/daemon/health-check.js.map +0 -1
- package/dist/chat/daemon/inbox-read.d.ts +0 -35
- package/dist/chat/daemon/inbox-read.d.ts.map +0 -1
- package/dist/chat/daemon/inbox-read.js +0 -75
- package/dist/chat/daemon/inbox-read.js.map +0 -1
- package/dist/chat/daemon/inbox-read.test.js +0 -97
- package/dist/chat/daemon/inbox.d.ts +0 -63
- package/dist/chat/daemon/inbox.d.ts.map +0 -1
- package/dist/chat/daemon/inbox.js +0 -56
- package/dist/chat/daemon/inbox.js.map +0 -1
- package/dist/chat/daemon/inbox.test.js +0 -110
- package/dist/chat/daemon/lifecycle.d.ts +0 -71
- package/dist/chat/daemon/lifecycle.d.ts.map +0 -1
- package/dist/chat/daemon/lifecycle.js +0 -221
- package/dist/chat/daemon/lifecycle.js.map +0 -1
- package/dist/chat/daemon/lifecycle.test.js +0 -163
- package/dist/chat/daemon/protocol.d.ts +0 -107
- package/dist/chat/daemon/protocol.d.ts.map +0 -1
- package/dist/chat/daemon/protocol.js +0 -54
- package/dist/chat/daemon/protocol.js.map +0 -1
- package/dist/chat/daemon/routing.d.ts +0 -140
- package/dist/chat/daemon/routing.d.ts.map +0 -1
- package/dist/chat/daemon/routing.js +0 -198
- package/dist/chat/daemon/routing.js.map +0 -1
- package/dist/chat/daemon/routing.test.js +0 -259
- package/dist/chat/daemon/rpc-client.d.ts +0 -45
- package/dist/chat/daemon/rpc-client.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-client.js +0 -133
- package/dist/chat/daemon/rpc-client.js.map +0 -1
- package/dist/chat/daemon/rpc-server.d.ts +0 -39
- package/dist/chat/daemon/rpc-server.d.ts.map +0 -1
- package/dist/chat/daemon/rpc-server.js +0 -385
- package/dist/chat/daemon/rpc-server.js.map +0 -1
- package/dist/chat/daemon/rpc.test.js +0 -177
- package/dist/chat/daemon/subscribers.js +0 -257
- package/dist/chat/daemon/worker.d.ts +0 -27
- package/dist/chat/daemon/worker.d.ts.map +0 -1
- package/dist/chat/daemon/worker.js +0 -313
- package/dist/chat/daemon/worker.js.map +0 -1
- package/dist/chat/daemon/workspace-topic.js +0 -324
- package/dist/chat/env-token.d.ts +0 -60
- package/dist/chat/env-token.d.ts.map +0 -1
- package/dist/chat/env-token.js +0 -137
- package/dist/chat/env-token.js.map +0 -1
- package/dist/chat/env-token.test.js +0 -160
- package/dist/chat/factory.d.ts +0 -30
- package/dist/chat/factory.d.ts.map +0 -1
- package/dist/chat/factory.js +0 -50
- package/dist/chat/factory.js.map +0 -1
- package/dist/chat/factory.test.js +0 -55
- package/dist/chat/gateway.d.ts +0 -176
- package/dist/chat/gateway.d.ts.map +0 -1
- package/dist/chat/gateway.js +0 -146
- package/dist/chat/gateway.js.map +0 -1
- package/dist/chat/gateway.test.js +0 -192
- package/dist/claude-md.d.ts +0 -39
- package/dist/claude-md.d.ts.map +0 -1
- package/dist/claude-md.js +0 -113
- package/dist/claude-md.js.map +0 -1
- package/dist/claude-md.test.js +0 -91
- package/dist/codex/activate.d.ts +0 -66
- package/dist/codex/activate.d.ts.map +0 -1
- package/dist/codex/activate.js +0 -329
- package/dist/codex/activate.js.map +0 -1
- package/dist/codex/activate.test.js +0 -229
- package/dist/codex/bundled-default/bundled-default.test.js +0 -161
- package/dist/codex/cli-publish.test.js +0 -133
- package/dist/codex/cli.d.ts +0 -35
- package/dist/codex/cli.d.ts.map +0 -1
- package/dist/codex/cli.js +0 -554
- package/dist/codex/cli.js.map +0 -1
- package/dist/codex/cli.test.js +0 -277
- package/dist/codex/import-skill-md.d.ts +0 -53
- package/dist/codex/import-skill-md.d.ts.map +0 -1
- package/dist/codex/import-skill-md.js +0 -236
- package/dist/codex/import-skill-md.js.map +0 -1
- package/dist/codex/import-skill-md.test.js +0 -225
- package/dist/codex/loader.d.ts +0 -27
- package/dist/codex/loader.d.ts.map +0 -1
- package/dist/codex/loader.js +0 -86
- package/dist/codex/loader.js.map +0 -1
- package/dist/codex/loader.test.js +0 -75
- package/dist/codex/parse.d.ts +0 -28
- package/dist/codex/parse.d.ts.map +0 -1
- package/dist/codex/parse.js +0 -309
- package/dist/codex/parse.js.map +0 -1
- package/dist/codex/parse.test.js +0 -241
- package/dist/codex/store.d.ts +0 -87
- package/dist/codex/store.d.ts.map +0 -1
- package/dist/codex/store.js +0 -205
- package/dist/codex/store.js.map +0 -1
- package/dist/codex/store.test.js +0 -242
- package/dist/codex/types.d.ts +0 -398
- package/dist/codex/types.d.ts.map +0 -1
- package/dist/codex/types.js +0 -21
- package/dist/codex/types.js.map +0 -1
- package/dist/config.d.ts +0 -53
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -202
- package/dist/config.js.map +0 -1
- package/dist/config.test.js +0 -117
- package/dist/engine/cli.d.ts +0 -14
- package/dist/engine/cli.d.ts.map +0 -1
- package/dist/engine/cli.js +0 -171
- package/dist/engine/cli.js.map +0 -1
- package/dist/engine/client.d.ts +0 -219
- package/dist/engine/client.d.ts.map +0 -1
- package/dist/engine/client.js +0 -312
- package/dist/engine/client.js.map +0 -1
- package/dist/engine/config.d.ts +0 -62
- package/dist/engine/config.d.ts.map +0 -1
- package/dist/engine/config.js +0 -223
- package/dist/engine/config.js.map +0 -1
- package/dist/engine/index.d.ts +0 -17
- package/dist/engine/index.d.ts.map +0 -1
- package/dist/engine/index.js +0 -16
- package/dist/engine/index.js.map +0 -1
- package/dist/engine/resolver.d.ts +0 -62
- package/dist/engine/resolver.d.ts.map +0 -1
- package/dist/engine/resolver.js +0 -103
- package/dist/engine/resolver.js.map +0 -1
- package/dist/engine/singleton.d.ts +0 -95
- package/dist/engine/singleton.d.ts.map +0 -1
- package/dist/engine/singleton.js +0 -325
- package/dist/engine/singleton.js.map +0 -1
- package/dist/engine/types.d.ts +0 -402
- package/dist/engine/types.d.ts.map +0 -1
- package/dist/engine/types.js +0 -22
- package/dist/engine/types.js.map +0 -1
- package/dist/engine-binary-resolver.js +0 -110
- package/dist/engine-binary-resolver.test.js +0 -61
- package/dist/engine-cli.js +0 -60
- package/dist/engine-client.js +0 -301
- package/dist/engine-client.test.js +0 -118
- package/dist/functions/chain_state.d.ts +0 -51
- package/dist/functions/chain_state.d.ts.map +0 -1
- package/dist/functions/chain_state.js +0 -59
- package/dist/functions/chain_state.js.map +0 -1
- package/dist/hooks/drift-catalog.d.ts +0 -68
- package/dist/hooks/drift-catalog.d.ts.map +0 -1
- package/dist/hooks/drift-catalog.js +0 -184
- package/dist/hooks/drift-catalog.js.map +0 -1
- package/dist/hooks/drift-catalog.test.js +0 -154
- package/dist/hooks/drift-patterns.d.ts +0 -110
- package/dist/hooks/drift-patterns.d.ts.map +0 -1
- package/dist/hooks/drift-patterns.js +0 -289
- package/dist/hooks/drift-patterns.js.map +0 -1
- package/dist/hooks/drift-patterns.test.js +0 -325
- package/dist/hooks/engine-vocab-gate.d.ts +0 -108
- package/dist/hooks/engine-vocab-gate.d.ts.map +0 -1
- package/dist/hooks/engine-vocab-gate.js +0 -225
- package/dist/hooks/engine-vocab-gate.js.map +0 -1
- package/dist/hooks/engine-vocab-gate.test.js +0 -170
- package/dist/hooks/heartbeat.d.ts +0 -107
- package/dist/hooks/heartbeat.d.ts.map +0 -1
- package/dist/hooks/heartbeat.js +0 -316
- package/dist/hooks/heartbeat.js.map +0 -1
- package/dist/hooks/heartbeat.test.js +0 -393
- package/dist/hooks/honesty-ledger-session-scope.test.js +0 -100
- package/dist/hooks/honesty-ledger.d.ts +0 -123
- package/dist/hooks/honesty-ledger.d.ts.map +0 -1
- package/dist/hooks/honesty-ledger.js +0 -226
- package/dist/hooks/honesty-ledger.js.map +0 -1
- package/dist/hooks/honesty-ledger.test.js +0 -466
- package/dist/hooks/inline-report-check.d.ts +0 -63
- package/dist/hooks/inline-report-check.d.ts.map +0 -1
- package/dist/hooks/inline-report-check.js +0 -88
- package/dist/hooks/inline-report-check.js.map +0 -1
- package/dist/hooks/inline-report-check.test.js +0 -96
- package/dist/hooks/pre-tool-use.d.ts +0 -62
- package/dist/hooks/pre-tool-use.d.ts.map +0 -1
- package/dist/hooks/pre-tool-use.js +0 -342
- package/dist/hooks/pre-tool-use.js.map +0 -1
- package/dist/hooks/pre-tool-use.test.js +0 -134
- package/dist/hooks/session-end.d.ts +0 -15
- package/dist/hooks/session-end.d.ts.map +0 -1
- package/dist/hooks/session-end.js +0 -60
- package/dist/hooks/session-end.js.map +0 -1
- package/dist/hooks/session-end.test.js +0 -52
- package/dist/hooks/stop.d.ts +0 -35
- package/dist/hooks/stop.d.ts.map +0 -1
- package/dist/hooks/stop.js +0 -136
- package/dist/hooks/stop.js.map +0 -1
- package/dist/hooks/transcript-active-task.test.js +0 -342
- package/dist/hooks/transcript.d.ts +0 -26
- package/dist/hooks/transcript.d.ts.map +0 -1
- package/dist/hooks/transcript.js +0 -266
- package/dist/hooks/transcript.js.map +0 -1
- package/dist/hooks/transcript.test.js +0 -103
- package/dist/hooks/user-prompt-submit.d.ts +0 -74
- package/dist/hooks/user-prompt-submit.d.ts.map +0 -1
- package/dist/hooks/user-prompt-submit.js +0 -256
- package/dist/hooks/user-prompt-submit.js.map +0 -1
- package/dist/hooks/user-prompt-submit.test.js +0 -118
- package/dist/hooks/versioning-gate.d.ts +0 -101
- package/dist/hooks/versioning-gate.d.ts.map +0 -1
- package/dist/hooks/versioning-gate.js +0 -245
- package/dist/hooks/versioning-gate.js.map +0 -1
- package/dist/hooks/versioning-gate.test.js +0 -368
- package/dist/hooks/workflow-gate.d.ts +0 -64
- package/dist/hooks/workflow-gate.d.ts.map +0 -1
- package/dist/hooks/workflow-gate.js +0 -152
- package/dist/hooks/workflow-gate.js.map +0 -1
- package/dist/hooks/workflow-gate.test.js +0 -197
- package/dist/hooks-cli.d.ts +0 -25
- package/dist/hooks-cli.d.ts.map +0 -1
- package/dist/hooks-cli.js +0 -286
- package/dist/hooks-cli.js.map +0 -1
- package/dist/hooks-cli.test.js +0 -148
- package/dist/origin.d.ts +0 -16
- package/dist/origin.d.ts.map +0 -1
- package/dist/origin.js +0 -92
- package/dist/origin.js.map +0 -1
- package/dist/packs/seed_lessons_ingest.d.ts +0 -30
- package/dist/packs/seed_lessons_ingest.d.ts.map +0 -1
- package/dist/packs/seed_lessons_ingest.js +0 -107
- package/dist/packs/seed_lessons_ingest.js.map +0 -1
- package/dist/project-cli.d.ts +0 -7
- package/dist/project-cli.d.ts.map +0 -1
- package/dist/project-cli.js +0 -145
- package/dist/project-cli.js.map +0 -1
- package/dist/project.d.ts +0 -127
- package/dist/project.d.ts.map +0 -1
- package/dist/project.js +0 -281
- package/dist/project.js.map +0 -1
- package/dist/project.test.js +0 -287
- package/dist/rag/backends/loop_engine.d.ts +0 -61
- package/dist/rag/backends/loop_engine.d.ts.map +0 -1
- package/dist/rag/backends/loop_engine.js +0 -160
- package/dist/rag/backends/loop_engine.js.map +0 -1
- package/dist/recall.d.ts +0 -82
- package/dist/recall.d.ts.map +0 -1
- package/dist/recall.js +0 -81
- package/dist/recall.js.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.d.ts +0 -131
- package/dist/runtime/agent_bridge/autospawn.d.ts.map +0 -1
- package/dist/runtime/agent_bridge/autospawn.js +0 -251
- package/dist/runtime/agent_bridge/autospawn.js.map +0 -1
- package/dist/runtime/chain_state.d.ts +0 -124
- package/dist/runtime/chain_state.d.ts.map +0 -1
- package/dist/runtime/chain_state.js +0 -189
- package/dist/runtime/chain_state.js.map +0 -1
- package/dist/runtime/hooks/permission_decision.d.ts +0 -34
- package/dist/runtime/hooks/permission_decision.d.ts.map +0 -1
- package/dist/runtime/hooks/permission_decision.js +0 -39
- package/dist/runtime/hooks/permission_decision.js.map +0 -1
- package/dist/runtime/workflow_fsm.d.ts +0 -21
- package/dist/runtime/workflow_fsm.d.ts.map +0 -1
- package/dist/runtime/workflow_fsm.js +0 -25
- package/dist/runtime/workflow_fsm.js.map +0 -1
- package/dist/runtime/workflow_map.d.ts +0 -26
- package/dist/runtime/workflow_map.d.ts.map +0 -1
- package/dist/runtime/workflow_map.js +0 -38
- package/dist/runtime/workflow_map.js.map +0 -1
- package/dist/scope.d.ts +0 -48
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -111
- package/dist/scope.js.map +0 -1
- package/dist/setup/cli/topic_create_step.d.ts +0 -84
- package/dist/setup/cli/topic_create_step.d.ts.map +0 -1
- package/dist/setup/cli/topic_create_step.js +0 -213
- package/dist/setup/cli/topic_create_step.js.map +0 -1
- package/dist/system-export.d.ts +0 -65
- package/dist/system-export.d.ts.map +0 -1
- package/dist/system-export.js +0 -194
- package/dist/system-export.js.map +0 -1
- package/dist/utterance/classifier.d.ts +0 -53
- package/dist/utterance/classifier.d.ts.map +0 -1
- package/dist/utterance/classifier.js +0 -184
- package/dist/utterance/classifier.js.map +0 -1
- package/dist/utterance/classifier.test.js +0 -147
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for inline-report-check (0.7.30 / D3 inline variant).
|
|
3
|
-
*
|
|
4
|
-
* Covers checkInlineReportFormat + the count helpers. Stop-hook
|
|
5
|
-
* integration left to integration tests (it composes the existing
|
|
6
|
-
* BrokenPromise pipeline which already has its own coverage).
|
|
7
|
-
*/
|
|
8
|
-
import { describe, expect, it } from "vitest";
|
|
9
|
-
import { checkInlineReportFormat, countCommitHashes, countVersionRefs, hasPhasesBlock, } from "./inline-report-check.js";
|
|
10
|
-
describe("countVersionRefs", () => {
|
|
11
|
-
it("counts distinct 0.X.Y refs", () => {
|
|
12
|
-
expect(countVersionRefs("shipped 0.7.20 then 0.7.21 and 0.7.22")).toBe(3);
|
|
13
|
-
});
|
|
14
|
-
it("dedupes repeated mentions", () => {
|
|
15
|
-
expect(countVersionRefs("0.7.20 fixed in 0.7.20 again")).toBe(1);
|
|
16
|
-
});
|
|
17
|
-
it("returns 0 on plain prose with no version", () => {
|
|
18
|
-
expect(countVersionRefs("hello world, no versions here")).toBe(0);
|
|
19
|
-
});
|
|
20
|
-
it("does NOT count partial versions like '0.7'", () => {
|
|
21
|
-
expect(countVersionRefs("the 0.7 series")).toBe(0);
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
describe("countCommitHashes", () => {
|
|
25
|
-
it("counts distinct 7-char hashes", () => {
|
|
26
|
-
expect(countCommitHashes("see 92fe415 and 0e59ba3 and 40342e3")).toBe(3);
|
|
27
|
-
});
|
|
28
|
-
it("requires at least one a-f letter (excludes pure decimal)", () => {
|
|
29
|
-
// 7-digit decimals like timestamps shouldn't trigger.
|
|
30
|
-
expect(countCommitHashes("count is 1234567 and 9876543")).toBe(0);
|
|
31
|
-
});
|
|
32
|
-
it("counts full 40-char shas", () => {
|
|
33
|
-
expect(countCommitHashes("4eaf39d2d21f0540342e3e881d2c47cde6d4ab39 abc")).toBe(1);
|
|
34
|
-
});
|
|
35
|
-
it("dedupes repeated hashes", () => {
|
|
36
|
-
expect(countCommitHashes("92fe415 ... revisit 92fe415")).toBe(1);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe("hasPhasesBlock", () => {
|
|
40
|
-
it("true for 'PHASES:' uppercase block", () => {
|
|
41
|
-
expect(hasPhasesBlock("\nPHASES:\n- pre_research: ...")).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
it("true for 'phases:' lowercase", () => {
|
|
44
|
-
expect(hasPhasesBlock("phases:\n- x")).toBe(true);
|
|
45
|
-
});
|
|
46
|
-
it("true when 'PHASES' is followed by a newline", () => {
|
|
47
|
-
expect(hasPhasesBlock("PHASES\n- pre_research")).toBe(true);
|
|
48
|
-
});
|
|
49
|
-
it("false when 'phases' appears in unrelated prose", () => {
|
|
50
|
-
expect(hasPhasesBlock("we have three phases of testing")).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
describe("checkInlineReportFormat", () => {
|
|
54
|
-
it("flags multi-version status report without PHASES", () => {
|
|
55
|
-
const text = `Shipped 0.7.20, 0.7.21, 0.7.22 today. All tests green.`;
|
|
56
|
-
const v = checkInlineReportFormat(text);
|
|
57
|
-
expect(v).not.toBeNull();
|
|
58
|
-
expect(v.signals.version_refs).toBe(3);
|
|
59
|
-
});
|
|
60
|
-
it("flags multi-commit chain-ship without PHASES", () => {
|
|
61
|
-
const text = `Commits: 92fe415, 0e59ba3, 40342e3, d6275a1 all pushed.`;
|
|
62
|
-
const v = checkInlineReportFormat(text);
|
|
63
|
-
expect(v).not.toBeNull();
|
|
64
|
-
expect(v.signals.hash_refs).toBeGreaterThanOrEqual(2);
|
|
65
|
-
});
|
|
66
|
-
it("does NOT flag when PHASES block is present", () => {
|
|
67
|
-
const text = `🦑 #7 shipped 0.7.20, 0.7.21, 0.7.22.\n\nPHASES:\n- pre_research: ...\n- learn: ...\n- code: ...\n- test: ...\n- audit: ...\n- post_research: ...\n- fix: ...`;
|
|
68
|
-
expect(checkInlineReportFormat(text)).toBeNull();
|
|
69
|
-
});
|
|
70
|
-
it("does NOT flag a single-version prose mention (signal too weak)", () => {
|
|
71
|
-
const text = `I bumped opensquid to 0.7.30 in package.json.`;
|
|
72
|
-
expect(checkInlineReportFormat(text)).toBeNull();
|
|
73
|
-
});
|
|
74
|
-
it("does NOT flag a single-commit-hash prose mention", () => {
|
|
75
|
-
const text = `The commit 92fe415 introduced the D9 guard.`;
|
|
76
|
-
expect(checkInlineReportFormat(text)).toBeNull();
|
|
77
|
-
});
|
|
78
|
-
it("does NOT flag empty input", () => {
|
|
79
|
-
expect(checkInlineReportFormat("")).toBeNull();
|
|
80
|
-
});
|
|
81
|
-
it("matched_text condenses to <=120 chars + trims whitespace", () => {
|
|
82
|
-
const long = "Shipped 0.7.20 and 0.7.21 today.\n\n".repeat(10);
|
|
83
|
-
const v = checkInlineReportFormat(long);
|
|
84
|
-
expect(v).not.toBeNull();
|
|
85
|
-
expect(v.matched_text.length).toBeLessThanOrEqual(120);
|
|
86
|
-
});
|
|
87
|
-
it("dogfood: catches the 'where are my 7 phases' incident shape", () => {
|
|
88
|
-
// The user's complaint was the summary table I posted with 10
|
|
89
|
-
// versions and 10 commit hashes but no PHASES heading.
|
|
90
|
-
const text = `Final tally: 0.7.20 92fe415, 0.7.21 0e59ba3, 0.7.22 40342e3, 0.7.23 d6275a1, 0.7.24 e881d2c, 0.7.25 e47b65c, 0.7.26 2d21f05, 0.7.27 4ab3977, 0.7.28 47cde6d, 0.7.29 4eaf39d.`;
|
|
91
|
-
const v = checkInlineReportFormat(text);
|
|
92
|
-
expect(v).not.toBeNull();
|
|
93
|
-
expect(v.signals.version_refs).toBe(10);
|
|
94
|
-
expect(v.signals.hash_refs).toBe(10);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `opensquid hook pre-tool-use` — Claude Code PreToolUse hook handler.
|
|
3
|
-
*
|
|
4
|
-
* Wired in `~/.claude/settings.json`:
|
|
5
|
-
*
|
|
6
|
-
* {
|
|
7
|
-
* "hooks": {
|
|
8
|
-
* "PreToolUse": [
|
|
9
|
-
* {
|
|
10
|
-
* "matcher": "Bash",
|
|
11
|
-
* "hooks": [
|
|
12
|
-
* { "type": "command",
|
|
13
|
-
* "command": "node /path/to/opensquid/dist/index.js hook pre-tool-use" }
|
|
14
|
-
* ]
|
|
15
|
-
* }
|
|
16
|
-
* ]
|
|
17
|
-
* }
|
|
18
|
-
* }
|
|
19
|
-
*
|
|
20
|
-
* Stdin: JSON describing the planned tool call (Claude Code's hook
|
|
21
|
-
* input schema). Stdout: empty on no-op. Stderr: drift findings.
|
|
22
|
-
* Exit code: 0 to proceed, 2 to block the call.
|
|
23
|
-
*
|
|
24
|
-
* The catalog of intercepts lives in src/hooks/drift-patterns.ts and
|
|
25
|
-
* grows lesson-by-lesson as drifts are observed and the user endorses
|
|
26
|
-
* the rule.
|
|
27
|
-
*/
|
|
28
|
-
import { type ToolCallInput } from "./drift-patterns.js";
|
|
29
|
-
/**
|
|
30
|
-
* #173 — return a warning string when an active-task-gated MCP tool is
|
|
31
|
-
* about to be called without an in_progress TodoWrite task in the
|
|
32
|
-
* transcript. Returns `null` when the tool isn't gated, no transcript
|
|
33
|
-
* path is available, or an active task is detected.
|
|
34
|
-
*
|
|
35
|
-
* Transcript-read failures (missing file, malformed JSONL) swallow to
|
|
36
|
-
* null — the hook must never block a legitimate call on its own bug.
|
|
37
|
-
* The workflow-gate fail-opens silently in this case
|
|
38
|
-
* (workflow-gate.ts:97-100); this surface makes that fail-open
|
|
39
|
-
* observable from the call site.
|
|
40
|
-
*
|
|
41
|
-
* Exported for direct testing.
|
|
42
|
-
*/
|
|
43
|
-
export declare function checkActiveTaskRequirement(call: ToolCallInput, transcriptPath: string | undefined): Promise<string | null>;
|
|
44
|
-
/**
|
|
45
|
-
* 0.7.25 / D3 — when `chat_send` is called with a body that looks like
|
|
46
|
-
* a task-completion report (starts with the agent's `🦑 #<N>` marker),
|
|
47
|
-
* verify the body includes a `PHASES` heading + at least 7 phase
|
|
48
|
-
* lines per `[[feedback_telegram_reports]]`. Returns a non-blocking
|
|
49
|
-
* warning string when the format is missing, null otherwise.
|
|
50
|
-
*
|
|
51
|
-
* Catches D3: paragraph summary sent instead of the locked 7-phase
|
|
52
|
-
* format. Detection is heuristic (the agent could write a real
|
|
53
|
-
* non-report message starting with `🦑 #N` — accepted noise).
|
|
54
|
-
*
|
|
55
|
-
* Exported for direct testing.
|
|
56
|
-
*/
|
|
57
|
-
export declare function checkChatSendReportFormat(call: ToolCallInput): string | null;
|
|
58
|
-
/**
|
|
59
|
-
* Read JSON from stdin, evaluate drifts, exit with the appropriate code.
|
|
60
|
-
*/
|
|
61
|
-
export declare function runPreToolUseHook(): Promise<void>;
|
|
62
|
-
//# sourceMappingURL=pre-tool-use.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pre-tool-use.d.ts","sourceRoot":"","sources":["../../src.legacy/hooks/pre-tool-use.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAsB7E;;;;;;;;;;;;;GAaG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,aAAa,EACnB,cAAc,EAAE,MAAM,GAAG,SAAS,GACjC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAkB5E;AAoBD;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CA2KvD"}
|
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `opensquid hook pre-tool-use` — Claude Code PreToolUse hook handler.
|
|
3
|
-
*
|
|
4
|
-
* Wired in `~/.claude/settings.json`:
|
|
5
|
-
*
|
|
6
|
-
* {
|
|
7
|
-
* "hooks": {
|
|
8
|
-
* "PreToolUse": [
|
|
9
|
-
* {
|
|
10
|
-
* "matcher": "Bash",
|
|
11
|
-
* "hooks": [
|
|
12
|
-
* { "type": "command",
|
|
13
|
-
* "command": "node /path/to/opensquid/dist/index.js hook pre-tool-use" }
|
|
14
|
-
* ]
|
|
15
|
-
* }
|
|
16
|
-
* ]
|
|
17
|
-
* }
|
|
18
|
-
* }
|
|
19
|
-
*
|
|
20
|
-
* Stdin: JSON describing the planned tool call (Claude Code's hook
|
|
21
|
-
* input schema). Stdout: empty on no-op. Stderr: drift findings.
|
|
22
|
-
* Exit code: 0 to proceed, 2 to block the call.
|
|
23
|
-
*
|
|
24
|
-
* The catalog of intercepts lives in src/hooks/drift-patterns.ts and
|
|
25
|
-
* grows lesson-by-lesson as drifts are observed and the user endorses
|
|
26
|
-
* the rule.
|
|
27
|
-
*/
|
|
28
|
-
import { decide, findDrifts } from "./drift-patterns.js";
|
|
29
|
-
import { evaluateEngineVocabGate } from "./engine-vocab-gate.js";
|
|
30
|
-
import { clearRecallRequired, isRecallRequired } from "./heartbeat.js";
|
|
31
|
-
import { recordToolCall } from "./honesty-ledger.js";
|
|
32
|
-
import { readActiveTaskId } from "./transcript.js";
|
|
33
|
-
import { evaluateVersioningGate } from "./versioning-gate.js";
|
|
34
|
-
import { evaluateWorkflowGate } from "./workflow-gate.js";
|
|
35
|
-
/**
|
|
36
|
-
* MCP tools that participate in the drift-protection track. When any
|
|
37
|
-
* of these is called WITHOUT an in_progress TodoWrite task in the
|
|
38
|
-
* transcript, the workflow-gate / chat-routing signals end up writing
|
|
39
|
-
* to a ledger that no gate validates against. We can't BLOCK these
|
|
40
|
-
* calls (legitimate ad-hoc usage exists), but emit a loud stderr
|
|
41
|
-
* warning so the gap is visible. #173 / drift D1 structural fix
|
|
42
|
-
* (locked 2026-05-17).
|
|
43
|
-
*/
|
|
44
|
-
const ACTIVE_TASK_GATED_MCP_TOOLS = new Set([
|
|
45
|
-
"mcp__opensquid__log_phase",
|
|
46
|
-
"mcp__opensquid__chat_send",
|
|
47
|
-
]);
|
|
48
|
-
/**
|
|
49
|
-
* #173 — return a warning string when an active-task-gated MCP tool is
|
|
50
|
-
* about to be called without an in_progress TodoWrite task in the
|
|
51
|
-
* transcript. Returns `null` when the tool isn't gated, no transcript
|
|
52
|
-
* path is available, or an active task is detected.
|
|
53
|
-
*
|
|
54
|
-
* Transcript-read failures (missing file, malformed JSONL) swallow to
|
|
55
|
-
* null — the hook must never block a legitimate call on its own bug.
|
|
56
|
-
* The workflow-gate fail-opens silently in this case
|
|
57
|
-
* (workflow-gate.ts:97-100); this surface makes that fail-open
|
|
58
|
-
* observable from the call site.
|
|
59
|
-
*
|
|
60
|
-
* Exported for direct testing.
|
|
61
|
-
*/
|
|
62
|
-
export async function checkActiveTaskRequirement(call, transcriptPath) {
|
|
63
|
-
if (!ACTIVE_TASK_GATED_MCP_TOOLS.has(call.tool))
|
|
64
|
-
return null;
|
|
65
|
-
if (!transcriptPath)
|
|
66
|
-
return null;
|
|
67
|
-
try {
|
|
68
|
-
const activeTaskId = await readActiveTaskId(transcriptPath);
|
|
69
|
-
if (activeTaskId)
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
return (`🦑 [opensquid] ${call.tool} called without an in_progress TodoWrite task — ` +
|
|
76
|
-
`the entries it writes WON'T be validated by the workflow-gate. ` +
|
|
77
|
-
`Call TaskCreate (and set in_progress) first so the gate has an active task to enforce against.\n`);
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* 0.7.25 / D3 — when `chat_send` is called with a body that looks like
|
|
81
|
-
* a task-completion report (starts with the agent's `🦑 #<N>` marker),
|
|
82
|
-
* verify the body includes a `PHASES` heading + at least 7 phase
|
|
83
|
-
* lines per `[[feedback_telegram_reports]]`. Returns a non-blocking
|
|
84
|
-
* warning string when the format is missing, null otherwise.
|
|
85
|
-
*
|
|
86
|
-
* Catches D3: paragraph summary sent instead of the locked 7-phase
|
|
87
|
-
* format. Detection is heuristic (the agent could write a real
|
|
88
|
-
* non-report message starting with `🦑 #N` — accepted noise).
|
|
89
|
-
*
|
|
90
|
-
* Exported for direct testing.
|
|
91
|
-
*/
|
|
92
|
-
export function checkChatSendReportFormat(call) {
|
|
93
|
-
if (call.tool !== "mcp__opensquid__chat_send")
|
|
94
|
-
return null;
|
|
95
|
-
const text = call.input.text;
|
|
96
|
-
if (typeof text !== "string")
|
|
97
|
-
return null;
|
|
98
|
-
// Trigger: message starts with the report marker `🦑 #N`.
|
|
99
|
-
if (!/^\s*🦑\s+#\d/.test(text))
|
|
100
|
-
return null;
|
|
101
|
-
// Format requirement: must include a PHASES heading and the 7 phase
|
|
102
|
-
// names. The phase names match the codex's default workflow.
|
|
103
|
-
if (!/\bPHASES\b/.test(text)) {
|
|
104
|
-
return (`🦑 [opensquid] chat_send body looks like a task-completion report ` +
|
|
105
|
-
`(starts with \`🦑 #N\`) but is missing the \`PHASES\` block. ` +
|
|
106
|
-
`Per [[feedback_telegram_reports]] reports must list each phase ` +
|
|
107
|
-
`(pre_research, learn, code, test, audit, post_research, fix) ` +
|
|
108
|
-
`with a concrete one-line finding — not just ✅. Catches drift D3.\n`);
|
|
109
|
-
}
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Read JSON from stdin, evaluate drifts, exit with the appropriate code.
|
|
114
|
-
*/
|
|
115
|
-
export async function runPreToolUseHook() {
|
|
116
|
-
let raw = "";
|
|
117
|
-
for await (const chunk of process.stdin) {
|
|
118
|
-
raw += chunk;
|
|
119
|
-
}
|
|
120
|
-
// Empty stdin → nothing to evaluate. Some hook configurations may
|
|
121
|
-
// pipe nothing during tests; bail gracefully.
|
|
122
|
-
if (!raw.trim()) {
|
|
123
|
-
process.exit(0);
|
|
124
|
-
}
|
|
125
|
-
let payload;
|
|
126
|
-
try {
|
|
127
|
-
payload = JSON.parse(raw);
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
// Malformed input — don't block on opensquid's own bug.
|
|
131
|
-
process.stderr.write("[opensquid hook] malformed PreToolUse input — proceeding\n");
|
|
132
|
-
process.exit(0);
|
|
133
|
-
}
|
|
134
|
-
if (!payload.tool_name || typeof payload.tool_name !== "string") {
|
|
135
|
-
process.exit(0);
|
|
136
|
-
}
|
|
137
|
-
const call = {
|
|
138
|
-
tool: payload.tool_name,
|
|
139
|
-
input: payload.tool_input ?? {},
|
|
140
|
-
};
|
|
141
|
-
// #173 / 0.7.29 — D1 BLOCK upgrade. When an active-task-gated MCP
|
|
142
|
-
// tool is called without an in_progress TodoWrite task in the
|
|
143
|
-
// transcript, BLOCK (was: WARN). The design doc rule #1 says BLOCK
|
|
144
|
-
// is the right semantic: log_phase / chat_send without an active
|
|
145
|
-
// task is exactly the failure mode the workflow-gate exists to
|
|
146
|
-
// prevent, and a soft warning was empirically ignored every time.
|
|
147
|
-
// Bypass: OPENSQUID_SKIP_ACTIVE_TASK_GATE=1 for legitimate
|
|
148
|
-
// non-task-scoped calls.
|
|
149
|
-
const taskWarning = await checkActiveTaskRequirement(call, payload.transcript_path);
|
|
150
|
-
if (taskWarning) {
|
|
151
|
-
process.stderr.write(taskWarning);
|
|
152
|
-
if (!checkActiveTaskBypassEnv()) {
|
|
153
|
-
process.stderr.write(` This is a BLOCK (was WARN through 0.7.28). Call TaskCreate first.\n` +
|
|
154
|
-
` Override (genuine non-task-scoped call): set OPENSQUID_SKIP_ACTIVE_TASK_GATE=1.\n`);
|
|
155
|
-
process.exit(2);
|
|
156
|
-
}
|
|
157
|
-
process.stderr.write(` 🦑 [opensquid active-task-gate] BYPASSED via OPENSQUID_SKIP_ACTIVE_TASK_GATE=1\n`);
|
|
158
|
-
}
|
|
159
|
-
// 0.7.25 / D3 — when chat_send body looks like a report but is missing
|
|
160
|
-
// the 7-phase format, warn. Non-blocking.
|
|
161
|
-
const formatWarning = checkChatSendReportFormat(call);
|
|
162
|
-
if (formatWarning) {
|
|
163
|
-
process.stderr.write(formatWarning);
|
|
164
|
-
}
|
|
165
|
-
// 0.7.26 / D7 — heartbeat-recall enforcement. When UPS surfaced a
|
|
166
|
-
// heartbeat nudge this turn, the recall-required flag is set; block
|
|
167
|
-
// any mcp__opensquid__* tool other than `recall` until the agent
|
|
168
|
-
// actually calls recall. On a recall call, clear the flag.
|
|
169
|
-
if (payload.session_id) {
|
|
170
|
-
try {
|
|
171
|
-
if (call.tool === "mcp__opensquid__recall" && (await isRecallRequired(payload.session_id))) {
|
|
172
|
-
await clearRecallRequired(payload.session_id);
|
|
173
|
-
}
|
|
174
|
-
else if (call.tool.startsWith("mcp__opensquid__") &&
|
|
175
|
-
!checkRecallBypassEnv() &&
|
|
176
|
-
(await isRecallRequired(payload.session_id))) {
|
|
177
|
-
process.stderr.write(buildRecallRequiredBlockMessage(call.tool));
|
|
178
|
-
process.exit(2);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
catch (err) {
|
|
182
|
-
process.stderr.write(`[opensquid hook] recall-required check failed (proceeding): ${err instanceof Error ? err.message : err}\n`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
// v0.4: append to the honesty ledger so the Stop hook can reconcile
|
|
186
|
-
// assistant claims against tool calls. Best-effort — never block the
|
|
187
|
-
// call on a ledger write failure.
|
|
188
|
-
if (payload.session_id) {
|
|
189
|
-
try {
|
|
190
|
-
const inputSummary = summarizeInput(call.tool, call.input);
|
|
191
|
-
await recordToolCall(payload.session_id, call.tool, inputSummary);
|
|
192
|
-
}
|
|
193
|
-
catch (err) {
|
|
194
|
-
process.stderr.write(`[opensquid hook] honesty-ledger write failed (non-fatal): ${err instanceof Error ? err.message : err}\n`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
const hits = findDrifts(call);
|
|
198
|
-
const { exit, stderr } = decide(hits, call);
|
|
199
|
-
if (stderr)
|
|
200
|
-
process.stderr.write(stderr);
|
|
201
|
-
if (exit !== 0) {
|
|
202
|
-
// Existing drift gate already blocking; don't spend RPC budget on
|
|
203
|
-
// the workflow gate.
|
|
204
|
-
process.exit(exit);
|
|
205
|
-
}
|
|
206
|
-
// v0.6.1 workflow gate — when the tool call is `git commit` (not
|
|
207
|
-
// --amend, which has its own gate), check that the active task's
|
|
208
|
-
// phase ledger has the required phases logged. Engine-RPC-backed,
|
|
209
|
-
// so only fires when the engine binary is reachable. Fail-open on
|
|
210
|
-
// any error (per [[honesty-ledger]] precedent — never block on
|
|
211
|
-
// opensquid's own bug). Spawning the engine just for this check is
|
|
212
|
-
// expensive (~hundreds of ms per hook), so it's scoped tightly to
|
|
213
|
-
// commit commands.
|
|
214
|
-
if (looksLikeGitCommit(call)) {
|
|
215
|
-
try {
|
|
216
|
-
const gateResult = await evaluateWorkflowGate({
|
|
217
|
-
transcriptPath: payload.transcript_path,
|
|
218
|
-
});
|
|
219
|
-
if (gateResult.block) {
|
|
220
|
-
process.stderr.write(gateResult.stderr);
|
|
221
|
-
process.exit(2);
|
|
222
|
-
}
|
|
223
|
-
else if (gateResult.stderr) {
|
|
224
|
-
// Warning only — proceed.
|
|
225
|
-
process.stderr.write(gateResult.stderr);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
catch (err) {
|
|
229
|
-
process.stderr.write(`[opensquid hook] workflow-gate failed (proceeding): ${err instanceof Error ? err.message : err}\n`);
|
|
230
|
-
}
|
|
231
|
-
// v0.6.3 versioning gate — enforce per-commit patch bumps. Local
|
|
232
|
-
// git diff inspection only (no RPC), so cheap. Same fail-open
|
|
233
|
-
// invariant as the workflow gate.
|
|
234
|
-
try {
|
|
235
|
-
const versionResult = await evaluateVersioningGate({ cwd: payload.cwd });
|
|
236
|
-
if (versionResult.block) {
|
|
237
|
-
process.stderr.write(versionResult.stderr);
|
|
238
|
-
process.exit(2);
|
|
239
|
-
}
|
|
240
|
-
else if (versionResult.stderr) {
|
|
241
|
-
process.stderr.write(versionResult.stderr);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
catch (err) {
|
|
245
|
-
process.stderr.write(`[opensquid hook] versioning-gate failed (proceeding): ${err instanceof Error ? err.message : err}\n`);
|
|
246
|
-
}
|
|
247
|
-
// 0.7.21 / D6 — engine vocabulary gate. Blocks engine commits whose
|
|
248
|
-
// -m message OR staged diff content references consumer-product
|
|
249
|
-
// names (opensquid, claude code, etc.). Early-exits on non-engine
|
|
250
|
-
// cwd; cheap when not applicable.
|
|
251
|
-
try {
|
|
252
|
-
const bashCmd = stringField(call.input, "command");
|
|
253
|
-
const vocabResult = await evaluateEngineVocabGate({
|
|
254
|
-
cwd: payload.cwd,
|
|
255
|
-
bashCommand: bashCmd ?? undefined,
|
|
256
|
-
});
|
|
257
|
-
if (vocabResult.block) {
|
|
258
|
-
process.stderr.write(vocabResult.stderr);
|
|
259
|
-
process.exit(2);
|
|
260
|
-
}
|
|
261
|
-
else if (vocabResult.stderr) {
|
|
262
|
-
process.stderr.write(vocabResult.stderr);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
catch (err) {
|
|
266
|
-
process.stderr.write(`[opensquid hook] engine-vocab-gate failed (proceeding): ${err instanceof Error ? err.message : err}\n`);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
process.exit(0);
|
|
270
|
-
}
|
|
271
|
-
function stringField(input, field) {
|
|
272
|
-
const v = input[field];
|
|
273
|
-
return typeof v === "string" ? v : null;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* 0.7.26 / D7 — emergency bypass for the recall-required block. For
|
|
277
|
-
* cases where the engine is unreachable and the agent can't call
|
|
278
|
-
* recall through normal channels.
|
|
279
|
-
*/
|
|
280
|
-
function checkRecallBypassEnv() {
|
|
281
|
-
return process.env.OPENSQUID_SKIP_RECALL_GATE === "1";
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* 0.7.29 / D1 BLOCK upgrade — emergency bypass for the active-task
|
|
285
|
-
* requirement. For legitimate non-task-scoped log_phase / chat_send
|
|
286
|
-
* calls (e.g. an ad-hoc one-shot ping to the user).
|
|
287
|
-
*/
|
|
288
|
-
function checkActiveTaskBypassEnv() {
|
|
289
|
-
return process.env.OPENSQUID_SKIP_ACTIVE_TASK_GATE === "1";
|
|
290
|
-
}
|
|
291
|
-
function buildRecallRequiredBlockMessage(tool) {
|
|
292
|
-
return (`🦑 [opensquid recall-gate] ${tool} blocked — heartbeat was surfaced ` +
|
|
293
|
-
`this turn but recall hasn't been called yet.\n` +
|
|
294
|
-
`Call \`mcp__opensquid__recall\` first to re-anchor on the active task, ` +
|
|
295
|
-
`then retry. Drift D7.\n` +
|
|
296
|
-
`Override (genuine emergency, e.g. engine unreachable): set ` +
|
|
297
|
-
`OPENSQUID_SKIP_RECALL_GATE=1 for this command.\n`);
|
|
298
|
-
}
|
|
299
|
-
function looksLikeGitCommit(call) {
|
|
300
|
-
if (call.tool !== "Bash")
|
|
301
|
-
return false;
|
|
302
|
-
const cmd = call.input.command;
|
|
303
|
-
if (typeof cmd !== "string")
|
|
304
|
-
return false;
|
|
305
|
-
// Match `git commit ...` but NOT `git commit --amend` (handled by
|
|
306
|
-
// existing drift pattern). Also skip cases where the command is
|
|
307
|
-
// clearly a comment or inside quotes (drift-patterns.ts uses the
|
|
308
|
-
// same quote-stripping; we duplicate the minimal version here).
|
|
309
|
-
const stripped = cmd.replace(/"[^"]*"/g, "").replace(/'[^']*'/g, "");
|
|
310
|
-
if (!/\bgit\s+commit\b/.test(stripped))
|
|
311
|
-
return false;
|
|
312
|
-
if (/\bgit\s+commit\b[^|;&]*--amend/.test(stripped))
|
|
313
|
-
return false;
|
|
314
|
-
return true;
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Tight summary of tool input for the ledger. We keep just enough to
|
|
318
|
-
* reconcile claims (e.g. "did the agent run npm test?") without writing
|
|
319
|
-
* the whole tool_input blob.
|
|
320
|
-
*/
|
|
321
|
-
function summarizeInput(tool, input) {
|
|
322
|
-
if (tool === "Bash") {
|
|
323
|
-
const cmd = input.command;
|
|
324
|
-
return typeof cmd === "string" ? cmd.slice(0, 500) : "";
|
|
325
|
-
}
|
|
326
|
-
if (tool === "Edit" || tool === "Write" || tool === "Read") {
|
|
327
|
-
const fp = input.file_path;
|
|
328
|
-
return typeof fp === "string" ? fp : "";
|
|
329
|
-
}
|
|
330
|
-
if (tool === "Agent") {
|
|
331
|
-
const desc = input.description ?? input.subagent_type ?? "";
|
|
332
|
-
return typeof desc === "string" ? desc.slice(0, 200) : "";
|
|
333
|
-
}
|
|
334
|
-
// Default — short JSON peek for unknown tools.
|
|
335
|
-
try {
|
|
336
|
-
return JSON.stringify(input).slice(0, 200);
|
|
337
|
-
}
|
|
338
|
-
catch {
|
|
339
|
-
return "";
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
//# sourceMappingURL=pre-tool-use.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pre-tool-use.js","sourceRoot":"","sources":["../../src.legacy/hooks/pre-tool-use.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pre-tool-use hook tests — focused on the #173 (drift D1) active-task
|
|
3
|
-
* requirement check. Runs against a synthetic transcript file with
|
|
4
|
-
* controllable TodoWrite shape.
|
|
5
|
-
*/
|
|
6
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
7
|
-
import * as crypto from "node:crypto";
|
|
8
|
-
import { promises as fs } from "node:fs";
|
|
9
|
-
import * as os from "node:os";
|
|
10
|
-
import * as path from "node:path";
|
|
11
|
-
import { checkActiveTaskRequirement, checkChatSendReportFormat } from "./pre-tool-use.js";
|
|
12
|
-
let tmpDir;
|
|
13
|
-
let transcriptPath;
|
|
14
|
-
beforeEach(async () => {
|
|
15
|
-
tmpDir = path.join(os.tmpdir(), `opensquid-ptu-${crypto.randomUUID()}`);
|
|
16
|
-
await fs.mkdir(tmpDir, { recursive: true });
|
|
17
|
-
transcriptPath = path.join(tmpDir, "transcript.jsonl");
|
|
18
|
-
});
|
|
19
|
-
afterEach(async () => {
|
|
20
|
-
await fs.rm(tmpDir, { recursive: true, force: true });
|
|
21
|
-
});
|
|
22
|
-
async function writeTranscriptWithActiveTask(taskId) {
|
|
23
|
-
const event = {
|
|
24
|
-
type: "assistant",
|
|
25
|
-
message: {
|
|
26
|
-
role: "assistant",
|
|
27
|
-
content: [
|
|
28
|
-
{
|
|
29
|
-
type: "tool_use",
|
|
30
|
-
name: "TodoWrite",
|
|
31
|
-
input: {
|
|
32
|
-
todos: [{ id: taskId, status: "in_progress", content: "active thing" }],
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
await fs.writeFile(transcriptPath, JSON.stringify(event) + "\n", "utf8");
|
|
39
|
-
}
|
|
40
|
-
describe("checkActiveTaskRequirement — #173 / drift D1 fix", () => {
|
|
41
|
-
it("returns null for tools that aren't active-task-gated", async () => {
|
|
42
|
-
const result = await checkActiveTaskRequirement({ tool: "Bash", input: { command: "ls" } }, transcriptPath);
|
|
43
|
-
expect(result).toBeNull();
|
|
44
|
-
});
|
|
45
|
-
it("returns null when no transcript path is provided", async () => {
|
|
46
|
-
const result = await checkActiveTaskRequirement({ tool: "mcp__opensquid__log_phase", input: {} }, undefined);
|
|
47
|
-
expect(result).toBeNull();
|
|
48
|
-
});
|
|
49
|
-
it("returns null when an in_progress task exists in the transcript", async () => {
|
|
50
|
-
await writeTranscriptWithActiveTask("42");
|
|
51
|
-
const result = await checkActiveTaskRequirement({ tool: "mcp__opensquid__log_phase", input: { task_id: "42", phase: "code" } }, transcriptPath);
|
|
52
|
-
expect(result).toBeNull();
|
|
53
|
-
});
|
|
54
|
-
it("returns a warning when log_phase is called with no in_progress task", async () => {
|
|
55
|
-
await fs.writeFile(transcriptPath, "", "utf8"); // empty transcript = no tasks
|
|
56
|
-
const result = await checkActiveTaskRequirement({ tool: "mcp__opensquid__log_phase", input: { task_id: "42", phase: "code" } }, transcriptPath);
|
|
57
|
-
expect(result).not.toBeNull();
|
|
58
|
-
expect(result).toContain("mcp__opensquid__log_phase called without an in_progress");
|
|
59
|
-
expect(result).toContain("TaskCreate");
|
|
60
|
-
});
|
|
61
|
-
it("returns a warning when chat_send is called with no in_progress task", async () => {
|
|
62
|
-
await fs.writeFile(transcriptPath, "", "utf8");
|
|
63
|
-
const result = await checkActiveTaskRequirement({ tool: "mcp__opensquid__chat_send", input: { channel: "telegram:1", text: "hi" } }, transcriptPath);
|
|
64
|
-
expect(result).not.toBeNull();
|
|
65
|
-
expect(result).toContain("mcp__opensquid__chat_send called without an in_progress");
|
|
66
|
-
});
|
|
67
|
-
it("emits the warning even when the transcript file is missing (readActiveTaskId returns null gracefully)", async () => {
|
|
68
|
-
// readActiveTaskId handles missing files internally and returns
|
|
69
|
-
// null. The D1 check treats null-active-task as "no in_progress
|
|
70
|
-
// signal" regardless of cause, which is the right semantic — the
|
|
71
|
-
// agent skipped TaskCreate is the failure mode either way.
|
|
72
|
-
const result = await checkActiveTaskRequirement({ tool: "mcp__opensquid__log_phase", input: {} }, path.join(tmpDir, "nonexistent.jsonl"));
|
|
73
|
-
expect(result).not.toBeNull();
|
|
74
|
-
expect(result).toContain("called without an in_progress");
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
describe("checkChatSendReportFormat — 0.7.25 / drift D3", () => {
|
|
78
|
-
it("returns null for tools other than chat_send", () => {
|
|
79
|
-
const result = checkChatSendReportFormat({
|
|
80
|
-
tool: "Bash",
|
|
81
|
-
input: { command: "🦑 #4 fake bash" },
|
|
82
|
-
});
|
|
83
|
-
expect(result).toBeNull();
|
|
84
|
-
});
|
|
85
|
-
it("returns null when text doesn't start with the 🦑 #N marker", () => {
|
|
86
|
-
const result = checkChatSendReportFormat({
|
|
87
|
-
tool: "mcp__opensquid__chat_send",
|
|
88
|
-
input: { text: "just a regular ping" },
|
|
89
|
-
});
|
|
90
|
-
expect(result).toBeNull();
|
|
91
|
-
});
|
|
92
|
-
it("returns null when text starts with the marker AND has PHASES heading", () => {
|
|
93
|
-
const result = checkChatSendReportFormat({
|
|
94
|
-
tool: "mcp__opensquid__chat_send",
|
|
95
|
-
input: {
|
|
96
|
-
text: "🦑 #170 — engine-client startupAck fix\n\n" +
|
|
97
|
-
"PHASES:\n" +
|
|
98
|
-
"- pre_research: surveyed engine-client.ts and engine-client.test.ts\n" +
|
|
99
|
-
"- learn: bug localized to startupAck memoization\n" +
|
|
100
|
-
"- code: 3-LOC fix in src/engine-client.ts:103-110\n" +
|
|
101
|
-
"- test: 2 new tests for respawn-after-exit\n" +
|
|
102
|
-
"- audit: reviewed for regressions; none found\n" +
|
|
103
|
-
"- post_research: docs note on resume behavior\n" +
|
|
104
|
-
"- fix: applied + committed\n",
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
expect(result).toBeNull();
|
|
108
|
-
});
|
|
109
|
-
it("WARNS when text starts with marker but PHASES heading missing", () => {
|
|
110
|
-
const result = checkChatSendReportFormat({
|
|
111
|
-
tool: "mcp__opensquid__chat_send",
|
|
112
|
-
input: {
|
|
113
|
-
text: "🦑 #170 — engine-client startupAck fix\n\nshipped, all green",
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
expect(result).not.toBeNull();
|
|
117
|
-
expect(result).toContain("missing the `PHASES` block");
|
|
118
|
-
expect(result).toContain("drift D3");
|
|
119
|
-
});
|
|
120
|
-
it("respects leading whitespace before the marker", () => {
|
|
121
|
-
const result = checkChatSendReportFormat({
|
|
122
|
-
tool: "mcp__opensquid__chat_send",
|
|
123
|
-
input: { text: " 🦑 #4 cleanup\n\nno phases here" },
|
|
124
|
-
});
|
|
125
|
-
expect(result).not.toBeNull();
|
|
126
|
-
});
|
|
127
|
-
it("returns null when text input is missing entirely", () => {
|
|
128
|
-
const result = checkChatSendReportFormat({
|
|
129
|
-
tool: "mcp__opensquid__chat_send",
|
|
130
|
-
input: { channel: "telegram:1" },
|
|
131
|
-
});
|
|
132
|
-
expect(result).toBeNull();
|
|
133
|
-
});
|
|
134
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `opensquid hook session-end` — Claude Code SessionEnd hook handler.
|
|
3
|
-
*
|
|
4
|
-
* Fires when a Claude Code session terminates. Wipes the session's
|
|
5
|
-
* honesty-ledger state (turn-ledger.jsonl + broken-promises.jsonl) so
|
|
6
|
-
* the ledger doesn't grow unbounded across all-time sessions.
|
|
7
|
-
*
|
|
8
|
-
* Without this hook, every Claude Code session creates a
|
|
9
|
-
* <data-root>/sessions/<id>/ directory with two JSONL files that
|
|
10
|
-
* never get cleaned up. Disk usage grows linearly with session count.
|
|
11
|
-
*
|
|
12
|
-
* Exit 0 always — SessionEnd is cleanup, not blocking.
|
|
13
|
-
*/
|
|
14
|
-
export declare function runSessionEndHook(): Promise<void>;
|
|
15
|
-
//# sourceMappingURL=session-end.d.ts.map
|