claude-scope 0.8.13 → 0.8.15

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.
Files changed (2) hide show
  1. package/dist/claude-scope.cjs +198 -85
  2. package/package.json +1 -1
@@ -6997,6 +6997,11 @@ function generateBalancedLayout(style, themeName) {
6997
6997
  bar: theme.context.bar
6998
6998
  }
6999
6999
  },
7000
+ {
7001
+ id: "lines",
7002
+ style,
7003
+ colors: { added: theme.lines.added, removed: theme.lines.removed }
7004
+ },
7000
7005
  {
7001
7006
  id: "cost",
7002
7007
  style,
@@ -7006,11 +7011,6 @@ function generateBalancedLayout(style, themeName) {
7006
7011
  id: "duration",
7007
7012
  style,
7008
7013
  colors: { value: theme.duration.value, unit: theme.duration.unit }
7009
- },
7010
- {
7011
- id: "lines",
7012
- style,
7013
- colors: { added: theme.lines.added, removed: theme.lines.removed }
7014
7014
  }
7015
7015
  ],
7016
7016
  "1": [
@@ -7019,6 +7019,11 @@ function generateBalancedLayout(style, themeName) {
7019
7019
  style,
7020
7020
  colors: { branch: theme.git.branch, changes: theme.git.changes }
7021
7021
  },
7022
+ {
7023
+ id: "git-tag",
7024
+ style,
7025
+ colors: { base: theme.base.text }
7026
+ },
7022
7027
  {
7023
7028
  id: "cache-metrics",
7024
7029
  style,
@@ -7033,21 +7038,7 @@ function generateBalancedLayout(style, themeName) {
7033
7038
  {
7034
7039
  id: "config-count",
7035
7040
  style,
7036
- colors: {
7037
- base: theme.base.muted
7038
- }
7039
- },
7040
- {
7041
- id: "active-tools",
7042
- style,
7043
- colors: {
7044
- running: theme.tools.running,
7045
- completed: theme.tools.completed,
7046
- error: theme.tools.error,
7047
- name: theme.tools.name,
7048
- target: theme.tools.target,
7049
- count: theme.tools.count
7050
- }
7041
+ colors: { base: theme.base.muted }
7051
7042
  }
7052
7043
  ]
7053
7044
  }
@@ -7115,14 +7106,14 @@ function generateRichLayout(style, themeName) {
7115
7106
  }
7116
7107
  },
7117
7108
  {
7118
- id: "cost",
7109
+ id: "lines",
7119
7110
  style,
7120
- colors: { amount: theme.cost.amount, currency: theme.cost.currency }
7111
+ colors: { added: theme.lines.added, removed: theme.lines.removed }
7121
7112
  },
7122
7113
  {
7123
- id: "lines",
7114
+ id: "cost",
7124
7115
  style,
7125
- colors: { added: theme.lines.added, removed: theme.lines.removed }
7116
+ colors: { amount: theme.cost.amount, currency: theme.cost.currency }
7126
7117
  },
7127
7118
  {
7128
7119
  id: "duration",
@@ -7141,20 +7132,6 @@ function generateRichLayout(style, themeName) {
7141
7132
  style,
7142
7133
  colors: { base: theme.base.text }
7143
7134
  },
7144
- {
7145
- id: "active-tools",
7146
- style,
7147
- colors: {
7148
- running: theme.tools.running,
7149
- completed: theme.tools.completed,
7150
- error: theme.tools.error,
7151
- name: theme.tools.name,
7152
- target: theme.tools.target,
7153
- count: theme.tools.count
7154
- }
7155
- }
7156
- ],
7157
- "2": [
7158
7135
  {
7159
7136
  id: "cache-metrics",
7160
7137
  style,
@@ -7171,6 +7148,39 @@ function generateRichLayout(style, themeName) {
7171
7148
  style,
7172
7149
  colors: { base: theme.base.muted }
7173
7150
  }
7151
+ ],
7152
+ "2": [
7153
+ {
7154
+ id: "dev-server",
7155
+ style,
7156
+ colors: {
7157
+ name: theme.devServer.name,
7158
+ status: theme.devServer.status,
7159
+ label: theme.devServer.label
7160
+ }
7161
+ },
7162
+ {
7163
+ id: "docker",
7164
+ style,
7165
+ colors: {
7166
+ label: theme.docker.label,
7167
+ count: theme.docker.count,
7168
+ running: theme.docker.running,
7169
+ stopped: theme.docker.stopped
7170
+ }
7171
+ },
7172
+ {
7173
+ id: "active-tools",
7174
+ style,
7175
+ colors: {
7176
+ running: theme.tools.running,
7177
+ completed: theme.tools.completed,
7178
+ error: theme.tools.error,
7179
+ name: theme.tools.name,
7180
+ target: theme.tools.target,
7181
+ count: theme.tools.count
7182
+ }
7183
+ }
7174
7184
  ]
7175
7185
  }
7176
7186
  };
@@ -7222,19 +7232,19 @@ async function selectLayout() {
7222
7232
  const layoutChoices = [
7223
7233
  {
7224
7234
  name: "Balanced",
7225
- description: "2 lines: AI metrics + Git, Cache, Tools, MCP, Hooks",
7235
+ description: "2 lines: Model|Context|Lines|Cost|Duration + Git|GitTag|Cache|Config",
7226
7236
  value: "balanced",
7227
7237
  getConfig: (s, t) => generateBalancedLayout(s, t)
7228
7238
  },
7229
7239
  {
7230
7240
  name: "Compact",
7231
- description: "1 line: Model, Context, Cost, Git, Duration",
7241
+ description: "1 line: Model|Context|Cost|Git|Duration",
7232
7242
  value: "compact",
7233
7243
  getConfig: (s, t) => generateCompactLayout(s, t)
7234
7244
  },
7235
7245
  {
7236
7246
  name: "Rich",
7237
- description: "3 lines: Full details with Git Tag, Config Count",
7247
+ description: "3 lines: + Dev Server|Docker|Active Tools on line 3",
7238
7248
  value: "rich",
7239
7249
  getConfig: (s, t) => generateRichLayout(s, t)
7240
7250
  }
@@ -7581,11 +7591,130 @@ init_context_widget();
7581
7591
  init_cost_widget();
7582
7592
 
7583
7593
  // src/widgets/dev-server/dev-server-widget.ts
7584
- var import_node_child_process2 = require("node:child_process");
7585
- var import_node_util3 = require("node:util");
7586
7594
  init_widget_types();
7587
7595
  init_theme();
7588
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
+
7589
7718
  // src/widgets/dev-server/styles.ts
7590
7719
  init_colors();
7591
7720
  var devServerStyles = {
@@ -7637,13 +7766,12 @@ var devServerStyles = {
7637
7766
  };
7638
7767
 
7639
7768
  // src/widgets/dev-server/dev-server-widget.ts
7640
- var execFileAsync2 = (0, import_node_util3.promisify)(import_node_child_process2.execFile);
7641
7769
  var DevServerWidget = class {
7642
7770
  id = "dev-server";
7643
7771
  metadata = createWidgetMetadata(
7644
7772
  "Dev Server",
7645
- "Detects running dev server processes",
7646
- "1.0.0",
7773
+ "Detects running dev server processes using hybrid port+process detection",
7774
+ "1.1.0",
7647
7775
  "claude-scope",
7648
7776
  0
7649
7777
  );
@@ -7652,22 +7780,12 @@ var DevServerWidget = class {
7652
7780
  _lineOverride;
7653
7781
  styleFn = devServerStyles.balanced;
7654
7782
  cwd = null;
7655
- processPatterns = [
7656
- // Generic server patterns - more specific to avoid shell history false positives
7657
- { regex: /^[\w\s]+\/npm\s+(exec|run)\s+serve/i, name: "Server", icon: "\u{1F310}" },
7658
- { regex: /^[\w\s]+\/npx\s+serve\s+-/i, name: "Server", icon: "\u{1F310}" },
7659
- { regex: /^[\w\s]+\/(python|python3)\s+-m\s+http\.server/i, name: "HTTP", icon: "\u{1F310}" },
7660
- // Generic dev/build patterns - require full command path
7661
- { regex: /^[\w\s]+\/(npm|yarn|pnpm|bun)\s+run\s+dev\s*$/i, name: "Dev", icon: "\u{1F680}" },
7662
- { regex: /^[\w\s]+\/(npm|yarn|pnpm|bun)\s+run\s+build\s*$/i, name: "Build", icon: "\u{1F528}" },
7663
- // Framework-specific patterns - require executable path
7664
- { regex: /\/(nuxt|next|astro|remix|svelte)\s+dev/i, name: "Framework", icon: "\u26A1" },
7665
- { regex: /\/node.*\/vite\s*$/i, name: "Vite", icon: "\u26A1" },
7666
- // Fallback: simpler patterns but checked last
7667
- { regex: /\s(nuxt|next|vite)\s+dev\s/i, name: "DevServer", icon: "\u26A1" }
7668
- ];
7783
+ portDetector;
7784
+ processDetector;
7669
7785
  constructor(colors2) {
7670
7786
  this.colors = colors2 ?? DEFAULT_THEME;
7787
+ this.portDetector = new PortDetector();
7788
+ this.processDetector = new ProcessDetector();
7671
7789
  }
7672
7790
  /**
7673
7791
  * Set display style
@@ -7736,35 +7854,30 @@ var DevServerWidget = class {
7736
7854
  return this.styleFn(renderData, this.colors.devServer);
7737
7855
  }
7738
7856
  /**
7739
- * Detect running dev server by parsing system process list
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
+ *
7740
7862
  * @returns Detected server status or null
7741
7863
  */
7742
7864
  async detectDevServer() {
7743
- try {
7744
- const { stdout } = await execFileAsync2("ps", ["aux"], {
7745
- timeout: 1e3
7746
- });
7747
- for (const pattern of this.processPatterns) {
7748
- if (pattern.regex.test(stdout)) {
7749
- const isBuilding = pattern.name.toLowerCase().includes("build");
7750
- const isRunning = !isBuilding;
7751
- return {
7752
- name: pattern.name,
7753
- icon: pattern.icon,
7754
- isRunning,
7755
- isBuilding
7756
- };
7757
- }
7758
- }
7759
- } catch {
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
+ };
7760
7873
  }
7761
- return null;
7874
+ return await this.processDetector.detect();
7762
7875
  }
7763
7876
  };
7764
7877
 
7765
7878
  // src/widgets/docker/docker-widget.ts
7766
- var import_node_child_process3 = require("node:child_process");
7767
- var import_node_util4 = require("node:util");
7879
+ var import_node_child_process4 = require("node:child_process");
7880
+ var import_node_util5 = require("node:util");
7768
7881
  init_widget_types();
7769
7882
  init_theme();
7770
7883
 
@@ -7831,7 +7944,7 @@ var dockerStyles = {
7831
7944
  };
7832
7945
 
7833
7946
  // src/widgets/docker/docker-widget.ts
7834
- var execFileAsync3 = (0, import_node_util4.promisify)(import_node_child_process3.execFile);
7947
+ var execFileAsync4 = (0, import_node_util5.promisify)(import_node_child_process4.execFile);
7835
7948
  var DockerWidget = class {
7836
7949
  id = "docker";
7837
7950
  metadata = createWidgetMetadata(
@@ -7891,12 +8004,12 @@ var DockerWidget = class {
7891
8004
  }
7892
8005
  async getDockerStatus() {
7893
8006
  try {
7894
- await execFileAsync3("docker", ["info"], { timeout: 2e3 });
7895
- const { stdout: runningOutput } = await execFileAsync3("docker", ["ps", "-q"], {
8007
+ await execFileAsync4("docker", ["info"], { timeout: 2e3 });
8008
+ const { stdout: runningOutput } = await execFileAsync4("docker", ["ps", "-q"], {
7896
8009
  timeout: 1e3
7897
8010
  });
7898
8011
  const running = runningOutput.trim().split("\n").filter((line) => line).length;
7899
- const { stdout: allOutput } = await execFileAsync3("docker", ["ps", "-aq"], {
8012
+ const { stdout: allOutput } = await execFileAsync4("docker", ["ps", "-aq"], {
7900
8013
  timeout: 1e3
7901
8014
  });
7902
8015
  const total = allOutput.trim().split("\n").filter((line) => line).length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-scope",
3
- "version": "0.8.13",
3
+ "version": "0.8.15",
4
4
  "description": "Claude Code plugin for session status and analytics",
5
5
  "license": "MIT",
6
6
  "type": "module",