@dotdotgod/claude-code 0.3.9 → 0.3.10

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/mcp/cli.mjs CHANGED
@@ -1434,7 +1434,7 @@ function parseCommon(argv) {
1434
1434
 
1435
1435
  // packages/cli/src/validate/run.mjs
1436
1436
  import { existsSync as existsSync10, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "node:fs";
1437
- import { basename as basename7, dirname as dirname5, extname as extname4, join as join8, resolve as resolve7 } from "node:path";
1437
+ import { basename as basename7, dirname as dirname6, extname as extname4, join as join8, resolve as resolve8 } from "node:path";
1438
1438
 
1439
1439
  // packages/cli/src/docs/traceability.mjs
1440
1440
  import { existsSync as existsSync5 } from "node:fs";
@@ -1533,7 +1533,8 @@ function stripTraceabilityLinksRegion(content) {
1533
1533
  if (region.status === "present") ranges.push({ start: region.start, end: region.end });
1534
1534
  for (const block of extractDotdotgodTraceabilityBlocks(content)) ranges.push({ start: block.index, end: block.end });
1535
1535
  if (ranges.length === 0) return content;
1536
- return ranges.sort((a, b) => b.start - a.start).reduce((next, range) => `${next.slice(0, range.start)}${next.slice(range.end)}`, content);
1536
+ const stripped = ranges.sort((a, b) => b.start - a.start).reduce((next, range) => `${next.slice(0, range.start)}${next.slice(range.end)}`, content);
1537
+ return stripped.replace(/(?:^[ \t]*\r?\n)*^##[ \t]+Traceability[ \t]*\r?(?:\n(?:[ \t]*\r?\n)*|$)/gm, "");
1537
1538
  }
1538
1539
  function markdownLinkPath(root, file, target) {
1539
1540
  const fromDir = dirname2(file);
@@ -2321,6 +2322,138 @@ function graphSummary(index) {
2321
2322
  return { nodes: graph.nodes.length, edges: graph.edges.length, byType, byRelation };
2322
2323
  }
2323
2324
 
2325
+ // packages/cli/src/validate/references.mjs
2326
+ import { statSync as statSync5 } from "node:fs";
2327
+ import { dirname as dirname5, relative as relative3, resolve as resolve7 } from "node:path";
2328
+ var blank = (text) => text.replace(/[^\n\r]/g, " ");
2329
+ var unescape = (text) => text.replace(/\\([!"#$%&'()*+,\-./:;<=>?@[\]\\^_`{|}~])/g, "$1");
2330
+ var labelKey = (text) => unescape(text).trim().replace(/\s+/g, " ").toLowerCase();
2331
+ var escaped = (text, index) => {
2332
+ let count = 0;
2333
+ while (index > 0 && text[--index] === "\\") count++;
2334
+ return count % 2 === 1;
2335
+ };
2336
+ function visibleContent(content) {
2337
+ const region = findTraceabilityLinksRegion(content);
2338
+ let text = region.status === "present" ? content.slice(0, region.start) + blank(content.slice(region.start, region.end)) + content.slice(region.end) : content;
2339
+ const tokens = /<!--[\s\S]*?(?:-->|(?![\s\S]))|^ {0,3}(`{3,}|~{3,})[^\n]*/gm;
2340
+ let token;
2341
+ while (token = tokens.exec(text)) {
2342
+ let end = tokens.lastIndex;
2343
+ if (token[1]) {
2344
+ const closing = new RegExp(`^ {0,3}${token[1][0]}{${token[1].length},}[ \\t]*\\r?$`, "gm");
2345
+ closing.lastIndex = end + 1;
2346
+ end = closing.exec(text) ? closing.lastIndex : text.length;
2347
+ }
2348
+ text = text.slice(0, token.index) + blank(text.slice(token.index, end)) + text.slice(end);
2349
+ tokens.lastIndex = end;
2350
+ }
2351
+ return text;
2352
+ }
2353
+ function destination(text, start) {
2354
+ let i = start;
2355
+ while (/\s/.test(text[i] ?? "") && i < text.length) i++;
2356
+ const begin = i;
2357
+ if (text[i] === "<") {
2358
+ for (++i; i < text.length; i++) {
2359
+ if (text[i] === ">" && !escaped(text, i)) return { href: text.slice(begin + 1, i), end: i + 1 };
2360
+ if (text[i] === "\n") return null;
2361
+ }
2362
+ return null;
2363
+ }
2364
+ let depth = 0;
2365
+ for (; i < text.length; i++) {
2366
+ if (escaped(text, i)) continue;
2367
+ if (/\s/.test(text[i]) || text[i] === ")" && depth === 0) break;
2368
+ if (text[i] === "(") depth++;
2369
+ if (text[i] === ")") depth--;
2370
+ }
2371
+ return depth === 0 && i > begin ? { href: text.slice(begin, i), end: i } : null;
2372
+ }
2373
+ function extractMemoryReferences(content) {
2374
+ let text = visibleContent(content);
2375
+ const references = [];
2376
+ const code = [];
2377
+ const lineAt = (offset) => content.slice(0, offset).split("\n").length;
2378
+ text = text.replace(/(`+)([\s\S]*?)\1(?!`)/g, (match2, ticks, value, offset) => {
2379
+ if (escaped(text, offset)) return match2;
2380
+ code.push({ href: value.trim(), offset, end: offset + match2.length, kind: "inline-code", line: lineAt(offset) });
2381
+ return blank(match2);
2382
+ });
2383
+ const definitions2 = /* @__PURE__ */ new Map();
2384
+ text = text.replace(/^ {0,3}\[([^\]\n]+)\]:[ \t]*(.*)$/gm, (match2, label, value) => {
2385
+ const parsed = destination(value, 0);
2386
+ if (parsed && !definitions2.has(labelKey(label))) definitions2.set(labelKey(label), parsed.href);
2387
+ return blank(match2);
2388
+ });
2389
+ const linkRanges = [];
2390
+ const labels = /!?\[((?:\\.|[^\]\\\n])*)\]/g;
2391
+ let match;
2392
+ while (match = labels.exec(text)) {
2393
+ if (escaped(text, match.index) || match[0][0] === "!" && escaped(text, match.index + 1)) continue;
2394
+ const after = labels.lastIndex;
2395
+ let end = after;
2396
+ let href;
2397
+ if (text[after] === "(") {
2398
+ const parsed = destination(text, after + 1);
2399
+ if (parsed) {
2400
+ const tail = text.slice(parsed.end).match(/^\s*(?:(?:"[^"\n]*"|'[^'\n]*'|\([^\n)]*\))\s*)?\)/);
2401
+ if (tail) {
2402
+ href = parsed.href;
2403
+ end = parsed.end + tail[0].length;
2404
+ }
2405
+ }
2406
+ } else {
2407
+ const suffix = text.slice(after).match(/^\[([^\]\n]*)\]/);
2408
+ const label = suffix?.[1] || match[1];
2409
+ href = definitions2.get(labelKey(label));
2410
+ if (suffix) end += suffix[0].length;
2411
+ }
2412
+ if (href !== void 0) {
2413
+ references.push({ href: unescape(href), kind: "markdown-link", line: lineAt(match.index) });
2414
+ linkRanges.push([match.index, end]);
2415
+ labels.lastIndex = end;
2416
+ }
2417
+ }
2418
+ for (const item of code) {
2419
+ if (linkRanges.some(([start, end]) => item.offset >= start && item.end <= end)) continue;
2420
+ const path = item.href.split("#")[0];
2421
+ if (!path.includes("/") && !/\.[\w-]+$/.test(path) || /[\s*?<>\[\]{}]/.test(path) || path.endsWith("/")) continue;
2422
+ references.push({ href: item.href, kind: item.kind, line: item.line });
2423
+ }
2424
+ return references;
2425
+ }
2426
+ function sharedLocalReferences(content, root, file, config) {
2427
+ const source = resolveMemoryArea(relative3(root, file).split("\\").join("/"), config);
2428
+ if (source?.scope !== "shared") return [];
2429
+ const errors = [];
2430
+ for (const item of extractMemoryReferences(content)) {
2431
+ if (/^(?:[a-z][a-z\d+.-]*:|\/\/|#)/i.test(item.href)) continue;
2432
+ let path;
2433
+ try {
2434
+ path = decodeURIComponent(item.href.split(/[?#]/)[0]);
2435
+ } catch {
2436
+ continue;
2437
+ }
2438
+ if (!path) continue;
2439
+ const base = item.kind === "markdown-link" || /^\.\.?\//.test(path) ? dirname5(file) : root;
2440
+ const target = relative3(root, resolve7(base, path)).split("\\").join("/");
2441
+ if (target === ".." || target.startsWith("../")) continue;
2442
+ if (item.kind === "inline-code") {
2443
+ const configuredDirectory = (config.areas ?? []).some((area3) => (area3.paths ?? []).some((pattern) => pattern === `${target}/**`));
2444
+ let directory = configuredDirectory;
2445
+ try {
2446
+ directory ||= statSync5(resolve7(root, target)).isDirectory();
2447
+ } catch {
2448
+ }
2449
+ if (directory) continue;
2450
+ }
2451
+ const area2 = resolveMemoryArea(target, config);
2452
+ if (area2?.scope === "local") errors.push({ ...item, target, sourceArea: source.id, targetArea: area2.id });
2453
+ }
2454
+ return errors;
2455
+ }
2456
+
2324
2457
  // packages/cli/src/validate/run.mjs
2325
2458
  function runValidate(argv) {
2326
2459
  const options = { root: ".", includeLocalMemory: false, checkIndex: false, maxLines: null, maxChars: null, linkCheck: true, json: false };
@@ -2335,7 +2468,7 @@ function runValidate(argv) {
2335
2468
  else if (!arg.startsWith("-")) options.root = arg;
2336
2469
  else usage(`Unknown option: ${arg}`, "validate");
2337
2470
  }
2338
- const root = resolve7(options.root);
2471
+ const root = resolve8(options.root);
2339
2472
  const memoryConfig = readMemoryConfig(root);
2340
2473
  const documentationRoot = memoryConfig.documentation?.root ?? "docs";
2341
2474
  const docs = join8(root, documentationRoot);
@@ -2399,7 +2532,7 @@ README.md directory indexes, clear HTTP-status filenames such as RESPONSE_200_OK
2399
2532
  walk(docs);
2400
2533
  const dirSiblingMap = /* @__PURE__ */ new Map();
2401
2534
  for (const f of markdownFiles) {
2402
- const dir = dirname5(f);
2535
+ const dir = dirname6(f);
2403
2536
  if (!dirSiblingMap.has(dir)) dirSiblingMap.set(dir, []);
2404
2537
  dirSiblingMap.get(dir).push(basename7(f));
2405
2538
  }
@@ -2415,7 +2548,7 @@ README.md directory indexes, clear HTTP-status filenames such as RESPONSE_200_OK
2415
2548
  const fp = validationPolicy.markdown?.filename ?? {};
2416
2549
  const allow = fp.allow ?? [];
2417
2550
  if (!allow.some((p) => matchPathPattern(rootRel, p)) && (fp.warnNumberedSeries ?? true)) {
2418
- const siblings = dirSiblingMap.get(dirname5(file)) ?? [];
2551
+ const siblings = dirSiblingMap.get(dirname6(file)) ?? [];
2419
2552
  if (isNumberedSeriesFilename(name, siblings)) {
2420
2553
  const heading = extractFirstHeading(content);
2421
2554
  const suggestion = suggestFilenameFromHeading(heading);
@@ -2457,18 +2590,23 @@ ${traceabilityExample()}`, block.line);
2457
2590
  }
2458
2591
  }
2459
2592
  const byDir = /* @__PURE__ */ new Map();
2460
- for (const file of markdownFiles) byDir.set(dirname5(file), [...byDir.get(dirname5(file)) ?? [], file]);
2593
+ for (const file of markdownFiles) byDir.set(dirname6(file), [...byDir.get(dirname6(file)) ?? [], file]);
2461
2594
  for (const [dir, files] of byDir) {
2462
2595
  if (files.length > 1 && !files.some((file) => basename7(file) === "README.md")) addError(dir, "MISSING_README", "Directory with multiple markdown files must include README.md", null, "add a README.md in this directory that indexes the important markdown files and their purpose.");
2463
2596
  }
2597
+ for (const [file, content] of fileCache) {
2598
+ for (const item of sharedLocalReferences(content, root, file, memoryConfig)) {
2599
+ addError(file, "SHARED_LOCAL_MEMORY_REFERENCE", `${item.kind} from shared area "${item.sourceArea}" targets local area "${item.targetArea}": ${item.target}`, item.line, "replace the local reference with shared durable documentation, or remove the dependency. Inline-code directory, glob, and placeholder usage examples are allowed.");
2600
+ }
2601
+ }
2464
2602
  if (options.linkCheck) {
2465
2603
  for (const [file, content] of fileCache) {
2466
- const fileDir = dirname5(file);
2604
+ const fileDir = dirname6(file);
2467
2605
  for (const { href, line } of extractLinks(content)) {
2468
2606
  const hashIndex = href.indexOf("#");
2469
2607
  const pathPart = hashIndex === -1 ? href : href.slice(0, hashIndex);
2470
2608
  const anchor = hashIndex === -1 ? "" : href.slice(hashIndex + 1);
2471
- const target = pathPart ? resolve7(fileDir, pathPart) : file;
2609
+ const target = pathPart ? resolve8(fileDir, pathPart) : file;
2472
2610
  if (pathPart && !existsSync10(target)) {
2473
2611
  addError(file, "BROKEN_LINK", `Local link target does not exist: ${pathPart}`, line, "update the link target to an existing local file, create the intended file, or remove the stale link.");
2474
2612
  continue;
@@ -2538,8 +2676,8 @@ Prompt: ${error.prompt}` : ""}`);
2538
2676
  }
2539
2677
 
2540
2678
  // packages/cli/src/commands/config.mjs
2541
- import { existsSync as existsSync11, statSync as statSync5, writeFileSync as writeFileSync3 } from "node:fs";
2542
- import { join as join9, resolve as resolve8 } from "node:path";
2679
+ import { existsSync as existsSync11, statSync as statSync6, writeFileSync as writeFileSync3 } from "node:fs";
2680
+ import { join as join9, resolve as resolve9 } from "node:path";
2543
2681
  function parseConfigOptions(argv, usageKey = "config") {
2544
2682
  const options = { root: ".", json: false, template: null };
2545
2683
  let rootSet = false;
@@ -2557,7 +2695,7 @@ function parseConfigOptions(argv, usageKey = "config") {
2557
2695
  } else if (!arg.startsWith("-")) usage(`Unexpected argument: ${arg}`, usageKey);
2558
2696
  else usage(`Unknown option: ${arg}`, usageKey);
2559
2697
  }
2560
- options.root = resolve8(options.root);
2698
+ options.root = resolve9(options.root);
2561
2699
  return options;
2562
2700
  }
2563
2701
  function configInitError(options, code, message, path = null) {
@@ -2602,7 +2740,7 @@ function runConfig(argv) {
2602
2740
  if (isInit) {
2603
2741
  if (!existsSync11(options.root)) configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
2604
2742
  try {
2605
- if (!statSync5(options.root).isDirectory()) configInitError(options, "ROOT_NOT_DIRECTORY", `Project root is not a directory: ${options.root}`);
2743
+ if (!statSync6(options.root).isDirectory()) configInitError(options, "ROOT_NOT_DIRECTORY", `Project root is not a directory: ${options.root}`);
2606
2744
  } catch {
2607
2745
  configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
2608
2746
  }
@@ -2633,11 +2771,11 @@ function runConfig(argv) {
2633
2771
  }
2634
2772
 
2635
2773
  // packages/cli/src/commands/query.mjs
2636
- import { resolve as resolve9 } from "node:path";
2774
+ import { resolve as resolve10 } from "node:path";
2637
2775
 
2638
2776
  // packages/cli/src/query/chunks.mjs
2639
2777
  import { readFileSync as readFileSync10, readdirSync as readdirSync3 } from "node:fs";
2640
- import { join as join10, relative as relative3 } from "node:path";
2778
+ import { join as join10, relative as relative4 } from "node:path";
2641
2779
  import { createHash as createHash3 } from "node:crypto";
2642
2780
  var MAX_CHUNK_CHARS = 1600;
2643
2781
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
@@ -2658,7 +2796,7 @@ function collectDocumentationMarkdown(root, exclude = ["docs/plan", "docs/archiv
2658
2796
  for (const entry of entries) {
2659
2797
  if (entry.name.startsWith(".")) continue;
2660
2798
  const absolute = join10(directory, entry.name);
2661
- const path = relative3(root, absolute).replaceAll("\\", "/");
2799
+ const path = relative4(root, absolute).replaceAll("\\", "/");
2662
2800
  if (isSecretLikePathPattern(path) || exclude.some((pattern) => matchPathPattern(path, pattern) || path === pattern || path.startsWith(`${pattern}/`))) continue;
2663
2801
  if (entry.isDirectory()) {
2664
2802
  if (!SKIPPED_DIRECTORIES.has(entry.name)) walk(absolute);
@@ -2951,7 +3089,7 @@ function parseQueryOptions(argv) {
2951
3089
  options.limit = value;
2952
3090
  } else if (arg.startsWith("-")) usage(`Unknown option: ${arg}`, "query");
2953
3091
  else if (!rootSet) {
2954
- options.root = resolve9(arg);
3092
+ options.root = resolve10(arg);
2955
3093
  rootSet = true;
2956
3094
  } else queryParts.push(arg);
2957
3095
  }
@@ -3062,7 +3200,7 @@ async function runQuery(argv) {
3062
3200
  }
3063
3201
 
3064
3202
  // packages/cli/src/commands/embedding.mjs
3065
- import { resolve as resolve10 } from "node:path";
3203
+ import { resolve as resolve11 } from "node:path";
3066
3204
  function parse(argv) {
3067
3205
  const [action2 = "status", ...rest] = argv;
3068
3206
  let root = ".";
@@ -3074,7 +3212,7 @@ function parse(argv) {
3074
3212
  else if (!arg.startsWith("-") && root === ".") root = arg;
3075
3213
  else usage(`Unknown embedding option: ${arg}`, `embedding ${action2}`);
3076
3214
  }
3077
- return { action: action2, root: resolve10(root), json, confirm };
3215
+ return { action: action2, root: resolve11(root), json, confirm };
3078
3216
  }
3079
3217
  function runEmbedding(argv, options = {}) {
3080
3218
  const parsed = parse(argv);
@@ -3094,11 +3232,11 @@ function runEmbedding(argv, options = {}) {
3094
3232
  }
3095
3233
 
3096
3234
  // packages/cli/src/commands/map.mjs
3097
- import { resolve as resolve12 } from "node:path";
3235
+ import { resolve as resolve13 } from "node:path";
3098
3236
 
3099
3237
  // packages/cli/src/memory/documentation-map.mjs
3100
- import { existsSync as existsSync14, readdirSync as readdirSync4, statSync as statSync6 } from "node:fs";
3101
- import { join as join13, resolve as resolve11 } from "node:path";
3238
+ import { existsSync as existsSync14, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
3239
+ import { join as join13, resolve as resolve12 } from "node:path";
3102
3240
  var SKIPPED_DIRECTORIES2 = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
3103
3241
  function node() {
3104
3242
  return { directories: /* @__PURE__ */ new Map(), files: [] };
@@ -3178,10 +3316,10 @@ function discoverMarkdown(root, documentationRoot, exclude) {
3178
3316
  return paths.sort();
3179
3317
  }
3180
3318
  function buildDocumentationMap(projectRoot = ".", { depth = 5 } = {}) {
3181
- const root = resolve11(projectRoot);
3319
+ const root = resolve12(projectRoot);
3182
3320
  if (!existsSync14(root)) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
3183
3321
  try {
3184
- if (!statSync6(root).isDirectory()) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
3322
+ if (!statSync7(root).isDirectory()) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
3185
3323
  } catch {
3186
3324
  return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
3187
3325
  }
@@ -3227,7 +3365,7 @@ function parseMapOptions(argv) {
3227
3365
  } else if (!arg.startsWith("-")) fail(options.json, "UNEXPECTED_ARGUMENT", `Unexpected argument: ${arg}`);
3228
3366
  else fail(options.json, "UNKNOWN_OPTION", `Unknown option: ${arg}`);
3229
3367
  }
3230
- options.root = resolve12(options.root);
3368
+ options.root = resolve13(options.root);
3231
3369
  return options;
3232
3370
  }
3233
3371
  function runMap(argv) {
@@ -3239,7 +3377,7 @@ function runMap(argv) {
3239
3377
  }
3240
3378
 
3241
3379
  // packages/cli/src/reference/resolve.mjs
3242
- import { resolve as resolve14 } from "node:path";
3380
+ import { resolve as resolve15 } from "node:path";
3243
3381
 
3244
3382
  // node_modules/.pnpm/leiden-ts@0.1.0/node_modules/leiden-ts/dist/index.js
3245
3383
  var GraphValidationError = class extends Error {
@@ -4475,8 +4613,8 @@ function buildCommunities(index, limits = {}) {
4475
4613
  byCommunity.get(communityId).push(projection.durable[index2]);
4476
4614
  });
4477
4615
  const communities = [...byCommunity.entries()].map(([communityId, nodes]) => {
4478
- const labelKey = labelForLeidenCommunity(nodes, config);
4479
- const community = makeCommunity(`leiden-${communityId}`, communityLabel(labelKey));
4616
+ const labelKey2 = labelForLeidenCommunity(nodes, config);
4617
+ const community = makeCommunity(`leiden-${communityId}`, communityLabel(labelKey2));
4480
4618
  for (const node2 of nodes.sort((a, b) => a.id.localeCompare(b.id))) addCommunityDetails(community, node2, itemLimit, config);
4481
4619
  return community;
4482
4620
  });
@@ -4611,14 +4749,14 @@ function compareImpactItems(seeds) {
4611
4749
 
4612
4750
  // packages/cli/src/impact/vector-profile.mjs
4613
4751
  import { lstatSync, readFileSync as readFileSync13, realpathSync } from "node:fs";
4614
- import { extname as extname5, relative as relative4, resolve as resolve13, sep } from "node:path";
4752
+ import { extname as extname5, relative as relative5, resolve as resolve14, sep } from "node:path";
4615
4753
  var MAX_VECTOR_PROFILE_FILE_BYTES = 64 * 1024;
4616
4754
  var MAX_VECTOR_PROFILE_CHARS = 4e3;
4617
4755
  var MAX_VECTOR_PROFILE_METADATA_ITEMS = 20;
4618
4756
  var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([".c", ".cc", ".cpp", ".css", ".go", ".h", ".hpp", ".html", ".java", ".js", ".json", ".jsx", ".md", ".mjs", ".py", ".rb", ".rs", ".sh", ".ts", ".tsx", ".txt", ".yaml", ".yml"]);
4619
4757
  var GENERATED_SEGMENTS = /* @__PURE__ */ new Set([".dotdotgod", "build", "coverage", "dist", "node_modules"]);
4620
4758
  function insideRoot(root, absolute) {
4621
- const normalizedRoot = resolve13(root);
4759
+ const normalizedRoot = resolve14(root);
4622
4760
  return absolute === normalizedRoot || absolute.startsWith(`${normalizedRoot}${sep}`);
4623
4761
  }
4624
4762
  function normalizeChangedPath(path) {
@@ -4629,17 +4767,17 @@ function normalizeChangedPath(path) {
4629
4767
  function canonicalizeChangedPath(root, path) {
4630
4768
  const normalized = normalizeChangedPath(path);
4631
4769
  if (!normalized) return null;
4632
- const rootAbsolute = resolve13(root);
4633
- const absolute = resolve13(rootAbsolute, normalized);
4770
+ const rootAbsolute = resolve14(root);
4771
+ const absolute = resolve14(rootAbsolute, normalized);
4634
4772
  if (!insideRoot(rootAbsolute, absolute)) return normalized;
4635
4773
  try {
4636
4774
  const canonicalRoot = realpathSync(rootAbsolute);
4637
4775
  const canonicalAbsolute = realpathSync(absolute);
4638
4776
  if (!insideRoot(canonicalRoot, canonicalAbsolute)) return normalized;
4639
- const canonical = relative4(canonicalRoot, canonicalAbsolute).replaceAll("\\", "/");
4777
+ const canonical = relative5(canonicalRoot, canonicalAbsolute).replaceAll("\\", "/");
4640
4778
  return canonical || normalized;
4641
4779
  } catch {
4642
- return relative4(rootAbsolute, absolute).replaceAll("\\", "/") || normalized;
4780
+ return relative5(rootAbsolute, absolute).replaceAll("\\", "/") || normalized;
4643
4781
  }
4644
4782
  }
4645
4783
  function unsafeProfilePath(path) {
@@ -4661,11 +4799,11 @@ function graphMetadata(graph, seedId) {
4661
4799
  function safeTextPrefix(root, path) {
4662
4800
  if (unsafeProfilePath(path) || !TEXT_EXTENSIONS.has(extname5(path).toLowerCase())) return "";
4663
4801
  try {
4664
- const absolute = resolve13(root, path);
4802
+ const absolute = resolve14(root, path);
4665
4803
  if (!insideRoot(root, absolute)) return "";
4666
4804
  const canonicalAbsolute = realpathSync(absolute);
4667
4805
  if (!insideRoot(realpathSync(root), canonicalAbsolute)) return "";
4668
- const canonicalPath = relative4(realpathSync(root), canonicalAbsolute).replaceAll("\\", "/");
4806
+ const canonicalPath = relative5(realpathSync(root), canonicalAbsolute).replaceAll("\\", "/");
4669
4807
  if (canonicalPath !== path || unsafeProfilePath(canonicalPath) || !TEXT_EXTENSIONS.has(extname5(canonicalPath).toLowerCase())) return "";
4670
4808
  const stats = lstatSync(canonicalAbsolute);
4671
4809
  if (!stats.isFile() || stats.isSymbolicLink() || stats.size > MAX_VECTOR_PROFILE_FILE_BYTES) return "";
@@ -4881,7 +5019,7 @@ function buildCompactImpactReport(impact, limits = {}) {
4881
5019
  }
4882
5020
 
4883
5021
  // packages/cli/src/reference/extract.mjs
4884
- import { basename as basename8, dirname as dirname6, extname as extname6 } from "node:path";
5022
+ import { basename as basename8, dirname as dirname7, extname as extname6 } from "node:path";
4885
5023
  var DEFAULT_REFERENCE_LIMIT = 5;
4886
5024
  function extractBracketReferences(prompt = "") {
4887
5025
  const refs = [];
@@ -4932,7 +5070,7 @@ function aliasEntriesForPath(path = "") {
4932
5070
  const parts = withoutMd.split("/").filter(Boolean);
4933
5071
  for (let i = 0; i < parts.length; i += 1) entries.push({ alias: parts.slice(i).join("/"), kind: "path-suffix" });
4934
5072
  if (base === "README.md") {
4935
- const dir = dirname6(path).replace(/\\/g, "/");
5073
+ const dir = dirname7(path).replace(/\\/g, "/");
4936
5074
  entries.push({ alias: basename8(dir), kind: "path" });
4937
5075
  }
4938
5076
  for (const alias of [path, withoutMd, base, stem]) entries.push({ alias, kind: "path" });
@@ -4986,8 +5124,8 @@ function extractFuzzyReferences(prompt = "", index = null, options = {}) {
4986
5124
  }
4987
5125
  }
4988
5126
  for (const { alias } of [...aliasByKey.values()].sort((a, b) => b.priority - a.priority || b.alias.length - a.alias.length)) {
4989
- const escaped = alias.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "[\\s_-]+");
4990
- const pattern = new RegExp(`(^|[^a-z0-9])${escaped}([^a-z0-9]|$)`, "i");
5127
+ const escaped2 = alias.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "[\\s_-]+");
5128
+ const pattern = new RegExp(`(^|[^a-z0-9])${escaped2}([^a-z0-9]|$)`, "i");
4991
5129
  if (pattern.test(lower)) add(alias, "known_alias", "medium");
4992
5130
  if (refs.length >= (options.maxFuzzyRefs ?? 5)) break;
4993
5131
  }
@@ -5062,7 +5200,7 @@ function parseReferenceOptions(argv, command) {
5062
5200
  else filtered.push(arg);
5063
5201
  }
5064
5202
  const operands = filtered.filter((arg) => !arg.startsWith("-"));
5065
- return { ...options, root: resolve14(operands[0] ?? "."), json: filtered.includes("--json"), rootArgv: operands.slice(1) };
5203
+ return { ...options, root: resolve15(operands[0] ?? "."), json: filtered.includes("--json"), rootArgv: operands.slice(1) };
5066
5204
  }
5067
5205
  function formatReferenceOutput(payload) {
5068
5206
  const refreshNote = payload.metadata.cacheRefreshed ? ", refreshed" : "";
@@ -5112,7 +5250,7 @@ function runExpand(argv) {
5112
5250
 
5113
5251
  // packages/cli/src/commands/traceability.mjs
5114
5252
  import { existsSync as existsSync15, readFileSync as readFileSync14, readdirSync as readdirSync5, writeFileSync as writeFileSync6 } from "node:fs";
5115
- import { join as join14, resolve as resolve15 } from "node:path";
5253
+ import { join as join14, resolve as resolve16 } from "node:path";
5116
5254
  function collectDocsMarkdownFiles(root) {
5117
5255
  const docs = join14(root, "docs");
5118
5256
  const files = [];
@@ -5144,7 +5282,7 @@ function parseTraceabilityOptions(argv) {
5144
5282
  }
5145
5283
  if (options.check && options.write) usage("Choose only one traceability links mode: --check or --write.", "traceability links");
5146
5284
  options.check = options.check || !options.write;
5147
- options.root = resolve15(options.root);
5285
+ options.root = resolve16(options.root);
5148
5286
  return options;
5149
5287
  }
5150
5288
  function runTraceability(argv) {
package/mcp/server.mjs CHANGED
@@ -23157,7 +23157,7 @@ function markdownTree(root2, maxDepth = 5) {
23157
23157
  walk(docs, 1);
23158
23158
  return output;
23159
23159
  }
23160
- async function cliJson(args, cwd) {
23160
+ async function cliText(args, cwd) {
23161
23161
  return await new Promise((resolvePromise, reject) => {
23162
23162
  const child = spawn2(process.execPath, [cli, ...args], { cwd, stdio: ["ignore", "pipe", "pipe"] });
23163
23163
  const stdout = [];
@@ -23168,14 +23168,18 @@ async function cliJson(args, cwd) {
23168
23168
  child.once("close", (code) => {
23169
23169
  const out = Buffer.concat(stdout).toString("utf8");
23170
23170
  if (code !== 0) return reject(new Error(Buffer.concat(stderr).toString("utf8") || out || `dotdotgod exited ${code}`));
23171
- try {
23172
- resolvePromise(JSON.parse(out));
23173
- } catch {
23174
- reject(new Error(`Expected JSON from dotdotgod: ${out.slice(0, 500)}`));
23175
- }
23171
+ resolvePromise(out);
23176
23172
  });
23177
23173
  });
23178
23174
  }
23175
+ async function cliJson(args, cwd) {
23176
+ const out = await cliText(args, cwd);
23177
+ try {
23178
+ return JSON.parse(out);
23179
+ } catch {
23180
+ throw new Error(`Expected JSON from dotdotgod: ${out.slice(0, 500)}`);
23181
+ }
23182
+ }
23179
23183
  async function projectLoad(input) {
23180
23184
  const root2 = resolve5(input.root || process.cwd());
23181
23185
  const focus = input.focus?.trim() ?? "";
@@ -23217,8 +23221,8 @@ async function projectImpact(input) {
23217
23221
  if (paths.length > 20) throw new Error("paths is limited to 20 entries per call");
23218
23222
  const args = ["graph", "impact", root2];
23219
23223
  for (const path of paths) args.push("--changed", path);
23220
- args.push("--json");
23221
- return await cliJson(args, root2);
23224
+ args.push("--compact");
23225
+ return { ok: true, summary: (await cliText(args, root2)).trim() };
23222
23226
  }
23223
23227
  async function projectInitialize(input) {
23224
23228
  const root2 = resolve5(input.root || process.cwd());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dotdotgod/claude-code",
3
- "version": "0.3.9",
3
+ "version": "0.3.10",
4
4
  "description": "Claude Code adapter for dotdotgod project memory workflows.",
5
5
  "type": "module",
6
6
  "license": "Elastic-2.0",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "esbuild": "0.27.3",
28
- "@dotdotgod/context": "0.3.9"
28
+ "@dotdotgod/context": "0.3.10"
29
29
  },
30
30
  "files": [
31
31
  ".claude-plugin",
@@ -10,20 +10,31 @@ version: 1.0.0
10
10
 
11
11
  ## Memory-Area Context
12
12
 
13
- Run `dotdotgod config <root> --json` and use the resolved `config.areas`. Match the target path against the ordered areas and use the first match. If the CLI is unavailable, continue from the target document, nearest README, and direct links.
13
+ Use `dotdotgod config <root> --json` (or the source-checkout CLI equivalent) to obtain resolved `config.areas`; reuse current resolved settings already in context when the project/config has not changed. Normalize the target to a repository-relative path. Check areas in order, applying each area's `excludePaths` before selecting the first matching `paths` entry. Keep the matched area's relevant guidance in context rather than repeatedly injecting the entire configuration. If no area matches, use the document's purpose and repository conventions without inventing metadata.
14
14
 
15
15
  ## Goal
16
16
 
17
17
  Make project documentation easier to understand and act on while preserving established behavior, decisions, task state, historical meaning, and traceability.
18
18
 
19
+ ## Editing Checklist
20
+
21
+ Adapted from [Google Technical Writing One](https://developers.google.com/tech-writing/one); these principles supplement repository policy. Apply them directly without requiring runtime web access.
22
+
23
+ - **Audience:** distinguish what readers already know from what they need to learn or do. Add only evidence-backed context that closes that gap.
24
+ - **Terminology:** use one consistent name per concept; explain unfamiliar terms and abbreviations when readers need them. Preserve exact API names and identifiers.
25
+ - **Sentences:** use specific verbs and make responsibility clear. Prefer active voice when it helps; retain passive voice when appropriate and never invent an unknown actor. Keep one main idea per sentence while preserving conditions, exceptions, and logical relationships.
26
+ - **Paragraphs:** focus each paragraph on one topic and make its main point clear near the beginning. Remove sentences that do not support that topic or relocate them within the approved scope.
27
+ - **Lists and tables:** use numbered lists when order matters, bullets otherwise, and parallel phrasing for comparable items. Use tables when readers need to compare the same attributes across items, not merely to decorate prose.
28
+ - **Language:** apply these principles idiomatically in the document's language. Do not impose English word order, a passive-voice ban, or sentence-length quotas on Korean or other languages.
29
+
19
30
  ## Workflow
20
31
 
21
- 1. Confirm the target document and requested outcome. Use its matched memory-area metadata when available: `clarify` guidance first, then `description`, `label`, and `role`.
32
+ 1. Confirm the target, audience, requested outcome, and edit scope. Diagnose ambiguity, repetition, or missing information before editing; small changes need no separate diagnostic report. Use matched memory-area metadata when available: `clarify` guidance first, then `description`, `label`, and `role`.
22
33
  2. Read only the context needed to preserve meaning: the nearest README, directly linked documents, relevant query results, and targeted history when a past decision matters.
23
- 3. Clarify purpose, next actions, terms, ownership, headings, links, and runnable examples. Keep exact commands, paths, package names, API names, requirements, and behavior claims unless verified evidence supports a change.
24
- 4. Prefer direct affirmative statements. Use “X is not Y; it is Z” only when that contrast resolves a likely ambiguity. Remove repeated framing, indirect wording, mixed responsibilities, and background that obscures the document's job.
25
- 5. Follow repository-local generated markers and canonical-source instructions. For dotdotgod traceability, edit the fenced `json dotdotgod` block as the canonical mapping and use `dotdotgod traceability links <root> --write` to refresh generated links.
34
+ 3. Apply criteria suited to the document: README navigation and starting points; spec conditions, behavior, and exceptions; test procedures and pass criteria; architecture boundaries, rationale, and constraints; plan ordering, dependencies, and completion criteria; archive decisions and outcomes in their historical context. Repository-specific guidance takes priority. Clarify terms, ownership, headings, links, and runnable examples. Preserve commands, paths, package/API names, requirement strength, conditions, exceptions, unresolved questions, limitations, and evidence unless verified sources support a change. Do not invent missing facts.
35
+ 4. Prefer direct affirmative statements. Remove repeated framing, indirect wording, mixed responsibilities, and irrelevant background. Remove negative or absence statements only when deleting them leaves the reader's understanding, actions, and decisions unchanged. Rewrite redundant contrasts affirmatively: “It is not automatic; the user starts it” becomes “The user starts it.” Preserve meaningful prohibitions, unsupported cases, exceptions, security constraints, and unverified status. Use “X is not Y; it is Z” when the contrast resolves a likely ambiguity. Accuracy takes priority over brevity.
36
+ 5. Keep edits within the agreed scope. Confirm scope before splitting or moving files, changing meaning, or editing additional documents outside that scope. Update the nearest README when approved structural changes affect navigation. Follow generated markers and canonical-source instructions. For dotdotgod traceability, edit the fenced `json dotdotgod` block as the canonical mapping. Inspect `dotdotgod traceability links <root> --check --json` before using `--write`; the root-wide write can change unrelated documents, so obtain approval if its affected files exceed the agreed scope. Apply ordinary reference policy: shared documents must not depend on concrete local file paths in inline code or Markdown links; inline-code directory, glob, and placeholder usage examples remain allowed. Preserve traceability's separate stricter local-target prohibition.
26
37
  6. Ask for a decision when the requested clarification would change established meaning or when current sources conflict.
27
- 7. Run verification that matches the changed surface. Use documentation and traceability checks for ordinary docs; add generation checks, focused package tests, dry-runs, or workspace verification when shared resources or product behavior are affected.
38
+ 7. Run verification that matches the changed surface. Use documentation and traceability checks for ordinary docs; add generation checks, focused package tests, dry-runs, or workspace verification when shared resources or product behavior are affected. Report briefly: what became clearer; important meaning preserved and unresolved questions; checks run and checks not run. Do not equate shorter text with improved accuracy.
28
39
 
29
40
  When CLI-backed routing is unavailable, continue from the target document, nearest README, direct links, and repository conventions.