@staff0rd/assist 0.196.1 → 0.197.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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +341 -140
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -195,6 +195,7 @@ After installation, the `assist` command will be available globally. You can als
195
195
  - `assist voice logs [-n <count>]` - Show recent voice daemon log entries
196
196
  - `assist sessions` - Start the sessions web dashboard (same as `sessions web`)
197
197
  - `assist sessions web [-p, --port <number>]` - Start a web dashboard for Claude Code sessions with xterm.js terminals (default port 3100)
198
+ - `assist sessions summarise [-f, --force] [-n, --limit <count>]` - Generate one-line summaries for unsummarised Claude sessions (force re-generates all; limit caps how many to process)
198
199
  - `assist next` - Alias for `backlog next -w`
199
200
  - `assist draft` (alias: `feat`) - Launch Claude in `/draft` mode, chain into next on `/next` signal
200
201
  - `assist bug` - Launch Claude in `/bug` mode, chain into next on `/next` signal
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.196.1",
9
+ version: "0.197.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -12590,11 +12590,319 @@ function screenshot(processName) {
12590
12590
  }
12591
12591
  }
12592
12592
 
12593
+ // src/commands/sessions/summarise/index.ts
12594
+ import * as fs27 from "fs";
12595
+ import chalk134 from "chalk";
12596
+
12597
+ // src/commands/sessions/web/discoverSessions.ts
12598
+ import * as fs24 from "fs";
12599
+ import * as os from "os";
12600
+ import * as path47 from "path";
12601
+
12602
+ // src/commands/sessions/web/parseSessionFile.ts
12603
+ import * as fs23 from "fs";
12604
+ import * as path46 from "path";
12605
+
12606
+ // src/commands/sessions/web/extractSessionMeta.ts
12607
+ function extractSessionMeta(lines) {
12608
+ let sessionId = "";
12609
+ let cwd = "";
12610
+ let timestamp = "";
12611
+ let name = "";
12612
+ for (const line of lines) {
12613
+ const entry = safeParse(line);
12614
+ if (!entry) continue;
12615
+ sessionId ||= typeof entry.sessionId === "string" ? entry.sessionId : "";
12616
+ timestamp ||= typeof entry.timestamp === "string" ? entry.timestamp : "";
12617
+ cwd ||= typeof entry.cwd === "string" ? entry.cwd : "";
12618
+ if (entry.type === "user" && !entry.isMeta) {
12619
+ name = extractName(entry);
12620
+ break;
12621
+ }
12622
+ }
12623
+ return { sessionId, cwd, timestamp, name };
12624
+ }
12625
+ function safeParse(line) {
12626
+ try {
12627
+ return JSON.parse(line);
12628
+ } catch {
12629
+ return null;
12630
+ }
12631
+ }
12632
+ function extractName(entry) {
12633
+ const msg = entry.message;
12634
+ const content = msg?.content;
12635
+ const text = typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
12636
+ return text.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
12637
+ }
12638
+
12639
+ // src/commands/sessions/web/parseSessionFile.ts
12640
+ async function parseSessionFile(filePath) {
12641
+ let handle;
12642
+ try {
12643
+ handle = await fs23.promises.open(filePath, "r");
12644
+ const buf = Buffer.alloc(16384);
12645
+ const { bytesRead } = await handle.read(buf, 0, buf.length, 0);
12646
+ const lines = buf.toString("utf8", 0, bytesRead).split("\n").filter(Boolean);
12647
+ const meta = extractSessionMeta(lines);
12648
+ if (!meta.sessionId) return null;
12649
+ const timestamp = meta.timestamp || (await fs23.promises.stat(filePath)).mtime.toISOString();
12650
+ const project = meta.cwd ? path46.basename(meta.cwd) : dirNameToProject(filePath);
12651
+ return {
12652
+ sessionId: meta.sessionId,
12653
+ name: meta.name || `Session ${meta.sessionId.slice(0, 8)}`,
12654
+ project,
12655
+ cwd: meta.cwd,
12656
+ timestamp
12657
+ };
12658
+ } catch {
12659
+ return null;
12660
+ } finally {
12661
+ await handle?.close();
12662
+ }
12663
+ }
12664
+ function dirNameToProject(filePath) {
12665
+ const dirName = path46.basename(path46.dirname(filePath));
12666
+ const parts = dirName.split("--");
12667
+ return parts[parts.length - 1].replace(/-/g, "/");
12668
+ }
12669
+
12670
+ // src/commands/sessions/web/discoverSessions.ts
12671
+ async function discoverSessionJsonlPaths() {
12672
+ const projectsDir = path47.join(os.homedir(), ".claude", "projects");
12673
+ let projectDirs;
12674
+ try {
12675
+ projectDirs = await fs24.promises.readdir(projectsDir);
12676
+ } catch {
12677
+ return [];
12678
+ }
12679
+ const paths = [];
12680
+ await Promise.all(
12681
+ projectDirs.map(async (dirName) => {
12682
+ const dirPath = path47.join(projectsDir, dirName);
12683
+ let entries;
12684
+ try {
12685
+ entries = await fs24.promises.readdir(dirPath);
12686
+ } catch {
12687
+ return;
12688
+ }
12689
+ const jsonlFiles = entries.filter((e) => e.endsWith(".jsonl"));
12690
+ for (const file of jsonlFiles) {
12691
+ paths.push(path47.join(dirPath, file));
12692
+ }
12693
+ })
12694
+ );
12695
+ return paths;
12696
+ }
12697
+ async function discoverSessions() {
12698
+ const paths = await discoverSessionJsonlPaths();
12699
+ const sessions = [];
12700
+ await Promise.all(
12701
+ paths.map(async (filePath) => {
12702
+ const session = await parseSessionFile(filePath);
12703
+ if (session) sessions.push(session);
12704
+ })
12705
+ );
12706
+ sessions.sort(
12707
+ (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
12708
+ );
12709
+ return sessions;
12710
+ }
12711
+
12712
+ // src/commands/sessions/summarise/shared.ts
12713
+ import * as fs25 from "fs";
12714
+ function writeSummary(jsonlPath, summary) {
12715
+ fs25.writeFileSync(summaryPathFor(jsonlPath), `${summary.trim()}
12716
+ `, "utf8");
12717
+ }
12718
+ function hasSummary(jsonlPath) {
12719
+ return fs25.existsSync(summaryPathFor(jsonlPath));
12720
+ }
12721
+ function summaryPathFor(jsonlPath) {
12722
+ return jsonlPath.replace(/\.jsonl$/, ".summary");
12723
+ }
12724
+
12725
+ // src/commands/sessions/summarise/summariseSession.ts
12726
+ import { execFileSync as execFileSync2 } from "child_process";
12727
+
12728
+ // src/commands/sessions/summarise/iterateUserMessages.ts
12729
+ import * as fs26 from "fs";
12730
+ function* iterateUserMessages(filePath, maxBytes = 65536) {
12731
+ let content;
12732
+ try {
12733
+ const fd = fs26.openSync(filePath, "r");
12734
+ try {
12735
+ const buf = Buffer.alloc(maxBytes);
12736
+ const bytesRead = fs26.readSync(fd, buf, 0, buf.length, 0);
12737
+ content = buf.toString("utf8", 0, bytesRead);
12738
+ } finally {
12739
+ fs26.closeSync(fd);
12740
+ }
12741
+ } catch {
12742
+ return;
12743
+ }
12744
+ for (const line of content.split("\n")) {
12745
+ if (!line) continue;
12746
+ let entry;
12747
+ try {
12748
+ entry = JSON.parse(line);
12749
+ } catch {
12750
+ continue;
12751
+ }
12752
+ if (entry.type !== "user") continue;
12753
+ const msg = entry.message;
12754
+ const c = msg?.content;
12755
+ if (typeof c === "string") {
12756
+ yield c;
12757
+ } else if (Array.isArray(c)) {
12758
+ const text = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
12759
+ if (text) yield text;
12760
+ }
12761
+ }
12762
+ }
12763
+
12764
+ // src/commands/sessions/summarise/extractFirstUserMessage.ts
12765
+ function extractFirstUserMessage(filePath) {
12766
+ for (const text of iterateUserMessages(filePath)) {
12767
+ return truncate2(text);
12768
+ }
12769
+ return void 0;
12770
+ }
12771
+ function truncate2(text, maxChars = 500) {
12772
+ const trimmed = text.trim();
12773
+ if (trimmed.length <= maxChars) return trimmed;
12774
+ return `${trimmed.slice(0, maxChars)}\u2026`;
12775
+ }
12776
+
12777
+ // src/commands/sessions/summarise/scanSessionBacklogRefs.ts
12778
+ function scanSessionBacklogRefs(filePath) {
12779
+ const ids = /* @__PURE__ */ new Set();
12780
+ for (const text of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
12781
+ for (const id of extractBacklogIds(text)) {
12782
+ ids.add(id);
12783
+ }
12784
+ }
12785
+ return [...ids].sort((a, b) => a - b);
12786
+ }
12787
+ function extractBacklogIds(text) {
12788
+ const ids = [];
12789
+ for (const m of text.matchAll(/backlog\s+run\s+(\d+)/gi)) {
12790
+ ids.push(Number.parseInt(m[1], 10));
12791
+ }
12792
+ for (const m of text.matchAll(/backlog\s+(?:item\s+)?#(\d+)/gi)) {
12793
+ ids.push(Number.parseInt(m[1], 10));
12794
+ }
12795
+ for (const m of text.matchAll(/backlog\s+phase-done\s+(\d+)/gi)) {
12796
+ ids.push(Number.parseInt(m[1], 10));
12797
+ }
12798
+ for (const m of text.matchAll(/backlog\s+comment\s+(\d+)/gi)) {
12799
+ ids.push(Number.parseInt(m[1], 10));
12800
+ }
12801
+ for (const m of text.matchAll(/(?:^|[\s(])#(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
12802
+ ids.push(Number.parseInt(m[1], 10));
12803
+ }
12804
+ return ids;
12805
+ }
12806
+
12807
+ // src/commands/sessions/summarise/summariseSession.ts
12808
+ function summariseSession(jsonlPath) {
12809
+ const firstMessage = extractFirstUserMessage(jsonlPath);
12810
+ const backlogIds = scanSessionBacklogRefs(jsonlPath);
12811
+ if (!firstMessage && backlogIds.length === 0) {
12812
+ return void 0;
12813
+ }
12814
+ const prompt = buildPrompt2(firstMessage, backlogIds);
12815
+ try {
12816
+ const output = execFileSync2("claude", ["-p", "--model", "haiku", prompt], {
12817
+ encoding: "utf8",
12818
+ timeout: 3e4,
12819
+ stdio: ["ignore", "pipe", "ignore"]
12820
+ });
12821
+ const summary = output.trim();
12822
+ if (!summary) return void 0;
12823
+ return summary.split("\n")[0].replace(/^["']|["']$/g, "").trim();
12824
+ } catch {
12825
+ return void 0;
12826
+ }
12827
+ }
12828
+ function buildPrompt2(firstMessage, backlogIds) {
12829
+ const parts = [
12830
+ "Summarise this Claude Code session in ONE short sentence (under 100 chars).",
12831
+ "Return ONLY the summary, no quotes or explanation."
12832
+ ];
12833
+ if (backlogIds.length > 0) {
12834
+ const refs = backlogIds.map((id) => `#${id}`).join(", ");
12835
+ parts.push(
12836
+ `The session references backlog item(s) ${refs}. Start the summary with "Backlog ${refs} \u2014" if relevant.`
12837
+ );
12838
+ }
12839
+ if (firstMessage) {
12840
+ parts.push(`First user message:
12841
+ ${firstMessage}`);
12842
+ }
12843
+ return parts.join("\n");
12844
+ }
12845
+
12846
+ // src/commands/sessions/summarise/index.ts
12847
+ async function summarise3(options2) {
12848
+ const files = await discoverSessionJsonlPaths();
12849
+ if (files.length === 0) {
12850
+ console.log(chalk134.yellow("No sessions found."));
12851
+ return;
12852
+ }
12853
+ const toProcess = selectCandidates(files, options2);
12854
+ if (toProcess.length === 0) {
12855
+ console.log(chalk134.green("All sessions already summarised."));
12856
+ return;
12857
+ }
12858
+ console.log(
12859
+ chalk134.cyan(
12860
+ `Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
12861
+ )
12862
+ );
12863
+ const { succeeded, failed } = processSessions(toProcess);
12864
+ console.log(
12865
+ chalk134.green(`Done: ${succeeded} summarised`) + (failed > 0 ? chalk134.yellow(`, ${failed} skipped`) : "")
12866
+ );
12867
+ }
12868
+ function selectCandidates(files, options2) {
12869
+ const candidates = options2.force ? files : files.filter((f) => !hasSummary(f));
12870
+ candidates.sort((a, b) => {
12871
+ try {
12872
+ return fs27.statSync(b).mtimeMs - fs27.statSync(a).mtimeMs;
12873
+ } catch {
12874
+ return 0;
12875
+ }
12876
+ });
12877
+ const limit = options2.limit ? Number.parseInt(options2.limit, 10) : void 0;
12878
+ return limit && limit > 0 ? candidates.slice(0, limit) : candidates;
12879
+ }
12880
+ function processSessions(files) {
12881
+ let succeeded = 0;
12882
+ let failed = 0;
12883
+ for (let i = 0; i < files.length; i++) {
12884
+ const file = files[i];
12885
+ process.stdout.write(chalk134.dim(` [${i + 1}/${files.length}] `));
12886
+ const summary = summariseSession(file);
12887
+ if (summary) {
12888
+ writeSummary(file, summary);
12889
+ succeeded++;
12890
+ process.stdout.write(`${chalk134.green("\u2713")} ${summary}
12891
+ `);
12892
+ } else {
12893
+ failed++;
12894
+ process.stdout.write(` ${chalk134.yellow("skip")}
12895
+ `);
12896
+ }
12897
+ }
12898
+ return { succeeded, failed };
12899
+ }
12900
+
12593
12901
  // src/commands/sessions/web/index.ts
12594
12902
  import { WebSocketServer } from "ws";
12595
12903
 
12596
12904
  // src/commands/sessions/web/handleRequest.ts
12597
- import { readFileSync as readFileSync34 } from "fs";
12905
+ import { readFileSync as readFileSync35 } from "fs";
12598
12906
  import { createRequire as createRequire2 } from "module";
12599
12907
 
12600
12908
  // src/commands/sessions/web/getHtml.ts
@@ -12629,7 +12937,7 @@ function createCssHandler(packageEntry) {
12629
12937
  return (_req, res) => {
12630
12938
  if (!cache) {
12631
12939
  const resolved = require3.resolve(packageEntry);
12632
- cache = readFileSync34(resolved, "utf-8");
12940
+ cache = readFileSync35(resolved, "utf-8");
12633
12941
  }
12634
12942
  res.writeHead(200, { "Content-Type": "text/css" });
12635
12943
  res.end(cache);
@@ -12752,114 +13060,6 @@ function resumeSession(id, sessionId, cwd, name) {
12752
13060
  };
12753
13061
  }
12754
13062
 
12755
- // src/commands/sessions/web/discoverSessions.ts
12756
- import * as fs24 from "fs";
12757
- import * as os from "os";
12758
- import * as path47 from "path";
12759
-
12760
- // src/commands/sessions/web/parseSessionFile.ts
12761
- import * as fs23 from "fs";
12762
- import * as path46 from "path";
12763
-
12764
- // src/commands/sessions/web/extractSessionMeta.ts
12765
- function extractSessionMeta(lines) {
12766
- let sessionId = "";
12767
- let cwd = "";
12768
- let timestamp = "";
12769
- let name = "";
12770
- for (const line of lines) {
12771
- const entry = safeParse(line);
12772
- if (!entry) continue;
12773
- sessionId ||= typeof entry.sessionId === "string" ? entry.sessionId : "";
12774
- timestamp ||= typeof entry.timestamp === "string" ? entry.timestamp : "";
12775
- cwd ||= typeof entry.cwd === "string" ? entry.cwd : "";
12776
- if (entry.type === "user" && !entry.isMeta) {
12777
- name = extractName(entry);
12778
- break;
12779
- }
12780
- }
12781
- return { sessionId, cwd, timestamp, name };
12782
- }
12783
- function safeParse(line) {
12784
- try {
12785
- return JSON.parse(line);
12786
- } catch {
12787
- return null;
12788
- }
12789
- }
12790
- function extractName(entry) {
12791
- const msg = entry.message;
12792
- const content = msg?.content;
12793
- const text = typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
12794
- return text.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
12795
- }
12796
-
12797
- // src/commands/sessions/web/parseSessionFile.ts
12798
- async function parseSessionFile(filePath) {
12799
- let handle;
12800
- try {
12801
- handle = await fs23.promises.open(filePath, "r");
12802
- const buf = Buffer.alloc(16384);
12803
- const { bytesRead } = await handle.read(buf, 0, buf.length, 0);
12804
- const lines = buf.toString("utf8", 0, bytesRead).split("\n").filter(Boolean);
12805
- const meta = extractSessionMeta(lines);
12806
- if (!meta.sessionId) return null;
12807
- const timestamp = meta.timestamp || (await fs23.promises.stat(filePath)).mtime.toISOString();
12808
- const project = meta.cwd ? path46.basename(meta.cwd) : dirNameToProject(filePath);
12809
- return {
12810
- sessionId: meta.sessionId,
12811
- name: meta.name || `Session ${meta.sessionId.slice(0, 8)}`,
12812
- project,
12813
- cwd: meta.cwd,
12814
- timestamp
12815
- };
12816
- } catch {
12817
- return null;
12818
- } finally {
12819
- await handle?.close();
12820
- }
12821
- }
12822
- function dirNameToProject(filePath) {
12823
- const dirName = path46.basename(path46.dirname(filePath));
12824
- const parts = dirName.split("--");
12825
- return parts[parts.length - 1].replace(/-/g, "/");
12826
- }
12827
-
12828
- // src/commands/sessions/web/discoverSessions.ts
12829
- async function discoverSessions() {
12830
- const projectsDir = path47.join(os.homedir(), ".claude", "projects");
12831
- let projectDirs;
12832
- try {
12833
- projectDirs = await fs24.promises.readdir(projectsDir);
12834
- } catch {
12835
- return [];
12836
- }
12837
- const sessions = [];
12838
- await Promise.all(
12839
- projectDirs.map(async (dirName) => {
12840
- const dirPath = path47.join(projectsDir, dirName);
12841
- let entries;
12842
- try {
12843
- entries = await fs24.promises.readdir(dirPath);
12844
- } catch {
12845
- return;
12846
- }
12847
- const jsonlFiles = entries.filter((e) => e.endsWith(".jsonl"));
12848
- await Promise.all(
12849
- jsonlFiles.map(async (file) => {
12850
- const filePath = path47.join(dirPath, file);
12851
- const session = await parseSessionFile(filePath);
12852
- if (session) sessions.push(session);
12853
- })
12854
- );
12855
- })
12856
- );
12857
- sessions.sort(
12858
- (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
12859
- );
12860
- return sessions;
12861
- }
12862
-
12863
13063
  // src/commands/sessions/web/scheduleIdle.ts
12864
13064
  var IDLE_MS = 3e3;
12865
13065
  function scheduleIdle(session, onIdle) {
@@ -13033,13 +13233,14 @@ async function web3(options2) {
13033
13233
  function registerSessions(program2) {
13034
13234
  const cmd = program2.command("sessions").description("Web dashboard for Claude Code sessions").action(() => web3({ port: "3100" }));
13035
13235
  cmd.command("web").description("Start the sessions web dashboard").option("-p, --port <number>", "Port to listen on", "3100").action(web3);
13236
+ cmd.command("summarise").description("Generate one-line summaries for Claude sessions").option("-f, --force", "Re-generate all summaries, even existing ones").option("-n, --limit <count>", "Maximum number of sessions to summarise").action(summarise3);
13036
13237
  }
13037
13238
 
13038
13239
  // src/commands/statusLine.ts
13039
- import chalk135 from "chalk";
13240
+ import chalk136 from "chalk";
13040
13241
 
13041
13242
  // src/commands/buildLimitsSegment.ts
13042
- import chalk134 from "chalk";
13243
+ import chalk135 from "chalk";
13043
13244
  var FIVE_HOUR_SECONDS = 5 * 3600;
13044
13245
  var SEVEN_DAY_SECONDS = 7 * 86400;
13045
13246
  function formatTimeLeft(resetsAt) {
@@ -13062,10 +13263,10 @@ function projectUsage(pct, resetsAt, windowSeconds) {
13062
13263
  function colorizeRateLimit(pct, resetsAt, windowSeconds) {
13063
13264
  const label2 = `${Math.round(pct)}%`;
13064
13265
  const projected = projectUsage(pct, resetsAt, windowSeconds);
13065
- if (projected == null) return chalk134.green(label2);
13066
- if (projected > 100) return chalk134.red(label2);
13067
- if (projected > 75) return chalk134.yellow(label2);
13068
- return chalk134.green(label2);
13266
+ if (projected == null) return chalk135.green(label2);
13267
+ if (projected > 100) return chalk135.red(label2);
13268
+ if (projected > 75) return chalk135.yellow(label2);
13269
+ return chalk135.green(label2);
13069
13270
  }
13070
13271
  function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel) {
13071
13272
  const timeLabel = resetsAt ? formatTimeLeft(resetsAt) : fallbackLabel;
@@ -13091,14 +13292,14 @@ function buildLimitsSegment(rateLimits) {
13091
13292
  }
13092
13293
 
13093
13294
  // src/commands/statusLine.ts
13094
- chalk135.level = 3;
13295
+ chalk136.level = 3;
13095
13296
  function formatNumber(num) {
13096
13297
  return num.toLocaleString("en-US");
13097
13298
  }
13098
13299
  function colorizePercent(pct) {
13099
13300
  const label2 = `${Math.round(pct)}%`;
13100
- if (pct > 80) return chalk135.red(label2);
13101
- if (pct > 40) return chalk135.yellow(label2);
13301
+ if (pct > 80) return chalk136.red(label2);
13302
+ if (pct > 40) return chalk136.yellow(label2);
13102
13303
  return label2;
13103
13304
  }
13104
13305
  async function statusLine() {
@@ -13113,29 +13314,29 @@ async function statusLine() {
13113
13314
  }
13114
13315
 
13115
13316
  // src/commands/sync.ts
13116
- import * as fs27 from "fs";
13317
+ import * as fs30 from "fs";
13117
13318
  import * as os2 from "os";
13118
13319
  import * as path50 from "path";
13119
13320
  import { fileURLToPath as fileURLToPath7 } from "url";
13120
13321
 
13121
13322
  // src/commands/sync/syncClaudeMd.ts
13122
- import * as fs25 from "fs";
13323
+ import * as fs28 from "fs";
13123
13324
  import * as path48 from "path";
13124
- import chalk136 from "chalk";
13325
+ import chalk137 from "chalk";
13125
13326
  async function syncClaudeMd(claudeDir, targetBase, options2) {
13126
13327
  const source = path48.join(claudeDir, "CLAUDE.md");
13127
13328
  const target = path48.join(targetBase, "CLAUDE.md");
13128
- const sourceContent = fs25.readFileSync(source, "utf-8");
13129
- if (fs25.existsSync(target)) {
13130
- const targetContent = fs25.readFileSync(target, "utf-8");
13329
+ const sourceContent = fs28.readFileSync(source, "utf-8");
13330
+ if (fs28.existsSync(target)) {
13331
+ const targetContent = fs28.readFileSync(target, "utf-8");
13131
13332
  if (sourceContent !== targetContent) {
13132
13333
  console.log(
13133
- chalk136.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
13334
+ chalk137.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
13134
13335
  );
13135
13336
  console.log();
13136
13337
  printDiff(targetContent, sourceContent);
13137
13338
  const confirm = options2?.yes || await promptConfirm(
13138
- chalk136.red("Overwrite existing CLAUDE.md?"),
13339
+ chalk137.red("Overwrite existing CLAUDE.md?"),
13139
13340
  false
13140
13341
  );
13141
13342
  if (!confirm) {
@@ -13144,21 +13345,21 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
13144
13345
  }
13145
13346
  }
13146
13347
  }
13147
- fs25.copyFileSync(source, target);
13348
+ fs28.copyFileSync(source, target);
13148
13349
  console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
13149
13350
  }
13150
13351
 
13151
13352
  // src/commands/sync/syncSettings.ts
13152
- import * as fs26 from "fs";
13353
+ import * as fs29 from "fs";
13153
13354
  import * as path49 from "path";
13154
- import chalk137 from "chalk";
13355
+ import chalk138 from "chalk";
13155
13356
  async function syncSettings(claudeDir, targetBase, options2) {
13156
13357
  const source = path49.join(claudeDir, "settings.json");
13157
13358
  const target = path49.join(targetBase, "settings.json");
13158
- const sourceContent = fs26.readFileSync(source, "utf-8");
13359
+ const sourceContent = fs29.readFileSync(source, "utf-8");
13159
13360
  const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
13160
- if (fs26.existsSync(target)) {
13161
- const targetContent = fs26.readFileSync(target, "utf-8");
13361
+ if (fs29.existsSync(target)) {
13362
+ const targetContent = fs29.readFileSync(target, "utf-8");
13162
13363
  const normalizedTarget = JSON.stringify(
13163
13364
  JSON.parse(targetContent),
13164
13365
  null,
@@ -13167,14 +13368,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
13167
13368
  if (mergedContent !== normalizedTarget) {
13168
13369
  if (!options2?.yes) {
13169
13370
  console.log(
13170
- chalk137.yellow(
13371
+ chalk138.yellow(
13171
13372
  "\n\u26A0\uFE0F Warning: settings.json differs from existing file"
13172
13373
  )
13173
13374
  );
13174
13375
  console.log();
13175
13376
  printDiff(targetContent, mergedContent);
13176
13377
  const confirm = await promptConfirm(
13177
- chalk137.red("Overwrite existing settings.json?"),
13378
+ chalk138.red("Overwrite existing settings.json?"),
13178
13379
  false
13179
13380
  );
13180
13381
  if (!confirm) {
@@ -13184,7 +13385,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
13184
13385
  }
13185
13386
  }
13186
13387
  }
13187
- fs26.writeFileSync(target, mergedContent);
13388
+ fs29.writeFileSync(target, mergedContent);
13188
13389
  console.log("Copied settings.json to ~/.claude/settings.json");
13189
13390
  }
13190
13391
 
@@ -13203,10 +13404,10 @@ async function sync(options2) {
13203
13404
  function syncCommands(claudeDir, targetBase) {
13204
13405
  const sourceDir = path50.join(claudeDir, "commands");
13205
13406
  const targetDir = path50.join(targetBase, "commands");
13206
- fs27.mkdirSync(targetDir, { recursive: true });
13207
- const files = fs27.readdirSync(sourceDir);
13407
+ fs30.mkdirSync(targetDir, { recursive: true });
13408
+ const files = fs30.readdirSync(sourceDir);
13208
13409
  for (const file of files) {
13209
- fs27.copyFileSync(path50.join(sourceDir, file), path50.join(targetDir, file));
13410
+ fs30.copyFileSync(path50.join(sourceDir, file), path50.join(targetDir, file));
13210
13411
  console.log(`Copied ${file} to ${targetDir}`);
13211
13412
  }
13212
13413
  console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.196.1",
3
+ "version": "0.197.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {