@ryuenn3123/agentic-senior-core 6.16.3 → 6.16.6
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/.agents/plugins/agentic-senior-core/.codex-plugin/plugin.json +1 -1
- package/.agents/plugins/agentic-senior-core/commands/asc-pentest.md +2 -0
- package/.agents/plugins/agentic-senior-core/commands/asc-pentest.toml +3 -0
- package/.agents/plugins/agentic-senior-core/commands/asc-test.md +1 -1
- package/.agents/plugins/agentic-senior-core/commands/asc-ui.md +1 -1
- package/.agents/plugins/agentic-senior-core/hooks/dedup-gate.js +137 -128
- package/.agents/plugins/agentic-senior-core/hooks/hooks.json +97 -0
- package/.agents/plugins/agentic-senior-core/hooks/ladder-pulse.js +53 -36
- package/.agents/plugins/agentic-senior-core/hooks/lib/known-ui-slop-patterns.json +5 -0
- package/.agents/plugins/agentic-senior-core/hooks/path-util.cjs +118 -4
- package/.agents/plugins/agentic-senior-core/hooks/post-edit-enforce.js +98 -81
- package/.agents/plugins/agentic-senior-core/hooks/pre-tool-dependency-gate.js +168 -191
- package/.agents/plugins/agentic-senior-core/hooks/session-pulse.js +48 -35
- package/.agents/plugins/agentic-senior-core/hooks/session-start.js +62 -11
- package/.agents/plugins/agentic-senior-core/hooks.json +4 -94
- package/.agents/plugins/agentic-senior-core/plugin.json +5 -13
- package/.agents/plugins/agentic-senior-core/rules/agentic-senior-core.md +3 -1
- package/.agents/plugins/agentic-senior-core/skills/asc/SKILL.md +5 -2
- package/.agents/plugins/agentic-senior-core/skills/asc/references/writing-skills.md +1 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-develop/SKILL.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/SKILL.md +97 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/api-security-idor.md +105 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/dast-baseline.md +78 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/fuzzing-dsl.md +66 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-pentest/references/pentest-task-tree.md +70 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-review/SKILL.md +1 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-test/SKILL.md +10 -3
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/mutation-testing.md +55 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/performance-budgets.md +67 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/property-testing.md +72 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/regression-proof.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/tdd-cycle.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-test/references/uat.md +1 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/SKILL.md +31 -34
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/accessibility.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/copy-i18n.md +24 -8
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/color-surfaces.md +32 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/composition-patterns.md +299 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/craft/themes-catalog.md +199 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/divergence.md +65 -24
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/experience.md +4 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/genre-calibration.md +93 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/operate.md +31 -1
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/persuade.md +33 -23
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/modes/read.md +4 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/redesign-audit.md +5 -10
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/states-feedback.md +2 -2
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-attio-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-attio.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-cal-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-cal.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-clerk-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-clerk.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-dub-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-dub.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-linear-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-linear.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-marketplace-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-marketplace.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-mintlify-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-mintlify.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-raycast-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-raycast.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-resend-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-resend.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-stripe-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-stripe.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-supabase-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-supabase.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-tldraw-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-tldraw.png +0 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-vercel-dom.json +5 -0
- package/.agents/plugins/agentic-senior-core/skills/asc-ui/references/visuals/real-vercel.png +0 -0
- package/bin/agentic-senior-core.js +19 -4
- package/gemini-extension.json +2 -2
- package/lib/cli/ascx/runtime.mjs +5 -27
- package/lib/cli/ascx/windows-spawn.mjs +348 -0
- package/lib/cli/commands/bootstrap.mjs +56 -0
- package/lib/cli/commands/dedup.mjs +500 -0
- package/lib/cli/commands/git-hook-generator.mjs +3 -2
- package/lib/cli/commands/global.mjs +109 -53
- package/lib/cli/commands/preview.mjs +34 -20
- package/package.json +1 -1
- package/plugin.yaml +3 -1
|
@@ -6,9 +6,7 @@ const os = require('os');
|
|
|
6
6
|
* Computes a stable path for the workflow gate file based on the project path.
|
|
7
7
|
* Normalizes the path (resolves absolute, lowercase, forward slashes, no trailing slash)
|
|
8
8
|
* to ensure identical hashes regardless of OS path separator or drive letter casing.
|
|
9
|
-
*
|
|
10
|
-
* accepting the negligible risk of collisions for the benefit of Windows stability.
|
|
11
|
-
*
|
|
9
|
+
*
|
|
12
10
|
* @param {string} projectPath - The root path of the project.
|
|
13
11
|
* @returns {string} Absolute path to the global workflow gate JSON file.
|
|
14
12
|
*/
|
|
@@ -18,6 +16,122 @@ function getWorkflowGatePath(projectPath) {
|
|
|
18
16
|
return path.join(os.homedir(), '.config', 'agentic-senior-core', 'gates', `${projectHash}.json`);
|
|
19
17
|
}
|
|
20
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Detects whether a hook stdin payload originates from Antigravity (App, CLI, or IDE).
|
|
21
|
+
*/
|
|
22
|
+
function isAntigravityPayload(data) {
|
|
23
|
+
if (!data || typeof data !== 'object') return false;
|
|
24
|
+
return Boolean(
|
|
25
|
+
data.toolCall ||
|
|
26
|
+
Array.isArray(data.workspacePaths) ||
|
|
27
|
+
typeof data.transcriptPath === 'string' ||
|
|
28
|
+
typeof data.invocationNum === 'number' ||
|
|
29
|
+
typeof data.executionNum === 'number'
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolves the active user workspace directory from hook stdin JSON.
|
|
35
|
+
* Antigravity executes hooks with cwd set to the directory containing hooks.json,
|
|
36
|
+
* so workspacePaths[0] or toolCall.args.Cwd must take precedence over process.cwd().
|
|
37
|
+
*/
|
|
38
|
+
function resolveWorkspaceRoot(data) {
|
|
39
|
+
if (data && typeof data === 'object') {
|
|
40
|
+
if (data.toolCall && data.toolCall.args && typeof data.toolCall.args.Cwd === 'string' && data.toolCall.args.Cwd.trim()) {
|
|
41
|
+
return path.resolve(data.toolCall.args.Cwd);
|
|
42
|
+
}
|
|
43
|
+
if (Array.isArray(data.workspacePaths) && data.workspacePaths.length > 0 && typeof data.workspacePaths[0] === 'string' && data.workspacePaths[0].trim()) {
|
|
44
|
+
return path.resolve(data.workspacePaths[0]);
|
|
45
|
+
}
|
|
46
|
+
if (typeof data.cwd === 'string' && data.cwd.trim()) {
|
|
47
|
+
return path.resolve(data.cwd);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return process.cwd();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Normalizes file edit/write tool calls across Antigravity (edit_file, write_file,
|
|
55
|
+
* replace_file_content, multi_replace_file_content, write_to_file) and Claude/Codex/Copilot (Edit, Write).
|
|
56
|
+
*/
|
|
57
|
+
function normalizeEditToolCall(toolName, rawArgs) {
|
|
58
|
+
const args = rawArgs && typeof rawArgs === 'object' ? rawArgs : {};
|
|
59
|
+
const name = String(toolName || '');
|
|
60
|
+
|
|
61
|
+
if (name === 'edit_file') {
|
|
62
|
+
return {
|
|
63
|
+
canonicalTool: 'Edit',
|
|
64
|
+
filePath: args.Path || args.file_path || '',
|
|
65
|
+
newContent: args.Replacement || '',
|
|
66
|
+
oldContent: args.Target || '',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (name === 'replace_file_content') {
|
|
71
|
+
return {
|
|
72
|
+
canonicalTool: 'Edit',
|
|
73
|
+
filePath: args.TargetFile || args.file_path || '',
|
|
74
|
+
newContent: args.ReplacementContent || '',
|
|
75
|
+
oldContent: args.TargetContent || '',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (name === 'multi_replace_file_content') {
|
|
80
|
+
const chunks = Array.isArray(args.ReplacementChunks) ? args.ReplacementChunks : [];
|
|
81
|
+
return {
|
|
82
|
+
canonicalTool: 'Edit',
|
|
83
|
+
filePath: args.TargetFile || args.file_path || '',
|
|
84
|
+
newContent: chunks.length > 0
|
|
85
|
+
? chunks.map(function (c) { return (c && c.ReplacementContent) || ''; }).join('\n')
|
|
86
|
+
: (args.ReplacementContent || ''),
|
|
87
|
+
oldContent: chunks.length > 0
|
|
88
|
+
? chunks.map(function (c) { return (c && c.TargetContent) || ''; }).join('\n')
|
|
89
|
+
: (args.TargetContent || ''),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (name === 'Edit') {
|
|
94
|
+
return {
|
|
95
|
+
canonicalTool: 'Edit',
|
|
96
|
+
filePath: args.file_path || args.path || args.TargetFile || args.Path || '',
|
|
97
|
+
newContent: args.new_string || args.ReplacementContent || args.Replacement || args.content || '',
|
|
98
|
+
oldContent: args.old_string || args.TargetContent || args.Target || '',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (name === 'write_file') {
|
|
103
|
+
return {
|
|
104
|
+
canonicalTool: 'Write',
|
|
105
|
+
filePath: args.Path || args.file_path || '',
|
|
106
|
+
newContent: args.Content || args.content || '',
|
|
107
|
+
oldContent: '',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (name === 'write_to_file') {
|
|
112
|
+
return {
|
|
113
|
+
canonicalTool: 'Write',
|
|
114
|
+
filePath: args.TargetFile || args.file_path || '',
|
|
115
|
+
newContent: args.CodeContent || args.content || '',
|
|
116
|
+
oldContent: '',
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (name === 'Write') {
|
|
121
|
+
return {
|
|
122
|
+
canonicalTool: 'Write',
|
|
123
|
+
filePath: args.file_path || args.path || args.TargetFile || args.Path || '',
|
|
124
|
+
newContent: args.content || args.CodeContent || args.Content || '',
|
|
125
|
+
oldContent: '',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
21
132
|
module.exports = {
|
|
22
|
-
getWorkflowGatePath
|
|
133
|
+
getWorkflowGatePath,
|
|
134
|
+
isAntigravityPayload,
|
|
135
|
+
resolveWorkspaceRoot,
|
|
136
|
+
normalizeEditToolCall,
|
|
23
137
|
};
|
|
@@ -55,6 +55,13 @@ const {
|
|
|
55
55
|
SESSION_DRIFT_THRESHOLD,
|
|
56
56
|
LADDER_PULSE_INTERVAL,
|
|
57
57
|
} = require('./constants.cjs');
|
|
58
|
+
const {
|
|
59
|
+
isAntigravityPayload,
|
|
60
|
+
resolveWorkspaceRoot,
|
|
61
|
+
normalizeEditToolCall,
|
|
62
|
+
} = require('./path-util.cjs');
|
|
63
|
+
|
|
64
|
+
let sourceEditCount = 0;
|
|
58
65
|
|
|
59
66
|
// Module-level counter for Claude Code path (resets per process spawn)
|
|
60
67
|
function logHook(event, msg) {
|
|
@@ -90,49 +97,63 @@ const stdinTimeout = setTimeout(() => {
|
|
|
90
97
|
process.exit(0);
|
|
91
98
|
}, 3000);
|
|
92
99
|
|
|
100
|
+
let handled = false;
|
|
93
101
|
let inputBuffer = '';
|
|
102
|
+
|
|
103
|
+
function dispatchInput(data) {
|
|
104
|
+
if (handled) return;
|
|
105
|
+
handled = true;
|
|
106
|
+
clearTimeout(stdinTimeout);
|
|
107
|
+
|
|
108
|
+
if (isAntigravityPayload(data)) {
|
|
109
|
+
if (data.stepIdx !== undefined) {
|
|
110
|
+
// Antigravity PostToolUse strictly expects {} on stdout
|
|
111
|
+
process.stdout.write('{}\n');
|
|
112
|
+
process.exit(0);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
handleAntigravityPostInvocation(data);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const workspaceRoot = resolveWorkspaceRoot(data);
|
|
120
|
+
const rawTool = data.tool_name || data.toolName || '';
|
|
121
|
+
const rawInput = data.tool_input || data.toolInput || {};
|
|
122
|
+
const norm = normalizeEditToolCall(rawTool, rawInput);
|
|
123
|
+
|
|
124
|
+
if (norm) {
|
|
125
|
+
const mappedInput = norm.canonicalTool === 'Edit'
|
|
126
|
+
? { file_path: norm.filePath, new_string: norm.newContent, old_string: norm.oldContent }
|
|
127
|
+
: { file_path: norm.filePath, content: norm.newContent };
|
|
128
|
+
processSingleEdit(norm.canonicalTool, mappedInput, function (nudge) {
|
|
129
|
+
emitClaude(nudge);
|
|
130
|
+
}, workspaceRoot, false);
|
|
131
|
+
}
|
|
132
|
+
process.exit(0);
|
|
133
|
+
}
|
|
134
|
+
|
|
94
135
|
process.stdin.setEncoding('utf8');
|
|
95
136
|
process.stdin.on('data', chunk => {
|
|
96
137
|
inputBuffer += chunk;
|
|
97
138
|
logHook('DataChunk', `len=${chunk.length} preview=${chunk.slice(0, 100).replace(/[\r\n]+/g, ' ')}`);
|
|
98
139
|
try {
|
|
99
140
|
const data = JSON.parse(inputBuffer);
|
|
100
|
-
clearTimeout(stdinTimeout);
|
|
101
141
|
logHook('StdinData', 'keys=' + Object.keys(data).join(','));
|
|
102
|
-
|
|
103
|
-
if (data.transcriptPath) {
|
|
104
|
-
handleAntigravityPostInvocation(data);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const toolName = data.tool_name || '';
|
|
109
|
-
const toolInput = data.tool_input || {};
|
|
110
|
-
processSingleEdit(toolName, toolInput, function (nudge) {
|
|
111
|
-
emitClaude(nudge);
|
|
112
|
-
});
|
|
113
|
-
process.exit(0);
|
|
142
|
+
dispatchInput(data);
|
|
114
143
|
} catch (e) {
|
|
115
144
|
// incomplete json, wait for more chunks
|
|
116
145
|
}
|
|
117
146
|
});
|
|
118
147
|
|
|
119
148
|
process.stdin.on('end', () => {
|
|
149
|
+
if (handled) return;
|
|
120
150
|
clearTimeout(stdinTimeout);
|
|
121
151
|
logHook('StdinEnd', `bufferLen=${inputBuffer.length}`);
|
|
122
152
|
if (inputBuffer.trim()) {
|
|
123
153
|
try {
|
|
124
154
|
const data = JSON.parse(inputBuffer);
|
|
125
155
|
logHook('ParsedInputOnEnd', 'keys=' + Object.keys(data).join(','));
|
|
126
|
-
|
|
127
|
-
handleAntigravityPostInvocation(data);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const toolName = data.tool_name || '';
|
|
131
|
-
const toolInput = data.tool_input || {};
|
|
132
|
-
processSingleEdit(toolName, toolInput, function (nudge) {
|
|
133
|
-
emitClaude(nudge);
|
|
134
|
-
});
|
|
135
|
-
process.exit(0);
|
|
156
|
+
dispatchInput(data);
|
|
136
157
|
} catch (e) {
|
|
137
158
|
logHook('JsonParseErrorOnEnd', e.message);
|
|
138
159
|
process.exit(0);
|
|
@@ -144,84 +165,78 @@ process.stdin.on('end', () => {
|
|
|
144
165
|
|
|
145
166
|
function handleAntigravityPostInvocation(data) {
|
|
146
167
|
try {
|
|
147
|
-
|
|
168
|
+
const workspaceRoot = resolveWorkspaceRoot(data);
|
|
169
|
+
if (!data.transcriptPath || !fs.existsSync(data.transcriptPath)) {
|
|
170
|
+
process.stdout.write('{}\n');
|
|
171
|
+
process.exit(0);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
148
175
|
const lines = fs.readFileSync(data.transcriptPath, 'utf8').split('\n').filter(Boolean);
|
|
149
176
|
const findings = [];
|
|
177
|
+
const minStep = typeof data.initialNumSteps === 'number' ? data.initialNumSteps : 0;
|
|
150
178
|
|
|
151
|
-
// Session drift check: count source file edits since initialNumSteps
|
|
152
179
|
let sourceEditsSinceStart = 0;
|
|
153
180
|
for (let i = 0; i < lines.length; i++) {
|
|
154
|
-
|
|
155
|
-
|
|
181
|
+
let step;
|
|
182
|
+
try {
|
|
183
|
+
step = JSON.parse(lines[i]);
|
|
184
|
+
} catch (_) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (step.step_index >= minStep && step.type === 'PLANNER_RESPONSE' && Array.isArray(step.tool_calls)) {
|
|
156
188
|
for (let j = 0; j < step.tool_calls.length; j++) {
|
|
157
189
|
const tc = step.tool_calls[j];
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (tc.name === 'replace_file_content' || tc.name === 'multi_replace_file_content') {
|
|
162
|
-
toolName = 'Edit';
|
|
163
|
-
toolInput = {
|
|
164
|
-
file_path: tc.args.TargetFile || '',
|
|
165
|
-
new_string: tc.args.ReplacementContent || '',
|
|
166
|
-
old_string: tc.args.TargetContent || ''
|
|
167
|
-
};
|
|
168
|
-
} else if (tc.name === 'write_to_file') {
|
|
169
|
-
toolName = 'Write';
|
|
170
|
-
toolInput = {
|
|
171
|
-
file_path: tc.args.TargetFile || '',
|
|
172
|
-
content: tc.args.CodeContent || ''
|
|
173
|
-
};
|
|
174
|
-
}
|
|
190
|
+
const norm = tc ? normalizeEditToolCall(tc.name, tc.args) : null;
|
|
191
|
+
if (!norm || !norm.filePath) continue;
|
|
175
192
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
const ext = path.extname(fp).slice(1);
|
|
179
|
-
if (SOURCE_EXTENSIONS.has(ext)) sourceEditsSinceStart++;
|
|
193
|
+
const ext = path.extname(norm.filePath).slice(1);
|
|
194
|
+
if (SOURCE_EXTENSIONS.has(ext)) sourceEditsSinceStart++;
|
|
180
195
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
196
|
+
const mappedInput = norm.canonicalTool === 'Edit'
|
|
197
|
+
? { file_path: norm.filePath, new_string: norm.newContent, old_string: norm.oldContent }
|
|
198
|
+
: { file_path: norm.filePath, content: norm.newContent };
|
|
199
|
+
|
|
200
|
+
processSingleEdit(norm.canonicalTool, mappedInput, function (nudge) {
|
|
201
|
+
findings.push(nudge);
|
|
202
|
+
}, workspaceRoot, true);
|
|
185
203
|
}
|
|
186
204
|
}
|
|
187
205
|
}
|
|
188
206
|
|
|
189
|
-
// Inject drift nudge if 4+ source files modified this invocation
|
|
190
207
|
if (sourceEditsSinceStart >= SESSION_DRIFT_THRESHOLD) {
|
|
191
208
|
findings.push('[ASC Session Drift] ' + sourceEditsSinceStart + ' source file edits this invocation. '
|
|
192
209
|
+ 'Re-read the decision ladder before continuing: (1) Does this need to be built? '
|
|
193
210
|
+ '(2) Does the codebase already have this? (3) Stdlib/native? (4) Existing dependency?');
|
|
194
211
|
}
|
|
195
212
|
|
|
196
|
-
|
|
197
|
-
logHook('AntigravityResult', `sourceEdits=${sourceEditsSinceStart}, findings=${findings.length}, isPostToolUse=${isPostToolUse}`);
|
|
213
|
+
logHook('AntigravityResult', `sourceEdits=${sourceEditsSinceStart}, findings=${findings.length}`);
|
|
198
214
|
|
|
199
|
-
if (
|
|
200
|
-
process.stdout.write(JSON.stringify({}) + '\n');
|
|
201
|
-
} else if (findings.length > 0) {
|
|
215
|
+
if (findings.length > 0) {
|
|
202
216
|
const injectSteps = findings.map(function (f) { return { ephemeralMessage: f }; });
|
|
203
217
|
process.stdout.write(JSON.stringify({ injectSteps: injectSteps }) + '\n');
|
|
204
218
|
} else {
|
|
205
|
-
process.stdout.write(
|
|
219
|
+
process.stdout.write('{}\n');
|
|
206
220
|
}
|
|
207
221
|
} catch (e) {
|
|
208
222
|
logHook('AntigravityError', e.message);
|
|
209
|
-
process.stdout.write(
|
|
223
|
+
process.stdout.write('{}\n');
|
|
210
224
|
}
|
|
211
225
|
process.exit(0);
|
|
212
226
|
}
|
|
213
227
|
|
|
214
|
-
function processSingleEdit(toolName, toolInput, emitFn,
|
|
215
|
-
const
|
|
216
|
-
if (!
|
|
228
|
+
function processSingleEdit(toolName, toolInput, emitFn, workspaceRoot, skipSessionCounter) {
|
|
229
|
+
const rawFilePath = toolInput.file_path || toolInput.TargetFile || toolInput.Path || toolInput.filePath || '';
|
|
230
|
+
if (!rawFilePath) return;
|
|
231
|
+
const root = workspaceRoot || process.cwd();
|
|
232
|
+
const filePath = path.isAbsolute(rawFilePath) ? rawFilePath : path.resolve(root, rawFilePath);
|
|
217
233
|
const findings = [];
|
|
218
234
|
|
|
219
|
-
const isEdit = toolName === 'Edit' || toolName === 'replace_file_content' || toolName === 'multi_replace_file_content';
|
|
220
|
-
const isWrite = toolName === 'Write' || toolName === 'write_to_file';
|
|
235
|
+
const isEdit = toolName === 'Edit' || toolName === 'edit_file' || toolName === 'replace_file_content' || toolName === 'multi_replace_file_content';
|
|
236
|
+
const isWrite = toolName === 'Write' || toolName === 'write_file' || toolName === 'write_to_file';
|
|
221
237
|
|
|
222
|
-
// Increment per-process counter for Claude Code drift detection
|
|
223
238
|
const ext = path.extname(filePath).slice(1);
|
|
224
|
-
if (SOURCE_EXTENSIONS.has(ext)) {
|
|
239
|
+
if (!skipSessionCounter && SOURCE_EXTENSIONS.has(ext)) {
|
|
225
240
|
sourceEditCount++;
|
|
226
241
|
if (sourceEditCount === SESSION_DRIFT_THRESHOLD) {
|
|
227
242
|
findings.push('[ASC Session Drift] ' + SESSION_DRIFT_THRESHOLD + '+ source file edits this session. '
|
|
@@ -252,13 +267,13 @@ function processSingleEdit(toolName, toolInput, emitFn, skipArray) {
|
|
|
252
267
|
checkUiSlopPatterns(toolName, toolInput, filePath, findings);
|
|
253
268
|
}
|
|
254
269
|
|
|
255
|
-
checkUniversalLinter(filePath, ext, findings);
|
|
270
|
+
checkUniversalLinter(filePath, ext, findings, root);
|
|
256
271
|
|
|
257
272
|
checkLivingDocNudge(filePath, findings);
|
|
258
|
-
checkTestTampering(filePath, findings);
|
|
273
|
+
checkTestTampering(filePath, findings, root);
|
|
259
274
|
|
|
260
275
|
if (ext !== 'md') {
|
|
261
|
-
checkWorkflowGate(toolName, filePath, ext, findings);
|
|
276
|
+
checkWorkflowGate(toolName, filePath, ext, findings, root);
|
|
262
277
|
}
|
|
263
278
|
|
|
264
279
|
logHook('ProcessSingleEdit', 'tool=' + toolName + ', file=' + filePath + ', findings=' + findings.length);
|
|
@@ -439,9 +454,9 @@ function checkStubPatterns(toolName, toolInput, filePath, findings) {
|
|
|
439
454
|
}
|
|
440
455
|
}
|
|
441
456
|
|
|
442
|
-
function checkUniversalLinter(filePath, ext, findings) {
|
|
457
|
+
function checkUniversalLinter(filePath, ext, findings, workspaceRoot) {
|
|
443
458
|
var execSync = require('child_process').execSync;
|
|
444
|
-
var cwd = process.cwd();
|
|
459
|
+
var cwd = workspaceRoot || process.cwd();
|
|
445
460
|
|
|
446
461
|
if (ext === 'js' || ext === 'ts' || ext === 'jsx' || ext === 'tsx' || ext === 'mjs' || ext === 'cjs') {
|
|
447
462
|
try {
|
|
@@ -519,14 +534,15 @@ function checkLivingDocNudge(filePath, findings) {
|
|
|
519
534
|
}
|
|
520
535
|
}
|
|
521
536
|
|
|
522
|
-
function checkTestTampering(filePath, findings) {
|
|
537
|
+
function checkTestTampering(filePath, findings, workspaceRoot) {
|
|
523
538
|
var isTest = /(?:\.test\.|\.spec\.|_test\.|__tests__[\/\\]|tests?[\/\\]).*\.(?:js|ts|jsx|tsx|py|go|rs|rb)$/i.test(filePath);
|
|
524
539
|
if (!isTest) return;
|
|
525
540
|
|
|
526
541
|
try {
|
|
542
|
+
var root = workspaceRoot || process.cwd();
|
|
527
543
|
var pathUtil = require('./path-util.cjs');
|
|
528
|
-
var localGate = path.join(
|
|
529
|
-
var gatePath = fs.existsSync(localGate) ? localGate : pathUtil.getWorkflowGatePath(
|
|
544
|
+
var localGate = path.join(root, 'workflow-gate.json');
|
|
545
|
+
var gatePath = fs.existsSync(localGate) ? localGate : pathUtil.getWorkflowGatePath(root);
|
|
530
546
|
if (!fs.existsSync(gatePath)) return;
|
|
531
547
|
|
|
532
548
|
var gate = JSON.parse(fs.readFileSync(gatePath, 'utf8'));
|
|
@@ -539,11 +555,12 @@ function checkTestTampering(filePath, findings) {
|
|
|
539
555
|
} catch (_) { }
|
|
540
556
|
}
|
|
541
557
|
|
|
542
|
-
function checkWorkflowGate(toolName, filePath, ext, findings) {
|
|
558
|
+
function checkWorkflowGate(toolName, filePath, ext, findings, workspaceRoot) {
|
|
543
559
|
try {
|
|
560
|
+
var root = workspaceRoot || process.cwd();
|
|
544
561
|
var pathUtil = require('./path-util.cjs');
|
|
545
|
-
var localGate = path.join(
|
|
546
|
-
var gatePath = fs.existsSync(localGate) ? localGate : pathUtil.getWorkflowGatePath(
|
|
562
|
+
var localGate = path.join(root, 'workflow-gate.json');
|
|
563
|
+
var gatePath = fs.existsSync(localGate) ? localGate : pathUtil.getWorkflowGatePath(root);
|
|
547
564
|
if (!fs.existsSync(gatePath)) return;
|
|
548
565
|
|
|
549
566
|
var gateStr = fs.readFileSync(gatePath, 'utf8');
|
|
@@ -564,16 +581,16 @@ function checkWorkflowGate(toolName, filePath, ext, findings) {
|
|
|
564
581
|
+ 'Stop and wait for phase approval. If you must proceed, log this bypass to the debt ledger.'
|
|
565
582
|
);
|
|
566
583
|
} else if (gate.phase === 'implement') {
|
|
567
|
-
validateDocSpecs(gate.workflow, findings);
|
|
584
|
+
validateDocSpecs(gate.workflow, findings, root);
|
|
568
585
|
}
|
|
569
586
|
} catch (_) {
|
|
570
587
|
// Silent fail
|
|
571
588
|
}
|
|
572
589
|
}
|
|
573
590
|
|
|
574
|
-
function validateDocSpecs(workflow, findings) {
|
|
591
|
+
function validateDocSpecs(workflow, findings, workspaceRoot) {
|
|
575
592
|
try {
|
|
576
|
-
var cwd = process.cwd();
|
|
593
|
+
var cwd = workspaceRoot || process.cwd();
|
|
577
594
|
var docsDir = path.join(cwd, 'docs');
|
|
578
595
|
|
|
579
596
|
// Anti-typo check for common doc typos (e.g. Architectyre.md)
|