claude-code-extensions 0.1.0 → 0.1.1
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-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 +18 -0
- package/dist/cli.js +218 -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 +60 -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} +4 -0
- package/dist/patches/index.js +22 -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/session-export.d.ts +16 -0
- package/dist/patches/session-export.js +93 -0
- package/dist/patches/session-export.js.map +1 -0
- package/dist/patches/session-timer.d.ts +18 -0
- package/dist/patches/session-timer.js +217 -0
- package/dist/patches/session-timer.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 +30 -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 +16 -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,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Disable Paste Collapse
|
|
3
|
-
*
|
|
4
|
-
* Prevents pasted text from being collapsed into "[Pasted text #N +X lines]"
|
|
5
|
-
* placeholders. All pasted content is inserted inline so you can review and
|
|
6
|
-
* edit it before submitting.
|
|
7
|
-
*
|
|
8
|
-
* Addresses: https://github.com/anthropics/claude-code/issues/23134 (77 👍)
|
|
9
|
-
*
|
|
10
|
-
* Strategy: find the paste-collapse condition in onTextPaste() — identified
|
|
11
|
-
* by the call to formatPastedTextRef (Xd8) which contains the stable string
|
|
12
|
-
* literal "Pasted text #" — and replace the guard condition with `false`.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
export default {
|
|
16
|
-
id: 'disable-paste-collapse',
|
|
17
|
-
name: 'Disable Paste Collapse',
|
|
18
|
-
description: 'Show pasted text inline instead of collapsing into [Pasted text #N]',
|
|
19
|
-
|
|
20
|
-
apply(ctx) {
|
|
21
|
-
const { ast, editor, find, src, assert } = ctx;
|
|
22
|
-
const { findFirst } = find;
|
|
23
|
-
|
|
24
|
-
// Find the formatPastedTextRef function by its "Pasted text #" template literal.
|
|
25
|
-
const fmtFn = findFirst(ast, n =>
|
|
26
|
-
n.type === 'FunctionDeclaration' &&
|
|
27
|
-
src(n).includes('Pasted text #'));
|
|
28
|
-
assert(fmtFn, 'Could not find formatPastedTextRef function');
|
|
29
|
-
const fmtName = fmtFn.id.name; // e.g. "Xd8"
|
|
30
|
-
|
|
31
|
-
// Find the onTextPaste function: contains a call to formatPastedTextRef
|
|
32
|
-
// and an if-statement whose consequent calls it.
|
|
33
|
-
// We look for the IfStatement whose consequent block contains a call
|
|
34
|
-
// to fmtName — that's the collapse gate.
|
|
35
|
-
const collapseIf = findFirst(ast, n => {
|
|
36
|
-
if (n.type !== 'IfStatement') return false;
|
|
37
|
-
// The consequent must contain a call to fmtName
|
|
38
|
-
const hasFmtCall = findFirst(n.consequent, c =>
|
|
39
|
-
c.type === 'CallExpression' &&
|
|
40
|
-
c.callee.type === 'Identifier' &&
|
|
41
|
-
c.callee.name === fmtName);
|
|
42
|
-
if (!hasFmtCall) return false;
|
|
43
|
-
// The alternate should insert text directly (else branch)
|
|
44
|
-
return n.alternate !== null;
|
|
45
|
-
});
|
|
46
|
-
assert(collapseIf, 'Could not find paste-collapse if-statement');
|
|
47
|
-
|
|
48
|
-
// Replace the test expression with `false`
|
|
49
|
-
const test = collapseIf.test;
|
|
50
|
-
editor.replaceRange(test.start, test.end, 'false');
|
|
51
|
-
},
|
|
52
|
-
};
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Disable Telemetry Patch
|
|
3
|
-
*
|
|
4
|
-
* Strips Datadog and 1P (first-party) analytics calls.
|
|
5
|
-
*
|
|
6
|
-
* Three surgical cuts:
|
|
7
|
-
* 1. Kill the analytics sink — logEvent() queues forever, nothing drains
|
|
8
|
-
* 2. Noop Datadog HTTP flush + blank endpoint — no POST to Datadog
|
|
9
|
-
* 3. Noop 1P event logging init — prevents exporter + retry of old events
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
export default {
|
|
13
|
-
id: 'disable-telemetry',
|
|
14
|
-
name: 'Disable Telemetry',
|
|
15
|
-
description: 'Strip Datadog and 1P analytics calls',
|
|
16
|
-
|
|
17
|
-
apply(ctx) {
|
|
18
|
-
const { ast, editor, find, query, assert } = ctx;
|
|
19
|
-
const { findFirst } = find;
|
|
20
|
-
|
|
21
|
-
// ── 1. Kill the analytics sink ───────────────────────────────────────
|
|
22
|
-
// initializeAnalyticsSink() calls attachAnalyticsSink({logEvent, logEventAsync}).
|
|
23
|
-
// Replace that call with void 0 so the sink never attaches. Without a
|
|
24
|
-
// sink, logEvent() just pushes to an internal queue that never drains —
|
|
25
|
-
// neither Datadog nor 1P receives anything.
|
|
26
|
-
//
|
|
27
|
-
// Marker: object literal with preserved keys "logEvent" + "logEventAsync"
|
|
28
|
-
// passed as sole argument to a call expression.
|
|
29
|
-
|
|
30
|
-
const sinkCall = findFirst(ast, n => {
|
|
31
|
-
if (n.type !== 'CallExpression' || n.arguments.length !== 1) return false;
|
|
32
|
-
const arg = n.arguments[0];
|
|
33
|
-
if (arg.type !== 'ObjectExpression') return false;
|
|
34
|
-
const keys = arg.properties
|
|
35
|
-
.filter(p => p.type === 'Property')
|
|
36
|
-
.map(p => p.key.name || p.key.value);
|
|
37
|
-
return keys.includes('logEvent') && keys.includes('logEventAsync');
|
|
38
|
-
});
|
|
39
|
-
assert(sinkCall, 'Could not find attachAnalyticsSink({logEvent, logEventAsync})');
|
|
40
|
-
editor.replaceRange(sinkCall.start, sinkCall.end, 'void 0');
|
|
41
|
-
|
|
42
|
-
// ── 2. Noop Datadog HTTP flush + blank endpoint ─────────────────────
|
|
43
|
-
// shutdownDatadog() calls flushLogs() on exit, which would POST
|
|
44
|
-
// whatever's in logBatch. Insert early return to prevent any HTTP call.
|
|
45
|
-
//
|
|
46
|
-
// Marker: "DD-API-KEY" header string. Take the smallest zero-param
|
|
47
|
-
// function containing it (flushLogs, not a module wrapper).
|
|
48
|
-
|
|
49
|
-
const ddCandidates = query.findFunctionsContainingStrings(ast, 'DD-API-KEY');
|
|
50
|
-
const flushFns = ddCandidates
|
|
51
|
-
.filter(fn => fn.params.length === 0)
|
|
52
|
-
.sort((a, b) => (a.end - a.start) - (b.end - b.start));
|
|
53
|
-
assert(flushFns.length >= 1, 'Could not find Datadog flushLogs function');
|
|
54
|
-
const flushBody = flushFns[0].body;
|
|
55
|
-
assert(flushBody.type === 'BlockStatement', 'flushLogs: expected block body');
|
|
56
|
-
editor.insertAt(flushBody.start + 1, 'return;');
|
|
57
|
-
|
|
58
|
-
// Blank the endpoint URL as extra safety (token is not a plain literal
|
|
59
|
-
// in the bundle, so we target the URL instead).
|
|
60
|
-
const endpointLit = findFirst(ast, n =>
|
|
61
|
-
n.type === 'Literal' &&
|
|
62
|
-
n.value === 'https://http-intake.logs.us5.datadoghq.com/api/v2/logs');
|
|
63
|
-
assert(endpointLit, 'Could not find Datadog endpoint URL');
|
|
64
|
-
editor.replaceRange(endpointLit.start, endpointLit.end, '""');
|
|
65
|
-
|
|
66
|
-
// ── 3. Noop 1P event logging init ───────────────────────────────────
|
|
67
|
-
// Prevents the OpenTelemetry exporter from being created, which also
|
|
68
|
-
// stops retryPreviousBatches from re-sending old failed events.
|
|
69
|
-
//
|
|
70
|
-
// Marker: "com.anthropic.claude_code.events" instrumentation scope.
|
|
71
|
-
// Take the smallest zero-param function containing it.
|
|
72
|
-
|
|
73
|
-
const init1PCandidates = query.findFunctionsContainingStrings(
|
|
74
|
-
ast, 'com.anthropic.claude_code.events');
|
|
75
|
-
const init1PFns = init1PCandidates
|
|
76
|
-
.filter(fn => fn.params.length === 0)
|
|
77
|
-
.sort((a, b) => (a.end - a.start) - (b.end - b.start));
|
|
78
|
-
assert(init1PFns.length >= 1, 'Could not find initialize1PEventLogging');
|
|
79
|
-
const init1PBody = init1PFns[0].body;
|
|
80
|
-
assert(init1PBody.type === 'BlockStatement',
|
|
81
|
-
'initialize1PEventLogging: expected block body');
|
|
82
|
-
editor.insertAt(init1PBody.start + 1, 'return;');
|
|
83
|
-
},
|
|
84
|
-
};
|
|
@@ -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
|
-
};
|