claude-scope 0.2.5 → 0.3.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/dist/claude-scope.cjs +285 -30
- package/package.json +2 -1
package/dist/claude-scope.cjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
9
|
var __export = (target, all) => {
|
|
8
10
|
for (var name in all)
|
|
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/index.ts
|
|
@@ -143,34 +153,51 @@ var Renderer = class {
|
|
|
143
153
|
this.showErrors = options.showErrors ?? false;
|
|
144
154
|
}
|
|
145
155
|
/**
|
|
146
|
-
* Render widgets into
|
|
156
|
+
* Render widgets into multiple lines with error boundaries
|
|
147
157
|
*
|
|
148
|
-
* Widgets
|
|
149
|
-
*
|
|
158
|
+
* Widgets are grouped by their metadata.line property and rendered
|
|
159
|
+
* on separate lines. Widgets that throw errors are logged (via onError
|
|
160
|
+
* callback) and skipped, allowing other widgets to continue rendering.
|
|
150
161
|
*
|
|
151
162
|
* @param widgets - Array of widgets to render
|
|
152
163
|
* @param context - Render context with width and timestamp
|
|
153
|
-
* @returns
|
|
164
|
+
* @returns Array of rendered lines (one per line number)
|
|
154
165
|
*/
|
|
155
166
|
async render(widgets, context) {
|
|
156
|
-
const
|
|
167
|
+
const lineMap = /* @__PURE__ */ new Map();
|
|
157
168
|
for (const widget of widgets) {
|
|
158
169
|
if (!widget.isEnabled()) {
|
|
159
170
|
continue;
|
|
160
171
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
172
|
+
const line = widget.metadata.line ?? 0;
|
|
173
|
+
if (!lineMap.has(line)) {
|
|
174
|
+
lineMap.set(line, []);
|
|
175
|
+
}
|
|
176
|
+
lineMap.get(line).push(widget);
|
|
177
|
+
}
|
|
178
|
+
const lines = [];
|
|
179
|
+
const sortedLines = Array.from(lineMap.entries()).sort((a, b) => a[0] - b[0]);
|
|
180
|
+
for (const [, widgetsForLine] of sortedLines) {
|
|
181
|
+
const outputs = [];
|
|
182
|
+
for (const widget of widgetsForLine) {
|
|
183
|
+
try {
|
|
184
|
+
const output = await widget.render(context);
|
|
185
|
+
if (output !== null) {
|
|
186
|
+
outputs.push(output);
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
this.handleError(error, widget);
|
|
190
|
+
if (this.showErrors) {
|
|
191
|
+
outputs.push(`${widget.id}:<err>`);
|
|
192
|
+
}
|
|
170
193
|
}
|
|
171
194
|
}
|
|
195
|
+
const line = outputs.join(this.separator);
|
|
196
|
+
if (line) {
|
|
197
|
+
lines.push(line);
|
|
198
|
+
}
|
|
172
199
|
}
|
|
173
|
-
return
|
|
200
|
+
return lines;
|
|
174
201
|
}
|
|
175
202
|
/**
|
|
176
203
|
* Set custom separator
|
|
@@ -193,12 +220,13 @@ var Renderer = class {
|
|
|
193
220
|
};
|
|
194
221
|
|
|
195
222
|
// src/core/widget-types.ts
|
|
196
|
-
function createWidgetMetadata(name, description, version = "1.0.0", author = "claude-scope") {
|
|
223
|
+
function createWidgetMetadata(name, description, version = "1.0.0", author = "claude-scope", line = 0) {
|
|
197
224
|
return {
|
|
198
225
|
name,
|
|
199
226
|
description,
|
|
200
227
|
version,
|
|
201
|
-
author
|
|
228
|
+
author,
|
|
229
|
+
line
|
|
202
230
|
};
|
|
203
231
|
}
|
|
204
232
|
|
|
@@ -252,7 +280,11 @@ var GitWidget = class {
|
|
|
252
280
|
id = "git";
|
|
253
281
|
metadata = createWidgetMetadata(
|
|
254
282
|
"Git Widget",
|
|
255
|
-
"Displays current git branch"
|
|
283
|
+
"Displays current git branch",
|
|
284
|
+
"1.0.0",
|
|
285
|
+
"claude-scope",
|
|
286
|
+
0
|
|
287
|
+
// First line
|
|
256
288
|
);
|
|
257
289
|
gitFactory;
|
|
258
290
|
git = null;
|
|
@@ -360,7 +392,11 @@ var ModelWidget = class extends StdinDataWidget {
|
|
|
360
392
|
id = "model";
|
|
361
393
|
metadata = createWidgetMetadata(
|
|
362
394
|
"Model",
|
|
363
|
-
"Displays the current Claude model name"
|
|
395
|
+
"Displays the current Claude model name",
|
|
396
|
+
"1.0.0",
|
|
397
|
+
"claude-scope",
|
|
398
|
+
0
|
|
399
|
+
// First line
|
|
364
400
|
);
|
|
365
401
|
renderWithData(data, context) {
|
|
366
402
|
return data.model.display_name;
|
|
@@ -424,7 +460,11 @@ var ContextWidget = class extends StdinDataWidget {
|
|
|
424
460
|
id = "context";
|
|
425
461
|
metadata = createWidgetMetadata(
|
|
426
462
|
"Context",
|
|
427
|
-
"Displays context window usage with progress bar"
|
|
463
|
+
"Displays context window usage with progress bar",
|
|
464
|
+
"1.0.0",
|
|
465
|
+
"claude-scope",
|
|
466
|
+
0
|
|
467
|
+
// First line
|
|
428
468
|
);
|
|
429
469
|
renderWithData(data, context) {
|
|
430
470
|
const { current_usage, context_window_size } = data.context_window;
|
|
@@ -442,7 +482,11 @@ var CostWidget = class extends StdinDataWidget {
|
|
|
442
482
|
id = "cost";
|
|
443
483
|
metadata = createWidgetMetadata(
|
|
444
484
|
"Cost",
|
|
445
|
-
"Displays session cost in USD"
|
|
485
|
+
"Displays session cost in USD",
|
|
486
|
+
"1.0.0",
|
|
487
|
+
"claude-scope",
|
|
488
|
+
0
|
|
489
|
+
// First line
|
|
446
490
|
);
|
|
447
491
|
renderWithData(data, context) {
|
|
448
492
|
if (!data.cost || data.cost.total_cost_usd === void 0) return null;
|
|
@@ -455,7 +499,11 @@ var LinesWidget = class extends StdinDataWidget {
|
|
|
455
499
|
id = "lines";
|
|
456
500
|
metadata = createWidgetMetadata(
|
|
457
501
|
"Lines",
|
|
458
|
-
"Displays lines added/removed in session"
|
|
502
|
+
"Displays lines added/removed in session",
|
|
503
|
+
"1.0.0",
|
|
504
|
+
"claude-scope",
|
|
505
|
+
0
|
|
506
|
+
// First line
|
|
459
507
|
);
|
|
460
508
|
renderWithData(data, context) {
|
|
461
509
|
const added = data.cost?.total_lines_added ?? 0;
|
|
@@ -471,7 +519,11 @@ var DurationWidget = class extends StdinDataWidget {
|
|
|
471
519
|
id = "duration";
|
|
472
520
|
metadata = createWidgetMetadata(
|
|
473
521
|
"Duration",
|
|
474
|
-
"Displays elapsed session time"
|
|
522
|
+
"Displays elapsed session time",
|
|
523
|
+
"1.0.0",
|
|
524
|
+
"claude-scope",
|
|
525
|
+
0
|
|
526
|
+
// First line
|
|
475
527
|
);
|
|
476
528
|
renderWithData(data, context) {
|
|
477
529
|
if (!data.cost || data.cost.total_duration_ms === void 0) return null;
|
|
@@ -484,7 +536,11 @@ var GitChangesWidget = class {
|
|
|
484
536
|
id = "git-changes";
|
|
485
537
|
metadata = createWidgetMetadata(
|
|
486
538
|
"Git Changes",
|
|
487
|
-
"Displays git diff statistics"
|
|
539
|
+
"Displays git diff statistics",
|
|
540
|
+
"1.0.0",
|
|
541
|
+
"claude-scope",
|
|
542
|
+
0
|
|
543
|
+
// First line
|
|
488
544
|
);
|
|
489
545
|
gitFactory;
|
|
490
546
|
git = null;
|
|
@@ -549,16 +605,214 @@ var GitChangesWidget = class {
|
|
|
549
605
|
}
|
|
550
606
|
};
|
|
551
607
|
|
|
608
|
+
// src/providers/config-provider.ts
|
|
609
|
+
var fs = __toESM(require("fs/promises"), 1);
|
|
610
|
+
var path = __toESM(require("path"), 1);
|
|
611
|
+
var os = __toESM(require("os"), 1);
|
|
612
|
+
var ConfigProvider = class {
|
|
613
|
+
cachedCounts;
|
|
614
|
+
lastScan = 0;
|
|
615
|
+
cacheInterval = 5e3;
|
|
616
|
+
// 5 seconds
|
|
617
|
+
/**
|
|
618
|
+
* Get config counts with hybrid caching
|
|
619
|
+
* Scans filesystem if cache is stale (>5 seconds)
|
|
620
|
+
*/
|
|
621
|
+
async getConfigs(options = {}) {
|
|
622
|
+
const now = Date.now();
|
|
623
|
+
if (this.cachedCounts && now - this.lastScan < this.cacheInterval) {
|
|
624
|
+
return this.cachedCounts;
|
|
625
|
+
}
|
|
626
|
+
this.cachedCounts = await this.scanConfigs(options);
|
|
627
|
+
this.lastScan = now;
|
|
628
|
+
return this.cachedCounts;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Scan filesystem for Claude Code configurations
|
|
632
|
+
*/
|
|
633
|
+
async scanConfigs(options) {
|
|
634
|
+
let claudeMdCount = 0;
|
|
635
|
+
let rulesCount = 0;
|
|
636
|
+
let mcpCount = 0;
|
|
637
|
+
let hooksCount = 0;
|
|
638
|
+
const homeDir = os.homedir();
|
|
639
|
+
const claudeDir = path.join(homeDir, ".claude");
|
|
640
|
+
const cwd = options.cwd;
|
|
641
|
+
if (await this.fileExists(path.join(claudeDir, "CLAUDE.md"))) {
|
|
642
|
+
claudeMdCount++;
|
|
643
|
+
}
|
|
644
|
+
rulesCount += await this.countRulesInDir(path.join(claudeDir, "rules"));
|
|
645
|
+
const userSettings = path.join(claudeDir, "settings.json");
|
|
646
|
+
const userSettingsData = await this.readJsonFile(userSettings);
|
|
647
|
+
if (userSettingsData) {
|
|
648
|
+
mcpCount += this.countMcpServers(userSettingsData);
|
|
649
|
+
hooksCount += this.countHooks(userSettingsData);
|
|
650
|
+
}
|
|
651
|
+
const userClaudeJson = path.join(homeDir, ".claude.json");
|
|
652
|
+
const userClaudeData = await this.readJsonFile(userClaudeJson);
|
|
653
|
+
if (userClaudeData) {
|
|
654
|
+
const userMcpCount = this.countMcpServers(userClaudeData);
|
|
655
|
+
mcpCount += Math.max(0, userMcpCount - this.countMcpServers(userSettingsData || {}));
|
|
656
|
+
}
|
|
657
|
+
if (cwd) {
|
|
658
|
+
if (await this.fileExists(path.join(cwd, "CLAUDE.md"))) {
|
|
659
|
+
claudeMdCount++;
|
|
660
|
+
}
|
|
661
|
+
if (await this.fileExists(path.join(cwd, "CLAUDE.local.md"))) {
|
|
662
|
+
claudeMdCount++;
|
|
663
|
+
}
|
|
664
|
+
if (await this.fileExists(path.join(cwd, ".claude", "CLAUDE.md"))) {
|
|
665
|
+
claudeMdCount++;
|
|
666
|
+
}
|
|
667
|
+
if (await this.fileExists(path.join(cwd, ".claude", "CLAUDE.local.md"))) {
|
|
668
|
+
claudeMdCount++;
|
|
669
|
+
}
|
|
670
|
+
rulesCount += await this.countRulesInDir(path.join(cwd, ".claude", "rules"));
|
|
671
|
+
const mcpJson = path.join(cwd, ".mcp.json");
|
|
672
|
+
const mcpData = await this.readJsonFile(mcpJson);
|
|
673
|
+
if (mcpData) {
|
|
674
|
+
mcpCount += this.countMcpServers(mcpData);
|
|
675
|
+
}
|
|
676
|
+
const projectSettings = path.join(cwd, ".claude", "settings.json");
|
|
677
|
+
const projectSettingsData = await this.readJsonFile(projectSettings);
|
|
678
|
+
if (projectSettingsData) {
|
|
679
|
+
mcpCount += this.countMcpServers(projectSettingsData);
|
|
680
|
+
hooksCount += this.countHooks(projectSettingsData);
|
|
681
|
+
}
|
|
682
|
+
const localSettings = path.join(cwd, ".claude", "settings.local.json");
|
|
683
|
+
const localSettingsData = await this.readJsonFile(localSettings);
|
|
684
|
+
if (localSettingsData) {
|
|
685
|
+
mcpCount += this.countMcpServers(localSettingsData);
|
|
686
|
+
hooksCount += this.countHooks(localSettingsData);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return { claudeMdCount, rulesCount, mcpCount, hooksCount };
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Check if file exists
|
|
693
|
+
*/
|
|
694
|
+
async fileExists(filePath) {
|
|
695
|
+
try {
|
|
696
|
+
await fs.access(filePath);
|
|
697
|
+
return true;
|
|
698
|
+
} catch {
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Read and parse JSON file
|
|
704
|
+
*/
|
|
705
|
+
async readJsonFile(filePath) {
|
|
706
|
+
try {
|
|
707
|
+
const content = await fs.readFile(filePath, "utf8");
|
|
708
|
+
return JSON.parse(content);
|
|
709
|
+
} catch {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Count MCP servers in config object
|
|
715
|
+
*/
|
|
716
|
+
countMcpServers(config) {
|
|
717
|
+
if (!config || !config.mcpServers || typeof config.mcpServers !== "object") {
|
|
718
|
+
return 0;
|
|
719
|
+
}
|
|
720
|
+
return Object.keys(config.mcpServers).length;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Count hooks in config object
|
|
724
|
+
*/
|
|
725
|
+
countHooks(config) {
|
|
726
|
+
if (!config || !config.hooks || typeof config.hooks !== "object") {
|
|
727
|
+
return 0;
|
|
728
|
+
}
|
|
729
|
+
return Object.keys(config.hooks).length;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Recursively count .md files in directory
|
|
733
|
+
*/
|
|
734
|
+
async countRulesInDir(rulesDir) {
|
|
735
|
+
const exists = await this.fileExists(rulesDir);
|
|
736
|
+
if (!exists) return 0;
|
|
737
|
+
try {
|
|
738
|
+
let count = 0;
|
|
739
|
+
const entries = await fs.readdir(rulesDir, { withFileTypes: true });
|
|
740
|
+
for (const entry of entries) {
|
|
741
|
+
const fullPath = path.join(rulesDir, entry.name);
|
|
742
|
+
if (entry.isDirectory()) {
|
|
743
|
+
count += await this.countRulesInDir(fullPath);
|
|
744
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
745
|
+
count++;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return count;
|
|
749
|
+
} catch {
|
|
750
|
+
return 0;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
// src/widgets/config-count-widget.ts
|
|
756
|
+
var ConfigCountWidget = class {
|
|
757
|
+
id = "config-count";
|
|
758
|
+
metadata = createWidgetMetadata(
|
|
759
|
+
"Config Count",
|
|
760
|
+
"Displays Claude Code configuration counts",
|
|
761
|
+
"1.0.0",
|
|
762
|
+
"claude-scope",
|
|
763
|
+
1
|
|
764
|
+
// Second line
|
|
765
|
+
);
|
|
766
|
+
configProvider = new ConfigProvider();
|
|
767
|
+
configs;
|
|
768
|
+
cwd;
|
|
769
|
+
async initialize() {
|
|
770
|
+
}
|
|
771
|
+
async update(data) {
|
|
772
|
+
this.cwd = data.cwd;
|
|
773
|
+
this.configs = await this.configProvider.getConfigs({ cwd: data.cwd });
|
|
774
|
+
}
|
|
775
|
+
isEnabled() {
|
|
776
|
+
if (!this.configs) {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
const { claudeMdCount, rulesCount, mcpCount, hooksCount } = this.configs;
|
|
780
|
+
return claudeMdCount > 0 || rulesCount > 0 || mcpCount > 0 || hooksCount > 0;
|
|
781
|
+
}
|
|
782
|
+
async render(context) {
|
|
783
|
+
if (!this.configs) {
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
const { claudeMdCount, rulesCount, mcpCount, hooksCount } = this.configs;
|
|
787
|
+
const parts = [];
|
|
788
|
+
if (claudeMdCount > 0) {
|
|
789
|
+
parts.push(`\u{1F4C4} ${claudeMdCount} CLAUDE.md`);
|
|
790
|
+
}
|
|
791
|
+
if (rulesCount > 0) {
|
|
792
|
+
parts.push(`\u{1F4DC} ${rulesCount} rules`);
|
|
793
|
+
}
|
|
794
|
+
if (mcpCount > 0) {
|
|
795
|
+
parts.push(`\u{1F50C} ${mcpCount} MCPs`);
|
|
796
|
+
}
|
|
797
|
+
if (hooksCount > 0) {
|
|
798
|
+
parts.push(`\u{1FA9D} ${hooksCount} hooks`);
|
|
799
|
+
}
|
|
800
|
+
return parts.join(" \u2502 ") || null;
|
|
801
|
+
}
|
|
802
|
+
async cleanup() {
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
|
|
552
806
|
// src/validation/result.ts
|
|
553
807
|
function success(data) {
|
|
554
808
|
return { success: true, data };
|
|
555
809
|
}
|
|
556
|
-
function failure(
|
|
557
|
-
return { success: false, error: { path, message, value } };
|
|
810
|
+
function failure(path2, message, value) {
|
|
811
|
+
return { success: false, error: { path: path2, message, value } };
|
|
558
812
|
}
|
|
559
813
|
function formatError(error) {
|
|
560
|
-
const
|
|
561
|
-
return `${
|
|
814
|
+
const path2 = error.path.length > 0 ? error.path.join(".") : "root";
|
|
815
|
+
return `${path2}: ${error.message}`;
|
|
562
816
|
}
|
|
563
817
|
|
|
564
818
|
// src/validation/validators.ts
|
|
@@ -751,6 +1005,7 @@ async function main() {
|
|
|
751
1005
|
await registry.register(new DurationWidget());
|
|
752
1006
|
await registry.register(new GitWidget());
|
|
753
1007
|
await registry.register(new GitChangesWidget());
|
|
1008
|
+
await registry.register(new ConfigCountWidget());
|
|
754
1009
|
const renderer = new Renderer({
|
|
755
1010
|
separator: " \u2502 ",
|
|
756
1011
|
onError: (error, widget) => {
|
|
@@ -760,11 +1015,11 @@ async function main() {
|
|
|
760
1015
|
for (const widget of registry.getAll()) {
|
|
761
1016
|
await widget.update(stdinData);
|
|
762
1017
|
}
|
|
763
|
-
const
|
|
1018
|
+
const lines = await renderer.render(
|
|
764
1019
|
registry.getEnabledWidgets(),
|
|
765
1020
|
{ width: 80, timestamp: Date.now() }
|
|
766
1021
|
);
|
|
767
|
-
return
|
|
1022
|
+
return lines.join("\n");
|
|
768
1023
|
} catch (error) {
|
|
769
1024
|
const fallback = await tryGitFallback();
|
|
770
1025
|
return fallback;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-scope",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Claude Code plugin for session status and analytics",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"c8": "^10.1.3",
|
|
33
33
|
"chai": "^6.2.2",
|
|
34
34
|
"esbuild": "^0.24.2",
|
|
35
|
+
"rimraf": "^6.1.2",
|
|
35
36
|
"tsx": "^4.19.2",
|
|
36
37
|
"typescript": "^5.7.2"
|
|
37
38
|
},
|