aw-ecc 1.4.23 → 1.4.25
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 +37 -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 +3 -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 +37 -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
|
@@ -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"
|
|
@@ -2,4 +2,11 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
-
|
|
5
|
+
REMINDER="$(bash "$SCRIPT_DIR/shared/user-prompt-submit.sh" || true)"
|
|
6
|
+
|
|
7
|
+
if [[ -z "${REMINDER}" ]]; then
|
|
8
|
+
exit 0
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
JSON_REMINDER="$(printf '%s' "$REMINDER" | python3 -c 'import json, sys; print(json.dumps(sys.stdin.read()))')"
|
|
12
|
+
printf '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":%s}}\n' "$JSON_REMINDER"
|
|
@@ -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,48 @@
|
|
|
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_rules/platform" \
|
|
22
|
+
"$cwd_dir/.aw_rules/platform" \
|
|
23
|
+
"$HOME/.aw_rules/platform" \
|
|
24
|
+
"$HOME/.aw/.aw_registry/.aw_rules/platform"
|
|
25
|
+
do
|
|
26
|
+
if [ -n "$candidate" ] && [ -d "$candidate" ]; then
|
|
27
|
+
printf '%s' "$candidate"
|
|
28
|
+
return 0
|
|
29
|
+
fi
|
|
30
|
+
done
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
fi
|
|
32
|
+
return 1
|
|
33
|
+
}
|
|
40
34
|
|
|
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."
|
|
35
|
+
WORKSPACE_ROOT="$(extract_workspace_root "$RAW")"
|
|
36
|
+
if [ -z "$WORKSPACE_ROOT" ]; then
|
|
37
|
+
WORKSPACE_ROOT="$(extract_cwd "$RAW")"
|
|
51
38
|
fi
|
|
52
39
|
|
|
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"
|
|
40
|
+
RULES_ROOT="$(resolve_rules_root "$WORKSPACE_ROOT" || true)"
|
|
41
|
+
if [ -z "$RULES_ROOT" ]; then
|
|
42
|
+
RULES_ROOT="$HOME/.aw_rules/platform"
|
|
64
43
|
fi
|
|
65
44
|
|
|
66
45
|
cat <<EOF
|
|
67
|
-
[AW Router reminder] Re-apply using-aw-skills
|
|
68
|
-
[
|
|
46
|
+
[AW Router reminder] Re-apply using-aw-skills and select the smallest correct AW route before substantive work.
|
|
47
|
+
[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
48
|
EOF
|
|
70
|
-
|
|
71
|
-
exit 0
|
|
@@ -9,9 +9,11 @@ const CURSOR_AW_HOOK_FILES = Object.freeze([
|
|
|
9
9
|
'before-mcp-execution.js',
|
|
10
10
|
'before-shell-execution.js',
|
|
11
11
|
'before-submit-prompt.js',
|
|
12
|
+
'before-submit-prompt.sh',
|
|
12
13
|
'pre-compact.js',
|
|
13
14
|
'session-end.js',
|
|
14
15
|
'session-start.js',
|
|
16
|
+
'session-start.sh',
|
|
15
17
|
'stop.js',
|
|
16
18
|
]);
|
|
17
19
|
|
|
@@ -1,16 +1,46 @@
|
|
|
1
1
|
const { getCursorMappedEventNames } = require('./aw-hook-contract');
|
|
2
2
|
|
|
3
|
+
function buildManagedCursorHookCommand(scriptFileName) {
|
|
4
|
+
const scriptName = String(scriptFileName || '').replace(/\\/g, '/');
|
|
5
|
+
const launcher = [
|
|
6
|
+
"const fs=require('fs')",
|
|
7
|
+
"const path=require('path')",
|
|
8
|
+
"const os=require('os')",
|
|
9
|
+
`const scriptName=${JSON.stringify(scriptName)}`,
|
|
10
|
+
"const candidates=[path.join(process.cwd(), '.cursor', 'hooks', scriptName),path.join(os.homedir(), '.cursor', 'hooks', scriptName)]",
|
|
11
|
+
"const target=candidates.find(candidate => fs.existsSync(candidate))",
|
|
12
|
+
"if(!target) process.exit(0)",
|
|
13
|
+
"require(target)",
|
|
14
|
+
].join(';');
|
|
15
|
+
|
|
16
|
+
return `node -e ${JSON.stringify(launcher)}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function buildManagedCursorShellCommand(scriptFileName) {
|
|
20
|
+
const scriptName = String(scriptFileName || '').replace(/\\/g, '/');
|
|
21
|
+
const command = [
|
|
22
|
+
'bash -lc',
|
|
23
|
+
JSON.stringify(
|
|
24
|
+
`for candidate in "$PWD/.cursor/hooks/${scriptName}" "$HOME/.cursor/hooks/${scriptName}"; do ` +
|
|
25
|
+
`if [ -f "$candidate" ]; then exec bash "$candidate"; fi; ` +
|
|
26
|
+
'done; exit 0'
|
|
27
|
+
),
|
|
28
|
+
].join(' ');
|
|
29
|
+
|
|
30
|
+
return command;
|
|
31
|
+
}
|
|
32
|
+
|
|
3
33
|
const CURSOR_HOOK_ENTRIES = Object.freeze({
|
|
4
34
|
sessionStart: [
|
|
5
35
|
{
|
|
6
|
-
command: '
|
|
36
|
+
command: buildManagedCursorShellCommand('session-start.sh'),
|
|
7
37
|
event: 'sessionStart',
|
|
8
38
|
description: 'Load previous context and detect environment',
|
|
9
39
|
},
|
|
10
40
|
],
|
|
11
41
|
sessionEnd: [
|
|
12
42
|
{
|
|
13
|
-
command: '
|
|
43
|
+
command: buildManagedCursorHookCommand('session-end.js'),
|
|
14
44
|
event: 'sessionEnd',
|
|
15
45
|
description: 'Persist session state and evaluate patterns',
|
|
16
46
|
},
|
|
@@ -22,91 +52,91 @@ const CURSOR_HOOK_ENTRIES = Object.freeze({
|
|
|
22
52
|
description: 'Block git hook-bypass flag to protect pre-commit, commit-msg, and pre-push hooks from being skipped',
|
|
23
53
|
},
|
|
24
54
|
{
|
|
25
|
-
command: '
|
|
55
|
+
command: buildManagedCursorHookCommand('before-shell-execution.js'),
|
|
26
56
|
event: 'beforeShellExecution',
|
|
27
57
|
description: 'Tmux dev server blocker, tmux reminder, git push review',
|
|
28
58
|
},
|
|
29
59
|
],
|
|
30
60
|
afterShellExecution: [
|
|
31
61
|
{
|
|
32
|
-
command: '
|
|
62
|
+
command: buildManagedCursorHookCommand('after-shell-execution.js'),
|
|
33
63
|
event: 'afterShellExecution',
|
|
34
64
|
description: 'PR URL logging, build analysis',
|
|
35
65
|
},
|
|
36
66
|
],
|
|
37
67
|
afterFileEdit: [
|
|
38
68
|
{
|
|
39
|
-
command: '
|
|
69
|
+
command: buildManagedCursorHookCommand('after-file-edit.js'),
|
|
40
70
|
event: 'afterFileEdit',
|
|
41
71
|
description: 'Auto-format, TypeScript check, console.log warning',
|
|
42
72
|
},
|
|
43
73
|
],
|
|
44
74
|
beforeMCPExecution: [
|
|
45
75
|
{
|
|
46
|
-
command: '
|
|
76
|
+
command: buildManagedCursorHookCommand('before-mcp-execution.js'),
|
|
47
77
|
event: 'beforeMCPExecution',
|
|
48
78
|
description: 'MCP audit logging and untrusted server warning',
|
|
49
79
|
},
|
|
50
80
|
],
|
|
51
81
|
afterMCPExecution: [
|
|
52
82
|
{
|
|
53
|
-
command: '
|
|
83
|
+
command: buildManagedCursorHookCommand('after-mcp-execution.js'),
|
|
54
84
|
event: 'afterMCPExecution',
|
|
55
85
|
description: 'MCP result logging',
|
|
56
86
|
},
|
|
57
87
|
],
|
|
58
88
|
beforeReadFile: [
|
|
59
89
|
{
|
|
60
|
-
command: '
|
|
90
|
+
command: buildManagedCursorHookCommand('before-read-file.js'),
|
|
61
91
|
event: 'beforeReadFile',
|
|
62
92
|
description: 'Warn when reading sensitive files (.env, .key, .pem)',
|
|
63
93
|
},
|
|
64
94
|
],
|
|
65
95
|
beforeSubmitPrompt: [
|
|
66
96
|
{
|
|
67
|
-
command: '
|
|
97
|
+
command: buildManagedCursorShellCommand('before-submit-prompt.sh'),
|
|
68
98
|
event: 'beforeSubmitPrompt',
|
|
69
99
|
description: 'Detect secrets in prompts (sk-, ghp_, AKIA patterns)',
|
|
70
100
|
},
|
|
71
101
|
],
|
|
72
102
|
subagentStart: [
|
|
73
103
|
{
|
|
74
|
-
command: '
|
|
104
|
+
command: buildManagedCursorHookCommand('subagent-start.js'),
|
|
75
105
|
event: 'subagentStart',
|
|
76
106
|
description: 'Log agent spawning for observability',
|
|
77
107
|
},
|
|
78
108
|
],
|
|
79
109
|
subagentStop: [
|
|
80
110
|
{
|
|
81
|
-
command: '
|
|
111
|
+
command: buildManagedCursorHookCommand('subagent-stop.js'),
|
|
82
112
|
event: 'subagentStop',
|
|
83
113
|
description: 'Log agent completion',
|
|
84
114
|
},
|
|
85
115
|
],
|
|
86
116
|
beforeTabFileRead: [
|
|
87
117
|
{
|
|
88
|
-
command: '
|
|
118
|
+
command: buildManagedCursorHookCommand('before-tab-file-read.js'),
|
|
89
119
|
event: 'beforeTabFileRead',
|
|
90
120
|
description: 'Block Tab from reading secrets (.env, .key, .pem, credentials)',
|
|
91
121
|
},
|
|
92
122
|
],
|
|
93
123
|
afterTabFileEdit: [
|
|
94
124
|
{
|
|
95
|
-
command: '
|
|
125
|
+
command: buildManagedCursorHookCommand('after-tab-file-edit.js'),
|
|
96
126
|
event: 'afterTabFileEdit',
|
|
97
127
|
description: 'Auto-format Tab edits',
|
|
98
128
|
},
|
|
99
129
|
],
|
|
100
130
|
preCompact: [
|
|
101
131
|
{
|
|
102
|
-
command: '
|
|
132
|
+
command: buildManagedCursorHookCommand('pre-compact.js'),
|
|
103
133
|
event: 'preCompact',
|
|
104
134
|
description: 'Save state before context compaction',
|
|
105
135
|
},
|
|
106
136
|
],
|
|
107
137
|
stop: [
|
|
108
138
|
{
|
|
109
|
-
command: '
|
|
139
|
+
command: buildManagedCursorHookCommand('stop.js'),
|
|
110
140
|
event: 'stop',
|
|
111
141
|
description: 'Console.log audit on all modified files',
|
|
112
142
|
},
|
|
@@ -144,5 +174,7 @@ module.exports = {
|
|
|
144
174
|
CURSOR_HOOK_ENTRIES,
|
|
145
175
|
buildCursorHookEntries,
|
|
146
176
|
buildCursorHookConfig,
|
|
177
|
+
buildManagedCursorHookCommand,
|
|
178
|
+
buildManagedCursorShellCommand,
|
|
147
179
|
serializeCursorHookConfig,
|
|
148
180
|
};
|
|
@@ -418,6 +418,13 @@ function planCursorLegacyInstall(context) {
|
|
|
418
418
|
sourceRelativeDir: path.join('.cursor', 'commands'),
|
|
419
419
|
destinationDir: path.join(targetRoot, 'commands'),
|
|
420
420
|
});
|
|
421
|
+
addRecursiveCopyOperations(operations, {
|
|
422
|
+
moduleId: 'legacy-cursor-install',
|
|
423
|
+
sourceRoot: context.sourceRoot,
|
|
424
|
+
sourceRelativeDir: path.join('scripts', 'hooks'),
|
|
425
|
+
destinationDir: path.join(targetRoot, 'scripts', 'hooks'),
|
|
426
|
+
strategy: 'sync-root-children',
|
|
427
|
+
});
|
|
421
428
|
addRecursiveCopyOperations(operations, {
|
|
422
429
|
moduleId: 'legacy-cursor-install',
|
|
423
430
|
sourceRoot: context.sourceRoot,
|
|
@@ -49,6 +49,13 @@ module.exports = createInstallTargetAdapter({
|
|
|
49
49
|
if (sourceRelativePath === '.cursor') {
|
|
50
50
|
return [
|
|
51
51
|
adapter.createScaffoldOperation(module.id, sourceRelativePath, planningInput),
|
|
52
|
+
createRemappedOperation(
|
|
53
|
+
adapter,
|
|
54
|
+
module.id,
|
|
55
|
+
path.join('scripts', 'hooks'),
|
|
56
|
+
path.join(targetRoot, 'scripts', 'hooks'),
|
|
57
|
+
{ strategy: 'sync-root-children' }
|
|
58
|
+
),
|
|
52
59
|
createRemappedOperation(
|
|
53
60
|
adapter,
|
|
54
61
|
module.id,
|