@tricknowtech/context 0.1.0 → 0.1.1

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.
@@ -228,12 +228,41 @@ var PROJECT_CONTEXT_GLOBS = [
228
228
  ".cursorrules",
229
229
  ".github/copilot-instructions.md",
230
230
  ".claude/settings.json",
231
+ // `.local.json` variants are gitignored by default, so nothing else carries
232
+ // them — which makes them exactly the kind of file this tool exists for.
233
+ ".claude/settings.local.json",
231
234
  ".claude/memory/",
232
235
  ".claude/plans/",
233
236
  ".claude/commands/",
234
237
  ".claude/agents/",
235
238
  ".claude/skills/"
236
239
  ];
240
+ function planStem(fileName) {
241
+ return fileName.replace(/\.md$/, "").replace(/-agent-[0-9a-f]+$/i, "");
242
+ }
243
+ function collectPlans(plansDir, projectRoot, exclude) {
244
+ const all = walk(plansDir, { exclude });
245
+ if (all.length === 0) return [];
246
+ const projectName = path3.basename(projectRoot);
247
+ const related = /* @__PURE__ */ new Set();
248
+ const stems = /* @__PURE__ */ new Set();
249
+ for (const abs of all) {
250
+ let text = "";
251
+ try {
252
+ text = fs3.readFileSync(abs, "utf8");
253
+ } catch {
254
+ continue;
255
+ }
256
+ if (text.includes(projectRoot) || text.includes(projectName)) {
257
+ related.add(abs);
258
+ stems.add(planStem(path3.basename(abs)));
259
+ }
260
+ }
261
+ for (const abs of all) {
262
+ if (stems.has(planStem(path3.basename(abs)))) related.add(abs);
263
+ }
264
+ return [...related];
265
+ }
237
266
  function push(out, sourcePath, storePath, tier, sourceRoot, ctx) {
238
267
  let size = 0;
239
268
  try {
@@ -268,10 +297,24 @@ function collect(projectRoot, cfg, tiers) {
268
297
  }
269
298
  push(files, abs, `project/${rel}`, "core", "project", ctx);
270
299
  }
271
- const memoryDir = path3.join(userClaude, "projects", key, "memory");
272
- for (const abs of walk(memoryDir, { exclude })) {
273
- const rel = toPosix(path3.relative(memoryDir, abs));
274
- push(files, abs, `memory/${rel}`, "core", "user", ctx);
300
+ const projectsDir = path3.join(userClaude, "projects");
301
+ let projectKeys = [];
302
+ try {
303
+ projectKeys = fs3.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && (d.name === key || d.name.startsWith(key + "-"))).map((d) => d.name);
304
+ } catch {
305
+ projectKeys = [];
306
+ }
307
+ for (const pk of projectKeys) {
308
+ const memoryDir = path3.join(projectsDir, pk, "memory");
309
+ for (const abs of walk(memoryDir, { exclude })) {
310
+ const rel = toPosix(path3.relative(memoryDir, abs));
311
+ const storePath = pk === key ? `memory/${rel}` : `memory-sub/${pk.slice(key.length + 1)}/${rel}`;
312
+ push(files, abs, storePath, "core", "user", ctx);
313
+ }
314
+ }
315
+ for (const abs of collectPlans(path3.join(userClaude, "plans"), projectRoot, exclude)) {
316
+ const rel = toPosix(path3.relative(path3.join(userClaude, "plans"), abs));
317
+ push(files, abs, `plans/${rel}`, "core", "user", ctx);
275
318
  }
276
319
  const skillsDir = path3.join(userClaude, "skills");
277
320
  for (const abs of walk(skillsDir, { exclude })) {
@@ -307,6 +350,44 @@ function collect(projectRoot, cfg, tiers) {
307
350
  }
308
351
  return { files, skippedTracked, ctx };
309
352
  }
353
+ function describeExcluded(projectRoot, tiers) {
354
+ const userClaude = userClaudeDir();
355
+ const key = cwdKey(projectRoot);
356
+ const out = [];
357
+ const measure = (dir, filter) => {
358
+ let files = 0;
359
+ let bytes = 0;
360
+ for (const abs of walk(dir, { exclude: [] })) {
361
+ if (filter && !filter(abs)) continue;
362
+ files++;
363
+ try {
364
+ bytes += fs3.statSync(abs).size;
365
+ } catch {
366
+ }
367
+ }
368
+ return { files, bytes };
369
+ };
370
+ if (!tiers.includes("transcripts")) {
371
+ const projDir = path3.join(userClaude, "projects", key);
372
+ const m = measure(projDir, (p) => !p.includes(`${path3.sep}memory${path3.sep}`));
373
+ if (m.files > 0) {
374
+ out.push({
375
+ label: "session transcripts",
376
+ ...m,
377
+ reason: "cloud-only \u2014 append-only logs this large would permanently bloat the repo"
378
+ });
379
+ }
380
+ }
381
+ for (const [dir, label, reason] of [
382
+ ["uploads", "pasted files/images", "session-scoped binaries; regenerate rather than sync"],
383
+ ["file-history", "edit-undo snapshots", "transient local undo state, not portable context"],
384
+ ["tasks", "task outputs", "session-scoped tool output"]
385
+ ]) {
386
+ const m = measure(path3.join(userClaude, dir));
387
+ if (m.files > 0) out.push({ label, ...m, reason });
388
+ }
389
+ return out.filter((g) => g.bytes > 0);
390
+ }
310
391
  function summarize(files) {
311
392
  const empty = { count: 0, bytes: 0 };
312
393
  const out = {
@@ -606,6 +687,7 @@ export {
606
687
  loadConfig,
607
688
  saveConfig,
608
689
  collect,
690
+ describeExcluded,
609
691
  summarize,
610
692
  SLASH_COMMAND_PATH,
611
693
  SLASH_COMMAND_BODY,
package/dist/cli.cjs CHANGED
@@ -273,12 +273,41 @@ var PROJECT_CONTEXT_GLOBS = [
273
273
  ".cursorrules",
274
274
  ".github/copilot-instructions.md",
275
275
  ".claude/settings.json",
276
+ // `.local.json` variants are gitignored by default, so nothing else carries
277
+ // them — which makes them exactly the kind of file this tool exists for.
278
+ ".claude/settings.local.json",
276
279
  ".claude/memory/",
277
280
  ".claude/plans/",
278
281
  ".claude/commands/",
279
282
  ".claude/agents/",
280
283
  ".claude/skills/"
281
284
  ];
285
+ function planStem(fileName) {
286
+ return fileName.replace(/\.md$/, "").replace(/-agent-[0-9a-f]+$/i, "");
287
+ }
288
+ function collectPlans(plansDir, projectRoot, exclude) {
289
+ const all = walk(plansDir, { exclude });
290
+ if (all.length === 0) return [];
291
+ const projectName = import_node_path3.default.basename(projectRoot);
292
+ const related = /* @__PURE__ */ new Set();
293
+ const stems = /* @__PURE__ */ new Set();
294
+ for (const abs of all) {
295
+ let text = "";
296
+ try {
297
+ text = import_node_fs3.default.readFileSync(abs, "utf8");
298
+ } catch {
299
+ continue;
300
+ }
301
+ if (text.includes(projectRoot) || text.includes(projectName)) {
302
+ related.add(abs);
303
+ stems.add(planStem(import_node_path3.default.basename(abs)));
304
+ }
305
+ }
306
+ for (const abs of all) {
307
+ if (stems.has(planStem(import_node_path3.default.basename(abs)))) related.add(abs);
308
+ }
309
+ return [...related];
310
+ }
282
311
  function push(out2, sourcePath, storePath, tier, sourceRoot, ctx) {
283
312
  let size = 0;
284
313
  try {
@@ -313,10 +342,24 @@ function collect(projectRoot, cfg, tiers) {
313
342
  }
314
343
  push(files, abs, `project/${rel}`, "core", "project", ctx);
315
344
  }
316
- const memoryDir = import_node_path3.default.join(userClaude, "projects", key, "memory");
317
- for (const abs of walk(memoryDir, { exclude })) {
318
- const rel = toPosix(import_node_path3.default.relative(memoryDir, abs));
319
- push(files, abs, `memory/${rel}`, "core", "user", ctx);
345
+ const projectsDir = import_node_path3.default.join(userClaude, "projects");
346
+ let projectKeys = [];
347
+ try {
348
+ projectKeys = import_node_fs3.default.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && (d.name === key || d.name.startsWith(key + "-"))).map((d) => d.name);
349
+ } catch {
350
+ projectKeys = [];
351
+ }
352
+ for (const pk of projectKeys) {
353
+ const memoryDir = import_node_path3.default.join(projectsDir, pk, "memory");
354
+ for (const abs of walk(memoryDir, { exclude })) {
355
+ const rel = toPosix(import_node_path3.default.relative(memoryDir, abs));
356
+ const storePath = pk === key ? `memory/${rel}` : `memory-sub/${pk.slice(key.length + 1)}/${rel}`;
357
+ push(files, abs, storePath, "core", "user", ctx);
358
+ }
359
+ }
360
+ for (const abs of collectPlans(import_node_path3.default.join(userClaude, "plans"), projectRoot, exclude)) {
361
+ const rel = toPosix(import_node_path3.default.relative(import_node_path3.default.join(userClaude, "plans"), abs));
362
+ push(files, abs, `plans/${rel}`, "core", "user", ctx);
320
363
  }
321
364
  const skillsDir = import_node_path3.default.join(userClaude, "skills");
322
365
  for (const abs of walk(skillsDir, { exclude })) {
@@ -352,6 +395,44 @@ function collect(projectRoot, cfg, tiers) {
352
395
  }
353
396
  return { files, skippedTracked, ctx };
354
397
  }
398
+ function describeExcluded(projectRoot, tiers) {
399
+ const userClaude = userClaudeDir();
400
+ const key = cwdKey(projectRoot);
401
+ const out2 = [];
402
+ const measure = (dir, filter) => {
403
+ let files = 0;
404
+ let bytes = 0;
405
+ for (const abs of walk(dir, { exclude: [] })) {
406
+ if (filter && !filter(abs)) continue;
407
+ files++;
408
+ try {
409
+ bytes += import_node_fs3.default.statSync(abs).size;
410
+ } catch {
411
+ }
412
+ }
413
+ return { files, bytes };
414
+ };
415
+ if (!tiers.includes("transcripts")) {
416
+ const projDir = import_node_path3.default.join(userClaude, "projects", key);
417
+ const m = measure(projDir, (p) => !p.includes(`${import_node_path3.default.sep}memory${import_node_path3.default.sep}`));
418
+ if (m.files > 0) {
419
+ out2.push({
420
+ label: "session transcripts",
421
+ ...m,
422
+ reason: "cloud-only \u2014 append-only logs this large would permanently bloat the repo"
423
+ });
424
+ }
425
+ }
426
+ for (const [dir, label, reason] of [
427
+ ["uploads", "pasted files/images", "session-scoped binaries; regenerate rather than sync"],
428
+ ["file-history", "edit-undo snapshots", "transient local undo state, not portable context"],
429
+ ["tasks", "task outputs", "session-scoped tool output"]
430
+ ]) {
431
+ const m = measure(import_node_path3.default.join(userClaude, dir));
432
+ if (m.files > 0) out2.push({ label, ...m, reason });
433
+ }
434
+ return out2.filter((g) => g.bytes > 0);
435
+ }
355
436
  function summarize(files) {
356
437
  const empty = { count: 0, bytes: 0 };
357
438
  const out2 = {
@@ -735,6 +816,13 @@ function cmdPush(opts = {}) {
735
816
  if (skippedTracked.length > 0) {
736
817
  lines.push("", `${skippedTracked.length} project files skipped \u2014 git already tracks them.`);
737
818
  }
819
+ const excluded = describeExcluded(root, tiers);
820
+ if (excluded.length > 0) {
821
+ lines.push("", "Not included (by design):");
822
+ for (const g of excluded) {
823
+ lines.push(` ${g.label.padEnd(22)} ${formatBytes(g.bytes).padStart(9)} ${g.reason}`);
824
+ }
825
+ }
738
826
  if (!opts.dryRun) {
739
827
  lines.push("", "Commit .contextsync/ to carry this context with the repo.");
740
828
  }
@@ -854,7 +942,7 @@ function parseArgs(argv) {
854
942
  function run(argv) {
855
943
  const { command, flags } = parseArgs(argv);
856
944
  if (flags.has("h") || flags.has("help")) return { code: 0, lines: [USAGE] };
857
- if (flags.has("v") || flags.has("version")) return { code: 0, lines: ["0.1.0"] };
945
+ if (flags.has("v") || flags.has("version")) return { code: 0, lines: ["0.1.1"] };
858
946
  switch (command) {
859
947
  case "init":
860
948
  return cmdInit({ artifacts: flags.has("artifacts"), force: flags.has("force") });
package/dist/cli.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  configPath,
8
8
  cwdKey,
9
9
  defaultConfig,
10
+ describeExcluded,
10
11
  ensureGitignoreEntries,
11
12
  findProjectRoot,
12
13
  formatBytes,
@@ -19,7 +20,7 @@ import {
19
20
  storeDir,
20
21
  summarize,
21
22
  userClaudeDir
22
- } from "./chunk-DEH5MUFT.js";
23
+ } from "./chunk-BWATZKYM.js";
23
24
 
24
25
  // src/commands.ts
25
26
  import fs from "fs";
@@ -129,6 +130,13 @@ function cmdPush(opts = {}) {
129
130
  if (skippedTracked.length > 0) {
130
131
  lines.push("", `${skippedTracked.length} project files skipped \u2014 git already tracks them.`);
131
132
  }
133
+ const excluded = describeExcluded(root, tiers);
134
+ if (excluded.length > 0) {
135
+ lines.push("", "Not included (by design):");
136
+ for (const g of excluded) {
137
+ lines.push(` ${g.label.padEnd(22)} ${formatBytes(g.bytes).padStart(9)} ${g.reason}`);
138
+ }
139
+ }
132
140
  if (!opts.dryRun) {
133
141
  lines.push("", "Commit .contextsync/ to carry this context with the repo.");
134
142
  }
@@ -248,7 +256,7 @@ function parseArgs(argv) {
248
256
  function run(argv) {
249
257
  const { command, flags } = parseArgs(argv);
250
258
  if (flags.has("h") || flags.has("help")) return { code: 0, lines: [USAGE] };
251
- if (flags.has("v") || flags.has("version")) return { code: 0, lines: ["0.1.0"] };
259
+ if (flags.has("v") || flags.has("version")) return { code: 0, lines: ["0.1.1"] };
252
260
  switch (command) {
253
261
  case "init":
254
262
  return cmdInit({ artifacts: flags.has("artifacts"), force: flags.has("force") });
package/dist/index.cjs CHANGED
@@ -299,12 +299,41 @@ var PROJECT_CONTEXT_GLOBS = [
299
299
  ".cursorrules",
300
300
  ".github/copilot-instructions.md",
301
301
  ".claude/settings.json",
302
+ // `.local.json` variants are gitignored by default, so nothing else carries
303
+ // them — which makes them exactly the kind of file this tool exists for.
304
+ ".claude/settings.local.json",
302
305
  ".claude/memory/",
303
306
  ".claude/plans/",
304
307
  ".claude/commands/",
305
308
  ".claude/agents/",
306
309
  ".claude/skills/"
307
310
  ];
311
+ function planStem(fileName) {
312
+ return fileName.replace(/\.md$/, "").replace(/-agent-[0-9a-f]+$/i, "");
313
+ }
314
+ function collectPlans(plansDir, projectRoot, exclude) {
315
+ const all = walk(plansDir, { exclude });
316
+ if (all.length === 0) return [];
317
+ const projectName = import_node_path3.default.basename(projectRoot);
318
+ const related = /* @__PURE__ */ new Set();
319
+ const stems = /* @__PURE__ */ new Set();
320
+ for (const abs of all) {
321
+ let text = "";
322
+ try {
323
+ text = import_node_fs3.default.readFileSync(abs, "utf8");
324
+ } catch {
325
+ continue;
326
+ }
327
+ if (text.includes(projectRoot) || text.includes(projectName)) {
328
+ related.add(abs);
329
+ stems.add(planStem(import_node_path3.default.basename(abs)));
330
+ }
331
+ }
332
+ for (const abs of all) {
333
+ if (stems.has(planStem(import_node_path3.default.basename(abs)))) related.add(abs);
334
+ }
335
+ return [...related];
336
+ }
308
337
  function push(out, sourcePath, storePath, tier, sourceRoot, ctx) {
309
338
  let size = 0;
310
339
  try {
@@ -339,10 +368,24 @@ function collect(projectRoot, cfg, tiers) {
339
368
  }
340
369
  push(files, abs, `project/${rel}`, "core", "project", ctx);
341
370
  }
342
- const memoryDir = import_node_path3.default.join(userClaude, "projects", key, "memory");
343
- for (const abs of walk(memoryDir, { exclude })) {
344
- const rel = toPosix(import_node_path3.default.relative(memoryDir, abs));
345
- push(files, abs, `memory/${rel}`, "core", "user", ctx);
371
+ const projectsDir = import_node_path3.default.join(userClaude, "projects");
372
+ let projectKeys = [];
373
+ try {
374
+ projectKeys = import_node_fs3.default.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && (d.name === key || d.name.startsWith(key + "-"))).map((d) => d.name);
375
+ } catch {
376
+ projectKeys = [];
377
+ }
378
+ for (const pk of projectKeys) {
379
+ const memoryDir = import_node_path3.default.join(projectsDir, pk, "memory");
380
+ for (const abs of walk(memoryDir, { exclude })) {
381
+ const rel = toPosix(import_node_path3.default.relative(memoryDir, abs));
382
+ const storePath = pk === key ? `memory/${rel}` : `memory-sub/${pk.slice(key.length + 1)}/${rel}`;
383
+ push(files, abs, storePath, "core", "user", ctx);
384
+ }
385
+ }
386
+ for (const abs of collectPlans(import_node_path3.default.join(userClaude, "plans"), projectRoot, exclude)) {
387
+ const rel = toPosix(import_node_path3.default.relative(import_node_path3.default.join(userClaude, "plans"), abs));
388
+ push(files, abs, `plans/${rel}`, "core", "user", ctx);
346
389
  }
347
390
  const skillsDir = import_node_path3.default.join(userClaude, "skills");
348
391
  for (const abs of walk(skillsDir, { exclude })) {
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  summarize,
32
32
  userClaudeDir,
33
33
  walk
34
- } from "./chunk-DEH5MUFT.js";
34
+ } from "./chunk-BWATZKYM.js";
35
35
  export {
36
36
  ALL_TIERS,
37
37
  CONFIG_FILE,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tricknowtech/context",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Carry a project's LLM context — memory, skills, instructions and session handoff — between machines. Local-first, commit it to your repo.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -14,8 +14,8 @@
14
14
  }
15
15
  },
16
16
  "bin": {
17
- "ctx": "./dist/cli.js",
18
- "tricknowtech-context": "./dist/cli.js"
17
+ "ctx": "dist/cli.js",
18
+ "tricknowtech-context": "dist/cli.js"
19
19
  },
20
20
  "files": [
21
21
  "dist"