skillstore-cli 1.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.
- package/README.md +95 -0
- package/data/bundles/devflow-complete.json +19 -0
- package/data/free-skills/devflow-agile/manifest.json +19 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/retro.md +23 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/review.md +21 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/sprint.md +30 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile/standup.md +20 -0
- package/data/free-skills/devflow-agile/plugin/commands/agile.md +35 -0
- package/data/free-skills/devflow-agile/plugin/commands/devflow.md +42 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/SKILL.md +93 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/assets/sample-output.md +182 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-architecture.md +361 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/clean-code-guide.md +207 -0
- package/data/free-skills/devflow-agile/plugin/skills/developer/references/debugging-methodology.md +191 -0
- package/data/free-skills/devflow-agile/template/agents/agile-coach.md +76 -0
- package/data/free-skills/devflow-agile/template/workflows/agile-sprint-workflow.md +81 -0
- package/data/free-skills/devflow-bootstrap/manifest.json +8 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/bootstrap/auto.md +31 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/bootstrap.md +38 -0
- package/data/free-skills/devflow-bootstrap/plugin/commands/devflow.md +20 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/SKILL.md +56 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/assets/sample-output.md +216 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/references/architecture-decisions.md +254 -0
- package/data/free-skills/devflow-bootstrap/plugin/skills/project-scaffold/references/stack-templates.md +400 -0
- package/data/free-skills/devflow-bootstrap/template/agents/bootstrap-specialist.md +56 -0
- package/data/free-skills/devflow-bootstrap/template/workflows/bootstrap-workflow.md +70 -0
- package/data/free-skills/devflow-docs/manifest.json +8 -0
- package/data/free-skills/devflow-docs/plugin/commands/devflow.md +20 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs/generate.md +17 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs/parse.md +19 -0
- package/data/free-skills/devflow-docs/plugin/commands/docs.md +26 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/SKILL.md +59 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/assets/sample-output.md +114 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/references/extraction-techniques.md +115 -0
- package/data/free-skills/devflow-docs/plugin/skills/pdf-processor/references/ocr-strategies.md +167 -0
- package/data/free-skills/devflow-docs/template/agents/docs-specialist.md +35 -0
- package/data/free-skills/devflow-docs/template/workflows/docs-workflow.md +70 -0
- package/data/free-skills/devflow-postproject/manifest.json +13 -0
- package/data/free-skills/devflow-postproject/plugin/commands/devflow.md +34 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/handover.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/retro.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject/support.md +21 -0
- package/data/free-skills/devflow-postproject/plugin/commands/postproject.md +32 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/SKILL.md +70 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/assets/sample-output.md +79 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/facilitation-techniques.md +178 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/lessons-learned-template.md +118 -0
- package/data/free-skills/devflow-postproject/plugin/skills/retrospective/references/retro-techniques.md +100 -0
- package/data/free-skills/devflow-postproject/template/agents/transition-manager.md +71 -0
- package/data/free-skills/devflow-postproject/template/workflows/transition-workflow.md +72 -0
- package/data/free-skills/devflow-presale/manifest.json +15 -0
- package/data/free-skills/devflow-presale/plugin/commands/devflow.md +47 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/analyze.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/estimate.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/price.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale/propose.md +30 -0
- package/data/free-skills/devflow-presale/plugin/commands/presale.md +42 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/SKILL.md +63 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/assets/sample-output.md +129 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/references/extraction-framework.md +140 -0
- package/data/free-skills/devflow-presale/plugin/skills/requirement-analysis/references/output-template.md +132 -0
- package/data/free-skills/devflow-presale/template/agents/presale-lead.md +83 -0
- package/data/free-skills/devflow-presale/template/agents/proposal-reviewer.md +63 -0
- package/data/free-skills/devflow-presale/template/workflows/presale-workflow.md +70 -0
- package/data/registry/categories.json +7 -0
- package/data/registry/packages.json +184 -0
- package/data/shared/framework/agents/brainstormer.md +74 -0
- package/data/shared/framework/agents/code-reviewer.md +87 -0
- package/data/shared/framework/agents/debugger.md +84 -0
- package/data/shared/framework/agents/docs-manager.md +55 -0
- package/data/shared/framework/agents/git-manager.md +59 -0
- package/data/shared/framework/agents/planner.md +68 -0
- package/data/shared/framework/agents/researcher.md +66 -0
- package/data/shared/framework/agents/tester.md +65 -0
- package/data/shared/framework/commands/cook/auto.md +27 -0
- package/data/shared/framework/commands/cook.md +45 -0
- package/data/shared/framework/commands/fix/ci.md +21 -0
- package/data/shared/framework/commands/fix/test.md +26 -0
- package/data/shared/framework/commands/fix/types.md +29 -0
- package/data/shared/framework/commands/fix.md +26 -0
- package/data/shared/framework/commands/git/cm.md +37 -0
- package/data/shared/framework/commands/git/pr.md +40 -0
- package/data/shared/framework/config/CLAUDE.md.template +26 -0
- package/data/shared/framework/config/settings.json +41 -0
- package/data/shared/framework/config/skillstore.config.json +29 -0
- package/data/shared/framework/hooks/discord-notify.sh +85 -0
- package/data/shared/framework/hooks/docs-sync.sh +53 -0
- package/data/shared/framework/hooks/modularization-hook.js +103 -0
- package/data/shared/framework/hooks/notification.js +94 -0
- package/data/shared/framework/hooks/quality-gate.js +109 -0
- package/data/shared/framework/hooks/scout-block.js +77 -0
- package/data/shared/framework/hooks/telegram-notify.sh +77 -0
- package/data/shared/framework/protocols/error-recovery.md +80 -0
- package/data/shared/framework/protocols/orchestration-protocol.md +112 -0
- package/data/shared/framework/quality/review-protocol.md +76 -0
- package/data/shared/framework/quality/verification-protocol.md +66 -0
- package/data/shared/framework/rules/development-rules.md +75 -0
- package/data/shared/framework/skills/backend-development/SKILL.md +77 -0
- package/data/shared/framework/skills/backend-development/assets/sample-output.md +175 -0
- package/data/shared/framework/skills/backend-development/references/advanced-patterns.md +180 -0
- package/data/shared/framework/skills/backend-development/references/api-design-guide.md +160 -0
- package/data/shared/framework/skills/backend-development/references/architecture-patterns.md +183 -0
- package/data/shared/framework/skills/backend-development/references/observability-resilience.md +155 -0
- package/data/shared/framework/skills/backend-development/references/troubleshooting.md +199 -0
- package/data/shared/framework/skills/codebase-analysis/SKILL.md +72 -0
- package/data/shared/framework/skills/codebase-analysis/assets/sample-output.md +263 -0
- package/data/shared/framework/skills/codebase-analysis/references/analysis-techniques.md +241 -0
- package/data/shared/framework/skills/codebase-analysis/references/dependency-mapping.md +280 -0
- package/data/shared/framework/skills/codebase-analysis/references/tech-debt-assessment.md +208 -0
- package/data/shared/framework/skills/databases/SKILL.md +72 -0
- package/data/shared/framework/skills/databases/assets/sample-output.md +212 -0
- package/data/shared/framework/skills/databases/references/advanced-data-patterns.md +259 -0
- package/data/shared/framework/skills/databases/references/query-optimization.md +214 -0
- package/data/shared/framework/skills/databases/references/schema-design.md +159 -0
- package/data/shared/framework/skills/databases/references/troubleshooting.md +214 -0
- package/data/shared/framework/skills/debugging-investigation/SKILL.md +84 -0
- package/data/shared/framework/skills/debugging-investigation/assets/sample-output.md +314 -0
- package/data/shared/framework/skills/debugging-investigation/references/systematic-debugging.md +197 -0
- package/data/shared/framework/skills/debugging-investigation/references/tool-specific-guides.md +202 -0
- package/data/shared/framework/skills/debugging-investigation/references/troubleshooting-patterns.md +196 -0
- package/data/shared/framework/skills/frontend-development/SKILL.md +67 -0
- package/data/shared/framework/skills/frontend-development/assets/sample-output.md +110 -0
- package/data/shared/framework/skills/frontend-development/references/component-patterns.md +112 -0
- package/data/shared/framework/skills/frontend-development/references/performance-guide.md +169 -0
- package/data/shared/framework/skills/frontend-development/references/routing-forms-realtime.md +374 -0
- package/data/shared/framework/skills/frontend-development/references/ssr-rsc-patterns.md +284 -0
- package/data/shared/framework/skills/frontend-development/references/troubleshooting.md +154 -0
- package/data/shared/framework/skills/mobile-development/SKILL.md +67 -0
- package/data/shared/framework/skills/mobile-development/assets/sample-output.md +382 -0
- package/data/shared/framework/skills/mobile-development/references/mobile-patterns.md +681 -0
- package/data/shared/framework/skills/mobile-development/references/mobile-performance.md +524 -0
- package/data/shared/framework/skills/mobile-development/references/troubleshooting.md +158 -0
- package/data/shared/framework/skills/security-audit/SKILL.md +83 -0
- package/data/shared/framework/skills/security-audit/assets/sample-output.md +451 -0
- package/data/shared/framework/skills/security-audit/references/owasp-checklist.md +580 -0
- package/data/shared/framework/skills/security-audit/references/secure-coding-patterns.md +433 -0
- package/data/shared/framework/skills/security-audit/references/vulnerability-remediation.md +331 -0
- package/data/shared/framework/skills/ui-generation/SKILL.md +70 -0
- package/data/shared/framework/skills/ui-generation/assets/sample-output.md +139 -0
- package/data/shared/framework/skills/ui-generation/references/accessibility-responsive.md +127 -0
- package/data/shared/framework/skills/ui-generation/references/compound-components.md +252 -0
- package/data/shared/framework/skills/ui-generation/references/generation-patterns.md +110 -0
- package/data/shared/framework/skills/ui-generation/references/storybook-design-system.md +278 -0
- package/data/shared/framework/skills/ui-generation/references/troubleshooting.md +198 -0
- package/data/shared/framework/workflows/documentation-management.md +58 -0
- package/data/shared/framework/workflows/primary-workflow.md +88 -0
- package/dist/commands/activate.d.ts +3 -0
- package/dist/commands/activate.d.ts.map +1 -0
- package/dist/commands/activate.js +34 -0
- package/dist/commands/activate.js.map +1 -0
- package/dist/commands/bundle.d.ts +3 -0
- package/dist/commands/bundle.d.ts.map +1 -0
- package/dist/commands/bundle.js +64 -0
- package/dist/commands/bundle.js.map +1 -0
- package/dist/commands/install.d.ts +3 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +99 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +37 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/search.d.ts +3 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +30 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +35 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +68 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/download/cache.d.ts +3 -0
- package/dist/download/cache.d.ts.map +1 -0
- package/dist/download/cache.js +18 -0
- package/dist/download/cache.js.map +1 -0
- package/dist/download/client.d.ts +2 -0
- package/dist/download/client.d.ts.map +1 -0
- package/dist/download/client.js +58 -0
- package/dist/download/client.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/installer/file-copier.d.ts +6 -0
- package/dist/installer/file-copier.d.ts.map +1 -0
- package/dist/installer/file-copier.js +32 -0
- package/dist/installer/file-copier.js.map +1 -0
- package/dist/installer/plugin-installer.d.ts +12 -0
- package/dist/installer/plugin-installer.d.ts.map +1 -0
- package/dist/installer/plugin-installer.js +33 -0
- package/dist/installer/plugin-installer.js.map +1 -0
- package/dist/installer/template-installer.d.ts +12 -0
- package/dist/installer/template-installer.d.ts.map +1 -0
- package/dist/installer/template-installer.js +45 -0
- package/dist/installer/template-installer.js.map +1 -0
- package/dist/license/crypto.d.ts +16 -0
- package/dist/license/crypto.d.ts.map +1 -0
- package/dist/license/crypto.js +50 -0
- package/dist/license/crypto.js.map +1 -0
- package/dist/license/license-store.d.ts +19 -0
- package/dist/license/license-store.d.ts.map +1 -0
- package/dist/license/license-store.js +99 -0
- package/dist/license/license-store.js.map +1 -0
- package/dist/license/validator.d.ts +32 -0
- package/dist/license/validator.d.ts.map +1 -0
- package/dist/license/validator.js +81 -0
- package/dist/license/validator.js.map +1 -0
- package/dist/registry/loader.d.ts +30 -0
- package/dist/registry/loader.d.ts.map +1 -0
- package/dist/registry/loader.js +22 -0
- package/dist/registry/loader.js.map +1 -0
- package/dist/registry/search-engine.d.ts +9 -0
- package/dist/registry/search-engine.d.ts.map +1 -0
- package/dist/registry/search-engine.js +30 -0
- package/dist/registry/search-engine.js.map +1 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +28 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +22 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +20 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +79 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Generate commit message from staged changes and commit
|
|
3
|
+
argument-hint: [optional context about the changes]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Your Mission
|
|
7
|
+
|
|
8
|
+
Create a commit for current staged changes.
|
|
9
|
+
|
|
10
|
+
<context>
|
|
11
|
+
$ARGUMENTS
|
|
12
|
+
</context>
|
|
13
|
+
|
|
14
|
+
## Process
|
|
15
|
+
|
|
16
|
+
1. Run `git diff --staged` to see all changes
|
|
17
|
+
2. Analyze changes — what was done and why
|
|
18
|
+
3. Generate commit message:
|
|
19
|
+
- Subject: imperative mood, < 72 chars, describes WHAT
|
|
20
|
+
- Body (if needed): explains WHY
|
|
21
|
+
4. Security check: scan staged files for secrets
|
|
22
|
+
5. Show commit message to user for approval
|
|
23
|
+
6. Commit on approval
|
|
24
|
+
|
|
25
|
+
## Commit Message Format
|
|
26
|
+
```
|
|
27
|
+
<type>: <subject>
|
|
28
|
+
|
|
29
|
+
<optional body explaining why>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Types: feat, fix, refactor, test, docs, chore, style, perf
|
|
33
|
+
|
|
34
|
+
## Rules
|
|
35
|
+
- ALWAYS show message and ask before committing
|
|
36
|
+
- NEVER commit if secrets detected
|
|
37
|
+
- If no staged changes: report and suggest `git add`
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Create a pull request with auto-generated title and description
|
|
3
|
+
argument-hint: [optional PR context]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Your Mission
|
|
7
|
+
|
|
8
|
+
Create a PR for current branch.
|
|
9
|
+
|
|
10
|
+
<context>
|
|
11
|
+
$ARGUMENTS
|
|
12
|
+
</context>
|
|
13
|
+
|
|
14
|
+
## Process
|
|
15
|
+
|
|
16
|
+
1. Check current branch and base branch
|
|
17
|
+
2. Run `git log base..HEAD` to see all commits
|
|
18
|
+
3. Run `git diff base...HEAD` to see all changes
|
|
19
|
+
4. Generate PR:
|
|
20
|
+
- Title: < 70 chars, describes the feature/fix
|
|
21
|
+
- Body: Summary (bullets), Changes (list), Test plan (checklist)
|
|
22
|
+
5. Show to user for approval
|
|
23
|
+
6. Create PR using `gh pr create`
|
|
24
|
+
|
|
25
|
+
## PR Body Format
|
|
26
|
+
```markdown
|
|
27
|
+
## Summary
|
|
28
|
+
- [1-3 bullet points]
|
|
29
|
+
|
|
30
|
+
## Changes
|
|
31
|
+
- [File/component level changes]
|
|
32
|
+
|
|
33
|
+
## Test Plan
|
|
34
|
+
- [ ] [Test scenarios]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Rules
|
|
38
|
+
- ALWAYS show PR content before creating
|
|
39
|
+
- Push branch to remote first if needed
|
|
40
|
+
- Include test plan even for small changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Project Configuration — DevFlow by SkillStore
|
|
2
|
+
|
|
3
|
+
## Workflows (MUST READ & FOLLOW)
|
|
4
|
+
|
|
5
|
+
- `.claude/workflows/primary-workflow.md` — Mandatory 5-step dev process
|
|
6
|
+
- `.claude/workflows/documentation-management.md` — Docs update triggers
|
|
7
|
+
- `.claude/rules/development-rules.md` — Code quality standards
|
|
8
|
+
- `.claude/protocols/orchestration-protocol.md` — Agent coordination
|
|
9
|
+
- `.claude/protocols/error-recovery.md` — Error handling & retry policy
|
|
10
|
+
|
|
11
|
+
## Quality
|
|
12
|
+
|
|
13
|
+
- `.claude/quality/verification-protocol.md` — Completion verification
|
|
14
|
+
- `.claude/quality/review-protocol.md` — Code review standards
|
|
15
|
+
|
|
16
|
+
## Configuration
|
|
17
|
+
|
|
18
|
+
- `.claude/config/skillstore.config.json` — Thresholds, models, strictness
|
|
19
|
+
|
|
20
|
+
## Key Rules
|
|
21
|
+
|
|
22
|
+
1. **Plan before implement** — Use `planner` agent for non-trivial tasks
|
|
23
|
+
2. **Test before claim done** — Use `tester` agent to verify
|
|
24
|
+
3. **Review before merge** — Use `code-reviewer` agent for quality gate
|
|
25
|
+
4. **Fix root cause, not symptoms** — Use `debugger` agent when stuck
|
|
26
|
+
5. **Keep docs current** — Use `docs-manager` agent when code changes significantly
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Bash",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/scout-block.js"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"PostToolUse": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "Write|Edit",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "node \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/modularization-hook.js"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"Stop": [
|
|
26
|
+
{
|
|
27
|
+
"matcher": "",
|
|
28
|
+
"hooks": [
|
|
29
|
+
{
|
|
30
|
+
"type": "command",
|
|
31
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/discord-notify.sh"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "command",
|
|
35
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/telegram-notify.sh"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"models": {
|
|
3
|
+
"primary": "sonnet",
|
|
4
|
+
"lightweight": "haiku",
|
|
5
|
+
"fallback": "haiku",
|
|
6
|
+
"external": null
|
|
7
|
+
},
|
|
8
|
+
"thresholds": {
|
|
9
|
+
"maxFileLines": 200,
|
|
10
|
+
"maxRetries": 3,
|
|
11
|
+
"agentTimeoutMs": 180000,
|
|
12
|
+
"commitMessageMaxChars": 72,
|
|
13
|
+
"maxAgentReportLines": 150
|
|
14
|
+
},
|
|
15
|
+
"strictness": "balanced",
|
|
16
|
+
"team": {
|
|
17
|
+
"mode": "individual",
|
|
18
|
+
"roles": []
|
|
19
|
+
},
|
|
20
|
+
"hooks": {
|
|
21
|
+
"qualityGate": true,
|
|
22
|
+
"docsSync": true,
|
|
23
|
+
"notifications": {
|
|
24
|
+
"enabled": false,
|
|
25
|
+
"discord": null,
|
|
26
|
+
"telegram": null
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Discord Notification Hook (Stop/SubagentStop)
|
|
4
|
+
#
|
|
5
|
+
# Sends rich embed notifications to Discord when sessions complete.
|
|
6
|
+
# Requires: DISCORD_WEBHOOK_URL in environment or .claude/.env
|
|
7
|
+
#
|
|
8
|
+
# Non-blocking: always exits 0.
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
# Load environment
|
|
13
|
+
load_env() {
|
|
14
|
+
local env_files=(
|
|
15
|
+
"$CLAUDE_PROJECT_DIR/.claude/hooks/.env"
|
|
16
|
+
"$CLAUDE_PROJECT_DIR/.claude/.env"
|
|
17
|
+
)
|
|
18
|
+
for f in "${env_files[@]}"; do
|
|
19
|
+
if [ -f "$f" ]; then
|
|
20
|
+
set -a
|
|
21
|
+
source "$f"
|
|
22
|
+
set +a
|
|
23
|
+
fi
|
|
24
|
+
done
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
load_env
|
|
28
|
+
|
|
29
|
+
# Check webhook URL
|
|
30
|
+
if [ -z "$DISCORD_WEBHOOK_URL" ]; then
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# Read stdin
|
|
35
|
+
INPUT=$(cat /dev/stdin)
|
|
36
|
+
|
|
37
|
+
# Parse event data
|
|
38
|
+
EVENT_TYPE=$(echo "$INPUT" | node -e "
|
|
39
|
+
const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8'));
|
|
40
|
+
console.log(d.event || 'unknown');
|
|
41
|
+
" 2>/dev/null || echo "unknown")
|
|
42
|
+
|
|
43
|
+
TIMESTAMP=$(TZ='Asia/Ho_Chi_Minh' date '+%Y-%m-%d %H:%M:%S')
|
|
44
|
+
SESSION_ID=$(echo "$INPUT" | node -e "
|
|
45
|
+
const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8'));
|
|
46
|
+
console.log(d.session_id || 'N/A');
|
|
47
|
+
" 2>/dev/null || echo "N/A")
|
|
48
|
+
|
|
49
|
+
# Build embed based on event type
|
|
50
|
+
case "$EVENT_TYPE" in
|
|
51
|
+
"Stop")
|
|
52
|
+
TITLE="Session Completed"
|
|
53
|
+
COLOR=65280 # Green
|
|
54
|
+
;;
|
|
55
|
+
"SubagentStop")
|
|
56
|
+
TITLE="Agent Completed"
|
|
57
|
+
COLOR=3447003 # Blue
|
|
58
|
+
;;
|
|
59
|
+
*)
|
|
60
|
+
TITLE="Event: $EVENT_TYPE"
|
|
61
|
+
COLOR=16776960 # Yellow
|
|
62
|
+
;;
|
|
63
|
+
esac
|
|
64
|
+
|
|
65
|
+
DESCRIPTION="**Time:** $TIMESTAMP\n**Session:** $SESSION_ID\n**Event:** $EVENT_TYPE"
|
|
66
|
+
|
|
67
|
+
# Send Discord embed
|
|
68
|
+
PAYLOAD=$(cat <<ENDJSON
|
|
69
|
+
{
|
|
70
|
+
"embeds": [{
|
|
71
|
+
"title": "$TITLE",
|
|
72
|
+
"description": "$DESCRIPTION",
|
|
73
|
+
"color": $COLOR,
|
|
74
|
+
"footer": {"text": "SkillStore DevFlow"},
|
|
75
|
+
"timestamp": "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
|
76
|
+
}]
|
|
77
|
+
}
|
|
78
|
+
ENDJSON
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
curl -s -H "Content-Type: application/json" \
|
|
82
|
+
-d "$PAYLOAD" \
|
|
83
|
+
"$DISCORD_WEBHOOK_URL" > /dev/null 2>&1 || true
|
|
84
|
+
|
|
85
|
+
exit 0
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Docs Sync Hook (PostToolUse — Bash with git commit)
|
|
4
|
+
#
|
|
5
|
+
# After git commits, checks if documentation might be stale.
|
|
6
|
+
# Compares code change timestamps with docs/ timestamps.
|
|
7
|
+
# Non-blocking: always exits 0.
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
# Read stdin for tool input
|
|
12
|
+
INPUT=$(cat /dev/stdin)
|
|
13
|
+
TOOL_NAME=$(echo "$INPUT" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')); console.log(d.tool_name || '')")
|
|
14
|
+
COMMAND=$(echo "$INPUT" | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf-8')); console.log(d.tool_params?.command || '')")
|
|
15
|
+
|
|
16
|
+
# Only trigger on Bash tool with git commit
|
|
17
|
+
if [ "$TOOL_NAME" != "Bash" ]; then
|
|
18
|
+
exit 0
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
if ! echo "$COMMAND" | grep -q "git commit"; then
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Check if docs/ directory exists
|
|
26
|
+
DOCS_DIR="./docs"
|
|
27
|
+
if [ ! -d "$DOCS_DIR" ]; then
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Find recently changed source files (last commit)
|
|
32
|
+
CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || echo "")
|
|
33
|
+
if [ -z "$CHANGED_FILES" ]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Check if any source files changed but docs didn't
|
|
38
|
+
SOURCE_CHANGED=false
|
|
39
|
+
DOCS_CHANGED=false
|
|
40
|
+
|
|
41
|
+
for f in $CHANGED_FILES; do
|
|
42
|
+
case "$f" in
|
|
43
|
+
docs/*) DOCS_CHANGED=true ;;
|
|
44
|
+
*.ts|*.js|*.tsx|*.jsx|*.py|*.go|*.rs|*.java) SOURCE_CHANGED=true ;;
|
|
45
|
+
esac
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
if [ "$SOURCE_CHANGED" = true ] && [ "$DOCS_CHANGED" = false ]; then
|
|
49
|
+
# Output warning as additional context
|
|
50
|
+
echo "{\"additionalContext\": \"Docs Sync: Source files changed but docs/ was not updated. Consider updating documentation.\"}"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
exit 0
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Modularization Hook (PostToolUse — Write/Edit)
|
|
5
|
+
*
|
|
6
|
+
* After file write/edit, checks if file exceeds LOC threshold.
|
|
7
|
+
* Provides actionable suggestions for splitting files.
|
|
8
|
+
* Non-blocking: always exits 0.
|
|
9
|
+
*
|
|
10
|
+
* Reads threshold from skillstore.config.json if available.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { readFileSync, existsSync } from 'fs';
|
|
14
|
+
import { join, relative, basename } from 'path';
|
|
15
|
+
|
|
16
|
+
function loadConfig() {
|
|
17
|
+
const configPaths = [
|
|
18
|
+
join(process.cwd(), '.claude', 'config', 'skillstore.config.json'),
|
|
19
|
+
join(process.cwd(), 'skillstore.config.json'),
|
|
20
|
+
];
|
|
21
|
+
for (const p of configPaths) {
|
|
22
|
+
if (existsSync(p)) {
|
|
23
|
+
try { return JSON.parse(readFileSync(p, 'utf-8')); } catch { /* ignore */ }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { thresholds: { maxFileLines: 200 } };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function analyzeFile(filePath, content) {
|
|
30
|
+
const lines = content.split('\n');
|
|
31
|
+
const lineCount = lines.length;
|
|
32
|
+
const config = loadConfig();
|
|
33
|
+
const threshold = config.thresholds?.maxFileLines || 200;
|
|
34
|
+
|
|
35
|
+
if (lineCount <= threshold) return null;
|
|
36
|
+
|
|
37
|
+
// Count functions/classes/exports for split suggestions
|
|
38
|
+
const functionCount = (content.match(/(?:function\s+\w+|const\s+\w+\s*=\s*(?:async\s*)?\(|(?:export\s+)?class\s+\w+)/g) || []).length;
|
|
39
|
+
const exportCount = (content.match(/export\s+(?:default\s+)?(?:function|class|const|interface|type)/g) || []).length;
|
|
40
|
+
|
|
41
|
+
const suggestions = [
|
|
42
|
+
`File ${relative(process.cwd(), filePath)} has ${lineCount} LOC (threshold: ${threshold}).`,
|
|
43
|
+
'Consider modularization:',
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
if (functionCount > 5) {
|
|
47
|
+
suggestions.push(`- ${functionCount} functions detected. Group related functions into separate modules.`);
|
|
48
|
+
}
|
|
49
|
+
if (exportCount > 3) {
|
|
50
|
+
suggestions.push(`- ${exportCount} exports detected. Consider splitting by export responsibility.`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
suggestions.push(
|
|
54
|
+
'- Use kebab-case naming with descriptive names (e.g., user-auth-service.ts)',
|
|
55
|
+
'- Ensure file names are self-documenting for LLM Grep/Glob tools',
|
|
56
|
+
'- Extract utility functions, types, and constants into separate files',
|
|
57
|
+
'- After modularization, continue with main task'
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return suggestions;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function main() {
|
|
64
|
+
try {
|
|
65
|
+
const input = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));
|
|
66
|
+
const toolName = input.tool_name;
|
|
67
|
+
|
|
68
|
+
if (!['Write', 'Edit'].includes(toolName)) {
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const filePath = input.tool_params?.file_path || input.tool_params?.path;
|
|
73
|
+
if (!filePath || !existsSync(filePath)) {
|
|
74
|
+
process.exit(0);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Skip non-text files
|
|
78
|
+
if (/\.(png|jpg|jpeg|gif|svg|ico|woff|ttf|eot|pdf|zip|tar|gz|lock)$/i.test(filePath)) {
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Skip markdown/config files
|
|
83
|
+
if (/\.(md|json|yaml|yml|toml|ini|cfg)$/i.test(filePath)) {
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
88
|
+
const suggestions = analyzeFile(filePath, content);
|
|
89
|
+
|
|
90
|
+
if (suggestions) {
|
|
91
|
+
const output = {
|
|
92
|
+
additionalContext: suggestions.join('\n')
|
|
93
|
+
};
|
|
94
|
+
process.stdout.write(JSON.stringify(output));
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
// Non-blocking: always exit 0
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
main();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Notification Hook (Stop/SubagentStop)
|
|
5
|
+
*
|
|
6
|
+
* Sends notifications to Discord/Telegram when sessions complete.
|
|
7
|
+
* Reads config from skillstore.config.json.
|
|
8
|
+
* Non-blocking: always exits 0.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, existsSync } from 'fs';
|
|
12
|
+
import { join } from 'path';
|
|
13
|
+
|
|
14
|
+
function loadConfig() {
|
|
15
|
+
const configPaths = [
|
|
16
|
+
join(process.cwd(), '.claude', 'config', 'skillstore.config.json'),
|
|
17
|
+
join(process.cwd(), 'skillstore.config.json'),
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
for (const p of configPaths) {
|
|
21
|
+
if (existsSync(p)) {
|
|
22
|
+
try { return JSON.parse(readFileSync(p, 'utf-8')); } catch { /* ignore */ }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function sendDiscord(webhookUrl, message) {
|
|
29
|
+
const payload = {
|
|
30
|
+
embeds: [{
|
|
31
|
+
title: message.title,
|
|
32
|
+
description: message.body,
|
|
33
|
+
color: message.success ? 0x00ff00 : 0xff0000,
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
footer: { text: 'SkillStore DevFlow' }
|
|
36
|
+
}]
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
await fetch(webhookUrl, {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: { 'Content-Type': 'application/json' },
|
|
42
|
+
body: JSON.stringify(payload),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function sendTelegram(botToken, chatId, message) {
|
|
47
|
+
const text = `*${message.title}*\n\n${message.body}`;
|
|
48
|
+
const url = `https://api.telegram.org/bot${botToken}/sendMessage`;
|
|
49
|
+
|
|
50
|
+
await fetch(url, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
|
53
|
+
body: JSON.stringify({
|
|
54
|
+
chat_id: chatId,
|
|
55
|
+
text,
|
|
56
|
+
parse_mode: 'Markdown',
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function main() {
|
|
62
|
+
const config = loadConfig();
|
|
63
|
+
if (!config?.hooks?.notifications?.enabled) {
|
|
64
|
+
process.exit(0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const input = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));
|
|
68
|
+
const eventType = input.event || 'unknown';
|
|
69
|
+
|
|
70
|
+
const message = {
|
|
71
|
+
title: `Session ${eventType === 'Stop' ? 'Completed' : 'Agent Completed'}`,
|
|
72
|
+
body: [
|
|
73
|
+
`Event: ${eventType}`,
|
|
74
|
+
`Time: ${new Date().toLocaleString('vi-VN', { timeZone: 'Asia/Ho_Chi_Minh' })}`,
|
|
75
|
+
`Tools used: ${input.tool_uses || 'N/A'}`,
|
|
76
|
+
].join('\n'),
|
|
77
|
+
success: true,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const { discord, telegram } = config.hooks.notifications;
|
|
81
|
+
|
|
82
|
+
const promises = [];
|
|
83
|
+
if (discord) {
|
|
84
|
+
promises.push(sendDiscord(discord, message).catch(() => {}));
|
|
85
|
+
}
|
|
86
|
+
if (telegram?.botToken && telegram?.chatId) {
|
|
87
|
+
promises.push(sendTelegram(telegram.botToken, telegram.chatId, message).catch(() => {}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await Promise.allSettled(promises);
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
main().catch(() => process.exit(0));
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Quality Gate Hook (PostToolUse — Write/Edit)
|
|
5
|
+
*
|
|
6
|
+
* Non-blocking hook that checks file quality after write/edit operations.
|
|
7
|
+
* Reads thresholds from skillstore.config.json if available.
|
|
8
|
+
*
|
|
9
|
+
* Checks:
|
|
10
|
+
* - File size exceeds maxFileLines threshold
|
|
11
|
+
* - TODO/FIXME/HACK comments detected
|
|
12
|
+
* - Potential secrets patterns
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readFileSync, existsSync } from 'fs';
|
|
16
|
+
import { join } from 'path';
|
|
17
|
+
|
|
18
|
+
// Load config
|
|
19
|
+
function loadConfig() {
|
|
20
|
+
const configPaths = [
|
|
21
|
+
join(process.cwd(), '.claude', 'config', 'skillstore.config.json'),
|
|
22
|
+
join(process.cwd(), 'skillstore.config.json'),
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
for (const p of configPaths) {
|
|
26
|
+
if (existsSync(p)) {
|
|
27
|
+
try { return JSON.parse(readFileSync(p, 'utf-8')); } catch { /* ignore */ }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { thresholds: { maxFileLines: 200 }, hooks: { qualityGate: true } };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function main() {
|
|
35
|
+
const input = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));
|
|
36
|
+
const toolName = input.tool_name;
|
|
37
|
+
|
|
38
|
+
// Only check Write and Edit tools
|
|
39
|
+
if (!['Write', 'Edit'].includes(toolName)) {
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const config = loadConfig();
|
|
44
|
+
if (!config.hooks?.qualityGate) {
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const filePath = input.tool_params?.file_path || input.tool_params?.path;
|
|
49
|
+
if (!filePath || !existsSync(filePath)) {
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Skip non-text files
|
|
54
|
+
if (/\.(png|jpg|jpeg|gif|svg|ico|woff|ttf|eot|pdf|zip|tar|gz)$/i.test(filePath)) {
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
59
|
+
const lines = content.split('\n');
|
|
60
|
+
const warnings = [];
|
|
61
|
+
|
|
62
|
+
// Check file size
|
|
63
|
+
const maxLines = config.thresholds?.maxFileLines || 200;
|
|
64
|
+
if (lines.length > maxLines) {
|
|
65
|
+
warnings.push(
|
|
66
|
+
`File has ${lines.length} lines (threshold: ${maxLines}). Consider splitting into smaller modules.`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check for TODO/FIXME/HACK
|
|
71
|
+
const todoPattern = /\b(TODO|FIXME|HACK|XXX|TEMP)\b/;
|
|
72
|
+
const todoLines = lines
|
|
73
|
+
.map((line, i) => ({ line: i + 1, match: line.match(todoPattern) }))
|
|
74
|
+
.filter(l => l.match);
|
|
75
|
+
|
|
76
|
+
if (todoLines.length > 0) {
|
|
77
|
+
warnings.push(
|
|
78
|
+
`Found ${todoLines.length} TODO/FIXME/HACK comment(s): lines ${todoLines.map(l => l.line).join(', ')}`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Check for potential secrets (simple patterns)
|
|
83
|
+
const secretPatterns = [
|
|
84
|
+
/(?:password|passwd|secret|api_key|apikey|access_token)\s*[:=]\s*['"][^'"]{8,}['"]/i,
|
|
85
|
+
/(?:AKIA|AIza)[A-Za-z0-9]{16,}/,
|
|
86
|
+
/-----BEGIN (?:RSA )?PRIVATE KEY-----/,
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const hasSecrets = lines.some(line =>
|
|
90
|
+
secretPatterns.some(p => p.test(line))
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (hasSecrets) {
|
|
94
|
+
warnings.push('Potential secret/credential detected. Verify before committing.');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Output warnings as additional context (non-blocking)
|
|
98
|
+
if (warnings.length > 0) {
|
|
99
|
+
const output = {
|
|
100
|
+
additionalContext: `Quality Gate: ${warnings.join(' | ')}`
|
|
101
|
+
};
|
|
102
|
+
process.stdout.write(JSON.stringify(output));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Always exit 0 — non-blocking
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
main();
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Scout-Block Hook (PreToolUse — Bash)
|
|
5
|
+
*
|
|
6
|
+
* Blocks bash commands that access heavy/dangerous directories.
|
|
7
|
+
* Prevents: node_modules, .git, dist, build, __pycache__, .next, .nuxt
|
|
8
|
+
*
|
|
9
|
+
* Exit codes:
|
|
10
|
+
* 0 = allowed
|
|
11
|
+
* 2 = blocked
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync } from 'fs';
|
|
15
|
+
|
|
16
|
+
const BLOCKED_PATTERNS = [
|
|
17
|
+
'node_modules',
|
|
18
|
+
'__pycache__',
|
|
19
|
+
'\\.git/',
|
|
20
|
+
'\\.git$',
|
|
21
|
+
'dist/',
|
|
22
|
+
'build/',
|
|
23
|
+
'\\.next/',
|
|
24
|
+
'\\.nuxt/',
|
|
25
|
+
'\\.venv/',
|
|
26
|
+
'vendor/',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const BLOCKED_REGEX = new RegExp(BLOCKED_PATTERNS.join('|'));
|
|
30
|
+
|
|
31
|
+
// Whitelist: commands that are always safe
|
|
32
|
+
const SAFE_COMMANDS = [
|
|
33
|
+
/^git\s/, // git commands are fine
|
|
34
|
+
/^pnpm\s/, // package manager
|
|
35
|
+
/^npm\s/, // package manager
|
|
36
|
+
/^yarn\s/, // package manager
|
|
37
|
+
/^node\s/, // node execution
|
|
38
|
+
/^npx\s/, // npx
|
|
39
|
+
/^which\s/, // which
|
|
40
|
+
/^echo\s/, // echo
|
|
41
|
+
/^pwd$/, // pwd
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
function main() {
|
|
45
|
+
try {
|
|
46
|
+
const input = JSON.parse(readFileSync('/dev/stdin', 'utf-8'));
|
|
47
|
+
const toolName = input.tool_name;
|
|
48
|
+
|
|
49
|
+
if (toolName !== 'Bash') {
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const command = input.tool_params?.command || '';
|
|
54
|
+
|
|
55
|
+
// Check if command is in safe list
|
|
56
|
+
if (SAFE_COMMANDS.some(pattern => pattern.test(command.trim()))) {
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Check for blocked patterns in command arguments
|
|
61
|
+
if (BLOCKED_REGEX.test(command)) {
|
|
62
|
+
const output = {
|
|
63
|
+
decision: 'block',
|
|
64
|
+
reason: `Blocked: command accesses restricted directory. Patterns blocked: ${BLOCKED_PATTERNS.join(', ')}`
|
|
65
|
+
};
|
|
66
|
+
process.stderr.write(JSON.stringify(output));
|
|
67
|
+
process.exit(2);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
process.exit(0);
|
|
71
|
+
} catch {
|
|
72
|
+
// On error, allow the command (fail open)
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
main();
|