claude-scope 0.8.15 → 0.8.16
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/claude-scope.cjs +544 -457
- package/package.json +1 -1
package/dist/claude-scope.cjs
CHANGED
|
@@ -1471,7 +1471,7 @@ var init_renderer = __esm({
|
|
|
1471
1471
|
for (const widget of widgetsForLine) {
|
|
1472
1472
|
try {
|
|
1473
1473
|
const output = await widget.render(context);
|
|
1474
|
-
if (output
|
|
1474
|
+
if (output && output.trim().length > 0) {
|
|
1475
1475
|
outputs.push(output);
|
|
1476
1476
|
}
|
|
1477
1477
|
} catch (error) {
|
|
@@ -1952,27 +1952,58 @@ var init_styles = __esm({
|
|
|
1952
1952
|
return compactStyle(data, colors2);
|
|
1953
1953
|
},
|
|
1954
1954
|
/**
|
|
1955
|
-
* playful: Emojis (📖✏️✨🔄🔍📁) with tool
|
|
1955
|
+
* playful: Emojis (📖✏️✨🔄🔍📁) with tool counts (like balanced but more fun)
|
|
1956
|
+
* - Shows running + completed counts like balanced style
|
|
1957
|
+
* - Uses emojis instead of text labels for a more playful look
|
|
1956
1958
|
*/
|
|
1957
1959
|
playful: (data, colors2) => {
|
|
1958
1960
|
const parts = [];
|
|
1961
|
+
const c = colors2 ?? getDefaultColors();
|
|
1959
1962
|
const emojis = {
|
|
1960
1963
|
Read: "\u{1F4D6}",
|
|
1961
1964
|
Write: "\u270F\uFE0F",
|
|
1962
1965
|
Edit: "\u2728",
|
|
1963
1966
|
Bash: "\u{1F504}",
|
|
1964
1967
|
Grep: "\u{1F50D}",
|
|
1965
|
-
Glob: "\u{1F4C1}"
|
|
1968
|
+
Glob: "\u{1F4C1}",
|
|
1969
|
+
Task: "\u{1F4CB}"
|
|
1966
1970
|
};
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
+
const allToolNames = /* @__PURE__ */ new Set();
|
|
1972
|
+
for (const tool of data.running) {
|
|
1973
|
+
allToolNames.add(tool.name);
|
|
1974
|
+
}
|
|
1975
|
+
for (const [name] of data.completed.slice(0, 3)) {
|
|
1976
|
+
allToolNames.add(name);
|
|
1977
|
+
}
|
|
1978
|
+
const completedMap = new Map(data.completed);
|
|
1979
|
+
const runningCounts = /* @__PURE__ */ new Map();
|
|
1980
|
+
for (const tool of data.running) {
|
|
1981
|
+
runningCounts.set(tool.name, (runningCounts.get(tool.name) ?? 0) + 1);
|
|
1982
|
+
}
|
|
1983
|
+
for (const name of allToolNames) {
|
|
1984
|
+
const runningCount = runningCounts.get(name) ?? 0;
|
|
1985
|
+
const completedCount = completedMap.get(name) ?? 0;
|
|
1986
|
+
const emoji = emojis[name] ?? "\u{1F527}";
|
|
1987
|
+
if (runningCount > 0 && completedCount > 0) {
|
|
1988
|
+
const nameStr = colorize(name, c.tools.name);
|
|
1989
|
+
const runningStr = colorize(`\u25B6${runningCount}`, c.tools.running);
|
|
1990
|
+
const doneStr = colorize(`\u2713${completedCount}`, c.tools.completed);
|
|
1991
|
+
parts.push(`${emoji} ${nameStr} (${runningStr}, ${doneStr})`);
|
|
1992
|
+
} else if (completedCount > 0) {
|
|
1993
|
+
const pluralName = pluralizeTool(name);
|
|
1994
|
+
const nameStr = colorize(pluralName, c.tools.name);
|
|
1995
|
+
const countStr = colorize(`${completedCount}`, c.tools.count);
|
|
1996
|
+
parts.push(`${emoji} ${nameStr}: ${countStr}`);
|
|
1997
|
+
} else if (runningCount > 0) {
|
|
1998
|
+
const nameStr = colorize(name, c.tools.name);
|
|
1999
|
+
const runningStr = colorize(`\u25B6${runningCount}`, c.tools.running);
|
|
2000
|
+
parts.push(`${emoji} ${nameStr} (${runningStr})`);
|
|
2001
|
+
}
|
|
1971
2002
|
}
|
|
1972
2003
|
if (parts.length === 0) {
|
|
1973
2004
|
return "";
|
|
1974
2005
|
}
|
|
1975
|
-
return parts.join("
|
|
2006
|
+
return parts.join(" | ");
|
|
1976
2007
|
},
|
|
1977
2008
|
/**
|
|
1978
2009
|
* verbose: Full text labels "Running:" and "Completed:"
|
|
@@ -3189,6 +3220,472 @@ var init_cost_widget = __esm({
|
|
|
3189
3220
|
}
|
|
3190
3221
|
});
|
|
3191
3222
|
|
|
3223
|
+
// src/widgets/dev-server/port-detector.ts
|
|
3224
|
+
var import_node_child_process, import_node_util, execFileAsync, PORT_MAPPING, DEV_PORTS, PortDetector;
|
|
3225
|
+
var init_port_detector = __esm({
|
|
3226
|
+
"src/widgets/dev-server/port-detector.ts"() {
|
|
3227
|
+
"use strict";
|
|
3228
|
+
import_node_child_process = require("node:child_process");
|
|
3229
|
+
import_node_util = require("node:util");
|
|
3230
|
+
execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
3231
|
+
PORT_MAPPING = {
|
|
3232
|
+
5173: { name: "Vite", icon: "\u26A1" },
|
|
3233
|
+
4200: { name: "Angular", icon: "\u25B2" },
|
|
3234
|
+
3e3: { name: "Dev", icon: "\u{1F680}" },
|
|
3235
|
+
8080: { name: "Webpack", icon: "\u{1F4E6}" },
|
|
3236
|
+
8e3: { name: "Dev", icon: "\u{1F680}" },
|
|
3237
|
+
8888: { name: "Dev", icon: "\u{1F680}" }
|
|
3238
|
+
};
|
|
3239
|
+
DEV_PORTS = [5173, 4200, 3e3, 8080, 8e3, 8888];
|
|
3240
|
+
PortDetector = class {
|
|
3241
|
+
execFn;
|
|
3242
|
+
/**
|
|
3243
|
+
* Create a new PortDetector
|
|
3244
|
+
* @param execFn - Optional execFile function for testing (dependency injection)
|
|
3245
|
+
*/
|
|
3246
|
+
constructor(execFn) {
|
|
3247
|
+
this.execFn = execFn ?? execFileAsync;
|
|
3248
|
+
}
|
|
3249
|
+
/**
|
|
3250
|
+
* Detect running dev servers by checking listening ports
|
|
3251
|
+
* @returns Detected server info or null
|
|
3252
|
+
*/
|
|
3253
|
+
async detect() {
|
|
3254
|
+
try {
|
|
3255
|
+
const args = [
|
|
3256
|
+
"-nP",
|
|
3257
|
+
// No host names, no port names
|
|
3258
|
+
"-iTCP",
|
|
3259
|
+
// Internet TCP
|
|
3260
|
+
"-sTCP:LISTEN"
|
|
3261
|
+
// TCP LISTEN state only
|
|
3262
|
+
];
|
|
3263
|
+
for (const port of DEV_PORTS) {
|
|
3264
|
+
args.push("-i", `:${port}`);
|
|
3265
|
+
}
|
|
3266
|
+
const { stdout } = await this.execFn("lsof", args, {
|
|
3267
|
+
timeout: 2e3
|
|
3268
|
+
});
|
|
3269
|
+
const lines = stdout.trim().split("\n");
|
|
3270
|
+
for (const line of lines) {
|
|
3271
|
+
if (!line || line.startsWith("COMMAND")) continue;
|
|
3272
|
+
const match = line.match(/:(\d+)\s*\(LISTEN\)/);
|
|
3273
|
+
if (match) {
|
|
3274
|
+
const port = parseInt(match[1], 10);
|
|
3275
|
+
const mapping = PORT_MAPPING[port];
|
|
3276
|
+
if (mapping) {
|
|
3277
|
+
return {
|
|
3278
|
+
name: mapping.name,
|
|
3279
|
+
icon: mapping.icon,
|
|
3280
|
+
port,
|
|
3281
|
+
isRunning: true,
|
|
3282
|
+
isBuilding: false
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
}
|
|
3286
|
+
}
|
|
3287
|
+
return null;
|
|
3288
|
+
} catch {
|
|
3289
|
+
return null;
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
};
|
|
3293
|
+
}
|
|
3294
|
+
});
|
|
3295
|
+
|
|
3296
|
+
// src/widgets/dev-server/process-detector.ts
|
|
3297
|
+
var import_node_child_process2, import_node_util2, execFileAsync2, ProcessDetector;
|
|
3298
|
+
var init_process_detector = __esm({
|
|
3299
|
+
"src/widgets/dev-server/process-detector.ts"() {
|
|
3300
|
+
"use strict";
|
|
3301
|
+
import_node_child_process2 = require("node:child_process");
|
|
3302
|
+
import_node_util2 = require("node:util");
|
|
3303
|
+
execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
3304
|
+
ProcessDetector = class {
|
|
3305
|
+
execFn;
|
|
3306
|
+
processPatterns = [
|
|
3307
|
+
// Generic server patterns - more specific to avoid shell history false positives
|
|
3308
|
+
{ regex: /^[\w\s]+\/npm\s+(exec|run)\s+serve/i, name: "Server", icon: "\u{1F310}" },
|
|
3309
|
+
{ regex: /^[\w\s]+\/npx\s+serve\s+-/i, name: "Server", icon: "\u{1F310}" },
|
|
3310
|
+
{ regex: /^[\w\s]+\/(python|python3)\s+-m\s+http\.server/i, name: "HTTP", icon: "\u{1F310}" },
|
|
3311
|
+
// Generic dev/build patterns - require full command path
|
|
3312
|
+
{ regex: /^[\w\s]+\/(npm|yarn|pnpm|bun)\s+run\s+dev\s*$/i, name: "Dev", icon: "\u{1F680}" },
|
|
3313
|
+
{ regex: /^[\w\s]+\/(npm|yarn|pnpm|bun)\s+run\s+build\s*$/i, name: "Build", icon: "\u{1F528}" },
|
|
3314
|
+
// Framework-specific patterns - require executable path
|
|
3315
|
+
{ regex: /\/(nuxt|next|astro|remix|svelte)\s+dev/i, name: "Framework", icon: "\u26A1" },
|
|
3316
|
+
{ regex: /\/node.*\/vite\s*$/i, name: "Vite", icon: "\u26A1" },
|
|
3317
|
+
// Fallback: simpler patterns but checked last
|
|
3318
|
+
{ regex: /\s(nuxt|next|vite)\s+dev\s/i, name: "DevServer", icon: "\u26A1" }
|
|
3319
|
+
];
|
|
3320
|
+
/**
|
|
3321
|
+
* Create a new ProcessDetector
|
|
3322
|
+
* @param execFn - Optional execFile function for testing (dependency injection)
|
|
3323
|
+
*/
|
|
3324
|
+
constructor(execFn) {
|
|
3325
|
+
this.execFn = execFn ?? execFileAsync2;
|
|
3326
|
+
}
|
|
3327
|
+
/**
|
|
3328
|
+
* Detect running dev server by parsing system process list
|
|
3329
|
+
* @returns Detected server status or null
|
|
3330
|
+
*/
|
|
3331
|
+
async detect() {
|
|
3332
|
+
try {
|
|
3333
|
+
const { stdout } = await this.execFn("ps", ["aux"], {
|
|
3334
|
+
timeout: 1e3
|
|
3335
|
+
});
|
|
3336
|
+
for (const pattern of this.processPatterns) {
|
|
3337
|
+
if (pattern.regex.test(stdout)) {
|
|
3338
|
+
const isBuilding = pattern.name.toLowerCase().includes("build");
|
|
3339
|
+
const isRunning = !isBuilding;
|
|
3340
|
+
return {
|
|
3341
|
+
name: pattern.name,
|
|
3342
|
+
icon: pattern.icon,
|
|
3343
|
+
isRunning,
|
|
3344
|
+
isBuilding
|
|
3345
|
+
};
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
} catch {
|
|
3349
|
+
}
|
|
3350
|
+
return null;
|
|
3351
|
+
}
|
|
3352
|
+
};
|
|
3353
|
+
}
|
|
3354
|
+
});
|
|
3355
|
+
|
|
3356
|
+
// src/widgets/dev-server/styles.ts
|
|
3357
|
+
var devServerStyles;
|
|
3358
|
+
var init_styles6 = __esm({
|
|
3359
|
+
"src/widgets/dev-server/styles.ts"() {
|
|
3360
|
+
"use strict";
|
|
3361
|
+
init_colors();
|
|
3362
|
+
devServerStyles = {
|
|
3363
|
+
balanced: (data, colors2) => {
|
|
3364
|
+
if (!data.server) return "";
|
|
3365
|
+
const { name, icon, isRunning, isBuilding } = data.server;
|
|
3366
|
+
const status = isRunning ? "running" : isBuilding ? "building" : "stopped";
|
|
3367
|
+
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
3368
|
+
const coloredStatus = colors2 ? colorize(`(${status})`, colors2.status) : `(${status})`;
|
|
3369
|
+
return `${icon} ${coloredName} ${coloredStatus}`;
|
|
3370
|
+
},
|
|
3371
|
+
compact: (data, colors2) => {
|
|
3372
|
+
if (!data.server) return "";
|
|
3373
|
+
const { name, icon, isRunning, isBuilding } = data.server;
|
|
3374
|
+
const statusIcon = isRunning ? "\u{1F680}" : isBuilding ? "\u{1F528}" : "\u{1F4A4}";
|
|
3375
|
+
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
3376
|
+
return `${icon} ${coloredName} ${statusIcon}`;
|
|
3377
|
+
},
|
|
3378
|
+
playful: (data, colors2) => {
|
|
3379
|
+
if (!data.server) return "";
|
|
3380
|
+
const { name, isRunning, isBuilding } = data.server;
|
|
3381
|
+
const emoji = isRunning ? "\u{1F3C3}" : isBuilding ? "\u{1F528}" : "\u{1F4A4}";
|
|
3382
|
+
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
3383
|
+
return `${emoji} ${coloredName}`;
|
|
3384
|
+
},
|
|
3385
|
+
verbose: (data, colors2) => {
|
|
3386
|
+
if (!data.server) return "";
|
|
3387
|
+
const { name, isRunning, isBuilding } = data.server;
|
|
3388
|
+
const status = isRunning ? "running" : isBuilding ? "building" : "stopped";
|
|
3389
|
+
const label = colors2 ? colorize("Dev Server:", colors2.label) : "Dev Server:";
|
|
3390
|
+
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
3391
|
+
const coloredStatus = colors2 ? colorize(`(${status})`, colors2.status) : `(${status})`;
|
|
3392
|
+
return `${label} ${coloredName} ${coloredStatus}`;
|
|
3393
|
+
},
|
|
3394
|
+
labeled: (data, colors2) => {
|
|
3395
|
+
if (!data.server) return "";
|
|
3396
|
+
const { name, icon, isRunning } = data.server;
|
|
3397
|
+
const status = isRunning ? "\u{1F7E2}" : "\u{1F534}";
|
|
3398
|
+
const label = colors2 ? colorize("Server:", colors2.label) : "Server:";
|
|
3399
|
+
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
3400
|
+
return `${label} ${icon} ${coloredName} ${status}`;
|
|
3401
|
+
},
|
|
3402
|
+
indicator: (data, colors2) => {
|
|
3403
|
+
if (!data.server) return "";
|
|
3404
|
+
const { name, icon } = data.server;
|
|
3405
|
+
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
3406
|
+
return `\u25CF ${icon} ${coloredName}`;
|
|
3407
|
+
}
|
|
3408
|
+
};
|
|
3409
|
+
}
|
|
3410
|
+
});
|
|
3411
|
+
|
|
3412
|
+
// src/widgets/dev-server/dev-server-widget.ts
|
|
3413
|
+
var DevServerWidget;
|
|
3414
|
+
var init_dev_server_widget = __esm({
|
|
3415
|
+
"src/widgets/dev-server/dev-server-widget.ts"() {
|
|
3416
|
+
"use strict";
|
|
3417
|
+
init_widget_types();
|
|
3418
|
+
init_theme();
|
|
3419
|
+
init_port_detector();
|
|
3420
|
+
init_process_detector();
|
|
3421
|
+
init_styles6();
|
|
3422
|
+
DevServerWidget = class {
|
|
3423
|
+
id = "dev-server";
|
|
3424
|
+
metadata = createWidgetMetadata(
|
|
3425
|
+
"Dev Server",
|
|
3426
|
+
"Detects running dev server processes using hybrid port+process detection",
|
|
3427
|
+
"1.1.0",
|
|
3428
|
+
"claude-scope",
|
|
3429
|
+
0
|
|
3430
|
+
);
|
|
3431
|
+
enabled = true;
|
|
3432
|
+
colors;
|
|
3433
|
+
_lineOverride;
|
|
3434
|
+
styleFn = devServerStyles.balanced;
|
|
3435
|
+
cwd = null;
|
|
3436
|
+
portDetector;
|
|
3437
|
+
processDetector;
|
|
3438
|
+
constructor(colors2) {
|
|
3439
|
+
this.colors = colors2 ?? DEFAULT_THEME;
|
|
3440
|
+
this.portDetector = new PortDetector();
|
|
3441
|
+
this.processDetector = new ProcessDetector();
|
|
3442
|
+
}
|
|
3443
|
+
/**
|
|
3444
|
+
* Set display style
|
|
3445
|
+
* @param style - Style to use for rendering
|
|
3446
|
+
*/
|
|
3447
|
+
setStyle(style = "balanced") {
|
|
3448
|
+
const fn = devServerStyles[style];
|
|
3449
|
+
if (fn) {
|
|
3450
|
+
this.styleFn = fn;
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
/**
|
|
3454
|
+
* Set display line override
|
|
3455
|
+
* @param line - Line number (0-indexed)
|
|
3456
|
+
*/
|
|
3457
|
+
setLine(line) {
|
|
3458
|
+
this._lineOverride = line;
|
|
3459
|
+
}
|
|
3460
|
+
/**
|
|
3461
|
+
* Get display line
|
|
3462
|
+
* @returns Line number (0-indexed)
|
|
3463
|
+
*/
|
|
3464
|
+
getLine() {
|
|
3465
|
+
return this._lineOverride ?? this.metadata.line ?? 0;
|
|
3466
|
+
}
|
|
3467
|
+
/**
|
|
3468
|
+
* Initialize widget with context
|
|
3469
|
+
* @param context - Widget initialization context
|
|
3470
|
+
*/
|
|
3471
|
+
async initialize(context) {
|
|
3472
|
+
this.enabled = context.config?.enabled !== false;
|
|
3473
|
+
}
|
|
3474
|
+
/**
|
|
3475
|
+
* Update widget with new stdin data
|
|
3476
|
+
* @param data - Stdin data from Claude Code
|
|
3477
|
+
*/
|
|
3478
|
+
async update(data) {
|
|
3479
|
+
this.cwd = data.cwd;
|
|
3480
|
+
}
|
|
3481
|
+
/**
|
|
3482
|
+
* Check if widget is enabled
|
|
3483
|
+
* @returns true if widget should render
|
|
3484
|
+
*/
|
|
3485
|
+
isEnabled() {
|
|
3486
|
+
return this.enabled;
|
|
3487
|
+
}
|
|
3488
|
+
/**
|
|
3489
|
+
* Cleanup resources
|
|
3490
|
+
*/
|
|
3491
|
+
async cleanup() {
|
|
3492
|
+
}
|
|
3493
|
+
/**
|
|
3494
|
+
* Render widget output
|
|
3495
|
+
* @param context - Render context
|
|
3496
|
+
* @returns Rendered string, or null if no dev server detected
|
|
3497
|
+
*/
|
|
3498
|
+
async render(_context) {
|
|
3499
|
+
if (!this.enabled || !this.cwd) {
|
|
3500
|
+
return null;
|
|
3501
|
+
}
|
|
3502
|
+
const server = await this.detectDevServer();
|
|
3503
|
+
if (!server) {
|
|
3504
|
+
return null;
|
|
3505
|
+
}
|
|
3506
|
+
const renderData = { server };
|
|
3507
|
+
return this.styleFn(renderData, this.colors.devServer);
|
|
3508
|
+
}
|
|
3509
|
+
/**
|
|
3510
|
+
* Detect running dev server using hybrid approach
|
|
3511
|
+
*
|
|
3512
|
+
* 1. Try port-based detection first (more reliable)
|
|
3513
|
+
* 2. Fall back to process-based detection
|
|
3514
|
+
*
|
|
3515
|
+
* @returns Detected server status or null
|
|
3516
|
+
*/
|
|
3517
|
+
async detectDevServer() {
|
|
3518
|
+
const portResult = await this.portDetector.detect();
|
|
3519
|
+
if (portResult) {
|
|
3520
|
+
return {
|
|
3521
|
+
name: portResult.name,
|
|
3522
|
+
icon: portResult.icon,
|
|
3523
|
+
isRunning: portResult.isRunning,
|
|
3524
|
+
isBuilding: portResult.isBuilding
|
|
3525
|
+
};
|
|
3526
|
+
}
|
|
3527
|
+
return await this.processDetector.detect();
|
|
3528
|
+
}
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
});
|
|
3532
|
+
|
|
3533
|
+
// src/widgets/docker/styles.ts
|
|
3534
|
+
var dockerStyles;
|
|
3535
|
+
var init_styles7 = __esm({
|
|
3536
|
+
"src/widgets/docker/styles.ts"() {
|
|
3537
|
+
"use strict";
|
|
3538
|
+
init_colors();
|
|
3539
|
+
dockerStyles = {
|
|
3540
|
+
balanced: (data, colors2) => {
|
|
3541
|
+
const { running, total } = data.status;
|
|
3542
|
+
if (running === 0) return "";
|
|
3543
|
+
const status = running > 0 ? "\u{1F7E2}" : "\u26AA";
|
|
3544
|
+
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
3545
|
+
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
3546
|
+
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
3547
|
+
return `${label} ${coloredCount} ${status}`;
|
|
3548
|
+
},
|
|
3549
|
+
compact: (data, colors2) => {
|
|
3550
|
+
const { running, total } = data.status;
|
|
3551
|
+
if (running === 0) return "";
|
|
3552
|
+
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
3553
|
+
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
3554
|
+
return `\u{1F433} ${coloredCount}`;
|
|
3555
|
+
},
|
|
3556
|
+
playful: (data, colors2) => {
|
|
3557
|
+
const { running, total } = data.status;
|
|
3558
|
+
if (running === 0) return "\u{1F433} Docker: \u{1F4A4}";
|
|
3559
|
+
const status = running > 0 ? "\u{1F7E2}" : "\u26AA";
|
|
3560
|
+
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
3561
|
+
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
3562
|
+
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
3563
|
+
return `\u{1F433} ${label} ${coloredCount} ${status}`;
|
|
3564
|
+
},
|
|
3565
|
+
verbose: (data, colors2) => {
|
|
3566
|
+
const { running, total } = data.status;
|
|
3567
|
+
if (running === 0) {
|
|
3568
|
+
const label2 = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
3569
|
+
return `${label2} no containers running`;
|
|
3570
|
+
}
|
|
3571
|
+
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
3572
|
+
const coloredRunning = colors2 ? colorize(String(running), colors2.count) : String(running);
|
|
3573
|
+
return `${label} ${coloredRunning} running${total > running ? ` / ${total} total` : ""}`;
|
|
3574
|
+
},
|
|
3575
|
+
labeled: (data, colors2) => {
|
|
3576
|
+
const { running, total } = data.status;
|
|
3577
|
+
if (running === 0) {
|
|
3578
|
+
const label2 = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
3579
|
+
return `${label2} --`;
|
|
3580
|
+
}
|
|
3581
|
+
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
3582
|
+
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
3583
|
+
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
3584
|
+
return `${label} ${coloredCount}`;
|
|
3585
|
+
},
|
|
3586
|
+
indicator: (data, colors2) => {
|
|
3587
|
+
const { running, total } = data.status;
|
|
3588
|
+
if (running === 0) {
|
|
3589
|
+
const label2 = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
3590
|
+
return `\u25CF ${label2} --`;
|
|
3591
|
+
}
|
|
3592
|
+
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
3593
|
+
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
3594
|
+
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
3595
|
+
return `\u25CF ${label} ${coloredCount}`;
|
|
3596
|
+
}
|
|
3597
|
+
};
|
|
3598
|
+
}
|
|
3599
|
+
});
|
|
3600
|
+
|
|
3601
|
+
// src/widgets/docker/docker-widget.ts
|
|
3602
|
+
var import_node_child_process3, import_node_util3, execFileAsync3, DockerWidget;
|
|
3603
|
+
var init_docker_widget = __esm({
|
|
3604
|
+
"src/widgets/docker/docker-widget.ts"() {
|
|
3605
|
+
"use strict";
|
|
3606
|
+
import_node_child_process3 = require("node:child_process");
|
|
3607
|
+
import_node_util3 = require("node:util");
|
|
3608
|
+
init_widget_types();
|
|
3609
|
+
init_theme();
|
|
3610
|
+
init_styles7();
|
|
3611
|
+
execFileAsync3 = (0, import_node_util3.promisify)(import_node_child_process3.execFile);
|
|
3612
|
+
DockerWidget = class {
|
|
3613
|
+
id = "docker";
|
|
3614
|
+
metadata = createWidgetMetadata(
|
|
3615
|
+
"Docker",
|
|
3616
|
+
"Shows Docker container count and status",
|
|
3617
|
+
"1.0.0",
|
|
3618
|
+
"claude-scope",
|
|
3619
|
+
0
|
|
3620
|
+
);
|
|
3621
|
+
enabled = true;
|
|
3622
|
+
colors;
|
|
3623
|
+
_lineOverride;
|
|
3624
|
+
styleFn = dockerStyles.balanced;
|
|
3625
|
+
cachedStatus = null;
|
|
3626
|
+
lastCheck = 0;
|
|
3627
|
+
CACHE_TTL = 5e3;
|
|
3628
|
+
constructor(colors2) {
|
|
3629
|
+
this.colors = colors2 ?? DEFAULT_THEME;
|
|
3630
|
+
}
|
|
3631
|
+
setStyle(style = "balanced") {
|
|
3632
|
+
const fn = dockerStyles[style];
|
|
3633
|
+
if (fn) {
|
|
3634
|
+
this.styleFn = fn;
|
|
3635
|
+
}
|
|
3636
|
+
}
|
|
3637
|
+
setLine(line) {
|
|
3638
|
+
this._lineOverride = line;
|
|
3639
|
+
}
|
|
3640
|
+
getLine() {
|
|
3641
|
+
return this._lineOverride ?? this.metadata.line ?? 0;
|
|
3642
|
+
}
|
|
3643
|
+
async initialize(context) {
|
|
3644
|
+
this.enabled = context.config?.enabled !== false;
|
|
3645
|
+
}
|
|
3646
|
+
async update(_data) {
|
|
3647
|
+
}
|
|
3648
|
+
isEnabled() {
|
|
3649
|
+
return this.enabled;
|
|
3650
|
+
}
|
|
3651
|
+
async cleanup() {
|
|
3652
|
+
}
|
|
3653
|
+
async render(_context) {
|
|
3654
|
+
if (!this.enabled) {
|
|
3655
|
+
return null;
|
|
3656
|
+
}
|
|
3657
|
+
const now = Date.now();
|
|
3658
|
+
if (this.cachedStatus && now - this.lastCheck < this.CACHE_TTL) {
|
|
3659
|
+
return this.styleFn({ status: this.cachedStatus }, this.colors.docker);
|
|
3660
|
+
}
|
|
3661
|
+
const status = await this.getDockerStatus();
|
|
3662
|
+
this.cachedStatus = status;
|
|
3663
|
+
this.lastCheck = now;
|
|
3664
|
+
if (!status.isAvailable) {
|
|
3665
|
+
return null;
|
|
3666
|
+
}
|
|
3667
|
+
return this.styleFn({ status }, this.colors.docker);
|
|
3668
|
+
}
|
|
3669
|
+
async getDockerStatus() {
|
|
3670
|
+
try {
|
|
3671
|
+
await execFileAsync3("docker", ["info"], { timeout: 2e3 });
|
|
3672
|
+
const { stdout: runningOutput } = await execFileAsync3("docker", ["ps", "-q"], {
|
|
3673
|
+
timeout: 1e3
|
|
3674
|
+
});
|
|
3675
|
+
const running = runningOutput.trim().split("\n").filter((line) => line).length;
|
|
3676
|
+
const { stdout: allOutput } = await execFileAsync3("docker", ["ps", "-aq"], {
|
|
3677
|
+
timeout: 1e3
|
|
3678
|
+
});
|
|
3679
|
+
const total = allOutput.trim().split("\n").filter((line) => line).length;
|
|
3680
|
+
return { running, total, isAvailable: true };
|
|
3681
|
+
} catch {
|
|
3682
|
+
return { running: 0, total: 0, isAvailable: false };
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3687
|
+
});
|
|
3688
|
+
|
|
3192
3689
|
// src/widgets/duration/styles.ts
|
|
3193
3690
|
function formatDurationWithColors(ms, colors2) {
|
|
3194
3691
|
if (ms <= 0) return colorize("0s", colors2.value);
|
|
@@ -3214,7 +3711,7 @@ function formatDurationWithColors(ms, colors2) {
|
|
|
3214
3711
|
return parts.join(" ");
|
|
3215
3712
|
}
|
|
3216
3713
|
var durationStyles;
|
|
3217
|
-
var
|
|
3714
|
+
var init_styles8 = __esm({
|
|
3218
3715
|
"src/widgets/duration/styles.ts"() {
|
|
3219
3716
|
"use strict";
|
|
3220
3717
|
init_colors();
|
|
@@ -3294,7 +3791,7 @@ var init_duration_widget = __esm({
|
|
|
3294
3791
|
init_widget_types();
|
|
3295
3792
|
init_theme();
|
|
3296
3793
|
init_stdin_data_widget();
|
|
3297
|
-
|
|
3794
|
+
init_styles8();
|
|
3298
3795
|
DurationWidget = class extends StdinDataWidget {
|
|
3299
3796
|
id = "duration";
|
|
3300
3797
|
metadata = createWidgetMetadata(
|
|
@@ -3339,13 +3836,13 @@ var init_duration_widget = __esm({
|
|
|
3339
3836
|
function createGit(cwd) {
|
|
3340
3837
|
return new NativeGit(cwd);
|
|
3341
3838
|
}
|
|
3342
|
-
var
|
|
3839
|
+
var import_node_child_process4, import_node_util4, execFileAsync4, NativeGit;
|
|
3343
3840
|
var init_git_provider = __esm({
|
|
3344
3841
|
"src/providers/git-provider.ts"() {
|
|
3345
3842
|
"use strict";
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3843
|
+
import_node_child_process4 = require("node:child_process");
|
|
3844
|
+
import_node_util4 = require("node:util");
|
|
3845
|
+
execFileAsync4 = (0, import_node_util4.promisify)(import_node_child_process4.execFile);
|
|
3349
3846
|
NativeGit = class {
|
|
3350
3847
|
cwd;
|
|
3351
3848
|
constructor(cwd) {
|
|
@@ -3353,7 +3850,7 @@ var init_git_provider = __esm({
|
|
|
3353
3850
|
}
|
|
3354
3851
|
async status() {
|
|
3355
3852
|
try {
|
|
3356
|
-
const { stdout } = await
|
|
3853
|
+
const { stdout } = await execFileAsync4("git", ["status", "--branch", "--short"], {
|
|
3357
3854
|
cwd: this.cwd
|
|
3358
3855
|
});
|
|
3359
3856
|
const match = stdout.match(/^##\s+(\S+)/m);
|
|
@@ -3369,7 +3866,7 @@ var init_git_provider = __esm({
|
|
|
3369
3866
|
args.push(...options);
|
|
3370
3867
|
}
|
|
3371
3868
|
try {
|
|
3372
|
-
const { stdout } = await
|
|
3869
|
+
const { stdout } = await execFileAsync4("git", args, {
|
|
3373
3870
|
cwd: this.cwd
|
|
3374
3871
|
});
|
|
3375
3872
|
const fileMatch = stdout.match(/(\d+)\s+file(s?)\s+changed/);
|
|
@@ -3386,7 +3883,7 @@ var init_git_provider = __esm({
|
|
|
3386
3883
|
}
|
|
3387
3884
|
async latestTag() {
|
|
3388
3885
|
try {
|
|
3389
|
-
const { stdout } = await
|
|
3886
|
+
const { stdout } = await execFileAsync4("git", ["describe", "--tags", "--abbrev=0"], {
|
|
3390
3887
|
cwd: this.cwd
|
|
3391
3888
|
});
|
|
3392
3889
|
return stdout.trim();
|
|
@@ -3400,7 +3897,7 @@ var init_git_provider = __esm({
|
|
|
3400
3897
|
|
|
3401
3898
|
// src/widgets/git-tag/styles.ts
|
|
3402
3899
|
var gitTagStyles;
|
|
3403
|
-
var
|
|
3900
|
+
var init_styles9 = __esm({
|
|
3404
3901
|
"src/widgets/git-tag/styles.ts"() {
|
|
3405
3902
|
"use strict";
|
|
3406
3903
|
init_colors();
|
|
@@ -3450,7 +3947,7 @@ var init_git_tag_widget = __esm({
|
|
|
3450
3947
|
init_widget_types();
|
|
3451
3948
|
init_git_provider();
|
|
3452
3949
|
init_theme();
|
|
3453
|
-
|
|
3950
|
+
init_styles9();
|
|
3454
3951
|
GitTagWidget = class {
|
|
3455
3952
|
id = "git-tag";
|
|
3456
3953
|
metadata = createWidgetMetadata(
|
|
@@ -3522,7 +4019,7 @@ var init_git_tag_widget = __esm({
|
|
|
3522
4019
|
|
|
3523
4020
|
// src/widgets/git/styles.ts
|
|
3524
4021
|
var gitStyles;
|
|
3525
|
-
var
|
|
4022
|
+
var init_styles10 = __esm({
|
|
3526
4023
|
"src/widgets/git/styles.ts"() {
|
|
3527
4024
|
"use strict";
|
|
3528
4025
|
init_colors();
|
|
@@ -3624,7 +4121,7 @@ var init_git_widget = __esm({
|
|
|
3624
4121
|
init_widget_types();
|
|
3625
4122
|
init_git_provider();
|
|
3626
4123
|
init_theme();
|
|
3627
|
-
|
|
4124
|
+
init_styles10();
|
|
3628
4125
|
GitWidget = class {
|
|
3629
4126
|
id = "git";
|
|
3630
4127
|
metadata = createWidgetMetadata(
|
|
@@ -3716,7 +4213,7 @@ var init_git_widget = __esm({
|
|
|
3716
4213
|
|
|
3717
4214
|
// src/widgets/lines/styles.ts
|
|
3718
4215
|
var linesStyles;
|
|
3719
|
-
var
|
|
4216
|
+
var init_styles11 = __esm({
|
|
3720
4217
|
"src/widgets/lines/styles.ts"() {
|
|
3721
4218
|
"use strict";
|
|
3722
4219
|
init_colors();
|
|
@@ -3776,7 +4273,7 @@ var init_lines_widget = __esm({
|
|
|
3776
4273
|
init_widget_types();
|
|
3777
4274
|
init_theme();
|
|
3778
4275
|
init_stdin_data_widget();
|
|
3779
|
-
|
|
4276
|
+
init_styles11();
|
|
3780
4277
|
LinesWidget = class extends StdinDataWidget {
|
|
3781
4278
|
id = "lines";
|
|
3782
4279
|
metadata = createWidgetMetadata(
|
|
@@ -3821,7 +4318,7 @@ function getShortName(displayName) {
|
|
|
3821
4318
|
return displayName.replace(/^Claude\s+/, "");
|
|
3822
4319
|
}
|
|
3823
4320
|
var modelStyles;
|
|
3824
|
-
var
|
|
4321
|
+
var init_styles12 = __esm({
|
|
3825
4322
|
"src/widgets/model/styles.ts"() {
|
|
3826
4323
|
"use strict";
|
|
3827
4324
|
init_colors();
|
|
@@ -3876,7 +4373,7 @@ var init_model_widget = __esm({
|
|
|
3876
4373
|
init_widget_types();
|
|
3877
4374
|
init_theme();
|
|
3878
4375
|
init_stdin_data_widget();
|
|
3879
|
-
|
|
4376
|
+
init_styles12();
|
|
3880
4377
|
ModelWidget = class extends StdinDataWidget {
|
|
3881
4378
|
id = "model";
|
|
3882
4379
|
metadata = createWidgetMetadata(
|
|
@@ -4018,6 +4515,16 @@ async function registerWidgetsFromConfig(registry, config, style, themeName) {
|
|
|
4018
4515
|
const w = new CacheMetricsWidget(themeColors);
|
|
4019
4516
|
w.setStyle(s);
|
|
4020
4517
|
return w;
|
|
4518
|
+
},
|
|
4519
|
+
"dev-server": (s) => {
|
|
4520
|
+
const w = new DevServerWidget(themeColors);
|
|
4521
|
+
w.setStyle(s);
|
|
4522
|
+
return w;
|
|
4523
|
+
},
|
|
4524
|
+
docker: (s) => {
|
|
4525
|
+
const w = new DockerWidget(themeColors);
|
|
4526
|
+
w.setStyle(s);
|
|
4527
|
+
return w;
|
|
4021
4528
|
}
|
|
4022
4529
|
};
|
|
4023
4530
|
for (const [lineNum, widgets] of Object.entries(config.lines)) {
|
|
@@ -4065,6 +4572,8 @@ var init_layout_preview = __esm({
|
|
|
4065
4572
|
init_config_count_widget();
|
|
4066
4573
|
init_context_widget();
|
|
4067
4574
|
init_cost_widget();
|
|
4575
|
+
init_dev_server_widget();
|
|
4576
|
+
init_docker_widget();
|
|
4068
4577
|
init_duration_widget();
|
|
4069
4578
|
init_git_tag_widget();
|
|
4070
4579
|
init_git_widget();
|
|
@@ -6639,10 +7148,10 @@ var init_esm2 = __esm({
|
|
|
6639
7148
|
});
|
|
6640
7149
|
|
|
6641
7150
|
// node_modules/@inquirer/core/dist/esm/lib/screen-manager.js
|
|
6642
|
-
var
|
|
7151
|
+
var import_node_util5, height, lastLine, ScreenManager;
|
|
6643
7152
|
var init_screen_manager = __esm({
|
|
6644
7153
|
"node_modules/@inquirer/core/dist/esm/lib/screen-manager.js"() {
|
|
6645
|
-
|
|
7154
|
+
import_node_util5 = require("node:util");
|
|
6646
7155
|
init_utils();
|
|
6647
7156
|
init_esm2();
|
|
6648
7157
|
height = (content) => content.split("\n").length;
|
|
@@ -6664,7 +7173,7 @@ var init_screen_manager = __esm({
|
|
|
6664
7173
|
}
|
|
6665
7174
|
render(content, bottomContent = "") {
|
|
6666
7175
|
const promptLine = lastLine(content);
|
|
6667
|
-
const rawPromptLine = (0,
|
|
7176
|
+
const rawPromptLine = (0, import_node_util5.stripVTControlCharacters)(promptLine);
|
|
6668
7177
|
let prompt = rawPromptLine;
|
|
6669
7178
|
if (this.rl.line.length > 0) {
|
|
6670
7179
|
prompt = prompt.slice(0, -this.rl.line.length);
|
|
@@ -7548,7 +8057,7 @@ async function ensureDefaultConfig() {
|
|
|
7548
8057
|
if (!(0, import_node_fs3.existsSync)(configDir)) {
|
|
7549
8058
|
(0, import_node_fs3.mkdirSync)(configDir, { recursive: true });
|
|
7550
8059
|
}
|
|
7551
|
-
const defaultConfig =
|
|
8060
|
+
const defaultConfig = generateRichLayout("balanced", "dracula");
|
|
7552
8061
|
(0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
|
|
7553
8062
|
}
|
|
7554
8063
|
|
|
@@ -7590,435 +8099,13 @@ init_config_count_widget();
|
|
|
7590
8099
|
init_context_widget();
|
|
7591
8100
|
init_cost_widget();
|
|
7592
8101
|
|
|
7593
|
-
// src/widgets/dev-server/
|
|
7594
|
-
|
|
7595
|
-
|
|
7596
|
-
|
|
7597
|
-
// src/widgets/dev-server/port-detector.ts
|
|
7598
|
-
var import_node_child_process2 = require("node:child_process");
|
|
7599
|
-
var import_node_util3 = require("node:util");
|
|
7600
|
-
var execFileAsync2 = (0, import_node_util3.promisify)(import_node_child_process2.execFile);
|
|
7601
|
-
var PORT_MAPPING = {
|
|
7602
|
-
5173: { name: "Vite", icon: "\u26A1" },
|
|
7603
|
-
4200: { name: "Angular", icon: "\u25B2" },
|
|
7604
|
-
3e3: { name: "Dev", icon: "\u{1F680}" },
|
|
7605
|
-
8080: { name: "Webpack", icon: "\u{1F4E6}" },
|
|
7606
|
-
8e3: { name: "Dev", icon: "\u{1F680}" },
|
|
7607
|
-
8888: { name: "Dev", icon: "\u{1F680}" }
|
|
7608
|
-
};
|
|
7609
|
-
var DEV_PORTS = [5173, 4200, 3e3, 8080, 8e3, 8888];
|
|
7610
|
-
var PortDetector = class {
|
|
7611
|
-
execFn;
|
|
7612
|
-
/**
|
|
7613
|
-
* Create a new PortDetector
|
|
7614
|
-
* @param execFn - Optional execFile function for testing (dependency injection)
|
|
7615
|
-
*/
|
|
7616
|
-
constructor(execFn) {
|
|
7617
|
-
this.execFn = execFn ?? execFileAsync2;
|
|
7618
|
-
}
|
|
7619
|
-
/**
|
|
7620
|
-
* Detect running dev servers by checking listening ports
|
|
7621
|
-
* @returns Detected server info or null
|
|
7622
|
-
*/
|
|
7623
|
-
async detect() {
|
|
7624
|
-
try {
|
|
7625
|
-
const args = [
|
|
7626
|
-
"-nP",
|
|
7627
|
-
// No host names, no port names
|
|
7628
|
-
"-iTCP",
|
|
7629
|
-
// Internet TCP
|
|
7630
|
-
"-sTCP:LISTEN"
|
|
7631
|
-
// TCP LISTEN state only
|
|
7632
|
-
];
|
|
7633
|
-
for (const port of DEV_PORTS) {
|
|
7634
|
-
args.push("-i", `:${port}`);
|
|
7635
|
-
}
|
|
7636
|
-
const { stdout } = await this.execFn("lsof", args, {
|
|
7637
|
-
timeout: 2e3
|
|
7638
|
-
});
|
|
7639
|
-
const lines = stdout.trim().split("\n");
|
|
7640
|
-
for (const line of lines) {
|
|
7641
|
-
if (!line || line.startsWith("COMMAND")) continue;
|
|
7642
|
-
const match = line.match(/:(\d+)\s*\(LISTEN\)/);
|
|
7643
|
-
if (match) {
|
|
7644
|
-
const port = parseInt(match[1], 10);
|
|
7645
|
-
const mapping = PORT_MAPPING[port];
|
|
7646
|
-
if (mapping) {
|
|
7647
|
-
return {
|
|
7648
|
-
name: mapping.name,
|
|
7649
|
-
icon: mapping.icon,
|
|
7650
|
-
port,
|
|
7651
|
-
isRunning: true,
|
|
7652
|
-
isBuilding: false
|
|
7653
|
-
};
|
|
7654
|
-
}
|
|
7655
|
-
}
|
|
7656
|
-
}
|
|
7657
|
-
return null;
|
|
7658
|
-
} catch {
|
|
7659
|
-
return null;
|
|
7660
|
-
}
|
|
7661
|
-
}
|
|
7662
|
-
};
|
|
7663
|
-
|
|
7664
|
-
// src/widgets/dev-server/process-detector.ts
|
|
7665
|
-
var import_node_child_process3 = require("node:child_process");
|
|
7666
|
-
var import_node_util4 = require("node:util");
|
|
7667
|
-
var execFileAsync3 = (0, import_node_util4.promisify)(import_node_child_process3.execFile);
|
|
7668
|
-
var ProcessDetector = class {
|
|
7669
|
-
execFn;
|
|
7670
|
-
processPatterns = [
|
|
7671
|
-
// Generic server patterns - more specific to avoid shell history false positives
|
|
7672
|
-
{ regex: /^[\w\s]+\/npm\s+(exec|run)\s+serve/i, name: "Server", icon: "\u{1F310}" },
|
|
7673
|
-
{ regex: /^[\w\s]+\/npx\s+serve\s+-/i, name: "Server", icon: "\u{1F310}" },
|
|
7674
|
-
{ regex: /^[\w\s]+\/(python|python3)\s+-m\s+http\.server/i, name: "HTTP", icon: "\u{1F310}" },
|
|
7675
|
-
// Generic dev/build patterns - require full command path
|
|
7676
|
-
{ regex: /^[\w\s]+\/(npm|yarn|pnpm|bun)\s+run\s+dev\s*$/i, name: "Dev", icon: "\u{1F680}" },
|
|
7677
|
-
{ regex: /^[\w\s]+\/(npm|yarn|pnpm|bun)\s+run\s+build\s*$/i, name: "Build", icon: "\u{1F528}" },
|
|
7678
|
-
// Framework-specific patterns - require executable path
|
|
7679
|
-
{ regex: /\/(nuxt|next|astro|remix|svelte)\s+dev/i, name: "Framework", icon: "\u26A1" },
|
|
7680
|
-
{ regex: /\/node.*\/vite\s*$/i, name: "Vite", icon: "\u26A1" },
|
|
7681
|
-
// Fallback: simpler patterns but checked last
|
|
7682
|
-
{ regex: /\s(nuxt|next|vite)\s+dev\s/i, name: "DevServer", icon: "\u26A1" }
|
|
7683
|
-
];
|
|
7684
|
-
/**
|
|
7685
|
-
* Create a new ProcessDetector
|
|
7686
|
-
* @param execFn - Optional execFile function for testing (dependency injection)
|
|
7687
|
-
*/
|
|
7688
|
-
constructor(execFn) {
|
|
7689
|
-
this.execFn = execFn ?? execFileAsync3;
|
|
7690
|
-
}
|
|
7691
|
-
/**
|
|
7692
|
-
* Detect running dev server by parsing system process list
|
|
7693
|
-
* @returns Detected server status or null
|
|
7694
|
-
*/
|
|
7695
|
-
async detect() {
|
|
7696
|
-
try {
|
|
7697
|
-
const { stdout } = await this.execFn("ps", ["aux"], {
|
|
7698
|
-
timeout: 1e3
|
|
7699
|
-
});
|
|
7700
|
-
for (const pattern of this.processPatterns) {
|
|
7701
|
-
if (pattern.regex.test(stdout)) {
|
|
7702
|
-
const isBuilding = pattern.name.toLowerCase().includes("build");
|
|
7703
|
-
const isRunning = !isBuilding;
|
|
7704
|
-
return {
|
|
7705
|
-
name: pattern.name,
|
|
7706
|
-
icon: pattern.icon,
|
|
7707
|
-
isRunning,
|
|
7708
|
-
isBuilding
|
|
7709
|
-
};
|
|
7710
|
-
}
|
|
7711
|
-
}
|
|
7712
|
-
} catch {
|
|
7713
|
-
}
|
|
7714
|
-
return null;
|
|
7715
|
-
}
|
|
7716
|
-
};
|
|
7717
|
-
|
|
7718
|
-
// src/widgets/dev-server/styles.ts
|
|
7719
|
-
init_colors();
|
|
7720
|
-
var devServerStyles = {
|
|
7721
|
-
balanced: (data, colors2) => {
|
|
7722
|
-
if (!data.server) return "";
|
|
7723
|
-
const { name, icon, isRunning, isBuilding } = data.server;
|
|
7724
|
-
const status = isRunning ? "running" : isBuilding ? "building" : "stopped";
|
|
7725
|
-
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
7726
|
-
const coloredStatus = colors2 ? colorize(`(${status})`, colors2.status) : `(${status})`;
|
|
7727
|
-
return `${icon} ${coloredName} ${coloredStatus}`;
|
|
7728
|
-
},
|
|
7729
|
-
compact: (data, colors2) => {
|
|
7730
|
-
if (!data.server) return "";
|
|
7731
|
-
const { name, icon, isRunning, isBuilding } = data.server;
|
|
7732
|
-
const statusIcon = isRunning ? "\u{1F680}" : isBuilding ? "\u{1F528}" : "\u{1F4A4}";
|
|
7733
|
-
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
7734
|
-
return `${icon} ${coloredName} ${statusIcon}`;
|
|
7735
|
-
},
|
|
7736
|
-
playful: (data, colors2) => {
|
|
7737
|
-
if (!data.server) return "";
|
|
7738
|
-
const { name, isRunning, isBuilding } = data.server;
|
|
7739
|
-
const emoji = isRunning ? "\u{1F3C3}" : isBuilding ? "\u{1F528}" : "\u{1F4A4}";
|
|
7740
|
-
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
7741
|
-
return `${emoji} ${coloredName}`;
|
|
7742
|
-
},
|
|
7743
|
-
verbose: (data, colors2) => {
|
|
7744
|
-
if (!data.server) return "";
|
|
7745
|
-
const { name, isRunning, isBuilding } = data.server;
|
|
7746
|
-
const status = isRunning ? "running" : isBuilding ? "building" : "stopped";
|
|
7747
|
-
const label = colors2 ? colorize("Dev Server:", colors2.label) : "Dev Server:";
|
|
7748
|
-
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
7749
|
-
const coloredStatus = colors2 ? colorize(`(${status})`, colors2.status) : `(${status})`;
|
|
7750
|
-
return `${label} ${coloredName} ${coloredStatus}`;
|
|
7751
|
-
},
|
|
7752
|
-
labeled: (data, colors2) => {
|
|
7753
|
-
if (!data.server) return "";
|
|
7754
|
-
const { name, icon, isRunning } = data.server;
|
|
7755
|
-
const status = isRunning ? "\u{1F7E2}" : "\u{1F534}";
|
|
7756
|
-
const label = colors2 ? colorize("Server:", colors2.label) : "Server:";
|
|
7757
|
-
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
7758
|
-
return `${label} ${icon} ${coloredName} ${status}`;
|
|
7759
|
-
},
|
|
7760
|
-
indicator: (data, colors2) => {
|
|
7761
|
-
if (!data.server) return "";
|
|
7762
|
-
const { name, icon } = data.server;
|
|
7763
|
-
const coloredName = colors2 ? colorize(name, colors2.name) : name;
|
|
7764
|
-
return `\u25CF ${icon} ${coloredName}`;
|
|
7765
|
-
}
|
|
7766
|
-
};
|
|
8102
|
+
// src/widgets/dev-server/index.ts
|
|
8103
|
+
init_dev_server_widget();
|
|
8104
|
+
init_port_detector();
|
|
8105
|
+
init_process_detector();
|
|
7767
8106
|
|
|
7768
|
-
// src/widgets/
|
|
7769
|
-
|
|
7770
|
-
id = "dev-server";
|
|
7771
|
-
metadata = createWidgetMetadata(
|
|
7772
|
-
"Dev Server",
|
|
7773
|
-
"Detects running dev server processes using hybrid port+process detection",
|
|
7774
|
-
"1.1.0",
|
|
7775
|
-
"claude-scope",
|
|
7776
|
-
0
|
|
7777
|
-
);
|
|
7778
|
-
enabled = true;
|
|
7779
|
-
colors;
|
|
7780
|
-
_lineOverride;
|
|
7781
|
-
styleFn = devServerStyles.balanced;
|
|
7782
|
-
cwd = null;
|
|
7783
|
-
portDetector;
|
|
7784
|
-
processDetector;
|
|
7785
|
-
constructor(colors2) {
|
|
7786
|
-
this.colors = colors2 ?? DEFAULT_THEME;
|
|
7787
|
-
this.portDetector = new PortDetector();
|
|
7788
|
-
this.processDetector = new ProcessDetector();
|
|
7789
|
-
}
|
|
7790
|
-
/**
|
|
7791
|
-
* Set display style
|
|
7792
|
-
* @param style - Style to use for rendering
|
|
7793
|
-
*/
|
|
7794
|
-
setStyle(style = "balanced") {
|
|
7795
|
-
const fn = devServerStyles[style];
|
|
7796
|
-
if (fn) {
|
|
7797
|
-
this.styleFn = fn;
|
|
7798
|
-
}
|
|
7799
|
-
}
|
|
7800
|
-
/**
|
|
7801
|
-
* Set display line override
|
|
7802
|
-
* @param line - Line number (0-indexed)
|
|
7803
|
-
*/
|
|
7804
|
-
setLine(line) {
|
|
7805
|
-
this._lineOverride = line;
|
|
7806
|
-
}
|
|
7807
|
-
/**
|
|
7808
|
-
* Get display line
|
|
7809
|
-
* @returns Line number (0-indexed)
|
|
7810
|
-
*/
|
|
7811
|
-
getLine() {
|
|
7812
|
-
return this._lineOverride ?? this.metadata.line ?? 0;
|
|
7813
|
-
}
|
|
7814
|
-
/**
|
|
7815
|
-
* Initialize widget with context
|
|
7816
|
-
* @param context - Widget initialization context
|
|
7817
|
-
*/
|
|
7818
|
-
async initialize(context) {
|
|
7819
|
-
this.enabled = context.config?.enabled !== false;
|
|
7820
|
-
}
|
|
7821
|
-
/**
|
|
7822
|
-
* Update widget with new stdin data
|
|
7823
|
-
* @param data - Stdin data from Claude Code
|
|
7824
|
-
*/
|
|
7825
|
-
async update(data) {
|
|
7826
|
-
this.cwd = data.cwd;
|
|
7827
|
-
}
|
|
7828
|
-
/**
|
|
7829
|
-
* Check if widget is enabled
|
|
7830
|
-
* @returns true if widget should render
|
|
7831
|
-
*/
|
|
7832
|
-
isEnabled() {
|
|
7833
|
-
return this.enabled;
|
|
7834
|
-
}
|
|
7835
|
-
/**
|
|
7836
|
-
* Cleanup resources
|
|
7837
|
-
*/
|
|
7838
|
-
async cleanup() {
|
|
7839
|
-
}
|
|
7840
|
-
/**
|
|
7841
|
-
* Render widget output
|
|
7842
|
-
* @param context - Render context
|
|
7843
|
-
* @returns Rendered string, or null if no dev server detected
|
|
7844
|
-
*/
|
|
7845
|
-
async render(_context) {
|
|
7846
|
-
if (!this.enabled || !this.cwd) {
|
|
7847
|
-
return null;
|
|
7848
|
-
}
|
|
7849
|
-
const server = await this.detectDevServer();
|
|
7850
|
-
if (!server) {
|
|
7851
|
-
return null;
|
|
7852
|
-
}
|
|
7853
|
-
const renderData = { server };
|
|
7854
|
-
return this.styleFn(renderData, this.colors.devServer);
|
|
7855
|
-
}
|
|
7856
|
-
/**
|
|
7857
|
-
* Detect running dev server using hybrid approach
|
|
7858
|
-
*
|
|
7859
|
-
* 1. Try port-based detection first (more reliable)
|
|
7860
|
-
* 2. Fall back to process-based detection
|
|
7861
|
-
*
|
|
7862
|
-
* @returns Detected server status or null
|
|
7863
|
-
*/
|
|
7864
|
-
async detectDevServer() {
|
|
7865
|
-
const portResult = await this.portDetector.detect();
|
|
7866
|
-
if (portResult) {
|
|
7867
|
-
return {
|
|
7868
|
-
name: portResult.name,
|
|
7869
|
-
icon: portResult.icon,
|
|
7870
|
-
isRunning: portResult.isRunning,
|
|
7871
|
-
isBuilding: portResult.isBuilding
|
|
7872
|
-
};
|
|
7873
|
-
}
|
|
7874
|
-
return await this.processDetector.detect();
|
|
7875
|
-
}
|
|
7876
|
-
};
|
|
7877
|
-
|
|
7878
|
-
// src/widgets/docker/docker-widget.ts
|
|
7879
|
-
var import_node_child_process4 = require("node:child_process");
|
|
7880
|
-
var import_node_util5 = require("node:util");
|
|
7881
|
-
init_widget_types();
|
|
7882
|
-
init_theme();
|
|
7883
|
-
|
|
7884
|
-
// src/widgets/docker/styles.ts
|
|
7885
|
-
init_colors();
|
|
7886
|
-
var dockerStyles = {
|
|
7887
|
-
balanced: (data, colors2) => {
|
|
7888
|
-
const { running, total } = data.status;
|
|
7889
|
-
if (running === 0) return "";
|
|
7890
|
-
const status = running > 0 ? "\u{1F7E2}" : "\u26AA";
|
|
7891
|
-
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
7892
|
-
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
7893
|
-
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
7894
|
-
return `${label} ${coloredCount} ${status}`;
|
|
7895
|
-
},
|
|
7896
|
-
compact: (data, colors2) => {
|
|
7897
|
-
const { running, total } = data.status;
|
|
7898
|
-
if (running === 0) return "";
|
|
7899
|
-
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
7900
|
-
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
7901
|
-
return `\u{1F433} ${coloredCount}`;
|
|
7902
|
-
},
|
|
7903
|
-
playful: (data, colors2) => {
|
|
7904
|
-
const { running, total } = data.status;
|
|
7905
|
-
if (running === 0) return "\u{1F433} Docker: \u{1F4A4}";
|
|
7906
|
-
const status = running > 0 ? "\u{1F7E2}" : "\u26AA";
|
|
7907
|
-
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
7908
|
-
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
7909
|
-
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
7910
|
-
return `\u{1F433} ${label} ${coloredCount} ${status}`;
|
|
7911
|
-
},
|
|
7912
|
-
verbose: (data, colors2) => {
|
|
7913
|
-
const { running, total } = data.status;
|
|
7914
|
-
if (running === 0) {
|
|
7915
|
-
const label2 = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
7916
|
-
return `${label2} no containers running`;
|
|
7917
|
-
}
|
|
7918
|
-
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
7919
|
-
const coloredRunning = colors2 ? colorize(String(running), colors2.count) : String(running);
|
|
7920
|
-
return `${label} ${coloredRunning} running${total > running ? ` / ${total} total` : ""}`;
|
|
7921
|
-
},
|
|
7922
|
-
labeled: (data, colors2) => {
|
|
7923
|
-
const { running, total } = data.status;
|
|
7924
|
-
if (running === 0) {
|
|
7925
|
-
const label2 = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
7926
|
-
return `${label2} --`;
|
|
7927
|
-
}
|
|
7928
|
-
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
7929
|
-
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
7930
|
-
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
7931
|
-
return `${label} ${coloredCount}`;
|
|
7932
|
-
},
|
|
7933
|
-
indicator: (data, colors2) => {
|
|
7934
|
-
const { running, total } = data.status;
|
|
7935
|
-
if (running === 0) {
|
|
7936
|
-
const label2 = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
7937
|
-
return `\u25CF ${label2} --`;
|
|
7938
|
-
}
|
|
7939
|
-
const count = total > running ? `${running}/${total}` : `${running}`;
|
|
7940
|
-
const label = colors2 ? colorize("Docker:", colors2.label) : "Docker:";
|
|
7941
|
-
const coloredCount = colors2 ? colorize(count, colors2.count) : count;
|
|
7942
|
-
return `\u25CF ${label} ${coloredCount}`;
|
|
7943
|
-
}
|
|
7944
|
-
};
|
|
7945
|
-
|
|
7946
|
-
// src/widgets/docker/docker-widget.ts
|
|
7947
|
-
var execFileAsync4 = (0, import_node_util5.promisify)(import_node_child_process4.execFile);
|
|
7948
|
-
var DockerWidget = class {
|
|
7949
|
-
id = "docker";
|
|
7950
|
-
metadata = createWidgetMetadata(
|
|
7951
|
-
"Docker",
|
|
7952
|
-
"Shows Docker container count and status",
|
|
7953
|
-
"1.0.0",
|
|
7954
|
-
"claude-scope",
|
|
7955
|
-
0
|
|
7956
|
-
);
|
|
7957
|
-
enabled = true;
|
|
7958
|
-
colors;
|
|
7959
|
-
_lineOverride;
|
|
7960
|
-
styleFn = dockerStyles.balanced;
|
|
7961
|
-
cachedStatus = null;
|
|
7962
|
-
lastCheck = 0;
|
|
7963
|
-
CACHE_TTL = 5e3;
|
|
7964
|
-
constructor(colors2) {
|
|
7965
|
-
this.colors = colors2 ?? DEFAULT_THEME;
|
|
7966
|
-
}
|
|
7967
|
-
setStyle(style = "balanced") {
|
|
7968
|
-
const fn = dockerStyles[style];
|
|
7969
|
-
if (fn) {
|
|
7970
|
-
this.styleFn = fn;
|
|
7971
|
-
}
|
|
7972
|
-
}
|
|
7973
|
-
setLine(line) {
|
|
7974
|
-
this._lineOverride = line;
|
|
7975
|
-
}
|
|
7976
|
-
getLine() {
|
|
7977
|
-
return this._lineOverride ?? this.metadata.line ?? 0;
|
|
7978
|
-
}
|
|
7979
|
-
async initialize(context) {
|
|
7980
|
-
this.enabled = context.config?.enabled !== false;
|
|
7981
|
-
}
|
|
7982
|
-
async update(_data) {
|
|
7983
|
-
}
|
|
7984
|
-
isEnabled() {
|
|
7985
|
-
return this.enabled;
|
|
7986
|
-
}
|
|
7987
|
-
async cleanup() {
|
|
7988
|
-
}
|
|
7989
|
-
async render(_context) {
|
|
7990
|
-
if (!this.enabled) {
|
|
7991
|
-
return null;
|
|
7992
|
-
}
|
|
7993
|
-
const now = Date.now();
|
|
7994
|
-
if (this.cachedStatus && now - this.lastCheck < this.CACHE_TTL) {
|
|
7995
|
-
return this.styleFn({ status: this.cachedStatus }, this.colors.docker);
|
|
7996
|
-
}
|
|
7997
|
-
const status = await this.getDockerStatus();
|
|
7998
|
-
this.cachedStatus = status;
|
|
7999
|
-
this.lastCheck = now;
|
|
8000
|
-
if (!status.isAvailable) {
|
|
8001
|
-
return null;
|
|
8002
|
-
}
|
|
8003
|
-
return this.styleFn({ status }, this.colors.docker);
|
|
8004
|
-
}
|
|
8005
|
-
async getDockerStatus() {
|
|
8006
|
-
try {
|
|
8007
|
-
await execFileAsync4("docker", ["info"], { timeout: 2e3 });
|
|
8008
|
-
const { stdout: runningOutput } = await execFileAsync4("docker", ["ps", "-q"], {
|
|
8009
|
-
timeout: 1e3
|
|
8010
|
-
});
|
|
8011
|
-
const running = runningOutput.trim().split("\n").filter((line) => line).length;
|
|
8012
|
-
const { stdout: allOutput } = await execFileAsync4("docker", ["ps", "-aq"], {
|
|
8013
|
-
timeout: 1e3
|
|
8014
|
-
});
|
|
8015
|
-
const total = allOutput.trim().split("\n").filter((line) => line).length;
|
|
8016
|
-
return { running, total, isAvailable: true };
|
|
8017
|
-
} catch {
|
|
8018
|
-
return { running: 0, total: 0, isAvailable: false };
|
|
8019
|
-
}
|
|
8020
|
-
}
|
|
8021
|
-
};
|
|
8107
|
+
// src/widgets/docker/index.ts
|
|
8108
|
+
init_docker_widget();
|
|
8022
8109
|
|
|
8023
8110
|
// src/core/widget-factory.ts
|
|
8024
8111
|
init_duration_widget();
|