@promptctl/cc-candybar 1.22.0 → 1.24.0
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/README.md +13 -9
- package/bin/cc-candybar +18 -6
- package/dist/index.mjs +91 -88
- package/package.json +6 -22
- package/src/check.ts +2 -1
- package/src/config/default-dsl-config.ts +120 -10
- package/src/config/dsl-loader.ts +13 -5
- package/src/config/loader/merge.ts +15 -7
- package/src/daemon/acquire.ts +148 -12
- package/src/daemon/cache/render.ts +5 -1
- package/src/daemon/paths.ts +11 -0
- package/src/daemon/server.ts +6 -0
- package/src/demo/dsl.ts +2 -1
- package/src/index.ts +8 -6
- package/src/install/index.ts +207 -85
package/src/install/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import os from "node:os";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
4
5
|
import { launchSync } from "../proc/launch";
|
|
5
6
|
import { tryClickViaDaemon } from "../daemon/client";
|
|
6
7
|
import type { PermanentOutcome } from "../daemon/client-transport";
|
|
@@ -8,9 +9,7 @@ import { obtainDaemonKick } from "../daemon/acquire";
|
|
|
8
9
|
import { URL_SCHEME, VERB_COPY } from "../click/wire";
|
|
9
10
|
|
|
10
11
|
// [LAW:one-source-of-truth] Replaced at build time by tsdown's `define` option
|
|
11
|
-
// from package.json
|
|
12
|
-
// pnpm's content-addressable cache key changes on every release — no stale
|
|
13
|
-
// versions sticking around because of `@latest` resolution.
|
|
12
|
+
// from package.json — the single version stamp install output reports.
|
|
14
13
|
declare const __PACKAGE_VERSION__: string;
|
|
15
14
|
const PACKAGE_VERSION =
|
|
16
15
|
typeof __PACKAGE_VERSION__ !== "undefined" ? __PACKAGE_VERSION__ : "dev";
|
|
@@ -20,28 +19,32 @@ const BUNDLE_ID = "com.cccandybar.url-handler";
|
|
|
20
19
|
const APP_NAME = "CCCandybarURLHandler";
|
|
21
20
|
|
|
22
21
|
// [LAW:one-source-of-truth] `install` writes no renderer flags into
|
|
23
|
-
// ~/.claude/settings.json.
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// the install command's job is just to wire the URL handler and the daemon
|
|
28
|
-
// entry. To customize, users edit a config file at one of the resolution
|
|
29
|
-
// paths or copy src/demo/statusline.json5 as a starting point.
|
|
22
|
+
// ~/.claude/settings.json. All authoring lives in `.cc-candybar.json5` or
|
|
23
|
+
// `.cc-candybar.json` (see resolveDslConfigPath — both extensions accepted,
|
|
24
|
+
// .json5 preferred); the install command's job is staging the runtime,
|
|
25
|
+
// wiring the URL handler, and pointing settings at the staged entry.
|
|
30
26
|
const DEFAULT_INSTALL_ARGS: readonly string[] = [];
|
|
31
27
|
|
|
28
|
+
// [LAW:one-type-per-behavior] One platform → one package name; the render
|
|
29
|
+
// entry is the same contract everywhere, only the artifact differs.
|
|
30
|
+
const PLATFORM_PACKAGES: Record<string, string> = {
|
|
31
|
+
"darwin-arm64": "@promptctl/cc-candybar-darwin-arm64",
|
|
32
|
+
"darwin-x64": "@promptctl/cc-candybar-darwin-x64",
|
|
33
|
+
"linux-x64": "@promptctl/cc-candybar-linux-x64",
|
|
34
|
+
"linux-arm64": "@promptctl/cc-candybar-linux-arm64",
|
|
35
|
+
};
|
|
36
|
+
|
|
32
37
|
function shellEscape(arg: string): string {
|
|
33
38
|
// Safe characters that don't need quoting in any reasonable shell.
|
|
34
39
|
if (/^[A-Za-z0-9_./=,:-]+$/.test(arg)) return arg;
|
|
35
40
|
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
function buildStatusLineCommand(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
...rendererArgs.map(shellEscape),
|
|
44
|
-
].join(" ");
|
|
43
|
+
function buildStatusLineCommand(
|
|
44
|
+
binPath: string,
|
|
45
|
+
rendererArgs: readonly string[],
|
|
46
|
+
): string {
|
|
47
|
+
return [binPath, ...rendererArgs].map(shellEscape).join(" ");
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
function appBundlePath(): string {
|
|
@@ -60,51 +63,52 @@ function ensureMacOS(): void {
|
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
// [LAW:one-source-of-truth] The staged runtime lives at ONE stable path per
|
|
67
|
+
// platform, outside any package manager's store — pnpm cache pruning or a
|
|
68
|
+
// version bump can never yank the files the statusline and daemon run from.
|
|
63
69
|
function supportDir(): string {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
if (process.platform === "darwin") {
|
|
71
|
+
return path.join(
|
|
72
|
+
os.homedir(),
|
|
73
|
+
"Library",
|
|
74
|
+
"Application Support",
|
|
75
|
+
"CCCandybar",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const xdgData =
|
|
79
|
+
process.env.XDG_DATA_HOME ?? path.join(os.homedir(), ".local", "share");
|
|
80
|
+
return path.join(xdgData, "cc-candybar");
|
|
70
81
|
}
|
|
71
82
|
|
|
72
|
-
function
|
|
73
|
-
return path.join(supportDir(), "
|
|
83
|
+
function stagedBinPath(): string {
|
|
84
|
+
return path.join(supportDir(), "bin", "cc-candybar");
|
|
74
85
|
}
|
|
75
86
|
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
): string {
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
// ~/Library/Application Support/CCCandybar that we own. NODE_PATH
|
|
85
|
-
// points at the project's node_modules so the handler can resolve deps
|
|
86
|
-
// (rich-js etc.) without being co-located with a package.json.
|
|
87
|
+
function stagedDistPath(): string {
|
|
88
|
+
return path.join(supportDir(), "dist", "index.mjs");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function appleScriptSource(nodePath: string, scriptPath: string): string {
|
|
92
|
+
// Bake absolute paths into the AppleScript so click-time invocation doesn't
|
|
93
|
+
// depend on PATH or install-time cache state. The dist bundle is fully
|
|
94
|
+
// self-contained (tsdown noExternal), so no NODE_PATH is needed.
|
|
87
95
|
const escNode = nodePath.replace(/"/g, '\\"');
|
|
88
96
|
const escScript = scriptPath.replace(/"/g, '\\"');
|
|
89
|
-
const escModules = nodeModulesPath.replace(/"/g, '\\"');
|
|
90
97
|
return [
|
|
91
98
|
"on open location L",
|
|
92
|
-
`\tdo shell script "
|
|
99
|
+
`\tdo shell script "'${escNode}' '${escScript}' url-handle " & quoted form of L`,
|
|
93
100
|
"end open location",
|
|
94
101
|
].join("\n");
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
// [LAW:one-source-of-truth] The bundle that contains *this* function is
|
|
98
|
-
// the thing we need to
|
|
99
|
-
// reach us:
|
|
105
|
+
// the thing we need to stage. Two invocation paths reach us:
|
|
100
106
|
// - via the bin shim: process.argv[1] = ".../bin/cc-candybar" which
|
|
101
|
-
//
|
|
102
|
-
// break — its relative import wouldn't resolve from the new location.
|
|
103
|
-
// So resolve to the sibling dist/index.mjs.
|
|
107
|
+
// dynamically imports ../dist/index.mjs — resolve the sibling dist.
|
|
104
108
|
// - direct node: process.argv[1] = ".../dist/index.mjs". Use as-is.
|
|
105
109
|
export function locateBundledDist(argv1: string | undefined): string {
|
|
106
110
|
if (!argv1) {
|
|
107
|
-
throw new Error("install
|
|
111
|
+
throw new Error("install: process.argv[1] not set");
|
|
108
112
|
}
|
|
109
113
|
if (argv1.endsWith(".mjs") || argv1.endsWith(".js")) {
|
|
110
114
|
return argv1;
|
|
@@ -113,17 +117,126 @@ export function locateBundledDist(argv1: string | undefined): string {
|
|
|
113
117
|
return path.resolve(path.dirname(argv1), "..", "dist", "index.mjs");
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
// The staged render entry: the prebuilt native binary when this platform has
|
|
121
|
+
// one, else the node bin shim. Same contract either way — read hookData on
|
|
122
|
+
// stdin, resolve ../dist/index.mjs by adjacency — so downstream code never
|
|
123
|
+
// cares which flavor was staged. [LAW:one-type-per-behavior]
|
|
124
|
+
type RenderEntry =
|
|
125
|
+
| { kind: "native"; sourcePath: string }
|
|
126
|
+
| { kind: "node-shim"; sourcePath: string };
|
|
127
|
+
|
|
128
|
+
function resolveRenderEntry(sourceDist: string): RenderEntry {
|
|
129
|
+
const key = `${process.platform}-${process.arch}`;
|
|
130
|
+
const pkgName = PLATFORM_PACKAGES[key];
|
|
131
|
+
if (pkgName) {
|
|
132
|
+
// Anchored to the bundle's real location, not this module's compiled
|
|
133
|
+
// form (which ts-jest loads as CJS where import.meta is illegal) — the
|
|
134
|
+
// platform package lives in node_modules beside the installed dist.
|
|
135
|
+
const require = createRequire(sourceDist);
|
|
136
|
+
try {
|
|
137
|
+
return {
|
|
138
|
+
kind: "native",
|
|
139
|
+
sourcePath: require.resolve(`${pkgName}/bin/cc-candybar`),
|
|
140
|
+
};
|
|
141
|
+
} catch {
|
|
142
|
+
// Optional dependency absent (unsupported install, pruned optionals).
|
|
143
|
+
// Falls through to the node shim — announced by the caller, never
|
|
144
|
+
// silent. [LAW:no-silent-failure]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
kind: "node-shim",
|
|
149
|
+
sourcePath: path.resolve(
|
|
150
|
+
path.dirname(sourceDist),
|
|
151
|
+
"..",
|
|
152
|
+
"bin",
|
|
153
|
+
"cc-candybar",
|
|
154
|
+
),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function stageFile(source: string, dest: string): void {
|
|
159
|
+
// Re-running install FROM the staged runtime makes source === dest;
|
|
160
|
+
// copyFileSync would truncate the file onto itself. Identity is "already
|
|
161
|
+
// staged", not an error.
|
|
162
|
+
if (path.resolve(source) === path.resolve(dest)) return;
|
|
163
|
+
fs.copyFileSync(source, dest);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// [LAW:one-source-of-truth] The staged file is the authority on what got
|
|
167
|
+
// staged. resolveRenderEntry's kind describes the *source lookup*, and on a
|
|
168
|
+
// re-run from the staged runtime that lookup resolves the identity path
|
|
169
|
+
// (source === dest), preserving whatever is on disk — possibly a native
|
|
170
|
+
// binary from a prior install that the lookup couldn't see. So the announced
|
|
171
|
+
// kind derives from the artifact itself: both flavors are ours, and the node
|
|
172
|
+
// shim is a "#!" script while the native binary is Mach-O/ELF.
|
|
173
|
+
function stagedEntryKind(binPath: string): RenderEntry["kind"] {
|
|
174
|
+
const fd = fs.openSync(binPath, "r");
|
|
175
|
+
try {
|
|
176
|
+
const magic = Buffer.alloc(2);
|
|
177
|
+
const bytesRead = fs.readSync(fd, magic, 0, 2, 0);
|
|
178
|
+
// [LAW:no-silent-failure] Under 2 bytes neither flavor exists — the file
|
|
179
|
+
// is a corrupt artifact (a crashed prior copy preserved by the identity
|
|
180
|
+
// path), not a native binary.
|
|
181
|
+
if (bytesRead < 2) {
|
|
182
|
+
throw new Error(
|
|
183
|
+
`install: staged render entry at ${binPath} is truncated ` +
|
|
184
|
+
`(${bytesRead} byte(s)). Re-run: pnpm dlx ${PACKAGE_NAME}@latest install`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return magic.toString("latin1") === "#!" ? "node-shim" : "native";
|
|
188
|
+
} finally {
|
|
189
|
+
fs.closeSync(fd);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface StagedRuntime {
|
|
194
|
+
binPath: string;
|
|
195
|
+
distPath: string;
|
|
196
|
+
entryKind: RenderEntry["kind"];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Stage the full runtime at the stable path: dist/index.mjs (daemon + CLI
|
|
200
|
+
// bundle) and bin/cc-candybar (render entry) as adjacent files. Adjacency IS
|
|
201
|
+
// the contract — every entry flavor locates the bundle via ../dist/index.mjs.
|
|
202
|
+
export function runStageRuntime(): StagedRuntime {
|
|
203
|
+
const sourceDist = locateBundledDist(process.argv[1]);
|
|
204
|
+
if (!fs.existsSync(sourceDist)) {
|
|
205
|
+
throw new Error(
|
|
206
|
+
`install: bundled dist not found at ${sourceDist}. Reinstall the package.`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const entry = resolveRenderEntry(sourceDist);
|
|
211
|
+
if (!fs.existsSync(entry.sourcePath)) {
|
|
119
212
|
throw new Error(
|
|
120
|
-
`install
|
|
213
|
+
`install: render entry not found at ${entry.sourcePath}. Reinstall the package.`,
|
|
121
214
|
);
|
|
122
215
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
fs.
|
|
126
|
-
|
|
216
|
+
|
|
217
|
+
fs.mkdirSync(path.dirname(stagedDistPath()), { recursive: true });
|
|
218
|
+
fs.mkdirSync(path.dirname(stagedBinPath()), { recursive: true });
|
|
219
|
+
stageFile(sourceDist, stagedDistPath());
|
|
220
|
+
stageFile(entry.sourcePath, stagedBinPath());
|
|
221
|
+
fs.chmodSync(stagedBinPath(), 0o755);
|
|
222
|
+
|
|
223
|
+
// The pre-1.21 layout kept a second copy of the bundle as url-handler.mjs;
|
|
224
|
+
// dist/index.mjs is the one staged bundle now. Remove the stale copy so it
|
|
225
|
+
// can't drift. [LAW:one-source-of-truth]
|
|
226
|
+
fs.rmSync(path.join(supportDir(), "url-handler.mjs"), { force: true });
|
|
227
|
+
|
|
228
|
+
const stagedKind = stagedEntryKind(stagedBinPath());
|
|
229
|
+
process.stdout.write(
|
|
230
|
+
`Staged cc-candybar v${PACKAGE_VERSION} runtime at ${supportDir()}\n` +
|
|
231
|
+
(stagedKind === "native"
|
|
232
|
+
? ` render entry: native binary (${process.platform}-${process.arch})\n`
|
|
233
|
+
: ` render entry: node shim (no native binary for ${process.platform}-${process.arch}; renders are correct but pay node startup)\n`),
|
|
234
|
+
);
|
|
235
|
+
return {
|
|
236
|
+
binPath: stagedBinPath(),
|
|
237
|
+
distPath: stagedDistPath(),
|
|
238
|
+
entryKind: stagedKind,
|
|
239
|
+
};
|
|
127
240
|
}
|
|
128
241
|
|
|
129
242
|
function infoPlistPatch(): Array<{ key: string; xml: string }> {
|
|
@@ -150,23 +263,10 @@ function infoPlistPatch(): Array<{ key: string; xml: string }> {
|
|
|
150
263
|
];
|
|
151
264
|
}
|
|
152
265
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
process.stdout.write(`Copied dist to ${stableScript}\n`);
|
|
158
|
-
|
|
159
|
-
// [LAW:one-source-of-truth] Derive node_modules from the source dist path
|
|
160
|
-
// (dist/index.mjs → ../node_modules). The stable copy lives elsewhere but
|
|
161
|
-
// needs the same node_modules to resolve deps at click time.
|
|
162
|
-
const sourceDist = locateBundledDist(process.argv[1]);
|
|
163
|
-
const nodeModules = path.join(path.dirname(sourceDist), "..", "node_modules");
|
|
164
|
-
if (!fs.existsSync(nodeModules)) {
|
|
165
|
-
throw new Error(
|
|
166
|
-
`install-url-handler: node_modules not found at ${nodeModules}. Install deps first.`,
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
266
|
+
// Callers establish the macOS precondition ([LAW:single-enforcer] — the
|
|
267
|
+
// subcommand entry checks before any side effect; runInstall reaches here
|
|
268
|
+
// only through its darwin dispatch).
|
|
269
|
+
function installUrlHandlerFrom(stagedDist: string): void {
|
|
170
270
|
const bundle = appBundlePath();
|
|
171
271
|
fs.mkdirSync(path.dirname(bundle), { recursive: true });
|
|
172
272
|
|
|
@@ -178,12 +278,7 @@ export function runInstallUrlHandler(): void {
|
|
|
178
278
|
process.stdout.write(`Building ${bundle}\n`);
|
|
179
279
|
const osa = launchSync({
|
|
180
280
|
bin: "/usr/bin/osacompile",
|
|
181
|
-
args: [
|
|
182
|
-
"-o",
|
|
183
|
-
bundle,
|
|
184
|
-
"-e",
|
|
185
|
-
appleScriptSource(process.execPath, stableScript, nodeModules),
|
|
186
|
-
],
|
|
281
|
+
args: ["-o", bundle, "-e", appleScriptSource(process.execPath, stagedDist)],
|
|
187
282
|
category: "install.osacompile",
|
|
188
283
|
});
|
|
189
284
|
if (!osa.ok) {
|
|
@@ -232,6 +327,14 @@ export function runInstallUrlHandler(): void {
|
|
|
232
327
|
);
|
|
233
328
|
}
|
|
234
329
|
|
|
330
|
+
export function runInstallUrlHandler(): void {
|
|
331
|
+
// Precondition before any side effect: on a non-mac this fails with zero
|
|
332
|
+
// files written, not after staging the runtime.
|
|
333
|
+
ensureMacOS();
|
|
334
|
+
const staged = runStageRuntime();
|
|
335
|
+
installUrlHandlerFrom(staged.distPath);
|
|
336
|
+
}
|
|
337
|
+
|
|
235
338
|
interface ParsedUrl {
|
|
236
339
|
verb: string;
|
|
237
340
|
value: string;
|
|
@@ -338,16 +441,23 @@ function formatPermanent(outcome: PermanentOutcome): string {
|
|
|
338
441
|
}
|
|
339
442
|
|
|
340
443
|
export function runInstall(rendererArgs: string[]): void {
|
|
341
|
-
ensureMacOS();
|
|
342
|
-
|
|
343
444
|
const force = rendererArgs.includes("--force");
|
|
344
445
|
const filteredArgs = rendererArgs.filter((a) => a !== "--force");
|
|
345
446
|
|
|
346
447
|
const argsToInstall =
|
|
347
448
|
filteredArgs.length > 0 ? filteredArgs : [...DEFAULT_INSTALL_ARGS];
|
|
348
449
|
|
|
349
|
-
|
|
350
|
-
|
|
450
|
+
const staged = runStageRuntime();
|
|
451
|
+
|
|
452
|
+
if (process.platform === "darwin") {
|
|
453
|
+
installUrlHandlerFrom(staged.distPath);
|
|
454
|
+
} else {
|
|
455
|
+
process.stdout.write(
|
|
456
|
+
"Skipping URL handler (cmd-click verbs are macOS-only).\n",
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
updateClaudeSettings(staged.binPath, argsToInstall, force);
|
|
351
461
|
|
|
352
462
|
process.stdout.write(`✓ install complete.\n`);
|
|
353
463
|
process.stdout.write(
|
|
@@ -356,6 +466,7 @@ export function runInstall(rendererArgs: string[]): void {
|
|
|
356
466
|
}
|
|
357
467
|
|
|
358
468
|
function updateClaudeSettings(
|
|
469
|
+
binPath: string,
|
|
359
470
|
rendererArgs: readonly string[],
|
|
360
471
|
force: boolean,
|
|
361
472
|
overridePath?: string,
|
|
@@ -376,12 +487,20 @@ function updateClaudeSettings(
|
|
|
376
487
|
}
|
|
377
488
|
|
|
378
489
|
const existing = settings.statusLine?.command as string | undefined;
|
|
379
|
-
// [LAW:one-source-of-truth] Detection:
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
|
|
490
|
+
// [LAW:one-source-of-truth] Detection: a command we (or a prior version of
|
|
491
|
+
// us) wrote either starts with the legacy `pnpm dlx` form (an open prefix —
|
|
492
|
+
// a version suffix follows) or has the staged bin path — quoted or bare —
|
|
493
|
+
// as its entire first token. Any other value is a user customization we
|
|
494
|
+
// must not silently destroy.
|
|
495
|
+
// [LAW:types-are-the-program] Token, not prefix: a bare startsWith(binPath)
|
|
496
|
+
// would also claim `<binPath>-backup …` as ours and overwrite it.
|
|
497
|
+
const managedTokens = [binPath, shellEscape(binPath)];
|
|
383
498
|
const isOurs =
|
|
384
|
-
typeof existing === "string" &&
|
|
499
|
+
typeof existing === "string" &&
|
|
500
|
+
(existing.startsWith(`pnpm dlx ${PACKAGE_NAME}@`) ||
|
|
501
|
+
managedTokens.some(
|
|
502
|
+
(token) => existing === token || existing.startsWith(`${token} `),
|
|
503
|
+
));
|
|
385
504
|
|
|
386
505
|
if (existing && !isOurs && !force) {
|
|
387
506
|
process.stderr.write(
|
|
@@ -394,7 +513,7 @@ function updateClaudeSettings(
|
|
|
394
513
|
|
|
395
514
|
settings.statusLine = {
|
|
396
515
|
type: "command",
|
|
397
|
-
command: buildStatusLineCommand(rendererArgs),
|
|
516
|
+
command: buildStatusLineCommand(binPath, rendererArgs),
|
|
398
517
|
};
|
|
399
518
|
|
|
400
519
|
fs.writeFileSync(target, JSON.stringify(settings, null, 2));
|
|
@@ -407,4 +526,7 @@ export const __test__ = {
|
|
|
407
526
|
buildStatusLineCommand,
|
|
408
527
|
DEFAULT_INSTALL_ARGS,
|
|
409
528
|
updateClaudeSettings,
|
|
529
|
+
resolveRenderEntry,
|
|
530
|
+
stageFile,
|
|
531
|
+
stagedEntryKind,
|
|
410
532
|
};
|