@ulpi/codemap 0.3.7 → 0.3.8

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/index.js +267 -14
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import "./chunk-WR342MP3.js";
5
5
  import "./chunk-2OBAJYRP.js";
6
6
 
7
7
  // src/index.ts
8
- import path13 from "path";
8
+ import path14 from "path";
9
9
  import { fileURLToPath } from "url";
10
10
 
11
11
  // ../../packages/intelligence/codemap-engine/dist/index.js
@@ -4250,6 +4250,19 @@ function releaseCodemapLock(projectDir, branch) {
4250
4250
  } catch {
4251
4251
  }
4252
4252
  }
4253
+ function isCodemapLocked(projectDir, branch) {
4254
+ const file = lockPath(projectDir, branch);
4255
+ const info = readLock(file);
4256
+ if (!info) return false;
4257
+ if (isStale(info)) {
4258
+ try {
4259
+ fs6.unlinkSync(file);
4260
+ } catch {
4261
+ }
4262
+ return false;
4263
+ }
4264
+ return true;
4265
+ }
4253
4266
  function saveManifest(branchDir, manifest) {
4254
4267
  fs7.mkdirSync(branchDir, { recursive: true });
4255
4268
  const file = path7.join(branchDir, "manifest.json");
@@ -6164,13 +6177,14 @@ function registerStatus(program2) {
6164
6177
  const projectDir = program2.opts().cwd || process.cwd();
6165
6178
  const branch = getCurrentBranch(projectDir);
6166
6179
  const statsFile = codemapStatsFile(projectDir, branch, getDataDir2());
6180
+ const indexing = isCodemapLocked(projectDir, branch);
6167
6181
  let stats = null;
6168
6182
  try {
6169
6183
  stats = JSON.parse(fs14.readFileSync(statsFile, "utf-8"));
6170
6184
  } catch {
6171
6185
  }
6172
6186
  if (opts.json) {
6173
- console.log(JSON.stringify({ projectDir, branch, stats }, null, 2));
6187
+ console.log(JSON.stringify({ projectDir, branch, stats, indexing }, null, 2));
6174
6188
  return;
6175
6189
  }
6176
6190
  console.log(chalk6.bold("Codemap Status"));
@@ -6179,6 +6193,10 @@ function registerStatus(program2) {
6179
6193
  console.log(`Branch: ${branch}`);
6180
6194
  console.log(`Data dir: ${getDataDir2()}`);
6181
6195
  console.log();
6196
+ if (indexing) {
6197
+ console.log(chalk6.yellow("Indexing in progress..."));
6198
+ console.log();
6199
+ }
6182
6200
  if (!stats) {
6183
6201
  console.log(chalk6.yellow("No index found. Run `codemap index` first."));
6184
6202
  return;
@@ -6869,13 +6887,10 @@ function registerCoupling(program2) {
6869
6887
  const projectDir = program2.opts().cwd || process.cwd();
6870
6888
  const branch = getCurrentBranch(projectDir);
6871
6889
  const graphFile = depgraphGraphFile(projectDir, branch, getDataDir2());
6872
- let graph;
6873
- try {
6874
- graph = loadGraph(graphFile);
6875
- } catch {
6890
+ const graph = loadGraph(graphFile);
6891
+ if (!graph) {
6876
6892
  console.error(chalk14.red("Dependency graph not available. Run `codemap index` first."));
6877
6893
  process.exit(1);
6878
- return;
6879
6894
  }
6880
6895
  let coupling = computeCoupling(graph);
6881
6896
  if (opts.module) {
@@ -6914,13 +6929,10 @@ function registerGraphStats(program2) {
6914
6929
  program2.command("graph-stats").description("Show aggregate dependency graph statistics").option("--json", "Output as JSON").action(async (opts) => {
6915
6930
  const projectDir = program2.opts().cwd || process.cwd();
6916
6931
  const branch = getCurrentBranch(projectDir);
6917
- let metrics;
6918
- try {
6919
- metrics = loadMetrics(depgraphMetricsFile(projectDir, branch, getDataDir2()));
6920
- } catch {
6932
+ const metrics = loadMetrics(depgraphMetricsFile(projectDir, branch, getDataDir2()));
6933
+ if (!metrics) {
6921
6934
  console.error(chalk15.red("Dependency metrics not available. Run `codemap index` first."));
6922
6935
  process.exit(1);
6923
- return;
6924
6936
  }
6925
6937
  const result = {
6926
6938
  totalFiles: metrics.totalFiles,
@@ -7010,9 +7022,249 @@ function registerRebuild(program2) {
7010
7022
  });
7011
7023
  }
7012
7024
 
7025
+ // src/commands/statusline.ts
7026
+ import chalk17 from "chalk";
7027
+ import fs18 from "fs";
7028
+ import path13 from "path";
7029
+ import { homedir as homedir3 } from "os";
7030
+
7031
+ // src/commands/statusline-script.ts
7032
+ function buildStatuslineScript(originalScript) {
7033
+ const body = originalScript.replace(/^#!\/.*\n/, "").trim();
7034
+ return `#!/bin/bash
7035
+ # CodeMap statusline for Claude Code
7036
+ # Generated by: codemap statusline
7037
+ # Do not edit \u2014 re-run \`codemap statusline\` to regenerate.
7038
+ # Original script backed up to statusline-command.sh.bak
7039
+
7040
+ set -euo pipefail
7041
+
7042
+ # Read session JSON from stdin (Claude Code pipes it)
7043
+ SESSION_JSON=$(cat)
7044
+
7045
+ # --- Original statusline ---
7046
+ ${body ? `(
7047
+ ${body}
7048
+ ) <<< "$SESSION_JSON"` : "# (none)"}
7049
+
7050
+ # --- CodeMap status ---
7051
+ # Resolve the working directory from session JSON
7052
+ current_dir=$(echo "$SESSION_JSON" | ${jsonExtract("cwd")} 2>/dev/null || pwd)
7053
+
7054
+ # Run codemap status --json for the project directory
7055
+ cm_json=$(codemap status --json --cwd "$current_dir" 2>/dev/null || true)
7056
+
7057
+ if [ -z "$cm_json" ]; then
7058
+ exit 0
7059
+ fi
7060
+
7061
+ # Parse fields
7062
+ stats_null=$(echo "$cm_json" | ${jsonExtract("stats")} 2>/dev/null || echo "null")
7063
+ if [ "$stats_null" = "null" ] || [ -z "$stats_null" ]; then
7064
+ echo "CM: \u2014 no index"
7065
+ exit 0
7066
+ fi
7067
+
7068
+ total_files=$(echo "$cm_json" | ${jsonPath("stats", "totalFiles")} 2>/dev/null || echo "0")
7069
+ total_chunks=$(echo "$cm_json" | ${jsonPath("stats", "totalChunks")} 2>/dev/null || echo "0")
7070
+ stale_files=$(echo "$cm_json" | ${jsonPath("stats", "staleFiles")} 2>/dev/null || echo "0")
7071
+ last_updated=$(echo "$cm_json" | ${jsonPath("stats", "lastUpdated")} 2>/dev/null || echo "")
7072
+ indexing=$(echo "$cm_json" | ${jsonExtract("indexing")} 2>/dev/null || echo "false")
7073
+
7074
+ # Format chunks (e.g. 12400 \u2192 12.4k)
7075
+ format_chunks() {
7076
+ local n=$1
7077
+ if [ "$n" -ge 1000 ]; then
7078
+ local k=$((n / 100))
7079
+ local whole=$((k / 10))
7080
+ local frac=$((k % 10))
7081
+ echo "\${whole}.\${frac}k"
7082
+ else
7083
+ echo "$n"
7084
+ fi
7085
+ }
7086
+
7087
+ # Calculate time ago
7088
+ time_ago() {
7089
+ local updated="$1"
7090
+ if [ -z "$updated" ]; then
7091
+ echo "unknown"
7092
+ return
7093
+ fi
7094
+ local now
7095
+ now=$(date +%s)
7096
+ local then_ts
7097
+ # macOS date -j vs GNU date
7098
+ if date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$updated" | cut -c1-19)" +%s >/dev/null 2>&1; then
7099
+ then_ts=$(date -j -f "%Y-%m-%dT%H:%M:%S" "$(echo "$updated" | cut -c1-19)" +%s 2>/dev/null || echo "$now")
7100
+ else
7101
+ then_ts=$(date -d "$updated" +%s 2>/dev/null || echo "$now")
7102
+ fi
7103
+ local diff=$(( now - then_ts ))
7104
+ if [ "$diff" -lt 60 ]; then
7105
+ echo "\${diff}s ago"
7106
+ elif [ "$diff" -lt 3600 ]; then
7107
+ echo "$(( diff / 60 ))m ago"
7108
+ elif [ "$diff" -lt 86400 ]; then
7109
+ echo "$(( diff / 3600 ))h ago"
7110
+ else
7111
+ echo "$(( diff / 86400 ))d ago"
7112
+ fi
7113
+ }
7114
+
7115
+ # Watcher detection
7116
+ watcher_icon="\u25CB"
7117
+ if pgrep -f "codemap watch" >/dev/null 2>&1; then
7118
+ watcher_icon="\u25CF"
7119
+ fi
7120
+
7121
+ # Indexing indicator
7122
+ idx_icon=""
7123
+ if [ "$indexing" = "true" ]; then
7124
+ idx_icon=" IDX"
7125
+ fi
7126
+
7127
+ chunks_fmt=$(format_chunks "$total_chunks")
7128
+
7129
+ # Build status line
7130
+ line="CM: \${watcher_icon} \${total_files}f \${chunks_fmt}\u25C6"
7131
+
7132
+ if [ "$stale_files" -gt 0 ] 2>/dev/null; then
7133
+ line="\${line} \u26A0\${stale_files}"
7134
+ fi
7135
+
7136
+ line="\${line}\${idx_icon} | $(time_ago "$last_updated")"
7137
+
7138
+ echo "$line"
7139
+ `;
7140
+ }
7141
+ function jsonExtract(field) {
7142
+ return `grep -o '"${field}"[[:space:]]*:[[:space:]]*[^,}]*' | sed 's/.*:[[:space:]]*//' | sed 's/"//g' | head -1`;
7143
+ }
7144
+ function jsonPath(parent, child) {
7145
+ return `grep -o '"${child}"[[:space:]]*:[[:space:]]*[^,}]*' | sed 's/.*:[[:space:]]*//' | sed 's/"//g' | head -1`;
7146
+ }
7147
+
7148
+ // src/commands/statusline.ts
7149
+ var CLAUDE_DIR = path13.join(homedir3(), ".claude");
7150
+ var SETTINGS_FILE2 = path13.join(CLAUDE_DIR, "settings.json");
7151
+ var SCRIPT_FILE = path13.join(CLAUDE_DIR, "statusline-command.sh");
7152
+ var BACKUP_FILE = path13.join(CLAUDE_DIR, "statusline-command.sh.bak");
7153
+ function readSettings() {
7154
+ try {
7155
+ return JSON.parse(fs18.readFileSync(SETTINGS_FILE2, "utf-8"));
7156
+ } catch {
7157
+ return {};
7158
+ }
7159
+ }
7160
+ function writeSettings(settings) {
7161
+ fs18.mkdirSync(CLAUDE_DIR, { recursive: true });
7162
+ fs18.writeFileSync(SETTINGS_FILE2, JSON.stringify(settings, null, 2) + "\n");
7163
+ }
7164
+ function registerStatusline(program2) {
7165
+ program2.command("statusline").description("Install CodeMap status into Claude Code statusline").option("--uninstall", "Remove CodeMap from statusline and restore backup").action(async (opts) => {
7166
+ if (opts.uninstall) {
7167
+ uninstall();
7168
+ return;
7169
+ }
7170
+ install();
7171
+ });
7172
+ }
7173
+ function install() {
7174
+ const settings = readSettings();
7175
+ if (fs18.existsSync(SCRIPT_FILE)) {
7176
+ try {
7177
+ const existing = fs18.readFileSync(SCRIPT_FILE, "utf-8");
7178
+ if (existing.includes("Generated by: codemap statusline")) {
7179
+ console.log(chalk17.yellow("CodeMap statusline is already installed."));
7180
+ console.log(chalk17.dim("Run `codemap statusline --uninstall` to remove, then reinstall."));
7181
+ return;
7182
+ }
7183
+ } catch {
7184
+ }
7185
+ }
7186
+ let originalScript = "";
7187
+ if (settings.statusline_command) {
7188
+ const currentCommand = settings.statusline_command;
7189
+ if (currentCommand.includes(SCRIPT_FILE) || currentCommand.endsWith("statusline-command.sh")) {
7190
+ if (fs18.existsSync(SCRIPT_FILE)) {
7191
+ originalScript = fs18.readFileSync(SCRIPT_FILE, "utf-8");
7192
+ }
7193
+ } else if (currentCommand.startsWith("/") || currentCommand.startsWith("~")) {
7194
+ const resolved = currentCommand.replace(/^~/, homedir3());
7195
+ try {
7196
+ originalScript = fs18.readFileSync(resolved, "utf-8");
7197
+ } catch {
7198
+ originalScript = `#!/bin/bash
7199
+ ${currentCommand}`;
7200
+ }
7201
+ } else {
7202
+ originalScript = `#!/bin/bash
7203
+ ${currentCommand}`;
7204
+ }
7205
+ }
7206
+ if (fs18.existsSync(SCRIPT_FILE)) {
7207
+ fs18.copyFileSync(SCRIPT_FILE, BACKUP_FILE);
7208
+ console.log(chalk17.dim(`Backed up existing script to ${BACKUP_FILE}`));
7209
+ }
7210
+ const script = buildStatuslineScript(originalScript);
7211
+ fs18.mkdirSync(CLAUDE_DIR, { recursive: true });
7212
+ fs18.writeFileSync(SCRIPT_FILE, script, { mode: 493 });
7213
+ if (settings.statusline_command !== SCRIPT_FILE) {
7214
+ settings.statusline_command = SCRIPT_FILE;
7215
+ writeSettings(settings);
7216
+ console.log(chalk17.dim(`Updated ${SETTINGS_FILE2}`));
7217
+ }
7218
+ console.log();
7219
+ console.log(chalk17.green("CodeMap statusline installed."));
7220
+ console.log();
7221
+ console.log("The statusline will show:");
7222
+ console.log(chalk17.cyan(" CM: \u25CF 842f 12.4k\u25C6 | 23m ago") + chalk17.dim(" \u2014 watcher running, index fresh"));
7223
+ console.log(chalk17.cyan(" CM: \u25CB 842f 12.4k\u25C6 \u26A03 | 2h ago") + chalk17.dim(" \u2014 no watcher, 3 stale files"));
7224
+ console.log(chalk17.cyan(" CM: \u25CB 842f 12.4k\u25C6 IDX | 5s ago") + chalk17.dim(" \u2014 indexing in progress"));
7225
+ console.log(chalk17.cyan(" CM: \u2014 no index") + chalk17.dim(" \u2014 no stats.json found"));
7226
+ console.log();
7227
+ console.log(chalk17.dim("Restart Claude Code to see the statusline."));
7228
+ console.log(chalk17.dim("Run `codemap statusline --uninstall` to remove."));
7229
+ }
7230
+ function uninstall() {
7231
+ let restored = false;
7232
+ if (fs18.existsSync(BACKUP_FILE)) {
7233
+ fs18.copyFileSync(BACKUP_FILE, SCRIPT_FILE);
7234
+ fs18.unlinkSync(BACKUP_FILE);
7235
+ console.log(chalk17.dim(`Restored original script from backup.`));
7236
+ restored = true;
7237
+ } else if (fs18.existsSync(SCRIPT_FILE)) {
7238
+ try {
7239
+ const content = fs18.readFileSync(SCRIPT_FILE, "utf-8");
7240
+ if (content.includes("Generated by: codemap statusline")) {
7241
+ fs18.unlinkSync(SCRIPT_FILE);
7242
+ console.log(chalk17.dim(`Removed statusline script.`));
7243
+ const settings = readSettings();
7244
+ if (settings.statusline_command === SCRIPT_FILE) {
7245
+ delete settings.statusline_command;
7246
+ writeSettings(settings);
7247
+ console.log(chalk17.dim(`Cleared statusline_command from settings.`));
7248
+ }
7249
+ }
7250
+ } catch {
7251
+ }
7252
+ }
7253
+ if (!restored) {
7254
+ const settings = readSettings();
7255
+ if (settings.statusline_command) {
7256
+ delete settings.statusline_command;
7257
+ writeSettings(settings);
7258
+ }
7259
+ }
7260
+ console.log();
7261
+ console.log(chalk17.green("CodeMap statusline removed."));
7262
+ console.log(chalk17.dim("Restart Claude Code for changes to take effect."));
7263
+ }
7264
+
7013
7265
  // src/index.ts
7014
- var __dirname = path13.dirname(fileURLToPath(import.meta.url));
7015
- var grammarsDir = path13.join(__dirname, "grammars");
7266
+ var __dirname = path14.dirname(fileURLToPath(import.meta.url));
7267
+ var grammarsDir = path14.join(__dirname, "grammars");
7016
7268
  setGrammarDir(grammarsDir);
7017
7269
  var program = new Command();
7018
7270
  program.name("codemap").description("Code intelligence CLI \u2014 hybrid search, dependency analysis, PageRank").version("0.3.5").option("--cwd <dir>", "Project directory (default: cwd)");
@@ -7033,4 +7285,5 @@ registerSummary(program);
7033
7285
  registerCoupling(program);
7034
7286
  registerGraphStats(program);
7035
7287
  registerRebuild(program);
7288
+ registerStatusline(program);
7036
7289
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulpi/codemap",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "type": "module",
5
5
  "description": "Standalone code intelligence CLI — hybrid vector + BM25 search, dependency analysis, PageRank",
6
6
  "bin": {