opensquid 0.5.441 → 0.5.449
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/functions/recall_pre_inject.d.ts.map +1 -1
- package/dist/functions/recall_pre_inject.js +12 -0
- package/dist/functions/recall_pre_inject.js.map +1 -1
- package/dist/rag/store_git.d.ts +23 -0
- package/dist/rag/store_git.d.ts.map +1 -0
- package/dist/rag/store_git.js +57 -0
- package/dist/rag/store_git.js.map +1 -0
- 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-end.js +11 -0
- package/dist/runtime/hooks/session-end.js.map +1 -1
- 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/docs/pack-runtime.md +15 -12
- package/docs/skill-grammar-guide.md +4 -4
- 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,256 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `opensquid hook user-prompt-submit` — Claude Code UserPromptSubmit
|
|
3
|
-
* hook handler.
|
|
4
|
-
*
|
|
5
|
-
* Fires when the user submits a prompt (start of every new turn).
|
|
6
|
-
* Surfaces TWO accumulator surfaces via stdout so the agent sees them
|
|
7
|
-
* in its system context:
|
|
8
|
-
*
|
|
9
|
-
* 1. broken-promises.jsonl — claims from the prior assistant turn
|
|
10
|
-
* that the honesty ledger flagged as unfulfilled.
|
|
11
|
-
* 2. heartbeat-pending.txt — token-threshold re-anchor nudge written
|
|
12
|
-
* by the Stop hook when the transcript grew past the configured
|
|
13
|
-
* threshold (default 20K tokens, OPENSQUID_HEARTBEAT_TOKENS).
|
|
14
|
-
*
|
|
15
|
-
* Both surfaces are CLEARED after surfacing — one chance per item per
|
|
16
|
-
* session, no infinite nagging.
|
|
17
|
-
*
|
|
18
|
-
* Exit 0 always — UserPromptSubmit is observational.
|
|
19
|
-
*
|
|
20
|
-
* Pre-#124: this hook also surfaced auto-classify-candidates.jsonl
|
|
21
|
-
* written by a detached LLM subprocess. Removed alongside the auto-
|
|
22
|
-
* classifier deletion — the agent classifies utterances inline per
|
|
23
|
-
* CLAUDE.md, and the heartbeat reminds it to re-anchor periodically.
|
|
24
|
-
*/
|
|
25
|
-
import * as crypto from "node:crypto";
|
|
26
|
-
import { promises as fs } from "node:fs";
|
|
27
|
-
import * as path from "node:path";
|
|
28
|
-
import { resolveDataRoot } from "../codex/store.js";
|
|
29
|
-
import { consumePendingHeartbeat, markRecallRequired } from "./heartbeat.js";
|
|
30
|
-
/**
|
|
31
|
-
* 0.7.10 (#164): minimum gap between consecutive UserPromptSubmit
|
|
32
|
-
* firings before we consider the session "resumed" rather than
|
|
33
|
-
* continuous. 5 minutes is short enough that a coffee-break doesn't
|
|
34
|
-
* trigger, long enough that genuine process restarts always do.
|
|
35
|
-
*/
|
|
36
|
-
const RESUME_GAP_MS = 5 * 60 * 1000;
|
|
37
|
-
export async function runUserPromptSubmitHook() {
|
|
38
|
-
let raw = "";
|
|
39
|
-
for await (const chunk of process.stdin) {
|
|
40
|
-
raw += chunk;
|
|
41
|
-
}
|
|
42
|
-
if (!raw.trim())
|
|
43
|
-
process.exit(0);
|
|
44
|
-
let payload;
|
|
45
|
-
try {
|
|
46
|
-
payload = JSON.parse(raw);
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
process.exit(0);
|
|
50
|
-
}
|
|
51
|
-
const sessionId = payload.session_id;
|
|
52
|
-
if (!sessionId)
|
|
53
|
-
process.exit(0);
|
|
54
|
-
const out = [];
|
|
55
|
-
// 0.7.10 (#164): detect resumed sessions and inject a re-anchor
|
|
56
|
-
// prompt. The signal is "gap since last UPS firing for this session
|
|
57
|
-
// > RESUME_GAP_MS." First firing ever writes the marker without
|
|
58
|
-
// injecting (the session just started; no resume happened yet).
|
|
59
|
-
const resumeMsg = await detectResumeAndUpdateMarker(sessionId);
|
|
60
|
-
if (resumeMsg)
|
|
61
|
-
out.push(resumeMsg);
|
|
62
|
-
// #112-audit finding 1: read+clear is racy if a writer appends concurrently.
|
|
63
|
-
// Rename-then-read atomically claims the file contents — any bytes that
|
|
64
|
-
// arrive after the rename land in a fresh file the next consumer will pick up.
|
|
65
|
-
const broken = await consumeJsonl(path.join(resolveDataRoot(), "sessions", sessionId, "broken-promises.jsonl"));
|
|
66
|
-
if (broken.length > 0) {
|
|
67
|
-
out.push("🦑 [opensquid honesty-ledger] unresolved claims from the previous turn:");
|
|
68
|
-
for (const p of broken) {
|
|
69
|
-
out.push(` 🦑 ${p.claim_id}: "${p.matched_text}" — needed ${p.claim_label}`);
|
|
70
|
-
}
|
|
71
|
-
out.push("Acknowledge these in your reply: either do the missing action now, or " +
|
|
72
|
-
"retract the claim explicitly. Don't repeat the pattern.");
|
|
73
|
-
}
|
|
74
|
-
// #124: heartbeat nudge surfaces here when Stop hook armed one. The agent
|
|
75
|
-
// sees this at the top of its context for the new turn and acts on it
|
|
76
|
-
// inline (calls recall, scans for substantive recent user turns, calls
|
|
77
|
-
// memorize/remember/promote per CLAUDE.md classify-and-act).
|
|
78
|
-
// 0.7.27 / D8 — surface a plan-mirror reminder when the user's
|
|
79
|
-
// prompt contains multiple task identifiers in sequence. Catches the
|
|
80
|
-
// "166 then 168" → agent does 166 + defers 168 misread.
|
|
81
|
-
if (typeof payload.prompt === "string") {
|
|
82
|
-
const mirror = detectMultiTaskDirective(payload.prompt);
|
|
83
|
-
if (mirror) {
|
|
84
|
-
if (out.length > 0)
|
|
85
|
-
out.push("");
|
|
86
|
-
out.push(mirror);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
const heartbeat = await consumePendingHeartbeat(sessionId);
|
|
90
|
-
if (heartbeat) {
|
|
91
|
-
if (out.length > 0)
|
|
92
|
-
out.push("");
|
|
93
|
-
out.push(heartbeat);
|
|
94
|
-
// 0.7.26 / D7 — set the recall-required flag. pre-tool-use will
|
|
95
|
-
// block any mcp__opensquid__* tool call (other than recall) until
|
|
96
|
-
// the agent actually calls recall. Catches the "heartbeat fires,
|
|
97
|
-
// agent acknowledges, continues without complying" drift.
|
|
98
|
-
try {
|
|
99
|
-
await markRecallRequired(sessionId);
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
process.stderr.write(`[opensquid hook user-prompt-submit] markRecallRequired failed (non-fatal): ${err instanceof Error ? err.message : err}\n`);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (out.length > 0) {
|
|
106
|
-
process.stdout.write(out.join("\n") + "\n");
|
|
107
|
-
}
|
|
108
|
-
process.exit(0);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* 0.7.27 / D8 — detect a multi-task directive in the user's prompt
|
|
112
|
-
* and return a "mirror back your parsed plan" reminder message.
|
|
113
|
-
*
|
|
114
|
-
* Trigger patterns (case-insensitive):
|
|
115
|
-
* - "<num1> then <num2>" — bare-number sequencing (the D8 incident
|
|
116
|
-
* shape: user said "166 then 168" — agent did 166 + deferred 168)
|
|
117
|
-
* - "first <ref> then <ref>"
|
|
118
|
-
* - "after <ref> do <ref>" / "after X then Y"
|
|
119
|
-
* - Two or more `#<num>` references
|
|
120
|
-
*
|
|
121
|
-
* Returns the surface message when a match fires, null otherwise.
|
|
122
|
-
* Exported for direct testing.
|
|
123
|
-
*/
|
|
124
|
-
export function detectMultiTaskDirective(prompt) {
|
|
125
|
-
const refs = extractTaskRefs(prompt);
|
|
126
|
-
if (refs.length < 2)
|
|
127
|
-
return null;
|
|
128
|
-
return (`🦑 [opensquid] multi-task directive detected (refs: ${refs.slice(0, 5).join(", ")}). ` +
|
|
129
|
-
`Per [[feedback_user_words_have_top_weight]] + drift D8: BEFORE executing, ` +
|
|
130
|
-
`mirror back your parsed plan ("read as: do X, then do Y") so we catch a ` +
|
|
131
|
-
`misread before it ships. Don't auto-defer items the user listed in parallel.`);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Extract probable task references from a user prompt. Returns the
|
|
135
|
-
* distinct references in document order, capped at 10.
|
|
136
|
-
*
|
|
137
|
-
* Heuristics (intentionally narrow to keep false-positives low):
|
|
138
|
-
* - `#\d+` shapes always count
|
|
139
|
-
* - bare 2-4-digit numbers count IF they're connected by a
|
|
140
|
-
* sequencing word ("then" / "after" / "first" / "and then")
|
|
141
|
-
*
|
|
142
|
-
* Exported for testing.
|
|
143
|
-
*/
|
|
144
|
-
export function extractTaskRefs(prompt) {
|
|
145
|
-
const refs = [];
|
|
146
|
-
const seen = new Set();
|
|
147
|
-
// Pass 1: explicit #N references
|
|
148
|
-
for (const m of prompt.matchAll(/#\d+/g)) {
|
|
149
|
-
if (!seen.has(m[0])) {
|
|
150
|
-
refs.push(m[0]);
|
|
151
|
-
seen.add(m[0]);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// Pass 2: bare-number sequences via "then" / "after" / "first ... then"
|
|
155
|
-
// Pattern: two 2-4-digit numbers separated by "then"/"after"/"and then".
|
|
156
|
-
for (const m of prompt.matchAll(/\b(\d{2,4})\s*(?:,\s*|\s+(?:then|after|and then|and)\s+)(\d{2,4})\b/gi)) {
|
|
157
|
-
for (const num of [m[1], m[2]]) {
|
|
158
|
-
const key = `#${num}`;
|
|
159
|
-
if (!seen.has(key)) {
|
|
160
|
-
refs.push(key);
|
|
161
|
-
seen.add(key);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return refs.slice(0, 10);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* 0.7.10 (#164): detect resumed sessions by tracking the wall-clock
|
|
169
|
-
* gap between consecutive UPS firings. Returns a re-anchor message
|
|
170
|
-
* when a gap exceeds RESUME_GAP_MS, OR null when continuous /
|
|
171
|
-
* first-ever firing.
|
|
172
|
-
*
|
|
173
|
-
* Marker file: ~/.opensquid/sessions/<sid>/ups-last-at.txt
|
|
174
|
-
* Contents: ISO 8601 timestamp of the last UPS firing.
|
|
175
|
-
*
|
|
176
|
-
* On EVERY call: read prior timestamp → write current timestamp.
|
|
177
|
-
* First call: marker missing → write current; return null (no resume
|
|
178
|
-
* happened, just the session starting).
|
|
179
|
-
* Subsequent call: gap < RESUME_GAP_MS → continuous (return null).
|
|
180
|
-
* Subsequent call: gap >= RESUME_GAP_MS → resumed (return message).
|
|
181
|
-
*
|
|
182
|
-
* Exported for direct testing.
|
|
183
|
-
*/
|
|
184
|
-
export async function detectResumeAndUpdateMarker(sessionId, options = {}) {
|
|
185
|
-
const root = resolveDataRoot(options.dataRoot);
|
|
186
|
-
const dir = path.join(root, "sessions", sessionId);
|
|
187
|
-
const markerPath = path.join(dir, "ups-last-at.txt");
|
|
188
|
-
const nowMs = options.now ?? Date.now();
|
|
189
|
-
let prior = null;
|
|
190
|
-
try {
|
|
191
|
-
const raw = (await fs.readFile(markerPath, "utf8")).trim();
|
|
192
|
-
const parsed = Date.parse(raw);
|
|
193
|
-
if (Number.isFinite(parsed))
|
|
194
|
-
prior = parsed;
|
|
195
|
-
}
|
|
196
|
-
catch {
|
|
197
|
-
/* no marker — first firing */
|
|
198
|
-
}
|
|
199
|
-
await fs.mkdir(dir, { recursive: true });
|
|
200
|
-
await fs.writeFile(markerPath, new Date(nowMs).toISOString() + "\n", "utf8");
|
|
201
|
-
if (prior === null)
|
|
202
|
-
return null; // first firing for this session
|
|
203
|
-
const gapMs = nowMs - prior;
|
|
204
|
-
if (gapMs < RESUME_GAP_MS)
|
|
205
|
-
return null;
|
|
206
|
-
const gapMin = Math.round(gapMs / 60000);
|
|
207
|
-
return (`🦑 [opensquid] Session resumed (${gapMin}m since last activity). ` +
|
|
208
|
-
`Before continuing, re-anchor: call \`recall\` for the active task, ` +
|
|
209
|
-
`scan recent assistant turns for any unfulfilled commitments, and ` +
|
|
210
|
-
`re-read any locked rule the next action would touch.`);
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Atomically claim a JSONL accumulator file: rename it to a unique
|
|
214
|
-
* sibling, then read+parse, then delete the renamed file. Any writer
|
|
215
|
-
* that opens the original path after the rename starts a fresh file
|
|
216
|
-
* that the next consumer will pick up.
|
|
217
|
-
*/
|
|
218
|
-
async function consumeJsonl(filePath) {
|
|
219
|
-
const claimed = `${filePath}.consuming.${crypto.randomUUID()}`;
|
|
220
|
-
try {
|
|
221
|
-
await fs.rename(filePath, claimed);
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
// ENOENT — nothing to consume
|
|
225
|
-
return [];
|
|
226
|
-
}
|
|
227
|
-
let raw;
|
|
228
|
-
try {
|
|
229
|
-
raw = await fs.readFile(claimed, "utf8");
|
|
230
|
-
}
|
|
231
|
-
catch {
|
|
232
|
-
raw = "";
|
|
233
|
-
}
|
|
234
|
-
try {
|
|
235
|
-
await fs.rm(claimed);
|
|
236
|
-
}
|
|
237
|
-
catch {
|
|
238
|
-
// already gone
|
|
239
|
-
}
|
|
240
|
-
if (!raw.trim())
|
|
241
|
-
return [];
|
|
242
|
-
const items = [];
|
|
243
|
-
for (const line of raw.split("\n")) {
|
|
244
|
-
const t = line.trim();
|
|
245
|
-
if (!t)
|
|
246
|
-
continue;
|
|
247
|
-
try {
|
|
248
|
-
items.push(JSON.parse(t));
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
// skip malformed line
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
return items;
|
|
255
|
-
}
|
|
256
|
-
//# sourceMappingURL=user-prompt-submit.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"user-prompt-submit.js","sourceRoot":"","sources":["../../src.legacy/hooks/user-prompt-submit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for UserPromptSubmit hook helpers — focused on the resume-
|
|
3
|
-
* detection logic added in 0.7.10 (#164).
|
|
4
|
-
*/
|
|
5
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
6
|
-
import * as crypto from "node:crypto";
|
|
7
|
-
import { promises as fs } from "node:fs";
|
|
8
|
-
import * as os from "node:os";
|
|
9
|
-
import * as path from "node:path";
|
|
10
|
-
import { detectMultiTaskDirective, detectResumeAndUpdateMarker, extractTaskRefs, } from "./user-prompt-submit.js";
|
|
11
|
-
let tmpRoot;
|
|
12
|
-
const SESSION = "test-session";
|
|
13
|
-
beforeEach(async () => {
|
|
14
|
-
tmpRoot = path.join(os.tmpdir(), `oscli-ups-${crypto.randomUUID()}`);
|
|
15
|
-
await fs.mkdir(tmpRoot, { recursive: true });
|
|
16
|
-
});
|
|
17
|
-
afterEach(async () => {
|
|
18
|
-
await fs.rm(tmpRoot, { recursive: true, force: true });
|
|
19
|
-
});
|
|
20
|
-
describe("detectResumeAndUpdateMarker (#164)", () => {
|
|
21
|
-
it("returns null on first firing (no marker yet) but creates the marker", async () => {
|
|
22
|
-
const now = Date.parse("2026-05-17T10:00:00Z");
|
|
23
|
-
const msg = await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now });
|
|
24
|
-
expect(msg).toBeNull();
|
|
25
|
-
// Marker should exist with current timestamp.
|
|
26
|
-
const written = await fs.readFile(path.join(tmpRoot, "sessions", SESSION, "ups-last-at.txt"), "utf8");
|
|
27
|
-
expect(written.trim()).toBe(new Date(now).toISOString());
|
|
28
|
-
});
|
|
29
|
-
it("returns null when gap < 5 minutes (continuous session)", async () => {
|
|
30
|
-
const first = Date.parse("2026-05-17T10:00:00Z");
|
|
31
|
-
await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: first });
|
|
32
|
-
const second = first + 60 * 1000; // 1 minute later
|
|
33
|
-
const msg = await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: second });
|
|
34
|
-
expect(msg).toBeNull();
|
|
35
|
-
});
|
|
36
|
-
it("returns a resume message when gap >= 5 minutes", async () => {
|
|
37
|
-
const first = Date.parse("2026-05-17T10:00:00Z");
|
|
38
|
-
await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: first });
|
|
39
|
-
const second = first + 6 * 60 * 1000; // 6 minutes later
|
|
40
|
-
const msg = await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: second });
|
|
41
|
-
expect(msg).not.toBeNull();
|
|
42
|
-
expect(msg).toContain("Session resumed");
|
|
43
|
-
expect(msg).toContain("6m");
|
|
44
|
-
expect(msg).toContain("re-anchor");
|
|
45
|
-
expect(msg).toContain("recall");
|
|
46
|
-
});
|
|
47
|
-
it("returns a resume message after a long gap (hours)", async () => {
|
|
48
|
-
const first = Date.parse("2026-05-17T00:00:00Z");
|
|
49
|
-
await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: first });
|
|
50
|
-
const second = first + 8 * 60 * 60 * 1000; // 8 hours later
|
|
51
|
-
const msg = await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: second });
|
|
52
|
-
expect(msg).not.toBeNull();
|
|
53
|
-
expect(msg).toContain("480m"); // 8h * 60min
|
|
54
|
-
});
|
|
55
|
-
it("updates the marker on every firing (so next gap is measured from the most-recent)", async () => {
|
|
56
|
-
const t0 = Date.parse("2026-05-17T10:00:00Z");
|
|
57
|
-
await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: t0 });
|
|
58
|
-
const t1 = t0 + 60_000;
|
|
59
|
-
await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: t1 });
|
|
60
|
-
const written = await fs.readFile(path.join(tmpRoot, "sessions", SESSION, "ups-last-at.txt"), "utf8");
|
|
61
|
-
expect(written.trim()).toBe(new Date(t1).toISOString());
|
|
62
|
-
});
|
|
63
|
-
it("returns null at exactly the 5-minute boundary (just under)", async () => {
|
|
64
|
-
const first = Date.parse("2026-05-17T10:00:00Z");
|
|
65
|
-
await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: first });
|
|
66
|
-
const second = first + 5 * 60 * 1000 - 1; // 4m59.999s
|
|
67
|
-
const msg = await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now: second });
|
|
68
|
-
expect(msg).toBeNull();
|
|
69
|
-
});
|
|
70
|
-
it("tolerates a corrupt marker file (returns null, overwrites)", async () => {
|
|
71
|
-
const dir = path.join(tmpRoot, "sessions", SESSION);
|
|
72
|
-
await fs.mkdir(dir, { recursive: true });
|
|
73
|
-
await fs.writeFile(path.join(dir, "ups-last-at.txt"), "not a date", "utf8");
|
|
74
|
-
const now = Date.parse("2026-05-17T10:00:00Z");
|
|
75
|
-
const msg = await detectResumeAndUpdateMarker(SESSION, { dataRoot: tmpRoot, now });
|
|
76
|
-
expect(msg).toBeNull();
|
|
77
|
-
// Should still have written the new timestamp.
|
|
78
|
-
const written = await fs.readFile(path.join(dir, "ups-last-at.txt"), "utf8");
|
|
79
|
-
expect(written.trim()).toBe(new Date(now).toISOString());
|
|
80
|
-
});
|
|
81
|
-
it("isolates per-session (gap on session A doesn't affect session B)", async () => {
|
|
82
|
-
const t0 = Date.parse("2026-05-17T10:00:00Z");
|
|
83
|
-
await detectResumeAndUpdateMarker("A", { dataRoot: tmpRoot, now: t0 });
|
|
84
|
-
// Session B's first firing 6h later — still its FIRST firing, so null.
|
|
85
|
-
const tLater = t0 + 6 * 60 * 60 * 1000;
|
|
86
|
-
const msgB = await detectResumeAndUpdateMarker("B", { dataRoot: tmpRoot, now: tLater });
|
|
87
|
-
expect(msgB).toBeNull();
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
describe("detectMultiTaskDirective — D8 (0.7.27)", () => {
|
|
91
|
-
it("fires on '166 then 168' (bare-number sequencing — D8 incident shape)", () => {
|
|
92
|
-
const m = detectMultiTaskDirective("166 then 168");
|
|
93
|
-
expect(m).not.toBeNull();
|
|
94
|
-
expect(m).toContain("#166");
|
|
95
|
-
expect(m).toContain("#168");
|
|
96
|
-
});
|
|
97
|
-
it("fires on '#171 then #172' (explicit references)", () => {
|
|
98
|
-
expect(detectMultiTaskDirective("#171 then #172")).not.toBeNull();
|
|
99
|
-
});
|
|
100
|
-
it("fires on '166, 168' comma-separated", () => {
|
|
101
|
-
expect(detectMultiTaskDirective("166, 168")).not.toBeNull();
|
|
102
|
-
});
|
|
103
|
-
it("does NOT fire on a single task reference", () => {
|
|
104
|
-
expect(detectMultiTaskDirective("work on #170")).toBeNull();
|
|
105
|
-
expect(detectMultiTaskDirective("can you do 168")).toBeNull();
|
|
106
|
-
});
|
|
107
|
-
it("does NOT fire on unrelated number prose", () => {
|
|
108
|
-
// No sequencing connector → no D8 risk.
|
|
109
|
-
expect(detectMultiTaskDirective("we shipped 5 patches in 30 minutes")).toBeNull();
|
|
110
|
-
});
|
|
111
|
-
it("extractTaskRefs returns refs in document order", () => {
|
|
112
|
-
expect(extractTaskRefs("166 then 168")).toEqual(["#166", "#168"]);
|
|
113
|
-
expect(extractTaskRefs("#171 and #172")).toEqual(["#171", "#172"]);
|
|
114
|
-
});
|
|
115
|
-
it("extractTaskRefs dedupes references", () => {
|
|
116
|
-
expect(extractTaskRefs("#170 then #170")).toEqual(["#170"]);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Versioning gate — pre-commit check enforcing per-commit patch bumps
|
|
3
|
-
* (v0.6.3). Wired into the PreToolUse hook for `git commit` commands.
|
|
4
|
-
*
|
|
5
|
-
* Problem this fixes: I keep batching multiple fixes into one commit
|
|
6
|
-
* and bumping the minor (or no bump at all) instead of one patch per
|
|
7
|
-
* fix. The discipline rule was memorized (`mem-d2cc0e78`) but rules
|
|
8
|
-
* I can ignore aren't structural protection. This gate makes the
|
|
9
|
-
* discipline mechanical — if your commit touches source code AND
|
|
10
|
-
* doesn't include a manifest version bump in the same commit, it
|
|
11
|
-
* gets rejected before `git commit` runs.
|
|
12
|
-
*
|
|
13
|
-
* Detection:
|
|
14
|
-
* 1. `git diff --cached --name-only` → list of staged files
|
|
15
|
-
* 2. If no `src/**` files staged → allow (docs/CI/config commits
|
|
16
|
-
* don't need version bumps)
|
|
17
|
-
* 3. If `src/**` files staged → require a manifest (Cargo.toml or
|
|
18
|
-
* package.json) to also be staged with a `version` line diff
|
|
19
|
-
* 4. Otherwise → block with actionable message
|
|
20
|
-
*
|
|
21
|
-
* Fail-open invariant: any error running git or parsing output →
|
|
22
|
-
* allow with a stderr warning (per the honesty-ledger + workflow-gate
|
|
23
|
-
* precedent — never block on opensquid's own bug).
|
|
24
|
-
*
|
|
25
|
-
* Emergency override: `OPENSQUID_SKIP_VERSION_GATE=1` bypasses with a
|
|
26
|
-
* loud stderr warning. For genuine emergencies (revert commits,
|
|
27
|
-
* generated-code-only diffs, etc.) where the discipline doesn't
|
|
28
|
-
* apply.
|
|
29
|
-
*/
|
|
30
|
-
export interface VersioningGateInput {
|
|
31
|
-
/** Working directory (the repo root). Defaults to process.cwd(). */
|
|
32
|
-
cwd?: string;
|
|
33
|
-
}
|
|
34
|
-
export interface VersioningGateResult {
|
|
35
|
-
/** True when the commit should be blocked. */
|
|
36
|
-
block: boolean;
|
|
37
|
-
/** Stderr message — always present when stderr should be written.
|
|
38
|
-
* Non-blocking warnings also use this. */
|
|
39
|
-
stderr: string;
|
|
40
|
-
}
|
|
41
|
-
export declare function evaluateVersioningGate(input?: VersioningGateInput): Promise<VersioningGateResult>;
|
|
42
|
-
/**
|
|
43
|
-
* Is this a source file that should trigger version-bump enforcement?
|
|
44
|
-
* Generous definition: anything under `src/` for any language we support.
|
|
45
|
-
*/
|
|
46
|
-
export declare function isSourceFile(p: string): boolean;
|
|
47
|
-
/** Is this the repo's version manifest? */
|
|
48
|
-
export declare function isManifestFile(p: string): boolean;
|
|
49
|
-
/**
|
|
50
|
-
* Parsed version jump from a manifest's staged diff.
|
|
51
|
-
*
|
|
52
|
-
* 0.7.23 / D5 — added so the gate can surface multi-patch catch-up
|
|
53
|
-
* jumps (e.g. 0.7.10 → 0.7.14) which indicate previous src commits
|
|
54
|
-
* shipped without bumps.
|
|
55
|
-
*/
|
|
56
|
-
export interface VersionJump {
|
|
57
|
-
from: string;
|
|
58
|
-
to: string;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Look at the staged diff of a manifest and return the version jump
|
|
62
|
-
* (from → to). Returns null when the diff doesn't touch a `version`
|
|
63
|
-
* line at all.
|
|
64
|
-
*
|
|
65
|
-
* Exported for direct testing.
|
|
66
|
-
*/
|
|
67
|
-
export declare function readManifestVersionBump(cwd: string, manifestPath: string): Promise<VersionJump | null>;
|
|
68
|
-
/**
|
|
69
|
-
* Parse `+`/`-` lines from a manifest diff and extract the version
|
|
70
|
-
* jump. Cargo: `version = "..."`. package.json: `"version": "..."`.
|
|
71
|
-
*
|
|
72
|
-
* Anchor discipline:
|
|
73
|
-
* - Cargo (TOML, line-oriented) → anchor `^version` so we don't
|
|
74
|
-
* match a dep with `version = "..."` in `[dependencies.foo]`.
|
|
75
|
-
* - package.json (JSON, can be MINIFIED single-line) → do NOT
|
|
76
|
-
* anchor; `"version"` can appear mid-line in minified JSON.
|
|
77
|
-
*
|
|
78
|
-
* Exported for direct testing.
|
|
79
|
-
*/
|
|
80
|
-
export declare function parseVersionJumpFromDiff(diff: string): VersionJump | null;
|
|
81
|
-
/**
|
|
82
|
-
* Detect a multi-patch jump: same major.minor, but patch advances by
|
|
83
|
-
* more than 1 (catch-up bump).
|
|
84
|
-
*
|
|
85
|
-
* Returns false for:
|
|
86
|
-
* - First-time bumps (from === "")
|
|
87
|
-
* - Same-patch (no actual jump, shouldn't happen with proper diff)
|
|
88
|
-
* - Minor/major bumps (those are user-authorized; PATCH-ONLY rule
|
|
89
|
-
* forbids the agent from naming them but doesn't make them "drift")
|
|
90
|
-
* - Non-SemVer version strings (best-effort parse)
|
|
91
|
-
*
|
|
92
|
-
* Exported for direct testing.
|
|
93
|
-
*/
|
|
94
|
-
export declare function isMultiPatchJump(jump: VersionJump): boolean;
|
|
95
|
-
/**
|
|
96
|
-
* Emergency-override env var. Loud stderr warning on bypass so it
|
|
97
|
-
* always shows up in scrollback / CI logs. Exported for the test
|
|
98
|
-
* suite.
|
|
99
|
-
*/
|
|
100
|
-
export declare function checkOverrideEnv(): boolean;
|
|
101
|
-
//# sourceMappingURL=versioning-gate.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"versioning-gate.d.ts","sourceRoot":"","sources":["../../src.legacy/hooks/versioning-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAOH,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACnC,8CAA8C;IAC9C,KAAK,EAAE,OAAO,CAAC;IACf;8CAC0C;IAC1C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,GAAE,mBAAwB,GAC9B,OAAO,CAAC,oBAAoB,CAAC,CAsF/B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAG/C;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAGjD;AAED;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAY7B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAuBzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAQ3D;AAmCD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
|