codeforge-dev 1.14.2 → 2.0.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 (106) hide show
  1. package/{.devcontainer/config/defaults → .codeforge/config}/ccstatusline-settings.json +44 -6
  2. package/{.devcontainer/config/defaults → .codeforge/config}/main-system-prompt.md +14 -6
  3. package/.codeforge/config/orchestrator-system-prompt.md +333 -0
  4. package/{.devcontainer/config/defaults → .codeforge/config}/settings.json +3 -1
  5. package/{.devcontainer/config → .codeforge}/file-manifest.json +15 -9
  6. package/{.devcontainer → .codeforge/scripts}/connect-external-terminal.sh +3 -1
  7. package/.devcontainer/.env.example +5 -5
  8. package/.devcontainer/.secrets.example +3 -0
  9. package/.devcontainer/CHANGELOG.md +242 -0
  10. package/.devcontainer/CLAUDE.md +129 -22
  11. package/.devcontainer/README.md +34 -19
  12. package/.devcontainer/devcontainer.json +28 -10
  13. package/.devcontainer/features/agent-browser/install.sh +2 -0
  14. package/.devcontainer/features/ast-grep/install.sh +2 -0
  15. package/.devcontainer/features/biome/install.sh +2 -0
  16. package/.devcontainer/features/ccburn/install.sh +2 -0
  17. package/.devcontainer/features/ccms/install.sh +2 -0
  18. package/.devcontainer/features/ccstatusline/README.md +7 -6
  19. package/.devcontainer/features/ccstatusline/install.sh +9 -4
  20. package/.devcontainer/features/ccusage/install.sh +2 -0
  21. package/.devcontainer/features/chromaterm/chromaterm.yml +2 -2
  22. package/.devcontainer/features/chromaterm/install.sh +2 -0
  23. package/.devcontainer/features/claude-code-native/README.md +47 -0
  24. package/.devcontainer/features/claude-code-native/devcontainer-feature.json +29 -0
  25. package/.devcontainer/features/claude-code-native/install.sh +131 -0
  26. package/.devcontainer/features/claude-monitor/install.sh +2 -0
  27. package/.devcontainer/features/claude-session-dashboard/README.md +2 -2
  28. package/.devcontainer/features/claude-session-dashboard/install.sh +2 -0
  29. package/.devcontainer/features/dprint/install.sh +2 -0
  30. package/.devcontainer/features/hadolint/install.sh +2 -0
  31. package/.devcontainer/features/kitty-terminfo/README.md +3 -1
  32. package/.devcontainer/features/kitty-terminfo/install.sh +2 -0
  33. package/.devcontainer/features/lsp-servers/install.sh +2 -0
  34. package/.devcontainer/features/mcp-qdrant/CHANGES.md +3 -3
  35. package/.devcontainer/features/mcp-qdrant/README.md +1 -0
  36. package/.devcontainer/features/mcp-qdrant/devcontainer-feature.json +1 -1
  37. package/.devcontainer/features/mcp-qdrant/install.sh +9 -2
  38. package/.devcontainer/features/mcp-qdrant/poststart-hook.sh +9 -2
  39. package/.devcontainer/features/notify-hook/devcontainer-feature.json +1 -1
  40. package/.devcontainer/features/notify-hook/install.sh +2 -0
  41. package/.devcontainer/features/ruff/install.sh +2 -0
  42. package/.devcontainer/features/shellcheck/install.sh +2 -0
  43. package/.devcontainer/features/shfmt/install.sh +2 -0
  44. package/.devcontainer/features/tmux/README.md +3 -3
  45. package/.devcontainer/features/tmux/install.sh +3 -1
  46. package/.devcontainer/features/tree-sitter/install.sh +2 -0
  47. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +27 -11
  48. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/README.md +23 -4
  49. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/claude-guide.md +4 -4
  50. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/documenter.md +254 -0
  51. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/implementer.md +260 -0
  52. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/investigator.md +255 -0
  53. package/.devcontainer/plugins/devs-marketplace/plugins/agent-system/agents/tester.md +304 -0
  54. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/README.md +1 -1
  55. package/.devcontainer/plugins/devs-marketplace/plugins/auto-code-quality/scripts/advisory-test-runner.py +4 -2
  56. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +2 -2
  57. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/.claude-plugin/plugin.json +7 -0
  58. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/README.md +125 -0
  59. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/pr-review/SKILL.md +325 -0
  60. package/.devcontainer/plugins/devs-marketplace/plugins/git-workflow/skills/ship/SKILL.md +314 -0
  61. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/.claude-plugin/plugin.json +5 -0
  62. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/README.md +52 -0
  63. package/.devcontainer/plugins/devs-marketplace/plugins/prompt-snippets/skills/ps/SKILL.md +37 -0
  64. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected-bash.py +1 -1
  65. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +1 -1
  66. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/README.md +30 -14
  67. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/hooks/hooks.json +13 -1
  68. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/collect-session-edits.py +44 -0
  69. package/.devcontainer/plugins/devs-marketplace/plugins/session-context/scripts/commit-reminder.py +89 -10
  70. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/.claude-plugin/plugin.json +1 -1
  71. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/README.md +19 -11
  72. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/scripts/skill-suggester.py +476 -282
  73. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/SKILL.md +227 -0
  74. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/manual-worktree-commands.md +238 -0
  75. package/.devcontainer/plugins/devs-marketplace/plugins/skill-engine/skills/worktree/references/parallel-workflow-patterns.md +228 -0
  76. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/scripts/ticket-linker.py +2 -2
  77. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/README.md +1 -1
  78. package/.devcontainer/plugins/devs-marketplace/plugins/workspace-scope-guard/scripts/guard-workspace-scope.py +3 -2
  79. package/.devcontainer/scripts/check-setup.sh +5 -3
  80. package/.devcontainer/scripts/preflight.sh +113 -0
  81. package/.devcontainer/scripts/setup-aliases.sh +13 -8
  82. package/.devcontainer/scripts/setup-auth.sh +46 -0
  83. package/.devcontainer/scripts/setup-config.sh +29 -10
  84. package/.devcontainer/scripts/setup-migrate-claude.sh +80 -0
  85. package/.devcontainer/scripts/setup-migrate-codeforge.sh +60 -0
  86. package/.devcontainer/scripts/setup-plugins.sh +3 -1
  87. package/.devcontainer/scripts/setup-projects.sh +3 -1
  88. package/.devcontainer/scripts/setup-terminal.sh +3 -1
  89. package/.devcontainer/scripts/setup-update-claude.sh +22 -27
  90. package/.devcontainer/scripts/setup.sh +57 -5
  91. package/LICENSE.txt +14 -0
  92. package/README.md +79 -5
  93. package/package.json +2 -1
  94. package/setup.js +392 -21
  95. package/.devcontainer/docs/configuration-reference.md +0 -93
  96. package/.devcontainer/docs/keybindings.md +0 -100
  97. package/.devcontainer/docs/optional-features.md +0 -64
  98. package/.devcontainer/docs/plugins.md +0 -176
  99. package/.devcontainer/docs/troubleshooting.md +0 -128
  100. package/.devcontainer/scripts/setup-symlink-claude.sh +0 -36
  101. /package/{.devcontainer/config/defaults → .codeforge/config}/keybindings.json +0 -0
  102. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/session-search.md +0 -0
  103. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/spec-workflow.md +0 -0
  104. /package/{.devcontainer/config/defaults → .codeforge/config}/rules/workspace-scope.md +0 -0
  105. /package/{.devcontainer/config/defaults → .codeforge/config}/writing-system-prompt.md +0 -0
  106. /package/{.devcontainer → .codeforge/scripts}/connect-external-terminal.ps1 +0 -0
@@ -5,419 +5,567 @@ Detects which hook event called it via input JSON shape:
5
5
  - UserPromptSubmit: {"prompt": "..."} -> {"additionalContext": "..."}
6
6
  - SubagentStart: {"subagent_type": "Plan", "prompt": "..."} -> {"additionalContext": "..."}
7
7
 
8
- Matches user prompts against skill keyword maps (phrases + terms) and suggests
9
- relevant skills. Outputs nothing when no skills match.
8
+ Uses weighted scoring with negative patterns and context guards to suggest
9
+ the most relevant skills. Returns at most MAX_SKILLS suggestions, ranked
10
+ by confidence score.
10
11
  """
11
12
 
12
13
  import json
13
14
  import re
14
15
  import sys
15
16
 
16
- SKILLS = {
17
+ # Maximum number of skills to suggest per prompt.
18
+ MAX_SKILLS = 3
19
+
20
+ # Minimum score for a match to appear in final results. Set to 0 so that
21
+ # even low-weight phrases can survive if they pass context guard checks.
22
+ # The context guard + MAX_SKILLS cap handle quality control instead.
23
+ MIN_SCORE = 0.0
24
+
25
+ # Fixed score assigned to whole-word term matches (regex \b...\b).
26
+ TERM_WEIGHT = 0.6
27
+
28
+ # Threshold below which context guards are enforced. Matches scoring below
29
+ # this value must have at least one context guard word present in the prompt,
30
+ # otherwise the match is discarded as low-confidence.
31
+ CONTEXT_GUARD_THRESHOLD = 0.6
32
+
33
+ # ---------------------------------------------------------------------------
34
+ # Skill definitions
35
+ #
36
+ # Each skill has:
37
+ # phrases — list of (substring, weight) tuples. Weight 0.0-1.0
38
+ # reflects how confidently the phrase indicates the skill.
39
+ # terms — list of whole-word regex terms (case-insensitive).
40
+ # All term matches receive TERM_WEIGHT.
41
+ # negative — (optional) list of substrings that instantly disqualify
42
+ # the skill, even if phrases/terms matched.
43
+ # context_guards — (optional) list of substrings. When the best match
44
+ # score is below CONTEXT_GUARD_THRESHOLD, at least one
45
+ # guard must be present in the prompt or the match is
46
+ # dropped.
47
+ # priority — integer tie-breaker. Higher = preferred when scores
48
+ # are equal. 10 = explicit commands, 7 = technology,
49
+ # 5 = practice/pattern, 3 = meta/generic.
50
+ # ---------------------------------------------------------------------------
51
+
52
+ SKILLS: dict[str, dict] = {
53
+ # ------------------------------------------------------------------
54
+ # Technology skills (priority 7)
55
+ # ------------------------------------------------------------------
17
56
  "fastapi": {
18
57
  "phrases": [
19
- "build a fastapi app",
20
- "rest api with fastapi",
21
- "fastapi",
22
- "fast api",
23
- "add sse streaming",
24
- "dependency injection in fastapi",
25
- "define pydantic models",
26
- "stream llm responses",
27
- "add middleware to fastapi",
28
- "pydantic model",
29
- ],
30
- "terms": ["fastapi", "pydantic", "uvicorn", "starlette", "sse-starlette"],
58
+ ("build a fastapi app", 1.0),
59
+ ("rest api with fastapi", 1.0),
60
+ ("fastapi", 0.9),
61
+ ("fast api", 0.9),
62
+ ("add sse streaming", 0.5),
63
+ ("dependency injection in fastapi", 1.0),
64
+ ("define pydantic models", 0.4),
65
+ ("stream llm responses", 0.3),
66
+ ("add middleware to fastapi", 1.0),
67
+ ("pydantic model", 0.3),
68
+ ],
69
+ "terms": ["fastapi", "uvicorn", "starlette", "sse-starlette"],
70
+ "negative": ["pydanticai", "pydantic-ai", "pydantic ai"],
71
+ "context_guards": [
72
+ "fastapi", "fast api", "api", "endpoint", "route",
73
+ "uvicorn", "rest", "server", "web",
74
+ ],
75
+ "priority": 7,
31
76
  },
32
77
  "sqlite": {
33
78
  "phrases": [
34
- "sqlite",
35
- "set up a sqlite database",
36
- "wal mode",
37
- "fts5",
38
- "full-text search",
39
- "better-sqlite3",
40
- "cloudflare d1",
41
- "store json in sqlite",
42
- "write ctes",
43
- "window functions",
79
+ ("sqlite", 0.9),
80
+ ("set up a sqlite database", 1.0),
81
+ ("wal mode", 0.9),
82
+ ("fts5", 0.9),
83
+ ("full-text search", 0.3),
84
+ ("better-sqlite3", 1.0),
85
+ ("cloudflare d1", 0.8),
86
+ ("store json in sqlite", 1.0),
87
+ ("write ctes", 0.3),
88
+ ("window functions", 0.3),
44
89
  ],
45
90
  "terms": ["aiosqlite", "better-sqlite3"],
91
+ "negative": ["elasticsearch", "algolia", "meilisearch", "postgres", "mysql"],
92
+ "context_guards": [
93
+ "sqlite", "sql", "database", "db", "query", "table",
94
+ ],
95
+ "priority": 7,
46
96
  },
47
97
  "claude-code-headless": {
48
98
  "phrases": [
49
- "headless mode",
50
- "claude -p",
51
- "stream-json",
52
- "claude code headless",
53
- "run claude in ci",
54
- "claude in pipeline",
55
- "parse stream-json output",
56
- "track costs programmatically",
57
- "permissions for scripts",
99
+ ("headless mode", 0.8),
100
+ ("claude -p", 0.9),
101
+ ("stream-json", 0.9),
102
+ ("claude code headless", 1.0),
103
+ ("run claude in ci", 1.0),
104
+ ("claude in pipeline", 0.9),
105
+ ("parse stream-json output", 1.0),
106
+ ("track costs programmatically", 0.7),
107
+ ("permissions for scripts", 0.3),
58
108
  ],
59
109
  "terms": ["--output-format stream-json", "--permission-mode"],
110
+ "context_guards": ["claude", "headless", "ci", "pipeline", "script"],
111
+ "priority": 7,
60
112
  },
61
113
  "claude-agent-sdk": {
62
114
  "phrases": [
63
- "agent sdk",
64
- "claude agent sdk",
65
- "build an agent with the claude agent sdk",
66
- "canusetool",
67
- "sdk permissions",
68
- "create mcp tools",
69
- "define subagents",
70
- "configure sdk hooks",
71
- "stream sdk messages",
115
+ ("agent sdk", 0.9),
116
+ ("claude agent sdk", 1.0),
117
+ ("build an agent with the claude agent sdk", 1.0),
118
+ ("canusetool", 1.0),
119
+ ("sdk permissions", 0.7),
120
+ ("create mcp tools", 0.7),
121
+ ("define subagents", 0.8),
122
+ ("configure sdk hooks", 0.8),
123
+ ("stream sdk messages", 0.8),
72
124
  ],
73
125
  "terms": ["claude-agent-sdk", "claude_agent_sdk", "createSdkMcpServer"],
126
+ "priority": 7,
74
127
  },
75
128
  "pydantic-ai": {
76
129
  "phrases": [
77
- "pydantic ai",
78
- "pydantic-ai",
79
- "pydanticai",
80
- "build a pydanticai agent",
81
- "add tools to an agent",
82
- "stream responses with pydanticai",
83
- "test a pydanticai agent",
84
- "connect pydanticai to svelte",
85
- "configure model fallbacks",
130
+ ("pydantic ai", 0.9),
131
+ ("pydantic-ai", 1.0),
132
+ ("pydanticai", 1.0),
133
+ ("build a pydanticai agent", 1.0),
134
+ ("add tools to an agent", 0.5),
135
+ ("stream responses with pydanticai", 1.0),
136
+ ("test a pydanticai agent", 1.0),
137
+ ("connect pydanticai to svelte", 1.0),
138
+ ("configure model fallbacks", 0.5),
86
139
  ],
87
140
  "terms": ["pydanticai", "RunContext", "VercelAIAdapter", "FallbackModel"],
88
- },
89
- "testing": {
90
- "phrases": [
91
- "write tests",
92
- "write a test",
93
- "add tests",
94
- "add a test",
95
- "pytest fixture",
96
- "vitest config",
97
- "testing library",
98
- "mock dependencies",
99
- "test endpoint",
100
- "test component",
101
- "test sse streaming",
102
- "unit test",
103
- "integration test",
141
+ "context_guards": [
142
+ "pydantic", "agent", "ai", "model", "tool", "llm",
104
143
  ],
105
- "terms": ["pytest", "vitest", "pytest-anyio", "httpx AsyncClient"],
144
+ "priority": 7,
106
145
  },
107
146
  "docker-py": {
108
147
  "phrases": [
109
- "docker-py",
110
- "docker py",
111
- "docker sdk",
112
- "docker engine api",
113
- "docker from python",
114
- "docker api",
115
- "manage docker containers from python",
116
- "create containers programmatically",
117
- "stream container logs",
118
- "monitor container health from python",
148
+ ("docker-py", 1.0),
149
+ ("docker py", 1.0),
150
+ ("docker sdk", 0.9),
151
+ ("docker engine api", 0.9),
152
+ ("docker from python", 1.0),
153
+ ("docker api", 0.7),
154
+ ("manage docker containers from python", 1.0),
155
+ ("create containers programmatically", 0.8),
156
+ ("stream container logs", 0.7),
157
+ ("monitor container health from python", 1.0),
119
158
  ],
120
159
  "terms": ["aiodocker", "DockerClient"],
160
+ "priority": 7,
121
161
  },
122
162
  "svelte5": {
123
163
  "phrases": [
124
- "svelte component",
125
- "sveltekit",
126
- "svelte kit",
127
- "svelte rune",
128
- "svelte 5",
129
- "svelte5",
130
- "migrate from svelte 4",
131
- "manage state with $state",
132
- "drag and drop to svelte",
164
+ ("svelte component", 0.8),
165
+ ("sveltekit", 0.9),
166
+ ("svelte kit", 0.9),
167
+ ("svelte rune", 1.0),
168
+ ("svelte 5", 1.0),
169
+ ("svelte5", 1.0),
170
+ ("migrate from svelte 4", 1.0),
171
+ ("manage state with $state", 1.0),
172
+ ("drag and drop to svelte", 0.9),
133
173
  ],
134
174
  "terms": ["sveltekit", "svelte", "svelte-dnd-action", "@ai-sdk/svelte"],
175
+ "priority": 7,
135
176
  },
136
177
  "docker": {
137
178
  "phrases": [
138
- "dockerfile",
139
- "docker compose",
140
- "docker-compose",
141
- "compose file",
142
- "multi-stage build",
143
- "health check",
144
- "healthcheck",
145
- "docker compose watch",
146
- "optimize docker image",
179
+ ("dockerfile", 0.9),
180
+ ("docker compose", 0.9),
181
+ ("docker-compose", 0.9),
182
+ ("compose file", 0.8),
183
+ ("multi-stage build", 0.8),
184
+ ("health check", 0.3),
185
+ ("healthcheck", 0.3),
186
+ ("docker compose watch", 1.0),
187
+ ("optimize docker image", 1.0),
147
188
  ],
148
189
  "terms": ["dockerfile", "compose.yaml", "BuildKit"],
190
+ "negative": ["docker-py", "docker py", "docker sdk", "docker from python",
191
+ "aiodocker", "dockerclient"],
192
+ "context_guards": [
193
+ "docker", "container", "compose", "image", "dockerfile",
194
+ ],
195
+ "priority": 7,
196
+ },
197
+
198
+ # ------------------------------------------------------------------
199
+ # Practice / pattern skills (priority 5)
200
+ # ------------------------------------------------------------------
201
+ "testing": {
202
+ "phrases": [
203
+ ("write tests", 0.4),
204
+ ("write a test", 0.4),
205
+ ("add tests", 0.4),
206
+ ("add a test", 0.4),
207
+ ("pytest fixture", 0.9),
208
+ ("vitest config", 0.9),
209
+ ("testing library", 0.6),
210
+ ("mock dependencies", 0.7),
211
+ ("test endpoint", 0.5),
212
+ ("test component", 0.5),
213
+ ("test sse streaming", 0.8),
214
+ ("unit test", 0.5),
215
+ ("integration test", 0.6),
216
+ ],
217
+ "terms": ["pytest", "vitest", "pytest-anyio", "httpx AsyncClient"],
218
+ "priority": 5,
149
219
  },
150
220
  "skill-building": {
151
221
  "phrases": [
152
- "build a skill",
153
- "create a skill",
154
- "write a skill",
155
- "skill.md",
156
- "skill instructions",
157
- "skill authoring",
158
- "design a skill",
159
- "improve a skill description",
160
- "optimize skill content",
222
+ ("build a skill", 0.9),
223
+ ("create a skill", 0.9),
224
+ ("write a skill", 0.9),
225
+ ("skill.md", 1.0),
226
+ ("skill instructions", 0.8),
227
+ ("skill authoring", 1.0),
228
+ ("design a skill", 0.8),
229
+ ("improve a skill description", 0.9),
230
+ ("optimize skill content", 0.8),
161
231
  ],
162
232
  "terms": [],
233
+ "priority": 5,
163
234
  },
164
235
  "debugging": {
165
236
  "phrases": [
166
- "debug logs",
167
- "check logs",
168
- "check container logs",
169
- "find error",
170
- "investigate failure",
171
- "what went wrong",
172
- "why did this crash",
173
- "diagnose the issue",
174
- "look at the logs",
175
- "read the logs",
176
- "read docker logs",
177
- "analyze error",
237
+ ("debug logs", 0.7),
238
+ ("check logs", 0.4),
239
+ ("check container logs", 0.9),
240
+ ("find error", 0.3),
241
+ ("investigate failure", 0.7),
242
+ ("what went wrong", 0.3),
243
+ ("why did this crash", 0.8),
244
+ ("diagnose the issue", 0.8),
245
+ ("look at the logs", 0.4),
246
+ ("read the logs", 0.4),
247
+ ("read docker logs", 0.9),
248
+ ("analyze error", 0.5),
178
249
  ],
179
250
  "terms": ["diagnose", "troubleshoot", "OOMKilled", "ECONNREFUSED"],
251
+ "context_guards": [
252
+ "log", "crash", "fail", "bug", "container", "stack",
253
+ "trace", "exception", "runtime", "service", "process",
254
+ ],
255
+ "priority": 5,
180
256
  },
181
257
  "refactoring-patterns": {
182
258
  "phrases": [
183
- "refactor this",
184
- "clean up code",
185
- "clean up this function",
186
- "extract a method",
187
- "fix code smells",
188
- "reduce code duplication",
189
- "simplify this class",
190
- "break up this large function",
191
- "remove dead code",
259
+ ("refactor this", 0.4),
260
+ ("clean up code", 0.4),
261
+ ("clean up this function", 0.6),
262
+ ("extract a method", 0.8),
263
+ ("fix code smells", 0.9),
264
+ ("reduce code duplication", 0.8),
265
+ ("simplify this class", 0.6),
266
+ ("break up this large function", 0.8),
267
+ ("remove dead code", 0.7),
192
268
  ],
193
269
  "terms": ["refactor", "refactoring", "code smell", "feature envy", "god class"],
270
+ "priority": 5,
194
271
  },
195
272
  "security-checklist": {
196
273
  "phrases": [
197
- "security review",
198
- "security issues",
199
- "security vulnerabilities",
200
- "check for vulnerabilities",
201
- "scan for secrets",
202
- "audit security",
203
- "review for injection",
204
- "owasp compliance",
205
- "hardcoded credentials",
274
+ ("security review", 0.8),
275
+ ("security issues", 0.5),
276
+ ("security vulnerabilities", 0.8),
277
+ ("check for vulnerabilities", 0.7),
278
+ ("scan for secrets", 0.9),
279
+ ("audit security", 0.9),
280
+ ("review for injection", 0.9),
281
+ ("owasp compliance", 1.0),
282
+ ("hardcoded credentials", 0.8),
206
283
  ],
207
284
  "terms": ["owasp", "injection", "xss", "cve", "trivy", "gitleaks"],
285
+ "priority": 5,
208
286
  },
209
287
  "git-forensics": {
210
288
  "phrases": [
211
- "git history",
212
- "who changed this",
213
- "when did this break",
214
- "git blame",
215
- "bisect a regression",
216
- "recover a lost commit",
217
- "search git history",
218
- "find when code was removed",
219
- "trace the history",
220
- "use git reflog",
289
+ ("git history", 0.7),
290
+ ("who changed this", 0.7),
291
+ ("when did this break", 0.7),
292
+ ("git blame", 0.9),
293
+ ("bisect a regression", 1.0),
294
+ ("recover a lost commit", 0.9),
295
+ ("search git history", 0.9),
296
+ ("find when code was removed", 0.8),
297
+ ("trace the history", 0.5),
298
+ ("use git reflog", 1.0),
221
299
  ],
222
300
  "terms": ["bisect", "blame", "pickaxe", "reflog", "git log -S"],
301
+ "context_guards": ["git", "commit", "branch", "history", "repo"],
302
+ "priority": 5,
223
303
  },
224
304
  "specification-writing": {
225
305
  "phrases": [
226
- "write a spec",
227
- "write requirements",
228
- "define requirements",
229
- "acceptance criteria",
230
- "user stories",
231
- "use ears format",
232
- "given/when/then",
233
- "write given/when/then scenarios",
234
- "structure requirements",
306
+ ("write a spec", 0.8),
307
+ ("write requirements", 0.7),
308
+ ("define requirements", 0.7),
309
+ ("acceptance criteria", 0.6),
310
+ ("user stories", 0.6),
311
+ ("use ears format", 1.0),
312
+ ("given/when/then", 0.8),
313
+ ("write given/when/then scenarios", 1.0),
314
+ ("structure requirements", 0.7),
235
315
  ],
236
316
  "terms": ["specification", "ears", "gherkin", "given when then"],
317
+ "priority": 5,
237
318
  },
238
319
  "performance-profiling": {
239
320
  "phrases": [
240
- "profile this code",
241
- "profile performance",
242
- "find bottleneck",
243
- "find the bottleneck",
244
- "benchmark this",
245
- "create a flamegraph",
246
- "find memory leaks",
247
- "why is this slow",
248
- "measure execution time",
249
- "reduce latency",
321
+ ("profile this code", 0.9),
322
+ ("profile performance", 0.9),
323
+ ("find bottleneck", 0.7),
324
+ ("find the bottleneck", 0.7),
325
+ ("benchmark this", 0.8),
326
+ ("create a flamegraph", 1.0),
327
+ ("find memory leaks", 0.8),
328
+ ("why is this slow", 0.4),
329
+ ("measure execution time", 0.7),
330
+ ("reduce latency", 0.4),
250
331
  ],
251
332
  "terms": ["cProfile", "py-spy", "scalene", "flamegraph", "hyperfine"],
252
- },
253
- "api-design": {
254
- "phrases": [
255
- "api design",
256
- "rest api design",
257
- "design an api",
258
- "design rest endpoints",
259
- "api versioning",
260
- "pagination strategy",
261
- "design error responses",
262
- "rate limiting",
263
- "openapi documentation",
333
+ "context_guards": [
334
+ "profile", "profiler", "benchmark", "performance", "slow",
335
+ "latency", "bottleneck", "memory",
264
336
  ],
265
- "terms": ["openapi", "swagger", "rfc7807", "rfc 7807"],
337
+ "priority": 5,
266
338
  },
267
339
  "ast-grep-patterns": {
268
340
  "phrases": [
269
- "ast-grep",
270
- "ast grep",
271
- "structural search",
272
- "syntax-aware search",
273
- "find code patterns",
274
- "search with ast-grep",
275
- "use tree-sitter",
341
+ ("ast-grep", 1.0),
342
+ ("ast grep", 1.0),
343
+ ("structural search", 0.8),
344
+ ("syntax-aware search", 0.9),
345
+ ("find code patterns", 0.5),
346
+ ("search with ast-grep", 1.0),
347
+ ("use tree-sitter", 0.8),
276
348
  ],
277
349
  "terms": ["sg run", "ast-grep", "tree-sitter"],
350
+ "context_guards": ["ast", "syntax", "pattern", "structural", "tree-sitter"],
351
+ "priority": 5,
278
352
  },
279
353
  "dependency-management": {
280
354
  "phrases": [
281
- "check dependencies",
282
- "audit dependencies",
283
- "outdated packages",
284
- "dependency health",
285
- "license check",
286
- "unused dependencies",
287
- "vulnerability scan",
288
- "find unused dependencies",
355
+ ("check dependencies", 0.5),
356
+ ("audit dependencies", 0.8),
357
+ ("outdated packages", 0.8),
358
+ ("dependency health", 0.8),
359
+ ("license check", 0.6),
360
+ ("unused dependencies", 0.8),
361
+ ("vulnerability scan", 0.7),
362
+ ("find unused dependencies", 0.9),
289
363
  ],
290
364
  "terms": ["pip-audit", "npm audit", "cargo audit", "govulncheck"],
365
+ "context_guards": [
366
+ "dependency", "dependencies", "package", "packages",
367
+ "npm", "pip", "cargo", "audit",
368
+ ],
369
+ "priority": 5,
370
+ },
371
+ "team": {
372
+ "phrases": [
373
+ ("spawn a team", 1.0),
374
+ ("create a team", 0.8),
375
+ ("team of agents", 0.9),
376
+ ("use a swarm", 0.8),
377
+ ("work in parallel", 0.4),
378
+ ("coordinate multiple agents", 0.9),
379
+ ("split this across agents", 0.9),
380
+ ("team up", 0.5),
381
+ ],
382
+ "terms": ["TeamCreate", "SendMessage"],
383
+ # Note: "parallel", "swarm", "coordinate", "team" omitted — overlap phrases
384
+ "context_guards": ["agent", "agents", "teammate", "teammates"],
385
+ "priority": 5,
386
+ },
387
+
388
+ # ------------------------------------------------------------------
389
+ # Meta / generic skills (priority 3)
390
+ # ------------------------------------------------------------------
391
+ "api-design": {
392
+ "phrases": [
393
+ ("api design", 0.6),
394
+ ("rest api design", 0.8),
395
+ ("design an api", 0.7),
396
+ ("design rest endpoints", 0.9),
397
+ ("api versioning", 0.8),
398
+ ("pagination strategy", 0.7),
399
+ ("design error responses", 0.8),
400
+ ("rate limiting", 0.6),
401
+ ("openapi documentation", 0.9),
402
+ ],
403
+ "terms": ["openapi", "swagger", "rfc7807", "rfc 7807"],
404
+ "priority": 3,
291
405
  },
292
406
  "documentation-patterns": {
293
407
  "phrases": [
294
- "write a readme",
295
- "write documentation",
296
- "add docstrings",
297
- "add jsdoc",
298
- "document the api",
299
- "create architecture docs",
300
- "update the docs",
408
+ ("write a readme", 0.6),
409
+ ("write documentation", 0.4),
410
+ ("add docstrings", 0.8),
411
+ ("add jsdoc", 0.9),
412
+ ("document the api", 0.7),
413
+ ("create architecture docs", 0.8),
414
+ ("update the docs", 0.3),
301
415
  ],
302
416
  "terms": ["docstring", "jsdoc", "tsdoc", "rustdoc", "Sphinx"],
417
+ # Note: "docs" omitted — it overlaps with the phrase "update the docs"
418
+ "context_guards": [
419
+ "documentation", "docstring", "readme", "jsdoc", "api doc",
420
+ "rustdoc", "tsdoc", "sphinx",
421
+ ],
422
+ "priority": 3,
303
423
  },
304
424
  "migration-patterns": {
305
425
  "phrases": [
306
- "migrate from",
307
- "upgrade to",
308
- "version upgrade",
309
- "framework migration",
310
- "bump python",
311
- "upgrade pydantic",
312
- "migrate express",
313
- "modernize the codebase",
314
- "commonjs to esm",
426
+ ("migrate from", 0.4),
427
+ ("upgrade to", 0.3),
428
+ ("version upgrade", 0.4),
429
+ ("framework migration", 0.8),
430
+ ("bump python", 0.6),
431
+ ("upgrade pydantic", 0.7),
432
+ ("migrate express", 0.8),
433
+ ("modernize the codebase", 0.7),
434
+ ("commonjs to esm", 1.0),
315
435
  ],
316
436
  "terms": ["migrate", "migration"],
437
+ # Note: "upgrade", "version", "modernize" omitted — overlap with phrases
438
+ "context_guards": [
439
+ "framework", "breaking", "compatibility", "deprecated",
440
+ "legacy", "esm", "commonjs",
441
+ ],
442
+ "priority": 3,
317
443
  },
444
+
445
+ # ------------------------------------------------------------------
446
+ # Spec-workflow command skills (priority 10)
447
+ # ------------------------------------------------------------------
318
448
  "spec-build": {
319
449
  "phrases": [
320
- "implement the spec",
321
- "build from spec",
322
- "start building",
323
- "spec-build",
324
- "implement this feature",
325
- "build what the spec describes",
326
- "run spec-build",
450
+ ("implement the spec", 0.9),
451
+ ("build from spec", 0.9),
452
+ ("building from spec", 0.9),
453
+ ("building from the spec", 0.9),
454
+ ("start building", 0.2),
455
+ ("spec-build", 1.0),
456
+ ("implement this feature", 0.2),
457
+ ("build what the spec describes", 1.0),
458
+ ("run spec-build", 1.0),
327
459
  ],
328
460
  "terms": ["spec-build"],
461
+ "context_guards": ["spec", "specification", ".specs"],
462
+ "priority": 10,
329
463
  },
330
464
  "spec-review": {
331
465
  "phrases": [
332
- "review the spec",
333
- "check spec adherence",
334
- "verify implementation",
335
- "spec-review",
336
- "does code match spec",
337
- "audit implementation",
338
- "run spec-review",
339
- "regression check",
466
+ ("review the spec", 0.8),
467
+ ("check spec adherence", 0.9),
468
+ ("verify implementation", 0.3),
469
+ ("spec-review", 1.0),
470
+ ("does code match spec", 0.9),
471
+ ("audit implementation", 0.4),
472
+ ("run spec-review", 1.0),
473
+ ("regression check", 0.3),
340
474
  ],
341
475
  "terms": ["spec-review"],
476
+ "context_guards": ["spec", "specification", ".specs"],
477
+ "priority": 10,
342
478
  },
343
479
  "spec-check": {
344
480
  "phrases": [
345
- "check spec health",
346
- "audit specs",
347
- "which specs are stale",
348
- "find missing specs",
349
- "review spec quality",
350
- "run spec-check",
351
- "are my specs up to date",
481
+ ("check spec health", 0.9),
482
+ ("audit specs", 0.9),
483
+ ("which specs are stale", 1.0),
484
+ ("find missing specs", 0.9),
485
+ ("review spec quality", 0.9),
486
+ ("run spec-check", 1.0),
487
+ ("are my specs up to date", 0.9),
352
488
  ],
353
489
  "terms": ["spec-check"],
490
+ "priority": 10,
354
491
  },
355
492
  "spec-init": {
356
493
  "phrases": [
357
- "initialize specs",
358
- "specs directory",
359
- "set up specs",
360
- "bootstrap specs",
361
- "start using specs",
362
- "create spec directory",
363
- "init specs",
364
- "set up .specs",
494
+ ("initialize specs", 0.9),
495
+ ("specs directory", 0.7),
496
+ ("set up specs", 0.8),
497
+ ("bootstrap specs", 0.9),
498
+ ("start using specs", 0.8),
499
+ ("create spec directory", 0.9),
500
+ ("init specs", 0.9),
501
+ ("set up .specs", 1.0),
365
502
  ],
366
503
  "terms": ["spec-init"],
504
+ "priority": 10,
367
505
  },
368
506
  "spec-new": {
369
507
  "phrases": [
370
- "create a spec",
371
- "new spec",
372
- "new feature spec",
373
- "write a spec for",
374
- "spec this feature",
375
- "start a new spec",
376
- "plan a feature",
377
- "add a spec",
508
+ ("create a spec", 0.8),
509
+ ("new spec", 0.8),
510
+ ("new feature spec", 0.9),
511
+ ("write a spec for", 0.9),
512
+ ("spec this feature", 0.9),
513
+ ("start a new spec", 0.9),
514
+ ("plan a feature", 0.2),
515
+ ("add a spec", 0.8),
378
516
  ],
379
517
  "terms": ["spec-new"],
518
+ "context_guards": ["spec", "specification", ".specs"],
519
+ "priority": 10,
380
520
  },
381
521
  "spec-refine": {
382
522
  "phrases": [
383
- "refine the spec",
384
- "review spec assumptions",
385
- "validate spec decisions",
386
- "approve the spec",
387
- "walk me through the spec",
388
- "check spec for assumptions",
389
- "iterate on the spec",
523
+ ("refine the spec", 0.9),
524
+ ("review spec assumptions", 0.9),
525
+ ("validate spec decisions", 0.9),
526
+ ("approve the spec", 0.8),
527
+ ("walk me through the spec", 0.8),
528
+ ("check spec for assumptions", 0.9),
529
+ ("iterate on the spec", 0.8),
390
530
  ],
391
531
  "terms": ["spec-refine"],
532
+ "priority": 10,
392
533
  },
393
534
  "spec-update": {
394
535
  "phrases": [
395
- "update the spec",
396
- "mark spec as implemented",
397
- "as-built update",
398
- "finish the spec",
399
- "close the spec",
400
- "update spec status",
401
- "sync spec with code",
536
+ ("update the spec", 0.8),
537
+ ("mark spec as implemented", 0.9),
538
+ ("as-built update", 0.9),
539
+ ("finish the spec", 0.7),
540
+ ("close the spec", 0.7),
541
+ ("update spec status", 0.8),
542
+ ("sync spec with code", 0.8),
402
543
  ],
403
544
  "terms": ["spec-update"],
545
+ "priority": 10,
404
546
  },
405
- "team": {
547
+ "worktree": {
406
548
  "phrases": [
407
- "spawn a team",
408
- "create a team",
409
- "team of agents",
410
- "use a swarm",
411
- "work in parallel",
412
- "coordinate multiple agents",
413
- "split this across agents",
414
- "team up",
549
+ ("create a worktree", 0.9),
550
+ ("work in a worktree", 0.8),
551
+ ("git worktree", 0.9),
552
+ ("worktree", 0.7),
553
+ ("parallel branches", 0.6),
554
+ ("isolate my work", 0.5),
555
+ ("clean up worktrees", 0.8),
556
+ ("list worktrees", 0.7),
557
+ ("set up a worktree", 0.8),
558
+ ("enter worktree", 0.8),
415
559
  ],
416
- "terms": ["TeamCreate", "SendMessage"],
560
+ "terms": ["worktree", "EnterWorktree", "WorktreeCreate"],
561
+ "priority": 5,
417
562
  },
418
563
  }
419
564
 
565
+ # ---------------------------------------------------------------------------
420
566
  # Pre-compile term patterns for whole-word matching
567
+ # ---------------------------------------------------------------------------
568
+
421
569
  _TERM_PATTERNS: dict[str, re.Pattern[str]] = {}
422
570
  for _skill, _cfg in SKILLS.items():
423
571
  for _term in _cfg["terms"]:
@@ -427,26 +575,72 @@ for _skill, _cfg in SKILLS.items():
427
575
  )
428
576
 
429
577
 
578
+ # ---------------------------------------------------------------------------
579
+ # Scoring engine
580
+ # ---------------------------------------------------------------------------
581
+
582
+
583
+ def _score_skill(cfg: dict, prompt: str, lowered: str) -> float:
584
+ """Return a confidence score for how well *prompt* matches *cfg*.
585
+
586
+ Returns 0.0 when the skill should not be suggested.
587
+ """
588
+
589
+ # 1. Negative patterns — instant disqualification
590
+ for neg in cfg.get("negative", []):
591
+ if neg in lowered:
592
+ return 0.0
593
+
594
+ # 2. Phrase scoring — take the highest matching weight
595
+ best_phrase: float = 0.0
596
+ for phrase, weight in cfg["phrases"]:
597
+ if phrase in lowered:
598
+ if weight > best_phrase:
599
+ best_phrase = weight
600
+
601
+ # 3. Term scoring — fixed weight for any whole-word match
602
+ term_score: float = 0.0
603
+ for term in cfg["terms"]:
604
+ if _TERM_PATTERNS[term].search(prompt):
605
+ term_score = TERM_WEIGHT
606
+ break
607
+
608
+ base = max(best_phrase, term_score)
609
+ if base < MIN_SCORE:
610
+ return 0.0
611
+
612
+ # 4. Context guard — low-confidence matches need confirmation
613
+ if base < CONTEXT_GUARD_THRESHOLD:
614
+ guards = cfg.get("context_guards")
615
+ if guards and not any(g in lowered for g in guards):
616
+ return 0.0
617
+
618
+ return base
619
+
620
+
430
621
  def match_skills(prompt: str) -> list[str]:
431
- """Return sorted list of skill names matching the prompt."""
622
+ """Return up to MAX_SKILLS skill names, ranked by confidence score."""
432
623
  lowered = prompt.lower()
433
- matched: list[str] = []
624
+ scored: list[tuple[str, float, int]] = []
434
625
 
435
626
  for skill, cfg in SKILLS.items():
436
- # Check phrases (substring match on lowercased prompt)
437
- if any(phrase in lowered for phrase in cfg["phrases"]):
438
- matched.append(skill)
439
- continue
627
+ score = _score_skill(cfg, prompt, lowered)
628
+ if score > 0.0:
629
+ scored.append((skill, score, cfg.get("priority", 0)))
630
+
631
+ # Sort by score descending, then priority descending for ties
632
+ scored.sort(key=lambda x: (x[1], x[2]), reverse=True)
633
+
634
+ return [name for name, _, _ in scored[:MAX_SKILLS]]
440
635
 
441
- # Check terms (whole-word regex match)
442
- if any(_TERM_PATTERNS[term].search(prompt) for term in cfg["terms"]):
443
- matched.append(skill)
444
636
 
445
- matched.sort()
446
- return matched
637
+ # ---------------------------------------------------------------------------
638
+ # Hook entry point
639
+ # ---------------------------------------------------------------------------
447
640
 
448
641
 
449
642
  def main() -> None:
643
+ """Read a hook event from stdin, score skills, and print suggestions to stdout."""
450
644
  raw = sys.stdin.read().strip()
451
645
  if not raw:
452
646
  return