@silverassist/agents-toolkit 2.2.0 → 2.3.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 +35 -0
- package/bin/cli.js +53 -1
- package/package.json +1 -1
- package/src/index.js +3 -1
- package/templates/shared/hooks/lint-format.json +11 -0
- package/templates/shared/hooks/scripts/lint-format.sh +64 -0
- package/templates/shared/hooks/scripts/validate-tsx.sh +77 -0
- package/templates/shared/hooks/validate-tsx.json +11 -0
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ Reusable AI agent prompts for development workflows — supports **GitHub Copilo
|
|
|
14
14
|
- ✅ **Global Install**: Install once for all projects with `--global`
|
|
15
15
|
- ✅ **Modular Partials**: Reusable prompt fragments
|
|
16
16
|
- ✅ **Customizable**: Easy to extend and modify
|
|
17
|
+
- ✅ **PostToolUse Hooks**: Automated validation and formatting after Copilot edits
|
|
17
18
|
- ✅ **CLI Tool**: Quick installation in any project
|
|
18
19
|
|
|
19
20
|
## Installation
|
|
@@ -257,6 +258,7 @@ npx @silverassist/agents-toolkit@latest install [options]
|
|
|
257
258
|
| `--instructions-only` | Only install instructions and instructions file |
|
|
258
259
|
| `--partials-only` | Only install partials |
|
|
259
260
|
| `--skills-only` | Only install skills |
|
|
261
|
+
| `--hooks-only` | Only install hooks (PostToolUse validation scripts) |
|
|
260
262
|
| `--dry-run` | Show what would be installed without making changes |
|
|
261
263
|
|
|
262
264
|
**Examples:**
|
|
@@ -384,6 +386,39 @@ Specialized knowledge guides for domain-specific patterns:
|
|
|
384
386
|
|
|
385
387
|
**Codex** — skills are stored in `.github/skills/` and can be referenced from `AGENTS.md` and task context.
|
|
386
388
|
|
|
389
|
+
## Hooks
|
|
390
|
+
|
|
391
|
+
PostToolUse hooks run automatically after GitHub Copilot edits files. They provide real-time validation and formatting without manual intervention.
|
|
392
|
+
|
|
393
|
+
| Hook | Trigger | Description |
|
|
394
|
+
|------|---------|-------------|
|
|
395
|
+
| `validate-tsx` | `*.tsx` in `components/` | Validates kebab-case folders, `index.tsx` naming, default export, and Props interface |
|
|
396
|
+
| `lint-format` | `*.ts, *.tsx, *.js, *.jsx, *.css` | Runs ESLint `--fix` and Prettier `--write` on the modified file |
|
|
397
|
+
|
|
398
|
+
Hooks are installed to:
|
|
399
|
+
|
|
400
|
+
- **Project** (default): `.github/hooks/`
|
|
401
|
+
- **Global** (`--global`): `~/.copilot/hooks/`
|
|
402
|
+
|
|
403
|
+
```
|
|
404
|
+
.github/hooks/ # or ~/.copilot/hooks/ for global
|
|
405
|
+
├── validate-tsx.json # Hook config (PostToolUse trigger)
|
|
406
|
+
├── lint-format.json # Hook config (PostToolUse trigger)
|
|
407
|
+
└── scripts/
|
|
408
|
+
├── validate-tsx.sh # Validation logic (exit 1 = warning)
|
|
409
|
+
└── lint-format.sh # Auto-fix logic (always exit 0)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Install only hooks:
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
# Project-level (hooks apply to this project only)
|
|
416
|
+
npx @silverassist/agents-toolkit@latest install --hooks-only
|
|
417
|
+
|
|
418
|
+
# Global (hooks apply to all Copilot sessions)
|
|
419
|
+
npx @silverassist/agents-toolkit@latest install --hooks-only --global
|
|
420
|
+
```
|
|
421
|
+
|
|
387
422
|
## Agent Instructions Files
|
|
388
423
|
|
|
389
424
|
### AGENTS.md (Copilot/Codex Agent)
|
package/bin/cli.js
CHANGED
|
@@ -286,14 +286,16 @@ function getInstallScope(options = {}) {
|
|
|
286
286
|
partialsOnly = false,
|
|
287
287
|
skillsOnly = false,
|
|
288
288
|
instructionsOnly = false,
|
|
289
|
+
hooksOnly = false,
|
|
289
290
|
} = options;
|
|
290
291
|
|
|
291
|
-
const hasSpecificFlag = promptsOnly || partialsOnly || skillsOnly || instructionsOnly;
|
|
292
|
+
const hasSpecificFlag = promptsOnly || partialsOnly || skillsOnly || instructionsOnly || hooksOnly;
|
|
292
293
|
|
|
293
294
|
return {
|
|
294
295
|
shouldInstallPrompts: !hasSpecificFlag || promptsOnly || partialsOnly,
|
|
295
296
|
shouldInstallInstructions: !hasSpecificFlag || instructionsOnly,
|
|
296
297
|
shouldInstallSkills: !hasSpecificFlag || skillsOnly,
|
|
298
|
+
shouldInstallHooks: !hasSpecificFlag || hooksOnly,
|
|
297
299
|
};
|
|
298
300
|
}
|
|
299
301
|
|
|
@@ -301,6 +303,37 @@ function getChangeCount(result, dryRun) {
|
|
|
301
303
|
return dryRun ? result.planned : result.written;
|
|
302
304
|
}
|
|
303
305
|
|
|
306
|
+
function installHooks({ targetDir, force = false, dryRun = false }) {
|
|
307
|
+
const hooksSrc = path.join(TEMPLATES_DIR, 'shared', 'hooks');
|
|
308
|
+
const hooksDest = path.join(targetDir, 'hooks');
|
|
309
|
+
|
|
310
|
+
if (!fs.existsSync(hooksSrc)) {
|
|
311
|
+
warn('No hooks templates found — skipping');
|
|
312
|
+
return { written: 0, skipped: 0, planned: 0 };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Copy hook JSON configs and scripts
|
|
316
|
+
const result = copyDir(hooksSrc, hooksDest, { force, dryRun });
|
|
317
|
+
|
|
318
|
+
// Make scripts executable (non-dry-run only)
|
|
319
|
+
if (!dryRun) {
|
|
320
|
+
const scriptsDir = path.join(hooksDest, 'scripts');
|
|
321
|
+
if (fs.existsSync(scriptsDir)) {
|
|
322
|
+
const scripts = fs.readdirSync(scriptsDir).filter(f => f.endsWith('.sh'));
|
|
323
|
+
for (const script of scripts) {
|
|
324
|
+
const scriptPath = path.join(scriptsDir, script);
|
|
325
|
+
fs.chmodSync(scriptPath, 0o755);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!dryRun && result.written > 0) {
|
|
331
|
+
success(`Installed ${result.written} hook files`);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
|
|
304
337
|
function ensureConfigFile({ dryRun = false, global = false } = {}) {
|
|
305
338
|
const configDir = global ? getHomeDir() : process.cwd();
|
|
306
339
|
const configPath = path.join(configDir, '.agents-toolkit.json');
|
|
@@ -495,6 +528,12 @@ function installGitBasedTarget(options = {}, target = 'copilot') {
|
|
|
495
528
|
}
|
|
496
529
|
}
|
|
497
530
|
|
|
531
|
+
if (scope.shouldInstallHooks) {
|
|
532
|
+
info('Installing hooks...');
|
|
533
|
+
const hooksResult = installHooks({ targetDir, force, dryRun });
|
|
534
|
+
totalChanges += getChangeCount(hooksResult, dryRun);
|
|
535
|
+
}
|
|
536
|
+
|
|
498
537
|
const configResult = ensureConfigFile({ dryRun, global: isGlobal });
|
|
499
538
|
totalChanges += getChangeCount(configResult, dryRun);
|
|
500
539
|
|
|
@@ -715,6 +754,17 @@ function list() {
|
|
|
715
754
|
console.log(` • ${s}`);
|
|
716
755
|
});
|
|
717
756
|
}
|
|
757
|
+
|
|
758
|
+
console.log('');
|
|
759
|
+
log('Hooks:', 'cyan');
|
|
760
|
+
const hooksDir = path.join(TEMPLATES_DIR, 'shared', 'hooks');
|
|
761
|
+
if (fs.existsSync(hooksDir)) {
|
|
762
|
+
const hooks = fs.readdirSync(hooksDir)
|
|
763
|
+
.filter(f => f.endsWith('.json'));
|
|
764
|
+
hooks.forEach(h => {
|
|
765
|
+
console.log(` • ${h.replace('.json', '')}`);
|
|
766
|
+
});
|
|
767
|
+
}
|
|
718
768
|
console.log('');
|
|
719
769
|
}
|
|
720
770
|
|
|
@@ -745,6 +795,7 @@ function showHelp() {
|
|
|
745
795
|
console.log(' --instructions-only Only install instructions');
|
|
746
796
|
console.log(' --partials-only Only install partials');
|
|
747
797
|
console.log(' --skills-only Only install skills');
|
|
798
|
+
console.log(' --hooks-only Only install hooks (PostToolUse validation scripts)');
|
|
748
799
|
console.log(' --dry-run Show what would be installed');
|
|
749
800
|
|
|
750
801
|
console.log('');
|
|
@@ -821,6 +872,7 @@ function parseArgs() {
|
|
|
821
872
|
partialsOnly: flags.includes('--partials-only'),
|
|
822
873
|
skillsOnly: flags.includes('--skills-only'),
|
|
823
874
|
instructionsOnly: flags.includes('--instructions-only'),
|
|
875
|
+
hooksOnly: flags.includes('--hooks-only'),
|
|
824
876
|
dryRun: flags.includes('--dry-run'),
|
|
825
877
|
claude: flags.includes('--claude'),
|
|
826
878
|
codex: flags.includes('--codex'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@silverassist/agents-toolkit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Reusable AI agent prompts for development workflows with Jira integration — supports GitHub Copilot, Claude Code, and Codex",
|
|
5
5
|
"author": "Santiago Ramirez",
|
|
6
6
|
"license": "PolyForm-Noncommercial-1.0.0",
|
package/src/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module @silverassist/agents-toolkit
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export const VERSION = "2.
|
|
6
|
+
export const VERSION = "2.3.0";
|
|
7
7
|
|
|
8
8
|
export const PROMPTS = {
|
|
9
9
|
workflow: [
|
|
@@ -40,6 +40,8 @@ export const SKILLS = [
|
|
|
40
40
|
"ai-seo-optimization",
|
|
41
41
|
];
|
|
42
42
|
|
|
43
|
+
export const HOOKS = ["validate-tsx", "lint-format"];
|
|
44
|
+
|
|
43
45
|
// Claude Code equivalents
|
|
44
46
|
export const CLAUDE_COMMANDS = [
|
|
45
47
|
"analyze-ticket",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lint-format.sh — PostToolUse hook for VS Code Copilot
|
|
3
|
+
# Runs eslint --fix and prettier --write on files modified by the agent.
|
|
4
|
+
# This ensures agent-generated code follows project formatting standards.
|
|
5
|
+
# Exit 0 = pass (always), diagnostics printed to stderr.
|
|
6
|
+
|
|
7
|
+
set -uo pipefail
|
|
8
|
+
|
|
9
|
+
FILE="${COPILOT_FILE:-}"
|
|
10
|
+
|
|
11
|
+
if [[ -z "$FILE" ]]; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Only process TypeScript, JavaScript, and CSS files
|
|
16
|
+
case "$FILE" in
|
|
17
|
+
*.ts|*.tsx|*.js|*.jsx|*.css) ;;
|
|
18
|
+
*) exit 0 ;;
|
|
19
|
+
esac
|
|
20
|
+
|
|
21
|
+
# Resolve to absolute path to prevent infinite loop in directory walk
|
|
22
|
+
if [[ "$FILE" != /* ]]; then
|
|
23
|
+
FILE="$(cd "$(dirname "$FILE")" && pwd)/$(basename "$FILE")"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# File must exist
|
|
27
|
+
if [[ ! -f "$FILE" ]]; then
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# Find project root (look for package.json)
|
|
32
|
+
DIR="$(dirname "$FILE")"
|
|
33
|
+
PROJECT_ROOT=""
|
|
34
|
+
while [[ "$DIR" != "/" ]]; do
|
|
35
|
+
if [[ -f "$DIR/package.json" ]]; then
|
|
36
|
+
PROJECT_ROOT="$DIR"
|
|
37
|
+
break
|
|
38
|
+
fi
|
|
39
|
+
DIR="$(dirname "$DIR")"
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Resolve tool paths
|
|
47
|
+
ESLINT="$PROJECT_ROOT/node_modules/.bin/eslint"
|
|
48
|
+
PRETTIER="$PROJECT_ROOT/node_modules/.bin/prettier"
|
|
49
|
+
|
|
50
|
+
# Run ESLint --fix (only on TS/JS files)
|
|
51
|
+
case "$FILE" in
|
|
52
|
+
*.ts|*.tsx|*.js|*.jsx)
|
|
53
|
+
if [[ -x "$ESLINT" ]]; then
|
|
54
|
+
"$ESLINT" --fix "$FILE" || true
|
|
55
|
+
fi
|
|
56
|
+
;;
|
|
57
|
+
esac
|
|
58
|
+
|
|
59
|
+
# Run Prettier --write
|
|
60
|
+
if [[ -x "$PRETTIER" ]]; then
|
|
61
|
+
"$PRETTIER" --write "$FILE" || true
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
exit 0
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# validate-tsx.sh — PostToolUse hook for VS Code Copilot
|
|
3
|
+
# Validates that TSX component files follow project conventions.
|
|
4
|
+
# Exit 0 = pass, Exit 1 = fail (shown as warning to Copilot).
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Only run when a .tsx file was modified
|
|
9
|
+
FILE="${COPILOT_FILE:-}"
|
|
10
|
+
|
|
11
|
+
if [[ -z "$FILE" ]]; then
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
if [[ "$FILE" != *.tsx ]]; then
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Only validate files inside components/ directories
|
|
20
|
+
if [[ "$FILE" != *"/components/"* ]]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
ERRORS=()
|
|
25
|
+
|
|
26
|
+
# --- Rule 1: Component must be inside a folder with index.tsx ---
|
|
27
|
+
BASENAME=$(basename "$FILE")
|
|
28
|
+
DIRNAME=$(basename "$(dirname "$FILE")")
|
|
29
|
+
|
|
30
|
+
if [[ "$BASENAME" != "index.tsx" && "$DIRNAME" != "__tests__" ]]; then
|
|
31
|
+
ERRORS+=("Component files must be named index.tsx inside a kebab-case folder (found: $BASENAME)")
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# --- Rule 2: Parent folder must be kebab-case ---
|
|
35
|
+
if [[ "$BASENAME" == "index.tsx" ]]; then
|
|
36
|
+
if [[ ! "$DIRNAME" =~ ^[a-z][a-z0-9]*(-[a-z0-9]+)*$ ]]; then
|
|
37
|
+
ERRORS+=("Component folder must be kebab-case (found: $DIRNAME)")
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# --- Rule 3: Must have a default export function (PascalCase) ---
|
|
42
|
+
if [[ "$BASENAME" == "index.tsx" ]] && [[ -f "$FILE" ]]; then
|
|
43
|
+
if ! grep -qE "^export default function [A-Z]" "$FILE"; then
|
|
44
|
+
ERRORS+=("Component must use 'export default function PascalName' pattern")
|
|
45
|
+
fi
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# --- Rule 4: Props interface should exist for non-trivial components ---
|
|
49
|
+
if [[ "$BASENAME" == "index.tsx" ]] && [[ -f "$FILE" ]]; then
|
|
50
|
+
# Check if there are props being destructured
|
|
51
|
+
if grep -qE "^export default function [A-Z][a-zA-Z]+\(" "$FILE"; then
|
|
52
|
+
# If it has props (not empty parens), check for interface
|
|
53
|
+
if grep -qE "^export default function [A-Z][a-zA-Z]+\(\{" "$FILE"; then
|
|
54
|
+
if ! grep -qE "^(export )?(interface|type) [A-Z][a-zA-Z]+Props" "$FILE"; then
|
|
55
|
+
ERRORS+=("Components with props should define a Props interface (e.g., interface ComponentNameProps)")
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
fi
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# --- Rule 5: No relative imports (must use @/ prefix) ---
|
|
62
|
+
if [[ -f "$FILE" ]]; then
|
|
63
|
+
if grep -qE "^import .+ from ['\"]\.\.?/" "$FILE" || grep -qE "^import ['\"]\.\.?/" "$FILE"; then
|
|
64
|
+
ERRORS+=("Relative imports found. Use absolute imports with @/ prefix instead of ./ or ../")
|
|
65
|
+
fi
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# --- Report ---
|
|
69
|
+
if [[ ${#ERRORS[@]} -gt 0 ]]; then
|
|
70
|
+
echo "⚠️ TSX validation issues in: $FILE"
|
|
71
|
+
for err in "${ERRORS[@]}"; do
|
|
72
|
+
echo " • $err"
|
|
73
|
+
done
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
exit 0
|