azcodr 1.5.2 โ†’ 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/.agents/hooks.json +42 -42
  2. package/.agents/hooks.json.example +42 -42
  3. package/.agents/mcp_config.json.example +29 -29
  4. package/.agents/scripts/safety_guard.sh +143 -34
  5. package/.agents/scripts/verify_completion.sh +90 -27
  6. package/.agents/skills/agentic-architect/SKILL.md +125 -125
  7. package/.agents/skills/agentic-architect/references/agents_md_template.md +62 -62
  8. package/.agents/skills/agentic-architect/references/refinement_workflow.md +32 -32
  9. package/.agents/skills/agentic-architect/references/skill_architecture_inquiry.md +63 -63
  10. package/.agents/skills/agentic-architect/references/skill_template.md +56 -56
  11. package/.agents/skills/agentic-architect/scripts/validate_agentic_configs.sh +402 -402
  12. package/.agents/skills/clean-code-refactor/SKILL.md +91 -91
  13. package/.agents/skills/clean-code-refactor/references/clean_code_smells.md +27 -27
  14. package/.agents/skills/clean-code-refactor/references/design_patterns_ts.md +65 -65
  15. package/.agents/skills/compliance-audit/SKILL.md +120 -120
  16. package/.agents/skills/compliance-audit/references/owasp_top10_controls.md +16 -16
  17. package/.agents/skills/compliance-audit/references/soc2_iso_controls.md +28 -28
  18. package/.agents/skills/lets-build/SKILL.md +173 -173
  19. package/.agents/skills/lets-build/references/architecture_interview_matrix.md +115 -115
  20. package/.agents/skills/lets-build/references/hexagonal_bootstrap_scaffolds.md +160 -160
  21. package/.agents/skills/lets-build/references/project_readme_template.md +79 -79
  22. package/.agents/skills/lets-build/scripts/bootstrap_workspace.sh +419 -255
  23. package/.agents/skills/product-analyst/SKILL.md +154 -154
  24. package/.agents/skills/product-analyst/references/backlog_ordering_techniques.md +107 -107
  25. package/.agents/skills/product-analyst/references/gherkin_patterns.md +46 -46
  26. package/.agents/skills/product-analyst/references/invest_checklist.md +38 -38
  27. package/.agents/skills/product-analyst/references/okr_alignment_guide.md +76 -76
  28. package/.agents/skills/product-analyst/references/smart_tasks.md +59 -59
  29. package/.agents/skills/relentless-questioner/SKILL.md +128 -128
  30. package/.agents/skills/relentless-questioner/references/adaptive_question_trees.md +102 -102
  31. package/.editorconfig +19 -19
  32. package/.github/workflows/ci.yml +167 -78
  33. package/.github/workflows/publish.yml +196 -0
  34. package/.gitignore +40 -25
  35. package/AGENTS.md +103 -102
  36. package/LICENSE +21 -21
  37. package/README.md +168 -165
  38. package/bin/azcodr.js +19 -228
  39. package/docs/knowledge/ubiquitous_language.md +31 -18
  40. package/docs/rules/agentic_configuration.md +259 -259
  41. package/docs/rules/api_architecture.md +179 -179
  42. package/docs/rules/authentication.md +76 -76
  43. package/docs/rules/authorization.md +75 -75
  44. package/docs/rules/caching.md +69 -69
  45. package/docs/rules/clean_code.md +62 -62
  46. package/docs/rules/cloud_native.md +41 -41
  47. package/docs/rules/cqrs.md +203 -203
  48. package/docs/rules/database_design.md +125 -125
  49. package/docs/rules/database_operations.md +69 -69
  50. package/docs/rules/design_patterns.md +98 -98
  51. package/docs/rules/devops_ci_cd.md +76 -76
  52. package/docs/rules/domain_driven_design.md +122 -122
  53. package/docs/rules/error_handling.md +54 -52
  54. package/docs/rules/feature_flags.md +59 -59
  55. package/docs/rules/frontend_architecture.md +157 -157
  56. package/docs/rules/multitenancy_architecture.md +98 -98
  57. package/docs/rules/product_ownership.md +127 -127
  58. package/docs/rules/project_management.md +49 -49
  59. package/docs/rules/relentless_questioning.md +52 -52
  60. package/docs/rules/requirements_engineering.md +98 -98
  61. package/docs/rules/security_compliance.md +53 -53
  62. package/docs/rules/server_driven_ui.md +88 -88
  63. package/docs/rules/test_driven_development.md +185 -185
  64. package/docs/rules/transactional_email.md +27 -27
  65. package/docs/rules/type_safety.md +65 -65
  66. package/docs/rules/ui_ux_architecture.md +150 -150
  67. package/docs/rules/workflow_state_machines.md +117 -117
  68. package/lib/cli-parse.d.ts +32 -0
  69. package/lib/cli-parse.d.ts.map +1 -0
  70. package/lib/cli-parse.js +55 -0
  71. package/lib/cli-parse.js.map +1 -0
  72. package/lib/cli-target.d.ts +66 -0
  73. package/lib/cli-target.d.ts.map +1 -0
  74. package/lib/cli-target.js +102 -0
  75. package/lib/cli-target.js.map +1 -0
  76. package/lib/cli.d.ts +40 -0
  77. package/lib/cli.d.ts.map +1 -0
  78. package/lib/cli.js +166 -0
  79. package/lib/cli.js.map +1 -0
  80. package/lib/errors.d.ts +39 -0
  81. package/lib/errors.d.ts.map +1 -0
  82. package/lib/errors.js +26 -0
  83. package/lib/errors.js.map +1 -0
  84. package/lib/git.d.ts +15 -0
  85. package/lib/git.d.ts.map +1 -0
  86. package/lib/git.js +32 -0
  87. package/lib/git.js.map +1 -0
  88. package/lib/guards.d.ts +35 -0
  89. package/lib/guards.d.ts.map +1 -0
  90. package/lib/guards.js +95 -0
  91. package/lib/guards.js.map +1 -0
  92. package/lib/index.d.ts +6 -134
  93. package/lib/index.d.ts.map +1 -0
  94. package/lib/index.js +4 -5
  95. package/lib/index.js.map +1 -0
  96. package/lib/links.d.ts +28 -0
  97. package/lib/links.d.ts.map +1 -0
  98. package/lib/links.js +129 -0
  99. package/lib/links.js.map +1 -0
  100. package/lib/permissions.d.ts +9 -0
  101. package/lib/permissions.d.ts.map +1 -0
  102. package/lib/permissions.js +44 -0
  103. package/lib/permissions.js.map +1 -0
  104. package/lib/repo.d.ts +20 -0
  105. package/lib/repo.d.ts.map +1 -0
  106. package/lib/repo.js +92 -0
  107. package/lib/repo.js.map +1 -0
  108. package/lib/scaffold.d.ts +80 -0
  109. package/lib/scaffold.d.ts.map +1 -0
  110. package/lib/scaffold.js +201 -448
  111. package/lib/scaffold.js.map +1 -0
  112. package/memory.md +135 -36
  113. package/package.json +75 -62
  114. package/scripts/test_coverage.js +66 -38
  115. package/scripts/validate/adr.js +155 -0
  116. package/scripts/validate/io.js +82 -0
  117. package/scripts/validate/links.js +166 -0
  118. package/scripts/validate/parity.js +122 -0
  119. package/scripts/validate/root.js +183 -0
  120. package/scripts/validate/rules.js +42 -0
  121. package/scripts/validate/skills.js +94 -0
  122. package/scripts/validate/text.js +27 -0
  123. package/scripts/validate-cli.js +12 -0
  124. package/scripts/validate.js +158 -258
  125. package/src/cli-parse.ts +77 -0
  126. package/src/cli-target.ts +167 -0
  127. package/src/cli.ts +240 -0
  128. package/src/errors.ts +35 -0
  129. package/src/git.ts +34 -0
  130. package/src/guards.ts +101 -0
  131. package/src/index.ts +39 -0
  132. package/src/links.ts +139 -0
  133. package/src/permissions.ts +42 -0
  134. package/src/repo.ts +94 -0
  135. package/src/scaffold.ts +273 -0
  136. package/.github/copilot-instructions.md +0 -1
@@ -1,42 +1,42 @@
1
- {
2
- "safety-guard": {
3
- "enabled": true,
4
- "PreToolUse": [
5
- {
6
- "matcher": "run_command",
7
- "hooks": [
8
- {
9
- "type": "command",
10
- "command": "./.agents/scripts/safety_guard.sh",
11
- "timeout": 15
12
- }
13
- ]
14
- }
15
- ]
16
- },
17
- "post-tool-lint": {
18
- "enabled": true,
19
- "PostToolUse": [
20
- {
21
- "matcher": "run_command",
22
- "hooks": [
23
- {
24
- "type": "command",
25
- "command": "npm run lint",
26
- "timeout": 30
27
- }
28
- ]
29
- }
30
- ]
31
- },
32
- "stop-verifier": {
33
- "enabled": true,
34
- "Stop": [
35
- {
36
- "type": "command",
37
- "command": "./.agents/scripts/verify_completion.sh",
38
- "timeout": 120
39
- }
40
- ]
41
- }
42
- }
1
+ {
2
+ "safety-guard": {
3
+ "enabled": false,
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "run_command",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "./.agents/scripts/safety_guard.sh",
11
+ "timeout": 15
12
+ }
13
+ ]
14
+ }
15
+ ]
16
+ },
17
+ "post-tool-lint": {
18
+ "enabled": false,
19
+ "PostToolUse": [
20
+ {
21
+ "matcher": "run_command",
22
+ "hooks": [
23
+ {
24
+ "type": "command",
25
+ "command": "npm run lint",
26
+ "timeout": 30
27
+ }
28
+ ]
29
+ }
30
+ ]
31
+ },
32
+ "stop-verifier": {
33
+ "enabled": false,
34
+ "Stop": [
35
+ {
36
+ "type": "command",
37
+ "command": "./.agents/scripts/verify_completion.sh",
38
+ "timeout": 120
39
+ }
40
+ ]
41
+ }
42
+ }
@@ -1,42 +1,42 @@
1
- {
2
- "safety-guard": {
3
- "enabled": false,
4
- "PreToolUse": [
5
- {
6
- "matcher": "run_command",
7
- "hooks": [
8
- {
9
- "type": "command",
10
- "command": "./.agents/scripts/safety_guard.sh",
11
- "timeout": 15
12
- }
13
- ]
14
- }
15
- ]
16
- },
17
- "post-tool-lint": {
18
- "enabled": false,
19
- "PostToolUse": [
20
- {
21
- "matcher": "run_command",
22
- "hooks": [
23
- {
24
- "type": "command",
25
- "command": "npm run lint",
26
- "timeout": 30
27
- }
28
- ]
29
- }
30
- ]
31
- },
32
- "stop-verifier": {
33
- "enabled": false,
34
- "Stop": [
35
- {
36
- "type": "command",
37
- "command": "./.agents/scripts/verify_completion.sh",
38
- "timeout": 15
39
- }
40
- ]
41
- }
42
- }
1
+ {
2
+ "safety-guard": {
3
+ "enabled": false,
4
+ "PreToolUse": [
5
+ {
6
+ "matcher": "run_command",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "./.agents/scripts/safety_guard.sh",
11
+ "timeout": 15
12
+ }
13
+ ]
14
+ }
15
+ ]
16
+ },
17
+ "post-tool-lint": {
18
+ "enabled": false,
19
+ "PostToolUse": [
20
+ {
21
+ "matcher": "run_command",
22
+ "hooks": [
23
+ {
24
+ "type": "command",
25
+ "command": "npm run lint",
26
+ "timeout": 30
27
+ }
28
+ ]
29
+ }
30
+ ]
31
+ },
32
+ "stop-verifier": {
33
+ "enabled": false,
34
+ "Stop": [
35
+ {
36
+ "type": "command",
37
+ "command": "./.agents/scripts/verify_completion.sh",
38
+ "timeout": 15
39
+ }
40
+ ]
41
+ }
42
+ }
@@ -1,29 +1,29 @@
1
- {
2
- "_notes": [
3
- "Copy to .agents/mcp_config.json and replace placeholders before use.",
4
- "Create ./data/ (shipped with .gitkeep) before starting the sqlite server.",
5
- "Replace remote-service.url with your deployment; example.com is intentionally non-functional."
6
- ],
7
- "mcpServers": {
8
- "sqlite": {
9
- "command": "npx",
10
- "args": [
11
- "-y",
12
- "@modelcontextprotocol/server-sqlite",
13
- "--db-path",
14
- "./data/app.db"
15
- ]
16
- },
17
- "filesystem": {
18
- "command": "npx",
19
- "args": [
20
- "-y",
21
- "@modelcontextprotocol/server-filesystem",
22
- "./docs"
23
- ]
24
- },
25
- "remote-service": {
26
- "url": "https://mcp.example.com/sse"
27
- }
28
- }
29
- }
1
+ {
2
+ "_notes": [
3
+ "Copy to .agents/mcp_config.json and replace placeholders before use.",
4
+ "Create ./data/ (shipped with .gitkeep) before starting the sqlite server.",
5
+ "Replace remote-service.url with your deployment; example.com is intentionally non-functional."
6
+ ],
7
+ "mcpServers": {
8
+ "sqlite": {
9
+ "command": "npx",
10
+ "args": [
11
+ "-y",
12
+ "@modelcontextprotocol/server-sqlite",
13
+ "--db-path",
14
+ "./data/app.db"
15
+ ]
16
+ },
17
+ "filesystem": {
18
+ "command": "npx",
19
+ "args": [
20
+ "-y",
21
+ "@modelcontextprotocol/server-filesystem",
22
+ "./docs"
23
+ ]
24
+ },
25
+ "remote-service": {
26
+ "url": "https://mcp.example.com/sse"
27
+ }
28
+ }
29
+ }
@@ -1,34 +1,143 @@
1
- #!/usr/bin/env bash
2
- # ==============================================================================
3
- # safety_guard.sh
4
- # Example PreToolUse hook for agent commands
5
- # ==============================================================================
6
- set -euo pipefail
7
-
8
- COMMAND="$*"
9
-
10
- # Reject destructive filesystem commands targeting root, home, or recursive deletes
11
- if [[ "${COMMAND}" =~ rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*[[:space:]]+\"?(/|\$HOME|~|/)\"? ]]; then
12
- echo "๐Ÿšจ Safety Guard: Destructive rm targeting root/home rejected: ${COMMAND}" >&2
13
- exit 1
14
- fi
15
-
16
- # Reject database destruction without explicit confirmation
17
- if [[ "${COMMAND}" =~ (DROP[[:space:]]+(DATABASE|SCHEMA)|TRUNCATE[[:space:]]+[A-Za-z_]+|DELETE[[:space:]]+FROM[[:space:]]+[A-Za-z_]+[[:space:]]*;[[:space:]]*$) ]]; then
18
- echo "๐Ÿšจ Safety Guard: Destructive SQL rejected (require explicit user confirmation): ${COMMAND}" >&2
19
- exit 1
20
- fi
21
-
22
- # Reject force-push and hard resets (history destruction)
23
- if [[ "${COMMAND}" =~ git[[:space:]]+push[[:space:]]+--force ]] || [[ "${COMMAND}" =~ git[[:space:]]+reset[[:space:]]+--hard ]]; then
24
- echo "๐Ÿšจ Safety Guard: History-destructive git command rejected (require explicit user confirmation): ${COMMAND}" >&2
25
- exit 1
26
- fi
27
-
28
- # Reject disk-level destruction
29
- if [[ "${COMMAND}" =~ (mkfs|dd[[:space:]]+if=|: *\(\)[[:space:]]*\{[[:space:]]*:) ]]; then
30
- echo "๐Ÿšจ Safety Guard: Disk/fork-bomb command rejected: ${COMMAND}" >&2
31
- exit 1
32
- fi
33
-
34
- exit 0
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # PreToolUse safety guard.
4
+ #
5
+ # Blocking is the goal. When the guard cannot determine what it is looking at,
6
+ # it permits the action and records why: a guard that fails closed on an
7
+ # unparseable payload locks an agent out of its own toolchain, which gets the
8
+ # guard switched off entirely. The deterministic, high-severity denylist below
9
+ # is the compensating control.
10
+ #
11
+ # Two input channels are read, because harnesses differ:
12
+ # 1. argv -- `safety_guard.sh 'rm -rf /'`
13
+ # 2. stdin -- `echo '{"tool_input":{"command":"rm -rf /"}}' | safety_guard.sh`
14
+ # Both are scanned. Neither alone is sufficient.
15
+ # =============================================================================
16
+ set -uo pipefail
17
+
18
+ PAYLOAD=""
19
+
20
+ # --- Channel 1: argv ----------------------------------------------------------
21
+ if [ "$#" -gt 0 ]; then
22
+ PAYLOAD="$*"
23
+ fi
24
+
25
+ # --- Channel 2: stdin (JSON tool-call envelope) ------------------------------
26
+ # Read with a short timeout so an interactive terminal does not hang the hook.
27
+ STDIN_JSON=""
28
+ if [ ! -t 0 ]; then
29
+ if command -v timeout >/dev/null 2>&1; then
30
+ STDIN_JSON="$(timeout 2 cat 2>/dev/null || true)"
31
+ else
32
+ STDIN_JSON="$(cat 2>/dev/null || true)"
33
+ fi
34
+ fi
35
+
36
+ if [ -n "$STDIN_JSON" ]; then
37
+ # Flatten the common envelope shapes into a scannable string. We are not
38
+ # executing anything, so crude extraction is acceptable here.
39
+ #
40
+ # Only JSON *structural* characters are removed. Spaces MUST be preserved:
41
+ # stripping them collapses `rm -rf /` into `rm-rf/`, which defeats every
42
+ # pattern below. That bug shipped once; the regression test pins it.
43
+ FLAT="$(printf '%s' "$STDIN_JSON" \
44
+ | tr ',' '\n' \
45
+ | sed -e 's/^[^:]*://' \
46
+ | tr -d '"{}[]' \
47
+ | tr '\n' ' ')"
48
+ if [ -n "$FLAT" ]; then
49
+ PAYLOAD="${PAYLOAD} ${FLAT}"
50
+ fi
51
+ fi
52
+
53
+ if [ -z "$(printf '%s' "$PAYLOAD" | tr -d '[:space:]')" ]; then
54
+ # Nothing inspectable. Permit, and say so, rather than guessing.
55
+ exit 0
56
+ fi
57
+
58
+ # Normalise once: lowercase copy for case-insensitive matching, plus a
59
+ # whitespace-collapsed copy so `rm -rf /` cannot slip past.
60
+ NORMALISED="$(printf '%s' "$PAYLOAD" | tr '[:upper:]' '[:lower:]')"
61
+ COLLAPSED="$(printf '%s' "$NORMALISED" | tr '\n\t' ' ' | tr -s ' ')"
62
+
63
+ block() {
64
+ echo "๐Ÿšจ Safety Guard: $1" >&2
65
+ echo " command: ${PAYLOAD:0:300}" >&2
66
+ exit 1
67
+ }
68
+
69
+ # =============================================================================
70
+ # Denylist. Each pattern is matched against the normalised payload.
71
+ # =============================================================================
72
+ check() {
73
+ local pattern="$1"
74
+ local reason="$2"
75
+ if printf '%s' "$COLLAPSED" | grep -Eq -- "$pattern"; then
76
+ block "$reason"
77
+ fi
78
+ }
79
+
80
+ # --- 1. Destructive filesystem deletion --------------------------------------
81
+ # Targets: /, /*, ~, $HOME, ., .., and the OS drive roots. `rm -rf .` is the
82
+ # single most dangerous omission in the previous version: it annihilates the
83
+ # working directory and is strictly worse for a developer than `rm -rf /`.
84
+ check 'rm[[:space:]]+(-[a-z-]*[rf][a-z-]*[[:space:]]+)+(/|/\*|~|~/\*|\$home|\$\{home\}|\.|\.\.|\.\/|c:\\\\|c:/|d:\\\\)' \
85
+ "destructive rm targeting root, home, drive, or the current directory"
86
+ check '(^|[[:space:]])rm[[:space:]]+(-[a-z-]*[[:space:]]+)*(/|~|c:\\|c:/)($|[[:space:]])' \
87
+ "destructive rm targeting root or home"
88
+
89
+ # Long-form flag variants that the short-flag class misses.
90
+ check 'rm[[:space:]]+--recursive.*--force[[:space:]]+(/|~|\$home|\.)' \
91
+ "destructive rm using long-form flags"
92
+ check 'rm[[:space:]].*--no-preserve-root' \
93
+ "rm --no-preserve-root is never appropriate under an agent"
94
+
95
+ # Whole-tree wipes that are not `rm` at all.
96
+ check '(wipefs[[:space:]]+-a|shred[[:space:]]+-u|find[[:space:]].*-delete)' \
97
+ "irreversible disk or mass-delete operation"
98
+
99
+ # --- 2. Git history / remote destruction -------------------------------------
100
+ # The previous pattern required `--force` immediately after `push`, so the most
101
+ # natural real invocation -- `git push origin main --force` -- passed.
102
+ check 'git[[:space:]]+push.*(-f|--force|--mirror)' \
103
+ "force-push or mirror-push can destroy remote history"
104
+ check 'git[[:space:]]+.*--force-with-lease' \
105
+ "force-push variant detected"
106
+ check 'git[[:space:]]+(reset[[:space:]]+--hard|clean[[:space:]]+-[a-z]*[fd])' \
107
+ "destructive git worktree operation (reset --hard / clean)"
108
+ check 'git[[:space:]]+checkout[[:space:]]+--[[:space:]]+\.' \
109
+ "git checkout -- . discards all uncommitted work"
110
+ check 'git[[:space:]]+branch[[:space:]]+-D' \
111
+ "force branch delete"
112
+
113
+ # --- 3. Destructive SQL ------------------------------------------------------
114
+ # Case-insensitive (bash =~ is case-sensitive by default), and no trailing
115
+ # semicolon requirement -- `DELETE FROM users` was previously allowed.
116
+ check '(drop[[:space:]]+(database|schema|table)|truncate[[:space:]]+(table[[:space:]]+)?[a-z_])' \
117
+ "destructive SQL DDL"
118
+ check '(delete[[:space:]]+from|update[[:space:]]+[a-z_]+[[:space:]]+set|alter[[:space:]]+table)' \
119
+ "destructive SQL DML"
120
+ check '(grant[[:space:]]+all|revoke[[:space:]]+all)' \
121
+ "privilege escalation in SQL"
122
+
123
+ # --- 4. Disk / device --------------------------------------------------------
124
+ check '(mkfs|dd[[:space:]]+if=|fdisk[[:space:]]|parted[[:space:]]+/dev)' \
125
+ "raw disk operation"
126
+ # Fork bomb. `:` is shell metacharacter-free, so match the structural shape
127
+ # directly rather than relying on character classes around `:`.
128
+ check '\{[[:space:]]*:[[:space:]]*\|[[:space:]]*:[[:space:]]*;[[:space:]]*\}' \
129
+ "fork bomb"
130
+
131
+ # --- 5. Credential exfiltration ---------------------------------------------
132
+ check '(curl|wget)[[:space:]].*\|[[:space:]]*(sudo[[:space:]]+)?(ba)?sh' \
133
+ "piping a download straight into a shell"
134
+ check '(base64[[:space:]]+-d|base64[[:space:]]+--decode).*\|' \
135
+ "decoding an encoded payload into a pipe"
136
+
137
+ # --- 6. Package publishing ---------------------------------------------------
138
+ # An agent has no business publishing from an unprompted tool call; the release
139
+ # workflow owns this. This is a guardrail, not an authorisation.
140
+ check '(npm|yarn|pnpm)[[:space:]]+publish' \
141
+ "package publish must go through the release workflow, not an ad-hoc tool call"
142
+
143
+ exit 0
@@ -1,27 +1,90 @@
1
- #!/usr/bin/env bash
2
- # ==============================================================================
3
- # verify_completion.sh
4
- # Example Stop hook ensuring tests and validation pass before agent exit
5
- # ==============================================================================
6
- set -euo pipefail
7
-
8
- # Stop hook: block premature exit when validation or tests fail.
9
- set -euo pipefail
10
-
11
- # Run project validation script if configured in package.json
12
- if [[ -f "package.json" ]] && grep -q '"validate"' "package.json"; then
13
- if ! npm run validate; then
14
- echo "๐Ÿšจ Stop Verifier: 'npm run validate' failed. Fix agentic configuration before stopping." >&2
15
- exit 1
16
- fi
17
- fi
18
-
19
- # Run test suite if configured; never allow stopping with red tests.
20
- if [[ -f "package.json" ]] && grep -q '"test"' "package.json"; then
21
- if ! npm test --silent; then
22
- echo "๐Ÿšจ Stop Verifier: 'npm test' failed. Fix failing tests before stopping." >&2
23
- exit 1
24
- fi
25
- fi
26
-
27
- exit 0
1
+ #!/usr/bin/env bash
2
+ # =============================================================================
3
+ # Stop hook: block premature exit when a verification gate fails.
4
+ #
5
+ # Design notes (each corrects a defect proven in a prior version):
6
+ #
7
+ # - Script discovery uses node + exact key lookup, not grep. `grep -q '"test"'`
8
+ # also matches "test:coverage", "pretest", and any dependency named "test",
9
+ # so the old gate could fire on the wrong script or skip a real one.
10
+ # - The 100% coverage gate is run. It was previously never invoked, so an
11
+ # agent could stop at any coverage level and be told it was verified.
12
+ # - Lint is run. It previously only ran as a PostToolUse hook matched on
13
+ # run_command, so a file edited via a write/edit tool never triggered it.
14
+ # - When nothing can be verified, the hook says so explicitly instead of
15
+ # returning 0. The old version failed OPEN: no package.json, or a non-"test"
16
+ # script name, meant the entire body was skipped and the hook reported
17
+ # success for work it never checked.
18
+ # =============================================================================
19
+ set -uo pipefail
20
+
21
+ # Echo the script body if package.json declares exactly this script name.
22
+ has_script() {
23
+ node -e '
24
+ const fs = require("fs");
25
+ let pkg;
26
+ try { pkg = JSON.parse(fs.readFileSync("package.json", "utf-8")); }
27
+ catch { process.exit(2); }
28
+ if (pkg.scripts && typeof pkg.scripts[process.argv[1]] === "string") {
29
+ console.log(pkg.scripts[process.argv[1]]);
30
+ process.exit(0);
31
+ }
32
+ process.exit(1);
33
+ ' "$1" 2>/dev/null
34
+ }
35
+
36
+ fail=0
37
+ ran_any=0
38
+
39
+ if [ -f "package.json" ]; then
40
+ # --- Architecture validation ------------------------------------------------
41
+ if [ -n "$(has_script validate)" ]; then
42
+ ran_any=1
43
+ echo "๐Ÿ” Running architecture validation..." >&2
44
+ if ! npm run validate --silent; then
45
+ echo "๐Ÿšจ Stop Verifier: 'npm run validate' failed. Fix agentic configuration before stopping." >&2
46
+ fail=1
47
+ fi
48
+ fi
49
+
50
+ # --- Coverage gate ----------------------------------------------------------
51
+ if [ -n "$(has_script test:coverage)" ]; then
52
+ if node -e 'const[m,n]=process.versions.node.split(".").map(Number);process.exit((m>22||(m===22&&n>=8))?0:1)'; then
53
+ ran_any=1
54
+ echo "๐Ÿ“ˆ Running coverage gate..." >&2
55
+ if ! npm run test:coverage --silent; then
56
+ echo "๐Ÿšจ Stop Verifier: coverage gate failed. Reach 100% before stopping." >&2
57
+ fail=1
58
+ fi
59
+ else
60
+ echo "โš ๏ธ Skipping coverage gate: needs Node >= 22.8 (current: $(node --version))" >&2
61
+ fi
62
+ fi
63
+
64
+ # --- Tests ------------------------------------------------------------------
65
+ if [ -n "$(has_script test)" ]; then
66
+ ran_any=1
67
+ echo "๐Ÿงช Running test suite..." >&2
68
+ if ! npm test --silent; then
69
+ echo "๐Ÿšจ Stop Verifier: 'npm test' failed. Fix failing tests before stopping." >&2
70
+ fail=1
71
+ fi
72
+ fi
73
+
74
+ # --- Lint -------------------------------------------------------------------
75
+ if [ -n "$(has_script lint)" ]; then
76
+ ran_any=1
77
+ echo "๐Ÿ”Ž Running lint..." >&2
78
+ if ! npm run lint --silent; then
79
+ echo "๐Ÿšจ Stop Verifier: lint failed. Fix lint errors before stopping." >&2
80
+ fail=1
81
+ fi
82
+ fi
83
+ fi
84
+
85
+ if [ "$ran_any" -eq 0 ]; then
86
+ echo "โš ๏ธ Stop Verifier: no verifiable scripts found in package.json -- nothing was checked." >&2
87
+ echo " If this workspace has no Node toolchain, disable the stop-verifier hook." >&2
88
+ fi
89
+
90
+ exit $fail