@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 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.2.0",
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.2.0";
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,11 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "type": "command",
6
+ "command": "scripts/lint-format.sh",
7
+ "timeout": 30
8
+ }
9
+ ]
10
+ }
11
+ }
@@ -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
@@ -0,0 +1,11 @@
1
+ {
2
+ "hooks": {
3
+ "PostToolUse": [
4
+ {
5
+ "type": "command",
6
+ "command": "scripts/validate-tsx.sh",
7
+ "timeout": 30
8
+ }
9
+ ]
10
+ }
11
+ }