aw-ecc 1.4.23 → 1.4.31

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 (73) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.cursor/INSTALL.md +9 -0
  3. package/.cursor/hooks/adapter.js +34 -3
  4. package/.cursor/hooks/aw-phase-definitions.js +11 -0
  5. package/.cursor/hooks/before-submit-prompt.sh +17 -0
  6. package/.cursor/hooks/session-start.sh +36 -0
  7. package/.cursor/hooks/shared/aw-phase-definitions.js +1 -19
  8. package/.cursor/hooks/shared/aw-phase-runner.js +38 -2
  9. package/.cursor/hooks/shared/user-prompt-submit.sh +40 -60
  10. package/.cursor/hooks.json +15 -15
  11. package/.cursor/rules/common-aw-routing.md +43 -0
  12. package/.cursor/skills/api-and-interface-design/SKILL.md +75 -0
  13. package/.cursor/skills/aw-brainstorm/SKILL.md +115 -0
  14. package/.cursor/skills/aw-build/SKILL.md +152 -0
  15. package/.cursor/skills/aw-build/evals/build-stage-cases.json +28 -0
  16. package/.cursor/skills/aw-debug/SKILL.md +49 -0
  17. package/.cursor/skills/aw-deploy/SKILL.md +101 -0
  18. package/.cursor/skills/aw-deploy/evals/deploy-stage-cases.json +32 -0
  19. package/.cursor/skills/aw-execute/SKILL.md +47 -0
  20. package/.cursor/skills/aw-execute/references/mode-code.md +47 -0
  21. package/.cursor/skills/aw-execute/references/mode-docs.md +28 -0
  22. package/.cursor/skills/aw-execute/references/mode-infra.md +44 -0
  23. package/.cursor/skills/aw-execute/references/mode-migration.md +58 -0
  24. package/.cursor/skills/aw-execute/references/worker-implementer.md +26 -0
  25. package/.cursor/skills/aw-execute/references/worker-parallel-worker.md +23 -0
  26. package/.cursor/skills/aw-execute/references/worker-quality-reviewer.md +23 -0
  27. package/.cursor/skills/aw-execute/references/worker-spec-reviewer.md +23 -0
  28. package/.cursor/skills/aw-execute/scripts/build-worker-bundle.js +229 -0
  29. package/.cursor/skills/aw-finish/SKILL.md +111 -0
  30. package/.cursor/skills/aw-investigate/SKILL.md +109 -0
  31. package/.cursor/skills/aw-plan/SKILL.md +368 -0
  32. package/.cursor/skills/aw-prepare/SKILL.md +118 -0
  33. package/.cursor/skills/aw-review/SKILL.md +118 -0
  34. package/.cursor/skills/aw-ship/SKILL.md +115 -0
  35. package/.cursor/skills/aw-spec/SKILL.md +104 -0
  36. package/.cursor/skills/aw-tasks/SKILL.md +138 -0
  37. package/.cursor/skills/aw-test/SKILL.md +118 -0
  38. package/.cursor/skills/aw-verify/SKILL.md +51 -0
  39. package/.cursor/skills/aw-yolo/SKILL.md +111 -0
  40. package/.cursor/skills/browser-testing-with-devtools/SKILL.md +81 -0
  41. package/.cursor/skills/ci-cd-and-automation/SKILL.md +71 -0
  42. package/.cursor/skills/code-simplification/SKILL.md +74 -0
  43. package/.cursor/skills/context-engineering/SKILL.md +74 -0
  44. package/.cursor/skills/deprecation-and-migration/SKILL.md +75 -0
  45. package/.cursor/skills/documentation-and-adrs/SKILL.md +75 -0
  46. package/.cursor/skills/frontend-ui-engineering/SKILL.md +68 -0
  47. package/.cursor/skills/git-workflow-and-versioning/SKILL.md +75 -0
  48. package/.cursor/skills/idea-refine/SKILL.md +84 -0
  49. package/.cursor/skills/incremental-implementation/SKILL.md +75 -0
  50. package/.cursor/skills/performance-optimization/SKILL.md +77 -0
  51. package/.cursor/skills/security-and-hardening/SKILL.md +70 -0
  52. package/.cursor/skills/using-aw-skills/SKILL.md +290 -0
  53. package/.cursor/skills/using-aw-skills/evals/skill-trigger-cases.tsv +25 -0
  54. package/.cursor/skills/using-aw-skills/evals/test-skill-triggers.sh +171 -0
  55. package/.cursor/skills/using-aw-skills/hooks/hooks.json +9 -0
  56. package/.cursor/skills/using-aw-skills/hooks/session-start.sh +67 -0
  57. package/.cursor/skills/using-platform-skills/SKILL.md +163 -0
  58. package/.cursor/skills/using-platform-skills/evals/platform-selection-cases.json +52 -0
  59. package/.opencode/package.json +1 -1
  60. package/package.json +5 -1
  61. package/scripts/cursor-aw-home/hooks.json +15 -15
  62. package/scripts/cursor-aw-hooks/adapter.js +34 -3
  63. package/scripts/cursor-aw-hooks/aw-phase-definitions.js +11 -0
  64. package/scripts/cursor-aw-hooks/before-submit-prompt.sh +17 -0
  65. package/scripts/cursor-aw-hooks/session-start.sh +36 -0
  66. package/scripts/hooks/session-start-rules-context.sh +8 -1
  67. package/scripts/hooks/shared/aw-phase-definitions.js +1 -19
  68. package/scripts/hooks/shared/aw-phase-runner.js +38 -2
  69. package/scripts/hooks/shared/user-prompt-submit.sh +40 -60
  70. package/scripts/lib/cursor-aw-hook-files.js +2 -0
  71. package/scripts/lib/cursor-hook-config.js +47 -15
  72. package/scripts/lib/install-executor.js +7 -0
  73. package/scripts/lib/install-targets/cursor-project.js +7 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aw",
3
- "version": "1.4.23",
3
+ "version": "1.4.25",
4
4
  "description": "GoHighLevel Agentic Workspace Engine — agents, skills, commands, hooks, and rules for GHL development workflows.",
5
5
  "author": {
6
6
  "name": "GoHighLevel",
@@ -11,6 +11,7 @@ Cursor-facing rules or prompts should point at:
11
11
  - `AGENTS.md`
12
12
  - `commands/`
13
13
  - `skills/`
14
+ - `.cursor/rules/common-aw-routing.md`
14
15
  - `defaults/aw-sdlc/`
15
16
  - `docs/aw-sdlc-command-contracts.md`
16
17
  - `docs/aw-sdlc-command-skill-architecture.md`
@@ -29,6 +30,14 @@ Map intent to the same public stage surface:
29
30
 
30
31
  Cursor-specific prompt wrappers may exist, but they should stay thinner than the AW SDLC stage skills and must not introduce extra public stages.
31
32
 
33
+ The global Cursor routing rule should be `alwaysApply: true` and must:
34
+
35
+ - require `using-aw-skills` first
36
+ - require the smallest correct AW stage route first
37
+ - point Cursor at bundled AW skills under `~/.cursor/skills/`
38
+ - point Cursor at org rules under `~/.aw_rules/platform/`
39
+ - keep repo-local instructions additive only
40
+
32
41
  ## Artifact Rule
33
42
 
34
43
  Deterministic artifacts stay under:
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  const { execFileSync } = require('child_process');
9
+ const fs = require('fs');
9
10
  const path = require('path');
10
11
 
11
12
  const MAX_STDIN = 1024 * 1024;
@@ -22,7 +23,18 @@ function readStdin() {
22
23
  }
23
24
 
24
25
  function getPluginRoot() {
25
- return path.resolve(__dirname, '..', '..');
26
+ const homeStyleRoot = path.resolve(__dirname, '..');
27
+ const repoStyleRoot = path.resolve(__dirname, '..', '..');
28
+
29
+ if (
30
+ fs.existsSync(path.join(repoStyleRoot, 'package.json'))
31
+ && fs.existsSync(path.join(repoStyleRoot, 'scripts', 'hooks'))
32
+ && fs.existsSync(path.join(repoStyleRoot, '.cursor', 'hooks'))
33
+ ) {
34
+ return repoStyleRoot;
35
+ }
36
+
37
+ return homeStyleRoot;
26
38
  }
27
39
 
28
40
  function transformToClaude(cursorInput, overrides = {}) {
@@ -83,12 +95,31 @@ function runManagedCommand(command, args, stdinData) {
83
95
  }
84
96
 
85
97
  function runExistingHook(scriptName, stdinData) {
86
- const scriptPath = path.join(getPluginRoot(), 'scripts', 'hooks', scriptName);
98
+ const root = getPluginRoot();
99
+ const candidates = [
100
+ path.join(root, 'scripts', 'hooks', scriptName),
101
+ path.join(root, 'hooks', scriptName),
102
+ ];
103
+ const scriptPath = candidates.find(candidate => fs.existsSync(candidate)) || candidates[0];
87
104
  return runManagedCommand('node', [scriptPath], stdinData);
88
105
  }
89
106
 
90
107
  function runManagedShellHook(relativeScriptPath, stdinData) {
91
- const scriptPath = path.join(getPluginRoot(), relativeScriptPath);
108
+ const root = getPluginRoot();
109
+ const normalized = String(relativeScriptPath || '').replace(/\\/g, '/');
110
+ const fallbackCandidates = [
111
+ normalized,
112
+ normalized.replace(/^\.cursor\//, ''),
113
+ normalized.replace(/^scripts\//, ''),
114
+ normalized.replace(/^hooks\//, ''),
115
+ path.posix.join('.cursor', normalized),
116
+ path.posix.join('scripts', normalized),
117
+ path.posix.join('hooks', normalized),
118
+ ];
119
+ const scriptPath = fallbackCandidates
120
+ .map(candidate => path.join(root, candidate))
121
+ .find(candidate => fs.existsSync(candidate))
122
+ || path.join(root, normalized);
92
123
  return runManagedCommand('bash', [scriptPath], stdinData);
93
124
  }
94
125
 
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {
4
+ SHARED_AW_PHASE_STEPS: CURSOR_AW_PHASE_STEPS,
5
+ getSharedAwPhaseSteps: getCursorAwPhaseSteps,
6
+ } = require('./shared/aw-phase-definitions');
7
+
8
+ module.exports = {
9
+ CURSOR_AW_PHASE_STEPS,
10
+ getCursorAwPhaseSteps,
11
+ };
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ RAW="$(cat || true)"
5
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
+
7
+ RESULT="$(printf '%s' "$RAW" | node "$SCRIPT_DIR/before-submit-prompt.js" 2>&1 1>/tmp/aw_cursor_before_submit_stdout.$$)"
8
+ STATUS=$?
9
+ STDOUT_CONTENT="$(cat /tmp/aw_cursor_before_submit_stdout.$$ 2>/dev/null || true)"
10
+ rm -f /tmp/aw_cursor_before_submit_stdout.$$
11
+
12
+ if [ -n "$RESULT" ]; then
13
+ printf '%s\n' "$RESULT" >&2
14
+ fi
15
+
16
+ printf '%s' "${STDOUT_CONTENT:-$RAW}"
17
+ exit "$STATUS"
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ RAW="$(cat || true)"
5
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
6
+
7
+ RESULT="$(printf '%s' "$RAW" | bash "$SCRIPT_DIR/shared/session-start.sh")"
8
+
9
+ if [ -z "$RESULT" ]; then
10
+ printf '%s' "$RAW"
11
+ exit 0
12
+ fi
13
+
14
+ JSON_CONTEXT="$(
15
+ printf '%s' "$RESULT" | node -e '
16
+ let input = "";
17
+ process.stdin.setEncoding("utf8");
18
+ process.stdin.on("data", chunk => input += chunk);
19
+ process.stdin.on("end", () => {
20
+ try {
21
+ const parsed = JSON.parse(input || "{}");
22
+ const context =
23
+ (parsed && parsed.hookSpecificOutput && parsed.hookSpecificOutput.additionalContext) ||
24
+ parsed.additional_context ||
25
+ "";
26
+ if (typeof context === "string" && context.trim()) {
27
+ process.stdout.write(JSON.stringify({ additional_context: context }, null, 2) + "\n");
28
+ return;
29
+ }
30
+ } catch {}
31
+ process.stdout.write(process.env.ECC_FALLBACK_RAW || "");
32
+ });
33
+ ' ECC_FALLBACK_RAW="$RAW"
34
+ )"
35
+
36
+ printf '%s' "$JSON_CONTEXT"
@@ -6,6 +6,7 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
6
6
  runner: 'shell',
7
7
  relativeScriptPath: '.cursor/hooks/shared/session-start.sh',
8
8
  payloadMode: 'raw',
9
+ outputMode: 'cursor-session-start',
9
10
  },
10
11
  ],
11
12
  'user-prompt-submit': [
@@ -157,24 +158,6 @@ const SHARED_AW_PHASE_STEPS = Object.freeze({
157
158
  ],
158
159
  });
159
160
 
160
- /**
161
- * Canonical string constants for all AW hook phase names.
162
- * Use these instead of raw string literals to get typo-safety and IDE
163
- * auto-complete in files that call runNamedCursorAwPhase() or equivalent.
164
- */
165
- const PHASE_NAMES = Object.freeze({
166
- SESSION_START: 'session-start',
167
- USER_PROMPT_SUBMIT: 'user-prompt-submit',
168
- PRE_TOOL_USE_SHELL: 'pre-tool-use-shell',
169
- PRE_TOOL_USE_MCP: 'pre-tool-use-mcp',
170
- POST_TOOL_USE_SHELL: 'post-tool-use-shell',
171
- POST_TOOL_USE_FILE_EDIT: 'post-tool-use-file-edit',
172
- POST_TOOL_USE_MCP: 'post-tool-use-mcp',
173
- PRE_COMPACT: 'pre-compact',
174
- SESSION_END: 'session-end',
175
- STOP: 'stop',
176
- });
177
-
178
161
  function getSharedAwPhaseSteps(phaseName) {
179
162
  const steps = SHARED_AW_PHASE_STEPS[phaseName];
180
163
  if (!steps) {
@@ -184,7 +167,6 @@ function getSharedAwPhaseSteps(phaseName) {
184
167
  }
185
168
 
186
169
  module.exports = {
187
- PHASE_NAMES,
188
170
  SHARED_AW_PHASE_STEPS,
189
171
  getSharedAwPhaseSteps,
190
172
  };
@@ -26,6 +26,37 @@ function executeStep(runtime, step, payload) {
26
26
  return runtime.runManagedNodeHook(step.relativeScriptPath, payload);
27
27
  }
28
28
 
29
+ function formatCursorSessionStartOutput(stdout, fallbackRaw) {
30
+ if (typeof stdout !== 'string' || stdout.trim() === '') {
31
+ return fallbackRaw;
32
+ }
33
+
34
+ try {
35
+ const payload = JSON.parse(stdout);
36
+ const additionalContext = payload?.hookSpecificOutput?.additionalContext
37
+ || payload?.additional_context;
38
+
39
+ if (typeof additionalContext === 'string' && additionalContext.trim() !== '') {
40
+ return `${JSON.stringify({ additional_context: additionalContext }, null, 2)}\n`;
41
+ }
42
+ } catch {}
43
+
44
+ return fallbackRaw;
45
+ }
46
+
47
+ function resolveStepOutput(step, execution, fallbackRaw) {
48
+ if (!execution) {
49
+ return null;
50
+ }
51
+
52
+ switch (step.outputMode) {
53
+ case 'cursor-session-start':
54
+ return formatCursorSessionStartOutput(execution.stdout, fallbackRaw);
55
+ default:
56
+ return null;
57
+ }
58
+ }
59
+
29
60
  async function runSharedAwPhase({ raw, steps, deps = {} }) {
30
61
  const runtime = {
31
62
  transformToClaude: deps.transformToClaude,
@@ -43,6 +74,7 @@ async function runSharedAwPhase({ raw, steps, deps = {} }) {
43
74
 
44
75
  const needsClaudeInput = steps.some(step => (step.payloadMode || 'claude') === 'claude');
45
76
  const claudeInput = needsClaudeInput ? runtime.transformToClaude(parsedInput) : null;
77
+ let result = raw;
46
78
 
47
79
  for (const step of steps) {
48
80
  if (!shouldRunStep(step, runtime)) {
@@ -50,10 +82,14 @@ async function runSharedAwPhase({ raw, steps, deps = {} }) {
50
82
  }
51
83
 
52
84
  const payload = resolveStepPayload(step, raw, parsedInput, claudeInput);
53
- await executeStep(runtime, step, payload);
85
+ const execution = await executeStep(runtime, step, payload);
86
+ const nextResult = resolveStepOutput(step, execution, raw);
87
+ if (typeof nextResult === 'string') {
88
+ result = nextResult;
89
+ }
54
90
  }
55
91
 
56
- return raw;
92
+ return result;
57
93
  }
58
94
 
59
95
  module.exports = {
@@ -1,71 +1,51 @@
1
1
  #!/usr/bin/env bash
2
- # UserPromptSubmit hook — emit a small, reliable AW routing reminder.
3
- #
4
- # This hook intentionally stays simple:
5
- # - drain stdin so the harness can always finish writing payloads
6
- # - remind the model to re-apply using-aw-skills
7
- # - point at AGENTS.md and the canonical .aw_rules/platform tree when present
8
-
9
2
  set -euo pipefail
10
3
 
11
- cat >/dev/null || true
12
-
13
- # On Windows (Git Bash / MSYS), pwd returns POSIX paths (/tmp/...)
14
- # but callers use native Windows paths (C:\Users\...).
15
- # Convert via cygpath when available so paths match the caller's format.
16
- if command -v cygpath >/dev/null 2>&1; then
17
- CWD="$(cygpath -w "$(pwd)")"
18
- else
19
- CWD="$(pwd)"
20
- fi
21
- AGENTS_PATH="$CWD/AGENTS.md"
22
- RULES_ROOT=""
23
-
24
- for candidate in \
25
- "$CWD/.aw_rules/platform" \
26
- "$HOME/.aw_rules/platform" \
27
- "$HOME/.aw/.aw_rules/platform"
28
- do
29
- if [ -d "$candidate" ]; then
30
- RULES_ROOT="$candidate"
31
- break
32
- fi
33
- done
34
-
35
- RULE_REFS=()
4
+ RAW="$(cat || true)"
5
+
6
+ extract_workspace_root() {
7
+ printf '%s' "$1" | sed -n 's/.*"workspace_roots"[[:space:]]*:[[:space:]]*\[[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1
8
+ }
9
+
10
+ extract_cwd() {
11
+ printf '%s' "$1" | sed -n 's/.*"cwd"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1
12
+ }
13
+
14
+ resolve_rules_root() {
15
+ local root="${1:-}"
16
+ local cwd_dir
17
+ cwd_dir="$(pwd)"
18
+ local candidate
19
+
20
+ for candidate in \
21
+ "$root/.aw/.aw_rules/platform" \
22
+ "$root/.aw_rules/platform" \
23
+ "$cwd_dir/.aw/.aw_rules/platform" \
24
+ "$cwd_dir/.aw_rules/platform" \
25
+ "$HOME/.aw/.aw_rules/platform" \
26
+ "$HOME/.aw_rules/platform" \
27
+ "$HOME/.aw/.aw_registry/.aw_rules/platform"
28
+ do
29
+ if [ -n "$candidate" ] && [ -d "$candidate" ]; then
30
+ printf '%s' "$candidate"
31
+ return 0
32
+ fi
33
+ done
36
34
 
37
- if [ -f "$AGENTS_PATH" ]; then
38
- RULE_REFS+=("$AGENTS_PATH")
39
- fi
35
+ return 1
36
+ }
40
37
 
41
- if [ -n "$RULES_ROOT" ]; then
42
- if [ -f "$RULES_ROOT/universal/AGENTS.md" ]; then
43
- RULE_REFS+=("$RULES_ROOT/universal/AGENTS.md")
44
- fi
45
- if [ -f "$RULES_ROOT/security/AGENTS.md" ]; then
46
- RULE_REFS+=("$RULES_ROOT/security/AGENTS.md")
47
- fi
48
- RULE_SCOPE="Applicable domain rules live under $RULES_ROOT."
49
- else
50
- RULE_SCOPE="Applicable domain rules live under .aw_rules/platform when synced."
38
+ WORKSPACE_ROOT="$(extract_workspace_root "$RAW")"
39
+ if [ -z "$WORKSPACE_ROOT" ]; then
40
+ WORKSPACE_ROOT="$(extract_cwd "$RAW")"
51
41
  fi
52
42
 
53
- if [ "${#RULE_REFS[@]}" -gt 0 ]; then
54
- RULE_PATHS=""
55
- while IFS= read -r line; do
56
- if [ -z "$RULE_PATHS" ]; then
57
- RULE_PATHS="$line"
58
- else
59
- RULE_PATHS="$RULE_PATHS, $line"
60
- fi
61
- done < <(printf '%s\n' "${RULE_REFS[@]}" | awk '!seen[$0]++')
62
- else
63
- RULE_PATHS="AGENTS.md"
43
+ RULES_ROOT="$(resolve_rules_root "$WORKSPACE_ROOT" || true)"
44
+ if [ -z "$RULES_ROOT" ]; then
45
+ RULES_ROOT="$HOME/.aw_rules/platform"
64
46
  fi
65
47
 
66
48
  cat <<EOF
67
- [AW Router reminder] Re-apply using-aw-skills for this prompt and select the smallest correct AW route and stage skill before substantive work.
68
- [Rules reminder] Start with $RULE_PATHS. Universal and security rules always apply. $RULE_SCOPE
49
+ [AW Router reminder] Re-apply using-aw-skills and select the smallest correct AW route before substantive work.
50
+ [Rule reminder] Read ${RULES_ROOT}/universal/AGENTS.md and ${RULES_ROOT}/security/AGENTS.md, then the touched domain AGENTS.md plus references/ on demand.
69
51
  EOF
70
-
71
- exit 0
@@ -3,14 +3,14 @@
3
3
  "hooks": {
4
4
  "sessionStart": [
5
5
  {
6
- "command": "node .cursor/hooks/session-start.js",
6
+ "command": "bash -lc \"for candidate in \\\"$PWD/.cursor/hooks/session-start.sh\\\" \\\"$HOME/.cursor/hooks/session-start.sh\\\"; do if [ -f \\\"$candidate\\\" ]; then exec bash \\\"$candidate\\\"; fi; done; exit 0\"",
7
7
  "event": "sessionStart",
8
8
  "description": "Load previous context and detect environment"
9
9
  }
10
10
  ],
11
11
  "sessionEnd": [
12
12
  {
13
- "command": "node .cursor/hooks/session-end.js",
13
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"session-end.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
14
14
  "event": "sessionEnd",
15
15
  "description": "Persist session state and evaluate patterns"
16
16
  }
@@ -22,91 +22,91 @@
22
22
  "description": "Block git hook-bypass flag to protect pre-commit, commit-msg, and pre-push hooks from being skipped"
23
23
  },
24
24
  {
25
- "command": "node .cursor/hooks/before-shell-execution.js",
25
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"before-shell-execution.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
26
26
  "event": "beforeShellExecution",
27
27
  "description": "Tmux dev server blocker, tmux reminder, git push review"
28
28
  }
29
29
  ],
30
30
  "afterShellExecution": [
31
31
  {
32
- "command": "node .cursor/hooks/after-shell-execution.js",
32
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"after-shell-execution.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
33
33
  "event": "afterShellExecution",
34
34
  "description": "PR URL logging, build analysis"
35
35
  }
36
36
  ],
37
37
  "afterFileEdit": [
38
38
  {
39
- "command": "node .cursor/hooks/after-file-edit.js",
39
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"after-file-edit.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
40
40
  "event": "afterFileEdit",
41
41
  "description": "Auto-format, TypeScript check, console.log warning"
42
42
  }
43
43
  ],
44
44
  "beforeMCPExecution": [
45
45
  {
46
- "command": "node .cursor/hooks/before-mcp-execution.js",
46
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"before-mcp-execution.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
47
47
  "event": "beforeMCPExecution",
48
48
  "description": "MCP audit logging and untrusted server warning"
49
49
  }
50
50
  ],
51
51
  "afterMCPExecution": [
52
52
  {
53
- "command": "node .cursor/hooks/after-mcp-execution.js",
53
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"after-mcp-execution.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
54
54
  "event": "afterMCPExecution",
55
55
  "description": "MCP result logging"
56
56
  }
57
57
  ],
58
58
  "beforeReadFile": [
59
59
  {
60
- "command": "node .cursor/hooks/before-read-file.js",
60
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"before-read-file.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
61
61
  "event": "beforeReadFile",
62
62
  "description": "Warn when reading sensitive files (.env, .key, .pem)"
63
63
  }
64
64
  ],
65
65
  "beforeSubmitPrompt": [
66
66
  {
67
- "command": "node .cursor/hooks/before-submit-prompt.js",
67
+ "command": "bash -lc \"for candidate in \\\"$PWD/.cursor/hooks/before-submit-prompt.sh\\\" \\\"$HOME/.cursor/hooks/before-submit-prompt.sh\\\"; do if [ -f \\\"$candidate\\\" ]; then exec bash \\\"$candidate\\\"; fi; done; exit 0\"",
68
68
  "event": "beforeSubmitPrompt",
69
69
  "description": "Detect secrets in prompts (sk-, ghp_, AKIA patterns)"
70
70
  }
71
71
  ],
72
72
  "subagentStart": [
73
73
  {
74
- "command": "node .cursor/hooks/subagent-start.js",
74
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"subagent-start.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
75
75
  "event": "subagentStart",
76
76
  "description": "Log agent spawning for observability"
77
77
  }
78
78
  ],
79
79
  "subagentStop": [
80
80
  {
81
- "command": "node .cursor/hooks/subagent-stop.js",
81
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"subagent-stop.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
82
82
  "event": "subagentStop",
83
83
  "description": "Log agent completion"
84
84
  }
85
85
  ],
86
86
  "beforeTabFileRead": [
87
87
  {
88
- "command": "node .cursor/hooks/before-tab-file-read.js",
88
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"before-tab-file-read.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
89
89
  "event": "beforeTabFileRead",
90
90
  "description": "Block Tab from reading secrets (.env, .key, .pem, credentials)"
91
91
  }
92
92
  ],
93
93
  "afterTabFileEdit": [
94
94
  {
95
- "command": "node .cursor/hooks/after-tab-file-edit.js",
95
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"after-tab-file-edit.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
96
96
  "event": "afterTabFileEdit",
97
97
  "description": "Auto-format Tab edits"
98
98
  }
99
99
  ],
100
100
  "preCompact": [
101
101
  {
102
- "command": "node .cursor/hooks/pre-compact.js",
102
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"pre-compact.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
103
103
  "event": "preCompact",
104
104
  "description": "Save state before context compaction"
105
105
  }
106
106
  ],
107
107
  "stop": [
108
108
  {
109
- "command": "node .cursor/hooks/stop.js",
109
+ "command": "node -e \"const fs=require('fs');const path=require('path');const os=require('os');const scriptName=\\\"stop.js\\\";const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)];const target=candidates.find(candidate => fs.existsSync(candidate));if(!target) process.exit(0);require(target)\"",
110
110
  "event": "stop",
111
111
  "description": "Console.log audit on all modified files"
112
112
  }
@@ -0,0 +1,43 @@
1
+ ---
2
+ description: "AW global routing: use the AW router skill first, then apply org rules and references by domain"
3
+ alwaysApply: true
4
+ ---
5
+ # AW Global Routing
6
+
7
+ - Before any substantive response, load `using-aw-skills` from `~/.cursor/skills/using-aw-skills/SKILL.md`.
8
+ - Select the smallest correct AW route first:
9
+ - `/aw:plan`
10
+ - `/aw:build`
11
+ - `/aw:investigate`
12
+ - `/aw:test`
13
+ - `/aw:review`
14
+ - `/aw:deploy`
15
+ - `/aw:ship`
16
+ - For non-trivial requests, the first substantive line should be `Selected AW Route: <route>`.
17
+ - After the route is known, load the matching AW stage skill from `~/.cursor/skills/` and only the smallest supporting skills needed.
18
+
19
+ ## Org Rules
20
+
21
+ - Always read:
22
+ - `~/.aw_rules/platform/universal/AGENTS.md`
23
+ - `~/.aw_rules/platform/security/AGENTS.md`
24
+ - Then pick the smallest correct domain rule based on the requirement and current context, and read it before acting:
25
+ - `api-design` -> `~/.aw_rules/platform/api-design/AGENTS.md`
26
+ - `backend` -> `~/.aw_rules/platform/backend/AGENTS.md`
27
+ - `data` -> `~/.aw_rules/platform/data/AGENTS.md`
28
+ - `frontend` -> `~/.aw_rules/platform/frontend/AGENTS.md`
29
+ - `infra` -> `~/.aw_rules/platform/infra/AGENTS.md`
30
+ - `mobile` -> `~/.aw_rules/platform/mobile/AGENTS.md`
31
+ - `sdet` -> `~/.aw_rules/platform/sdet/AGENTS.md`
32
+ - Load `references/` under the chosen domain on demand when implementation details or canonical links matter.
33
+ - If repo-local instructions conflict with org-level AW rules, follow the org-level AW rules.
34
+
35
+ ## Route Hints
36
+
37
+ - Ideas, architecture, specs, planning, new project setup, and database choice -> `/aw:plan`
38
+ - Implementation, bug fixing, and build work -> `/aw:build`
39
+ - Unclear failures, alerts, or root-cause hunting -> `/aw:investigate`
40
+ - QA proof, regression evidence, and validation -> `/aw:test`
41
+ - Findings, readiness, and approval decisions -> `/aw:review`
42
+ - One release action -> `/aw:deploy`
43
+ - Rollout, rollback readiness, and closeout -> `/aw:ship`
@@ -0,0 +1,75 @@
1
+ ---
2
+ name: api-and-interface-design
3
+ description: Designs stable APIs and module boundaries. Use when changing public contracts, service interfaces, component props, or any boundary another system or team depends on.
4
+ origin: ECC
5
+ ---
6
+
7
+ # API and Interface Design
8
+
9
+ ## Overview
10
+
11
+ Design interfaces that are stable, documented, and hard to misuse.
12
+ This applies to REST and GraphQL APIs, TypeScript contracts, event payloads, component props, database-informed boundaries, and service-to-service interfaces.
13
+
14
+ ## When to Use
15
+
16
+ - designing or changing API endpoints
17
+ - defining interfaces between modules, services, or teams
18
+ - shaping component props or shared library contracts
19
+ - planning data contracts that other code depends on
20
+ - changing existing public behavior that consumers may already rely on
21
+
22
+ **When NOT to use**
23
+
24
+ - the change is fully internal and does not cross a meaningful boundary
25
+ - the work is purely implementation with no contract implications
26
+
27
+ ## Workflow
28
+
29
+ 1. Name the boundary and its consumers first.
30
+ Clarify who consumes the interface and what kind of compatibility risk exists:
31
+ user clients, internal services, frontend callers, workers, or shared packages.
32
+ 2. Define the contract before the implementation.
33
+ Write the request and response shape, error semantics, idempotency rules, and ordering guarantees before coding the handler.
34
+ Use `../../references/interface-stability.md` and `api-design` when the surface is HTTP-facing.
35
+ 3. Treat observable behavior as part of the contract.
36
+ Apply Hyrum's Law thinking:
37
+ if consumers can observe it, they may depend on it.
38
+ Avoid leaking implementation details, inconsistent error patterns, or unstable defaults.
39
+ 4. Validate at the boundary, not everywhere.
40
+ Validate user input, third-party responses, environment configuration, and external payloads where they enter the system.
41
+ Keep internal code paths simpler once the boundary is trusted.
42
+ 5. Prefer extension over breaking change.
43
+ Add optional fields, new endpoints, new event versions, or adapters before removing or changing existing semantics.
44
+ If a breaking change is unavoidable, load `deprecation-and-migration`.
45
+ 6. Record the long-lived design decision.
46
+ For important public or architectural contracts, update docs or ADRs through `documentation-and-adrs`.
47
+ In GHL- or AW-governed repos, align the contract with `.aw_rules`, platform APIs, and baseline expectations.
48
+
49
+ ## Common Rationalizations
50
+
51
+ | Rationalization | Reality |
52
+ |---|---|
53
+ | "We can document the contract later." | The contract is the design. If it is unclear now, implementation will drift. |
54
+ | "Nobody depends on that behavior." | If it is observable, somebody eventually will. |
55
+ | "We can just support two versions forever." | Version sprawl multiplies maintenance and creates dependency pain. |
56
+ | "Validation everywhere is safer." | Boundary validation is safer and simpler than repeating checks throughout internal code. |
57
+
58
+ ## Red Flags
59
+
60
+ - different endpoints or modules expose inconsistent error behavior
61
+ - breaking changes are introduced without migration or compatibility planning
62
+ - boundary validation is missing for user or third-party input
63
+ - implementation details leak into public behavior or naming
64
+ - the team cannot explain what part of the behavior is contract versus accident
65
+
66
+ ## Verification
67
+
68
+ After designing or changing an interface, confirm:
69
+
70
+ - [ ] the consumers and compatibility surface are explicit
71
+ - [ ] the contract exists before or alongside implementation
72
+ - [ ] error semantics and validation boundaries are consistent
73
+ - [ ] additions were preferred over breaking changes where possible
74
+ - [ ] deprecation or migration is planned for any unavoidable break
75
+ - [ ] important interface decisions are documented for future engineers and agents