@mycontxt/cli 0.1.7 → 0.1.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.
package/dist/contxt.js CHANGED
@@ -10,8 +10,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
10
  import { Command } from "commander";
11
11
 
12
12
  // src/commands/init.ts
13
- import { mkdirSync } from "fs";
14
- import { basename } from "path";
13
+ import { mkdirSync, existsSync as existsSync3, writeFileSync, readFileSync as readFileSync2, chmodSync } from "fs";
14
+ import { basename, join as join3 } from "path";
15
+ import { homedir as homedir2 } from "os";
16
+ import { spawn } from "child_process";
15
17
  import { SQLiteDatabase as SQLiteDatabase2 } from "@mycontxt/adapters/sqlite";
16
18
 
17
19
  // src/utils/project.ts
@@ -85,6 +87,9 @@ function error(message) {
85
87
  function info(message) {
86
88
  console.log(chalk.blue("\u2139"), message);
87
89
  }
90
+ function warn(message) {
91
+ console.log(chalk.yellow("\u26A0"), message);
92
+ }
88
93
  function formatEntry(entry) {
89
94
  const lines = [];
90
95
  lines.push(chalk.bold(entry.title));
@@ -119,11 +124,236 @@ function formatEntryList(entries) {
119
124
  return ` ${id} ${title} ${time}`;
120
125
  }).join("\n");
121
126
  }
127
+ function loading(message) {
128
+ console.log(chalk.cyan("~"), message);
129
+ }
130
+ function dryRun(message) {
131
+ console.log(chalk.dim("\u2192"), chalk.dim(message));
132
+ }
133
+ function conflict(message) {
134
+ console.log(chalk.yellow("!"), message);
135
+ }
136
+ function header(title) {
137
+ console.log(chalk.bold.cyan("\u203A"), chalk.bold(title));
138
+ }
122
139
  function section(title) {
123
140
  return chalk.bold.underline(title);
124
141
  }
125
142
 
143
+ // src/utils/usage-gate.ts
144
+ import { existsSync as existsSync2, readFileSync } from "fs";
145
+ import { homedir } from "os";
146
+ import { join as join2 } from "path";
147
+ import chalk2 from "chalk";
148
+ import { UsageGate } from "@mycontxt/core/engine/usage";
149
+ import { resolveUserPlan } from "@mycontxt/core/engine/plan-resolver";
150
+ import { SupabaseDatabase } from "@mycontxt/adapters/supabase";
151
+
152
+ // src/config.ts
153
+ var SUPABASE_URL = "";
154
+ var SUPABASE_ANON_KEY = "";
155
+ function getSupabaseConfig() {
156
+ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
157
+ throw new Error(
158
+ "Contxt is not configured for cloud sync.\nIf you installed via npm, please reinstall the latest version.\nIf building locally, set CONTXT_SUPABASE_URL and CONTXT_SUPABASE_ANON_KEY before running `pnpm build`."
159
+ );
160
+ }
161
+ return { url: SUPABASE_URL, anonKey: SUPABASE_ANON_KEY };
162
+ }
163
+
164
+ // src/utils/usage-gate.ts
165
+ var AUTH_FILE = join2(homedir(), ".contxt", "auth.json");
166
+ function getCurrentUserId() {
167
+ if (!existsSync2(AUTH_FILE)) {
168
+ return null;
169
+ }
170
+ try {
171
+ const content = readFileSync(AUTH_FILE, "utf-8");
172
+ const auth2 = JSON.parse(content);
173
+ return auth2.userId;
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+ function getRemoteDb() {
179
+ if (!existsSync2(AUTH_FILE)) {
180
+ return null;
181
+ }
182
+ try {
183
+ const content = readFileSync(AUTH_FILE, "utf-8");
184
+ const auth2 = JSON.parse(content);
185
+ const config = getSupabaseConfig();
186
+ return new SupabaseDatabase({
187
+ url: config.url,
188
+ anonKey: config.anonKey,
189
+ accessToken: auth2.accessToken
190
+ });
191
+ } catch {
192
+ return null;
193
+ }
194
+ }
195
+ async function getUsageCounts(localDb, userId, projectId) {
196
+ const projectCountQuery = "SELECT COUNT(*) as count FROM projects";
197
+ const projectRow = localDb.db.prepare(projectCountQuery).get();
198
+ const totalProjects = projectRow.count;
199
+ const totalEntriesQuery = "SELECT COUNT(*) as count FROM memory_entries WHERE is_archived = 0";
200
+ const totalEntriesRow = localDb.db.prepare(totalEntriesQuery).get();
201
+ const totalEntries = totalEntriesRow.count;
202
+ let entriesInProject = 0;
203
+ if (projectId) {
204
+ const projectEntriesQuery = "SELECT COUNT(*) as count FROM memory_entries WHERE project_id = ? AND is_archived = 0";
205
+ const projectEntriesRow = localDb.db.prepare(projectEntriesQuery).get(projectId);
206
+ entriesInProject = projectEntriesRow.count;
207
+ }
208
+ return {
209
+ totalProjects,
210
+ totalEntries,
211
+ entriesInProject,
212
+ totalSeats: 1
213
+ // Single user for now
214
+ };
215
+ }
216
+ async function createUsageGate(localDb, projectId) {
217
+ const userId = getCurrentUserId();
218
+ const remoteDb = getRemoteDb();
219
+ const planId = await resolveUserPlan(localDb, remoteDb, userId);
220
+ return new UsageGate(planId, () => getUsageCounts(localDb, userId, projectId));
221
+ }
222
+ function enforceGate(result) {
223
+ if (!result.allowed) {
224
+ console.error(chalk2.red(`
225
+ \u2716 ${result.reason}`));
226
+ console.error(chalk2.dim(` Current: ${result.current} / Limit: ${result.limit === null ? "\u221E" : result.limit}`));
227
+ console.error(chalk2.cyan(`
228
+ ${result.upgradeHint}
229
+ `));
230
+ process.exit(1);
231
+ }
232
+ }
233
+
126
234
  // src/commands/init.ts
235
+ var CLAUDE_MD_TEMPLATE = `# Contxt \u2014 Active Context Capture
236
+
237
+ This project uses [Contxt](https://mycontxt.co) (MCP) to automatically capture technical decisions and patterns from your conversations.
238
+
239
+ ## Your Role
240
+
241
+ During every conversation in this project, actively capture knowledge using the Contxt MCP tools:
242
+
243
+ - When the developer makes an architectural or technical decision \u2192 call \`contxt_auto_capture_decision\` or \`contxt_capture_discussion\`
244
+ - When a reusable code pattern or convention is established \u2192 call \`contxt_auto_capture_pattern\`
245
+ - At the end of each conversation \u2192 call \`contxt_update_session\` with a summary of what was accomplished
246
+
247
+ Use a consistent \`conversationId\` (a UUID you generate at the start of the session) on every tool call so entries from the same conversation are linked.
248
+
249
+ ## When to Capture a Decision
250
+
251
+ Capture a **decision** when the developer (or you together):
252
+ - Chooses one technology, library, or approach over alternatives
253
+ - Establishes a project structure, naming convention, or architectural boundary
254
+ - Resolves a tradeoff (e.g. performance vs. simplicity, SQL vs. NoSQL)
255
+ - Says "let's use X", "we'll go with Y", or "we decided to Z"
256
+
257
+ Use \`contxt_capture_discussion\` when the decision came out of back-and-forth so you can capture the context and what was rejected.
258
+
259
+ ## When to Capture a Pattern
260
+
261
+ Capture a **pattern** when:
262
+ - A code shape is established that will be repeated (component structure, API format, hook convention, etc.)
263
+ - A rule is set about how something should always be done in this project
264
+
265
+ ## What NOT to Capture
266
+
267
+ - One-off debugging steps or experiments
268
+ - Trivial implementation details (e.g. "added a console.log")
269
+ - Things the developer explicitly says to ignore
270
+
271
+ ## After Capture
272
+
273
+ All entries are saved as **drafts**. The developer reviews and confirms them with:
274
+ \`\`\`
275
+ contxt review
276
+ \`\`\`
277
+ `;
278
+ var MCP_CONFIG = {
279
+ mcpServers: {
280
+ contxt: {
281
+ command: "contxt",
282
+ args: ["mcp"],
283
+ env: {}
284
+ }
285
+ }
286
+ };
287
+ var CONTXT_BLOCK_START = "# --- contxt hook start ---";
288
+ var CONTXT_BLOCK_END = "# --- contxt hook end ---";
289
+ var ALL_HOOKS = ["post-commit", "pre-push", "post-checkout", "prepare-commit-msg"];
290
+ function writeMcpConfigs(cwd) {
291
+ const mcpJson = JSON.stringify(MCP_CONFIG, null, 2);
292
+ writeFileSync(join3(cwd, ".mcp.json"), mcpJson, "utf-8");
293
+ const cursorDir = join3(cwd, ".cursor");
294
+ mkdirSync(cursorDir, { recursive: true });
295
+ writeFileSync(join3(cursorDir, "mcp.json"), mcpJson, "utf-8");
296
+ }
297
+ function installGitHooks(cwd) {
298
+ const gitDir = join3(cwd, ".git");
299
+ if (!existsSync3(gitDir)) return false;
300
+ const gitHooksDir = join3(gitDir, "hooks");
301
+ mkdirSync(gitHooksDir, { recursive: true });
302
+ for (const hookName of ALL_HOOKS) {
303
+ const hookPath = join3(gitHooksDir, hookName);
304
+ const contxtBlock = `${CONTXT_BLOCK_START}
305
+ contxt hook run ${hookName} "$@"
306
+ ${CONTXT_BLOCK_END}`;
307
+ if (existsSync3(hookPath)) {
308
+ const content = readFileSync2(hookPath, "utf-8");
309
+ if (!content.includes(CONTXT_BLOCK_START)) {
310
+ writeFileSync(hookPath, content.trimEnd() + "\n\n" + contxtBlock + "\n", "utf-8");
311
+ }
312
+ } else {
313
+ writeFileSync(hookPath, `#!/bin/sh
314
+
315
+ ${contxtBlock}
316
+ `, "utf-8");
317
+ }
318
+ chmodSync(hookPath, "755");
319
+ }
320
+ return true;
321
+ }
322
+ function startWatchDaemon(cwd) {
323
+ const pidFile = join3(cwd, ".contxt", ".watch.pid");
324
+ if (existsSync3(pidFile)) return;
325
+ const child = spawn("contxt", ["watch", "--daemon"], {
326
+ detached: true,
327
+ stdio: "ignore",
328
+ cwd
329
+ });
330
+ child.unref();
331
+ }
332
+ function registerClaudeCodeHook() {
333
+ const claudeDir = join3(homedir2(), ".claude");
334
+ const settingsPath = join3(claudeDir, "settings.json");
335
+ const hookCommand2 = `bash -c 'PROMPT=$(cat | jq -r ".prompt // empty" 2>/dev/null | head -c 300); [ -n "$PROMPT" ] && contxt load --task "$PROMPT" 2>/dev/null || true'`;
336
+ let settings = {};
337
+ if (existsSync3(settingsPath)) {
338
+ try {
339
+ settings = JSON.parse(readFileSync2(settingsPath, "utf-8"));
340
+ } catch {
341
+ }
342
+ }
343
+ if (!settings.hooks) settings.hooks = {};
344
+ if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = [];
345
+ const alreadyRegistered = settings.hooks.UserPromptSubmit.some(
346
+ (h) => h.hooks?.some((hh) => typeof hh.command === "string" && hh.command.includes("contxt load"))
347
+ );
348
+ if (!alreadyRegistered) {
349
+ settings.hooks.UserPromptSubmit.push({
350
+ matcher: "",
351
+ hooks: [{ type: "command", command: hookCommand2 }]
352
+ });
353
+ mkdirSync(claudeDir, { recursive: true });
354
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
355
+ }
356
+ }
127
357
  async function initCommand(options) {
128
358
  try {
129
359
  const cwd = process.cwd();
@@ -133,25 +363,34 @@ async function initCommand(options) {
133
363
  }
134
364
  const contxtDir = getContxtDir(cwd);
135
365
  mkdirSync(contxtDir, { recursive: true });
366
+ const claudeMdPath = join3(contxtDir, "CLAUDE.md");
367
+ writeFileSync(claudeMdPath, CLAUDE_MD_TEMPLATE, "utf-8");
136
368
  const dbPath = getDbPath(cwd);
137
369
  const db = new SQLiteDatabase2(dbPath);
138
370
  await db.initialize();
371
+ const gate = await createUsageGate(db);
372
+ const result = await gate.checkProjectCreate();
373
+ enforceGate(result);
139
374
  const projectName = options.name || basename(cwd);
140
375
  const project = await db.initProject({
141
376
  name: projectName,
142
377
  path: cwd,
143
- stack: []
144
- // TODO: Auto-detect stack
378
+ stack: [],
379
+ config: { autoSync: true }
145
380
  });
381
+ await db.close();
382
+ writeMcpConfigs(cwd);
383
+ const hooksInstalled = installGitHooks(cwd);
384
+ startWatchDaemon(cwd);
385
+ registerClaudeCodeHook();
146
386
  success(`Initialized Contxt project: ${project.name}`);
147
- info(`Project ID: ${project.id}`);
148
- info(`Database: ${dbPath}`);
149
387
  console.log();
150
- console.log("Get started:");
151
- console.log(' contxt decision add -t "..." -r "..."');
152
- console.log(' contxt pattern add -t "..." -c "..."');
153
- console.log(" contxt status");
154
- await db.close();
388
+ info("\u2713 MCP server configured (.mcp.json + .cursor/mcp.json)");
389
+ if (hooksInstalled) info("\u2713 Git hooks installed (post-commit, pre-push, post-checkout)");
390
+ info("\u2713 Watch daemon started (auto-sync enabled)");
391
+ info("\u2713 Claude Code context hook registered");
392
+ console.log();
393
+ console.log("Contxt is running. You won't need to think about it again.");
155
394
  } catch (err) {
156
395
  error(`Failed to initialize project: ${err.message}`);
157
396
  process.exit(1);
@@ -162,6 +401,9 @@ async function initCommand(options) {
162
401
  async function add(options) {
163
402
  try {
164
403
  const { engine, projectId, db } = await loadProject();
404
+ const gate = await createUsageGate(db, projectId);
405
+ const result = await gate.checkEntryCreate();
406
+ enforceGate(result);
165
407
  const input = {
166
408
  title: options.title,
167
409
  rationale: options.rationale,
@@ -212,6 +454,9 @@ var decisionCommand = {
212
454
  async function add2(options) {
213
455
  try {
214
456
  const { engine, projectId, db } = await loadProject();
457
+ const gate = await createUsageGate(db, projectId);
458
+ const result = await gate.checkEntryCreate();
459
+ enforceGate(result);
215
460
  const input = {
216
461
  title: options.title,
217
462
  content: options.content,
@@ -314,7 +559,7 @@ var contextCommand = {
314
559
  };
315
560
 
316
561
  // src/commands/status.ts
317
- import chalk2 from "chalk";
562
+ import chalk3 from "chalk";
318
563
  async function statusCommand() {
319
564
  try {
320
565
  const { engine, projectId, db } = await loadProject();
@@ -333,14 +578,14 @@ async function statusCommand() {
333
578
  console.log();
334
579
  console.log(section("Project Status"));
335
580
  console.log();
336
- console.log(chalk2.bold("Name:"), project.name);
337
- console.log(chalk2.bold("Path:"), project.path);
338
- console.log(chalk2.bold("ID:"), project.id);
581
+ console.log(chalk3.bold("Name:"), project.name);
582
+ console.log(chalk3.bold("Path:"), project.path);
583
+ console.log(chalk3.bold("ID:"), project.id);
339
584
  console.log();
340
585
  console.log(section("Branches"));
341
586
  console.log();
342
587
  for (const branch2 of branches) {
343
- const marker = branch2.name === activeBranch ? chalk2.green("\u25CF") : " ";
588
+ const marker = branch2.name === activeBranch ? chalk3.green("\u25CF") : " ";
344
589
  console.log(` ${marker} ${branch2.name}`);
345
590
  }
346
591
  console.log();
@@ -356,21 +601,21 @@ async function statusCommand() {
356
601
  console.log(section("Current Context"));
357
602
  console.log();
358
603
  if (context2.metadata.feature) {
359
- console.log(chalk2.bold("Feature:"), context2.metadata.feature);
604
+ console.log(chalk3.bold("Feature:"), context2.metadata.feature);
360
605
  }
361
606
  if (context2.metadata.blockers?.length > 0) {
362
- console.log(chalk2.bold("Blockers:"));
607
+ console.log(chalk3.bold("Blockers:"));
363
608
  context2.metadata.blockers.forEach((b) => console.log(` - ${b}`));
364
609
  }
365
610
  if (context2.metadata.nextSteps?.length > 0) {
366
- console.log(chalk2.bold("Next Steps:"));
611
+ console.log(chalk3.bold("Next Steps:"));
367
612
  context2.metadata.nextSteps.forEach((s) => console.log(` - ${s}`));
368
613
  }
369
614
  console.log();
370
615
  }
371
616
  if (unsynced.length > 0) {
372
- console.log(chalk2.yellow(`\u26A0 ${unsynced.length} unsynced entries`));
373
- console.log(chalk2.dim(' Run "contxt push" to sync'));
617
+ console.log(chalk3.yellow(`\u26A0 ${unsynced.length} unsynced entries`));
618
+ console.log(chalk3.dim(' Run "contxt push" to sync'));
374
619
  console.log();
375
620
  }
376
621
  await db.close();
@@ -381,14 +626,14 @@ async function statusCommand() {
381
626
  }
382
627
 
383
628
  // src/commands/doc.ts
384
- import { readFileSync } from "fs";
629
+ import { readFileSync as readFileSync3 } from "fs";
385
630
  async function add3(options) {
386
631
  try {
387
632
  const { engine, projectId, db } = await loadProject();
388
633
  let content = options.content || "";
389
634
  if (options.file) {
390
635
  try {
391
- content = readFileSync(options.file, "utf-8");
636
+ content = readFileSync3(options.file, "utf-8");
392
637
  } catch (err) {
393
638
  error(`Failed to read file: ${err.message}`);
394
639
  process.exit(1);
@@ -530,7 +775,7 @@ async function searchCommand(query, options) {
530
775
  }
531
776
 
532
777
  // src/commands/export.ts
533
- import { writeFileSync, readFileSync as readFileSync2 } from "fs";
778
+ import { writeFileSync as writeFileSync2, readFileSync as readFileSync4 } from "fs";
534
779
  async function exportCommand(options) {
535
780
  try {
536
781
  const { engine, projectId, db } = await loadProject();
@@ -557,7 +802,7 @@ async function exportCommand(options) {
557
802
  };
558
803
  const json = JSON.stringify(exportData, null, 2);
559
804
  if (options.output) {
560
- writeFileSync(options.output, json, "utf-8");
805
+ writeFileSync2(options.output, json, "utf-8");
561
806
  success(`Exported ${entries.length} entries to ${options.output}`);
562
807
  } else {
563
808
  console.log(json);
@@ -573,7 +818,7 @@ async function importCommand(options) {
573
818
  const { engine, projectId, db } = await loadProject();
574
819
  let data;
575
820
  try {
576
- const json = readFileSync2(options.file, "utf-8");
821
+ const json = readFileSync4(options.file, "utf-8");
577
822
  data = JSON.parse(json);
578
823
  } catch (err) {
579
824
  error(`Failed to read import file: ${err.message}`);
@@ -610,37 +855,23 @@ async function importCommand(options) {
610
855
 
611
856
  // src/commands/auth.ts
612
857
  import { SupabaseAuth } from "@mycontxt/adapters/supabase-auth";
613
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync3 } from "fs";
614
- import { homedir } from "os";
615
- import { join as join2 } from "path";
616
-
617
- // src/config.ts
618
- var SUPABASE_URL = "https://nreejrbrgbpcteliqqxm.supabase.co";
619
- var SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im5yZWVqcmJyZ2JwY3RlbGlxcXhtIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzEyODU3MDcsImV4cCI6MjA4Njg2MTcwN30.1oMu3WWwPQj1PU9jkAeWt7dbzaS4LN99pMkc299RWhY";
620
- function getSupabaseConfig() {
621
- if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
622
- throw new Error(
623
- "Contxt is not configured for cloud sync.\nIf you installed via npm, please reinstall the latest version.\nIf building locally, set CONTXT_SUPABASE_URL and CONTXT_SUPABASE_ANON_KEY before running `pnpm build`."
624
- );
625
- }
626
- return { url: SUPABASE_URL, anonKey: SUPABASE_ANON_KEY };
627
- }
628
-
629
- // src/commands/auth.ts
630
- var CONFIG_DIR = join2(homedir(), ".contxt");
631
- var AUTH_FILE = join2(CONFIG_DIR, "auth.json");
858
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3, readFileSync as readFileSync5 } from "fs";
859
+ import { homedir as homedir3 } from "os";
860
+ import { join as join4 } from "path";
861
+ var CONFIG_DIR = join4(homedir3(), ".contxt");
862
+ var AUTH_FILE2 = join4(CONFIG_DIR, "auth.json");
632
863
  function saveAuthData(data) {
633
- if (!existsSync3(CONFIG_DIR)) {
864
+ if (!existsSync4(CONFIG_DIR)) {
634
865
  mkdirSync2(CONFIG_DIR, { recursive: true });
635
866
  }
636
- writeFileSync2(AUTH_FILE, JSON.stringify(data, null, 2), "utf-8");
867
+ writeFileSync3(AUTH_FILE2, JSON.stringify(data, null, 2), "utf-8");
637
868
  }
638
869
  function loadAuthData() {
639
- if (!existsSync3(AUTH_FILE)) {
870
+ if (!existsSync4(AUTH_FILE2)) {
640
871
  return null;
641
872
  }
642
873
  try {
643
- const content = readFileSync3(AUTH_FILE, "utf-8");
874
+ const content = readFileSync5(AUTH_FILE2, "utf-8");
644
875
  return JSON.parse(content);
645
876
  } catch {
646
877
  return null;
@@ -651,10 +882,12 @@ var authCommand = {
651
882
  try {
652
883
  const config = getSupabaseConfig();
653
884
  const auth2 = new SupabaseAuth(config);
654
- console.log("\u{1F510} Contxt Authentication\n");
885
+ header("Contxt Authentication");
886
+ console.log("");
655
887
  if (options.email) {
656
888
  await auth2.loginWithMagicLink(options.email);
657
- console.log("\n\u2705 Magic link sent! Check your email and click the link.");
889
+ console.log("");
890
+ success("Magic link sent! Check your email and click the link.");
658
891
  console.log(" Then run `contxt auth status` to verify.");
659
892
  } else {
660
893
  console.log("Opening browser for GitHub authentication...\n");
@@ -665,18 +898,16 @@ var authCommand = {
665
898
  email: result.user.email,
666
899
  githubUsername: result.user.githubUsername
667
900
  });
668
- console.log("\n\u2705 Successfully authenticated!");
901
+ console.log("");
902
+ success("Successfully authenticated!");
669
903
  console.log(` Email: ${result.user.email}`);
670
904
  if (result.user.githubUsername) {
671
905
  console.log(` GitHub: @${result.user.githubUsername}`);
672
906
  }
673
907
  console.log("\nYou can now use `contxt push` and `contxt pull` to sync your memory.");
674
908
  }
675
- } catch (error2) {
676
- console.error(
677
- "\u274C Authentication failed:",
678
- error2 instanceof Error ? error2.message : error2
679
- );
909
+ } catch (err) {
910
+ error(`Authentication failed: ${err instanceof Error ? err.message : err}`);
680
911
  process.exit(1);
681
912
  }
682
913
  },
@@ -685,16 +916,13 @@ var authCommand = {
685
916
  const config = getSupabaseConfig();
686
917
  const auth2 = new SupabaseAuth(config);
687
918
  await auth2.logout();
688
- if (existsSync3(AUTH_FILE)) {
919
+ if (existsSync4(AUTH_FILE2)) {
689
920
  const fs = await import("fs/promises");
690
- await fs.unlink(AUTH_FILE);
921
+ await fs.unlink(AUTH_FILE2);
691
922
  }
692
- console.log("\u2705 Logged out successfully");
693
- } catch (error2) {
694
- console.error(
695
- "\u274C Logout failed:",
696
- error2 instanceof Error ? error2.message : error2
697
- );
923
+ success("Logged out successfully");
924
+ } catch (err) {
925
+ error(`Logout failed: ${err instanceof Error ? err.message : err}`);
698
926
  process.exit(1);
699
927
  }
700
928
  },
@@ -702,11 +930,11 @@ var authCommand = {
702
930
  try {
703
931
  const authData = loadAuthData();
704
932
  if (!authData) {
705
- console.log("\u274C Not authenticated");
933
+ error("Not authenticated");
706
934
  console.log("\nRun `contxt auth login` to authenticate.");
707
935
  process.exit(1);
708
936
  }
709
- console.log("\u2705 Authenticated");
937
+ success("Authenticated");
710
938
  console.log(` Email: ${authData.email}`);
711
939
  if (authData.githubUsername) {
712
940
  console.log(` GitHub: @${authData.githubUsername}`);
@@ -716,15 +944,14 @@ var authCommand = {
716
944
  const auth2 = new SupabaseAuth(config);
717
945
  try {
718
946
  await auth2.refreshSession();
719
- console.log("\n\u2705 Session is valid");
947
+ console.log("");
948
+ success("Session is valid");
720
949
  } catch {
721
- console.log("\n\u26A0\uFE0F Session expired. Run `contxt auth login` to re-authenticate.");
950
+ console.log("");
951
+ warn("Session expired. Run `contxt auth login` to re-authenticate.");
722
952
  }
723
- } catch (error2) {
724
- console.error(
725
- "\u274C Status check failed:",
726
- error2 instanceof Error ? error2.message : error2
727
- );
953
+ } catch (err) {
954
+ error(`Status check failed: ${err instanceof Error ? err.message : err}`);
728
955
  process.exit(1);
729
956
  }
730
957
  }
@@ -736,7 +963,7 @@ function getAccessToken() {
736
963
 
737
964
  // src/commands/sync.ts
738
965
  import { SQLiteDatabase as SQLiteDatabase3 } from "@mycontxt/adapters/sqlite";
739
- import { SupabaseDatabase } from "@mycontxt/adapters/supabase";
966
+ import { SupabaseDatabase as SupabaseDatabase2 } from "@mycontxt/adapters/supabase";
740
967
  import { SyncEngine } from "@mycontxt/core";
741
968
  var syncCommand = {
742
969
  /**
@@ -746,7 +973,7 @@ var syncCommand = {
746
973
  try {
747
974
  const accessToken = getAccessToken();
748
975
  if (!accessToken) {
749
- console.error("\u274C Not authenticated. Run `contxt auth login` first.");
976
+ error("Not authenticated. Run `contxt auth login` first.");
750
977
  process.exit(1);
751
978
  }
752
979
  const dbPath = getDbPath();
@@ -756,45 +983,41 @@ var syncCommand = {
756
983
  const cwd = process.cwd();
757
984
  const project = await localDb.getProjectByPath(cwd);
758
985
  if (!project) {
759
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
986
+ error("No Contxt project found. Run `contxt init` first.");
760
987
  process.exit(1);
761
988
  }
762
989
  const supabaseConfig = getSupabaseConfig();
763
- const remoteDb = new SupabaseDatabase({
990
+ const remoteDb = new SupabaseDatabase2({
764
991
  ...supabaseConfig,
765
992
  accessToken
766
993
  });
767
994
  await remoteDb.initialize();
768
995
  const syncEngine = new SyncEngine(localDb, remoteDb);
769
- console.log("\u{1F504} Pushing local changes to cloud...\n");
996
+ loading("Pushing local changes to cloud...");
997
+ console.log("");
770
998
  const result = await syncEngine.push(project.id, {
771
999
  force: options.force,
772
1000
  dryRun: options.dryRun
773
1001
  });
774
1002
  if (result.errors.length > 0) {
775
- console.error("\u274C Push failed:");
1003
+ error("Push failed:");
776
1004
  result.errors.forEach((err) => console.error(` ${err}`));
777
1005
  process.exit(1);
778
1006
  }
779
1007
  if (options.dryRun) {
780
- console.log(`\u{1F4CB} Dry run - would push ${result.pushed} entries`);
1008
+ dryRun(`Dry run \u2014 would push ${result.pushed} entries`);
781
1009
  } else {
782
- console.log(`\u2705 Successfully pushed ${result.pushed} entries`);
1010
+ success(`Pushed ${result.pushed} entries`);
783
1011
  }
784
1012
  if (result.conflicts > 0) {
785
- console.log(
786
- `\u26A0\uFE0F ${result.conflicts} conflict(s) detected. Use --force to override.`
787
- );
1013
+ conflict(`${result.conflicts} conflict(s) detected. Use --force to override.`);
788
1014
  }
789
1015
  await remoteDb.close();
790
1016
  } finally {
791
1017
  await localDb.close();
792
1018
  }
793
- } catch (error2) {
794
- console.error(
795
- "\u274C Push failed:",
796
- error2 instanceof Error ? error2.message : error2
797
- );
1019
+ } catch (err) {
1020
+ error(`Push failed: ${err instanceof Error ? err.message : err}`);
798
1021
  process.exit(1);
799
1022
  }
800
1023
  },
@@ -805,7 +1028,7 @@ var syncCommand = {
805
1028
  try {
806
1029
  const accessToken = getAccessToken();
807
1030
  if (!accessToken) {
808
- console.error("\u274C Not authenticated. Run `contxt auth login` first.");
1031
+ error("Not authenticated. Run `contxt auth login` first.");
809
1032
  process.exit(1);
810
1033
  }
811
1034
  const dbPath = getDbPath();
@@ -815,45 +1038,41 @@ var syncCommand = {
815
1038
  const cwd = process.cwd();
816
1039
  const project = await localDb.getProjectByPath(cwd);
817
1040
  if (!project) {
818
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
1041
+ error("No Contxt project found. Run `contxt init` first.");
819
1042
  process.exit(1);
820
1043
  }
821
1044
  const supabaseConfig = getSupabaseConfig();
822
- const remoteDb = new SupabaseDatabase({
1045
+ const remoteDb = new SupabaseDatabase2({
823
1046
  ...supabaseConfig,
824
1047
  accessToken
825
1048
  });
826
1049
  await remoteDb.initialize();
827
1050
  const syncEngine = new SyncEngine(localDb, remoteDb);
828
- console.log("\u{1F504} Pulling remote changes to local...\n");
1051
+ loading("Pulling remote changes to local...");
1052
+ console.log("");
829
1053
  const result = await syncEngine.pull(project.id, {
830
1054
  force: options.force,
831
1055
  dryRun: options.dryRun
832
1056
  });
833
1057
  if (result.errors.length > 0) {
834
- console.error("\u274C Pull failed:");
1058
+ error("Pull failed:");
835
1059
  result.errors.forEach((err) => console.error(` ${err}`));
836
1060
  process.exit(1);
837
1061
  }
838
1062
  if (options.dryRun) {
839
- console.log(`\u{1F4CB} Dry run - would pull ${result.pulled} entries`);
1063
+ dryRun(`Dry run \u2014 would pull ${result.pulled} entries`);
840
1064
  } else {
841
- console.log(`\u2705 Successfully pulled ${result.pulled} entries`);
1065
+ success(`Pulled ${result.pulled} entries`);
842
1066
  }
843
1067
  if (result.conflicts > 0) {
844
- console.log(
845
- `\u26A0\uFE0F ${result.conflicts} conflict(s) detected. Use --force to override.`
846
- );
1068
+ conflict(`${result.conflicts} conflict(s) detected. Use --force to override.`);
847
1069
  }
848
1070
  await remoteDb.close();
849
1071
  } finally {
850
1072
  await localDb.close();
851
1073
  }
852
- } catch (error2) {
853
- console.error(
854
- "\u274C Pull failed:",
855
- error2 instanceof Error ? error2.message : error2
856
- );
1074
+ } catch (err) {
1075
+ error(`Pull failed: ${err instanceof Error ? err.message : err}`);
857
1076
  process.exit(1);
858
1077
  }
859
1078
  },
@@ -864,7 +1083,7 @@ var syncCommand = {
864
1083
  try {
865
1084
  const accessToken = getAccessToken();
866
1085
  if (!accessToken) {
867
- console.error("\u274C Not authenticated. Run `contxt auth login` first.");
1086
+ error("Not authenticated. Run `contxt auth login` first.");
868
1087
  process.exit(1);
869
1088
  }
870
1089
  const dbPath = getDbPath();
@@ -874,49 +1093,43 @@ var syncCommand = {
874
1093
  const cwd = process.cwd();
875
1094
  const project = await localDb.getProjectByPath(cwd);
876
1095
  if (!project) {
877
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
1096
+ error("No Contxt project found. Run `contxt init` first.");
878
1097
  process.exit(1);
879
1098
  }
880
1099
  const supabaseConfig = getSupabaseConfig();
881
- const remoteDb = new SupabaseDatabase({
1100
+ const remoteDb = new SupabaseDatabase2({
882
1101
  ...supabaseConfig,
883
1102
  accessToken
884
1103
  });
885
1104
  await remoteDb.initialize();
886
1105
  const syncEngine = new SyncEngine(localDb, remoteDb);
887
- console.log("\u{1F504} Syncing with cloud (pull + push)...\n");
1106
+ loading("Syncing with cloud (pull + push)...");
1107
+ console.log("");
888
1108
  const result = await syncEngine.sync(project.id, {
889
1109
  force: options.force,
890
1110
  dryRun: options.dryRun
891
1111
  });
892
1112
  if (result.errors.length > 0) {
893
- console.error("\u274C Sync failed:");
1113
+ error("Sync failed:");
894
1114
  result.errors.forEach((err) => console.error(` ${err}`));
895
1115
  process.exit(1);
896
1116
  }
897
1117
  if (options.dryRun) {
898
- console.log(
899
- `\u{1F4CB} Dry run - would pull ${result.pulled} and push ${result.pushed} entries`
900
- );
1118
+ dryRun(`Dry run \u2014 would pull ${result.pulled} and push ${result.pushed} entries`);
901
1119
  } else {
902
- console.log(`\u2705 Successfully synced`);
1120
+ success("Synced successfully");
903
1121
  console.log(` Pulled: ${result.pulled} entries`);
904
1122
  console.log(` Pushed: ${result.pushed} entries`);
905
1123
  }
906
1124
  if (result.conflicts > 0) {
907
- console.log(
908
- `\u26A0\uFE0F ${result.conflicts} conflict(s) detected. Use --force to override.`
909
- );
1125
+ conflict(`${result.conflicts} conflict(s) detected. Use --force to override.`);
910
1126
  }
911
1127
  await remoteDb.close();
912
1128
  } finally {
913
1129
  await localDb.close();
914
1130
  }
915
- } catch (error2) {
916
- console.error(
917
- "\u274C Sync failed:",
918
- error2 instanceof Error ? error2.message : error2
919
- );
1131
+ } catch (err) {
1132
+ error(`Sync failed: ${err instanceof Error ? err.message : err}`);
920
1133
  process.exit(1);
921
1134
  }
922
1135
  }
@@ -937,20 +1150,20 @@ var branchCommand = {
937
1150
  const cwd = process.cwd();
938
1151
  const project = await db.getProjectByPath(cwd);
939
1152
  if (!project) {
940
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
1153
+ error("No Contxt project found. Run `contxt init` first.");
941
1154
  process.exit(1);
942
1155
  }
1156
+ const gate = await createUsageGate(db, project.id);
1157
+ const result = gate.checkFeature("branchingEnabled");
1158
+ enforceGate(result);
943
1159
  const fromBranch = options.from || await db.getActiveBranch(project.id);
944
1160
  await db.createBranch(project.id, name, fromBranch);
945
- console.log(`\u2705 Created branch '${name}' from '${fromBranch}'`);
1161
+ success(`Created branch '${name}' from '${fromBranch}'`);
946
1162
  } finally {
947
1163
  await db.close();
948
1164
  }
949
- } catch (error2) {
950
- console.error(
951
- "\u274C Branch create failed:",
952
- error2 instanceof Error ? error2.message : error2
953
- );
1165
+ } catch (err) {
1166
+ error(`Branch create failed: ${err instanceof Error ? err.message : err}`);
954
1167
  process.exit(1);
955
1168
  }
956
1169
  },
@@ -966,7 +1179,7 @@ var branchCommand = {
966
1179
  const cwd = process.cwd();
967
1180
  const project = await db.getProjectByPath(cwd);
968
1181
  if (!project) {
969
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
1182
+ error("No Contxt project found. Run `contxt init` first.");
970
1183
  process.exit(1);
971
1184
  }
972
1185
  const branches = await db.listBranches(project.id);
@@ -980,11 +1193,8 @@ var branchCommand = {
980
1193
  } finally {
981
1194
  await db.close();
982
1195
  }
983
- } catch (error2) {
984
- console.error(
985
- "\u274C Branch list failed:",
986
- error2 instanceof Error ? error2.message : error2
987
- );
1196
+ } catch (err) {
1197
+ error(`Branch list failed: ${err instanceof Error ? err.message : err}`);
988
1198
  process.exit(1);
989
1199
  }
990
1200
  },
@@ -1000,19 +1210,19 @@ var branchCommand = {
1000
1210
  const cwd = process.cwd();
1001
1211
  const project = await db.getProjectByPath(cwd);
1002
1212
  if (!project) {
1003
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
1213
+ error("No Contxt project found. Run `contxt init` first.");
1004
1214
  process.exit(1);
1005
1215
  }
1216
+ const gate = await createUsageGate(db, project.id);
1217
+ const result = gate.checkFeature("branchingEnabled");
1218
+ enforceGate(result);
1006
1219
  await db.switchBranch(project.id, name);
1007
- console.log(`\u2705 Switched to branch '${name}'`);
1220
+ success(`Switched to branch '${name}'`);
1008
1221
  } finally {
1009
1222
  await db.close();
1010
1223
  }
1011
- } catch (error2) {
1012
- console.error(
1013
- "\u274C Branch switch failed:",
1014
- error2 instanceof Error ? error2.message : error2
1015
- );
1224
+ } catch (err) {
1225
+ error(`Branch switch failed: ${err instanceof Error ? err.message : err}`);
1016
1226
  process.exit(1);
1017
1227
  }
1018
1228
  },
@@ -1028,28 +1238,25 @@ var branchCommand = {
1028
1238
  const cwd = process.cwd();
1029
1239
  const project = await db.getProjectByPath(cwd);
1030
1240
  if (!project) {
1031
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
1241
+ error("No Contxt project found. Run `contxt init` first.");
1032
1242
  process.exit(1);
1033
1243
  }
1034
1244
  const activeBranch = await db.getActiveBranch(project.id);
1035
1245
  if (name === activeBranch) {
1036
- console.error("\u274C Cannot delete active branch. Switch to another branch first.");
1246
+ error("Cannot delete active branch. Switch to another branch first.");
1037
1247
  process.exit(1);
1038
1248
  }
1039
1249
  if (name === "main") {
1040
- console.error("\u274C Cannot delete main branch.");
1250
+ error("Cannot delete main branch.");
1041
1251
  process.exit(1);
1042
1252
  }
1043
1253
  await db.deleteBranch(project.id, name);
1044
- console.log(`\u2705 Deleted branch '${name}'`);
1254
+ success(`Deleted branch '${name}'`);
1045
1255
  } finally {
1046
1256
  await db.close();
1047
1257
  }
1048
- } catch (error2) {
1049
- console.error(
1050
- "\u274C Branch delete failed:",
1051
- error2 instanceof Error ? error2.message : error2
1052
- );
1258
+ } catch (err) {
1259
+ error(`Branch delete failed: ${err instanceof Error ? err.message : err}`);
1053
1260
  process.exit(1);
1054
1261
  }
1055
1262
  },
@@ -1065,12 +1272,12 @@ var branchCommand = {
1065
1272
  const cwd = process.cwd();
1066
1273
  const project = await db.getProjectByPath(cwd);
1067
1274
  if (!project) {
1068
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
1275
+ error("No Contxt project found. Run `contxt init` first.");
1069
1276
  process.exit(1);
1070
1277
  }
1071
1278
  const targetBranch = await db.getActiveBranch(project.id);
1072
1279
  if (sourceBranch === targetBranch) {
1073
- console.error("\u274C Cannot merge a branch into itself.");
1280
+ error("Cannot merge a branch into itself.");
1074
1281
  process.exit(1);
1075
1282
  }
1076
1283
  const sourceEntries = await db.listEntries({
@@ -1106,17 +1313,12 @@ var branchCommand = {
1106
1313
  merged++;
1107
1314
  }
1108
1315
  }
1109
- console.log(
1110
- `\u2705 Merged ${merged} entries from '${sourceBranch}' into '${targetBranch}'`
1111
- );
1316
+ success(`Merged ${merged} entries from '${sourceBranch}' into '${targetBranch}'`);
1112
1317
  } finally {
1113
1318
  await db.close();
1114
1319
  }
1115
- } catch (error2) {
1116
- console.error(
1117
- "\u274C Branch merge failed:",
1118
- error2 instanceof Error ? error2.message : error2
1119
- );
1320
+ } catch (err) {
1321
+ error(`Branch merge failed: ${err instanceof Error ? err.message : err}`);
1120
1322
  process.exit(1);
1121
1323
  }
1122
1324
  }
@@ -1136,17 +1338,17 @@ var historyCommand = {
1136
1338
  try {
1137
1339
  const entry = await db.getEntry(entryId);
1138
1340
  if (!entry) {
1139
- console.error("\u274C Entry not found.");
1341
+ error("Entry not found.");
1140
1342
  process.exit(1);
1141
1343
  }
1142
1344
  const versions = await db.getVersionHistory(entryId);
1143
- console.log(`\u{1F4DC} Version History: ${entry.title}
1144
- `);
1345
+ console.log(section(`Version History: ${entry.title}`));
1346
+ console.log("");
1145
1347
  console.log(`Current (v${entry.version}):`);
1146
1348
  console.log(` Updated: ${entry.updatedAt.toLocaleString()}`);
1147
1349
  console.log(` Title: ${entry.title}`);
1148
- console.log(` Content: ${entry.content.substring(0, 100)}${entry.content.length > 100 ? "..." : ""}
1149
- `);
1350
+ console.log(` Content: ${entry.content.substring(0, 100)}${entry.content.length > 100 ? "..." : ""}`);
1351
+ console.log("");
1150
1352
  if (versions.length > 0) {
1151
1353
  console.log("Previous versions:");
1152
1354
  for (const version of versions) {
@@ -1164,11 +1366,8 @@ v${version.version}:`);
1164
1366
  } finally {
1165
1367
  await db.close();
1166
1368
  }
1167
- } catch (error2) {
1168
- console.error(
1169
- "\u274C History failed:",
1170
- error2 instanceof Error ? error2.message : error2
1171
- );
1369
+ } catch (err) {
1370
+ error(`History failed: ${err instanceof Error ? err.message : err}`);
1172
1371
  process.exit(1);
1173
1372
  }
1174
1373
  },
@@ -1182,16 +1381,13 @@ v${version.version}:`);
1182
1381
  await db.initialize();
1183
1382
  try {
1184
1383
  const restored = await db.restoreVersion(entryId, options.version);
1185
- console.log(`\u2705 Restored '${restored.title}' to version ${options.version}`);
1384
+ success(`Restored '${restored.title}' to version ${options.version}`);
1186
1385
  console.log(` Current version is now: v${restored.version}`);
1187
1386
  } finally {
1188
1387
  await db.close();
1189
1388
  }
1190
- } catch (error2) {
1191
- console.error(
1192
- "\u274C Restore failed:",
1193
- error2 instanceof Error ? error2.message : error2
1194
- );
1389
+ } catch (err) {
1390
+ error(`Restore failed: ${err instanceof Error ? err.message : err}`);
1195
1391
  process.exit(1);
1196
1392
  }
1197
1393
  }
@@ -1199,7 +1395,36 @@ v${version.version}:`);
1199
1395
 
1200
1396
  // src/commands/load.ts
1201
1397
  import { SQLiteDatabase as SQLiteDatabase6 } from "@mycontxt/adapters/sqlite";
1398
+ import { SupabaseDatabase as SupabaseDatabase3 } from "@mycontxt/adapters/supabase";
1202
1399
  import { buildContextPayload, buildContextSummary } from "@mycontxt/core";
1400
+ import chalk4 from "chalk";
1401
+ var MODEL_COSTS_PER_1K = {
1402
+ "claude-sonnet": 3e-3,
1403
+ "claude-haiku": 25e-5,
1404
+ "claude-opus": 0.015,
1405
+ "gpt-4o": 25e-4,
1406
+ "gpt-4": 0.03,
1407
+ "gpt-3.5": 5e-4
1408
+ };
1409
+ var DEFAULT_MODEL = "claude-sonnet";
1410
+ function formatCost(tokens, model) {
1411
+ const costPerK = MODEL_COSTS_PER_1K[model] ?? MODEL_COSTS_PER_1K[DEFAULT_MODEL];
1412
+ const cost = tokens / 1e3 * costPerK;
1413
+ return `$${cost.toFixed(4)}`;
1414
+ }
1415
+ async function generateQueryEmbedding(text, apiKey) {
1416
+ try {
1417
+ const res = await fetch("https://api.openai.com/v1/embeddings", {
1418
+ method: "POST",
1419
+ headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
1420
+ body: JSON.stringify({ model: "text-embedding-3-small", input: text })
1421
+ });
1422
+ const data = await res.json();
1423
+ return data.data?.[0]?.embedding ?? null;
1424
+ } catch {
1425
+ return null;
1426
+ }
1427
+ }
1203
1428
  async function loadCommand(options) {
1204
1429
  try {
1205
1430
  const dbPath = getDbPath();
@@ -1209,7 +1434,7 @@ async function loadCommand(options) {
1209
1434
  const cwd = process.cwd();
1210
1435
  const project = await db.getProjectByPath(cwd);
1211
1436
  if (!project) {
1212
- console.error("\u274C No Contxt project found. Run `contxt init` first.");
1437
+ error("No Contxt project found. Run `contxt init` first.");
1213
1438
  process.exit(1);
1214
1439
  }
1215
1440
  const branch2 = await db.getActiveBranch(project.id);
@@ -1224,11 +1449,11 @@ async function loadCommand(options) {
1224
1449
  }
1225
1450
  if (options.summary) {
1226
1451
  const summary = buildContextSummary(entries);
1227
- console.log(`\u{1F4CA} Context Summary
1228
- `);
1452
+ console.log(section("Context Summary"));
1453
+ console.log("");
1229
1454
  console.log(`Total entries: ${summary.totalEntries}`);
1230
- console.log(`Branch: ${branch2}
1231
- `);
1455
+ console.log(`Branch: ${branch2}`);
1456
+ console.log("");
1232
1457
  console.log("By type:");
1233
1458
  for (const [type, count] of Object.entries(summary.byType)) {
1234
1459
  console.log(` ${type}: ${count}`);
@@ -1256,7 +1481,30 @@ Date range: ${summary.oldestEntry.toLocaleDateString()} - ${summary.newestEntry.
1256
1481
  } else if (options.all) {
1257
1482
  mode = "all";
1258
1483
  }
1259
- const result = buildContextPayload(entries, {
1484
+ const totalEntryCount = entries.length;
1485
+ let resolvedEntries = entries;
1486
+ if (mode === "task" && options.task) {
1487
+ const apiKey = process.env.OPENAI_API_KEY;
1488
+ if (apiKey) {
1489
+ try {
1490
+ const config = getSupabaseConfig();
1491
+ const remoteDb = new SupabaseDatabase3(config);
1492
+ const embedding = await generateQueryEmbedding(options.task, apiKey);
1493
+ if (embedding) {
1494
+ const semanticResults = await remoteDb.semanticSearch(project.id, embedding, {
1495
+ branch: branch2,
1496
+ limit: 20,
1497
+ minSimilarity: 0.65
1498
+ }).catch(() => []);
1499
+ if (semanticResults.length > 0) {
1500
+ resolvedEntries = semanticResults;
1501
+ }
1502
+ }
1503
+ } catch {
1504
+ }
1505
+ }
1506
+ }
1507
+ const result = buildContextPayload(resolvedEntries, {
1260
1508
  projectId: project.id,
1261
1509
  type: mode,
1262
1510
  taskDescription: options.task,
@@ -1265,26 +1513,36 @@ Date range: ${summary.oldestEntry.toLocaleDateString()} - ${summary.newestEntry.
1265
1513
  includeTypes: options.type ? [options.type] : void 0
1266
1514
  });
1267
1515
  console.log(result.context);
1268
- console.error(
1269
- `
1270
- \u{1F4E6} Context: ${result.entriesIncluded} entries, ${result.tokensUsed}/${result.budget} tokens`
1271
- );
1516
+ const model = process.env.CONTXT_MODEL ?? DEFAULT_MODEL;
1517
+ const totalFiltered = totalEntryCount - result.entriesIncluded;
1518
+ if (totalFiltered > 0 && result.tokensSaved > 0) {
1519
+ const filteredCost = formatCost(result.tokensUsed, model);
1520
+ const fullCost = formatCost(result.tokensUsed + result.tokensSaved, model);
1521
+ console.error(
1522
+ chalk4.dim(
1523
+ `
1524
+ \u2192 ${result.entriesIncluded} entries loaded \xB7 ${result.tokensUsed} tokens \xB7 saved ${result.tokensSaved.toLocaleString()} tokens (${totalFiltered} filtered) \xB7 ~${filteredCost} vs ${fullCost} full load`
1525
+ )
1526
+ );
1527
+ } else {
1528
+ console.error(
1529
+ chalk4.dim(`
1530
+ \u2192 ${result.entriesIncluded} entries loaded \xB7 ${result.tokensUsed}/${result.budget} tokens`)
1531
+ );
1532
+ }
1272
1533
  } finally {
1273
1534
  await db.close();
1274
1535
  }
1275
- } catch (error2) {
1276
- console.error(
1277
- "\u274C Load failed:",
1278
- error2 instanceof Error ? error2.message : error2
1279
- );
1536
+ } catch (err) {
1537
+ error(`Load failed: ${err instanceof Error ? err.message : err}`);
1280
1538
  process.exit(1);
1281
1539
  }
1282
1540
  }
1283
1541
 
1284
1542
  // src/commands/scan.ts
1285
- import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
1286
- import { join as join3, relative } from "path";
1287
- import chalk3 from "chalk";
1543
+ import { readFileSync as readFileSync6, existsSync as existsSync5 } from "fs";
1544
+ import { join as join5, relative } from "path";
1545
+ import chalk5 from "chalk";
1288
1546
  import ora from "ora";
1289
1547
  import { parseFile, scanCommentToEntry } from "@mycontxt/core";
1290
1548
  import { glob } from "glob";
@@ -1319,9 +1577,9 @@ async function scanCommand(options = {}) {
1319
1577
  "**/*.lock",
1320
1578
  ".contxt/**"
1321
1579
  ];
1322
- const contxtIgnorePath = join3(process.cwd(), ".contxtignore");
1323
- if (existsSync4(contxtIgnorePath)) {
1324
- const contxtIgnore = readFileSync4(contxtIgnorePath, "utf-8").split("\n").filter((line) => line.trim() && !line.startsWith("#"));
1580
+ const contxtIgnorePath = join5(process.cwd(), ".contxtignore");
1581
+ if (existsSync5(contxtIgnorePath)) {
1582
+ const contxtIgnore = readFileSync6(contxtIgnorePath, "utf-8").split("\n").filter((line) => line.trim() && !line.startsWith("#"));
1325
1583
  ignore.push(...contxtIgnore);
1326
1584
  }
1327
1585
  const searchPath = options.path || process.cwd();
@@ -1334,13 +1592,13 @@ async function scanCommand(options = {}) {
1334
1592
  spinner.text = `Scanning ${files.length} files...`;
1335
1593
  const allComments = [];
1336
1594
  for (const file of files) {
1337
- const content = readFileSync4(file, "utf-8");
1595
+ const content = readFileSync6(file, "utf-8");
1338
1596
  const comments = parseFile(content, relative(process.cwd(), file));
1339
1597
  allComments.push(...comments);
1340
1598
  }
1341
1599
  spinner.succeed(`Found ${allComments.length} tagged comments across ${files.length} files`);
1342
1600
  if (allComments.length === 0) {
1343
- console.log(chalk3.gray("\nNo tagged comments found. Try adding @decision, @pattern, or @context tags to your code."));
1601
+ console.log(chalk5.gray("\nNo tagged comments found. Try adding @decision, @pattern, or @context tags to your code."));
1344
1602
  return;
1345
1603
  }
1346
1604
  const existingEntries = await db.listEntries({
@@ -1370,50 +1628,50 @@ async function scanCommand(options = {}) {
1370
1628
  const staleEntries = Array.from(existingHashes.values());
1371
1629
  console.log("");
1372
1630
  if (newComments.length > 0) {
1373
- console.log(chalk3.bold("NEW"));
1631
+ console.log(chalk5.bold("NEW"));
1374
1632
  for (const comment of newComments) {
1375
1633
  const icon = getTypeIcon(comment.tag);
1376
1634
  console.log(
1377
- ` ${chalk3.green("+")} ${icon} ${chalk3.bold(comment.title.substring(0, 50))} ${chalk3.gray(comment.file + ":" + comment.line)}`
1635
+ ` ${chalk5.green("+")} ${icon} ${chalk5.bold(comment.title.substring(0, 50))} ${chalk5.gray(comment.file + ":" + comment.line)}`
1378
1636
  );
1379
1637
  }
1380
1638
  console.log("");
1381
1639
  }
1382
1640
  if (updatedComments.length > 0) {
1383
- console.log(chalk3.bold("UPDATED"));
1641
+ console.log(chalk5.bold("UPDATED"));
1384
1642
  for (const comment of updatedComments) {
1385
1643
  const icon = getTypeIcon(comment.tag);
1386
1644
  console.log(
1387
- ` ${chalk3.yellow("~")} ${icon} ${chalk3.bold(comment.title.substring(0, 50))} ${chalk3.gray(comment.file + ":" + comment.line)}`
1645
+ ` ${chalk5.yellow("~")} ${icon} ${chalk5.bold(comment.title.substring(0, 50))} ${chalk5.gray(comment.file + ":" + comment.line)}`
1388
1646
  );
1389
1647
  }
1390
1648
  console.log("");
1391
1649
  }
1392
1650
  if (unchangedComments.length > 0) {
1393
- console.log(chalk3.bold("UNCHANGED"));
1651
+ console.log(chalk5.bold("UNCHANGED"));
1394
1652
  for (const comment of unchangedComments.slice(0, 3)) {
1395
1653
  const icon = getTypeIcon(comment.tag);
1396
1654
  console.log(
1397
- ` ${chalk3.gray("\xB7")} ${icon} ${chalk3.gray(comment.title.substring(0, 50))} ${chalk3.gray(comment.file + ":" + comment.line)}`
1655
+ ` ${chalk5.gray("\xB7")} ${icon} ${chalk5.gray(comment.title.substring(0, 50))} ${chalk5.gray(comment.file + ":" + comment.line)}`
1398
1656
  );
1399
1657
  }
1400
1658
  if (unchangedComments.length > 3) {
1401
- console.log(chalk3.gray(` ... and ${unchangedComments.length - 3} more`));
1659
+ console.log(chalk5.gray(` ... and ${unchangedComments.length - 3} more`));
1402
1660
  }
1403
1661
  console.log("");
1404
1662
  }
1405
1663
  if (staleEntries.length > 0) {
1406
- console.log(chalk3.bold("STALE (source comment removed)"));
1664
+ console.log(chalk5.bold("STALE (source comment removed)"));
1407
1665
  for (const entry of staleEntries) {
1408
1666
  const icon = getTypeIcon(entry.type);
1409
1667
  console.log(
1410
- ` ${chalk3.red("?")} ${icon} ${chalk3.gray(entry.title.substring(0, 50))} ${chalk3.gray("was: " + entry.metadata.file + ":" + entry.metadata.line)}`
1668
+ ` ${chalk5.red("?")} ${icon} ${chalk5.gray(entry.title.substring(0, 50))} ${chalk5.gray("was: " + entry.metadata.file + ":" + entry.metadata.line)}`
1411
1669
  );
1412
1670
  }
1413
1671
  console.log("");
1414
1672
  }
1415
1673
  if (options.dryRun) {
1416
- console.log(chalk3.yellow("Dry run - no changes saved."));
1674
+ console.log(chalk5.yellow("Dry run - no changes saved."));
1417
1675
  return;
1418
1676
  }
1419
1677
  const toSave = [...newComments, ...updatedComments];
@@ -1447,30 +1705,30 @@ async function scanCommand(options = {}) {
1447
1705
  for (const entry of staleEntries) {
1448
1706
  await db.updateEntry(entry.id, { status: "stale" });
1449
1707
  }
1450
- console.log(chalk3.gray(`Marked ${staleEntries.length} entries as stale.`));
1708
+ console.log(chalk5.gray(`Marked ${staleEntries.length} entries as stale.`));
1451
1709
  }
1452
1710
  if (!options.autoConfirm && toSave.length > 0) {
1453
1711
  console.log("");
1454
- console.log(chalk3.cyan(`Run ${chalk3.bold("contxt review")} to confirm drafts.`));
1712
+ console.log(chalk5.cyan(`Run ${chalk5.bold("contxt review")} to confirm drafts.`));
1455
1713
  }
1456
1714
  await db.close();
1457
1715
  } catch (error2) {
1458
1716
  spinner.fail("Scan failed");
1459
- console.error(chalk3.red(error2 instanceof Error ? error2.message : String(error2)));
1717
+ console.error(chalk5.red(error2 instanceof Error ? error2.message : String(error2)));
1460
1718
  process.exit(1);
1461
1719
  }
1462
1720
  }
1463
1721
  function getTypeIcon(type) {
1464
1722
  const icons = {
1465
- decision: chalk3.blue("DECISION"),
1466
- pattern: chalk3.magenta("PATTERN"),
1467
- context: chalk3.green("CONTEXT")
1723
+ decision: chalk5.blue("DECISION"),
1724
+ pattern: chalk5.magenta("PATTERN"),
1725
+ context: chalk5.green("CONTEXT")
1468
1726
  };
1469
1727
  return icons[type] || type.toUpperCase();
1470
1728
  }
1471
1729
 
1472
1730
  // src/commands/review.ts
1473
- import chalk4 from "chalk";
1731
+ import chalk6 from "chalk";
1474
1732
  import inquirer from "inquirer";
1475
1733
  import ora2 from "ora";
1476
1734
  async function reviewCommand(options = {}) {
@@ -1478,7 +1736,7 @@ async function reviewCommand(options = {}) {
1478
1736
  const db = await getProjectDb();
1479
1737
  const project = await db.getProjectByPath(process.cwd());
1480
1738
  if (!project) {
1481
- console.log(chalk4.red("Not a Contxt project. Run `contxt init` first."));
1739
+ console.log(chalk6.red("Not a Contxt project. Run `contxt init` first."));
1482
1740
  return;
1483
1741
  }
1484
1742
  let drafts = await db.listEntries({
@@ -1493,14 +1751,14 @@ async function reviewCommand(options = {}) {
1493
1751
  });
1494
1752
  }
1495
1753
  if (drafts.length === 0) {
1496
- console.log(chalk4.green("\u2713 No drafts pending review"));
1754
+ console.log(chalk6.green("\u2713 No drafts pending review"));
1497
1755
  return;
1498
1756
  }
1499
1757
  if (options.count) {
1500
1758
  console.log(`${drafts.length} drafts pending review`);
1501
1759
  return;
1502
1760
  }
1503
- console.log(chalk4.bold(`
1761
+ console.log(chalk6.bold(`
1504
1762
  ${drafts.length} drafts pending review
1505
1763
  `));
1506
1764
  if (options.confirmAll) {
@@ -1535,7 +1793,7 @@ ${drafts.length} drafts pending review
1535
1793
  let discarded = 0;
1536
1794
  let skipped = 0;
1537
1795
  for (const draft of drafts) {
1538
- console.log(chalk4.gray("\u2500".repeat(50)));
1796
+ console.log(chalk6.gray("\u2500".repeat(50)));
1539
1797
  console.log("");
1540
1798
  displayDraft(draft);
1541
1799
  console.log("");
@@ -1554,7 +1812,7 @@ ${drafts.length} drafts pending review
1554
1812
  ]);
1555
1813
  if (action === "confirm") {
1556
1814
  await db.updateEntry(draft.id, { status: "active" });
1557
- console.log(chalk4.green("\u2713 Confirmed"));
1815
+ console.log(chalk6.green("\u2713 Confirmed"));
1558
1816
  confirmed++;
1559
1817
  } else if (action === "edit") {
1560
1818
  const edited = await editDraft(draft);
@@ -1564,28 +1822,28 @@ ${drafts.length} drafts pending review
1564
1822
  metadata: edited.metadata,
1565
1823
  status: "active"
1566
1824
  });
1567
- console.log(chalk4.green("\u2713 Edited and confirmed"));
1825
+ console.log(chalk6.green("\u2713 Edited and confirmed"));
1568
1826
  confirmed++;
1569
1827
  } else if (action === "discard") {
1570
1828
  await db.deleteEntry(draft.id);
1571
- console.log(chalk4.red("\u2717 Discarded"));
1829
+ console.log(chalk6.red("\u2717 Discarded"));
1572
1830
  discarded++;
1573
1831
  } else {
1574
- console.log(chalk4.gray("\u25CB Skipped"));
1832
+ console.log(chalk6.gray("\u25CB Skipped"));
1575
1833
  skipped++;
1576
1834
  }
1577
1835
  console.log("");
1578
1836
  }
1579
- console.log(chalk4.gray("\u2500".repeat(50)));
1837
+ console.log(chalk6.gray("\u2500".repeat(50)));
1580
1838
  console.log("");
1581
- console.log(chalk4.bold("Review complete"));
1582
- console.log(` ${chalk4.green(confirmed)} confirmed`);
1583
- console.log(` ${chalk4.red(discarded)} discarded`);
1584
- console.log(` ${chalk4.gray(skipped)} skipped`);
1839
+ console.log(chalk6.bold("Review complete"));
1840
+ console.log(` ${chalk6.green(confirmed)} confirmed`);
1841
+ console.log(` ${chalk6.red(discarded)} discarded`);
1842
+ console.log(` ${chalk6.gray(skipped)} skipped`);
1585
1843
  console.log("");
1586
1844
  await db.close();
1587
1845
  } catch (error2) {
1588
- console.error(chalk4.red(error2 instanceof Error ? error2.message : String(error2)));
1846
+ console.error(chalk6.red(error2 instanceof Error ? error2.message : String(error2)));
1589
1847
  process.exit(1);
1590
1848
  }
1591
1849
  }
@@ -1594,28 +1852,28 @@ function displayDraft(draft) {
1594
1852
  const source = draft.metadata.source || "unknown";
1595
1853
  const file = draft.metadata.file ? ` \xB7 ${draft.metadata.file}:${draft.metadata.line}` : "";
1596
1854
  const timeAgo = getTimeAgo(draft.createdAt);
1597
- console.log(` ${icon} ${chalk4.bold(draft.title)}`);
1598
- console.log(` ${chalk4.gray(`Source: ${source}${file} \xB7 ${timeAgo}`)}`);
1855
+ console.log(` ${icon} ${chalk6.bold(draft.title)}`);
1856
+ console.log(` ${chalk6.gray(`Source: ${source}${file} \xB7 ${timeAgo}`)}`);
1599
1857
  const contentPreview = draft.content.split("\n")[0].substring(0, 80);
1600
1858
  if (contentPreview) {
1601
- console.log(` ${chalk4.gray(contentPreview)}${draft.content.length > 80 ? "..." : ""}`);
1859
+ console.log(` ${chalk6.gray(contentPreview)}${draft.content.length > 80 ? "..." : ""}`);
1602
1860
  }
1603
1861
  if (draft.type === "decision") {
1604
1862
  if (draft.metadata.rationale) {
1605
- console.log(` ${chalk4.dim("Rationale:")} ${draft.metadata.rationale.substring(0, 60)}...`);
1863
+ console.log(` ${chalk6.dim("Rationale:")} ${draft.metadata.rationale.substring(0, 60)}...`);
1606
1864
  }
1607
1865
  if (draft.metadata.alternatives) {
1608
- console.log(` ${chalk4.dim("Alternatives:")} ${draft.metadata.alternatives}`);
1866
+ console.log(` ${chalk6.dim("Alternatives:")} ${draft.metadata.alternatives}`);
1609
1867
  }
1610
1868
  }
1611
1869
  if (draft.type === "pattern") {
1612
1870
  if (draft.metadata.when) {
1613
- console.log(` ${chalk4.dim("When:")} ${draft.metadata.when}`);
1871
+ console.log(` ${chalk6.dim("When:")} ${draft.metadata.when}`);
1614
1872
  }
1615
1873
  }
1616
1874
  }
1617
1875
  async function editDraft(draft) {
1618
- console.log(chalk4.cyan("\nEdit mode (press Enter to keep current value):\n"));
1876
+ console.log(chalk6.cyan("\nEdit mode (press Enter to keep current value):\n"));
1619
1877
  const { title, content } = await inquirer.prompt([
1620
1878
  {
1621
1879
  type: "input",
@@ -1638,11 +1896,11 @@ async function editDraft(draft) {
1638
1896
  }
1639
1897
  function getTypeIcon2(type) {
1640
1898
  const icons = {
1641
- decision: chalk4.blue("DECISION"),
1642
- pattern: chalk4.magenta("PATTERN"),
1643
- context: chalk4.green("CONTEXT"),
1644
- document: chalk4.yellow("DOCUMENT"),
1645
- session: chalk4.cyan("SESSION")
1899
+ decision: chalk6.blue("DECISION"),
1900
+ pattern: chalk6.magenta("PATTERN"),
1901
+ context: chalk6.green("CONTEXT"),
1902
+ document: chalk6.yellow("DOCUMENT"),
1903
+ session: chalk6.cyan("SESSION")
1646
1904
  };
1647
1905
  return icons[type] || type.toUpperCase();
1648
1906
  }
@@ -1655,9 +1913,9 @@ function getTimeAgo(date) {
1655
1913
  }
1656
1914
 
1657
1915
  // src/commands/rules.ts
1658
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
1659
- import { join as join4 } from "path";
1660
- import chalk5 from "chalk";
1916
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync6 } from "fs";
1917
+ import { join as join6 } from "path";
1918
+ import chalk7 from "chalk";
1661
1919
  import ora3 from "ora";
1662
1920
  import { parseRulesFile, generateRulesFile } from "@mycontxt/core";
1663
1921
  async function syncCommand2(options = {}) {
@@ -1669,12 +1927,12 @@ async function syncCommand2(options = {}) {
1669
1927
  spinner.fail("Not a Contxt project. Run `contxt init` first.");
1670
1928
  return;
1671
1929
  }
1672
- const rulesPath = join4(process.cwd(), ".contxt", "rules.md");
1673
- if (!existsSync5(rulesPath)) {
1930
+ const rulesPath = join6(process.cwd(), ".contxt", "rules.md");
1931
+ if (!existsSync6(rulesPath)) {
1674
1932
  spinner.fail("No rules.md file found. Run `contxt rules generate` to create one.");
1675
1933
  return;
1676
1934
  }
1677
- const content = readFileSync5(rulesPath, "utf-8");
1935
+ const content = readFileSync7(rulesPath, "utf-8");
1678
1936
  const parsed = parseRulesFile(content);
1679
1937
  spinner.text = "Processing entries...";
1680
1938
  let added = 0;
@@ -1801,7 +2059,7 @@ async function syncCommand2(options = {}) {
1801
2059
  await db.close();
1802
2060
  } catch (error2) {
1803
2061
  spinner.fail("Sync failed");
1804
- console.error(chalk5.red(error2 instanceof Error ? error2.message : String(error2)));
2062
+ console.error(chalk7.red(error2 instanceof Error ? error2.message : String(error2)));
1805
2063
  process.exit(1);
1806
2064
  }
1807
2065
  }
@@ -1814,8 +2072,8 @@ async function generateCommand(options = {}) {
1814
2072
  spinner.fail("Not a Contxt project. Run `contxt init` first.");
1815
2073
  return;
1816
2074
  }
1817
- const rulesPath = join4(process.cwd(), ".contxt", "rules.md");
1818
- if (existsSync5(rulesPath) && !options.force) {
2075
+ const rulesPath = join6(process.cwd(), ".contxt", "rules.md");
2076
+ if (existsSync6(rulesPath) && !options.force) {
1819
2077
  spinner.fail("rules.md already exists. Use --force to overwrite.");
1820
2078
  return;
1821
2079
  }
@@ -1852,15 +2110,15 @@ async function generateCommand(options = {}) {
1852
2110
  if (options.dryRun) {
1853
2111
  spinner.succeed("Generated rules.md (dry run):");
1854
2112
  console.log("");
1855
- console.log(chalk5.gray(rulesContent));
2113
+ console.log(chalk7.gray(rulesContent));
1856
2114
  } else {
1857
- writeFileSync3(rulesPath, rulesContent, "utf-8");
2115
+ writeFileSync4(rulesPath, rulesContent, "utf-8");
1858
2116
  spinner.succeed(`Generated ${rulesPath}`);
1859
2117
  }
1860
2118
  await db.close();
1861
2119
  } catch (error2) {
1862
2120
  spinner.fail("Generate failed");
1863
- console.error(chalk5.red(error2 instanceof Error ? error2.message : String(error2)));
2121
+ console.error(chalk7.red(error2 instanceof Error ? error2.message : String(error2)));
1864
2122
  process.exit(1);
1865
2123
  }
1866
2124
  }
@@ -1873,12 +2131,12 @@ async function diffCommand() {
1873
2131
  spinner.fail("Not a Contxt project. Run `contxt init` first.");
1874
2132
  return;
1875
2133
  }
1876
- const rulesPath = join4(process.cwd(), ".contxt", "rules.md");
1877
- if (!existsSync5(rulesPath)) {
2134
+ const rulesPath = join6(process.cwd(), ".contxt", "rules.md");
2135
+ if (!existsSync6(rulesPath)) {
1878
2136
  spinner.fail("No rules.md file found.");
1879
2137
  return;
1880
2138
  }
1881
- const content = readFileSync5(rulesPath, "utf-8");
2139
+ const content = readFileSync7(rulesPath, "utf-8");
1882
2140
  const parsed = parseRulesFile(content);
1883
2141
  const branch2 = await db.getActiveBranch(project.id);
1884
2142
  const allEntries = await db.listEntries({
@@ -1895,67 +2153,67 @@ async function diffCommand() {
1895
2153
  for (const decision2 of parsed.decisions) {
1896
2154
  const existing = existingByTitle.get(decision2.title);
1897
2155
  if (!existing) {
1898
- toAdd.push(`${chalk5.blue("DECISION")} ${decision2.title}`);
2156
+ toAdd.push(`${chalk7.blue("DECISION")} ${decision2.title}`);
1899
2157
  } else if (existing.content !== decision2.content) {
1900
- toUpdate.push(`${chalk5.blue("DECISION")} ${decision2.title}`);
2158
+ toUpdate.push(`${chalk7.blue("DECISION")} ${decision2.title}`);
1901
2159
  } else {
1902
- inSync.push(`${chalk5.blue("DECISION")} ${decision2.title}`);
2160
+ inSync.push(`${chalk7.blue("DECISION")} ${decision2.title}`);
1903
2161
  }
1904
2162
  }
1905
2163
  for (const pattern2 of parsed.patterns) {
1906
2164
  const existing = existingByTitle.get(pattern2.title);
1907
2165
  if (!existing) {
1908
- toAdd.push(`${chalk5.magenta("PATTERN")} ${pattern2.title}`);
2166
+ toAdd.push(`${chalk7.magenta("PATTERN")} ${pattern2.title}`);
1909
2167
  } else if (existing.content !== pattern2.content) {
1910
- toUpdate.push(`${chalk5.magenta("PATTERN")} ${pattern2.title}`);
2168
+ toUpdate.push(`${chalk7.magenta("PATTERN")} ${pattern2.title}`);
1911
2169
  } else {
1912
- inSync.push(`${chalk5.magenta("PATTERN")} ${pattern2.title}`);
2170
+ inSync.push(`${chalk7.magenta("PATTERN")} ${pattern2.title}`);
1913
2171
  }
1914
2172
  }
1915
2173
  for (const doc2 of parsed.documents) {
1916
2174
  const existing = existingByTitle.get(doc2.title);
1917
2175
  if (!existing) {
1918
- toAdd.push(`${chalk5.cyan("DOCUMENT")} ${doc2.title}`);
2176
+ toAdd.push(`${chalk7.cyan("DOCUMENT")} ${doc2.title}`);
1919
2177
  } else if (existing.content !== doc2.content) {
1920
- toUpdate.push(`${chalk5.cyan("DOCUMENT")} ${doc2.title}`);
2178
+ toUpdate.push(`${chalk7.cyan("DOCUMENT")} ${doc2.title}`);
1921
2179
  } else {
1922
- inSync.push(`${chalk5.cyan("DOCUMENT")} ${doc2.title}`);
2180
+ inSync.push(`${chalk7.cyan("DOCUMENT")} ${doc2.title}`);
1923
2181
  }
1924
2182
  }
1925
2183
  console.log("");
1926
2184
  if (toAdd.length > 0) {
1927
- console.log(chalk5.bold("TO ADD (in rules.md, not in memory):"));
2185
+ console.log(chalk7.bold("TO ADD (in rules.md, not in memory):"));
1928
2186
  for (const item of toAdd) {
1929
- console.log(` ${chalk5.green("+")} ${item}`);
2187
+ console.log(` ${chalk7.green("+")} ${item}`);
1930
2188
  }
1931
2189
  console.log("");
1932
2190
  }
1933
2191
  if (toUpdate.length > 0) {
1934
- console.log(chalk5.bold("TO UPDATE (content differs):"));
2192
+ console.log(chalk7.bold("TO UPDATE (content differs):"));
1935
2193
  for (const item of toUpdate) {
1936
- console.log(` ${chalk5.yellow("~")} ${item}`);
2194
+ console.log(` ${chalk7.yellow("~")} ${item}`);
1937
2195
  }
1938
2196
  console.log("");
1939
2197
  }
1940
2198
  if (inSync.length > 0) {
1941
- console.log(chalk5.bold("IN SYNC:"));
2199
+ console.log(chalk7.bold("IN SYNC:"));
1942
2200
  for (const item of inSync.slice(0, 5)) {
1943
- console.log(` ${chalk5.gray("\xB7")} ${chalk5.gray(item)}`);
2201
+ console.log(` ${chalk7.gray("\xB7")} ${chalk7.gray(item)}`);
1944
2202
  }
1945
2203
  if (inSync.length > 5) {
1946
- console.log(chalk5.gray(` ... and ${inSync.length - 5} more`));
2204
+ console.log(chalk7.gray(` ... and ${inSync.length - 5} more`));
1947
2205
  }
1948
2206
  console.log("");
1949
2207
  }
1950
2208
  if (toAdd.length > 0 || toUpdate.length > 0) {
1951
- console.log(chalk5.cyan(`Run ${chalk5.bold("contxt rules sync")} to apply changes.`));
2209
+ console.log(chalk7.cyan(`Run ${chalk7.bold("contxt rules sync")} to apply changes.`));
1952
2210
  } else {
1953
- console.log(chalk5.green("\u2713 Everything in sync!"));
2211
+ console.log(chalk7.green("\u2713 Everything in sync!"));
1954
2212
  }
1955
2213
  await db.close();
1956
2214
  } catch (error2) {
1957
2215
  spinner.fail("Diff failed");
1958
- console.error(chalk5.red(error2 instanceof Error ? error2.message : String(error2)));
2216
+ console.error(chalk7.red(error2 instanceof Error ? error2.message : String(error2)));
1959
2217
  process.exit(1);
1960
2218
  }
1961
2219
  }
@@ -1966,9 +2224,9 @@ var rulesCommand = {
1966
2224
  };
1967
2225
 
1968
2226
  // src/commands/capture.ts
1969
- import { readFileSync as readFileSync6, existsSync as existsSync6, readdirSync } from "fs";
1970
- import { join as join5, basename as basename2 } from "path";
1971
- import chalk6 from "chalk";
2227
+ import { readFileSync as readFileSync8, existsSync as existsSync7, readdirSync } from "fs";
2228
+ import { join as join7, basename as basename2 } from "path";
2229
+ import chalk8 from "chalk";
1972
2230
  import ora4 from "ora";
1973
2231
  async function captureCommand(options = {}) {
1974
2232
  const spinner = ora4("Scanning project files...").start();
@@ -2006,24 +2264,24 @@ async function captureCommand(options = {}) {
2006
2264
  }
2007
2265
  spinner.succeed(`Found ${entries.length} entries across ${sources.length} source(s)`);
2008
2266
  if (entries.length === 0) {
2009
- console.log(chalk6.gray("\\nNo entries found to import."));
2267
+ console.log(chalk8.gray("\\nNo entries found to import."));
2010
2268
  return;
2011
2269
  }
2012
2270
  console.log("");
2013
2271
  const grouped = groupBy(entries, "source");
2014
2272
  for (const [source, items] of Object.entries(grouped)) {
2015
- console.log(chalk6.bold(`${source.toUpperCase()} (${items.length})`));
2273
+ console.log(chalk8.bold(`${source.toUpperCase()} (${items.length})`));
2016
2274
  for (const item of items.slice(0, 3)) {
2017
2275
  const icon = getTypeIcon3(item.type);
2018
- console.log(` ${icon} ${chalk6.bold(item.title.substring(0, 60))}`);
2276
+ console.log(` ${icon} ${chalk8.bold(item.title.substring(0, 60))}`);
2019
2277
  }
2020
2278
  if (items.length > 3) {
2021
- console.log(chalk6.gray(` ... and ${items.length - 3} more`));
2279
+ console.log(chalk8.gray(` ... and ${items.length - 3} more`));
2022
2280
  }
2023
2281
  console.log("");
2024
2282
  }
2025
2283
  if (options.dryRun) {
2026
- console.log(chalk6.yellow("Dry run - no entries saved."));
2284
+ console.log(chalk8.yellow("Dry run - no entries saved."));
2027
2285
  return;
2028
2286
  }
2029
2287
  const saveSpinner = ora4("Saving entries...").start();
@@ -2047,20 +2305,20 @@ async function captureCommand(options = {}) {
2047
2305
  );
2048
2306
  if (!options.autoConfirm) {
2049
2307
  console.log("");
2050
- console.log(chalk6.cyan(`Run ${chalk6.bold("contxt review")} to confirm drafts.`));
2308
+ console.log(chalk8.cyan(`Run ${chalk8.bold("contxt review")} to confirm drafts.`));
2051
2309
  }
2052
2310
  await db.close();
2053
2311
  } catch (error2) {
2054
2312
  spinner.fail("Import failed");
2055
- console.error(chalk6.red(error2 instanceof Error ? error2.message : String(error2)));
2313
+ console.error(chalk8.red(error2 instanceof Error ? error2.message : String(error2)));
2056
2314
  process.exit(1);
2057
2315
  }
2058
2316
  }
2059
2317
  function importReadme() {
2060
- const readmePath = join5(process.cwd(), "README.md");
2061
- if (!existsSync6(readmePath)) return [];
2318
+ const readmePath = join7(process.cwd(), "README.md");
2319
+ if (!existsSync7(readmePath)) return [];
2062
2320
  try {
2063
- const content = readFileSync6(readmePath, "utf-8");
2321
+ const content = readFileSync8(readmePath, "utf-8");
2064
2322
  const lines = content.split("\\n");
2065
2323
  const entries = [];
2066
2324
  let currentSection = null;
@@ -2107,10 +2365,10 @@ function importReadme() {
2107
2365
  }
2108
2366
  function importCursor() {
2109
2367
  const entries = [];
2110
- const cursorrulesPath = join5(process.cwd(), ".cursorrules");
2111
- if (existsSync6(cursorrulesPath)) {
2368
+ const cursorrulesPath = join7(process.cwd(), ".cursorrules");
2369
+ if (existsSync7(cursorrulesPath)) {
2112
2370
  try {
2113
- const content = readFileSync6(cursorrulesPath, "utf-8").trim();
2371
+ const content = readFileSync8(cursorrulesPath, "utf-8").trim();
2114
2372
  if (content.length > 0) {
2115
2373
  entries.push({
2116
2374
  type: "document",
@@ -2123,10 +2381,10 @@ function importCursor() {
2123
2381
  } catch {
2124
2382
  }
2125
2383
  }
2126
- const cursorRulesPath = join5(process.cwd(), ".cursor", "rules");
2127
- if (existsSync6(cursorRulesPath)) {
2384
+ const cursorRulesPath = join7(process.cwd(), ".cursor", "rules");
2385
+ if (existsSync7(cursorRulesPath)) {
2128
2386
  try {
2129
- const content = readFileSync6(cursorRulesPath, "utf-8").trim();
2387
+ const content = readFileSync8(cursorRulesPath, "utf-8").trim();
2130
2388
  if (content.length > 0) {
2131
2389
  entries.push({
2132
2390
  type: "document",
@@ -2143,10 +2401,10 @@ function importCursor() {
2143
2401
  }
2144
2402
  function importClaude() {
2145
2403
  const entries = [];
2146
- const claudePath = join5(process.cwd(), ".claude", "CLAUDE.md");
2147
- if (existsSync6(claudePath)) {
2404
+ const claudePath = join7(process.cwd(), ".claude", "CLAUDE.md");
2405
+ if (existsSync7(claudePath)) {
2148
2406
  try {
2149
- const content = readFileSync6(claudePath, "utf-8");
2407
+ const content = readFileSync8(claudePath, "utf-8");
2150
2408
  const lines = content.split("\\n");
2151
2409
  let currentSection = null;
2152
2410
  let currentContent = [];
@@ -2193,17 +2451,17 @@ function importClaude() {
2193
2451
  function importADR() {
2194
2452
  const entries = [];
2195
2453
  const adrDirs = [
2196
- join5(process.cwd(), "docs", "adr"),
2197
- join5(process.cwd(), "docs", "architecture"),
2198
- join5(process.cwd(), "adr")
2454
+ join7(process.cwd(), "docs", "adr"),
2455
+ join7(process.cwd(), "docs", "architecture"),
2456
+ join7(process.cwd(), "adr")
2199
2457
  ];
2200
2458
  for (const adrDir of adrDirs) {
2201
- if (!existsSync6(adrDir)) continue;
2459
+ if (!existsSync7(adrDir)) continue;
2202
2460
  try {
2203
- const files = readdirSync(adrDir).filter((f) => f.endsWith(".md")).map((f) => join5(adrDir, f));
2461
+ const files = readdirSync(adrDir).filter((f) => f.endsWith(".md")).map((f) => join7(adrDir, f));
2204
2462
  for (const file of files) {
2205
2463
  try {
2206
- const content = readFileSync6(file, "utf-8");
2464
+ const content = readFileSync8(file, "utf-8");
2207
2465
  const title = extractADRTitle(content, basename2(file, ".md"));
2208
2466
  entries.push({
2209
2467
  type: "decision",
@@ -2265,10 +2523,10 @@ function importCommits(limit) {
2265
2523
  }
2266
2524
  function importPackageFiles() {
2267
2525
  const entries = [];
2268
- const packagePath = join5(process.cwd(), "package.json");
2269
- if (existsSync6(packagePath)) {
2526
+ const packagePath = join7(process.cwd(), "package.json");
2527
+ if (existsSync7(packagePath)) {
2270
2528
  try {
2271
- const pkg = JSON.parse(readFileSync6(packagePath, "utf-8"));
2529
+ const pkg = JSON.parse(readFileSync8(packagePath, "utf-8"));
2272
2530
  if (pkg.description) {
2273
2531
  entries.push({
2274
2532
  type: "document",
@@ -2294,10 +2552,10 @@ function importPackageFiles() {
2294
2552
  } catch {
2295
2553
  }
2296
2554
  }
2297
- const requirementsPath = join5(process.cwd(), "requirements.txt");
2298
- if (existsSync6(requirementsPath)) {
2555
+ const requirementsPath = join7(process.cwd(), "requirements.txt");
2556
+ if (existsSync7(requirementsPath)) {
2299
2557
  try {
2300
- const content = readFileSync6(requirementsPath, "utf-8");
2558
+ const content = readFileSync8(requirementsPath, "utf-8");
2301
2559
  const packages = content.split("\\n").filter((line) => line.trim() && !line.startsWith("#")).map((line) => line.split("==")[0].split(">=")[0].trim());
2302
2560
  if (packages.length > 0) {
2303
2561
  entries.push({
@@ -2311,10 +2569,10 @@ function importPackageFiles() {
2311
2569
  } catch {
2312
2570
  }
2313
2571
  }
2314
- const cargoPath = join5(process.cwd(), "Cargo.toml");
2315
- if (existsSync6(cargoPath)) {
2572
+ const cargoPath = join7(process.cwd(), "Cargo.toml");
2573
+ if (existsSync7(cargoPath)) {
2316
2574
  try {
2317
- const content = readFileSync6(cargoPath, "utf-8");
2575
+ const content = readFileSync8(cargoPath, "utf-8");
2318
2576
  const nameMatch = content.match(/name\\s*=\\s*"([^"]+)"/);
2319
2577
  const descMatch = content.match(/description\\s*=\\s*"([^"]+)"/);
2320
2578
  if (descMatch) {
@@ -2341,19 +2599,19 @@ function groupBy(arr, key) {
2341
2599
  }
2342
2600
  function getTypeIcon3(type) {
2343
2601
  const icons = {
2344
- decision: chalk6.blue("DECISION"),
2345
- pattern: chalk6.magenta("PATTERN"),
2346
- context: chalk6.green("CONTEXT"),
2347
- document: chalk6.cyan("DOCUMENT"),
2348
- session: chalk6.yellow("SESSION")
2602
+ decision: chalk8.blue("DECISION"),
2603
+ pattern: chalk8.magenta("PATTERN"),
2604
+ context: chalk8.green("CONTEXT"),
2605
+ document: chalk8.cyan("DOCUMENT"),
2606
+ session: chalk8.yellow("SESSION")
2349
2607
  };
2350
2608
  return icons[type] || type.toUpperCase();
2351
2609
  }
2352
2610
 
2353
2611
  // src/commands/hook.ts
2354
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync8, chmodSync, mkdirSync as mkdirSync3 } from "fs";
2355
- import { join as join7 } from "path";
2356
- import chalk7 from "chalk";
2612
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync9, chmodSync as chmodSync2, mkdirSync as mkdirSync3 } from "fs";
2613
+ import { join as join9 } from "path";
2614
+ import chalk9 from "chalk";
2357
2615
 
2358
2616
  // src/hooks/post-commit.ts
2359
2617
  import { execSync } from "child_process";
@@ -2441,8 +2699,8 @@ function stripConventionalPrefix(msg) {
2441
2699
 
2442
2700
  // src/hooks/pre-push.ts
2443
2701
  import { execSync as execSync2 } from "child_process";
2444
- import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
2445
- import { join as join6 } from "path";
2702
+ import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
2703
+ import { join as join8 } from "path";
2446
2704
  async function runPrePush() {
2447
2705
  try {
2448
2706
  const db = await getProjectDb();
@@ -2494,10 +2752,10 @@ async function runPrePush() {
2494
2752
  `contxt: session updated \u2014 ${commitCount} commit${commitCount !== 1 ? "s" : ""}, ${changedFiles.length} files changed
2495
2753
  `
2496
2754
  );
2497
- const configPath = join6(process.cwd(), ".contxt", "config.json");
2498
- if (existsSync7(configPath)) {
2755
+ const configPath = join8(process.cwd(), ".contxt", "config.json");
2756
+ if (existsSync8(configPath)) {
2499
2757
  try {
2500
- const config = JSON.parse(readFileSync7(configPath, "utf-8"));
2758
+ const config = JSON.parse(readFileSync9(configPath, "utf-8"));
2501
2759
  if (config.hooks?.auto_push_on_push) {
2502
2760
  process.stdout.write("contxt: syncing to cloud...\n");
2503
2761
  execSync2("contxt push --quiet 2>/dev/null", {
@@ -2545,7 +2803,7 @@ async function runPostCheckout() {
2545
2803
  }
2546
2804
 
2547
2805
  // src/hooks/prepare-commit-msg.ts
2548
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
2806
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
2549
2807
  async function runPrepareCommitMsg() {
2550
2808
  try {
2551
2809
  const commitMsgFile = process.argv[4];
@@ -2586,45 +2844,45 @@ async function runPrepareCommitMsg() {
2586
2844
  lines.push(`# ${draftCount} drafts pending \u2014 run \`contxt review\``);
2587
2845
  }
2588
2846
  lines.push("#");
2589
- const existing = readFileSync8(commitMsgFile, "utf-8");
2590
- writeFileSync4(commitMsgFile, existing + lines.join("\n") + "\n", "utf-8");
2847
+ const existing = readFileSync10(commitMsgFile, "utf-8");
2848
+ writeFileSync5(commitMsgFile, existing + lines.join("\n") + "\n", "utf-8");
2591
2849
  await db.close();
2592
2850
  } catch {
2593
2851
  }
2594
2852
  }
2595
2853
 
2596
2854
  // src/commands/hook.ts
2597
- var CONTXT_BLOCK_START = "# --- contxt hook start ---";
2598
- var CONTXT_BLOCK_END = "# --- contxt hook end ---";
2599
- var ALL_HOOKS = ["post-commit", "pre-push", "post-checkout", "prepare-commit-msg"];
2855
+ var CONTXT_BLOCK_START2 = "# --- contxt hook start ---";
2856
+ var CONTXT_BLOCK_END2 = "# --- contxt hook end ---";
2857
+ var ALL_HOOKS2 = ["post-commit", "pre-push", "post-checkout", "prepare-commit-msg"];
2600
2858
  async function installCommand(options = {}) {
2601
- const gitHooksDir = join7(process.cwd(), ".git", "hooks");
2602
- if (!existsSync8(join7(process.cwd(), ".git"))) {
2603
- console.error(chalk7.red("Not a git repository."));
2859
+ const gitHooksDir = join9(process.cwd(), ".git", "hooks");
2860
+ if (!existsSync9(join9(process.cwd(), ".git"))) {
2861
+ console.error(chalk9.red("Not a git repository."));
2604
2862
  process.exit(1);
2605
2863
  }
2606
2864
  mkdirSync3(gitHooksDir, { recursive: true });
2607
- const hooksToInstall = options.hooks ? options.hooks.split(",").map((h) => h.trim()) : [...ALL_HOOKS];
2865
+ const hooksToInstall = options.hooks ? options.hooks.split(",").map((h) => h.trim()) : [...ALL_HOOKS2];
2608
2866
  let installed = 0;
2609
2867
  let updated = 0;
2610
2868
  for (const hookName of hooksToInstall) {
2611
- const hookPath = join7(gitHooksDir, hookName);
2869
+ const hookPath = join9(gitHooksDir, hookName);
2612
2870
  const contxtLine = `contxt hook run ${hookName} "$@"`;
2613
- const contxtBlock = `${CONTXT_BLOCK_START}
2871
+ const contxtBlock = `${CONTXT_BLOCK_START2}
2614
2872
  ${contxtLine}
2615
- ${CONTXT_BLOCK_END}`;
2616
- if (existsSync8(hookPath)) {
2617
- const content = readFileSync9(hookPath, "utf-8");
2618
- if (content.includes(CONTXT_BLOCK_START)) {
2873
+ ${CONTXT_BLOCK_END2}`;
2874
+ if (existsSync9(hookPath)) {
2875
+ const content = readFileSync11(hookPath, "utf-8");
2876
+ if (content.includes(CONTXT_BLOCK_START2)) {
2619
2877
  const updated_content = content.replace(
2620
- new RegExp(`${escapeRegex(CONTXT_BLOCK_START)}[\\s\\S]*?${escapeRegex(CONTXT_BLOCK_END)}`),
2878
+ new RegExp(`${escapeRegex(CONTXT_BLOCK_START2)}[\\s\\S]*?${escapeRegex(CONTXT_BLOCK_END2)}`),
2621
2879
  contxtBlock
2622
2880
  );
2623
- writeFileSync5(hookPath, updated_content, "utf-8");
2881
+ writeFileSync6(hookPath, updated_content, "utf-8");
2624
2882
  updated++;
2625
2883
  } else {
2626
2884
  const newContent = content.trimEnd() + "\n\n" + contxtBlock + "\n";
2627
- writeFileSync5(hookPath, newContent, "utf-8");
2885
+ writeFileSync6(hookPath, newContent, "utf-8");
2628
2886
  installed++;
2629
2887
  }
2630
2888
  } else {
@@ -2632,79 +2890,79 @@ ${CONTXT_BLOCK_END}`;
2632
2890
 
2633
2891
  ${contxtBlock}
2634
2892
  `;
2635
- writeFileSync5(hookPath, newContent, "utf-8");
2893
+ writeFileSync6(hookPath, newContent, "utf-8");
2636
2894
  installed++;
2637
2895
  }
2638
- chmodSync(hookPath, "755");
2639
- console.log(chalk7.green("\u2713"), `${hookName}`);
2896
+ chmodSync2(hookPath, "755");
2897
+ console.log(chalk9.green("\u2713"), `${hookName}`);
2640
2898
  }
2641
2899
  console.log("");
2642
- if (installed > 0) console.log(chalk7.green(`Installed ${installed} hook${installed !== 1 ? "s" : ""}.`));
2643
- if (updated > 0) console.log(chalk7.yellow(`Updated ${updated} existing hook${updated !== 1 ? "s" : ""}.`));
2900
+ if (installed > 0) console.log(chalk9.green(`Installed ${installed} hook${installed !== 1 ? "s" : ""}.`));
2901
+ if (updated > 0) console.log(chalk9.yellow(`Updated ${updated} existing hook${updated !== 1 ? "s" : ""}.`));
2644
2902
  console.log("");
2645
- console.log(chalk7.gray("Hooks will capture context from your git workflow automatically."));
2903
+ console.log(chalk9.gray("Hooks will capture context from your git workflow automatically."));
2646
2904
  }
2647
2905
  async function uninstallCommand(options = {}) {
2648
- const gitHooksDir = join7(process.cwd(), ".git", "hooks");
2649
- if (!existsSync8(join7(process.cwd(), ".git"))) {
2650
- console.error(chalk7.red("Not a git repository."));
2906
+ const gitHooksDir = join9(process.cwd(), ".git", "hooks");
2907
+ if (!existsSync9(join9(process.cwd(), ".git"))) {
2908
+ console.error(chalk9.red("Not a git repository."));
2651
2909
  process.exit(1);
2652
2910
  }
2653
- const hooksToRemove = options.hooks ? options.hooks.split(",").map((h) => h.trim()) : [...ALL_HOOKS];
2911
+ const hooksToRemove = options.hooks ? options.hooks.split(",").map((h) => h.trim()) : [...ALL_HOOKS2];
2654
2912
  let removed = 0;
2655
2913
  for (const hookName of hooksToRemove) {
2656
- const hookPath = join7(gitHooksDir, hookName);
2657
- if (!existsSync8(hookPath)) continue;
2658
- const content = readFileSync9(hookPath, "utf-8");
2659
- if (!content.includes(CONTXT_BLOCK_START)) continue;
2914
+ const hookPath = join9(gitHooksDir, hookName);
2915
+ if (!existsSync9(hookPath)) continue;
2916
+ const content = readFileSync11(hookPath, "utf-8");
2917
+ if (!content.includes(CONTXT_BLOCK_START2)) continue;
2660
2918
  const cleaned = content.replace(
2661
- new RegExp(`\\n*${escapeRegex(CONTXT_BLOCK_START)}[\\s\\S]*?${escapeRegex(CONTXT_BLOCK_END)}\\n*`),
2919
+ new RegExp(`\\n*${escapeRegex(CONTXT_BLOCK_START2)}[\\s\\S]*?${escapeRegex(CONTXT_BLOCK_END2)}\\n*`),
2662
2920
  "\n"
2663
2921
  ).trim();
2664
2922
  if (cleaned === "#!/bin/sh" || cleaned === "") {
2665
- writeFileSync5(hookPath, "#!/bin/sh\n", "utf-8");
2923
+ writeFileSync6(hookPath, "#!/bin/sh\n", "utf-8");
2666
2924
  } else {
2667
- writeFileSync5(hookPath, cleaned + "\n", "utf-8");
2925
+ writeFileSync6(hookPath, cleaned + "\n", "utf-8");
2668
2926
  }
2669
2927
  removed++;
2670
- console.log(chalk7.gray("\u2717"), hookName);
2928
+ console.log(chalk9.gray("\u2717"), hookName);
2671
2929
  }
2672
2930
  if (removed > 0) {
2673
2931
  console.log("");
2674
- console.log(chalk7.green(`Removed ${removed} hook${removed !== 1 ? "s" : ""}.`));
2932
+ console.log(chalk9.green(`Removed ${removed} hook${removed !== 1 ? "s" : ""}.`));
2675
2933
  } else {
2676
- console.log(chalk7.gray("No Contxt hooks found to remove."));
2934
+ console.log(chalk9.gray("No Contxt hooks found to remove."));
2677
2935
  }
2678
2936
  }
2679
2937
  async function statusCommand2() {
2680
- const gitHooksDir = join7(process.cwd(), ".git", "hooks");
2681
- if (!existsSync8(join7(process.cwd(), ".git"))) {
2682
- console.error(chalk7.red("Not a git repository."));
2938
+ const gitHooksDir = join9(process.cwd(), ".git", "hooks");
2939
+ if (!existsSync9(join9(process.cwd(), ".git"))) {
2940
+ console.error(chalk9.red("Not a git repository."));
2683
2941
  process.exit(1);
2684
2942
  }
2685
2943
  console.log("");
2686
- console.log(chalk7.bold("Git Hook Status"));
2944
+ console.log(chalk9.bold("Git Hook Status"));
2687
2945
  console.log("");
2688
- for (const hookName of ALL_HOOKS) {
2689
- const hookPath = join7(gitHooksDir, hookName);
2690
- const fileExists = existsSync8(hookPath);
2946
+ for (const hookName of ALL_HOOKS2) {
2947
+ const hookPath = join9(gitHooksDir, hookName);
2948
+ const fileExists = existsSync9(hookPath);
2691
2949
  let isInstalled = false;
2692
2950
  if (fileExists) {
2693
- const content = readFileSync9(hookPath, "utf-8");
2694
- isInstalled = content.includes(CONTXT_BLOCK_START);
2951
+ const content = readFileSync11(hookPath, "utf-8");
2952
+ isInstalled = content.includes(CONTXT_BLOCK_START2);
2695
2953
  }
2696
- const statusIcon = isInstalled ? chalk7.green("\u2713") : chalk7.gray("\u25CB");
2697
- const label = isInstalled ? chalk7.green(hookName) : chalk7.gray(hookName);
2698
- const note = !fileExists ? chalk7.gray(" (no hook file)") : isInstalled ? chalk7.gray(" installed") : chalk7.yellow(" not installed");
2954
+ const statusIcon = isInstalled ? chalk9.green("\u2713") : chalk9.gray("\u25CB");
2955
+ const label = isInstalled ? chalk9.green(hookName) : chalk9.gray(hookName);
2956
+ const note = !fileExists ? chalk9.gray(" (no hook file)") : isInstalled ? chalk9.gray(" installed") : chalk9.yellow(" not installed");
2699
2957
  console.log(` ${statusIcon} ${label}${note}`);
2700
2958
  }
2701
2959
  console.log("");
2702
- const installedCount = ALL_HOOKS.filter((h) => {
2703
- const hookPath = join7(gitHooksDir, h);
2704
- return existsSync8(hookPath) && readFileSync9(hookPath, "utf-8").includes(CONTXT_BLOCK_START);
2960
+ const installedCount = ALL_HOOKS2.filter((h) => {
2961
+ const hookPath = join9(gitHooksDir, h);
2962
+ return existsSync9(hookPath) && readFileSync11(hookPath, "utf-8").includes(CONTXT_BLOCK_START2);
2705
2963
  }).length;
2706
2964
  if (installedCount === 0) {
2707
- console.log(chalk7.cyan(`Run ${chalk7.bold("contxt hook install")} to enable automatic context capture.`));
2965
+ console.log(chalk9.cyan(`Run ${chalk9.bold("contxt hook install")} to enable automatic context capture.`));
2708
2966
  }
2709
2967
  }
2710
2968
  async function runCommand(hookName) {
@@ -2735,12 +2993,14 @@ var hookCommand = {
2735
2993
  };
2736
2994
 
2737
2995
  // src/commands/watch.ts
2738
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync9, unlinkSync } from "fs";
2739
- import { join as join8, relative as relative2 } from "path";
2740
- import { spawn } from "child_process";
2741
- import chalk8 from "chalk";
2996
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync10, unlinkSync } from "fs";
2997
+ import { join as join10, relative as relative2 } from "path";
2998
+ import { spawn as spawn2 } from "child_process";
2999
+ import chalk10 from "chalk";
2742
3000
  import chokidar from "chokidar";
2743
- import { parseFile as parseFile2, scanCommentToEntry as scanCommentToEntry2 } from "@mycontxt/core";
3001
+ import { parseFile as parseFile2, scanCommentToEntry as scanCommentToEntry2, SyncEngine as SyncEngine2 } from "@mycontxt/core";
3002
+ import { SQLiteDatabase as SQLiteDatabase7 } from "@mycontxt/adapters/sqlite";
3003
+ import { SupabaseDatabase as SupabaseDatabase4 } from "@mycontxt/adapters/supabase";
2744
3004
  var PID_FILE = ".contxt/.watch.pid";
2745
3005
  var LOG_FILE = ".contxt/watch.log";
2746
3006
  var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
@@ -2752,59 +3012,59 @@ async function startCommand(options = {}) {
2752
3012
  return runWatcher();
2753
3013
  }
2754
3014
  async function stopCommand() {
2755
- const pidFile = join8(process.cwd(), PID_FILE);
2756
- if (!existsSync9(pidFile)) {
2757
- console.log(chalk8.gray("No watch daemon running."));
3015
+ const pidFile = join10(process.cwd(), PID_FILE);
3016
+ if (!existsSync10(pidFile)) {
3017
+ console.log(chalk10.gray("No watch daemon running."));
2758
3018
  return;
2759
3019
  }
2760
- const pid = parseInt(readFileSync10(pidFile, "utf-8").trim(), 10);
3020
+ const pid = parseInt(readFileSync12(pidFile, "utf-8").trim(), 10);
2761
3021
  try {
2762
3022
  process.kill(pid, "SIGTERM");
2763
3023
  unlinkSync(pidFile);
2764
- console.log(chalk8.green("Watch daemon stopped."));
3024
+ console.log(chalk10.green("Watch daemon stopped."));
2765
3025
  } catch {
2766
- console.log(chalk8.yellow("Daemon not found \u2014 removing stale PID file."));
3026
+ console.log(chalk10.yellow("Daemon not found \u2014 removing stale PID file."));
2767
3027
  unlinkSync(pidFile);
2768
3028
  }
2769
3029
  }
2770
3030
  async function statusCommand3() {
2771
- const pidFile = join8(process.cwd(), PID_FILE);
2772
- if (!existsSync9(pidFile)) {
2773
- console.log(chalk8.gray("Watch daemon: not running"));
3031
+ const pidFile = join10(process.cwd(), PID_FILE);
3032
+ if (!existsSync10(pidFile)) {
3033
+ console.log(chalk10.gray("Watch daemon: not running"));
2774
3034
  return;
2775
3035
  }
2776
- const pid = parseInt(readFileSync10(pidFile, "utf-8").trim(), 10);
3036
+ const pid = parseInt(readFileSync12(pidFile, "utf-8").trim(), 10);
2777
3037
  try {
2778
3038
  process.kill(pid, 0);
2779
- console.log(chalk8.green(`Watch daemon: running (PID ${pid})`));
3039
+ console.log(chalk10.green(`Watch daemon: running (PID ${pid})`));
2780
3040
  } catch {
2781
- console.log(chalk8.yellow("Watch daemon: stale PID file (process not found)"));
3041
+ console.log(chalk10.yellow("Watch daemon: stale PID file (process not found)"));
2782
3042
  unlinkSync(pidFile);
2783
3043
  }
2784
3044
  }
2785
3045
  function startDaemon() {
2786
- const pidFile = join8(process.cwd(), PID_FILE);
2787
- if (existsSync9(pidFile)) {
2788
- const pid = parseInt(readFileSync10(pidFile, "utf-8").trim(), 10);
3046
+ const pidFile = join10(process.cwd(), PID_FILE);
3047
+ if (existsSync10(pidFile)) {
3048
+ const pid = parseInt(readFileSync12(pidFile, "utf-8").trim(), 10);
2789
3049
  try {
2790
3050
  process.kill(pid, 0);
2791
- console.log(chalk8.yellow(`Watch daemon already running (PID ${pid}).`));
3051
+ console.log(chalk10.yellow(`Watch daemon already running (PID ${pid}).`));
2792
3052
  return;
2793
3053
  } catch {
2794
3054
  }
2795
3055
  }
2796
- const logPath = join8(process.cwd(), LOG_FILE);
3056
+ const logPath = join10(process.cwd(), LOG_FILE);
2797
3057
  const logStream = __require("fs").openSync(logPath, "a");
2798
- const child = spawn(process.execPath, [process.argv[1], "watch"], {
3058
+ const child = spawn2(process.execPath, [process.argv[1], "watch"], {
2799
3059
  env: { ...process.env, CONTXT_WATCH_DAEMON: "1" },
2800
3060
  detached: true,
2801
3061
  stdio: ["ignore", logStream, logStream],
2802
3062
  cwd: process.cwd()
2803
3063
  });
2804
3064
  child.unref();
2805
- writeFileSync6(pidFile, String(child.pid), "utf-8");
2806
- console.log(chalk8.green(`Watch daemon started (PID ${child.pid}).`));
2807
- console.log(chalk8.gray(`Logs: ${logPath}`));
3065
+ writeFileSync7(pidFile, String(child.pid), "utf-8");
3066
+ console.log(chalk10.green(`Watch daemon started (PID ${child.pid}).`));
3067
+ console.log(chalk10.gray(`Logs: ${logPath}`));
2808
3068
  }
2809
3069
  async function runWatcher() {
2810
3070
  const cwd = process.cwd();
@@ -2812,12 +3072,12 @@ async function runWatcher() {
2812
3072
  const db = await getProjectDb(cwd);
2813
3073
  const project = await db.getProjectByPath(cwd);
2814
3074
  if (!project) {
2815
- if (!isDaemon) console.error(chalk8.red("Not a Contxt project."));
3075
+ if (!isDaemon) console.error(chalk10.red("Not a Contxt project."));
2816
3076
  return;
2817
3077
  }
2818
3078
  const branch2 = await db.getActiveBranch(project.id);
2819
3079
  if (!isDaemon) {
2820
- console.log(chalk8.bold(`contxt watch`) + ` \u2014 monitoring ${project.name} (${branch2})`);
3080
+ console.log(chalk10.bold(`contxt watch`) + ` \u2014 monitoring ${project.name} (${branch2})`);
2821
3081
  console.log("");
2822
3082
  }
2823
3083
  const pendingFiles = /* @__PURE__ */ new Set();
@@ -2854,9 +3114,9 @@ async function runWatcher() {
2854
3114
  usePolling: false,
2855
3115
  interval: 1e3
2856
3116
  });
2857
- const rulesPath = join8(cwd, ".contxt", "rules.md");
3117
+ const rulesPath = join10(cwd, ".contxt", "rules.md");
2858
3118
  const rulesWatcher = chokidar.watch(rulesPath, { ignoreInitial: true });
2859
- const gitHeadPath = join8(cwd, ".git", "HEAD");
3119
+ const gitHeadPath = join10(cwd, ".git", "HEAD");
2860
3120
  const gitWatcher = chokidar.watch(gitHeadPath, { ignoreInitial: true });
2861
3121
  watcher.on("change", (filePath) => {
2862
3122
  pendingFiles.add(filePath);
@@ -2872,7 +3132,7 @@ async function runWatcher() {
2872
3132
  log("rules", "rules.md changed \u2014 syncing...");
2873
3133
  try {
2874
3134
  const { parseRulesFile: parseRulesFile2 } = await import("@mycontxt/core");
2875
- const content = readFileSync10(rulesPath, "utf-8");
3135
+ const content = readFileSync12(rulesPath, "utf-8");
2876
3136
  const parsed = parseRulesFile2(content);
2877
3137
  let synced = 0;
2878
3138
  const existing = await db.listEntries({ projectId: project.id, branch: branch2 });
@@ -2894,7 +3154,7 @@ async function runWatcher() {
2894
3154
  });
2895
3155
  gitWatcher.on("change", async () => {
2896
3156
  try {
2897
- const headContent = readFileSync10(gitHeadPath, "utf-8").trim();
3157
+ const headContent = readFileSync12(gitHeadPath, "utf-8").trim();
2898
3158
  const branchMatch = headContent.match(/^ref: refs\/heads\/(.+)$/);
2899
3159
  if (!branchMatch) return;
2900
3160
  const newBranch = branchMatch[1];
@@ -2919,7 +3179,7 @@ async function runWatcher() {
2919
3179
  const activeCtx = entries.find((e) => e.status === "active");
2920
3180
  if (activeCtx) {
2921
3181
  const currentFiles = activeCtx.metadata.files || [];
2922
- const relFiles = files.map((f) => relative2(cwd, join8(cwd, f)));
3182
+ const relFiles = files.map((f) => relative2(cwd, join10(cwd, f)));
2923
3183
  const merged = Array.from(/* @__PURE__ */ new Set([...currentFiles, ...relFiles])).slice(0, 30);
2924
3184
  await db.updateEntry(activeCtx.id, { metadata: { ...activeCtx.metadata, files: merged } });
2925
3185
  }
@@ -2928,9 +3188,9 @@ async function runWatcher() {
2928
3188
  let newDrafts = 0;
2929
3189
  for (const file of files) {
2930
3190
  try {
2931
- const absPath = join8(cwd, file);
2932
- if (!existsSync9(absPath)) continue;
2933
- const content = readFileSync10(absPath, "utf-8");
3191
+ const absPath = join10(cwd, file);
3192
+ if (!existsSync10(absPath)) continue;
3193
+ const content = readFileSync12(absPath, "utf-8");
2934
3194
  const comments = parseFile2(content, file);
2935
3195
  if (comments.length === 0) continue;
2936
3196
  const existing = await db.listEntries({ projectId: project.id, branch: branch2 });
@@ -2946,6 +3206,25 @@ async function runWatcher() {
2946
3206
  }
2947
3207
  }
2948
3208
  log("files", `${files.length} file${files.length !== 1 ? "s" : ""} ${newDrafts > 0 ? `\xB7 +${newDrafts} draft${newDrafts !== 1 ? "s" : ""}` : ""}`);
3209
+ try {
3210
+ const freshProject = await db.getProjectByPath(cwd);
3211
+ if (freshProject?.config.autoSync) {
3212
+ const accessToken = getAccessToken();
3213
+ if (accessToken) {
3214
+ const supabaseConfig = getSupabaseConfig();
3215
+ const dbPath = getDbPath();
3216
+ const localDb = new SQLiteDatabase7(dbPath);
3217
+ await localDb.initialize();
3218
+ const remoteDb = new SupabaseDatabase4({ ...supabaseConfig, accessToken });
3219
+ await remoteDb.initialize();
3220
+ const syncEngine = new SyncEngine2(localDb, remoteDb);
3221
+ const result = await syncEngine.push(freshProject.id, {});
3222
+ await localDb.close();
3223
+ log("sync", `pushed ${result.pushed} entr${result.pushed !== 1 ? "ies" : "y"} to cloud`);
3224
+ }
3225
+ }
3226
+ } catch {
3227
+ }
2949
3228
  }
2950
3229
  function scheduleFlush() {
2951
3230
  if (flushTimer) clearTimeout(flushTimer);
@@ -2972,10 +3251,10 @@ async function runWatcher() {
2972
3251
  const time = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit" });
2973
3252
  const line = `${time} ${type.padEnd(8)} ${message}`;
2974
3253
  if (!isDaemon) {
2975
- console.log(` ${chalk8.gray(time)} ${chalk8.cyan(type.padEnd(8))} ${message}`);
3254
+ console.log(` ${chalk10.gray(time)} ${chalk10.cyan(type.padEnd(8))} ${message}`);
2976
3255
  } else {
2977
3256
  try {
2978
- __require("fs").appendFileSync(join8(cwd, LOG_FILE), line + "\n");
3257
+ __require("fs").appendFileSync(join10(cwd, LOG_FILE), line + "\n");
2979
3258
  } catch {
2980
3259
  }
2981
3260
  }
@@ -2987,14 +3266,14 @@ async function runWatcher() {
2987
3266
  rulesWatcher.close();
2988
3267
  gitWatcher.close();
2989
3268
  await db.close();
2990
- const pidFile = join8(cwd, PID_FILE);
2991
- if (existsSync9(pidFile)) unlinkSync(pidFile);
3269
+ const pidFile = join10(cwd, PID_FILE);
3270
+ if (existsSync10(pidFile)) unlinkSync(pidFile);
2992
3271
  process.exit(0);
2993
3272
  };
2994
3273
  process.on("SIGTERM", shutdown);
2995
3274
  process.on("SIGINT", shutdown);
2996
3275
  if (!isDaemon) {
2997
- console.log(chalk8.gray("Watching for file changes. Ctrl+C to stop.\n"));
3276
+ console.log(chalk10.gray("Watching for file changes. Ctrl+C to stop.\n"));
2998
3277
  }
2999
3278
  }
3000
3279
  var watchCommand = {