@tricknowtech/context 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -1
- package/dist/{chunk-6GB2SN2L.js → chunk-BLEGQVXA.js} +228 -102
- package/dist/cli.cjs +287 -154
- package/dist/cli.js +16 -5
- package/dist/index.cjs +260 -137
- package/dist/index.d.cts +49 -8
- package/dist/index.d.ts +49 -8
- package/dist/index.js +1 -1
- package/package.json +11 -3
package/dist/index.cjs
CHANGED
|
@@ -71,12 +71,12 @@ __export(index_exports, {
|
|
|
71
71
|
module.exports = __toCommonJS(index_exports);
|
|
72
72
|
|
|
73
73
|
// src/collector.ts
|
|
74
|
-
var
|
|
75
|
-
var
|
|
74
|
+
var import_node_fs4 = __toESM(require("fs"), 1);
|
|
75
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
76
76
|
|
|
77
|
-
// src/
|
|
78
|
-
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
77
|
+
// src/assistants.ts
|
|
79
78
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
79
|
+
var import_node_os2 = __toESM(require("os"), 1);
|
|
80
80
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
81
81
|
|
|
82
82
|
// src/fsutil.ts
|
|
@@ -93,8 +93,9 @@ function cwdKey(absPath) {
|
|
|
93
93
|
function makeTemplate(absPath, ctx) {
|
|
94
94
|
const candidates = [
|
|
95
95
|
[ctx.userClaude, "{userClaude}"],
|
|
96
|
-
[ctx.project, "{project}"]
|
|
97
|
-
|
|
96
|
+
[ctx.project, "{project}"],
|
|
97
|
+
[ctx.home, "{home}"]
|
|
98
|
+
].filter(([root]) => Boolean(root)).sort((a, b) => b[0].length - a[0].length);
|
|
98
99
|
let out = absPath;
|
|
99
100
|
for (const [root, token] of candidates) {
|
|
100
101
|
if (absPath === root || absPath.startsWith(root + import_node_path.default.sep)) {
|
|
@@ -105,7 +106,7 @@ function makeTemplate(absPath, ctx) {
|
|
|
105
106
|
return ctx.cwdKey ? out.split(ctx.cwdKey).join("{cwdKey}") : out;
|
|
106
107
|
}
|
|
107
108
|
function resolveTemplate(template, ctx) {
|
|
108
|
-
const expanded = template.split("{userClaude}").join(ctx.userClaude).split("{project}").join(ctx.project).split("{cwdKey}").join(ctx.cwdKey);
|
|
109
|
+
const expanded = template.split("{userClaude}").join(ctx.userClaude).split("{project}").join(ctx.project).split("{home}").join(ctx.home).split("{cwdKey}").join(ctx.cwdKey);
|
|
109
110
|
return import_node_path.default.normalize(expanded);
|
|
110
111
|
}
|
|
111
112
|
function toPosix(p) {
|
|
@@ -227,7 +228,176 @@ function formatBytes(n) {
|
|
|
227
228
|
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
228
229
|
}
|
|
229
230
|
|
|
231
|
+
// src/assistants.ts
|
|
232
|
+
function filesIn(dir, exclude, prefix) {
|
|
233
|
+
return walk(dir, { exclude }).map((abs) => ({
|
|
234
|
+
abs,
|
|
235
|
+
rel: `${prefix}/${toPosix(import_node_path2.default.relative(dir, abs))}`
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
function fileIfExists(abs, rel) {
|
|
239
|
+
return import_node_fs2.default.existsSync(abs) ? [{ abs, rel }] : [];
|
|
240
|
+
}
|
|
241
|
+
function planStem(fileName) {
|
|
242
|
+
return fileName.replace(/\.md$/, "").replace(/-agent-[0-9a-f]+$/i, "");
|
|
243
|
+
}
|
|
244
|
+
function relatedByProject(dir, projectRoot, exclude) {
|
|
245
|
+
const all = walk(dir, { exclude });
|
|
246
|
+
if (all.length === 0) return [];
|
|
247
|
+
const projectName = import_node_path2.default.basename(projectRoot);
|
|
248
|
+
const related = /* @__PURE__ */ new Set();
|
|
249
|
+
const stems = /* @__PURE__ */ new Set();
|
|
250
|
+
for (const abs of all) {
|
|
251
|
+
let text = "";
|
|
252
|
+
try {
|
|
253
|
+
text = import_node_fs2.default.readFileSync(abs, "utf8");
|
|
254
|
+
} catch {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (text.includes(projectRoot) || text.includes(projectName)) {
|
|
258
|
+
related.add(abs);
|
|
259
|
+
stems.add(planStem(import_node_path2.default.basename(abs)));
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
for (const abs of all) {
|
|
263
|
+
if (stems.has(planStem(import_node_path2.default.basename(abs)))) related.add(abs);
|
|
264
|
+
}
|
|
265
|
+
return [...related];
|
|
266
|
+
}
|
|
267
|
+
var ADAPTERS = [
|
|
268
|
+
{
|
|
269
|
+
id: "claude",
|
|
270
|
+
name: "Claude Code",
|
|
271
|
+
projectGlobs: [
|
|
272
|
+
"CLAUDE.md",
|
|
273
|
+
"CLAUDE.local.md",
|
|
274
|
+
"**/CLAUDE.md",
|
|
275
|
+
".claude/settings.json",
|
|
276
|
+
".claude/settings.local.json",
|
|
277
|
+
".claude/memory/",
|
|
278
|
+
".claude/plans/",
|
|
279
|
+
".claude/commands/",
|
|
280
|
+
".claude/agents/",
|
|
281
|
+
".claude/skills/"
|
|
282
|
+
],
|
|
283
|
+
userDir: () => userClaudeDir(),
|
|
284
|
+
collectUser: (projectRoot, _home, exclude) => {
|
|
285
|
+
const root = userClaudeDir();
|
|
286
|
+
const out = [];
|
|
287
|
+
const key = cwdKey(projectRoot);
|
|
288
|
+
const projectsDir = import_node_path2.default.join(root, "projects");
|
|
289
|
+
let keys = [];
|
|
290
|
+
try {
|
|
291
|
+
keys = import_node_fs2.default.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && (d.name === key || d.name.startsWith(key + "-"))).map((d) => d.name);
|
|
292
|
+
} catch {
|
|
293
|
+
keys = [];
|
|
294
|
+
}
|
|
295
|
+
for (const k of keys) {
|
|
296
|
+
const memDir = import_node_path2.default.join(projectsDir, k, "memory");
|
|
297
|
+
const prefix = k === key ? "memory" : `memory-sub/${k.slice(key.length + 1)}`;
|
|
298
|
+
out.push(...filesIn(memDir, exclude, prefix));
|
|
299
|
+
}
|
|
300
|
+
out.push(...filesIn(import_node_path2.default.join(root, "skills"), exclude, "skills"));
|
|
301
|
+
out.push(...filesIn(import_node_path2.default.join(root, "agents"), exclude, "agents"));
|
|
302
|
+
for (const abs of relatedByProject(import_node_path2.default.join(root, "plans"), projectRoot, exclude)) {
|
|
303
|
+
out.push({ abs, rel: `plans/${import_node_path2.default.basename(abs)}` });
|
|
304
|
+
}
|
|
305
|
+
out.push(...fileIfExists(import_node_path2.default.join(root, "CLAUDE.md"), "user/CLAUDE.md"));
|
|
306
|
+
out.push(...fileIfExists(import_node_path2.default.join(root, "settings.json"), "user/settings.json"));
|
|
307
|
+
return out;
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
id: "codex",
|
|
312
|
+
name: "OpenAI Codex",
|
|
313
|
+
projectGlobs: ["AGENTS.md", "**/AGENTS.md", ".codex/"],
|
|
314
|
+
userDir: (home) => import_node_path2.default.join(home, ".codex"),
|
|
315
|
+
collectUser: (projectRoot, home, exclude) => {
|
|
316
|
+
const root = import_node_path2.default.join(home, ".codex");
|
|
317
|
+
const out = [];
|
|
318
|
+
out.push(...fileIfExists(import_node_path2.default.join(root, "AGENTS.md"), "user/AGENTS.md"));
|
|
319
|
+
out.push(...fileIfExists(import_node_path2.default.join(root, "config.toml"), "user/config.toml"));
|
|
320
|
+
out.push(...filesIn(import_node_path2.default.join(root, "prompts"), exclude, "prompts"));
|
|
321
|
+
return out;
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
id: "cursor",
|
|
326
|
+
name: "Cursor",
|
|
327
|
+
// `.cursorrules` is the legacy single-file form; `.cursor/rules/*.mdc` is current.
|
|
328
|
+
projectGlobs: [".cursorrules", ".cursor/rules/", ".cursor/"],
|
|
329
|
+
userDir: (home) => import_node_path2.default.join(home, ".cursor"),
|
|
330
|
+
collectUser: (_projectRoot, home, exclude) => filesIn(import_node_path2.default.join(home, ".cursor", "rules"), exclude, "rules")
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: "copilot",
|
|
334
|
+
name: "GitHub Copilot",
|
|
335
|
+
projectGlobs: [
|
|
336
|
+
".github/copilot-instructions.md",
|
|
337
|
+
".github/instructions/",
|
|
338
|
+
".github/prompts/"
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
id: "windsurf",
|
|
343
|
+
name: "Windsurf",
|
|
344
|
+
projectGlobs: [".windsurfrules", ".windsurf/rules/", ".windsurf/"],
|
|
345
|
+
userDir: (home) => import_node_path2.default.join(home, ".windsurf")
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
id: "gemini",
|
|
349
|
+
name: "Gemini CLI",
|
|
350
|
+
projectGlobs: ["GEMINI.md", "**/GEMINI.md", ".gemini/"],
|
|
351
|
+
userDir: (home) => import_node_path2.default.join(home, ".gemini"),
|
|
352
|
+
collectUser: (_projectRoot, home, exclude) => {
|
|
353
|
+
const root = import_node_path2.default.join(home, ".gemini");
|
|
354
|
+
const out = [];
|
|
355
|
+
out.push(...fileIfExists(import_node_path2.default.join(root, "GEMINI.md"), "user/GEMINI.md"));
|
|
356
|
+
out.push(...fileIfExists(import_node_path2.default.join(root, "settings.json"), "user/settings.json"));
|
|
357
|
+
out.push(...filesIn(import_node_path2.default.join(root, "commands"), exclude, "commands"));
|
|
358
|
+
return out;
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
id: "cline",
|
|
363
|
+
name: "Cline",
|
|
364
|
+
projectGlobs: [".clinerules", ".clinerules/"]
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
id: "aider",
|
|
368
|
+
name: "Aider",
|
|
369
|
+
projectGlobs: ["CONVENTIONS.md", ".aider.conf.yml", ".aider.conf.yaml"]
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: "continue",
|
|
373
|
+
name: "Continue",
|
|
374
|
+
projectGlobs: [".continue/", ".continuerules"],
|
|
375
|
+
userDir: (home) => import_node_path2.default.join(home, ".continue")
|
|
376
|
+
}
|
|
377
|
+
];
|
|
378
|
+
function adapterById(id) {
|
|
379
|
+
return ADAPTERS.find((a) => a.id === id);
|
|
380
|
+
}
|
|
381
|
+
function homeDir() {
|
|
382
|
+
return import_node_os2.default.homedir();
|
|
383
|
+
}
|
|
384
|
+
function detectAssistants(projectRoot, projectFiles) {
|
|
385
|
+
const home = homeDir();
|
|
386
|
+
return ADAPTERS.filter((a) => {
|
|
387
|
+
if (a.userDir) {
|
|
388
|
+
try {
|
|
389
|
+
if (import_node_fs2.default.existsSync(a.userDir(home))) return true;
|
|
390
|
+
} catch {
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return projectFiles.some((rel) => matchesAny(rel, a.projectGlobs));
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
230
397
|
// src/config.ts
|
|
398
|
+
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
399
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
400
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
231
401
|
var STORE_DIR = ".contextsync";
|
|
232
402
|
var CONFIG_FILE = "config.json";
|
|
233
403
|
var HANDOFF_FILE = "handoff.json";
|
|
@@ -256,29 +426,30 @@ var DEFAULT_EXCLUDE = [
|
|
|
256
426
|
function defaultConfig(projectRoot) {
|
|
257
427
|
return {
|
|
258
428
|
projectId: import_node_crypto.default.randomUUID(),
|
|
259
|
-
name:
|
|
429
|
+
name: import_node_path3.default.basename(projectRoot),
|
|
260
430
|
rootHint: projectRoot,
|
|
261
431
|
tiers: ["core", "handoff"],
|
|
432
|
+
assistants: ["auto"],
|
|
262
433
|
artifactPaths: ["graphify-out"],
|
|
263
434
|
exclude: [...DEFAULT_EXCLUDE],
|
|
264
435
|
remotes: {}
|
|
265
436
|
};
|
|
266
437
|
}
|
|
267
438
|
function findProjectRoot(start = process.cwd()) {
|
|
268
|
-
let dir =
|
|
439
|
+
let dir = import_node_path3.default.resolve(start);
|
|
269
440
|
for (; ; ) {
|
|
270
|
-
if (
|
|
271
|
-
if (
|
|
272
|
-
const parent =
|
|
441
|
+
if (import_node_fs3.default.existsSync(import_node_path3.default.join(dir, STORE_DIR, CONFIG_FILE))) return dir;
|
|
442
|
+
if (import_node_fs3.default.existsSync(import_node_path3.default.join(dir, ".git"))) return dir;
|
|
443
|
+
const parent = import_node_path3.default.dirname(dir);
|
|
273
444
|
if (parent === dir) return null;
|
|
274
445
|
dir = parent;
|
|
275
446
|
}
|
|
276
447
|
}
|
|
277
448
|
function storeDir(projectRoot) {
|
|
278
|
-
return
|
|
449
|
+
return import_node_path3.default.join(projectRoot, STORE_DIR);
|
|
279
450
|
}
|
|
280
451
|
function configPath(projectRoot) {
|
|
281
|
-
return
|
|
452
|
+
return import_node_path3.default.join(storeDir(projectRoot), CONFIG_FILE);
|
|
282
453
|
}
|
|
283
454
|
function loadConfig(projectRoot) {
|
|
284
455
|
const cfg = readJson(configPath(projectRoot));
|
|
@@ -287,7 +458,9 @@ function loadConfig(projectRoot) {
|
|
|
287
458
|
...defaultConfig(projectRoot),
|
|
288
459
|
...cfg,
|
|
289
460
|
remotes: cfg.remotes ?? {},
|
|
290
|
-
tiers: cfg.tiers ?? ["core", "handoff"]
|
|
461
|
+
tiers: cfg.tiers ?? ["core", "handoff"],
|
|
462
|
+
// Stores written before multi-assistant support have no `assistants` key.
|
|
463
|
+
assistants: cfg.assistants ?? ["auto"]
|
|
291
464
|
};
|
|
292
465
|
}
|
|
293
466
|
function saveConfig(projectRoot, cfg) {
|
|
@@ -295,54 +468,12 @@ function saveConfig(projectRoot, cfg) {
|
|
|
295
468
|
}
|
|
296
469
|
|
|
297
470
|
// src/collector.ts
|
|
298
|
-
var PROJECT_CONTEXT_GLOBS = [
|
|
299
|
-
"CLAUDE.md",
|
|
300
|
-
"CLAUDE.local.md",
|
|
301
|
-
"AGENTS.md",
|
|
302
|
-
"**/CLAUDE.md",
|
|
303
|
-
"**/AGENTS.md",
|
|
304
|
-
".cursorrules",
|
|
305
|
-
".github/copilot-instructions.md",
|
|
306
|
-
".claude/settings.json",
|
|
307
|
-
// `.local.json` variants are gitignored by default, so nothing else carries
|
|
308
|
-
// them — which makes them exactly the kind of file this tool exists for.
|
|
309
|
-
".claude/settings.local.json",
|
|
310
|
-
".claude/memory/",
|
|
311
|
-
".claude/plans/",
|
|
312
|
-
".claude/commands/",
|
|
313
|
-
".claude/agents/",
|
|
314
|
-
".claude/skills/"
|
|
315
|
-
];
|
|
316
|
-
function planStem(fileName) {
|
|
317
|
-
return fileName.replace(/\.md$/, "").replace(/-agent-[0-9a-f]+$/i, "");
|
|
318
|
-
}
|
|
319
|
-
function collectPlans(plansDir, projectRoot, exclude) {
|
|
320
|
-
const all = walk(plansDir, { exclude });
|
|
321
|
-
if (all.length === 0) return [];
|
|
322
|
-
const projectName = import_node_path3.default.basename(projectRoot);
|
|
323
|
-
const related = /* @__PURE__ */ new Set();
|
|
324
|
-
const stems = /* @__PURE__ */ new Set();
|
|
325
|
-
for (const abs of all) {
|
|
326
|
-
let text = "";
|
|
327
|
-
try {
|
|
328
|
-
text = import_node_fs3.default.readFileSync(abs, "utf8");
|
|
329
|
-
} catch {
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
if (text.includes(projectRoot) || text.includes(projectName)) {
|
|
333
|
-
related.add(abs);
|
|
334
|
-
stems.add(planStem(import_node_path3.default.basename(abs)));
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
for (const abs of all) {
|
|
338
|
-
if (stems.has(planStem(import_node_path3.default.basename(abs)))) related.add(abs);
|
|
339
|
-
}
|
|
340
|
-
return [...related];
|
|
341
|
-
}
|
|
342
471
|
function push(out, sourcePath, storePath, tier, sourceRoot, ctx) {
|
|
343
472
|
let size = 0;
|
|
344
473
|
try {
|
|
345
|
-
|
|
474
|
+
const st = import_node_fs4.default.statSync(sourcePath);
|
|
475
|
+
if (!st.isFile()) return;
|
|
476
|
+
size = st.size;
|
|
346
477
|
} catch {
|
|
347
478
|
return;
|
|
348
479
|
}
|
|
@@ -355,76 +486,66 @@ function push(out, sourcePath, storePath, tier, sourceRoot, ctx) {
|
|
|
355
486
|
restoreTemplate: makeTemplate(sourcePath, ctx)
|
|
356
487
|
});
|
|
357
488
|
}
|
|
489
|
+
function templateContextFor(projectRoot) {
|
|
490
|
+
return {
|
|
491
|
+
userClaude: userClaudeDir(),
|
|
492
|
+
project: projectRoot,
|
|
493
|
+
cwdKey: cwdKey(projectRoot),
|
|
494
|
+
home: homeDir()
|
|
495
|
+
};
|
|
496
|
+
}
|
|
358
497
|
function collect(projectRoot, cfg, tiers) {
|
|
359
|
-
const
|
|
360
|
-
const
|
|
361
|
-
const ctx = { userClaude, project: projectRoot, cwdKey: key };
|
|
498
|
+
const ctx = templateContextFor(projectRoot);
|
|
499
|
+
const home = ctx.home;
|
|
362
500
|
const files = [];
|
|
363
501
|
const skippedTracked = [];
|
|
364
502
|
const exclude = [...HARD_DENY, ...cfg.exclude];
|
|
365
503
|
const tracked = gitTrackedSet(projectRoot);
|
|
504
|
+
const projectRel = walk(projectRoot, { exclude }).map((abs) => ({
|
|
505
|
+
abs,
|
|
506
|
+
rel: toPosix(import_node_path4.default.relative(projectRoot, abs))
|
|
507
|
+
}));
|
|
508
|
+
const configured = cfg.assistants && cfg.assistants.length > 0 && !cfg.assistants.includes("auto");
|
|
509
|
+
const assistants = configured ? cfg.assistants.map(adapterById).filter(Boolean) : detectAssistants(projectRoot, projectRel.map((p) => p.rel));
|
|
366
510
|
if (tiers.includes("core")) {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
511
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
512
|
+
for (const adapter of assistants) {
|
|
513
|
+
for (const { abs, rel } of projectRel) {
|
|
514
|
+
if (claimed.has(rel)) continue;
|
|
515
|
+
if (!matchesAny(rel, adapter.projectGlobs)) continue;
|
|
516
|
+
claimed.add(rel);
|
|
517
|
+
if (tracked.has(abs)) {
|
|
518
|
+
skippedTracked.push(rel);
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
push(files, abs, `project/${rel}`, "core", "project", ctx);
|
|
373
522
|
}
|
|
374
|
-
push(files, abs, `project/${rel}`, "core", "project", ctx);
|
|
375
|
-
}
|
|
376
|
-
const projectsDir = import_node_path3.default.join(userClaude, "projects");
|
|
377
|
-
let projectKeys = [];
|
|
378
|
-
try {
|
|
379
|
-
projectKeys = import_node_fs3.default.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory() && (d.name === key || d.name.startsWith(key + "-"))).map((d) => d.name);
|
|
380
|
-
} catch {
|
|
381
|
-
projectKeys = [];
|
|
382
523
|
}
|
|
383
|
-
for (const
|
|
384
|
-
|
|
385
|
-
for (const abs of
|
|
386
|
-
|
|
387
|
-
const storePath = pk === key ? `memory/${rel}` : `memory-sub/${pk.slice(key.length + 1)}/${rel}`;
|
|
388
|
-
push(files, abs, storePath, "core", "user", ctx);
|
|
524
|
+
for (const adapter of assistants) {
|
|
525
|
+
if (!adapter.collectUser) continue;
|
|
526
|
+
for (const { abs, rel } of adapter.collectUser(projectRoot, home, exclude)) {
|
|
527
|
+
push(files, abs, `assistants/${adapter.id}/${rel}`, "core", "user", ctx);
|
|
389
528
|
}
|
|
390
529
|
}
|
|
391
|
-
for (const abs of collectPlans(import_node_path3.default.join(userClaude, "plans"), projectRoot, exclude)) {
|
|
392
|
-
const rel = toPosix(import_node_path3.default.relative(import_node_path3.default.join(userClaude, "plans"), abs));
|
|
393
|
-
push(files, abs, `plans/${rel}`, "core", "user", ctx);
|
|
394
|
-
}
|
|
395
|
-
const skillsDir = import_node_path3.default.join(userClaude, "skills");
|
|
396
|
-
for (const abs of walk(skillsDir, { exclude })) {
|
|
397
|
-
const rel = toPosix(import_node_path3.default.relative(skillsDir, abs));
|
|
398
|
-
push(files, abs, `skills/${rel}`, "core", "user", ctx);
|
|
399
|
-
}
|
|
400
|
-
const agentsDir = import_node_path3.default.join(userClaude, "agents");
|
|
401
|
-
for (const abs of walk(agentsDir, { exclude })) {
|
|
402
|
-
const rel = toPosix(import_node_path3.default.relative(agentsDir, abs));
|
|
403
|
-
push(files, abs, `agents/${rel}`, "core", "user", ctx);
|
|
404
|
-
}
|
|
405
|
-
for (const name of ["CLAUDE.md", "settings.json"]) {
|
|
406
|
-
const abs = import_node_path3.default.join(userClaude, name);
|
|
407
|
-
if (import_node_fs3.default.existsSync(abs)) push(files, abs, `user/${name}`, "core", "user", ctx);
|
|
408
|
-
}
|
|
409
530
|
}
|
|
410
531
|
if (tiers.includes("artifacts")) {
|
|
411
532
|
for (const relDir of cfg.artifactPaths) {
|
|
412
|
-
const absDir =
|
|
533
|
+
const absDir = import_node_path4.default.join(projectRoot, relDir);
|
|
413
534
|
for (const abs of walk(absDir, { exclude })) {
|
|
414
|
-
const rel = toPosix(
|
|
535
|
+
const rel = toPosix(import_node_path4.default.relative(absDir, abs));
|
|
415
536
|
push(files, abs, `artifacts/${relDir}/${rel}`, "artifacts", "project", ctx);
|
|
416
537
|
}
|
|
417
538
|
}
|
|
418
539
|
}
|
|
419
540
|
if (tiers.includes("transcripts")) {
|
|
420
|
-
const projDir =
|
|
541
|
+
const projDir = import_node_path4.default.join(userClaudeDir(), "projects", ctx.cwdKey);
|
|
421
542
|
for (const abs of walk(projDir, { exclude })) {
|
|
422
|
-
const rel = toPosix(
|
|
543
|
+
const rel = toPosix(import_node_path4.default.relative(projDir, abs));
|
|
423
544
|
if (rel.startsWith("memory/")) continue;
|
|
424
|
-
push(files, abs, `transcripts/${rel}`, "transcripts", "user", ctx);
|
|
545
|
+
push(files, abs, `transcripts/claude/${rel}`, "transcripts", "user", ctx);
|
|
425
546
|
}
|
|
426
547
|
}
|
|
427
|
-
return { files, skippedTracked, ctx };
|
|
548
|
+
return { files, skippedTracked, assistants, ctx };
|
|
428
549
|
}
|
|
429
550
|
function describeExcluded(projectRoot, tiers) {
|
|
430
551
|
const userClaude = userClaudeDir();
|
|
@@ -437,15 +558,17 @@ function describeExcluded(projectRoot, tiers) {
|
|
|
437
558
|
if (filter && !filter(abs)) continue;
|
|
438
559
|
files++;
|
|
439
560
|
try {
|
|
440
|
-
bytes +=
|
|
561
|
+
bytes += import_node_fs4.default.statSync(abs).size;
|
|
441
562
|
} catch {
|
|
442
563
|
}
|
|
443
564
|
}
|
|
444
565
|
return { files, bytes };
|
|
445
566
|
};
|
|
446
567
|
if (!tiers.includes("transcripts")) {
|
|
447
|
-
const
|
|
448
|
-
|
|
568
|
+
const m = measure(
|
|
569
|
+
import_node_path4.default.join(userClaude, "projects", key),
|
|
570
|
+
(p) => !p.includes(`${import_node_path4.default.sep}memory${import_node_path4.default.sep}`)
|
|
571
|
+
);
|
|
449
572
|
if (m.files > 0) {
|
|
450
573
|
out.push({
|
|
451
574
|
label: "session transcripts",
|
|
@@ -459,7 +582,7 @@ function describeExcluded(projectRoot, tiers) {
|
|
|
459
582
|
["file-history", "edit-undo snapshots", "transient local undo state, not portable context"],
|
|
460
583
|
["tasks", "task outputs", "session-scoped tool output"]
|
|
461
584
|
]) {
|
|
462
|
-
const m = measure(
|
|
585
|
+
const m = measure(import_node_path4.default.join(userClaude, dir));
|
|
463
586
|
if (m.files > 0) out.push({ label, ...m, reason });
|
|
464
587
|
}
|
|
465
588
|
return out.filter((g) => g.bytes > 0);
|
|
@@ -560,8 +683,8 @@ function isStale(h, newestContentMs) {
|
|
|
560
683
|
}
|
|
561
684
|
|
|
562
685
|
// src/scaffold.ts
|
|
563
|
-
var
|
|
564
|
-
var
|
|
686
|
+
var import_node_fs5 = __toESM(require("fs"), 1);
|
|
687
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
565
688
|
var SLASH_COMMAND_PATH = ".claude/commands/context.md";
|
|
566
689
|
var SLASH_COMMAND_BODY = `---
|
|
567
690
|
description: Sync this project's LLM context (memory, skills, instructions, handoff)
|
|
@@ -611,26 +734,26 @@ Run the context-sync action requested in: $ARGUMENTS
|
|
|
611
734
|
Run: \`npx @tricknowtech/context status\` and summarize the result.
|
|
612
735
|
`;
|
|
613
736
|
function installSlashCommand(projectRoot) {
|
|
614
|
-
const dest =
|
|
615
|
-
if (
|
|
616
|
-
ensureDir(
|
|
617
|
-
|
|
737
|
+
const dest = import_node_path5.default.join(projectRoot, SLASH_COMMAND_PATH);
|
|
738
|
+
if (import_node_fs5.default.existsSync(dest)) return { path: SLASH_COMMAND_PATH, written: false };
|
|
739
|
+
ensureDir(import_node_path5.default.dirname(dest));
|
|
740
|
+
import_node_fs5.default.writeFileSync(dest, SLASH_COMMAND_BODY, "utf8");
|
|
618
741
|
return { path: SLASH_COMMAND_PATH, written: true };
|
|
619
742
|
}
|
|
620
743
|
function ensureGitignoreEntries(projectRoot, artifactsEnabled) {
|
|
621
|
-
const gitignore =
|
|
744
|
+
const gitignore = import_node_path5.default.join(projectRoot, ".gitignore");
|
|
622
745
|
const wanted = [".contextsync/transcripts/"];
|
|
623
746
|
if (!artifactsEnabled) wanted.push(".contextsync/artifacts/");
|
|
624
747
|
let existing = "";
|
|
625
748
|
try {
|
|
626
|
-
existing =
|
|
749
|
+
existing = import_node_fs5.default.readFileSync(gitignore, "utf8");
|
|
627
750
|
} catch {
|
|
628
751
|
}
|
|
629
752
|
const lines = new Set(existing.split("\n").map((l) => l.trim()));
|
|
630
753
|
const missing = wanted.filter((w) => !lines.has(w));
|
|
631
754
|
if (missing.length === 0) return [];
|
|
632
755
|
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
633
|
-
|
|
756
|
+
import_node_fs5.default.appendFileSync(
|
|
634
757
|
gitignore,
|
|
635
758
|
`${prefix}
|
|
636
759
|
# tricknowtech context-sync \u2014 never commit these tiers
|
|
@@ -642,7 +765,7 @@ ${missing.join("\n")}
|
|
|
642
765
|
}
|
|
643
766
|
|
|
644
767
|
// src/secrets.ts
|
|
645
|
-
var
|
|
768
|
+
var import_node_fs6 = __toESM(require("fs"), 1);
|
|
646
769
|
var BENIGN_KEY = /(?:^|[_-])(?:input|output|prompt|completion|total|max|min|num|new|cache|cached|remaining|used|count|context|window|budget|estimated?)[_-]?tokens?$|tokens?[_-]?(?:count|used|limit|remaining|in|out|usage|per|budget)$/i;
|
|
647
770
|
function looksLikeCredential(value) {
|
|
648
771
|
if (/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(value)) return false;
|
|
@@ -696,7 +819,7 @@ function scanFiles(files) {
|
|
|
696
819
|
for (const file of files) {
|
|
697
820
|
let buf;
|
|
698
821
|
try {
|
|
699
|
-
buf =
|
|
822
|
+
buf = import_node_fs6.default.readFileSync(file.sourcePath);
|
|
700
823
|
} catch {
|
|
701
824
|
continue;
|
|
702
825
|
}
|
|
@@ -734,8 +857,8 @@ function formatHits(hits) {
|
|
|
734
857
|
}
|
|
735
858
|
|
|
736
859
|
// src/store.ts
|
|
737
|
-
var
|
|
738
|
-
var
|
|
860
|
+
var import_node_fs7 = __toESM(require("fs"), 1);
|
|
861
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
739
862
|
var MANIFEST_FILE = "manifest.json";
|
|
740
863
|
var LocalStore = class {
|
|
741
864
|
constructor(projectRoot) {
|
|
@@ -746,13 +869,13 @@ var LocalStore = class {
|
|
|
746
869
|
return storeDir(this.projectRoot);
|
|
747
870
|
}
|
|
748
871
|
write(files, projectRoot) {
|
|
749
|
-
for (const name of
|
|
872
|
+
for (const name of import_node_fs7.default.existsSync(this.dir) ? import_node_fs7.default.readdirSync(this.dir) : []) {
|
|
750
873
|
if (name === "config.json" || name === HANDOFF_FILE) continue;
|
|
751
|
-
|
|
874
|
+
import_node_fs7.default.rmSync(import_node_path6.default.join(this.dir, name), { recursive: true, force: true });
|
|
752
875
|
}
|
|
753
876
|
const entries = [];
|
|
754
877
|
for (const file of files) {
|
|
755
|
-
const dest =
|
|
878
|
+
const dest = import_node_path6.default.join(this.dir, file.storePath);
|
|
756
879
|
try {
|
|
757
880
|
copyFile(file.sourcePath, dest);
|
|
758
881
|
} catch {
|
|
@@ -773,11 +896,11 @@ var LocalStore = class {
|
|
|
773
896
|
writtenFrom: projectRoot,
|
|
774
897
|
entries
|
|
775
898
|
};
|
|
776
|
-
writeJson(
|
|
899
|
+
writeJson(import_node_path6.default.join(this.dir, MANIFEST_FILE), manifest);
|
|
777
900
|
return manifest;
|
|
778
901
|
}
|
|
779
902
|
readManifest() {
|
|
780
|
-
return readJson(
|
|
903
|
+
return readJson(import_node_path6.default.join(this.dir, MANIFEST_FILE));
|
|
781
904
|
}
|
|
782
905
|
restore(ctx, opts = {}) {
|
|
783
906
|
const manifest = this.readManifest();
|
|
@@ -785,12 +908,12 @@ var LocalStore = class {
|
|
|
785
908
|
const skipped = [];
|
|
786
909
|
if (!manifest) return { restored, skipped };
|
|
787
910
|
for (const entry of manifest.entries) {
|
|
788
|
-
const src =
|
|
789
|
-
if (!
|
|
911
|
+
const src = import_node_path6.default.join(this.dir, entry.storePath);
|
|
912
|
+
if (!import_node_fs7.default.existsSync(src)) continue;
|
|
790
913
|
const dest = resolveTemplate(entry.restoreTemplate, ctx);
|
|
791
|
-
if (!opts.force &&
|
|
914
|
+
if (!opts.force && import_node_fs7.default.existsSync(dest)) {
|
|
792
915
|
try {
|
|
793
|
-
if (!
|
|
916
|
+
if (!import_node_fs7.default.readFileSync(dest).equals(import_node_fs7.default.readFileSync(src))) {
|
|
794
917
|
skipped.push(entry.storePath);
|
|
795
918
|
continue;
|
|
796
919
|
}
|
|
@@ -800,8 +923,8 @@ var LocalStore = class {
|
|
|
800
923
|
}
|
|
801
924
|
}
|
|
802
925
|
try {
|
|
803
|
-
ensureDir(
|
|
804
|
-
|
|
926
|
+
ensureDir(import_node_path6.default.dirname(dest));
|
|
927
|
+
import_node_fs7.default.copyFileSync(src, dest);
|
|
805
928
|
restored.push(entry.storePath);
|
|
806
929
|
} catch {
|
|
807
930
|
skipped.push(entry.storePath);
|
|
@@ -810,10 +933,10 @@ var LocalStore = class {
|
|
|
810
933
|
return { restored, skipped };
|
|
811
934
|
}
|
|
812
935
|
readHandoff() {
|
|
813
|
-
return readJson(
|
|
936
|
+
return readJson(import_node_path6.default.join(this.dir, HANDOFF_FILE));
|
|
814
937
|
}
|
|
815
938
|
writeHandoff(handoff) {
|
|
816
|
-
writeJson(
|
|
939
|
+
writeJson(import_node_path6.default.join(this.dir, HANDOFF_FILE), handoff);
|
|
817
940
|
}
|
|
818
941
|
};
|
|
819
942
|
|