claude-toolkit 0.1.9
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/CHANGELOG.md +38 -0
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/bin/cli.ts +112 -0
- package/core/agents/ct-code-reviewer.md +123 -0
- package/core/agents/ct-github-workflow.md +137 -0
- package/core/commands/ct/code-quality.md +59 -0
- package/core/commands/ct/onboard.md +84 -0
- package/core/commands/ct/pr-review.md +104 -0
- package/core/commands/ct/pr-summary.md +59 -0
- package/core/commands/ct/proto-check.md +74 -0
- package/core/commands/ct/ticket.md +71 -0
- package/core/hooks/skill-eval.js +381 -0
- package/core/hooks/skill-eval.sh +35 -0
- package/core/hooks/skill-rules.schema.json +112 -0
- package/core/skills/systematic-debugging/SKILL.md +44 -0
- package/core/skills/testing-patterns/SKILL.md +52 -0
- package/core/skills/typescript-conventions/SKILL.md +57 -0
- package/core/skills/verification-before-completion/SKILL.md +42 -0
- package/docs/README.md +49 -0
- package/docs/agents/code-reviewer.md +76 -0
- package/docs/agents/github-workflow.md +98 -0
- package/docs/best-practices/solidjs/README.md +43 -0
- package/docs/best-practices/solidjs/anti-patterns.md +166 -0
- package/docs/best-practices/solidjs/component-patterns.md +131 -0
- package/docs/best-practices/solidjs/context-and-global-state.md +131 -0
- package/docs/best-practices/solidjs/control-flow.md +124 -0
- package/docs/best-practices/solidjs/data-fetching.md +205 -0
- package/docs/best-practices/solidjs/effects-and-lifecycle.md +113 -0
- package/docs/best-practices/solidjs/performance.md +100 -0
- package/docs/best-practices/solidjs/props-patterns.md +100 -0
- package/docs/best-practices/solidjs/reactivity-model.md +104 -0
- package/docs/best-practices/solidjs/signals-and-state.md +78 -0
- package/docs/best-practices/solidjs/stores-and-nested-state.md +111 -0
- package/docs/best-practices/solidjs/typescript-integration.md +186 -0
- package/docs/best-practices/typescript/README.md +45 -0
- package/docs/best-practices/typescript/any-and-unknown.md +73 -0
- package/docs/best-practices/typescript/deriving-vs-decoupling.md +83 -0
- package/docs/best-practices/typescript/discriminated-unions.md +75 -0
- package/docs/best-practices/typescript/enums-alternatives.md +72 -0
- package/docs/best-practices/typescript/essential-patterns.md +119 -0
- package/docs/best-practices/typescript/generics-patterns.md +105 -0
- package/docs/best-practices/typescript/micro-opinions.md +87 -0
- package/docs/best-practices/typescript/runtime-validation.md +62 -0
- package/docs/best-practices/typescript/satisfies-operator.md +100 -0
- package/docs/best-practices/typescript/tsconfig-cheat-sheet.md +129 -0
- package/docs/best-practices/typescript/type-organization.md +64 -0
- package/docs/best-practices/typescript/type-vs-interface.md +80 -0
- package/docs/commands/code-quality.md +42 -0
- package/docs/commands/onboard.md +72 -0
- package/docs/commands/pr-review.md +102 -0
- package/docs/commands/pr-summary.md +50 -0
- package/docs/commands/proto-check.md +59 -0
- package/docs/commands/ticket.md +56 -0
- package/docs/skills/systematic-debugging.md +70 -0
- package/docs/skills/testing-patterns.md +89 -0
- package/docs/skills/typescript-conventions.md +137 -0
- package/docs/skills/verification-before-completion.md +91 -0
- package/docs/stacks/cloudflare-d1-kv.md +110 -0
- package/docs/stacks/i18n-typesafe.md +141 -0
- package/docs/stacks/protobuf-contracts.md +85 -0
- package/docs/stacks/rust-wasm-patterns.md +106 -0
- package/docs/stacks/solidjs-patterns.md +110 -0
- package/docs/stacks/vanilla-extract-patterns.md +115 -0
- package/package.json +58 -0
- package/src/generator.ts +317 -0
- package/src/index.ts +30 -0
- package/src/types.ts +85 -0
- package/src/utils.ts +53 -0
- package/stacks/cloudflare/skills/cloudflare-d1-kv/SKILL.md +84 -0
- package/stacks/cloudflare/stack.json +26 -0
- package/stacks/i18n-typesafe/skills/i18n-typesafe/SKILL.md +64 -0
- package/stacks/i18n-typesafe/stack.json +25 -0
- package/stacks/protobuf/skills/protobuf-contracts/SKILL.md +78 -0
- package/stacks/protobuf/stack.json +25 -0
- package/stacks/rust-wasm/skills/rust-wasm-patterns/SKILL.md +76 -0
- package/stacks/rust-wasm/stack.json +26 -0
- package/stacks/solidjs/skills/solidjs-patterns/SKILL.md +66 -0
- package/stacks/solidjs/stack.json +52 -0
- package/stacks/vanilla-extract/skills/vanilla-extract-patterns/SKILL.md +76 -0
- package/stacks/vanilla-extract/stack.json +40 -0
- package/templates/claude-toolkit.config.ts +34 -0
- package/templates/configs/biome.base.json +35 -0
- package/templates/configs/tsconfig.base.json +16 -0
- package/templates/skill-rules.base.json +98 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Review a pull request using project standards"
|
|
3
|
+
allowed-tools:
|
|
4
|
+
- Bash
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
- Grep
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Pull Request Review
|
|
11
|
+
|
|
12
|
+
Perform a checklist-based code review on the current PR or a specified PR number. Evaluate against project standards and common quality criteria.
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
1. **Get PR context.** If a PR number is provided, fetch it with `gh pr view {number} --json title,body,files,commits`. Otherwise, diff the current branch against the base branch (`main` or `master`).
|
|
17
|
+
|
|
18
|
+
2. **Read all changed files** in full to understand the complete context of the change. Do not review diffs in isolation — understand what the surrounding code does.
|
|
19
|
+
|
|
20
|
+
3. **Run the review checklist** against every changed file.
|
|
21
|
+
|
|
22
|
+
4. **Compile findings** into a structured review.
|
|
23
|
+
|
|
24
|
+
## Review Checklist
|
|
25
|
+
|
|
26
|
+
### Logic Correctness
|
|
27
|
+
- Are conditionals and branches correct? Any off-by-one errors?
|
|
28
|
+
- Are edge cases handled (empty inputs, null values, boundary conditions)?
|
|
29
|
+
- Does the control flow match the intended behavior?
|
|
30
|
+
- Are there any unreachable code paths?
|
|
31
|
+
|
|
32
|
+
### Type Safety
|
|
33
|
+
- Are types precise (no `any`, no unsafe casts, no overly broad unions)?
|
|
34
|
+
- Is type narrowing used correctly before accessing properties?
|
|
35
|
+
- Are generic constraints appropriate?
|
|
36
|
+
- Are return types explicit where they should be?
|
|
37
|
+
|
|
38
|
+
### Error Handling
|
|
39
|
+
- Are errors caught and handled meaningfully (not swallowed)?
|
|
40
|
+
- Do error messages provide enough context for debugging?
|
|
41
|
+
- Are error boundaries or fallbacks in place where needed?
|
|
42
|
+
- Is cleanup logic (finally blocks, defer, etc.) correct?
|
|
43
|
+
|
|
44
|
+
### Test Coverage
|
|
45
|
+
- Are new behaviors covered by tests?
|
|
46
|
+
- Do tests focus on behavior, not implementation details?
|
|
47
|
+
- Are edge cases and error paths tested?
|
|
48
|
+
- Are test descriptions clear and descriptive?
|
|
49
|
+
|
|
50
|
+
### Performance
|
|
51
|
+
- Any unnecessary re-renders, recomputations, or allocations?
|
|
52
|
+
- Any N+1 query patterns or missing pagination?
|
|
53
|
+
- Any missing memoization for expensive operations?
|
|
54
|
+
- Any potential memory leaks (unclosed resources, dangling listeners)?
|
|
55
|
+
|
|
56
|
+
### Security
|
|
57
|
+
- Any injection vulnerabilities (SQL, XSS, command injection)?
|
|
58
|
+
- Are user inputs validated and sanitized?
|
|
59
|
+
- Are auth checks present on protected operations?
|
|
60
|
+
- Are secrets or sensitive data properly handled (not logged, not exposed)?
|
|
61
|
+
|
|
62
|
+
### Code Style
|
|
63
|
+
- Does naming follow project conventions?
|
|
64
|
+
- Is the code readable without excessive comments?
|
|
65
|
+
- Are abstractions at the right level (not over- or under-engineered)?
|
|
66
|
+
- Is there unnecessary duplication?
|
|
67
|
+
|
|
68
|
+
## Output Format
|
|
69
|
+
|
|
70
|
+
```markdown
|
|
71
|
+
## PR Review: {title}
|
|
72
|
+
|
|
73
|
+
**Branch:** {branch} -> {base}
|
|
74
|
+
**Files changed:** {count}
|
|
75
|
+
**Verdict:** {APPROVE | REQUEST_CHANGES | COMMENT}
|
|
76
|
+
|
|
77
|
+
### Critical Issues
|
|
78
|
+
> Must be fixed before merge.
|
|
79
|
+
|
|
80
|
+
- **[CRITICAL]** {file}:{line} - {description}
|
|
81
|
+
|
|
82
|
+
### Warnings
|
|
83
|
+
> Should be addressed, but not blocking.
|
|
84
|
+
|
|
85
|
+
- **[WARNING]** {file}:{line} - {description}
|
|
86
|
+
|
|
87
|
+
### Suggestions
|
|
88
|
+
> Nice-to-have improvements.
|
|
89
|
+
|
|
90
|
+
- **[SUGGESTION]** {file}:{line} - {description}
|
|
91
|
+
|
|
92
|
+
### Positive Notes
|
|
93
|
+
- {things done well}
|
|
94
|
+
|
|
95
|
+
### Summary
|
|
96
|
+
{1-3 sentence overall assessment}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Notes
|
|
100
|
+
|
|
101
|
+
- Be specific. Reference file names and line numbers.
|
|
102
|
+
- Distinguish between opinion and objective issues.
|
|
103
|
+
- Acknowledge good patterns, not just problems.
|
|
104
|
+
- If the PR is too large to review effectively, say so and suggest splitting it.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Generate a PR summary from the current branch"
|
|
3
|
+
allowed-tools:
|
|
4
|
+
- Bash
|
|
5
|
+
- Read
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# PR Summary Generator
|
|
9
|
+
|
|
10
|
+
Generate a structured pull request summary by analyzing the diff and commit history of the current branch against its base.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
|
|
14
|
+
1. **Identify the base branch.** Check for `main` or `master`. Use `git merge-base` to find the common ancestor.
|
|
15
|
+
|
|
16
|
+
2. **Gather the diff.** Run `git diff {base}...HEAD --stat` for an overview and `git diff {base}...HEAD` for full changes.
|
|
17
|
+
|
|
18
|
+
3. **Gather commit history.** Run `git log {base}...HEAD --oneline --no-merges` to understand the progression of changes.
|
|
19
|
+
|
|
20
|
+
4. **Analyze changes** by reading modified files to understand the "why" behind each change. Group related changes together.
|
|
21
|
+
|
|
22
|
+
5. **Generate the summary.**
|
|
23
|
+
|
|
24
|
+
## Output Format
|
|
25
|
+
|
|
26
|
+
```markdown
|
|
27
|
+
## Summary
|
|
28
|
+
|
|
29
|
+
{1-3 bullet points describing the high-level purpose of this PR}
|
|
30
|
+
|
|
31
|
+
## What Changed
|
|
32
|
+
|
|
33
|
+
### {Category 1} (e.g., "Authentication", "Database", "UI")
|
|
34
|
+
- `{file}` - {what changed and why}
|
|
35
|
+
- `{file}` - {what changed and why}
|
|
36
|
+
|
|
37
|
+
### {Category 2}
|
|
38
|
+
- ...
|
|
39
|
+
|
|
40
|
+
## Impact Assessment
|
|
41
|
+
|
|
42
|
+
- **Risk level:** {low | medium | high}
|
|
43
|
+
- **Affected areas:** {list of features or systems impacted}
|
|
44
|
+
- **Breaking changes:** {none, or describe}
|
|
45
|
+
- **Migration needed:** {none, or describe}
|
|
46
|
+
|
|
47
|
+
## Testing Notes
|
|
48
|
+
|
|
49
|
+
- {How to test this change}
|
|
50
|
+
- {Key scenarios to verify}
|
|
51
|
+
- {Any manual testing required}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Notes
|
|
55
|
+
|
|
56
|
+
- Infer the "why" from commit messages, code comments, and the nature of changes. Do not just list file names.
|
|
57
|
+
- Group related file changes under meaningful categories, not one-file-per-bullet.
|
|
58
|
+
- Be honest about risk level. If you see potential issues, flag them in Testing Notes.
|
|
59
|
+
- Keep it concise. The summary should be scannable in under 60 seconds.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Validate protobuf definitions and check for breaking changes"
|
|
3
|
+
allowed-tools:
|
|
4
|
+
- Bash
|
|
5
|
+
- Read
|
|
6
|
+
- Glob
|
|
7
|
+
- Grep
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Protobuf Validation
|
|
11
|
+
|
|
12
|
+
Validate protobuf definitions for correctness, lint compliance, and backward compatibility. Regenerate types and verify the generated code compiles.
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
1. **Find proto files.** Locate all `.proto` files in the project. Identify the buf configuration (`buf.yaml`, `buf.gen.yaml`) or other protobuf tooling in use.
|
|
17
|
+
|
|
18
|
+
2. **Run lint checks.** Execute `buf lint` (or the project-configured linter) against proto definitions. Report any style or correctness violations.
|
|
19
|
+
|
|
20
|
+
3. **Check for breaking changes.** Run `buf breaking --against .git#branch=main` (or equivalent) to detect backward-incompatible changes. Common breaking changes include:
|
|
21
|
+
- Removing or renaming fields
|
|
22
|
+
- Changing field numbers
|
|
23
|
+
- Changing field types
|
|
24
|
+
- Removing services or RPCs
|
|
25
|
+
- Changing RPC signatures
|
|
26
|
+
|
|
27
|
+
4. **Regenerate types.** Run `buf generate` (or the project-configured generation command) to regenerate code from proto definitions.
|
|
28
|
+
|
|
29
|
+
5. **Verify generated code compiles.** Run the project's typecheck or build command to ensure the regenerated code is valid and integrates correctly with the rest of the codebase.
|
|
30
|
+
|
|
31
|
+
6. **Report results.**
|
|
32
|
+
|
|
33
|
+
## Output Format
|
|
34
|
+
|
|
35
|
+
```markdown
|
|
36
|
+
## Protobuf Validation Report
|
|
37
|
+
|
|
38
|
+
**Proto files found:** {count}
|
|
39
|
+
**Buf config:** {path or "not found"}
|
|
40
|
+
|
|
41
|
+
### Lint Results
|
|
42
|
+
- **Status:** {pass | fail}
|
|
43
|
+
- **Issues:** {count}
|
|
44
|
+
|
|
45
|
+
| File | Line | Rule | Message |
|
|
46
|
+
|------|------|------|---------|
|
|
47
|
+
| ... | ... | ... | ... |
|
|
48
|
+
|
|
49
|
+
### Breaking Change Check
|
|
50
|
+
- **Status:** {pass | fail}
|
|
51
|
+
- **Against:** {branch or reference}
|
|
52
|
+
- **Breaking changes found:** {count}
|
|
53
|
+
|
|
54
|
+
| File | Change | Description |
|
|
55
|
+
|------|--------|-------------|
|
|
56
|
+
| ... | ... | ... |
|
|
57
|
+
|
|
58
|
+
### Code Generation
|
|
59
|
+
- **Status:** {success | failure}
|
|
60
|
+
- **Files generated:** {count}
|
|
61
|
+
|
|
62
|
+
### Compilation Check
|
|
63
|
+
- **Status:** {pass | fail}
|
|
64
|
+
- **Errors:** {if any}
|
|
65
|
+
|
|
66
|
+
### Summary
|
|
67
|
+
{Overall status and recommended actions}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Notes
|
|
71
|
+
|
|
72
|
+
- If `buf` is not installed, check for alternative protobuf tooling (`protoc`, `grpc_tools`, etc.) before reporting failure.
|
|
73
|
+
- If no proto files are found, report that clearly rather than failing silently.
|
|
74
|
+
- Breaking changes are not always wrong — they just need to be intentional. Flag them for human review rather than treating them as errors.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Work end-to-end on a ticket or issue"
|
|
3
|
+
allowed-tools:
|
|
4
|
+
- Bash
|
|
5
|
+
- Read
|
|
6
|
+
- Write
|
|
7
|
+
- Edit
|
|
8
|
+
- Glob
|
|
9
|
+
- Grep
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Ticket Workflow
|
|
13
|
+
|
|
14
|
+
Work on a ticket or issue end-to-end: understand it, explore the codebase, plan the implementation, build it, test it, and prepare a PR.
|
|
15
|
+
|
|
16
|
+
## Workflow
|
|
17
|
+
|
|
18
|
+
### Phase 1: Understand
|
|
19
|
+
|
|
20
|
+
1. **Read the ticket.** If a GitHub issue number is provided, fetch it with `gh issue view {number}`. Otherwise, use the description provided by the user.
|
|
21
|
+
|
|
22
|
+
2. **Clarify requirements.** Identify acceptance criteria, edge cases, and constraints. If anything is ambiguous, ask before proceeding.
|
|
23
|
+
|
|
24
|
+
3. **Identify scope.** What needs to change? What should NOT change? Are there related tickets or dependencies?
|
|
25
|
+
|
|
26
|
+
### Phase 2: Explore
|
|
27
|
+
|
|
28
|
+
4. **Find relevant code.** Search for files, functions, and patterns related to the ticket. Read them fully to understand the current behavior.
|
|
29
|
+
|
|
30
|
+
5. **Understand the test landscape.** Find existing tests for the affected area. Note the testing framework, patterns, and coverage.
|
|
31
|
+
|
|
32
|
+
6. **Check for related changes.** Look at recent commits or PRs in the affected area for context.
|
|
33
|
+
|
|
34
|
+
### Phase 3: Plan
|
|
35
|
+
|
|
36
|
+
7. **Draft an implementation plan.** List the specific changes needed, in order:
|
|
37
|
+
- Files to create or modify
|
|
38
|
+
- Functions to add or change
|
|
39
|
+
- Tests to write
|
|
40
|
+
- Migrations or config changes needed
|
|
41
|
+
|
|
42
|
+
8. **Share the plan with the user** and get confirmation before proceeding. If the change is small and obvious, proceed directly.
|
|
43
|
+
|
|
44
|
+
### Phase 4: Implement
|
|
45
|
+
|
|
46
|
+
9. **Create a feature branch** following project conventions (e.g., `feat/description`, `fix/description`).
|
|
47
|
+
|
|
48
|
+
10. **Write tests first** when applicable (TDD). Write failing tests that describe the desired behavior, then implement to make them pass.
|
|
49
|
+
|
|
50
|
+
11. **Implement the changes.** Follow project conventions for code style, error handling, and naming. Make focused, atomic changes.
|
|
51
|
+
|
|
52
|
+
12. **Run quality checks.** Run lint, typecheck, and tests. Fix any issues introduced by the changes.
|
|
53
|
+
|
|
54
|
+
### Phase 5: Deliver
|
|
55
|
+
|
|
56
|
+
13. **Commit with conventional commit messages.** Group related changes into logical commits.
|
|
57
|
+
|
|
58
|
+
14. **Create a PR** with a clear title and description. Include:
|
|
59
|
+
- Summary of what changed and why
|
|
60
|
+
- How to test
|
|
61
|
+
- Any follow-up work needed
|
|
62
|
+
|
|
63
|
+
15. **Report back** with the PR link and a summary of what was done.
|
|
64
|
+
|
|
65
|
+
## Notes
|
|
66
|
+
|
|
67
|
+
- Always ask before making architectural decisions (new tables, new services, changing frameworks).
|
|
68
|
+
- Do not gold-plate. Implement what the ticket asks for, nothing more.
|
|
69
|
+
- If the ticket is too large for a single PR, propose splitting it and implement the first piece.
|
|
70
|
+
- If you discover bugs or issues outside the ticket scope, note them but do not fix them unless they block the ticket.
|
|
71
|
+
- Prefer small, reviewable PRs over large monolithic ones.
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Skill Evaluation Engine v2.0
|
|
4
|
+
*
|
|
5
|
+
* Intelligent skill activation based on:
|
|
6
|
+
* - Keywords and patterns in prompts
|
|
7
|
+
* - File paths mentioned or being edited
|
|
8
|
+
* - Directory mappings
|
|
9
|
+
* - Intent detection
|
|
10
|
+
* - Content pattern matching
|
|
11
|
+
*
|
|
12
|
+
* Outputs a structured reminder with matched skills and reasons.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require("node:fs");
|
|
16
|
+
const path = require("node:path");
|
|
17
|
+
|
|
18
|
+
// Configuration
|
|
19
|
+
const RULES_PATH = path.join(__dirname, "skill-rules.json");
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} SkillMatch
|
|
23
|
+
* @property {string} name - Skill name
|
|
24
|
+
* @property {number} score - Confidence score
|
|
25
|
+
* @property {string[]} reasons - Why this skill was matched
|
|
26
|
+
* @property {number} priority - Skill priority
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Load skill rules from JSON file
|
|
31
|
+
* @returns {Object} Parsed skill rules
|
|
32
|
+
*/
|
|
33
|
+
function loadRules() {
|
|
34
|
+
try {
|
|
35
|
+
const content = fs.readFileSync(RULES_PATH, "utf-8");
|
|
36
|
+
return JSON.parse(content);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(`Failed to load skill rules: ${error.message}`);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Extract file paths mentioned in the prompt
|
|
45
|
+
* @param {string} prompt - User's prompt text
|
|
46
|
+
* @returns {string[]} Array of detected file paths
|
|
47
|
+
*/
|
|
48
|
+
function extractFilePaths(prompt) {
|
|
49
|
+
const paths = new Set();
|
|
50
|
+
|
|
51
|
+
// Match explicit paths with extensions
|
|
52
|
+
const extensionPattern =
|
|
53
|
+
/(?:^|\s|["'`])([\w\-./]+\.(?:[tj]sx?|json|gql|ya?ml|md|sh|rs|proto|toml|css\.ts|wasm))\b/gi;
|
|
54
|
+
for (const match of prompt.matchAll(extensionPattern)) {
|
|
55
|
+
paths.add(match[1]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Match paths starting with common directories
|
|
59
|
+
const dirPattern =
|
|
60
|
+
/(?:^|\s|["'`])((?:src|app|components|worker|workers|proto|hooks|utils|services|styles|locales|tests|\.claude|\.github)\/[\w\-./]+)/gi;
|
|
61
|
+
for (const match of prompt.matchAll(dirPattern)) {
|
|
62
|
+
paths.add(match[1]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Match quoted paths
|
|
66
|
+
const quotedPattern = /["'`]([\w\-./]+\/[\w\-./]+)["'`]/g;
|
|
67
|
+
for (const match of prompt.matchAll(quotedPattern)) {
|
|
68
|
+
paths.add(match[1]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return Array.from(paths);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if a pattern matches the text
|
|
76
|
+
* @param {string} text - Text to search in
|
|
77
|
+
* @param {string} pattern - Regex pattern
|
|
78
|
+
* @param {string} flags - Regex flags
|
|
79
|
+
* @returns {boolean}
|
|
80
|
+
*/
|
|
81
|
+
function matchesPattern(text, pattern, flags = "i") {
|
|
82
|
+
try {
|
|
83
|
+
const regex = new RegExp(pattern, flags);
|
|
84
|
+
return regex.test(text);
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Check if a glob pattern matches a file path
|
|
92
|
+
* @param {string} filePath - File path to check
|
|
93
|
+
* @param {string} globPattern - Glob pattern (simplified)
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
function matchesGlob(filePath, globPattern) {
|
|
97
|
+
const regexPattern = globPattern
|
|
98
|
+
.replace(/\./g, "\\.")
|
|
99
|
+
.replace(/\*\*\//g, "<<<DOUBLESTARSLASH>>>")
|
|
100
|
+
.replace(/\*\*/g, "<<<DOUBLESTAR>>>")
|
|
101
|
+
.replace(/\*/g, "[^/]*")
|
|
102
|
+
.replace(/<<<DOUBLESTARSLASH>>>/g, "(.*\\/)?")
|
|
103
|
+
.replace(/<<<DOUBLESTAR>>>/g, ".*")
|
|
104
|
+
.replace(/\?/g, ".");
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const regex = new RegExp(`^${regexPattern}$`, "i");
|
|
108
|
+
return regex.test(filePath);
|
|
109
|
+
} catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check if file path matches any directory mapping
|
|
116
|
+
* @param {string} filePath - File path to check
|
|
117
|
+
* @param {Object} mappings - Directory to skill mappings
|
|
118
|
+
* @returns {string|null} Matched skill name or null
|
|
119
|
+
*/
|
|
120
|
+
function matchDirectoryMapping(filePath, mappings) {
|
|
121
|
+
for (const [dir, skillName] of Object.entries(mappings)) {
|
|
122
|
+
if (filePath === dir || filePath.startsWith(`${dir}/`)) {
|
|
123
|
+
return skillName;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Evaluate a single skill against the prompt and context
|
|
131
|
+
*/
|
|
132
|
+
function evaluateSkill(
|
|
133
|
+
skillName,
|
|
134
|
+
skill,
|
|
135
|
+
prompt,
|
|
136
|
+
promptLower,
|
|
137
|
+
filePaths,
|
|
138
|
+
rules,
|
|
139
|
+
) {
|
|
140
|
+
const { triggers = {}, excludePatterns = [], priority = 5 } = skill;
|
|
141
|
+
const scoring = rules.scoring;
|
|
142
|
+
|
|
143
|
+
let score = 0;
|
|
144
|
+
const reasons = [];
|
|
145
|
+
|
|
146
|
+
// Check exclude patterns first
|
|
147
|
+
for (const excludePattern of excludePatterns) {
|
|
148
|
+
if (matchesPattern(promptLower, excludePattern)) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 1. Check keywords
|
|
154
|
+
if (triggers.keywords) {
|
|
155
|
+
for (const keyword of triggers.keywords) {
|
|
156
|
+
if (promptLower.includes(keyword.toLowerCase())) {
|
|
157
|
+
score += scoring.keyword;
|
|
158
|
+
reasons.push(`keyword "${keyword}"`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 2. Check keyword patterns (regex)
|
|
164
|
+
if (triggers.keywordPatterns) {
|
|
165
|
+
for (const pattern of triggers.keywordPatterns) {
|
|
166
|
+
if (matchesPattern(promptLower, pattern)) {
|
|
167
|
+
score += scoring.keywordPattern;
|
|
168
|
+
reasons.push(`pattern /${pattern}/`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// 3. Check intent patterns
|
|
174
|
+
if (triggers.intentPatterns) {
|
|
175
|
+
for (const pattern of triggers.intentPatterns) {
|
|
176
|
+
if (matchesPattern(promptLower, pattern)) {
|
|
177
|
+
score += scoring.intentPattern;
|
|
178
|
+
reasons.push(`intent detected`);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 4. Check context patterns
|
|
185
|
+
if (triggers.contextPatterns) {
|
|
186
|
+
for (const pattern of triggers.contextPatterns) {
|
|
187
|
+
if (promptLower.includes(pattern.toLowerCase())) {
|
|
188
|
+
score += scoring.contextPattern;
|
|
189
|
+
reasons.push(`context "${pattern}"`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 5. Check file paths against path patterns
|
|
195
|
+
if (triggers.pathPatterns && filePaths.length > 0) {
|
|
196
|
+
for (const filePath of filePaths) {
|
|
197
|
+
for (const pattern of triggers.pathPatterns) {
|
|
198
|
+
if (matchesGlob(filePath, pattern)) {
|
|
199
|
+
score += scoring.pathPattern;
|
|
200
|
+
reasons.push(`path "${filePath}"`);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 6. Check directory mappings
|
|
208
|
+
if (rules.directoryMappings && filePaths.length > 0) {
|
|
209
|
+
for (const filePath of filePaths) {
|
|
210
|
+
const mappedSkill = matchDirectoryMapping(
|
|
211
|
+
filePath,
|
|
212
|
+
rules.directoryMappings,
|
|
213
|
+
);
|
|
214
|
+
if (mappedSkill === skillName) {
|
|
215
|
+
score += scoring.directoryMatch;
|
|
216
|
+
reasons.push(`directory mapping`);
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 7. Check content patterns in prompt (for code snippets)
|
|
223
|
+
if (triggers.contentPatterns) {
|
|
224
|
+
for (const pattern of triggers.contentPatterns) {
|
|
225
|
+
if (matchesPattern(prompt, pattern)) {
|
|
226
|
+
score += scoring.contentPattern;
|
|
227
|
+
reasons.push(`code pattern detected`);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (score > 0) {
|
|
234
|
+
return { name: skillName, score, reasons: [...new Set(reasons)], priority };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Get related skills that should also be suggested
|
|
242
|
+
*/
|
|
243
|
+
function getRelatedSkills(matches, skills) {
|
|
244
|
+
const matchedNames = new Set(matches.map((m) => m.name));
|
|
245
|
+
const related = new Set();
|
|
246
|
+
|
|
247
|
+
for (const match of matches) {
|
|
248
|
+
const skill = skills[match.name];
|
|
249
|
+
if (skill?.relatedSkills) {
|
|
250
|
+
for (const relatedName of skill.relatedSkills) {
|
|
251
|
+
if (!matchedNames.has(relatedName)) {
|
|
252
|
+
related.add(relatedName);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return Array.from(related);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Format confidence level based on score
|
|
263
|
+
*/
|
|
264
|
+
function formatConfidence(score, minScore) {
|
|
265
|
+
if (score >= minScore * 3) return "HIGH";
|
|
266
|
+
if (score >= minScore * 2) return "MEDIUM";
|
|
267
|
+
return "LOW";
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Main evaluation function
|
|
272
|
+
*/
|
|
273
|
+
function evaluate(prompt) {
|
|
274
|
+
const rules = loadRules();
|
|
275
|
+
const { config, skills } = rules;
|
|
276
|
+
|
|
277
|
+
const promptLower = prompt.toLowerCase();
|
|
278
|
+
const filePaths = extractFilePaths(prompt);
|
|
279
|
+
|
|
280
|
+
const matches = [];
|
|
281
|
+
for (const [name, skill] of Object.entries(skills)) {
|
|
282
|
+
const match = evaluateSkill(
|
|
283
|
+
name,
|
|
284
|
+
skill,
|
|
285
|
+
prompt,
|
|
286
|
+
promptLower,
|
|
287
|
+
filePaths,
|
|
288
|
+
rules,
|
|
289
|
+
);
|
|
290
|
+
if (match && match.score >= config.minConfidenceScore) {
|
|
291
|
+
matches.push(match);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (matches.length === 0) {
|
|
296
|
+
return "";
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
matches.sort((a, b) => {
|
|
300
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
301
|
+
return b.priority - a.priority;
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const topMatches = matches.slice(0, config.maxSkillsToShow);
|
|
305
|
+
const relatedSkills = getRelatedSkills(topMatches, skills);
|
|
306
|
+
|
|
307
|
+
let output = "<user-prompt-submit-hook>\n";
|
|
308
|
+
output += "SKILL ACTIVATION REQUIRED\n\n";
|
|
309
|
+
|
|
310
|
+
if (filePaths.length > 0) {
|
|
311
|
+
output += `Detected file paths: ${filePaths.join(", ")}\n\n`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
output += "Matched skills (ranked by relevance):\n";
|
|
315
|
+
|
|
316
|
+
for (let i = 0; i < topMatches.length; i++) {
|
|
317
|
+
const match = topMatches[i];
|
|
318
|
+
const confidence = formatConfidence(match.score, config.minConfidenceScore);
|
|
319
|
+
output += `${i + 1}. ${match.name} (${confidence} confidence)\n`;
|
|
320
|
+
if (config.showMatchReasons && match.reasons.length > 0) {
|
|
321
|
+
output += ` Matched: ${match.reasons.slice(0, 3).join(", ")}\n`;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (relatedSkills.length > 0) {
|
|
326
|
+
output += `\nRelated skills to consider: ${relatedSkills.join(", ")}\n`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
output += "\nBefore implementing, you MUST:\n";
|
|
330
|
+
output += "1. EVALUATE: State YES/NO for each skill with brief reasoning\n";
|
|
331
|
+
output += "2. ACTIVATE: Invoke the Skill tool for each YES skill\n";
|
|
332
|
+
output += "3. IMPLEMENT: Only proceed after skill activation\n";
|
|
333
|
+
output += "\nExample evaluation:\n";
|
|
334
|
+
output += `- ${topMatches[0].name}: YES - [your reasoning]\n`;
|
|
335
|
+
if (topMatches.length > 1) {
|
|
336
|
+
output += `- ${topMatches[1].name}: NO - [your reasoning]\n`;
|
|
337
|
+
}
|
|
338
|
+
output += "\nDO NOT skip this step. Invoke relevant skills NOW.\n";
|
|
339
|
+
output += "</user-prompt-submit-hook>";
|
|
340
|
+
|
|
341
|
+
return output;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Main execution
|
|
345
|
+
function main() {
|
|
346
|
+
let input = "";
|
|
347
|
+
|
|
348
|
+
process.stdin.setEncoding("utf8");
|
|
349
|
+
|
|
350
|
+
process.stdin.on("data", (chunk) => {
|
|
351
|
+
input += chunk;
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
process.stdin.on("end", () => {
|
|
355
|
+
let prompt = "";
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
const data = JSON.parse(input);
|
|
359
|
+
prompt = data.prompt || "";
|
|
360
|
+
} catch {
|
|
361
|
+
prompt = input;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (!prompt.trim()) {
|
|
365
|
+
process.exit(0);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
const output = evaluate(prompt);
|
|
370
|
+
if (output) {
|
|
371
|
+
console.log(output);
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error(`Skill evaluation failed: ${error.message}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
process.exit(0);
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
main();
|