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.
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor/INSTALL.md +9 -0
- package/.cursor/hooks/adapter.js +34 -3
- package/.cursor/hooks/aw-phase-definitions.js +11 -0
- package/.cursor/hooks/before-submit-prompt.sh +17 -0
- package/.cursor/hooks/session-start.sh +36 -0
- package/.cursor/hooks/shared/aw-phase-definitions.js +1 -19
- package/.cursor/hooks/shared/aw-phase-runner.js +38 -2
- package/.cursor/hooks/shared/user-prompt-submit.sh +40 -60
- package/.cursor/hooks.json +15 -15
- package/.cursor/rules/common-aw-routing.md +43 -0
- package/.cursor/skills/api-and-interface-design/SKILL.md +75 -0
- package/.cursor/skills/aw-brainstorm/SKILL.md +115 -0
- package/.cursor/skills/aw-build/SKILL.md +152 -0
- package/.cursor/skills/aw-build/evals/build-stage-cases.json +28 -0
- package/.cursor/skills/aw-debug/SKILL.md +49 -0
- package/.cursor/skills/aw-deploy/SKILL.md +101 -0
- package/.cursor/skills/aw-deploy/evals/deploy-stage-cases.json +32 -0
- package/.cursor/skills/aw-execute/SKILL.md +47 -0
- package/.cursor/skills/aw-execute/references/mode-code.md +47 -0
- package/.cursor/skills/aw-execute/references/mode-docs.md +28 -0
- package/.cursor/skills/aw-execute/references/mode-infra.md +44 -0
- package/.cursor/skills/aw-execute/references/mode-migration.md +58 -0
- package/.cursor/skills/aw-execute/references/worker-implementer.md +26 -0
- package/.cursor/skills/aw-execute/references/worker-parallel-worker.md +23 -0
- package/.cursor/skills/aw-execute/references/worker-quality-reviewer.md +23 -0
- package/.cursor/skills/aw-execute/references/worker-spec-reviewer.md +23 -0
- package/.cursor/skills/aw-execute/scripts/build-worker-bundle.js +229 -0
- package/.cursor/skills/aw-finish/SKILL.md +111 -0
- package/.cursor/skills/aw-investigate/SKILL.md +109 -0
- package/.cursor/skills/aw-plan/SKILL.md +368 -0
- package/.cursor/skills/aw-prepare/SKILL.md +118 -0
- package/.cursor/skills/aw-review/SKILL.md +118 -0
- package/.cursor/skills/aw-ship/SKILL.md +115 -0
- package/.cursor/skills/aw-spec/SKILL.md +104 -0
- package/.cursor/skills/aw-tasks/SKILL.md +138 -0
- package/.cursor/skills/aw-test/SKILL.md +118 -0
- package/.cursor/skills/aw-verify/SKILL.md +51 -0
- package/.cursor/skills/aw-yolo/SKILL.md +111 -0
- package/.cursor/skills/browser-testing-with-devtools/SKILL.md +81 -0
- package/.cursor/skills/ci-cd-and-automation/SKILL.md +71 -0
- package/.cursor/skills/code-simplification/SKILL.md +74 -0
- package/.cursor/skills/context-engineering/SKILL.md +74 -0
- package/.cursor/skills/deprecation-and-migration/SKILL.md +75 -0
- package/.cursor/skills/documentation-and-adrs/SKILL.md +75 -0
- package/.cursor/skills/frontend-ui-engineering/SKILL.md +68 -0
- package/.cursor/skills/git-workflow-and-versioning/SKILL.md +75 -0
- package/.cursor/skills/idea-refine/SKILL.md +84 -0
- package/.cursor/skills/incremental-implementation/SKILL.md +75 -0
- package/.cursor/skills/performance-optimization/SKILL.md +77 -0
- package/.cursor/skills/security-and-hardening/SKILL.md +70 -0
- package/.cursor/skills/using-aw-skills/SKILL.md +290 -0
- package/.cursor/skills/using-aw-skills/evals/skill-trigger-cases.tsv +25 -0
- package/.cursor/skills/using-aw-skills/evals/test-skill-triggers.sh +171 -0
- package/.cursor/skills/using-aw-skills/hooks/hooks.json +9 -0
- package/.cursor/skills/using-aw-skills/hooks/session-start.sh +67 -0
- package/.cursor/skills/using-platform-skills/SKILL.md +163 -0
- package/.cursor/skills/using-platform-skills/evals/platform-selection-cases.json +52 -0
- package/.opencode/package.json +1 -1
- package/package.json +5 -1
- package/scripts/cursor-aw-home/hooks.json +15 -15
- package/scripts/cursor-aw-hooks/adapter.js +34 -3
- package/scripts/cursor-aw-hooks/aw-phase-definitions.js +11 -0
- package/scripts/cursor-aw-hooks/before-submit-prompt.sh +17 -0
- package/scripts/cursor-aw-hooks/session-start.sh +36 -0
- package/scripts/hooks/session-start-rules-context.sh +8 -1
- package/scripts/hooks/shared/aw-phase-definitions.js +1 -19
- package/scripts/hooks/shared/aw-phase-runner.js +38 -2
- package/scripts/hooks/shared/user-prompt-submit.sh +40 -60
- package/scripts/lib/cursor-aw-hook-files.js +2 -0
- package/scripts/lib/cursor-hook-config.js +47 -15
- package/scripts/lib/install-executor.js +7 -0
- package/scripts/lib/install-targets/cursor-project.js +7 -0
package/.cursor/INSTALL.md
CHANGED
|
@@ -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:
|
package/.cursor/hooks/adapter.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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,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
|
|
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
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
fi
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
68
|
-
[
|
|
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
|
package/.cursor/hooks.json
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
"hooks": {
|
|
4
4
|
"sessionStart": [
|
|
5
5
|
{
|
|
6
|
-
"command": "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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": "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|