portawhip 0.1.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +162 -0
  4. package/SECURITY.md +15 -0
  5. package/VISION.md +281 -0
  6. package/adapters/hooks/hook-stub.mjs +21 -0
  7. package/adapters/hooks/hook-stub.test.mjs +43 -0
  8. package/adapters/hooks/universal-hook.mjs +348 -0
  9. package/adapters/hooks/universal-hook.test.mjs +294 -0
  10. package/adapters/instructions/generate.mjs +114 -0
  11. package/core/fixtures/bad-recipe.yaml +5 -0
  12. package/core/fixtures/capability-graph.json +10 -0
  13. package/core/fixtures/skill-with-metadata/SKILL.md +15 -0
  14. package/core/registry/capability-docs.mjs +145 -0
  15. package/core/registry/capability-graph-compiler.mjs +90 -0
  16. package/core/registry/capability-graph.mjs +53 -0
  17. package/core/registry/capability-kind.mjs +9 -0
  18. package/core/registry/cli-enrich.mjs +316 -0
  19. package/core/registry/cli-enrich.test.mjs +119 -0
  20. package/core/registry/discover.mjs +379 -0
  21. package/core/registry/enrich.mjs +213 -0
  22. package/core/registry/enrich.test.mjs +53 -0
  23. package/core/registry/eval-harvest.mjs +93 -0
  24. package/core/registry/eval-harvest.test.mjs +64 -0
  25. package/core/registry/registry.mjs +197 -0
  26. package/core/router/concept-vector.mjs +91 -0
  27. package/core/router/concept-vector.test.mjs +36 -0
  28. package/core/router/curated-trigger.test.mjs +28 -0
  29. package/core/router/dense-embedder.mjs +242 -0
  30. package/core/router/fusion.mjs +18 -0
  31. package/core/router/hybrid-router.mjs +375 -0
  32. package/core/router/intent-evidence.mjs +60 -0
  33. package/core/router/prompt-hygiene.mjs +31 -0
  34. package/core/router/route-entry.mjs +74 -0
  35. package/core/router/router-cli.mjs +218 -0
  36. package/core/router/router-cli.test.mjs +25 -0
  37. package/core/router/router-eval.mjs +166 -0
  38. package/core/router/router-live.test.mjs +89 -0
  39. package/core/router/router.test.mjs +838 -0
  40. package/core/router/scorer.mjs +72 -0
  41. package/core/router/sparse-retriever.mjs +79 -0
  42. package/core/router/tokenize.mjs +42 -0
  43. package/core/state/bundle-state.mjs +124 -0
  44. package/core/state/bundle-state.test.mjs +175 -0
  45. package/core/state/config.mjs +115 -0
  46. package/core/state/feedback.mjs +129 -0
  47. package/core/state/feedback.test.mjs +181 -0
  48. package/core/state/runtime-root.test.mjs +22 -0
  49. package/core/state/stack-detect.mjs +102 -0
  50. package/core/state/stack-detect.test.mjs +64 -0
  51. package/core/surface/config-sync-backends.mjs +224 -0
  52. package/core/surface/connector-targets.mjs +205 -0
  53. package/core/surface/discover-hooks.mjs +151 -0
  54. package/core/surface/discover-hooks.test.mjs +63 -0
  55. package/core/surface/discover-surface.test.mjs +49 -0
  56. package/core/surface/extra-hosts.mjs +48 -0
  57. package/core/surface/extra-hosts.test.mjs +24 -0
  58. package/core/surface/hook-targets.mjs +102 -0
  59. package/core/surface/surface-copy-targets.mjs +37 -0
  60. package/core/surface/surface-inventory.mjs +106 -0
  61. package/core/surface/surface-matrix.mjs +133 -0
  62. package/core/surface/surface-matrix.test.mjs +90 -0
  63. package/docs/router-eval-set.jsonl +38 -0
  64. package/hooks.manifest.yaml +16 -0
  65. package/package.json +101 -0
  66. package/recipe.yaml +247 -0
  67. package/recipes/foundry.yaml +27 -0
  68. package/recipes/manifest.yaml +18 -0
  69. package/recipes/roles/backend-data.yaml +26 -0
  70. package/recipes/roles/coding.yaml +26 -0
  71. package/recipes/roles/frontend.yaml +14 -0
  72. package/recipes/roles/research.yaml +31 -0
  73. package/recipes/roles/secure.yaml +27 -0
  74. package/router.config.yaml +120 -0
  75. package/scripts/bundles.mjs +131 -0
  76. package/scripts/doctor.mjs +117 -0
  77. package/scripts/embedded-hooks.mjs +27 -0
  78. package/scripts/hosts.mjs +60 -0
  79. package/scripts/link/link-connectors.mjs +190 -0
  80. package/scripts/link/link-connectors.test.mjs +111 -0
  81. package/scripts/link/link-hooks.mjs +259 -0
  82. package/scripts/link/link-hooks.test.mjs +112 -0
  83. package/scripts/link/link-surfaces.mjs +188 -0
  84. package/scripts/link/link-surfaces.test.mjs +76 -0
  85. package/scripts/load.mjs +143 -0
  86. package/scripts/package-contract.test.mjs +26 -0
  87. package/scripts/surface-inventory.mjs +6 -0
  88. package/scripts/sync/agents-surface.mjs +55 -0
  89. package/scripts/sync/agents-surface.test.mjs +18 -0
  90. package/scripts/sync/auto-sync.mjs +135 -0
  91. package/scripts/sync/auto-sync.test.mjs +41 -0
  92. package/scripts/sync/import-surfaces.mjs +331 -0
  93. package/scripts/sync/import-surfaces.test.mjs +106 -0
  94. package/scripts/sync/sync-config.mjs +230 -0
  95. package/scripts/sync/sync-config.test.mjs +141 -0
  96. package/scripts/sync/sync-surfaces.mjs +229 -0
  97. package/scripts/sync/sync-surfaces.test.mjs +66 -0
  98. package/scripts/tui-actions.mjs +59 -0
  99. package/scripts/tui-actions.test.mjs +53 -0
  100. package/scripts/tui.mjs +782 -0
  101. package/scripts/uninstall-all.mjs +39 -0
  102. package/server/mcp-server.mjs +129 -0
  103. package/server/mcp-server.test.mjs +175 -0
  104. package/skills/portawhip/SKILL.md +80 -0
  105. package/surface-matrix.yaml +93 -0
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "portawhip",
3
+ "private": false,
4
+ "type": "module",
5
+ "version": "0.1.0",
6
+ "description": "Tools collector + context-cheap router for AI agent hosts: dispatches capability install to add-mcp/mise/asm, then routes the right one at the right moment instead of loading everything into context.",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/VVeb1250/portawhip#readme",
9
+ "bugs": {
10
+ "url": "https://github.com/VVeb1250/portawhip/issues"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/VVeb1250/portawhip.git"
15
+ },
16
+ "keywords": [
17
+ "ai-agents",
18
+ "mcp",
19
+ "model-context-protocol",
20
+ "agent-tools",
21
+ "tool-router",
22
+ "claude-code",
23
+ "codex",
24
+ "gemini-cli",
25
+ "developer-tools"
26
+ ],
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "bin": {
31
+ "portawhip": "scripts/tui.mjs",
32
+ "portawhip-router": "core/router/router-cli.mjs",
33
+ "harness-router": "server/mcp-server.mjs"
34
+ },
35
+ "files": [
36
+ "adapters/",
37
+ "core/",
38
+ "docs/router-eval-set.jsonl",
39
+ "CHANGELOG.md",
40
+ "SECURITY.md",
41
+ "recipes/",
42
+ "scripts/",
43
+ "server/",
44
+ "skills/",
45
+ "hooks.manifest.yaml",
46
+ "recipe.yaml",
47
+ "router.config.yaml",
48
+ "surface-matrix.yaml",
49
+ "VISION.md"
50
+ ],
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "scripts": {
55
+ "load": "node scripts/load.mjs",
56
+ "doctor": "node scripts/doctor.mjs",
57
+ "connectors": "node scripts/link/link-connectors.mjs status",
58
+ "connectors:link": "node scripts/link/link-connectors.mjs install",
59
+ "hooks": "node scripts/link/link-hooks.mjs status",
60
+ "hooks:link": "node scripts/link/link-hooks.mjs install",
61
+ "sync-config": "node scripts/sync/sync-config.mjs status",
62
+ "sync-config:preview": "node scripts/sync/sync-config.mjs preview",
63
+ "agents": "node scripts/sync/agents-surface.mjs",
64
+ "agents:connect": "node scripts/sync/agents-surface.mjs connect",
65
+ "agents:mcp": "node scripts/sync/agents-surface.mjs mcp",
66
+ "surface": "node scripts/surface-inventory.mjs",
67
+ "import": "node scripts/sync/import-surfaces.mjs status",
68
+ "import:preview": "node scripts/sync/import-surfaces.mjs preview",
69
+ "hooks:embedded": "node scripts/embedded-hooks.mjs",
70
+ "surface:sync": "node scripts/sync/sync-surfaces.mjs sync",
71
+ "surface:sync:check": "node scripts/sync/sync-surfaces.mjs check",
72
+ "surface:watch": "node scripts/sync/sync-surfaces.mjs watch",
73
+ "tui": "node scripts/tui.mjs",
74
+ "bundles": "node scripts/bundles.mjs list",
75
+ "bundles:status": "node scripts/bundles.mjs status",
76
+ "bundles:select": "node scripts/bundles.mjs select",
77
+ "test": "node --test",
78
+ "route": "node core/router/router-cli.mjs route",
79
+ "route:list": "node core/router/router-cli.mjs list",
80
+ "route:eval": "node core/router/router-cli.mjs eval --engine hybrid",
81
+ "route:compare": "node core/router/router-cli.mjs compare",
82
+ "route:graph": "node core/router/router-cli.mjs graph-compile --out .hp-state/capability-graph.json",
83
+ "route:enrich": "node core/router/router-cli.mjs enrich"
84
+ },
85
+ "dependencies": {
86
+ "@huggingface/transformers": "^4.2.0",
87
+ "@modelcontextprotocol/sdk": "^1.29.0",
88
+ "add-mcp": "^1.13.0",
89
+ "agent-skill-manager": "^2.7.0",
90
+ "ansi-escapes": "^7.3.0",
91
+ "cross-spawn": "^7.0.6",
92
+ "ink": "^5.2.1",
93
+ "js-yaml": "^4.1.0",
94
+ "minisearch": "^7.2.0",
95
+ "react": "^18.3.1"
96
+ },
97
+ "devDependencies": {
98
+ "@agents-dev/cli": "^0.8.9",
99
+ "ai-config-sync-manager": "0.1.5"
100
+ }
101
+ }
package/recipe.yaml ADDED
@@ -0,0 +1,247 @@
1
+ # Step 1 proof-of-concept recipe.
2
+ # Each entry declares WHAT capability to load, not HOW — the dispatcher
3
+ # picks the backend per `type` and shells out to a tool that already
4
+ # solves that backend well (add-mcp / mise / asm). No install logic
5
+ # lives in this repo.
6
+ - id: context7
7
+ type: mcp
8
+ source: https://mcp.context7.com/mcp
9
+ route:
10
+ triggers: ["library docs", "api docs", "documentation", "how to use", "sdk usage", "package docs"]
11
+ description: "Fetch current docs/examples for a library, framework, or API"
12
+ when: [user_prompt]
13
+ inject: hint
14
+
15
+ - id: ripgrep
16
+ type: cli
17
+ source: ripgrep
18
+ route:
19
+ triggers: ["search codebase", "grep", "find text in files", "regex search"]
20
+ description: "Fast regex search across files"
21
+ when: [user_prompt]
22
+ inject: hint
23
+ binary: rg
24
+
25
+ - id: pdf
26
+ # id matches the skill's real installed name (not "anthropic-skills") on
27
+ # purpose: registry.mjs dedupes curated vs. auto-discovered entries by id,
28
+ # and this skill also gets auto-discovered once installed (asm reports it
29
+ # as "pdf" in every host's skill dir). Matching ids lets the curated,
30
+ # deliberately-authored route win outright instead of competing on score
31
+ # against a separately-discovered duplicate of the same capability.
32
+ type: skill
33
+ source: github:anthropics/skills
34
+ path: skills/pdf
35
+ route:
36
+ triggers: ["pdf", "merge pages", "ocr", "extract table"]
37
+ description: "Read/merge/split/OCR PDF files"
38
+ when: [user_prompt]
39
+ inject: hint
40
+
41
+ - id: codegraph
42
+ # Same curated-override-wins pattern as pdf above: codegraph is already
43
+ # auto-discovered (installed globally as an MCP server), but it also
44
+ # needs a per-project .codegraph/ index before it's actually usable here.
45
+ # Observed live: the router suggested codegraph in THIS repo, which has
46
+ # no .codegraph/ — readyMarker/readyHint let the suggestion say so
47
+ # instead of silently implying it just works.
48
+ type: mcp
49
+ source: codegraph
50
+ route:
51
+ triggers: ["codegraph", "code graph", "call graph", "blast radius", "symbol graph"]
52
+ description: "MCP server: codebase knowledge graph (symbols, edges, call paths)"
53
+ when: [user_prompt]
54
+ inject: hint
55
+ readyMarker: ".codegraph"
56
+ readyHint: "codegraph init"
57
+
58
+ - id: harness-router
59
+ type: mcp
60
+ source: node
61
+ path: "."
62
+ args: ["server/mcp-server.mjs"]
63
+ name: harness-router
64
+ scope: project
65
+ route:
66
+ triggers:
67
+ [
68
+ "harness-router",
69
+ "portawhip",
70
+ "hybrid-router.mjs",
71
+ "sparse-retriever.mjs",
72
+ "router-cli.mjs",
73
+ "fix the router",
74
+ "improve the router",
75
+ ]
76
+ description: "Inspect or improve this repo's harness-router/portawhip routing source"
77
+ when: [user_prompt]
78
+ inject: hint
79
+ action: inspect_code
80
+
81
+ - id: portawhip
82
+ # This repo's own management skill (skills/portawhip/SKILL.md). Curated so
83
+ # the router surfaces it when the user talks about importing/syncing tools
84
+ # across hosts. install:false — it lives in-repo and is read via the pointer,
85
+ # like the other instruction-backed skills below.
86
+ type: skill
87
+ source: portawhip
88
+ path: skills/portawhip
89
+ install: false
90
+ route:
91
+ triggers:
92
+ [
93
+ "import capability",
94
+ "import tool",
95
+ "sync tools across hosts",
96
+ "share skill across hosts",
97
+ "fan out to all hosts",
98
+ "manage portawhip",
99
+ "surface coverage matrix",
100
+ "what capabilities are synced",
101
+ "import newly installed",
102
+ ]
103
+ description: "Manage portawhip cross-host capability sync: import, enrich, check coverage, fan out to all hosts"
104
+ when: [user_prompt]
105
+ inject: hint
106
+ action: read_skill
107
+
108
+ - id: workspace-surface-audit
109
+ type: skill
110
+ source: workspace-surface-audit
111
+ install: false
112
+ route:
113
+ triggers:
114
+ [
115
+ "ecc:harness-audit",
116
+ "harness audit",
117
+ "audit connector",
118
+ "audit hook",
119
+ "connector audit",
120
+ "hook audit",
121
+ "mcp audit",
122
+ "workspace surface audit",
123
+ "harness surface",
124
+ ]
125
+ description: "Audit the active repo, MCP servers, connectors, hooks, plugins, and ECC harness surface"
126
+ when: [user_prompt]
127
+ inject: hint
128
+ action: read_skill
129
+
130
+ - id: ai-config-sync-manager
131
+ type: config-sync
132
+ source: ai-config-sync
133
+ install: false
134
+ route:
135
+ triggers:
136
+ [
137
+ "sync claude codex config",
138
+ "sync config",
139
+ "config drift",
140
+ "copy skills to codex",
141
+ "copy skills to claude",
142
+ "sync mcp config",
143
+ "sync hooks permissions",
144
+ "claude codex drift",
145
+ ]
146
+ description: "Bidirectional Claude Code/Codex config sync via ai-config-sync-manager"
147
+ when: [user_prompt]
148
+ inject: hint
149
+ action: run_sync_config
150
+
151
+ - id: agent-skill-manager-sync-probe
152
+ type: config-sync
153
+ source: agent-skill-manager
154
+ install: false
155
+ route:
156
+ triggers:
157
+ [
158
+ "agent-skill-manager",
159
+ "asm providers",
160
+ "skill providers",
161
+ "skills installed across hosts",
162
+ "sync skills",
163
+ ]
164
+ description: "Probe agent-skill-manager provider state used by the skill install backend"
165
+ when: [user_prompt]
166
+ inject: hint
167
+ action: run_sync_config_status
168
+
169
+ - id: agents-dotdir-sync
170
+ type: config-sync
171
+ source: agents
172
+ install: false
173
+ route:
174
+ triggers:
175
+ [
176
+ ".agents",
177
+ "agents sync",
178
+ "agents status",
179
+ "single source of truth",
180
+ "sync all hosts",
181
+ "sync codex claude gemini cursor",
182
+ ]
183
+ description: "Probe or run @agents-dev/cli .agents source-of-truth sync across many hosts"
184
+ when: [user_prompt]
185
+ inject: hint
186
+ action: run_sync_config
187
+
188
+ - id: configure-ecc
189
+ type: skill
190
+ source: configure-ecc
191
+ install: false
192
+ route:
193
+ triggers:
194
+ [
195
+ "update-config",
196
+ "update config",
197
+ "settings.json hook",
198
+ "claude code settings",
199
+ "codex config",
200
+ "mcp config",
201
+ "configure ecc",
202
+ "hook troubleshooting",
203
+ "fix config",
204
+ ]
205
+ description: "Configure or repair ECC/Codex/Claude harness config, hooks, settings, and MCP wiring"
206
+ when: [user_prompt]
207
+ inject: hint
208
+ action: read_skill
209
+
210
+ - id: verification-loop
211
+ type: skill
212
+ source: verification-loop
213
+ install: false
214
+ route:
215
+ triggers:
216
+ [
217
+ "verify",
218
+ "verification",
219
+ "run tests",
220
+ "test before commit",
221
+ "pre commit verification",
222
+ "quality gate",
223
+ "fix bug and run tests",
224
+ ]
225
+ description: "Run build/test/lint/security verification after code changes and before commits"
226
+ when: [user_prompt]
227
+ inject: hint
228
+ action: read_skill
229
+
230
+ - id: code-review
231
+ type: skill
232
+ source: code-review
233
+ install: false
234
+ route:
235
+ triggers:
236
+ [
237
+ "code-review",
238
+ "code review",
239
+ "review changed code",
240
+ "review before commit",
241
+ "bug fix review",
242
+ "pull request review",
243
+ ]
244
+ description: "Review changed code for bugs, regressions, missing tests, and maintainability before commit"
245
+ when: [user_prompt]
246
+ inject: hint
247
+ action: review_code
@@ -0,0 +1,27 @@
1
+ # Foundry - whipforaweeb's recommended core bundle. Recommended, not forced:
2
+ # `node scripts/bundles.mjs select --foundry` opts in, `select --none` opts
3
+ # out at any time.
4
+ #
5
+ # Every entry here MUST carry a route: block. This is the rule that keeps
6
+ # this bundle from repeating the predecessor project's own gripe: a set of
7
+ # curated tools that installed cleanly but was never wired into behavior
8
+ # ("bundle harness = bench harness, not production harness" -
9
+ # E:\portable-harness\docs\BUNDLE.md section 7). Selecting a bundle here
10
+ # writes the SAME recipe-file list that both scripts/load.mjs (install) and
11
+ # the router/hooks (routing) read - so nothing in this file can end up
12
+ # installed-then-silent.
13
+ #
14
+ # ast-grep is carried over from the predecessor project's already
15
+ # bench-proven 0-tax core (docs/BUNDLE.md: "+69% precision/+76-97% codemod"
16
+ # vs plain grep/sed on structural edits) and its mise registry name was
17
+ # re-verified live in this repo on 2026-07-05 (`mise registry` lists
18
+ # `ast-grep -> aqua:ast-grep/ast-grep`).
19
+ - id: ast-grep
20
+ type: cli
21
+ source: ast-grep
22
+ route:
23
+ triggers: ["structural search", "ast-grep", "codemod", "rewrite code pattern", "syntax-aware search"]
24
+ description: "Structural, syntax-aware code search and rewrite (AST-based, not plain text)"
25
+ when: [user_prompt]
26
+ inject: hint
27
+ binary: ast-grep
@@ -0,0 +1,18 @@
1
+ # Bundle catalog metadata (data only, no logic) — read by
2
+ # `scripts/bundles.mjs list`/`status`. Every bundle here is opt-in; foundry
3
+ # is only "recommended", never auto-installed or auto-selected (agreed
4
+ # 2026-07-05: bundles recommend, they never force).
5
+ foundry:
6
+ description: "whipforaweeb's recommended core - small, cross-OS, 0-MCP. Opt-out anytime with `bundles select --none`."
7
+ recommended: true
8
+ roles:
9
+ secure:
10
+ description: "Deterministic supply-chain/secret checks (0-MCP). Pick this if your role touches dependencies, CI, or handles credentials."
11
+ coding:
12
+ description: "Code-size orientation and perf verification (0-MCP). Pick this if you write/refactor code day to day."
13
+ research:
14
+ description: "Document format conversion (0-MCP). Pick this if you work with docs, papers, or notes across formats."
15
+ frontend:
16
+ description: "JS/TS/JSON lint and format (0-MCP). Pick this if your role touches frontend or Node code."
17
+ backend-data:
18
+ description: "Query CSV/Parquet/SQLite/JSON without loading them into context (0-MCP). Pick this if your role touches data files or APIs returning JSON."
@@ -0,0 +1,26 @@
1
+ # Role: backend-data - opt-in via
2
+ # `node scripts/bundles.mjs select --role backend-data`.
3
+ # duckdb + jq are carried over from the predecessor project's already
4
+ # bench-proven data-query set (docs/BUNDLE.md: "+99.96% vs Read" for bulk
5
+ # CSV/Parquet/SQLite/JSON queries, 0 MCP). Both mise registry names
6
+ # re-verified live in this repo on 2026-07-05 (`mise registry`: duckdb ->
7
+ # aqua:duckdb/duckdb, jq -> aqua:jqlang/jq).
8
+ - id: duckdb
9
+ type: cli
10
+ source: duckdb
11
+ route:
12
+ triggers: ["query csv", "duckdb", "query parquet", "sql on files", "analyze large dataset"]
13
+ description: "Run SQL directly over CSV/Parquet/SQLite/JSON files without loading them into context"
14
+ when: [user_prompt]
15
+ inject: hint
16
+ binary: duckdb
17
+
18
+ - id: jq
19
+ type: cli
20
+ source: jq
21
+ route:
22
+ triggers: ["parse json", "jq", "filter json", "transform json", "query json file"]
23
+ description: "Slice, filter, and transform JSON from the command line"
24
+ when: [user_prompt]
25
+ inject: hint
26
+ binary: jq
@@ -0,0 +1,26 @@
1
+ # Role: coding - opt-in via `node scripts/bundles.mjs select --role coding`.
2
+ # ast-grep (structural search/rewrite) already lives in foundry, so this
3
+ # role covers what foundry doesn't: perf verification while coding.
4
+ #
5
+ # tokei (LOC counter) was tried and dropped live on 2026-07-05: mise's
6
+ # registry only offers it via a cargo build (`cargo:tokei`) or an aqua entry
7
+ # that itself refuses non-binary cargo packages ("package type cargo is not
8
+ # supported in the aqua backend") - no prebuilt binary path on this
9
+ # platform. That's compile-tax, the same class of problem that got
10
+ # `headroom-ai` rejected from the predecessor project's bundle (see
11
+ # E:\portable-harness\docs\BUNDLE.md: "= compile-tax ทุก host ... ขัด North
12
+ # Star portable"). Not forcing a Rust toolchain install for one bundle tool.
13
+ # No prebuilt-binary LOC-counter alternative (scc, cloc) was found in mise's
14
+ # registry either - open gap, not silently worked around.
15
+ #
16
+ # hyperfine's mise registry name re-verified live on 2026-07-05
17
+ # (`mise registry`: hyperfine -> aqua:sharkdp/hyperfine cargo:hyperfine).
18
+ - id: hyperfine
19
+ type: cli
20
+ source: hyperfine
21
+ route:
22
+ triggers: ["benchmark command", "hyperfine", "compare performance", "measure runtime", "how fast is this"]
23
+ description: "Command-line benchmarking with statistically sound comparisons"
24
+ when: [user_prompt]
25
+ inject: hint
26
+ binary: hyperfine
@@ -0,0 +1,14 @@
1
+ # Role: frontend - opt-in via `node scripts/bundles.mjs select --role frontend`.
2
+ # biome is a single-binary linter+formatter for JS/TS/JSON (fast, no Node
3
+ # toolchain dependency at the binary level). mise registry name re-verified
4
+ # live in this repo on 2026-07-05 (`mise registry`: biome ->
5
+ # aqua:biomejs/biome npm:@biomejs/biome).
6
+ - id: biome
7
+ type: cli
8
+ source: biome
9
+ route:
10
+ triggers: ["lint javascript", "biome", "format typescript", "lint frontend code", "js formatter"]
11
+ description: "Lint and format JS/TS/JSON (fast, single binary)"
12
+ when: [user_prompt]
13
+ inject: hint
14
+ binary: biome
@@ -0,0 +1,31 @@
1
+ # Role: research - opt-in via `node scripts/bundles.mjs select --role research`.
2
+ # pandoc handles the common research friction of converting between document
3
+ # formats (docs/papers/notes) without a heavyweight app. mise registry name
4
+ # re-verified live in this repo on 2026-07-05 (`mise registry`: pandoc ->
5
+ # github:jgm/pandoc).
6
+ - id: pandoc
7
+ type: cli
8
+ source: pandoc
9
+ route:
10
+ triggers: ["convert document", "pandoc", "markdown to pdf", "docx to markdown", "document format conversion"]
11
+ description: "Convert between document formats (markdown, docx, pdf, html, and more)"
12
+ when: [user_prompt]
13
+ inject: hint
14
+ binary: pandoc
15
+
16
+ # markitdown converts binary office/PDF documents to LLM-friendly markdown -
17
+ # the common research-material-ingestion step pandoc doesn't cover well
18
+ # (pandoc is markup<->markup; markitdown targets docx/pptx/xlsx/pdf/images
19
+ # specifically). Installed live via mise's pipx backend on 2026-07-05
20
+ # (`source: pipx:markitdown` - no short-name alias in mise's own registry,
21
+ # but the pipx-qualified name resolves and installs cleanly: pure Python
22
+ # wheels, no compiler needed, ~25MB, a few seconds).
23
+ - id: markitdown
24
+ type: cli
25
+ source: pipx:markitdown
26
+ route:
27
+ triggers: ["convert to markdown", "markitdown", "extract text from pdf", "docx to text", "read office document"]
28
+ description: "Convert docx/pptx/xlsx/pdf/images to markdown for LLM-friendly reading"
29
+ when: [user_prompt]
30
+ inject: hint
31
+ binary: markitdown
@@ -0,0 +1,27 @@
1
+ # Role: secure - opt-in via `node scripts/bundles.mjs select --role secure`
2
+ # (combine with other roles: --role secure,coding).
3
+ #
4
+ # gitleaks + osv-scanner are carried over from the predecessor project's
5
+ # already bench-proven secure-agent set (docs/BUNDLE.md: "tested PASS",
6
+ # 0 MCP, deterministic). Both mise registry names were re-verified live in
7
+ # this repo on 2026-07-05 (`mise registry`: gitleaks -> aqua:gitleaks/gitleaks,
8
+ # osv-scanner -> aqua:google/osv-scanner).
9
+ - id: gitleaks
10
+ type: cli
11
+ source: gitleaks
12
+ route:
13
+ triggers: ["secret scan", "gitleaks", "leaked credentials", "hardcoded secret", "api key in code"]
14
+ description: "Scan for hardcoded secrets/credentials before they get committed"
15
+ when: [user_prompt]
16
+ inject: hint
17
+ binary: gitleaks
18
+
19
+ - id: osv-scanner
20
+ type: cli
21
+ source: osv-scanner
22
+ route:
23
+ triggers: ["vulnerability scan", "osv-scanner", "dependency vulnerabilities", "cve check", "supply chain"]
24
+ description: "Scan dependencies for known vulnerabilities (OSV database)"
25
+ when: [user_prompt]
26
+ inject: hint
27
+ binary: osv-scanner
@@ -0,0 +1,120 @@
1
+ # Tunable router defaults. This is data the scorer reads at runtime —
2
+ # not decision logic. Missing file / missing keys fall back to the same
3
+ # defaults shown here (core/state/config.mjs), so this file is optional.
4
+ #
5
+ # Two thresholds, not one — curated (recipe.yaml) triggers are deliberately
6
+ # authored, so a single hit is trustworthy (recipeThreshold=1). Auto-
7
+ # discovered triggers (core/registry/discover.mjs) are inferred from free-text
8
+ # descriptions across 300+ installed skills and produce real single-word
9
+ # noise (e.g. "write" fired on "write a poem about the ocean" against
10
+ # writing-related skills) — verified requiring 2 distinct hits removes that
11
+ # false positive without losing true matches (grep/pdf/docs still route).
12
+ engine: hybrid
13
+ threshold: 2
14
+ recipeThreshold: 1
15
+ # Hybrid retrieval uses minisearch (core/router/sparse-retriever.mjs) — score scale
16
+ # is in the hundreds/thousands, not the small BM25-ish numbers the old
17
+ # hand-rolled engine produced. Two thresholds here too, for the same reason
18
+ # as recipeThreshold above: curated entries (recipe.yaml) need a much lower
19
+ # bar than auto-discovered ones. Verified empirically (docs/archive/phase2.5-verify.md):
20
+ # generic domain vocabulary shared across hundreds of skill docs ("architecture",
21
+ # "patterns") can outscore a genuine curated match (ripgrep/context7), so one
22
+ # global threshold can't separate "real auto match" from "noise" AND "real
23
+ # curated match" at the same time. Calibrated against docs/router-eval-set.jsonl:
24
+ # 350 clears all 18 negatives (0 false positives, including both hard prompts
25
+ # that regress-test the old noisy hook) while keeping all 5 original Phase-2
26
+ # positives; costs 4 of 7 newer p25 positives (accepted: silence > wrong guess).
27
+ hybridThreshold: 350
28
+ hybridRecipeThreshold: 130
29
+ # Auto-discovered MCP/CLI tools are a third bucket, not "skills with a lower
30
+ # bar": add-mcp/mise give no description, so their documents are just a bare
31
+ # name with almost nothing for generic vocabulary to falsely match against.
32
+ # Verified: 0 false positives from any mcp/cli-type entry across the full
33
+ # negative eval set, so this can sit far below hybridThreshold without risk
34
+ # — recovers real "use <tool>" matches (exa/github/playwright) that
35
+ # hybridThreshold=350 was silently dropping.
36
+ hybridToolThreshold: 80
37
+ # Optional Phase-2.5 graph. If the file is absent, graph expansion is a no-op.
38
+ # Schema: {"edges":[{"from":"capability-id","to":"neighbor-id","type":"related_skill","weight":1}]}
39
+ graphPath: .hp-state/capability-graph.json
40
+ graphBoost: 0.25
41
+ k: 5
42
+ # A lane (skill or tool) where the top match barely beats the runner-up is
43
+ # diffuse-vocabulary noise, not a genuine pick - verified against the last
44
+ # remaining false positive in docs/router-eval-set.jsonl: "what context
45
+ # signals should a router observe before injecting a skill" scores
46
+ # example-skill and skill-development within 0.3% of each other (both lit up
47
+ # by the same generic "router"/"skill"/"injecting" words shared across
48
+ # hundreds of skill docs), while every genuine match in the eval set has a
49
+ # clear top pick with no close runner-up. Swept 1.05/1.08/1.12 against the
50
+ # full eval set - all three give the same result (a plateau, not a knife's
51
+ # edge), so 1.05 is not a fragile fit to one prompt. Confirmed this doesn't
52
+ # fix everything: it's a geometry-of-scores fix, so it can't separate a
53
+ # genuinely peaked-but-wrong match (a doc that's confidently, unambiguously
54
+ # the top hit yet still the wrong one) - that needs a semantic signal
55
+ # (planned: PLAN.md Phase 4's local embedding model), not a sharper number.
56
+ peakednessRatio: 1.05
57
+ # Phase 4 semantic layer (PLAN.md): dense retrieval via BAAI/bge-m3
58
+ # (core/router/dense-embedder.mjs) - a second, meaning-based channel additive to the
59
+ # lexical one above, on by default so every user gets it with zero manual
60
+ # setup (no install script, no model management - transformers.js downloads
61
+ # and caches the model itself on first use, MIT-licensed, ~543MB quantized,
62
+ # 100+ languages including Thai). Exists to close the one gap threshold/
63
+ # margin tuning on lexical scores alone provably cannot: a genuine paraphrase
64
+ # with little/no shared vocabulary (docs/router-eval-set.jsonl's e2e-testing
65
+ # miss - "use Playwright to test login flow" vs the skill's own wording).
66
+ # Fails soft to a no-op whenever the model isn't reachable, so this is safe
67
+ # to leave on - every caller sees identical output to before whenever dense
68
+ # doesn't contribute. denseThreshold is on cosine similarity's own 0..1
69
+ # scale, never comparable to hybridThreshold's hundreds/thousands scale.
70
+ # The push hook (adapters/hooks/universal-hook.mjs) explicitly disables this
71
+ # per-call: it's a fresh subprocess per prompt, so it can't amortize a
72
+ # 500MB+ model load the way the long-lived MCP server/CLI can.
73
+ denseEnabled: true
74
+ # 0.6, not the more intuitive-looking 0.55 - verified live against
75
+ # docs/router-eval-set.jsonl with the real BGE-M3 model (not simulated): 0.55
76
+ # let a genuinely irrelevant agent/skill pair through on "audit why route
77
+ # suggestions are noisy and injected at the wrong time" (network-config-
78
+ # reviewer/caveman-review, both boosted over the line by
79
+ # actionAlignmentFactor's "review" match stacked on top of raw cosine
80
+ # ~0.48-0.50 - BGE-M3's ambient similarity floor for unrelated short text,
81
+ # not a genuine match). Swept 0.55/0.56/0.58/0.60 against the full eval set:
82
+ # 0.55-0.56 fail, 0.58-0.60 both clean (falsePositiveCount 0, precisionAt1/
83
+ # recallAt3 still 1, including the e2e-testing paraphrase this channel exists
84
+ # to rescue) - a real plateau, not one lucky number. Picked 0.60 for extra
85
+ # margin over the 0.58 edge.
86
+ denseThreshold: 0.6
87
+ # WS-A mode differentiation: the push hook sees a raw prompt without the
88
+ # agent's reasoned intent summary. Characterization proved lexical/dense
89
+ # signals cannot reliably separate meta-discussion from an actionable request,
90
+ # so unsolicited push is silent by default. Set PORTAWHIP_PUSH_MODE=legacy as
91
+ # a temporary rollback switch; pull route() remains enabled and recall-generous.
92
+ pushMode: silent
93
+ # Phase 3 push-mode hook: hard char budget per injected suggestion block
94
+ # (proxy for tokens — no tokenizer dependency for this). Lines are added
95
+ # until the next one would exceed this, so it degrades to fewer/shorter
96
+ # lines rather than truncating mid-line.
97
+ #
98
+ # Bumped 320 -> 640 (2026-07-06): adding actionDirective() (invocation
99
+ # syntax per kind, so suggestions actually get acted on) made a first-time
100
+ # mcp line ~265 chars on its own (the ToolSearch-fallback clause is the
101
+ # biggest chunk) - 320 only fit one such line even when lane-reserved
102
+ # results (core/router/hybrid-router.mjs) legitimately surface 2-3 relevant
103
+ # capabilities across skill/agent/tool lanes at once. Repeat mentions
104
+ # within the same session already render tersely (universal-hook.mjs's
105
+ # sessionSuggestedIds), so this cost is paid at most once per id per
106
+ # session, not every turn - 640 fits ~2 full mcp lines or a full
107
+ # skill+agent+tool combo comfortably.
108
+ pushBudgetChars: 640
109
+ # Push/pull asymmetry (2026-07-09 audit): push is an unsolicited interruption,
110
+ # pull (MCP route()) is a solicited lookup. Advisory-systems literature calls
111
+ # the push failure mode alert fatigue - CDSS systems with 90%+ override rates
112
+ # get tuned out entirely, and this repo's own feedback log was at 22/26
113
+ # suggestions ignored. Push therefore drops any hit below this calibrated
114
+ # confidence unless it's required-tier (curated recipe.yaml entries); pull
115
+ # keeps generous recall untouched.
116
+ pushMinConfidence: 0.75
117
+ # Interrupt budget per capability per session: full line once, terse reminder
118
+ # once, then silent. (Repeat-terse already existed via sessionSuggestedIds;
119
+ # this adds the "then shut up" cap.)
120
+ pushMaxMentionsPerSession: 2