claude-code-extensions 0.1.0 → 0.1.2
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/dist/ast.d.ts +43 -0
- package/dist/ast.js +308 -0
- package/dist/ast.js.map +1 -0
- package/dist/cli-list.d.ts +5 -0
- package/dist/cli-list.js +33 -0
- package/dist/cli-list.js.map +1 -0
- package/dist/cli-reload.d.ts +7 -0
- package/dist/cli-reload.js +24 -0
- package/dist/cli-reload.js.map +1 -0
- package/dist/cli-setup.d.ts +14 -0
- package/dist/cli-setup.js +110 -0
- package/dist/cli-setup.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +183 -0
- package/dist/cli.js.map +1 -0
- package/dist/patch-worker.d.ts +6 -0
- package/dist/patch-worker.js +27 -0
- package/dist/patch-worker.js.map +1 -0
- package/dist/patches/always-show-context.d.ts +23 -0
- package/dist/patches/always-show-context.js +97 -0
- package/dist/patches/always-show-context.js.map +1 -0
- package/dist/patches/always-show-thinking.d.ts +18 -0
- package/dist/patches/always-show-thinking.js +55 -0
- package/dist/patches/always-show-thinking.js.map +1 -0
- package/dist/patches/banner.d.ts +10 -0
- package/dist/patches/banner.js +105 -0
- package/dist/patches/banner.js.map +1 -0
- package/dist/patches/cd-command.d.ts +16 -0
- package/dist/patches/cd-command.js +89 -0
- package/dist/patches/cd-command.js.map +1 -0
- package/dist/patches/cx-badge.d.ts +10 -0
- package/dist/patches/cx-badge.js +115 -0
- package/dist/patches/cx-badge.js.map +1 -0
- package/dist/patches/cx-resume-commands.d.ts +14 -0
- package/dist/patches/cx-resume-commands.js +53 -0
- package/dist/patches/cx-resume-commands.js.map +1 -0
- package/dist/patches/disable-paste-collapse.d.ts +16 -0
- package/dist/patches/disable-paste-collapse.js +49 -0
- package/dist/patches/disable-paste-collapse.js.map +1 -0
- package/dist/patches/disable-telemetry.d.ts +13 -0
- package/dist/patches/disable-telemetry.js +76 -0
- package/dist/patches/disable-telemetry.js.map +1 -0
- package/{patches/index.js → dist/patches/index.d.ts} +2 -0
- package/dist/patches/index.js +20 -0
- package/dist/patches/index.js.map +1 -0
- package/dist/patches/no-attribution.d.ts +15 -0
- package/dist/patches/no-attribution.js +42 -0
- package/dist/patches/no-attribution.js.map +1 -0
- package/dist/patches/no-feedback.d.ts +12 -0
- package/dist/patches/no-feedback.js +31 -0
- package/dist/patches/no-feedback.js.map +1 -0
- package/dist/patches/no-npm-warning.d.ts +9 -0
- package/dist/patches/no-npm-warning.js +31 -0
- package/dist/patches/no-npm-warning.js.map +1 -0
- package/dist/patches/no-tips.d.ts +10 -0
- package/dist/patches/no-tips.js +26 -0
- package/dist/patches/no-tips.js.map +1 -0
- package/dist/patches/persist-max-effort.d.ts +15 -0
- package/dist/patches/persist-max-effort.js +75 -0
- package/dist/patches/persist-max-effort.js.map +1 -0
- package/dist/patches/queue.d.ts +10 -0
- package/dist/patches/queue.js +202 -0
- package/dist/patches/queue.js.map +1 -0
- package/dist/patches/random-clawd.d.ts +9 -0
- package/dist/patches/random-clawd.js +49 -0
- package/dist/patches/random-clawd.js.map +1 -0
- package/dist/patches/reload.d.ts +10 -0
- package/dist/patches/reload.js +50 -0
- package/dist/patches/reload.js.map +1 -0
- package/dist/patches/show-file-in-collapsed-read.d.ts +17 -0
- package/dist/patches/show-file-in-collapsed-read.js +151 -0
- package/dist/patches/show-file-in-collapsed-read.js.map +1 -0
- package/dist/patches/simple-spinner.d.ts +12 -0
- package/dist/patches/simple-spinner.js +31 -0
- package/dist/patches/simple-spinner.js.map +1 -0
- package/dist/patches/swap-enter-submit.d.ts +26 -0
- package/dist/patches/swap-enter-submit.js +155 -0
- package/dist/patches/swap-enter-submit.js.map +1 -0
- package/dist/setup.d.ts +11 -0
- package/dist/setup.js +268 -0
- package/dist/setup.js.map +1 -0
- package/dist/transform-worker.d.ts +10 -0
- package/dist/transform-worker.js +35 -0
- package/dist/transform-worker.js.map +1 -0
- package/dist/transform.d.ts +12 -0
- package/dist/transform.js +83 -0
- package/dist/transform.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +19 -11
- package/ast.js +0 -276
- package/cx +0 -228
- package/cx-setup +0 -101
- package/patch-worker.js +0 -31
- package/patches/always-show-context.js +0 -123
- package/patches/always-show-thinking.js +0 -65
- package/patches/banner.js +0 -58
- package/patches/cd-command.js +0 -104
- package/patches/cx-badge.js +0 -112
- package/patches/cx-resume-commands.js +0 -58
- package/patches/disable-paste-collapse.js +0 -52
- package/patches/disable-telemetry.js +0 -84
- package/patches/no-attribution.js +0 -55
- package/patches/no-npm-warning.js +0 -32
- package/patches/no-tips.js +0 -29
- package/patches/persist-max-effort.js +0 -70
- package/patches/queue.js +0 -215
- package/patches/random-clawd.js +0 -52
- package/patches/reload.js +0 -68
- package/patches/show-file-in-collapsed-read.js +0 -178
- package/patches/swap-enter-submit.js +0 -188
- package/setup.js +0 -222
- package/transform-worker.js +0 -38
- package/transform.js +0 -99
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* No Attribution Patch
|
|
3
|
-
*
|
|
4
|
-
* Strips all Claude Code attribution from commits and PRs:
|
|
5
|
-
* - Removes "Co-Authored-By: <model> <noreply@anthropic.com>" from commits
|
|
6
|
-
* - Removes "🤖 Generated with Claude Code" from PR descriptions
|
|
7
|
-
* - Removes enhanced attribution ("X% N-shotted by ...") from PRs
|
|
8
|
-
*
|
|
9
|
-
* Works by patching getAttributionTexts() and getEnhancedPRAttribution()
|
|
10
|
-
* to return empty strings, which the prompt templates already handle
|
|
11
|
-
* gracefully via ternary guards.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export default {
|
|
15
|
-
id: 'no-attribution',
|
|
16
|
-
name: 'No Attribution',
|
|
17
|
-
description: 'Strip Claude Code attribution from commits and PRs',
|
|
18
|
-
|
|
19
|
-
apply(ctx) {
|
|
20
|
-
const { ast, editor, find, index, assert } = ctx;
|
|
21
|
-
|
|
22
|
-
// ── getAttributionTexts ──────────────────────────────────────────
|
|
23
|
-
// Returns { commit: "Co-Authored-By: ...", pr: "🤖 Generated..." }
|
|
24
|
-
// Find via TemplateLiteral quasis containing "noreply@anthropic.com"
|
|
25
|
-
|
|
26
|
-
const noreplyQuasi = find.findFirst(ast, n =>
|
|
27
|
-
n.type === 'TemplateElement' &&
|
|
28
|
-
n.value?.cooked?.includes('noreply@anthropic.com'));
|
|
29
|
-
assert(noreplyQuasi, 'Could not find TemplateElement with noreply@anthropic.com');
|
|
30
|
-
|
|
31
|
-
const getAttrTexts = index.enclosingFunction(noreplyQuasi);
|
|
32
|
-
assert(getAttrTexts, 'Could not find enclosing function for noreply quasi');
|
|
33
|
-
assert(getAttrTexts.body.type === 'BlockStatement',
|
|
34
|
-
'getAttributionTexts body is not a BlockStatement');
|
|
35
|
-
|
|
36
|
-
// Insert early return at start of function body
|
|
37
|
-
editor.insertAt(getAttrTexts.body.start + 1, 'return{commit:"",pr:""};');
|
|
38
|
-
|
|
39
|
-
// ── getEnhancedPRAttribution ─────────────────────────────────────
|
|
40
|
-
// Returns "🤖 Generated with Claude Code (X% N-shotted by ...)"
|
|
41
|
-
// Find via the unique debug string literal it contains
|
|
42
|
-
|
|
43
|
-
const enhancedFns = index.findFunctionsContainingStrings(
|
|
44
|
-
ast, 'PR Attribution: returning default (no data)');
|
|
45
|
-
assert(enhancedFns.length >= 1,
|
|
46
|
-
'Could not find getEnhancedPRAttribution (PR Attribution debug string)');
|
|
47
|
-
|
|
48
|
-
const getEnhanced = enhancedFns[0];
|
|
49
|
-
assert(getEnhanced.body.type === 'BlockStatement',
|
|
50
|
-
'getEnhancedPRAttribution body is not a BlockStatement');
|
|
51
|
-
|
|
52
|
-
// Insert early return (async fn, so return "" resolves to Promise<"">)
|
|
53
|
-
editor.insertAt(getEnhanced.body.start + 1, 'return"";');
|
|
54
|
-
},
|
|
55
|
-
};
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* No NPM Warning Patch
|
|
3
|
-
*
|
|
4
|
-
* Suppresses the "Claude Code has switched from npm to native installer"
|
|
5
|
-
* notification that appears on every startup for npm installs.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export default {
|
|
9
|
-
id: 'no-npm-warning',
|
|
10
|
-
name: 'No NPM Warning',
|
|
11
|
-
description: 'Suppress the "switched from npm to native installer" nag',
|
|
12
|
-
|
|
13
|
-
apply(ctx) {
|
|
14
|
-
const { ast, editor, find, assert } = ctx;
|
|
15
|
-
const { findFirst } = find;
|
|
16
|
-
|
|
17
|
-
// Find the ReturnStatement that returns {key:"npm-deprecation-warning",...}
|
|
18
|
-
const ret = findFirst(ast, n => {
|
|
19
|
-
if (n.type !== 'ReturnStatement') return false;
|
|
20
|
-
const arg = n.argument;
|
|
21
|
-
if (!arg || arg.type !== 'ObjectExpression') return false;
|
|
22
|
-
return arg.properties.some(p =>
|
|
23
|
-
p.type === 'Property' &&
|
|
24
|
-
p.key?.type === 'Identifier' && p.key.name === 'key' &&
|
|
25
|
-
p.value?.type === 'Literal' && p.value.value === 'npm-deprecation-warning');
|
|
26
|
-
});
|
|
27
|
-
assert(ret, 'Could not find return with key:"npm-deprecation-warning"');
|
|
28
|
-
|
|
29
|
-
// Replace the entire return statement with `return null`
|
|
30
|
-
editor.replaceRange(ret.start, ret.end, 'return null');
|
|
31
|
-
},
|
|
32
|
-
};
|
package/patches/no-tips.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* No Tips Patch
|
|
3
|
-
*
|
|
4
|
-
* Removes the "Tip: ..." messages shown in the spinner while Claude
|
|
5
|
-
* is thinking. Neutralises the effectiveTip variable so neither the
|
|
6
|
-
* scheduled tips nor the time-based /clear and /btw tips appear.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
id: 'no-tips',
|
|
11
|
-
name: 'No Tips',
|
|
12
|
-
description: 'Hide spinner tips',
|
|
13
|
-
|
|
14
|
-
apply(ctx) {
|
|
15
|
-
const { index, editor, assert } = ctx;
|
|
16
|
-
|
|
17
|
-
// The effectiveTip computation contains this unique string literal.
|
|
18
|
-
// Find it and walk up to the VariableDeclarator to replace the init.
|
|
19
|
-
const marker = 'Use /clear to start fresh when switching topics and free up context';
|
|
20
|
-
const hits = index.literalsByValue.get(marker) || [];
|
|
21
|
-
assert(hits.length > 0, `Could not find "${marker}" literal`);
|
|
22
|
-
|
|
23
|
-
const lit = hits[0];
|
|
24
|
-
const decl = index.ancestor(lit, 'VariableDeclarator');
|
|
25
|
-
assert(decl && decl.init, 'Could not find enclosing VariableDeclarator');
|
|
26
|
-
|
|
27
|
-
editor.replaceRange(decl.init.start, decl.init.end, 'void 0');
|
|
28
|
-
},
|
|
29
|
-
};
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Persist Max Effort Patch
|
|
3
|
-
*
|
|
4
|
-
* The public build strips "max" from toPersistableEffort(), so `/effort max`
|
|
5
|
-
* only lasts for the current session. This patch restores the "max" check
|
|
6
|
-
* so it persists to settings.json and survives restarts.
|
|
7
|
-
*
|
|
8
|
-
* Source (effort.ts):
|
|
9
|
-
* if (value === 'low' || value === 'medium' || value === 'high' || value === 'max')
|
|
10
|
-
* Bundle (stripped):
|
|
11
|
-
* if (q === "low" || q === "medium" || q === "high")
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export default {
|
|
15
|
-
id: 'persist-max-effort',
|
|
16
|
-
name: 'Persist Max Effort',
|
|
17
|
-
description: 'Save "max" effort to settings so it survives restarts',
|
|
18
|
-
|
|
19
|
-
apply(ctx) {
|
|
20
|
-
const { ast, editor, find, src, assert } = ctx;
|
|
21
|
-
const { findFirst, findAll } = find;
|
|
22
|
-
|
|
23
|
-
// Find the toPersistableEffort function. It's a function whose body has
|
|
24
|
-
// exactly the pattern: q==="low"||q==="medium"||q==="high"
|
|
25
|
-
// This is a unique 3-way string comparison in the bundle.
|
|
26
|
-
const persistFn = findFirst(ast, n => {
|
|
27
|
-
if (n.type !== 'FunctionDeclaration' && n.type !== 'FunctionExpression' && n.type !== 'ArrowFunctionExpression') return false;
|
|
28
|
-
// Find the LogicalExpression chain: q==="low" || q==="medium" || q==="high"
|
|
29
|
-
return findFirst(n, inner => {
|
|
30
|
-
if (inner.type !== 'LogicalExpression' || inner.operator !== '||') return false;
|
|
31
|
-
// The rightmost comparison should be q==="high"
|
|
32
|
-
if (inner.right.type !== 'BinaryExpression') return false;
|
|
33
|
-
if (inner.right.operator !== '===') return false;
|
|
34
|
-
if (inner.right.right?.type !== 'Literal' || inner.right.right.value !== 'high') return false;
|
|
35
|
-
// The left side should be another || with "medium"
|
|
36
|
-
if (inner.left.type !== 'LogicalExpression' || inner.left.operator !== '||') return false;
|
|
37
|
-
if (inner.left.right?.type !== 'BinaryExpression') return false;
|
|
38
|
-
if (inner.left.right.right?.type !== 'Literal' || inner.left.right.right.value !== 'medium') return false;
|
|
39
|
-
return true;
|
|
40
|
-
}) !== null;
|
|
41
|
-
});
|
|
42
|
-
assert(persistFn, 'Could not find toPersistableEffort function (low||medium||high pattern)');
|
|
43
|
-
|
|
44
|
-
// Find the "high" literal in the comparison chain
|
|
45
|
-
const highLiteral = findFirst(persistFn, n => {
|
|
46
|
-
if (n.type !== 'BinaryExpression' || n.operator !== '===') return false;
|
|
47
|
-
return n.right?.type === 'Literal' && n.right.value === 'high';
|
|
48
|
-
});
|
|
49
|
-
assert(highLiteral, 'Could not find q==="high" comparison');
|
|
50
|
-
|
|
51
|
-
// Get the parameter name (e.g., 'q') from the left side
|
|
52
|
-
const paramName = highLiteral.left.type === 'Identifier' ? highLiteral.left.name : src(highLiteral.left);
|
|
53
|
-
|
|
54
|
-
// Insert ||q==="max" after the q==="high" comparison
|
|
55
|
-
editor.insertAt(highLiteral.end, `||${paramName}==="max"`);
|
|
56
|
-
|
|
57
|
-
// Fix ["low","medium","high"] arrays (Zod schema + EFFORT_LEVELS) that also
|
|
58
|
-
// strip "max". Without this, settings.json validation silently drops "max"
|
|
59
|
-
// on read via .catch(undefined).
|
|
60
|
-
const effortArrays = findAll(ast, n => {
|
|
61
|
-
if (n.type !== 'ArrayExpression' || n.elements.length !== 3) return false;
|
|
62
|
-
const vals = n.elements.map(e => e?.type === 'Literal' ? e.value : null);
|
|
63
|
-
return vals[0] === 'low' && vals[1] === 'medium' && vals[2] === 'high';
|
|
64
|
-
});
|
|
65
|
-
assert(effortArrays.length > 0, 'Could not find ["low","medium","high"] arrays to patch');
|
|
66
|
-
for (const arr of effortArrays) {
|
|
67
|
-
editor.insertAt(arr.elements[2].end, ',"max"');
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
};
|
package/patches/queue.js
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ctrl+Q Message Queue Patch
|
|
3
|
-
*
|
|
4
|
-
* Adds a "queue" feature: pressing Ctrl+Q enqueues the current input
|
|
5
|
-
* with priority 'later', so it executes FIFO after the current turn
|
|
6
|
-
* completes (one queued command per turn).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
id: 'queue',
|
|
11
|
-
name: 'Ctrl+Q Message Queue',
|
|
12
|
-
description: 'Queue messages with Ctrl+Q to run sequentially after current turn',
|
|
13
|
-
|
|
14
|
-
apply(ctx) {
|
|
15
|
-
const { ast, editor, find, query, src, assert } = ctx;
|
|
16
|
-
const { findFirst, findAll } = find;
|
|
17
|
-
const {
|
|
18
|
-
findArrayWithConsecutiveStrings,
|
|
19
|
-
findObjectWithStringProps,
|
|
20
|
-
findHookCallWithObjectKeys,
|
|
21
|
-
findFunctionsContainingStrings,
|
|
22
|
-
getDestructuredName,
|
|
23
|
-
} = query;
|
|
24
|
-
|
|
25
|
-
// ── Variable Discovery ────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
// chatHandlers useMemo (by string keys in its object)
|
|
28
|
-
const chatHandlersMemo = findHookCallWithObjectKeys(ast, 'useMemo', [
|
|
29
|
-
'chat:undo', 'chat:newline', 'chat:stash', 'chat:imagePaste',
|
|
30
|
-
]);
|
|
31
|
-
assert(chatHandlersMemo, 'Could not find chatHandlers useMemo');
|
|
32
|
-
const R = src(chatHandlersMemo.callee.object); // React namespace
|
|
33
|
-
|
|
34
|
-
const handlersObject = findFirst(chatHandlersMemo.arguments[0], n =>
|
|
35
|
-
n.type === 'ObjectExpression' &&
|
|
36
|
-
n.properties.some(p => p.type === 'Property' && p.key.type === 'Literal' && p.key.value === 'chat:undo'));
|
|
37
|
-
assert(handlersObject, 'Could not find handlers object in useMemo');
|
|
38
|
-
|
|
39
|
-
const depsArray = chatHandlersMemo.arguments[1];
|
|
40
|
-
assert(depsArray?.type === 'ArrayExpression', 'Could not find deps array');
|
|
41
|
-
|
|
42
|
-
// PromptInput function (by ObjectPattern param with known prop keys)
|
|
43
|
-
const promptInputFn = findFirst(ast, node => {
|
|
44
|
-
if (node.type !== 'FunctionDeclaration' && node.type !== 'FunctionExpression') return false;
|
|
45
|
-
const p = node.params[0];
|
|
46
|
-
if (!p || p.type !== 'ObjectPattern') return false;
|
|
47
|
-
return getDestructuredName(p, 'input') !== null &&
|
|
48
|
-
getDestructuredName(p, 'mode') !== null &&
|
|
49
|
-
getDestructuredName(p, 'pastedContents') !== null;
|
|
50
|
-
});
|
|
51
|
-
assert(promptInputFn, 'Could not find PromptInput component function');
|
|
52
|
-
const propsPattern = promptInputFn.params[0];
|
|
53
|
-
|
|
54
|
-
// Props from destructured parameter (keys preserved in minified code)
|
|
55
|
-
const v = {};
|
|
56
|
-
for (const name of ['input', 'mode', 'pastedContents', 'setPastedContents', 'onInputChange']) {
|
|
57
|
-
v[name] = getDestructuredName(propsPattern, name);
|
|
58
|
-
assert(v[name], `Could not find "${name}" in props destructuring`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// setCursorOffset — from useState(input.length)
|
|
62
|
-
const useStateCandidates = findAll(promptInputFn, node => {
|
|
63
|
-
if (node.type !== 'VariableDeclarator' || node.id.type !== 'ArrayPattern' || node.id.elements.length < 2) return false;
|
|
64
|
-
const init = node.init;
|
|
65
|
-
if (!init || init.type !== 'CallExpression' || init.callee.type !== 'MemberExpression' || init.callee.property.name !== 'useState') return false;
|
|
66
|
-
const arg = init.arguments[0];
|
|
67
|
-
return arg?.type === 'MemberExpression' && arg.property.name === 'length' && arg.object.name === v.input;
|
|
68
|
-
});
|
|
69
|
-
assert(useStateCandidates.length === 1, `Expected 1 useState(${v.input}.length), found ${useStateCandidates.length}`);
|
|
70
|
-
v.setCursorOffset = useStateCandidates[0].id.elements[1].name;
|
|
71
|
-
|
|
72
|
-
// trackAndSetInput — useCallback with deps=[onInputChange]
|
|
73
|
-
const trackCandidates = findAll(promptInputFn, node => {
|
|
74
|
-
if (node.type !== 'VariableDeclarator' || node.id.type !== 'Identifier') return false;
|
|
75
|
-
const init = node.init;
|
|
76
|
-
if (!init || init.type !== 'CallExpression' || init.callee.type !== 'MemberExpression' || init.callee.property.name !== 'useCallback') return false;
|
|
77
|
-
const deps = init.arguments[1];
|
|
78
|
-
if (!deps || deps.type !== 'ArrayExpression' || deps.elements.length !== 1) return false;
|
|
79
|
-
return deps.elements[0]?.name === v.onInputChange;
|
|
80
|
-
});
|
|
81
|
-
assert(trackCandidates.length === 1, `Expected 1 useCallback with deps=[${v.onInputChange}]`);
|
|
82
|
-
v.trackAndSetInput = trackCandidates[0].id.name;
|
|
83
|
-
|
|
84
|
-
// clearBuffer — from useInputBuffer destructuring (key preserved)
|
|
85
|
-
const clearBufDecls = findAll(promptInputFn, node => {
|
|
86
|
-
if (node.type !== 'VariableDeclarator' || node.id.type !== 'ObjectPattern') return false;
|
|
87
|
-
return node.id.properties.some(p => p.type === 'Property' &&
|
|
88
|
-
((p.key.type === 'Identifier' && p.key.name === 'clearBuffer') ||
|
|
89
|
-
(p.key.type === 'Literal' && p.key.value === 'clearBuffer')));
|
|
90
|
-
});
|
|
91
|
-
assert(clearBufDecls.length >= 1, 'Could not find clearBuffer destructuring');
|
|
92
|
-
v.clearBuffer = getDestructuredName(clearBufDecls[0].id, 'clearBuffer');
|
|
93
|
-
|
|
94
|
-
// enqueue — function with push({...spread, priority: ?? "next"}) + "enqueue" string
|
|
95
|
-
const enqueueCandidates = findFunctionsContainingStrings(ast, 'enqueue');
|
|
96
|
-
const enqueueMatches = enqueueCandidates.filter(fn =>
|
|
97
|
-
findFirst(fn, n => {
|
|
98
|
-
if (n.type !== 'CallExpression' || n.callee.type !== 'MemberExpression' || n.callee.property.name !== 'push') return false;
|
|
99
|
-
const arg = n.arguments[0];
|
|
100
|
-
if (!arg || arg.type !== 'ObjectExpression') return false;
|
|
101
|
-
if (!arg.properties.some(p => p.type === 'SpreadElement')) return false;
|
|
102
|
-
const pp = arg.properties.find(p => p.type === 'Property' && p.key.name === 'priority');
|
|
103
|
-
return pp?.value.type === 'LogicalExpression' && pp.value.operator === '??' &&
|
|
104
|
-
pp.value.right.type === 'Literal' && pp.value.right.value === 'next';
|
|
105
|
-
}) !== null);
|
|
106
|
-
assert(enqueueMatches.length === 1, `Expected 1 enqueue function, found ${enqueueMatches.length}`);
|
|
107
|
-
v.enqueue = ctx.getFunctionName(enqueueMatches[0]);
|
|
108
|
-
assert(v.enqueue, 'Could not determine enqueue function name');
|
|
109
|
-
|
|
110
|
-
// expandPastedTextRefs — backward loop + .slice + .type/"text" + .index + .match
|
|
111
|
-
const expandCandidates = findFunctionsContainingStrings(ast, 'text');
|
|
112
|
-
const expandMatches = expandCandidates.filter(fn => {
|
|
113
|
-
if (fn.params.length !== 2) return false;
|
|
114
|
-
if (!findFirst(fn, n => n.type === 'ForStatement' && n.update?.type === 'UpdateExpression' && n.update.operator === '--')) return false;
|
|
115
|
-
if (findAll(fn, n => n.type === 'CallExpression' && n.callee.type === 'MemberExpression' && n.callee.property.name === 'slice').length < 2) return false;
|
|
116
|
-
if (!findFirst(fn, n => n.type === 'MemberExpression' && n.property.name === 'index')) return false;
|
|
117
|
-
if (!findFirst(fn, n => n.type === 'MemberExpression' && n.property.name === 'match')) return false;
|
|
118
|
-
return findFirst(fn, n => {
|
|
119
|
-
if (n.type !== 'BinaryExpression' || (n.operator !== '!==' && n.operator !== '===')) return false;
|
|
120
|
-
const unwrap = x => x.type === 'ChainExpression' ? x.expression : x;
|
|
121
|
-
const l = unwrap(n.left), r = unwrap(n.right);
|
|
122
|
-
return (l.type === 'MemberExpression' && l.property.name === 'type' && n.right.value === 'text') ||
|
|
123
|
-
(r.type === 'MemberExpression' && r.property.name === 'type' && n.left.value === 'text');
|
|
124
|
-
}) !== null;
|
|
125
|
-
});
|
|
126
|
-
assert(expandMatches.length === 1, `Expected 1 expandPastedTextRefs, found ${expandMatches.length}`);
|
|
127
|
-
v.expandPastedTextRefs = ctx.getFunctionName(expandMatches[0]);
|
|
128
|
-
assert(v.expandPastedTextRefs, 'Could not determine expandPastedTextRefs name');
|
|
129
|
-
|
|
130
|
-
// ── Patch 1: KEYBINDING_ACTIONS ───────────────────────────────────
|
|
131
|
-
|
|
132
|
-
const arr = findArrayWithConsecutiveStrings(ast, 'chat:submit', 'chat:newline');
|
|
133
|
-
assert(arr, 'Could not find KEYBINDING_ACTIONS array');
|
|
134
|
-
const el = arr.elements.find(e => e.type === 'Literal' && e.value === 'chat:submit');
|
|
135
|
-
editor.insertAt(el.end, ',"chat:queue"');
|
|
136
|
-
|
|
137
|
-
// ── Patch 2: DEFAULT_BINDINGS ─────────────────────────────────────
|
|
138
|
-
|
|
139
|
-
const obj = findObjectWithStringProps(ast, [['enter', 'chat:submit'], ['up', 'history:previous']]);
|
|
140
|
-
assert(obj, 'Could not find DEFAULT_BINDINGS object');
|
|
141
|
-
const enterProp = obj.properties.find(p => p.type === 'Property' &&
|
|
142
|
-
((p.key.type === 'Identifier' && p.key.name === 'enter') || (p.key.type === 'Literal' && p.key.value === 'enter')));
|
|
143
|
-
editor.insertAt(enterProp.end, ',"ctrl+q":"chat:queue"');
|
|
144
|
-
|
|
145
|
-
// ── Patch 3: handleQueue callback + chatHandlers ──────────────────
|
|
146
|
-
|
|
147
|
-
const qH = '__qH';
|
|
148
|
-
const code =
|
|
149
|
-
`let ${qH}=${R}.useCallback(()=>{` +
|
|
150
|
-
`let __t=${v.input}.trimEnd();if(__t==="")return;` +
|
|
151
|
-
`let __f=${v.expandPastedTextRefs}(__t,${v.pastedContents});` +
|
|
152
|
-
`${v.enqueue}({value:__f,preExpansionValue:__t,mode:${v.mode},` +
|
|
153
|
-
`priority:"later",pastedContents:Object.values(${v.pastedContents})` +
|
|
154
|
-
`.some(c=>c.type==="image")?${v.pastedContents}:void 0});` +
|
|
155
|
-
`${v.trackAndSetInput}(""),${v.setCursorOffset}(0),` +
|
|
156
|
-
`${v.setPastedContents}({}),${v.clearBuffer}()` +
|
|
157
|
-
`},[${v.input},${v.pastedContents},${v.mode},${v.trackAndSetInput},` +
|
|
158
|
-
`${v.setCursorOffset},${v.setPastedContents},${v.clearBuffer}]);`;
|
|
159
|
-
|
|
160
|
-
const memoDecl = findFirst(ast, n => n.type === 'VariableDeclaration' && n.declarations.some(d => d.init === chatHandlersMemo));
|
|
161
|
-
assert(memoDecl, 'Could not find VariableDeclaration for chatHandlers useMemo');
|
|
162
|
-
editor.insertAt(memoDecl.start, code);
|
|
163
|
-
|
|
164
|
-
const lastProp = handlersObject.properties[handlersObject.properties.length - 1];
|
|
165
|
-
editor.insertAt(lastProp.end, `,"chat:queue":${qH}`);
|
|
166
|
-
|
|
167
|
-
const lastDep = depsArray.elements[depsArray.elements.length - 1];
|
|
168
|
-
editor.insertAt(lastDep.end, `,${qH}`);
|
|
169
|
-
|
|
170
|
-
// ── Patch 4: processQueueIfReady ──────────────────────────────────
|
|
171
|
-
|
|
172
|
-
const processQueueFn = findFirst(ast, node => {
|
|
173
|
-
if (node.type !== 'FunctionDeclaration' && node.type !== 'FunctionExpression' && node.type !== 'ArrowFunctionExpression') return false;
|
|
174
|
-
if (findAll(node, n => n.type === 'Property' && n.key.type === 'Identifier' && n.key.name === 'processed').length < 2) return false;
|
|
175
|
-
return findFirst(node, n => n.type === 'BinaryExpression' && n.operator === '===' && n.right.type === 'Literal' && n.right.value === 'bash') !== null;
|
|
176
|
-
});
|
|
177
|
-
assert(processQueueFn, 'Could not find processQueueIfReady function');
|
|
178
|
-
|
|
179
|
-
const ifStmt = findFirst(processQueueFn, node => {
|
|
180
|
-
if (node.type !== 'IfStatement') return false;
|
|
181
|
-
return findFirst(node.test, n => n.type === 'BinaryExpression' && n.operator === '===' &&
|
|
182
|
-
n.left.type === 'MemberExpression' && n.left.property.name === 'mode' &&
|
|
183
|
-
n.right.type === 'Literal' && n.right.value === 'bash') !== null;
|
|
184
|
-
});
|
|
185
|
-
assert(ifStmt, 'Could not find if-statement with .mode === "bash"');
|
|
186
|
-
|
|
187
|
-
const bashCheck = findFirst(ifStmt.test, n => n.type === 'BinaryExpression' && n.operator === '===' &&
|
|
188
|
-
n.left.type === 'MemberExpression' && n.left.property.name === 'mode' && n.right.value === 'bash');
|
|
189
|
-
const nextVar = src(bashCheck.left.object);
|
|
190
|
-
editor.insertAt(ifStmt.test.end, `||${nextVar}.priority==="later"`);
|
|
191
|
-
|
|
192
|
-
const targetModeDecl = findFirst(processQueueFn, node => {
|
|
193
|
-
if (node.type !== 'VariableDeclarator' || node.start <= ifStmt.end) return false;
|
|
194
|
-
return node.init?.type === 'MemberExpression' && node.init.property.name === 'mode' && src(node.init.object) === nextVar;
|
|
195
|
-
});
|
|
196
|
-
assert(targetModeDecl, 'Could not find targetMode = next.mode');
|
|
197
|
-
const targetModeVar = targetModeDecl.id.name;
|
|
198
|
-
editor.insertAt(targetModeDecl.end, `,__p=${nextVar}.priority??"next"`);
|
|
199
|
-
|
|
200
|
-
const dqamCallback = findFirst(processQueueFn, node => {
|
|
201
|
-
if (node.type !== 'ArrowFunctionExpression' && node.type !== 'FunctionExpression') return false;
|
|
202
|
-
if (node.start <= ifStmt.end) return false;
|
|
203
|
-
return findFirst(node, n => n.type === 'BinaryExpression' && n.operator === '===' &&
|
|
204
|
-
n.left.type === 'MemberExpression' && n.left.property.name === 'mode' &&
|
|
205
|
-
n.right.type === 'Identifier' && n.right.name === targetModeVar) !== null;
|
|
206
|
-
});
|
|
207
|
-
assert(dqamCallback, 'Could not find dequeueAllMatching callback');
|
|
208
|
-
|
|
209
|
-
const modeComparison = findFirst(dqamCallback, n => n.type === 'BinaryExpression' && n.operator === '===' &&
|
|
210
|
-
n.left.type === 'MemberExpression' && n.left.property.name === 'mode' &&
|
|
211
|
-
n.right.type === 'Identifier' && n.right.name === targetModeVar);
|
|
212
|
-
const cbParam = dqamCallback.params[0].name;
|
|
213
|
-
editor.insertAt(modeComparison.end, `&&(${cbParam}.priority??"next")===__p`);
|
|
214
|
-
},
|
|
215
|
-
};
|
package/patches/random-clawd.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Random Clawd Color Patch
|
|
3
|
-
*
|
|
4
|
-
* Picks a random color for the Clawd mascot on each startup
|
|
5
|
-
* instead of the default orange theme color.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export default {
|
|
9
|
-
id: 'random-clawd',
|
|
10
|
-
name: 'Random Clawd Color',
|
|
11
|
-
description: 'Randomize the Clawd mascot color on each startup',
|
|
12
|
-
|
|
13
|
-
apply(ctx) {
|
|
14
|
-
const { ast, editor, find, assert } = ctx;
|
|
15
|
-
const { findFirst, findAll } = find;
|
|
16
|
-
|
|
17
|
-
// Find the Clawd component: function oM6(q) { ... }
|
|
18
|
-
// Identified by containing the strings "clawd_body" and "clawd_background"
|
|
19
|
-
// and the feet characters "▘▘ ▝▝".
|
|
20
|
-
const clawdFn = findFirst(ast, n => {
|
|
21
|
-
if (n.type !== 'FunctionDeclaration') return false;
|
|
22
|
-
let hasClawd = false, hasFeet = false;
|
|
23
|
-
for (const child of find.walkAST(n.body)) {
|
|
24
|
-
if (child.type === 'Literal') {
|
|
25
|
-
if (child.value === 'clawd_body') hasClawd = true;
|
|
26
|
-
if (child.value === '▘▘ ▝▝') hasFeet = true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return hasClawd && hasFeet;
|
|
30
|
-
});
|
|
31
|
-
assert(clawdFn, 'Could not find Clawd component function');
|
|
32
|
-
|
|
33
|
-
// Inject a random color picker before the function
|
|
34
|
-
const colors = [
|
|
35
|
-
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
|
|
36
|
-
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9',
|
|
37
|
-
'#E74C3C', '#2ECC71', '#3498DB', '#E67E22', '#1ABC9C',
|
|
38
|
-
'#9B59B6', '#F39C12', '#00BCD4', '#FF5722', '#8BC34A',
|
|
39
|
-
];
|
|
40
|
-
const colorArray = JSON.stringify(colors);
|
|
41
|
-
editor.insertAt(clawdFn.start,
|
|
42
|
-
`var __rc=${colorArray}[Math.floor(Math.random()*${colors.length})];`);
|
|
43
|
-
|
|
44
|
-
// Replace all "clawd_body" literals within the function with __rc
|
|
45
|
-
const clawdBodyLiterals = findAll(clawdFn, n =>
|
|
46
|
-
n.type === 'Literal' && n.value === 'clawd_body');
|
|
47
|
-
|
|
48
|
-
for (const lit of clawdBodyLiterals) {
|
|
49
|
-
editor.replaceRange(lit.start, lit.end, '__rc');
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
};
|
package/patches/reload.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ctrl+X Ctrl+R Reload Patch
|
|
3
|
-
*
|
|
4
|
-
* Adds a "reload" keybinding: pressing Ctrl+X Ctrl+R exits Claude with
|
|
5
|
-
* exit code 75. The cx wrapper catches this and re-spawns with --continue,
|
|
6
|
-
* picking up any patch changes while preserving the conversation.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
id: 'reload',
|
|
11
|
-
name: 'Ctrl+X Ctrl+R Reload',
|
|
12
|
-
description: 'Reload cx session with Ctrl+X Ctrl+R (re-applies patches, keeps conversation)',
|
|
13
|
-
|
|
14
|
-
apply(ctx) {
|
|
15
|
-
const { ast, editor, find, query, src, assert } = ctx;
|
|
16
|
-
const { findFirst } = find;
|
|
17
|
-
const {
|
|
18
|
-
findArrayWithConsecutiveStrings,
|
|
19
|
-
findObjectWithStringProps,
|
|
20
|
-
findHookCallWithObjectKeys,
|
|
21
|
-
} = query;
|
|
22
|
-
|
|
23
|
-
// ── 1. KEYBINDING_ACTIONS: register "chat:reload" ────────────────
|
|
24
|
-
|
|
25
|
-
const actionsArr = findArrayWithConsecutiveStrings(ast, 'chat:submit', 'chat:newline');
|
|
26
|
-
assert(actionsArr, 'Could not find KEYBINDING_ACTIONS array');
|
|
27
|
-
const newlineEl = actionsArr.elements.find(e => e.type === 'Literal' && e.value === 'chat:newline');
|
|
28
|
-
editor.insertAt(newlineEl.end, ',"chat:reload"');
|
|
29
|
-
|
|
30
|
-
// ── 2. DEFAULT_BINDINGS: bind Ctrl+X Ctrl+R → chat:reload ────────
|
|
31
|
-
|
|
32
|
-
const bindingsObj = findObjectWithStringProps(ast, [['enter', 'chat:submit'], ['up', 'history:previous']]);
|
|
33
|
-
assert(bindingsObj, 'Could not find DEFAULT_BINDINGS Chat object');
|
|
34
|
-
const upProp = bindingsObj.properties.find(p => p.type === 'Property' &&
|
|
35
|
-
((p.key.type === 'Identifier' && p.key.name === 'up') ||
|
|
36
|
-
(p.key.type === 'Literal' && p.key.value === 'up')));
|
|
37
|
-
editor.insertAt(upProp.end, ',"ctrl+x ctrl+r":"chat:reload"');
|
|
38
|
-
|
|
39
|
-
// ── 3. Handler: process.exit(75) in chatHandlers useMemo ─────────
|
|
40
|
-
|
|
41
|
-
const chatHandlersMemo = findHookCallWithObjectKeys(ast, 'useMemo', [
|
|
42
|
-
'chat:undo', 'chat:newline', 'chat:stash', 'chat:imagePaste',
|
|
43
|
-
]);
|
|
44
|
-
assert(chatHandlersMemo, 'Could not find chatHandlers useMemo');
|
|
45
|
-
const R = src(chatHandlersMemo.callee.object);
|
|
46
|
-
|
|
47
|
-
const handlersObject = findFirst(chatHandlersMemo.arguments[0], n =>
|
|
48
|
-
n.type === 'ObjectExpression' &&
|
|
49
|
-
n.properties.some(p => p.type === 'Property' && p.key.type === 'Literal' && p.key.value === 'chat:undo'));
|
|
50
|
-
assert(handlersObject, 'Could not find handlers object in useMemo');
|
|
51
|
-
|
|
52
|
-
const depsArray = chatHandlersMemo.arguments[1];
|
|
53
|
-
assert(depsArray?.type === 'ArrayExpression', 'Could not find deps array');
|
|
54
|
-
|
|
55
|
-
const memoDecl = findFirst(ast, n =>
|
|
56
|
-
n.type === 'VariableDeclaration' && n.declarations.some(d => d.init === chatHandlersMemo));
|
|
57
|
-
assert(memoDecl, 'Could not find VariableDeclaration for chatHandlers useMemo');
|
|
58
|
-
|
|
59
|
-
// useCallback with empty deps — process.exit(75) never changes
|
|
60
|
-
editor.insertAt(memoDecl.start, `let __rH=${R}.useCallback(()=>{process.exit(75)},[]);`);
|
|
61
|
-
|
|
62
|
-
const lastProp = handlersObject.properties[handlersObject.properties.length - 1];
|
|
63
|
-
editor.insertAt(lastProp.end, `,"chat:reload":__rH`);
|
|
64
|
-
|
|
65
|
-
const lastDep = depsArray.elements[depsArray.elements.length - 1];
|
|
66
|
-
editor.insertAt(lastDep.end, `,__rH`);
|
|
67
|
-
},
|
|
68
|
-
};
|