@toolu/opencode 6.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generated/GENERATED-NOTES.md +16 -0
- package/generated/agents/toolu--architect.md +52 -0
- package/generated/agents/toolu--deep-explore.md +65 -0
- package/generated/agents/toolu--implementer.md +50 -0
- package/generated/agents/toolu--quick-task.md +54 -0
- package/generated/agents/toolu--research-agent.md +101 -0
- package/generated/commands/toolu--commit.md +8 -0
- package/generated/commands/toolu--review-and-commit.md +8 -0
- package/generated/opencode.toolu.json +116 -0
- package/generated/skills/toolu--brainstorm/SKILL.md +57 -0
- package/generated/skills/toolu--brainstorm/references/design-questions.md +36 -0
- package/generated/skills/toolu--commit--skill/SKILL.md +11 -0
- package/generated/skills/toolu--debug/SKILL.md +54 -0
- package/generated/skills/toolu--deep-research/SKILL.md +51 -0
- package/generated/skills/toolu--execution/SKILL.md +77 -0
- package/generated/skills/toolu--orchestrator/SKILL.md +145 -0
- package/generated/skills/toolu--orchestrator/references/delegation-cost.md +104 -0
- package/generated/skills/toolu--orchestrator/references/model-routing.md +112 -0
- package/generated/skills/toolu--plan/SKILL.md +56 -0
- package/generated/skills/toolu--plan/references/ledger.md +18 -0
- package/generated/skills/toolu--plan-review/SKILL.md +48 -0
- package/generated/skills/toolu--review-and-commit--skill/SKILL.md +10 -0
- package/generated/skills/toolu--setup/SKILL.md +24 -0
- package/generated/skills/toolu--setup/scripts/setup.sh +206 -0
- package/generated/skills/toolu--spec/SKILL.md +67 -0
- package/generated/skills/toolu--spec-review/SKILL.md +50 -0
- package/generated/skills/toolu--test/SKILL.md +49 -0
- package/package.json +51 -0
- package/plugins/agent-browser/.claude-plugin/plugin.json +18 -0
- package/plugins/agent-browser/.codex-plugin/plugin.json +7 -0
- package/plugins/agent-browser/README.md +18 -0
- package/plugins/agent-browser/hooks/hooks.json +15 -0
- package/plugins/agent-browser/hooks/session-start.sh +42 -0
- package/plugins/agent-browser/skills/agent-browser/SKILL.md +77 -0
- package/plugins/agent-browser/skills/agent-browser/scripts/agent-browser.sh +80 -0
- package/plugins/ast-grep/.claude-plugin/plugin.json +18 -0
- package/plugins/ast-grep/.codex-plugin/plugin.json +7 -0
- package/plugins/ast-grep/README.md +21 -0
- package/plugins/ast-grep/hooks/hooks.json +15 -0
- package/plugins/ast-grep/hooks/post-tools.d/byte-savings.sh +67 -0
- package/plugins/ast-grep/hooks/pre-tools.d/search-nudge.sh +170 -0
- package/plugins/ast-grep/hooks/register.sh +68 -0
- package/plugins/ast-grep/scripts/byte-savings-report.sh +28 -0
- package/plugins/ast-grep/skills/ast-grep/SKILL.md +127 -0
- package/plugins/ast-grep/skills/ast-grep/references/ast-grep-advanced.md +44 -0
- package/plugins/ast-grep/skills/ast-grep/scripts/ast-grep.sh +103 -0
- package/plugins/context7/.claude-plugin/plugin.json +18 -0
- package/plugins/context7/.codex-plugin/plugin.json +7 -0
- package/plugins/context7/README.md +19 -0
- package/plugins/context7/hooks/hooks.json +15 -0
- package/plugins/context7/hooks/session-start.sh +42 -0
- package/plugins/context7/skills/context7/SKILL.md +64 -0
- package/plugins/context7/skills/context7/scripts/search.sh +151 -0
- package/plugins/exa-search/.claude-plugin/plugin.json +18 -0
- package/plugins/exa-search/.codex-plugin/plugin.json +7 -0
- package/plugins/exa-search/README.md +19 -0
- package/plugins/exa-search/hooks/hooks.json +15 -0
- package/plugins/exa-search/hooks/session-start.sh +34 -0
- package/plugins/exa-search/skills/exa-search/SKILL.md +91 -0
- package/plugins/exa-search/skills/exa-search/scripts/search.sh +221 -0
- package/plugins/jev/.claude-plugin/plugin.json +19 -0
- package/plugins/jev/.codex-plugin/plugin.json +7 -0
- package/plugins/jev/README.md +61 -0
- package/plugins/jev/hooks/hooks.json +25 -0
- package/plugins/jev/hooks/lib/common.sh +49 -0
- package/plugins/jev/hooks/session-start.sh +45 -0
- package/plugins/jev/hooks/user-prompt-submit.sh +41 -0
- package/plugins/jev/skills/jev/SKILL.md +81 -0
- package/plugins/jev/skills/jev/evals/README.md +98 -0
- package/plugins/jev/skills/jev/references/problem-solving.md +198 -0
- package/plugins/jev/skills/jev/scripts/jev.sh +405 -0
- package/plugins/jira/.claude-plugin/plugin.json +18 -0
- package/plugins/jira/.codex-plugin/plugin.json +7 -0
- package/plugins/jira/README.md +42 -0
- package/plugins/jira/hooks/hooks.json +15 -0
- package/plugins/jira/hooks/session-start.sh +34 -0
- package/plugins/jira/skills/jira/SKILL.md +123 -0
- package/plugins/jira/skills/jira/scripts/jira.sh +70 -0
- package/plugins/jira/skills/jira/scripts/lib/adf.sh +17 -0
- package/plugins/jira/skills/jira/scripts/lib/attachment.sh +94 -0
- package/plugins/jira/skills/jira/scripts/lib/board.sh +58 -0
- package/plugins/jira/skills/jira/scripts/lib/http.sh +184 -0
- package/plugins/jira/skills/jira/scripts/lib/issue.sh +159 -0
- package/plugins/jira/skills/jira/scripts/lib/paginate.sh +45 -0
- package/plugins/jira/skills/jira/scripts/lib/plan-parse.sh +70 -0
- package/plugins/jira/skills/jira/scripts/lib/plan-run.sh +115 -0
- package/plugins/jira/skills/jira/scripts/lib/plan-store.sh +168 -0
- package/plugins/jira/skills/jira/scripts/lib/plan.sh +108 -0
- package/plugins/jira/skills/jira/scripts/lib/project.sh +42 -0
- package/plugins/jira/skills/jira/scripts/lib/raw.sh +15 -0
- package/plugins/jira/skills/jira/scripts/lib/search.sh +41 -0
- package/plugins/jira/skills/jira/scripts/lib/sprint.sh +88 -0
- package/plugins/jira/skills/jira/scripts/lib/user.sh +45 -0
- package/plugins/jira/skills/jira/scripts/lib/worklog.sh +56 -0
- package/plugins/pr-babysit/.claude-plugin/plugin.json +25 -0
- package/plugins/pr-babysit/.codex-plugin/plugin.json +7 -0
- package/plugins/pr-babysit/README.md +38 -0
- package/plugins/pr-babysit/commands/babysit.md +13 -0
- package/plugins/pr-babysit/hooks/check-toolu.sh +20 -0
- package/plugins/pr-babysit/hooks/hooks.json +15 -0
- package/plugins/pr-babysit/scripts/babysit-tick.sh +133 -0
- package/plugins/pr-babysit/scripts/collect-pr.sh +224 -0
- package/plugins/pr-babysit/scripts/lib/common.sh +146 -0
- package/plugins/pr-babysit/scripts/lib/gh.sh +150 -0
- package/plugins/pr-babysit/scripts/lib/lock.sh +84 -0
- package/plugins/pr-babysit/scripts/lib/normalize.sh +98 -0
- package/plugins/pr-babysit/scripts/lib/state.sh +37 -0
- package/plugins/pr-babysit/scripts/parse-verdict.sh +171 -0
- package/plugins/pr-babysit/scripts/record.sh +69 -0
- package/plugins/pr-babysit/scripts/reduce-state.sh +245 -0
- package/plugins/pr-babysit/scripts/reply-thread.sh +90 -0
- package/plugins/pr-babysit/scripts/resolve-thread.sh +71 -0
- package/plugins/pr-babysit/skills/babysit/SKILL.md +31 -0
- package/plugins/pr-babysit/skills/babysit/references/helper.md +209 -0
- package/plugins/pr-babysit/workflows/babysit.md +722 -0
- package/plugins/python-quality/.claude-plugin/plugin.json +24 -0
- package/plugins/python-quality/.codex-plugin/plugin.json +6 -0
- package/plugins/python-quality/README.md +23 -0
- package/plugins/python-quality/hooks/check-toolu.sh +20 -0
- package/plugins/python-quality/hooks/concerns/00-preamble.sh +85 -0
- package/plugins/python-quality/hooks/concerns/10-size-file.sh +6 -0
- package/plugins/python-quality/hooks/concerns/20-tests.sh +37 -0
- package/plugins/python-quality/hooks/concerns/30-suppression.sh +33 -0
- package/plugins/python-quality/hooks/concerns/50-size-fn.sh +49 -0
- package/plugins/python-quality/hooks/concerns/70-no-mocks.sh +76 -0
- package/plugins/python-quality/hooks/concerns/90-docs.sh +52 -0
- package/plugins/python-quality/hooks/concerns/99-finalize.sh +36 -0
- package/plugins/python-quality/hooks/hooks.json +19 -0
- package/plugins/python-quality/hooks/register.sh +65 -0
- package/plugins/rust-quality/.claude-plugin/plugin.json +24 -0
- package/plugins/rust-quality/.codex-plugin/plugin.json +6 -0
- package/plugins/rust-quality/README.md +24 -0
- package/plugins/rust-quality/hooks/check-toolu.sh +20 -0
- package/plugins/rust-quality/hooks/concerns/00-preamble.sh +93 -0
- package/plugins/rust-quality/hooks/concerns/10-size-file.sh +10 -0
- package/plugins/rust-quality/hooks/concerns/20-tests.sh +84 -0
- package/plugins/rust-quality/hooks/concerns/30-suppression.sh +26 -0
- package/plugins/rust-quality/hooks/concerns/40-unsafe.sh +39 -0
- package/plugins/rust-quality/hooks/concerns/50-size-fn.sh +38 -0
- package/plugins/rust-quality/hooks/concerns/55-size-impl.sh +28 -0
- package/plugins/rust-quality/hooks/concerns/60-error-handling.sh +149 -0
- package/plugins/rust-quality/hooks/concerns/70-no-mocks.sh +139 -0
- package/plugins/rust-quality/hooks/concerns/90-docs.sh +32 -0
- package/plugins/rust-quality/hooks/concerns/99-finalize.sh +36 -0
- package/plugins/rust-quality/hooks/hooks.json +19 -0
- package/plugins/rust-quality/hooks/register.sh +65 -0
- package/plugins/statusline/.claude-plugin/plugin.json +19 -0
- package/plugins/statusline/.codex-plugin/plugin.json +7 -0
- package/plugins/statusline/README.md +108 -0
- package/plugins/statusline/commands/setup.md +30 -0
- package/plugins/statusline/hooks/hooks.json +15 -0
- package/plugins/statusline/hooks/session-start.sh +45 -0
- package/plugins/statusline/scripts/collect-status.sh +147 -0
- package/plugins/statusline/scripts/setup.sh +93 -0
- package/plugins/statusline/scripts/status.sh +55 -0
- package/plugins/statusline/skills/status/SKILL.md +13 -0
- package/plugins/statusline/statusline.sh +177 -0
- package/plugins/toolu/.claude-plugin/plugin.json +19 -0
- package/plugins/toolu/.codex-plugin/plugin.json +7 -0
- package/plugins/toolu/README.md +35 -0
- package/plugins/toolu/agents/.gitkeep +0 -0
- package/plugins/toolu/agents/architect.md +52 -0
- package/plugins/toolu/agents/deep-explore.md +65 -0
- package/plugins/toolu/agents/implementer.md +50 -0
- package/plugins/toolu/agents/quick-task.md +54 -0
- package/plugins/toolu/agents/research-agent.md +107 -0
- package/plugins/toolu/assets/agents/architect.toml +9 -0
- package/plugins/toolu/assets/agents/deep-explore.toml +9 -0
- package/plugins/toolu/assets/agents/implementer.toml +9 -0
- package/plugins/toolu/assets/agents/quick-task.toml +9 -0
- package/plugins/toolu/assets/agents/research-agent.toml +9 -0
- package/plugins/toolu/commands/.gitkeep +0 -0
- package/plugins/toolu/commands/commit.md +5 -0
- package/plugins/toolu/commands/review-and-commit.md +5 -0
- package/plugins/toolu/hooks/docs/gates.md +157 -0
- package/plugins/toolu/hooks/docs/model-routing.md +14 -0
- package/plugins/toolu/hooks/docs/post-compaction.md +3 -0
- package/plugins/toolu/hooks/docs/push-review.md +123 -0
- package/plugins/toolu/hooks/docs/session-start-python.md +3 -0
- package/plugins/toolu/hooks/docs/session-start-rust.md +4 -0
- package/plugins/toolu/hooks/docs/session-start-ts.md +3 -0
- package/plugins/toolu/hooks/docs/session-start.md +16 -0
- package/plugins/toolu/hooks/hooks.json +76 -0
- package/plugins/toolu/hooks/lib/config.sh +355 -0
- package/plugins/toolu/hooks/lib/detect.sh +929 -0
- package/plugins/toolu/hooks/lib/diff-sha.sh +32 -0
- package/plugins/toolu/hooks/lib/dispatch.sh +365 -0
- package/plugins/toolu/hooks/lib/docs-sync-config.sh +81 -0
- package/plugins/toolu/hooks/lib/edit-records.sh +157 -0
- package/plugins/toolu/hooks/lib/gate-file.sh +201 -0
- package/plugins/toolu/hooks/lib/gate-mode.sh +244 -0
- package/plugins/toolu/hooks/lib/host.sh +205 -0
- package/plugins/toolu/hooks/lib/permissions.sh +125 -0
- package/plugins/toolu/hooks/lib/plan-ledger-parse.sh +219 -0
- package/plugins/toolu/hooks/lib/plan-ledger-preflight.sh +137 -0
- package/plugins/toolu/hooks/lib/plan-ledger.sh +749 -0
- package/plugins/toolu/hooks/lib/push-waiver.sh +116 -0
- package/plugins/toolu/hooks/lib/quality-config.sh +184 -0
- package/plugins/toolu/hooks/lib/registry.sh +72 -0
- package/plugins/toolu/hooks/lib/state-sweeper.sh +194 -0
- package/plugins/toolu/hooks/lib/telemetry.sh +94 -0
- package/plugins/toolu/hooks/lib/verdict.sh +477 -0
- package/plugins/toolu/hooks/post-tools/mod.sh +42 -0
- package/plugins/toolu/hooks/post-tools/modules/gate-status.sh +140 -0
- package/plugins/toolu/hooks/post-tools/modules/push-waiver.sh +97 -0
- package/plugins/toolu/hooks/pre-compact.sh +18 -0
- package/plugins/toolu/hooks/pre-tools/agent-tier.sh +111 -0
- package/plugins/toolu/hooks/pre-tools/mod.sh +37 -0
- package/plugins/toolu/hooks/pre-tools/modules/bash-commands.sh +187 -0
- package/plugins/toolu/hooks/pre-tools/modules/code-edit-rules.sh +78 -0
- package/plugins/toolu/hooks/pre-tools/modules/commit-gate.sh +70 -0
- package/plugins/toolu/hooks/pre-tools/modules/docs-sync.sh +137 -0
- package/plugins/toolu/hooks/pre-tools/modules/mcp-blocker.sh +151 -0
- package/plugins/toolu/hooks/pre-tools/modules/plan-ledger.sh +201 -0
- package/plugins/toolu/hooks/pre-tools/modules/protected-files.sh +177 -0
- package/plugins/toolu/hooks/pre-tools/modules/push-review.sh +273 -0
- package/plugins/toolu/hooks/pre-tools/modules/quality-gate.sh +87 -0
- package/plugins/toolu/hooks/session-start.sh +379 -0
- package/plugins/toolu/hooks/user-prompt-submit.sh +197 -0
- package/plugins/toolu/scripts/context-budget.sh +111 -0
- package/plugins/toolu/scripts/debug-log.sh +98 -0
- package/plugins/toolu/scripts/debug-stack.sh +108 -0
- package/plugins/toolu/scripts/debug-testfail.sh +97 -0
- package/plugins/toolu/settings/.gitkeep +0 -0
- package/plugins/toolu/settings/README.md +127 -0
- package/plugins/toolu/settings/bash-allowlist.txt +12 -0
- package/plugins/toolu/settings/bash-denylist.txt +25 -0
- package/plugins/toolu/settings/code-edit-rules.json +67 -0
- package/plugins/toolu/settings/commit-prefixes.txt +12 -0
- package/plugins/toolu/settings/mcp-blocklist.txt +8 -0
- package/plugins/toolu/settings/permissions.fragment.json +36 -0
- package/plugins/toolu/settings/protected-files.txt +30 -0
- package/plugins/toolu/settings/rust-unsafe-exemptions.txt +5 -0
- package/plugins/toolu/settings/toolu.config.example.json +130 -0
- package/plugins/toolu/skills/.gitkeep +0 -0
- package/plugins/toolu/skills/brainstorm/SKILL.md +57 -0
- package/plugins/toolu/skills/brainstorm/references/design-questions.md +36 -0
- package/plugins/toolu/skills/commit/SKILL.md +11 -0
- package/plugins/toolu/skills/debug/SKILL.md +54 -0
- package/plugins/toolu/skills/debug/evals/README.md +17 -0
- package/plugins/toolu/skills/debug/evals/cases.json +24 -0
- package/plugins/toolu/skills/debug/evals/results.json +34 -0
- package/plugins/toolu/skills/deep-research/SKILL.md +51 -0
- package/plugins/toolu/skills/execution/SKILL.md +77 -0
- package/plugins/toolu/skills/orchestrator/SKILL.md +145 -0
- package/plugins/toolu/skills/orchestrator/references/delegation-cost.md +104 -0
- package/plugins/toolu/skills/orchestrator/references/model-routing.md +112 -0
- package/plugins/toolu/skills/plan/SKILL.md +56 -0
- package/plugins/toolu/skills/plan/references/ledger.md +18 -0
- package/plugins/toolu/skills/plan-review/SKILL.md +48 -0
- package/plugins/toolu/skills/review-and-commit/SKILL.md +10 -0
- package/plugins/toolu/skills/setup/SKILL.md +24 -0
- package/plugins/toolu/skills/setup/scripts/setup.sh +206 -0
- package/plugins/toolu/skills/spec/SKILL.md +67 -0
- package/plugins/toolu/skills/spec-review/SKILL.md +50 -0
- package/plugins/toolu/skills/test/SKILL.md +49 -0
- package/plugins/toolu/workflows/commit.md +21 -0
- package/plugins/toolu/workflows/host-mapping.md +22 -0
- package/plugins/toolu/workflows/review-and-commit.md +54 -0
- package/plugins/toolu/workflows/semantic-judgments.md +35 -0
- package/plugins/toolu-review/.claude-plugin/plugin.json +18 -0
- package/plugins/toolu-review/.codex-plugin/plugin.json +7 -0
- package/plugins/toolu-review/README.md +17 -0
- package/plugins/toolu-review/hooks/hooks.json +15 -0
- package/plugins/toolu-review/hooks/session-start.sh +34 -0
- package/plugins/toolu-review/skills/review/SKILL.md +90 -0
- package/plugins/toolu-review/skills/review/scripts/write-state.sh +189 -0
- package/plugins/ts-quality/.claude-plugin/plugin.json +24 -0
- package/plugins/ts-quality/.codex-plugin/plugin.json +6 -0
- package/plugins/ts-quality/README.md +24 -0
- package/plugins/ts-quality/hooks/check-toolu.sh +20 -0
- package/plugins/ts-quality/hooks/concerns/00-preamble.sh +123 -0
- package/plugins/ts-quality/hooks/concerns/10-imports.sh +33 -0
- package/plugins/ts-quality/hooks/concerns/15-type-as.sh +9 -0
- package/plugins/ts-quality/hooks/concerns/20-tests.sh +26 -0
- package/plugins/ts-quality/hooks/concerns/25-size-file.sh +40 -0
- package/plugins/ts-quality/hooks/concerns/30-size-fn.sh +53 -0
- package/plugins/ts-quality/hooks/concerns/35-react-hooks.sh +7 -0
- package/plugins/ts-quality/hooks/concerns/40-factory.sh +5 -0
- package/plugins/ts-quality/hooks/concerns/45-typeguard.sh +6 -0
- package/plugins/ts-quality/hooks/concerns/50-type-dup.sh +16 -0
- package/plugins/ts-quality/hooks/concerns/55-naming.sh +11 -0
- package/plugins/ts-quality/hooks/concerns/60-console.sh +6 -0
- package/plugins/ts-quality/hooks/concerns/65-suppression.sh +12 -0
- package/plugins/ts-quality/hooks/concerns/70-ui-confirm.sh +8 -0
- package/plugins/ts-quality/hooks/concerns/72-ui-radix.sh +12 -0
- package/plugins/ts-quality/hooks/concerns/74-react-props.sh +6 -0
- package/plugins/ts-quality/hooks/concerns/76-toast.sh +10 -0
- package/plugins/ts-quality/hooks/concerns/78-error-ast.sh +242 -0
- package/plugins/ts-quality/hooks/concerns/80-throw-literal.sh +23 -0
- package/plugins/ts-quality/hooks/concerns/85-no-mocks.sh +109 -0
- package/plugins/ts-quality/hooks/concerns/90-duplication.sh +30 -0
- package/plugins/ts-quality/hooks/concerns/92-docs.sh +36 -0
- package/plugins/ts-quality/hooks/concerns/94-handler.sh +15 -0
- package/plugins/ts-quality/hooks/concerns/99-finalize.sh +46 -0
- package/plugins/ts-quality/hooks/hooks.json +19 -0
- package/plugins/ts-quality/hooks/register.sh +65 -0
- package/src/adapter/__tests__/evaluate.test.ts +82 -0
- package/src/adapter/__tests__/permission-map.test.ts +91 -0
- package/src/adapter/evaluate.ts +54 -0
- package/src/adapter/permission-map.ts +177 -0
- package/src/bootstrap/__tests__/bootstrap.test.ts +148 -0
- package/src/bootstrap/entrypoint.ts +18 -0
- package/src/bootstrap/index.ts +5 -0
- package/src/bootstrap/readiness.ts +59 -0
- package/src/bootstrap/result.ts +21 -0
- package/src/bootstrap/runtime.ts +99 -0
- package/src/bootstrap/test-helpers.ts +25 -0
- package/src/host/__tests__/host.test.ts +37 -0
- package/src/host/detect.ts +46 -0
- package/src/host/index.ts +10 -0
- package/src/host/roots.ts +59 -0
- package/src/host/types.ts +14 -0
- package/src/inventory/__tests__/inventory.test.ts +69 -0
- package/src/inventory/index.ts +9 -0
- package/src/inventory/manifest.ts +46 -0
- package/src/inventory/scan.ts +66 -0
- package/src/inventory/selection.ts +92 -0
- package/src/inventory/types.ts +22 -0
- package/src/lifecycle/__tests__/lifecycle.test.ts +7 -0
- package/src/lifecycle/index.ts +2 -0
- package/src/lifecycle/table.ts +42 -0
- package/src/plugin/__tests__/plugin-stub.test.ts +6 -0
- package/src/plugin/plugin-stub.ts +7 -0
- package/src/plugin/toolu.ts +142 -0
- package/src/preflight/__tests__/preflight.test.ts +20 -0
- package/src/preflight/check.ts +75 -0
- package/src/preflight/index.ts +2 -0
- package/src/select/__tests__/select.test.ts +52 -0
- package/src/select/index.ts +2 -0
- package/src/select/resolve.ts +87 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SessionStart hook
|
|
3
|
+
# Event-aware: tailors context for startup, resume, clear, compact.
|
|
4
|
+
# Project-agnostic: detects project name, language, and package manager.
|
|
5
|
+
|
|
6
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
7
|
+
|
|
8
|
+
# Disable bash 5.2+ patsub_replacement so `&` in ${var//pat/repl} values is
|
|
9
|
+
# literal. No-op (option unknown) on older bash, including macOS /bin/bash 3.2.
|
|
10
|
+
shopt -u patsub_replacement 2>/dev/null || true
|
|
11
|
+
|
|
12
|
+
# shellcheck source=lib/detect.sh
|
|
13
|
+
. "$HOOK_DIR/lib/detect.sh"
|
|
14
|
+
# shellcheck source=lib/config.sh
|
|
15
|
+
. "$HOOK_DIR/lib/config.sh"
|
|
16
|
+
# shellcheck source=lib/registry.sh
|
|
17
|
+
. "$HOOK_DIR/lib/registry.sh"
|
|
18
|
+
# shellcheck source=lib/state-sweeper.sh
|
|
19
|
+
. "$HOOK_DIR/lib/state-sweeper.sh"
|
|
20
|
+
# shellcheck source=lib/permissions.sh
|
|
21
|
+
. "$HOOK_DIR/lib/permissions.sh"
|
|
22
|
+
# shellcheck source=lib/gate-mode.sh
|
|
23
|
+
. "$HOOK_DIR/lib/gate-mode.sh"
|
|
24
|
+
|
|
25
|
+
# Codex has no installed_plugins.json equivalent on the hot path. Snapshot the
|
|
26
|
+
# CLI's installed set once at SessionStart; all later checks consume this file.
|
|
27
|
+
toolu_snapshot_codex_plugins
|
|
28
|
+
toolu_registry_prune_inactive
|
|
29
|
+
|
|
30
|
+
# Transitional orphan sweep (safe to delete this block once users have migrated,
|
|
31
|
+
# ~v1.7+): the statusline moved to the standalone `statusline` plugin
|
|
32
|
+
# (~/.claude/statusline/statusline.sh, wired by its own SessionStart hook). Runs
|
|
33
|
+
# every session but no-ops once the symlink is gone. Remove the stale symlink
|
|
34
|
+
# toolu used to own at
|
|
35
|
+
# $config/toolu/statusline.sh so an un-migrated settings.json fails loudly
|
|
36
|
+
# (missing file) instead of dangling into a cleaned plugin cache. Only ever
|
|
37
|
+
# removes OUR symlink — a real file a user placed there is left untouched.
|
|
38
|
+
# Deliberately runs BEFORE the `toolu_enabled` opt-out below: a user who
|
|
39
|
+
# disables session-start context should still not be left with a dangling symlink.
|
|
40
|
+
_config_root="$(toolu_config_root)"
|
|
41
|
+
_old_sl="$_config_root/toolu/statusline.sh"
|
|
42
|
+
[ -L "$_old_sl" ] && rm -f "$_old_sl"
|
|
43
|
+
|
|
44
|
+
if ! toolu_enabled hooks session-start; then
|
|
45
|
+
cat > /dev/null 2>&1 || true
|
|
46
|
+
exit 0
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
PROJECT_ROOT="$(detect_project_root)"
|
|
50
|
+
[ -z "$PROJECT_ROOT" ] && PROJECT_ROOT="$(pwd)"
|
|
51
|
+
PROJECT_NAME="$(detect_project_name)"
|
|
52
|
+
NODE_PM="$(detect_node_pm)"
|
|
53
|
+
HAS_RUST="$(detect_rust)"
|
|
54
|
+
HAS_TS="$(detect_ts)"
|
|
55
|
+
HAS_PYTHON="$(detect_python)"
|
|
56
|
+
|
|
57
|
+
# ── Parse stdin to detect event type ────────────────────────────────────────
|
|
58
|
+
input=$(cat 2>/dev/null || echo "{}")
|
|
59
|
+
event="startup"
|
|
60
|
+
if command -v jq >/dev/null 2>&1; then
|
|
61
|
+
# Claude Code sends the SessionStart event type in `source`
|
|
62
|
+
# (startup | resume | clear | compact); keep legacy fields as fallbacks.
|
|
63
|
+
event=$(jq -r '.source // .session_event // .event // "startup"' <<< "$input" 2>/dev/null || echo "startup")
|
|
64
|
+
fi
|
|
65
|
+
[[ -z "$event" || "$event" == "null" ]] && event="startup"
|
|
66
|
+
|
|
67
|
+
# ── Render the main session doc with project tokens substituted ─────────────
|
|
68
|
+
render_doc() {
|
|
69
|
+
local src="$1"
|
|
70
|
+
[ -f "$src" ] || { echo ""; return 0; }
|
|
71
|
+
local content
|
|
72
|
+
content=$(cat "$src")
|
|
73
|
+
# Substitute placeholders with bash-native replacement — immune to sed
|
|
74
|
+
# metacharacters (|, &, \) in project names or package manager values.
|
|
75
|
+
# Replacement is deliberately UNQUOTED: quoting inside ${} inserts literal
|
|
76
|
+
# quote characters on bash 3.2 (stock macOS). Literal-& safety on bash 5.2+
|
|
77
|
+
# comes from `shopt -u patsub_replacement` at the top of this script.
|
|
78
|
+
local name="${PROJECT_NAME:-this project}"
|
|
79
|
+
local pm="${NODE_PM:-your package manager}"
|
|
80
|
+
content="${content//\{\{project_name\}\}/$name}"
|
|
81
|
+
content="${content//\{\{node_pm\}\}/$pm}"
|
|
82
|
+
# Model-tier placeholders: substituted from the resolved routing table so a
|
|
83
|
+
# user who remaps a tier in toolu.config.json sees THEIR aliases in the
|
|
84
|
+
# injected block, not the built-in defaults.
|
|
85
|
+
content="${content//\{\{model_mechanical\}\}/$MODEL_MECHANICAL}"
|
|
86
|
+
content="${content//\{\{model_exploration\}\}/$MODEL_EXPLORATION}"
|
|
87
|
+
content="${content//\{\{model_implementation\}\}/$MODEL_IMPLEMENTATION}"
|
|
88
|
+
content="${content//\{\{model_review\}\}/$MODEL_REVIEW}"
|
|
89
|
+
content="${content//\{\{model_synthesis\}\}/$MODEL_SYNTHESIS}"
|
|
90
|
+
content="${content//\{\{model_architecture\}\}/$MODEL_ARCHITECTURE}"
|
|
91
|
+
content="${content//\{\{effort_mechanical\}\}/$EFFORT_MECHANICAL}"
|
|
92
|
+
content="${content//\{\{effort_exploration\}\}/$EFFORT_EXPLORATION}"
|
|
93
|
+
content="${content//\{\{effort_implementation\}\}/$EFFORT_IMPLEMENTATION}"
|
|
94
|
+
content="${content//\{\{effort_review\}\}/$EFFORT_REVIEW}"
|
|
95
|
+
content="${content//\{\{effort_synthesis\}\}/$EFFORT_SYNTHESIS}"
|
|
96
|
+
content="${content//\{\{effort_architecture\}\}/$EFFORT_ARCHITECTURE}"
|
|
97
|
+
printf '%s' "$content"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Resolve the model-routing table once per session. Config-driven and stable
|
|
101
|
+
# within a session, so it is safe for the cached SessionStart prefix.
|
|
102
|
+
EFFORT_MECHANICAL="" EFFORT_EXPLORATION="" EFFORT_IMPLEMENTATION=""
|
|
103
|
+
EFFORT_REVIEW="" EFFORT_SYNTHESIS="" EFFORT_ARCHITECTURE=""
|
|
104
|
+
if [ "$(toolu_host)" = codex ]; then
|
|
105
|
+
IFS=$'\t' read -r MODEL_MECHANICAL _effort <<<"$(toolu_codex_model mechanical)"
|
|
106
|
+
EFFORT_MECHANICAL=", effort \`$_effort\`"
|
|
107
|
+
IFS=$'\t' read -r MODEL_EXPLORATION _effort <<<"$(toolu_codex_model exploration)"
|
|
108
|
+
EFFORT_EXPLORATION=", effort \`$_effort\`"
|
|
109
|
+
IFS=$'\t' read -r MODEL_IMPLEMENTATION _effort <<<"$(toolu_codex_model implementation)"
|
|
110
|
+
EFFORT_IMPLEMENTATION=", effort \`$_effort\`"
|
|
111
|
+
IFS=$'\t' read -r MODEL_REVIEW _effort <<<"$(toolu_codex_model review)"
|
|
112
|
+
EFFORT_REVIEW=", effort \`$_effort\`"
|
|
113
|
+
IFS=$'\t' read -r MODEL_SYNTHESIS _effort <<<"$(toolu_codex_model synthesis)"
|
|
114
|
+
EFFORT_SYNTHESIS=", effort \`$_effort\`"
|
|
115
|
+
IFS=$'\t' read -r MODEL_ARCHITECTURE _effort <<<"$(toolu_codex_model architecture)"
|
|
116
|
+
EFFORT_ARCHITECTURE=", effort \`$_effort\`"
|
|
117
|
+
else
|
|
118
|
+
MODEL_MECHANICAL=$(toolu_model mechanical)
|
|
119
|
+
MODEL_EXPLORATION=$(toolu_model exploration)
|
|
120
|
+
MODEL_IMPLEMENTATION=$(toolu_model implementation)
|
|
121
|
+
MODEL_REVIEW=$(toolu_model review)
|
|
122
|
+
MODEL_SYNTHESIS=$(toolu_model synthesis)
|
|
123
|
+
MODEL_ARCHITECTURE=$(toolu_model architecture)
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# ── Git context (branch only) ───────────────────────────────────────────────
|
|
127
|
+
# Cache discipline: this string lands in the once-cached SessionStart prefix, so
|
|
128
|
+
# it must depend only on stable inputs. The branch is stable within a session;
|
|
129
|
+
# the working-tree dirty count is NOT — it goes stale immediately and differs on
|
|
130
|
+
# every startup/resume/compact, perturbing the prefix for no lasting value. Live
|
|
131
|
+
# working state is already on the statusline and one `git status` away.
|
|
132
|
+
git_ctx=""
|
|
133
|
+
if command -v git >/dev/null 2>&1; then
|
|
134
|
+
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
|
135
|
+
[[ -n "$branch" ]] && git_ctx="Branch: $branch"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# Note: the failing-quality-gate reminder is deliberately NOT injected here. It
|
|
139
|
+
# is volatile (flips as you work) and already surfaced live by the statusline
|
|
140
|
+
# and per-prompt by user-prompt-submit.sh — re-injecting it into the cached
|
|
141
|
+
# prefix would duplicate context and perturb the cache on every gate flip.
|
|
142
|
+
|
|
143
|
+
# ── Build context based on event type ───────────────────────────────────────
|
|
144
|
+
parts=()
|
|
145
|
+
title=""
|
|
146
|
+
|
|
147
|
+
main_doc=$(render_doc "$HOOK_DIR/docs/session-start.md")
|
|
148
|
+
|
|
149
|
+
case "$event" in
|
|
150
|
+
startup)
|
|
151
|
+
title="Toolu is on!"
|
|
152
|
+
[ -n "$main_doc" ] && parts+=("$main_doc")
|
|
153
|
+
;;
|
|
154
|
+
resume)
|
|
155
|
+
title="Session resumed"
|
|
156
|
+
[ -n "$main_doc" ] && parts+=("$main_doc")
|
|
157
|
+
;;
|
|
158
|
+
clear)
|
|
159
|
+
title="Context cleared"
|
|
160
|
+
[ -n "$main_doc" ] && parts+=("$main_doc")
|
|
161
|
+
;;
|
|
162
|
+
compact)
|
|
163
|
+
title="Context compacted"
|
|
164
|
+
compaction=$(cat "$HOOK_DIR/docs/post-compaction.md" 2>/dev/null || echo "")
|
|
165
|
+
[[ -n "$compaction" ]] && parts+=("$compaction")
|
|
166
|
+
[ -n "$main_doc" ] && parts+=("$main_doc")
|
|
167
|
+
;;
|
|
168
|
+
esac
|
|
169
|
+
|
|
170
|
+
# Model routing — which tier handles which class of delegated work. Injected on
|
|
171
|
+
# every event (it must survive a compact, since post-compaction delegation is
|
|
172
|
+
# exactly where the agent otherwise falls back to one model for everything).
|
|
173
|
+
# Opt out with `"models": {"enabled": false}`.
|
|
174
|
+
if toolu_enabled models enabled; then
|
|
175
|
+
routing_doc=$(render_doc "$HOOK_DIR/docs/model-routing.md")
|
|
176
|
+
[ -n "$routing_doc" ] && parts+=("$routing_doc")
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Per-toolchain snippets — opt-in via $TOOLU_VERBOSE to save tokens.
|
|
180
|
+
# Default off: the session-start.md core already covers project rules.
|
|
181
|
+
# Set TOOLU_VERBOSE=1 to re-enable per-toolchain blocks. Any value other
|
|
182
|
+
# than "0" / unset / empty enables (so `=true`, `=on`, `=1` all work, but a
|
|
183
|
+
# user who sets `=0` to disable is not surprised).
|
|
184
|
+
if [ "${TOOLU_VERBOSE:-0}" != "0" ]; then
|
|
185
|
+
if [ "$HAS_TS" = "ts" ]; then
|
|
186
|
+
ts_doc=$(render_doc "$HOOK_DIR/docs/session-start-ts.md")
|
|
187
|
+
[ -n "$ts_doc" ] && parts+=("$ts_doc")
|
|
188
|
+
fi
|
|
189
|
+
if [ "$HAS_RUST" = "rust" ]; then
|
|
190
|
+
rust_doc=$(render_doc "$HOOK_DIR/docs/session-start-rust.md")
|
|
191
|
+
[ -n "$rust_doc" ] && parts+=("$rust_doc")
|
|
192
|
+
fi
|
|
193
|
+
if [ "$HAS_PYTHON" = "python" ]; then
|
|
194
|
+
python_doc=$(render_doc "$HOOK_DIR/docs/session-start-python.md")
|
|
195
|
+
[ -n "$python_doc" ] && parts+=("$python_doc")
|
|
196
|
+
fi
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
# Append project line only when name was detected.
|
|
200
|
+
if [ -n "$PROJECT_NAME" ]; then
|
|
201
|
+
parts+=("Project: $PROJECT_NAME")
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
# ── Housekeeping: reclaim spent state, and settle host permissions once ─────
|
|
205
|
+
# Both are best-effort and self-silencing; neither may keep a session from
|
|
206
|
+
# starting. The sweeper never speaks unless something went wrong; the
|
|
207
|
+
# permission writer speaks exactly once per repo, ever.
|
|
208
|
+
toolu_sweep_state "$PROJECT_ROOT"
|
|
209
|
+
|
|
210
|
+
_perm_summary=$(toolu_permissions_autowrite "$PROJECT_ROOT" 2>/dev/null || true)
|
|
211
|
+
[ -n "$_perm_summary" ] && parts+=("$_perm_summary")
|
|
212
|
+
|
|
213
|
+
# ── One-time notice: gates no longer prompt ─────────────────────────────────
|
|
214
|
+
# Existing users had push-review and the denylist asking. The default is now
|
|
215
|
+
# advise (quality gate still blocks commit/push), and a behavior change
|
|
216
|
+
# nobody was told about reads as a bug — so say it once per machine, to
|
|
217
|
+
# anyone who has not already made a choice about it.
|
|
218
|
+
_gate_notice_dir="$_config_root/toolu"
|
|
219
|
+
_gate_notice="$_gate_notice_dir/.gate-preset-notice-v6"
|
|
220
|
+
# Skip only when the user already pinned delivery (a preset, or a per-gate
|
|
221
|
+
# mode). A sweep/TTL-only `gates` object is not a delivery choice — those
|
|
222
|
+
# users still need to hear that balanced no longer prompts.
|
|
223
|
+
_gate_delivery_pinned=$(jq -r '
|
|
224
|
+
if (.gates | type) != "object" then "no"
|
|
225
|
+
elif .gates.preset? != null then "yes"
|
|
226
|
+
elif ([.gates | to_entries[] | select((.value | type) == "object" and (.value | has("mode")))] | length) > 0 then "yes"
|
|
227
|
+
else "no"
|
|
228
|
+
end
|
|
229
|
+
' <<< "$TOOLU_CFG_JSON" 2>/dev/null || echo "no")
|
|
230
|
+
if [ ! -f "$_gate_notice" ] && [ "$_TOOLU_HAS_JQ" = "1" ] \
|
|
231
|
+
&& [ "$_gate_delivery_pinned" != "yes" ]; then
|
|
232
|
+
parts+=("toolu gates no longer prompt: the \`balanced\` preset advises on push-review and denylist hits, and the quality gate still blocks only \`git commit\`/\`git push\`. Pin a prompt with \`gates.<name>.mode: ask\`, or the old hard denies with \`{\"gates\":{\"preset\":\"strict\"}}\`.")
|
|
233
|
+
mkdir -p "$_gate_notice_dir" 2>/dev/null && : > "$_gate_notice" 2>/dev/null
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
# Warn when optional tools referenced by docs/skills are missing — keeps the
|
|
237
|
+
# session start honest about which capabilities are actually available.
|
|
238
|
+
HAS_ASTGREP="$(detect_ast_grep)"
|
|
239
|
+
missing_tools=()
|
|
240
|
+
if [ "$HAS_ASTGREP" != "ast-grep" ] && toolu_enabled skills ast-grep; then
|
|
241
|
+
missing_tools+=("ast-grep (structural code search)")
|
|
242
|
+
fi
|
|
243
|
+
if [ "${#missing_tools[@]}" -gt 0 ]; then
|
|
244
|
+
warn="WARN: optional tools missing — features that depend on them are disabled:"
|
|
245
|
+
for t in "${missing_tools[@]}"; do
|
|
246
|
+
warn+="
|
|
247
|
+
• $t"
|
|
248
|
+
done
|
|
249
|
+
parts+=("$warn")
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
# ── Mandatory proactive tool use ────────────────────────────────────────────
|
|
253
|
+
# When the comemory / ast-grep / exa-search / context7 plugins are INSTALLED
|
|
254
|
+
# and their underlying tool is available (binary on PATH, or published CLI
|
|
255
|
+
# wrapper), front-load a hard, proactive mandate into session context. The
|
|
256
|
+
# skills are ALWAYS-ACTIVE, but their bodies only load on trigger and the agent
|
|
257
|
+
# tends to wait to be asked — this injection makes the requirement unmissable
|
|
258
|
+
# from turn one. Aggressive by design; the per-skill opt-out (toolu_enabled
|
|
259
|
+
# skills <key> = false) is the escape hatch for a user who wants it off.
|
|
260
|
+
mandates=()
|
|
261
|
+
if [ "$HAS_ASTGREP" = "ast-grep" ] && toolu_enabled skills ast-grep && toolu_plugin_active ast-grep@toolu; then
|
|
262
|
+
mandates+=("ast-grep (structural search) — for ANY search by code shape (signatures, call sites, impls, trait/interface usage, patterns) you MUST reach for \`ast-grep run --pattern …\` FIRST. Grep/ripgrep/sed are a FALLBACK ONLY — use them for plain-text literals in non-code files, or when a query genuinely cannot be expressed structurally. Never reach for them first on code.")
|
|
263
|
+
fi
|
|
264
|
+
# exa-search / context7 publish their CLI wrappers at a stable config-dir
|
|
265
|
+
# path via their own SessionStart hooks — there is no binary on PATH to
|
|
266
|
+
# detect, so the wrapper file itself is the presence signal. The symlink
|
|
267
|
+
# persists across sessions; only the very first session of a fresh install
|
|
268
|
+
# can race the publishing hook, and the mandate simply appears one session
|
|
269
|
+
# later. exa-search additionally gates on EXA_API_KEY: the wrapper hard-fails
|
|
270
|
+
# without it, and mandating a broken tool is worse than staying quiet.
|
|
271
|
+
exa_wrapper="$_config_root/exa-search/search.sh"
|
|
272
|
+
if [ -n "${EXA_API_KEY:-}" ] && [ -x "$exa_wrapper" ] && toolu_enabled skills exa-search && toolu_plugin_active exa-search@toolu; then
|
|
273
|
+
mandates+=("exa-search (web search) — for ANY web search, code-example hunt, URL crawl, or topic research you MUST reach for \`\"$exa_wrapper\"\` FIRST (commands: search / crawl / similar — see the exa-search skill). Native web tools are a FALLBACK ONLY — use them when the wrapper errors or a URL needs your logged-in session.")
|
|
274
|
+
fi
|
|
275
|
+
ctx7_wrapper="$_config_root/context7/search.sh"
|
|
276
|
+
if [ -x "$ctx7_wrapper" ] && toolu_enabled skills context7 && toolu_plugin_active context7@toolu; then
|
|
277
|
+
mandates+=("context7 (library docs) — for ANY third-party library/framework question (API usage, current docs, code examples, version behavior) you MUST query \`\"$ctx7_wrapper\"\` FIRST (\`search <library>\` to resolve the ID, then \`docs <id> <query>\`) BEFORE answering from memory or searching the web. Web search is a FALLBACK ONLY when context7 lacks coverage.")
|
|
278
|
+
fi
|
|
279
|
+
if [ "${#mandates[@]}" -gt 0 ]; then
|
|
280
|
+
mwarn="MANDATORY — proactive plugin use (installed + available; NOT optional, do NOT ask permission):"
|
|
281
|
+
for m in "${mandates[@]}"; do
|
|
282
|
+
mwarn+="
|
|
283
|
+
• $m"
|
|
284
|
+
done
|
|
285
|
+
mwarn+="
|
|
286
|
+
• Propagation: these mandates bind EVERY agent in this session, not just the main thread. When you delegate via Task/subagents (nested ones too), carry the same requirement into the subagent's prompt — recall/save via comemory, ast-grep first with Grep/sed as fallback. Delegation never exempts the work."
|
|
287
|
+
parts+=("$mwarn")
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
# Verify required plugin dependencies declared in plugin.json `dependencies`
|
|
291
|
+
# (Claude Code's official schema: array of "name" strings or
|
|
292
|
+
# {name, marketplace?, version?} objects). Auto-install only fires when the
|
|
293
|
+
# dep's marketplace is already configured; we surface the exact
|
|
294
|
+
# `/plugin install …` command so the user can fix in one paste.
|
|
295
|
+
#
|
|
296
|
+
# Scope: the check only fires when we can locate the toolu plugin manifest
|
|
297
|
+
# (via CLAUDE_PLUGIN_ROOT, set by Claude Code when this hook runs from the
|
|
298
|
+
# installed plugin, or via the in-repo path when working inside the toolu
|
|
299
|
+
# checkout itself). Outside both — e.g., a hook run by another project that
|
|
300
|
+
# inherits these helpers — the block silently no-ops. Intentional: repo-A
|
|
301
|
+
# should not WARN about plugin deps declared in repo-B's manifest.
|
|
302
|
+
plugin_manifest=""
|
|
303
|
+
plugin_root=$(toolu_plugin_root)
|
|
304
|
+
if [ "$(toolu_host)" = codex ] && [ -n "$plugin_root" ] && [ -f "$plugin_root/.codex-plugin/plugin.json" ]; then
|
|
305
|
+
plugin_manifest="$plugin_root/.codex-plugin/plugin.json"
|
|
306
|
+
elif [ -n "$plugin_root" ] && [ -f "$plugin_root/.claude-plugin/plugin.json" ]; then
|
|
307
|
+
plugin_manifest="$plugin_root/.claude-plugin/plugin.json"
|
|
308
|
+
elif [ -f "$PROJECT_ROOT/plugins/toolu/.claude-plugin/plugin.json" ]; then
|
|
309
|
+
plugin_manifest="$PROJECT_ROOT/plugins/toolu/.claude-plugin/plugin.json"
|
|
310
|
+
fi
|
|
311
|
+
if [[ -n "$plugin_manifest" && -f "$plugin_manifest" ]] && command -v jq >/dev/null 2>&1; then
|
|
312
|
+
dependency_manifest="$plugin_manifest"
|
|
313
|
+
# Codex's published manifest schema has no dependency field. The Claude
|
|
314
|
+
# manifest remains the shared dependency declaration inside each dual-host
|
|
315
|
+
# plugin, so Codex reads it when its native manifest has no dependencies.
|
|
316
|
+
if [ "$(toolu_host)" = codex ] && ! jq -e '.dependencies | type == "array"' \
|
|
317
|
+
"$dependency_manifest" >/dev/null 2>&1 && \
|
|
318
|
+
[ -f "$plugin_root/.claude-plugin/plugin.json" ]; then
|
|
319
|
+
dependency_manifest="$plugin_root/.claude-plugin/plugin.json"
|
|
320
|
+
fi
|
|
321
|
+
missing_plugins=()
|
|
322
|
+
indeterminate=0
|
|
323
|
+
while IFS= read -r req_spec; do
|
|
324
|
+
[ -z "$req_spec" ] && continue
|
|
325
|
+
installed=$(detect_plugin_installed "$req_spec")
|
|
326
|
+
rc=$?
|
|
327
|
+
if [ "$rc" -eq 2 ]; then
|
|
328
|
+
# Registry/jq unavailable on this box — suppress all WARNs in this
|
|
329
|
+
# block so we don't spam every required plugin as "missing" on a
|
|
330
|
+
# machine where the registry was moved or jq was uninstalled.
|
|
331
|
+
indeterminate=1
|
|
332
|
+
break
|
|
333
|
+
fi
|
|
334
|
+
if [ -z "$installed" ]; then
|
|
335
|
+
missing_plugins+=("$(toolu_plugin_install_command "$req_spec")")
|
|
336
|
+
fi
|
|
337
|
+
done < <(jq -r '
|
|
338
|
+
(.dependencies // [])[]
|
|
339
|
+
| if type == "string" then .
|
|
340
|
+
elif (type == "object" and (.name | type) == "string")
|
|
341
|
+
then (if .marketplace then "\(.name)@\(.marketplace)" else .name end)
|
|
342
|
+
else empty end
|
|
343
|
+
' "$dependency_manifest" 2>/dev/null)
|
|
344
|
+
if [ "$indeterminate" -eq 0 ] && [ "${#missing_plugins[@]}" -gt 0 ]; then
|
|
345
|
+
pwarn="WARN: required plugins missing — dependent workflows will fail. Install:"
|
|
346
|
+
for cmd in "${missing_plugins[@]}"; do
|
|
347
|
+
pwarn+="
|
|
348
|
+
• $cmd"
|
|
349
|
+
done
|
|
350
|
+
parts+=("$pwarn")
|
|
351
|
+
fi
|
|
352
|
+
fi
|
|
353
|
+
|
|
354
|
+
# Append git context (branch only — see the cache-discipline note above).
|
|
355
|
+
[[ -n "$git_ctx" ]] && parts+=("$git_ctx")
|
|
356
|
+
|
|
357
|
+
# ── Join and output ─────────────────────────────────────────────────────────
|
|
358
|
+
full_context=""
|
|
359
|
+
for part in "${parts[@]}"; do
|
|
360
|
+
if [[ -z "$full_context" ]]; then
|
|
361
|
+
full_context="$part"
|
|
362
|
+
else
|
|
363
|
+
full_context="$full_context
|
|
364
|
+
|
|
365
|
+
$part"
|
|
366
|
+
fi
|
|
367
|
+
done
|
|
368
|
+
|
|
369
|
+
if command -v jq >/dev/null 2>&1; then
|
|
370
|
+
jq -n --arg ctx "$full_context" --arg title "$title" '{
|
|
371
|
+
"hookSpecificOutput": {
|
|
372
|
+
"hookEventName": "SessionStart",
|
|
373
|
+
"additionalContext": $ctx
|
|
374
|
+
},
|
|
375
|
+
"systemMessage": $title
|
|
376
|
+
}'
|
|
377
|
+
fi
|
|
378
|
+
|
|
379
|
+
exit 0
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# UserPromptSubmit hook
|
|
3
|
+
# Validates prompts, injects optional per-project context, git context, intent hints.
|
|
4
|
+
# Project-agnostic: no project literals. Per-project hints opt in via the
|
|
5
|
+
# host-native project directory's context.sh.
|
|
6
|
+
|
|
7
|
+
HOOK_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
8
|
+
|
|
9
|
+
# shellcheck source=lib/detect.sh
|
|
10
|
+
. "$HOOK_DIR/lib/detect.sh"
|
|
11
|
+
# shellcheck source=lib/config.sh
|
|
12
|
+
. "$HOOK_DIR/lib/config.sh"
|
|
13
|
+
|
|
14
|
+
if ! toolu_enabled hooks user-prompt-submit; then
|
|
15
|
+
cat > /dev/null 2>&1 || true
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
input=$(cat)
|
|
20
|
+
prompt=""
|
|
21
|
+
if command -v jq >/dev/null 2>&1; then
|
|
22
|
+
prompt=$(jq -r '.prompt // ""' <<< "$input" 2>/dev/null || echo "")
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
[[ -z "$prompt" ]] && exit 0
|
|
26
|
+
|
|
27
|
+
PROJECT_ROOT="$(detect_project_root)"
|
|
28
|
+
[ -z "$PROJECT_ROOT" ] && PROJECT_ROOT="$(pwd)"
|
|
29
|
+
GATE_FILE="$(toolu_project_state_root "$PROJECT_ROOT")/quality-gate-status.json"
|
|
30
|
+
prompt_lower=$(printf '%s' "$prompt" | tr '[:upper:]' '[:lower:]')
|
|
31
|
+
|
|
32
|
+
# ── Skip trivial prompts (confirmations, short replies) ───────────────────────
|
|
33
|
+
if [[ "$prompt_lower" =~ ^(y|n|yes|no|ok|sure|thanks|thank\ you|go\ ahead|looks\ good|lgtm|correct|exactly|right|done|nah|nope|yep|yup|continue)[\.\!\?]?$ ]]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# ── Skip slash commands — skills handle their own context ─────────────────────
|
|
38
|
+
if [[ "$prompt_lower" =~ ^/ ]]; then
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# ── Block vague one-word prompts ──────────────────────────────────────────────
|
|
43
|
+
if [[ "$prompt_lower" =~ ^(fix|help|debug|check|look|see|run|do|try)[[:space:]]*$ ]]; then
|
|
44
|
+
if command -v jq >/dev/null 2>&1; then
|
|
45
|
+
jq -n '{"decision": "block", "reason": "Prompt too vague - specify what file/feature/error needs attention"}'
|
|
46
|
+
fi
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Word-boundary helpers — bash extended regex has no `\b`. WB/WE wrap each
|
|
51
|
+
# alternation so e.g. `impl` does NOT match `implement` and `move` does NOT
|
|
52
|
+
# match `remove`. Also dropped overly short tokens that no boundary trick
|
|
53
|
+
# can rescue: `impl` (vs implement), `ast` (vs fast/past/last), `drop`
|
|
54
|
+
# (vs dropdown). Inflected forms (tested, fixing) won't match — users
|
|
55
|
+
# typically write the base verb in a directive prompt, and silence is
|
|
56
|
+
# safer than the wrong hint.
|
|
57
|
+
WB='(^|[^a-z])'
|
|
58
|
+
WE='([^a-z]|$)'
|
|
59
|
+
|
|
60
|
+
# ── Quality gate warning (not block) ─────────────────────────────────────────
|
|
61
|
+
quality_gate_hint=""
|
|
62
|
+
if [[ -f "$GATE_FILE" ]] && command -v jq >/dev/null 2>&1; then
|
|
63
|
+
gate_json=$(cat "$GATE_FILE" 2>/dev/null)
|
|
64
|
+
gate_status=$(jq -r '.status // ""' <<< "$gate_json" 2>/dev/null)
|
|
65
|
+
if [[ "$gate_status" == "failing" ]]; then
|
|
66
|
+
# Word-boundary wrapped: `prefix` must not suppress via the `fix` substring.
|
|
67
|
+
if ! [[ "$prompt_lower" =~ ${WB}(fix|resolve|error|warning|test|lint|check|type)${WE} ]]; then
|
|
68
|
+
reason=$(jq -r '.reason // "Unknown quality failure"' <<< "$gate_json" 2>/dev/null)
|
|
69
|
+
quality_gate_hint="Quality gate failing: $reason. Prefer fixing before unrelated work."
|
|
70
|
+
fi
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# ── Build context parts ──────────────────────────────────────────────────────
|
|
75
|
+
# Token-budget rule: every part below must be opt-in via prompt content.
|
|
76
|
+
# We do NOT re-inject SessionStart material (branch and gates)
|
|
77
|
+
# on every prompt — that duplicates context already in the session.
|
|
78
|
+
|
|
79
|
+
HAS_ASTGREP="$(detect_ast_grep)"
|
|
80
|
+
if ! toolu_enabled skills ast-grep; then
|
|
81
|
+
HAS_ASTGREP=""
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# 2. Intent hint — at most ONE. Most-specific pattern wins.
|
|
86
|
+
intent=""
|
|
87
|
+
if [[ "$prompt_lower" =~ ${WB}(pattern|struct|trait|interface|all\ functions|all\ methods|every\ function|every\ method|syntax|code\ structure|signature|return\ type|where\ clause|lifetime|closure|macro|decorator|annotation)${WE} ]]; then
|
|
88
|
+
if [ "$HAS_ASTGREP" = "ast-grep" ]; then
|
|
89
|
+
intent="Structural pattern: use \`ast-grep run --pattern\` (not Grep)."
|
|
90
|
+
else
|
|
91
|
+
intent="WARN: ast-grep not installed — install via brew/cargo for structural matching."
|
|
92
|
+
fi
|
|
93
|
+
elif [[ "$prompt_lower" =~ ${WB}(rename|move|extract|split)${WE} ]]; then
|
|
94
|
+
intent="Rename: find all refs (ast-grep + Grep on configs) before rewriting."
|
|
95
|
+
elif [[ "$prompt_lower" =~ ${WB}(test|spec|coverage)${WE} ]]; then
|
|
96
|
+
intent="Tests: real-world data only, NO mocks."
|
|
97
|
+
elif [[ "$prompt_lower" =~ ${WB}(fix|debug|error|bug|issue)${WE} ]]; then
|
|
98
|
+
intent="Fix in code. Never suppress with disable comments."
|
|
99
|
+
elif [[ "$prompt_lower" =~ ${WB}(delete|remove|clean\ up)${WE} ]]; then
|
|
100
|
+
intent="Verify no deps before removing."
|
|
101
|
+
elif [[ "$prompt_lower" =~ ${WB}(review|audit)${WE} ]]; then
|
|
102
|
+
intent="Review: forbidden syntax, quality gates, test coverage."
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# 2b. Scale nudge — independent of the single intent hint above (a task can be
|
|
106
|
+
# both a "fix" AND large). Fires only on words that indicate real SCALE, and it
|
|
107
|
+
# suggests considering a split rather than instructing a fan-out.
|
|
108
|
+
#
|
|
109
|
+
# The trigger list used to include `refactor`, `audit` and `across`, which are
|
|
110
|
+
# everyday words: "refactor this function", "audit this helper" and "rename it
|
|
111
|
+
# across two files" are all single-thread work, and each one was being told to
|
|
112
|
+
# delegate and parallelize. Spawning agents for small tasks is slower than doing
|
|
113
|
+
# them, so a nudge that fires on ordinary phrasing costs time rather than saving
|
|
114
|
+
# it. What remains names a scope no single sweep covers.
|
|
115
|
+
#
|
|
116
|
+
# WB/WE-wrapped like the other hints to avoid substring false positives.
|
|
117
|
+
orchestrate=""
|
|
118
|
+
if [[ "$prompt_lower" =~ ${WB}(migrate|codebase-wide|throughout|end-to-end)${WE} ]]; then
|
|
119
|
+
orchestrate="Possibly large task — if it splits into genuinely independent units, consider decomposing it; if it is really one thread of work, just do it. The orchestrator skill has the test for which."
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# 2c. Brainstorm nudge — independent of intent and scale. These terms signal a
|
|
123
|
+
# design conversation, but the nudge remains advisory: bounded or mechanical
|
|
124
|
+
# requests should continue directly without launching a workflow.
|
|
125
|
+
brainstorm=""
|
|
126
|
+
if [[ "$prompt_lower" =~ ${WB}(brainstorms?|designs?|scopes?|approach(es)?|architectures?|trade-?offs?|redesigns?|overhauls?)${WE} ]] ||
|
|
127
|
+
[[ "$prompt_lower" =~ ${WB}new[[:space:]]+(feature|workflow|system)${WE} ]]; then
|
|
128
|
+
brainstorm="Scope may be unresolved — consider the \`brainstorm\` skill for material design choices; skip it when the request is already bounded or mechanical."
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# 2d. Research nudge — independent of the single intent hint (a prompt can be
|
|
132
|
+
# both a "fix" AND need external lookup). Fires on EXTERNAL-knowledge signals
|
|
133
|
+
# (live docs, latest releases, third-party APIs) so the work is delegated to the
|
|
134
|
+
# research-agent subagent — it isolates the token cost and routes
|
|
135
|
+
# exa-search/context7 with a native fallback. Deliberately tight and
|
|
136
|
+
# external-leaning: local-code questions should not be routed to web research.
|
|
137
|
+
# WB/WE-wrapped. Gated by the
|
|
138
|
+
# `agents.research-agent` toggle (default on).
|
|
139
|
+
research=""
|
|
140
|
+
if toolu_enabled agents research-agent &&
|
|
141
|
+
[[ "$prompt_lower" =~ ${WB}(latest|docs\ for|library\ docs|api\ reference|api\ docs|changelog|release\ notes|best\ practices?|look\ up|search\ the\ web|web\ search|how\ to\ use)${WE} ]]; then
|
|
142
|
+
research="External research — delegate to the research-agent subagent (routes exa-search/context7, native fallback) to keep main context lean."
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
# 2e. Jira discoverability nudge — independent of the single intent hint. Names
|
|
146
|
+
# the jira skill and warns off the Atlassian MCP so the skill wins
|
|
147
|
+
# tool-selection when the user mentions Jira (today the model often reaches for
|
|
148
|
+
# the Atlassian MCP instead). Fires on the words jira/atlassian, an
|
|
149
|
+
# *.atlassian.net/browse/<KEY> link, or an issue key (ABC-123) GATED by a
|
|
150
|
+
# jira-context word so bare key-shaped tokens (UTF-8, GPT-4, ISO-8601) don't
|
|
151
|
+
# trigger it. The URL and issue-key matches use the ORIGINAL $prompt: the key is
|
|
152
|
+
# case-sensitive ([A-Z]) and $prompt_lower has already been lowercased.
|
|
153
|
+
jira_nudge=""
|
|
154
|
+
if [[ "$prompt_lower" =~ ${WB}(jira|atlassian)${WE} ]] ||
|
|
155
|
+
[[ "$prompt" =~ atlassian[.]net/browse/[A-Z][A-Z0-9]+-[0-9]+ ]] ||
|
|
156
|
+
{ [[ "$prompt" =~ [A-Z][A-Z0-9]+-[0-9]+ ]] &&
|
|
157
|
+
[[ "$prompt_lower" =~ ${WB}(ticket|issue|board|sprint|epic|backlog)${WE} ]]; }; then
|
|
158
|
+
jira_nudge="Jira mentioned — use the \`jira\` skill (REST wrapper over jira.sh), NOT the Atlassian MCP."
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# 3. Per-project context hook — opt-in. Project may emit any string.
|
|
162
|
+
project_ctx=""
|
|
163
|
+
PROJECT_CONTEXT="$PROJECT_ROOT/$(toolu_project_dirname)/context.sh"
|
|
164
|
+
if [ -n "$PROJECT_ROOT" ] && [ -f "$PROJECT_CONTEXT" ]; then
|
|
165
|
+
# shellcheck disable=SC1091 # path is project-specific; sourced only if present
|
|
166
|
+
project_ctx=$(PROMPT="$prompt" bash "$PROJECT_CONTEXT" 2>/dev/null || true)
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# ── Combine and output ───────────────────────────────────────────────────────
|
|
170
|
+
parts=()
|
|
171
|
+
[[ -n "$intent" ]] && parts+=("$intent")
|
|
172
|
+
[[ -n "$orchestrate" ]] && parts+=("$orchestrate")
|
|
173
|
+
[[ -n "$brainstorm" ]] && parts+=("$brainstorm")
|
|
174
|
+
[[ -n "$research" ]] && parts+=("$research")
|
|
175
|
+
[[ -n "$jira_nudge" ]] && parts+=("$jira_nudge")
|
|
176
|
+
[[ -n "$project_ctx" ]] && parts+=("$project_ctx")
|
|
177
|
+
[[ -n "$quality_gate_hint" ]] && parts+=("$quality_gate_hint")
|
|
178
|
+
|
|
179
|
+
# Join parts with " | "
|
|
180
|
+
context=""
|
|
181
|
+
if [[ ${#parts[@]} -gt 0 ]]; then
|
|
182
|
+
context="${parts[0]}"
|
|
183
|
+
for part in "${parts[@]:1}"; do
|
|
184
|
+
context="$context | $part"
|
|
185
|
+
done
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
if command -v jq >/dev/null 2>&1; then
|
|
189
|
+
jq -n --arg ctx "$context" '{
|
|
190
|
+
"hookSpecificOutput": {
|
|
191
|
+
"hookEventName": "UserPromptSubmit",
|
|
192
|
+
"additionalContext": $ctx
|
|
193
|
+
}
|
|
194
|
+
}'
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
exit 0
|