@harness-engineering/cli 2.0.0 → 2.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.
Files changed (95) hide show
  1. package/dist/agents/skills/claude-code/harness-compound/SKILL.md +153 -0
  2. package/dist/agents/skills/claude-code/harness-compound/skill.yaml +60 -0
  3. package/dist/agents/skills/claude-code/harness-knowledge-pipeline/SKILL.md +12 -1
  4. package/dist/agents/skills/claude-code/harness-observability/SKILL.md +2 -0
  5. package/dist/agents/skills/claude-code/harness-pulse/SKILL.md +138 -0
  6. package/dist/agents/skills/claude-code/harness-pulse/references/interview.md +77 -0
  7. package/dist/agents/skills/claude-code/harness-pulse/skill.yaml +61 -0
  8. package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +25 -1
  9. package/dist/agents/skills/codex/harness-compound/SKILL.md +153 -0
  10. package/dist/agents/skills/codex/harness-compound/skill.yaml +60 -0
  11. package/dist/agents/skills/codex/harness-knowledge-pipeline/SKILL.md +12 -1
  12. package/dist/agents/skills/codex/harness-observability/SKILL.md +2 -0
  13. package/dist/agents/skills/codex/harness-pulse/SKILL.md +138 -0
  14. package/dist/agents/skills/codex/harness-pulse/references/interview.md +77 -0
  15. package/dist/agents/skills/codex/harness-pulse/skill.yaml +61 -0
  16. package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +25 -1
  17. package/dist/agents/skills/cursor/harness-compound/SKILL.md +153 -0
  18. package/dist/agents/skills/cursor/harness-compound/skill.yaml +60 -0
  19. package/dist/agents/skills/cursor/harness-knowledge-pipeline/SKILL.md +12 -1
  20. package/dist/agents/skills/cursor/harness-observability/SKILL.md +2 -0
  21. package/dist/agents/skills/cursor/harness-pulse/SKILL.md +138 -0
  22. package/dist/agents/skills/cursor/harness-pulse/references/interview.md +77 -0
  23. package/dist/agents/skills/cursor/harness-pulse/skill.yaml +61 -0
  24. package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +25 -1
  25. package/dist/agents/skills/gemini-cli/harness-compound/SKILL.md +153 -0
  26. package/dist/agents/skills/gemini-cli/harness-compound/skill.yaml +60 -0
  27. package/dist/agents/skills/gemini-cli/harness-knowledge-pipeline/SKILL.md +12 -1
  28. package/dist/agents/skills/gemini-cli/harness-observability/SKILL.md +2 -0
  29. package/dist/agents/skills/gemini-cli/harness-pulse/SKILL.md +138 -0
  30. package/dist/agents/skills/gemini-cli/harness-pulse/references/interview.md +77 -0
  31. package/dist/agents/skills/gemini-cli/harness-pulse/skill.yaml +61 -0
  32. package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +25 -1
  33. package/dist/agents/skills/package.json +1 -0
  34. package/dist/agents/skills/tests/fixtures/harness-compound/bug-track-fixture/input.json +10 -0
  35. package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/docs/solutions/bug-track/integration-issues/stalled-lease-cleanup.md +28 -0
  36. package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/expected.json +4 -0
  37. package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/input.json +10 -0
  38. package/dist/agents/skills/tests/fixtures/harness-compound/knowledge-track-fixture/input.json +10 -0
  39. package/dist/agents/skills/tests/harness-compound.test.ts +86 -0
  40. package/dist/{agents-md-6SAMHE7H.js → agents-md-L4X4MOWT.js} +3 -3
  41. package/dist/{architecture-5YR52NSV.js → architecture-WJRIYQ45.js} +4 -4
  42. package/dist/{assess-project-5X7TYQFM.js → assess-project-PHC2HO3V.js} +1 -1
  43. package/dist/bin/harness-mcp.js +16 -15
  44. package/dist/bin/harness.js +26 -25
  45. package/dist/{check-phase-gate-PLV2VDOL.js → check-phase-gate-646KY67H.js} +5 -4
  46. package/dist/{chunk-ZYLBIDLE.js → chunk-224XKVLP.js} +1 -1
  47. package/dist/{chunk-ELJ2MXMO.js → chunk-2QPHCR22.js} +9 -9
  48. package/dist/{chunk-SITVRO7K.js → chunk-2ZURBWEV.js} +9 -9
  49. package/dist/{chunk-2IBXUG6J.js → chunk-36X7TRUI.js} +2 -2
  50. package/dist/{chunk-6L2BU4BP.js → chunk-4HKVZO7R.js} +1 -1
  51. package/dist/{chunk-74YADB7T.js → chunk-7XQYOXTB.js} +96 -94
  52. package/dist/{chunk-S2WB4LE7.js → chunk-BMM3GXV6.js} +384 -28
  53. package/dist/{chunk-BYS3TQOO.js → chunk-CPYL6LJR.js} +1 -1
  54. package/dist/{chunk-QDRBWVVY.js → chunk-E7W3YOXL.js} +2 -2
  55. package/dist/{chunk-MTPHNOJW.js → chunk-FNCJ3UCU.js} +507 -244
  56. package/dist/{chunk-RYDPFIJY.js → chunk-JPWICLXQ.js} +1 -1
  57. package/dist/{chunk-RFYF7TJS.js → chunk-K4WRQTEF.js} +1835 -805
  58. package/dist/{chunk-DXGI7DL3.js → chunk-KTWKW6KJ.js} +1 -1
  59. package/dist/{chunk-JNKALGRM.js → chunk-KV7A5OZT.js} +5 -5
  60. package/dist/{chunk-KJD4J54I.js → chunk-LO7ZLV7S.js} +6 -1
  61. package/dist/{chunk-XHOGGZS7.js → chunk-QTUPHTTK.js} +1 -1
  62. package/dist/{chunk-R2CBBZA6.js → chunk-SVC7THFK.js} +1 -1
  63. package/dist/{chunk-PFM3BVVU.js → chunk-TNZYQTEE.js} +1 -1
  64. package/dist/{chunk-HT7HYYGT.js → chunk-VJQLUS5I.js} +1 -1
  65. package/dist/{chunk-6GXSJGSW.js → chunk-VKDBM3RX.js} +3 -3
  66. package/dist/{chunk-JZST2WZS.js → chunk-W4AFGFFB.js} +3 -3
  67. package/dist/{chunk-YJE23GRZ.js → chunk-YG4ZBGEH.js} +39 -5
  68. package/dist/{chunk-5ME3WG3G.js → chunk-YLOAQZLA.js} +5 -5
  69. package/dist/{chunk-7JQ7HLM4.js → chunk-YLTRFZAT.js} +8 -8
  70. package/dist/{chunk-BFBZK6BC.js → chunk-YWD6WXXM.js} +6 -6
  71. package/dist/chunk-ZCZD7FHU.js +16 -0
  72. package/dist/{ci-workflow-7T2XKB3P.js → ci-workflow-TOS5X527.js} +3 -3
  73. package/dist/{dist-77VNTHWV.js → dist-6RT2AVYU.js} +78 -2
  74. package/dist/{dist-42AYXCAK.js → dist-D27LJR2T.js} +7 -1
  75. package/dist/{docs-MSIJ6SDO.js → docs-HOJMQYDZ.js} +5 -4
  76. package/dist/{engine-F4P5RBE4.js → engine-D6UYH6DB.js} +3 -3
  77. package/dist/{entropy-GSUTIORR.js → entropy-LZ4IDGZR.js} +3 -3
  78. package/dist/{feedback-NST6T4HI.js → feedback-WV7EQVK7.js} +1 -1
  79. package/dist/{generate-agent-definitions-BOUGQIPA.js → generate-agent-definitions-ZHFFS6YG.js} +4 -4
  80. package/dist/glob-helper-FZPPTGJN.js +62 -0
  81. package/dist/{graph-loader-IAUPGLRV.js → graph-loader-5MKHAJJF.js} +1 -1
  82. package/dist/index.d.ts +44 -6
  83. package/dist/index.js +26 -25
  84. package/dist/{loader-6QTJC7IN.js → loader-Z23JTXIZ.js} +3 -3
  85. package/dist/{mcp-QFBGTUX6.js → mcp-QFZDPFNP.js} +16 -15
  86. package/dist/{performance-B2NAAIE5.js → performance-7ATLHTQU.js} +5 -4
  87. package/dist/{review-pipeline-6EYMBS6B.js → review-pipeline-COBGGXHJ.js} +3 -3
  88. package/dist/{runtime-VFZK6BCZ.js → runtime-KCBNQUXK.js} +3 -3
  89. package/dist/{scan-DB6FNYGG.js → scan-BIJYHOE7.js} +2 -1
  90. package/dist/{security-HRNTYHPM.js → security-MAPRIFQT.js} +1 -1
  91. package/dist/templates/orchestrator/harness.orchestrator.md +9 -1
  92. package/dist/{validate-YEMAFWJE.js → validate-74XTQR7Q.js} +4 -4
  93. package/dist/{validate-cross-check-DGHTJFVH.js → validate-cross-check-MI5MZ7MB.js} +3 -3
  94. package/package.json +7 -6
  95. package/dist/glob-helper-VCQXK5XY.js +0 -54
@@ -5,10 +5,12 @@ import {
5
5
  // ../graph/dist/index.mjs
6
6
  import { z } from "zod";
7
7
  import { readFile, writeFile, mkdir, access } from "fs/promises";
8
- import { createWriteStream } from "fs";
8
+ import { createReadStream, createWriteStream } from "fs";
9
+ import { createInterface } from "readline";
9
10
  import { join } from "path";
10
11
  import * as fs from "fs/promises";
11
12
  import * as path from "path";
13
+ import { minimatch } from "minimatch";
12
14
  import { execFile } from "child_process";
13
15
  import { promisify } from "util";
14
16
  import * as path2 from "path";
@@ -17,6 +19,7 @@ import * as path3 from "path";
17
19
  import * as crypto from "crypto";
18
20
  import * as fs3 from "fs/promises";
19
21
  import * as path4 from "path";
22
+ import { z as z2 } from "zod";
20
23
  import * as fs4 from "fs/promises";
21
24
  import * as path5 from "path";
22
25
  import * as fs5 from "fs/promises";
@@ -35,41 +38,101 @@ import * as fs10 from "fs/promises";
35
38
  import * as path11 from "path";
36
39
  import * as fs12 from "fs/promises";
37
40
  import * as path13 from "path";
38
- import { minimatch } from "minimatch";
41
+ import { minimatch as minimatch2 } from "minimatch";
39
42
  import { relative as relative5 } from "path";
40
43
  import * as fs13 from "fs/promises";
41
44
  import * as path14 from "path";
42
- function streamGraphJson(filePath, nodes, edges) {
45
+ function streamGraphNdjson(filePath, nodes, edges) {
43
46
  return new Promise((resolve, reject) => {
44
47
  const stream = createWriteStream(filePath, { encoding: "utf-8" });
45
48
  stream.on("error", reject);
46
- stream.write('{"nodes":[');
47
- for (let i = 0; i < nodes.length; i++) {
48
- if (i > 0) stream.write(",");
49
- stream.write(JSON.stringify(nodes[i]));
49
+ for (const node of nodes) {
50
+ stream.write(JSON.stringify({ kind: "node", ...node }));
51
+ stream.write("\n");
50
52
  }
51
- stream.write('],"edges":[');
52
- for (let i = 0; i < edges.length; i++) {
53
- if (i > 0) stream.write(",");
54
- stream.write(JSON.stringify(edges[i]));
53
+ for (const edge of edges) {
54
+ stream.write(JSON.stringify({ kind: "edge", ...edge }));
55
+ stream.write("\n");
55
56
  }
56
- stream.write("]}");
57
57
  stream.end(() => resolve());
58
58
  });
59
59
  }
60
+ function countNodesByType(nodes) {
61
+ const counts = {};
62
+ for (const node of nodes) {
63
+ counts[node.type] = (counts[node.type] ?? 0) + 1;
64
+ }
65
+ return counts;
66
+ }
60
67
  async function saveGraph(dirPath, nodes, edges) {
61
68
  await mkdir(dirPath, { recursive: true });
62
69
  const metadata = {
63
70
  schemaVersion: CURRENT_SCHEMA_VERSION,
64
71
  lastScanTimestamp: (/* @__PURE__ */ new Date()).toISOString(),
65
72
  nodeCount: nodes.length,
66
- edgeCount: edges.length
73
+ edgeCount: edges.length,
74
+ nodesByType: countNodesByType(nodes)
67
75
  };
68
76
  await Promise.all([
69
- streamGraphJson(join(dirPath, "graph.json"), nodes, edges),
77
+ streamGraphNdjson(join(dirPath, "graph.json"), nodes, edges),
70
78
  writeFile(join(dirPath, "metadata.json"), JSON.stringify(metadata, null, 2), "utf-8")
71
79
  ]);
72
80
  }
81
+ async function loadGraphMetadata(dirPath) {
82
+ const metaPath = join(dirPath, "metadata.json");
83
+ try {
84
+ await access(metaPath);
85
+ } catch {
86
+ return { status: "not_found" };
87
+ }
88
+ let metadata;
89
+ try {
90
+ const content = await readFile(metaPath, "utf-8");
91
+ metadata = JSON.parse(content);
92
+ } catch {
93
+ return { status: "not_found" };
94
+ }
95
+ if (metadata.schemaVersion !== CURRENT_SCHEMA_VERSION) {
96
+ return {
97
+ status: "schema_mismatch",
98
+ found: metadata.schemaVersion,
99
+ expected: CURRENT_SCHEMA_VERSION
100
+ };
101
+ }
102
+ return { status: "loaded", metadata };
103
+ }
104
+ async function streamReadGraph(filePath) {
105
+ const stream = createReadStream(filePath, { encoding: "utf-8" });
106
+ const rl = createInterface({ input: stream, crlfDelay: Infinity });
107
+ const nodes = [];
108
+ const edges = [];
109
+ try {
110
+ for await (const rawLine of rl) {
111
+ const line = rawLine.trim();
112
+ if (line === "") continue;
113
+ const record = JSON.parse(line);
114
+ if (record.kind === "node") {
115
+ const { kind: _kind, ...rest } = record;
116
+ nodes.push(rest);
117
+ } else if (record.kind === "edge") {
118
+ const { kind: _kind, ...rest } = record;
119
+ edges.push(rest);
120
+ }
121
+ }
122
+ } catch (err) {
123
+ if (err instanceof RangeError && /invalid string length/i.test(err.message)) {
124
+ throw new Error(
125
+ `Graph contains a record larger than V8 can hold in a single string (~512 MB). This usually means a node or edge has a multi-hundred-MB content/embedding field. Inspect ${filePath} for the offending record.`,
126
+ { cause: err }
127
+ );
128
+ }
129
+ throw err;
130
+ } finally {
131
+ rl.close();
132
+ stream.close();
133
+ }
134
+ return { nodes, edges };
135
+ }
73
136
  async function loadGraph(dirPath) {
74
137
  const metaPath = join(dirPath, "metadata.json");
75
138
  const graphPath = join(dirPath, "graph.json");
@@ -88,8 +151,8 @@ async function loadGraph(dirPath) {
88
151
  expected: CURRENT_SCHEMA_VERSION
89
152
  };
90
153
  }
91
- const graphContent = await readFile(graphPath, "utf-8");
92
- return { status: "loaded", graph: JSON.parse(graphContent) };
154
+ const graph = await streamReadGraph(graphPath);
155
+ return { status: "loaded", graph };
93
156
  }
94
157
  function safeMerge(target, source) {
95
158
  for (const key of Object.keys(source)) {
@@ -224,6 +287,13 @@ function groupNodesByImpact(nodes, excludeId) {
224
287
  }
225
288
  return { tests, docs, code, other };
226
289
  }
290
+ function resolveSkipDirs(options) {
291
+ const base = options?.skipDirs ? new Set(options.skipDirs) : new Set(DEFAULT_SKIP_DIRS);
292
+ if (options?.additionalSkipDirs) {
293
+ for (const name of options.additionalSkipDirs) base.add(name);
294
+ }
295
+ return base;
296
+ }
227
297
  function isExported(lang, line, name) {
228
298
  if (lang === "go") return /^[A-Z]/.test(name);
229
299
  if (lang === "rust") return /^\s*pub\b/.test(line);
@@ -243,6 +313,44 @@ function isSupportedSourceFile(name) {
243
313
  const ext = name.slice(name.lastIndexOf("."));
244
314
  return SUPPORTED_EXTENSIONS.has(ext);
245
315
  }
316
+ function isExcluded(relPath, patterns) {
317
+ if (patterns.length === 0) return false;
318
+ for (const pattern of patterns) {
319
+ if (minimatch(relPath, pattern, { dot: true, matchBase: false })) return true;
320
+ if (!pattern.includes("/") && minimatch(relPath, pattern, { dot: true, matchBase: true })) {
321
+ return true;
322
+ }
323
+ }
324
+ return false;
325
+ }
326
+ async function readGitignorePatterns(rootDir) {
327
+ let content;
328
+ try {
329
+ content = await fs.readFile(path.join(rootDir, ".gitignore"), "utf-8");
330
+ } catch {
331
+ return [];
332
+ }
333
+ const patterns = [];
334
+ for (const rawLine of content.split(/\r?\n/)) {
335
+ const line = rawLine.trim();
336
+ if (line === "" || line.startsWith("#")) continue;
337
+ if (line.startsWith("!")) continue;
338
+ const rooted = line.startsWith("/");
339
+ const stripped = rooted ? line.slice(1) : line;
340
+ const isDirOnly = stripped.endsWith("/");
341
+ const core = isDirOnly ? stripped.slice(0, -1) : stripped;
342
+ if (core === "") continue;
343
+ if (rooted) {
344
+ patterns.push(isDirOnly ? `${core}/**` : core);
345
+ if (isDirOnly) patterns.push(`${core}/`);
346
+ } else {
347
+ patterns.push(`**/${core}`);
348
+ patterns.push(`**/${core}/**`);
349
+ if (isDirOnly) patterns.push(`**/${core}/`);
350
+ }
351
+ }
352
+ return patterns;
353
+ }
246
354
  function finalizeCommit(current) {
247
355
  return {
248
356
  hash: current.hash,
@@ -362,6 +470,25 @@ function parseFrontmatter(raw) {
362
470
  body
363
471
  };
364
472
  }
473
+ function parseSolutionFrontmatter(raw) {
474
+ const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
475
+ if (!match) return null;
476
+ const yamlBlock = match[1];
477
+ const body = match[2];
478
+ const frontmatter = {};
479
+ for (const line of yamlBlock.split(/\r?\n/)) {
480
+ const kvMatch = line.match(/^(\w+):\s*(.+)$/);
481
+ if (!kvMatch) continue;
482
+ const key = kvMatch[1];
483
+ const value = kvMatch[2].trim();
484
+ if (value.startsWith("[") && value.endsWith("]")) {
485
+ frontmatter[key] = value.slice(1, -1).split(",").map((s) => s.trim());
486
+ } else {
487
+ frontmatter[key] = value;
488
+ }
489
+ }
490
+ return { frontmatter, body };
491
+ }
365
492
  function detectEarsPattern(text) {
366
493
  const lower = text.toLowerCase();
367
494
  if (/^if\b.+\bthen\b.+\bshall not\b/.test(lower)) return "unwanted";
@@ -1502,7 +1629,7 @@ function parseAntiPatterns(content) {
1502
1629
  }
1503
1630
  return patterns;
1504
1631
  }
1505
- var NODE_TYPES, EDGE_TYPES, OBSERVABILITY_TYPES, CURRENT_SCHEMA_VERSION, NODE_STABILITY, GraphNodeSchema, GraphEdgeSchema, POISONED_KEYS, GraphStore, VectorStore, DEFAULT_TTL_MS, PackedSummaryCache, ContextQL, TEST_TYPES, DOC_TYPES, CODE_TYPES, SKIP_METHOD_NAMES, FUNCTION_PATTERNS, CLASS_PATTERNS, INTERFACE_PATTERNS, METHOD_PATTERNS, GO_METHOD_PATTERN, VARIABLE_PATTERNS, SUPPORTED_EXTENSIONS, SKIP_EXTENSIONS, CodeIngestor, execFileAsync, GitIngestor, TopologicalLinker, CODE_NODE_TYPES, KnowledgeIngestor, BUSINESS_KNOWLEDGE_TYPES, GOVERNS_SOURCE_TYPES, CODE_NODE_TYPES2, MEASURABLE_TYPES, BusinessKnowledgeIngestor, CODE_NODE_TYPES3, DecisionIngestor, REQUIREMENT_SECTIONS, SECTION_HEADING_RE, NUMBERED_ITEM_RE, CODE_NODE_TYPES4, RequirementIngestor, DEFAULT_PATTERNS, DEFAULT_BLOCKLIST, CODE_EXTENSIONS, MermaidParser, D2Parser, PlantUmlParser, DIAGRAM_EXTENSIONS, SKIP_DIRS, DiagramParser, HEURISTIC_PATTERNS, SCANNABLE_TYPES, KnowledgeLinker, StructuralDriftDetector, BUSINESS_NODE_TYPES, KnowledgeStagingAggregator, SKIP_DIRS2, EXT_TO_LANGUAGE, SKIP_EXTENSIONS2, EXTRACTOR_EDGE_TYPE, ExtractionRunner, PATTERNS, TestDescriptionExtractor, EnumConstantExtractor, ValidationRuleExtractor, ApiPathExtractor, ALL_EXTRACTORS, ImageAnalysisExtractor, KNOWLEDGE_NODE_TYPES, SIMILARITY_THRESHOLD, SEVERITY_MAP, ContradictionDetector, KNOWLEDGE_TYPES, CODE_TYPES2, KNOWLEDGE_EDGE_TYPES, CoverageScorer, VALID_BUSINESS_TYPES, DEFAULT_MAX_DOCS, MAX_COLLISION_SUFFIX, KnowledgeDocMaterializer, BUSINESS_NODE_TYPES2, SNAPSHOT_NODE_TYPES, KnowledgePipelineRunner, CODE_NODE_TYPES5, RETRYABLE_STATUSES, SANITIZE_RULES, SyncManager, SUMMARIZE_PROMPT, JiraConnector, SlackConnector, ConfluenceConnector, CIConnector, CONSTRAINT_KEYWORDS, FigmaConnector, MIN_CONTENT_LENGTH, CONCEPT_ITEM_TYPES, MiroConnector, STOP_WORDS, FusionLayer, CODE_NODE_TYPES6, GraphEntropyAdapter, GraphComplexityAdapter, GraphCouplingAdapter, DEFAULT_THRESHOLD, DEFAULT_METRICS, RECOGNIZED_METRICS, GraphAnomalyAdapter, INTENTS, SIGNAL_WEIGHTS, INTENT_SIGNALS, IntentClassifier, INTENT_KEYWORDS, STOP_WORDS2, PASCAL_OR_CAMEL_RE, FILE_PATH_RE, QUOTED_RE, EntityExtractor, EntityResolver, ResponseFormatter, CompositeProbabilityStrategy, DEFAULT_PROBABILITY_FLOOR, DEFAULT_MAX_DEPTH, CascadeSimulator, ENTITY_REQUIRED_INTENTS, classifier, extractor, formatter, PHASE_NODE_TYPES, CODE_NODE_TYPES7, Assembler, GraphConstraintAdapter, DesignIngestor, DesignConstraintAdapter, GraphFeedbackAdapter, DEFAULT_EDGE_TYPES, TaskIndependenceAnalyzer, ConflictPredictor, VERSION;
1632
+ var NODE_TYPES, EDGE_TYPES, OBSERVABILITY_TYPES, CURRENT_SCHEMA_VERSION, NODE_STABILITY, GraphNodeSchema, GraphEdgeSchema, POISONED_KEYS, GraphStore, VectorStore, DEFAULT_TTL_MS, PackedSummaryCache, ContextQL, TEST_TYPES, DOC_TYPES, CODE_TYPES, DEFAULT_SKIP_DIRS, SKIP_METHOD_NAMES, FUNCTION_PATTERNS, CLASS_PATTERNS, INTERFACE_PATTERNS, METHOD_PATTERNS, GO_METHOD_PATTERN, VARIABLE_PATTERNS, SUPPORTED_EXTENSIONS, SKIP_EXTENSIONS, CodeIngestor, execFileAsync, GitIngestor, TopologicalLinker, CODE_NODE_TYPES, KnowledgeIngestor, BUG_TRACK_CATEGORIES, KNOWLEDGE_TRACK_CATEGORIES, ISO_DATE, SolutionBaseSchema, SolutionDocFrontmatterSchema, BUSINESS_KNOWLEDGE_TYPES, GOVERNS_SOURCE_TYPES, CODE_NODE_TYPES2, MEASURABLE_TYPES, BusinessKnowledgeIngestor, CODE_NODE_TYPES3, DecisionIngestor, REQUIREMENT_SECTIONS, SECTION_HEADING_RE, NUMBERED_ITEM_RE, CODE_NODE_TYPES4, RequirementIngestor, DEFAULT_PATTERNS, DEFAULT_BLOCKLIST, CODE_EXTENSIONS, MermaidParser, D2Parser, PlantUmlParser, DIAGRAM_EXTENSIONS, SKIP_DIRS, DiagramParser, HEURISTIC_PATTERNS, SCANNABLE_TYPES, KnowledgeLinker, StructuralDriftDetector, BUSINESS_NODE_TYPES, KnowledgeStagingAggregator, SKIP_DIRS2, EXT_TO_LANGUAGE, SKIP_EXTENSIONS2, EXTRACTOR_EDGE_TYPE, ExtractionRunner, PATTERNS, TestDescriptionExtractor, EnumConstantExtractor, ValidationRuleExtractor, ApiPathExtractor, ALL_EXTRACTORS, ImageAnalysisExtractor, KNOWLEDGE_NODE_TYPES, SIMILARITY_THRESHOLD, SEVERITY_MAP, ContradictionDetector, KNOWLEDGE_TYPES, CODE_TYPES2, KNOWLEDGE_EDGE_TYPES, CoverageScorer, VALID_BUSINESS_TYPES, DEFAULT_MAX_DOCS, MAX_COLLISION_SUFFIX, KnowledgeDocMaterializer, BUSINESS_NODE_TYPES2, SNAPSHOT_NODE_TYPES, KnowledgePipelineRunner, CODE_NODE_TYPES5, RETRYABLE_STATUSES, SANITIZE_RULES, SyncManager, SUMMARIZE_PROMPT, JiraConnector, SlackConnector, ConfluenceConnector, CIConnector, CONSTRAINT_KEYWORDS, FigmaConnector, MIN_CONTENT_LENGTH, CONCEPT_ITEM_TYPES, MiroConnector, STOP_WORDS, FusionLayer, CODE_NODE_TYPES6, GraphEntropyAdapter, GraphComplexityAdapter, GraphCouplingAdapter, DEFAULT_THRESHOLD, DEFAULT_METRICS, RECOGNIZED_METRICS, GraphAnomalyAdapter, INTENTS, SIGNAL_WEIGHTS, INTENT_SIGNALS, IntentClassifier, INTENT_KEYWORDS, STOP_WORDS2, PASCAL_OR_CAMEL_RE, FILE_PATH_RE, QUOTED_RE, EntityExtractor, EntityResolver, ResponseFormatter, CompositeProbabilityStrategy, DEFAULT_PROBABILITY_FLOOR, DEFAULT_MAX_DEPTH, CascadeSimulator, ENTITY_REQUIRED_INTENTS, classifier, extractor, formatter, PHASE_NODE_TYPES, CODE_NODE_TYPES7, Assembler, GraphConstraintAdapter, DesignIngestor, DesignConstraintAdapter, GraphFeedbackAdapter, DEFAULT_EDGE_TYPES, TaskIndependenceAnalyzer, ConflictPredictor, VERSION;
1506
1633
  var init_dist = __esm({
1507
1634
  "../graph/dist/index.mjs"() {
1508
1635
  NODE_TYPES = [
@@ -1596,7 +1723,7 @@ var init_dist = __esm({
1596
1723
  "caches"
1597
1724
  ];
1598
1725
  OBSERVABILITY_TYPES = /* @__PURE__ */ new Set(["span", "metric", "log"]);
1599
- CURRENT_SCHEMA_VERSION = 1;
1726
+ CURRENT_SCHEMA_VERSION = 2;
1600
1727
  NODE_STABILITY = {
1601
1728
  File: "session",
1602
1729
  Function: "session",
@@ -2026,6 +2153,72 @@ var init_dist = __esm({
2026
2153
  "method",
2027
2154
  "variable"
2028
2155
  ]);
2156
+ DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
2157
+ // VCS
2158
+ ".git",
2159
+ ".hg",
2160
+ ".svn",
2161
+ // Package managers
2162
+ "node_modules",
2163
+ ".pnpm-store",
2164
+ ".yarn",
2165
+ "vendor",
2166
+ // JS/TS build outputs
2167
+ "dist",
2168
+ "build",
2169
+ "out",
2170
+ "_build",
2171
+ "bin",
2172
+ "obj",
2173
+ "target",
2174
+ "deps",
2175
+ // JS/TS framework / tooling caches
2176
+ ".next",
2177
+ ".nuxt",
2178
+ ".svelte-kit",
2179
+ ".turbo",
2180
+ ".vite",
2181
+ ".cache",
2182
+ ".parcel-cache",
2183
+ ".docusaurus",
2184
+ ".wrangler",
2185
+ ".astro",
2186
+ ".remix",
2187
+ "storybook-static",
2188
+ // Test / coverage / reporter outputs
2189
+ "coverage",
2190
+ ".nyc_output",
2191
+ ".pytest_cache",
2192
+ "playwright-report",
2193
+ "test-results",
2194
+ ".e2e",
2195
+ // Python
2196
+ "__pycache__",
2197
+ ".venv",
2198
+ "venv",
2199
+ ".tox",
2200
+ ".mypy_cache",
2201
+ ".ruff_cache",
2202
+ // JVM
2203
+ ".gradle",
2204
+ ".gradle-home",
2205
+ // IDE / editor
2206
+ ".idea",
2207
+ ".vscode",
2208
+ ".vs",
2209
+ // Harness self
2210
+ ".harness",
2211
+ // AI agent sandboxes (these often contain full repo clones — skipping them
2212
+ // prevents the walker from re-ingesting nested copies of the project)
2213
+ ".claude",
2214
+ ".cursor",
2215
+ ".codex",
2216
+ ".gemini",
2217
+ ".aider",
2218
+ ".agents",
2219
+ ".agentastic",
2220
+ ".playwright-mcp"
2221
+ ]);
2029
2222
  SKIP_METHOD_NAMES = /* @__PURE__ */ new Set(["constructor", "if", "for", "while", "switch"]);
2030
2223
  FUNCTION_PATTERNS = {
2031
2224
  python: /^def\s+(\w+)\s*\(/,
@@ -2077,10 +2270,16 @@ var init_dist = __esm({
2077
2270
  ]);
2078
2271
  SKIP_EXTENSIONS = /* @__PURE__ */ new Set([".d.ts"]);
2079
2272
  CodeIngestor = class {
2080
- constructor(store) {
2273
+ constructor(store, options = {}) {
2081
2274
  this.store = store;
2275
+ this.skipDirs = resolveSkipDirs(options);
2276
+ this.excludePatterns = options.excludePatterns ?? [];
2277
+ this.respectGitignore = options.respectGitignore ?? true;
2082
2278
  }
2083
2279
  store;
2280
+ skipDirs;
2281
+ excludePatterns;
2282
+ respectGitignore;
2084
2283
  async ingest(rootDir) {
2085
2284
  const start = Date.now();
2086
2285
  const errors = [];
@@ -2170,19 +2369,55 @@ var init_dist = __esm({
2170
2369
  }
2171
2370
  files.add(relativePath);
2172
2371
  }
2173
- async findSourceFiles(dir) {
2372
+ /**
2373
+ * Walk `rootDir` iteratively (BFS via an explicit queue) and return absolute paths
2374
+ * of every supported source file that is not excluded by the configured filters.
2375
+ *
2376
+ * Iterative on purpose: a recursive walker burns one stack frame per nested
2377
+ * directory and crashes with `Maximum call stack size exceeded` on deeply nested
2378
+ * monorepos (issue #274). The queue-based form keeps memory bounded by the
2379
+ * frontier size rather than the deepest path.
2380
+ */
2381
+ async findSourceFiles(rootDir) {
2174
2382
  const results = [];
2175
- const entries = await fs.readdir(dir, { withFileTypes: true });
2176
- for (const entry of entries) {
2177
- const fullPath = path.join(dir, entry.name);
2178
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "target" && entry.name !== "build" && entry.name !== ".git" && entry.name !== "__pycache__" && entry.name !== ".venv" && entry.name !== ".turbo" && entry.name !== ".harness" && entry.name !== ".next" && entry.name !== ".vscode" && entry.name !== ".idea" && entry.name !== "vendor" && entry.name !== "out" && entry.name !== ".gradle" && entry.name !== ".gradle-home" && entry.name !== "bin" && entry.name !== "obj" && entry.name !== "venv" && entry.name !== "_build" && entry.name !== "deps" && entry.name !== "coverage" && entry.name !== ".nyc_output") {
2179
- results.push(...await this.findSourceFiles(fullPath));
2180
- } else if (entry.isFile() && isSupportedSourceFile(entry.name)) {
2181
- results.push(fullPath);
2383
+ const matchers = await this.buildExcludeMatchers(rootDir);
2384
+ const queue = [rootDir];
2385
+ while (queue.length > 0) {
2386
+ const dir = queue.pop();
2387
+ let entries;
2388
+ try {
2389
+ entries = await fs.readdir(dir, { withFileTypes: true });
2390
+ } catch {
2391
+ continue;
2392
+ }
2393
+ for (const entry of entries) {
2394
+ const fullPath = path.join(dir, entry.name);
2395
+ const relPath = path.relative(rootDir, fullPath).replaceAll("\\", "/");
2396
+ if (entry.isDirectory()) {
2397
+ if (this.skipDirs.has(entry.name)) continue;
2398
+ if (isExcluded(`${relPath}/`, matchers)) continue;
2399
+ queue.push(fullPath);
2400
+ } else if (entry.isFile()) {
2401
+ if (!isSupportedSourceFile(entry.name)) continue;
2402
+ if (isExcluded(relPath, matchers)) continue;
2403
+ results.push(fullPath);
2404
+ }
2182
2405
  }
2183
2406
  }
2184
2407
  return results;
2185
2408
  }
2409
+ /**
2410
+ * Compose the exclude matchers from `excludePatterns` and (optionally) `.gitignore`.
2411
+ * Returns a list of minimatch-compatible patterns; an empty list means "no excludes".
2412
+ */
2413
+ async buildExcludeMatchers(rootDir) {
2414
+ const patterns = [...this.excludePatterns];
2415
+ if (this.respectGitignore) {
2416
+ const gitignorePatterns = await readGitignorePatterns(rootDir);
2417
+ patterns.push(...gitignorePatterns);
2418
+ }
2419
+ return patterns;
2420
+ }
2186
2421
  extractSymbols(content, fileId, relativePath) {
2187
2422
  const results = [];
2188
2423
  const lines = content.split("\n");
@@ -3032,6 +3267,46 @@ var init_dist = __esm({
3032
3267
  return results;
3033
3268
  }
3034
3269
  };
3270
+ BUG_TRACK_CATEGORIES = [
3271
+ "build-errors",
3272
+ "test-failures",
3273
+ "runtime-errors",
3274
+ "performance-issues",
3275
+ "database-issues",
3276
+ "security-issues",
3277
+ "ui-bugs",
3278
+ "integration-issues",
3279
+ "logic-errors"
3280
+ ];
3281
+ KNOWLEDGE_TRACK_CATEGORIES = [
3282
+ "architecture-patterns",
3283
+ "design-patterns",
3284
+ "tooling-decisions",
3285
+ "conventions",
3286
+ "dx",
3287
+ "best-practices"
3288
+ ];
3289
+ ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
3290
+ SolutionBaseSchema = z2.object({
3291
+ module: z2.string().min(1),
3292
+ tags: z2.array(z2.string()),
3293
+ problem_type: z2.string().min(1),
3294
+ last_updated: z2.string().regex(ISO_DATE, "last_updated must be ISO date YYYY-MM-DD")
3295
+ });
3296
+ SolutionDocFrontmatterSchema = z2.discriminatedUnion("track", [
3297
+ SolutionBaseSchema.merge(
3298
+ z2.object({
3299
+ track: z2.literal("bug-track"),
3300
+ category: z2.enum(BUG_TRACK_CATEGORIES)
3301
+ })
3302
+ ),
3303
+ SolutionBaseSchema.merge(
3304
+ z2.object({
3305
+ track: z2.literal("knowledge-track"),
3306
+ category: z2.enum(KNOWLEDGE_TRACK_CATEGORIES)
3307
+ })
3308
+ )
3309
+ ]);
3035
3310
  BUSINESS_KNOWLEDGE_TYPES = /* @__PURE__ */ new Set([
3036
3311
  "business_rule",
3037
3312
  "business_process",
@@ -3074,6 +3349,65 @@ var init_dist = __esm({
3074
3349
  durationMs: Date.now() - start
3075
3350
  };
3076
3351
  }
3352
+ async ingestSolutions(solutionsDir) {
3353
+ const start = Date.now();
3354
+ const errors = [];
3355
+ let files;
3356
+ try {
3357
+ files = await this.findMarkdownFiles(solutionsDir);
3358
+ } catch {
3359
+ return emptyResult(Date.now() - start);
3360
+ }
3361
+ let nodesAdded = 0;
3362
+ for (const filePath of files) {
3363
+ try {
3364
+ const raw = await fs3.readFile(filePath, "utf-8");
3365
+ const parsed = parseSolutionFrontmatter(raw);
3366
+ if (!parsed) {
3367
+ errors.push(`${filePath}: no frontmatter found`);
3368
+ continue;
3369
+ }
3370
+ const validation = SolutionDocFrontmatterSchema.safeParse(parsed.frontmatter);
3371
+ if (!validation.success) {
3372
+ errors.push(`${filePath}: ${validation.error.message}`);
3373
+ continue;
3374
+ }
3375
+ if (validation.data.track !== "knowledge-track") continue;
3376
+ const relPath = path4.relative(solutionsDir, filePath).replaceAll("\\", "/");
3377
+ const filename = path4.basename(filePath, ".md");
3378
+ const nodeId = `bk:solutions:${validation.data.module}:${filename}`;
3379
+ const titleMatch = parsed.body.match(/^#\s+(.+)$/m);
3380
+ const name = titleMatch ? titleMatch[1].trim() : filename;
3381
+ const node = {
3382
+ id: nodeId,
3383
+ type: "business_concept",
3384
+ name,
3385
+ path: relPath,
3386
+ content: parsed.body.trim(),
3387
+ metadata: {
3388
+ domain: validation.data.module,
3389
+ tags: validation.data.tags,
3390
+ problem_type: validation.data.problem_type,
3391
+ last_updated: validation.data.last_updated,
3392
+ source: "solutions",
3393
+ category: validation.data.category
3394
+ }
3395
+ };
3396
+ this.store.addNode(node);
3397
+ nodesAdded++;
3398
+ } catch (err) {
3399
+ errors.push(`${filePath}: ${err instanceof Error ? err.message : String(err)}`);
3400
+ }
3401
+ }
3402
+ return {
3403
+ nodesAdded,
3404
+ nodesUpdated: 0,
3405
+ edgesAdded: 0,
3406
+ edgesUpdated: 0,
3407
+ errors,
3408
+ durationMs: Date.now() - start
3409
+ };
3410
+ }
3077
3411
  async createNodes(files, knowledgeDir, errors) {
3078
3412
  const entries = [];
3079
3413
  for (const filePath of files) {
@@ -5965,6 +6299,25 @@ var init_dist = __esm({
5965
6299
  durationMs: 0
5966
6300
  };
5967
6301
  }
6302
+ const solutionsDir = path12.join(options.projectDir, "docs", "solutions");
6303
+ let solutionsResult;
6304
+ try {
6305
+ solutionsResult = await bkIngestor.ingestSolutions(solutionsDir);
6306
+ } catch {
6307
+ solutionsResult = {
6308
+ nodesAdded: 0,
6309
+ nodesUpdated: 0,
6310
+ edgesAdded: 0,
6311
+ edgesUpdated: 0,
6312
+ errors: [],
6313
+ durationMs: 0
6314
+ };
6315
+ }
6316
+ bkResult = {
6317
+ ...bkResult,
6318
+ nodesAdded: bkResult.nodesAdded + solutionsResult.nodesAdded,
6319
+ errors: [...bkResult.errors, ...solutionsResult.errors]
6320
+ };
5968
6321
  const decisionsDir = path12.join(options.projectDir, "docs", "knowledge", "decisions");
5969
6322
  const decisionIngestor = new DecisionIngestor(this.store);
5970
6323
  let decisionResult;
@@ -8667,7 +9020,7 @@ ${replyLines.join("\n")}`;
8667
9020
  resolveLayer(filePath, layers) {
8668
9021
  for (const layer of layers) {
8669
9022
  for (const pattern of layer.patterns) {
8670
- if (minimatch(filePath, pattern)) {
9023
+ if (minimatch2(filePath, pattern)) {
8671
9024
  return layer;
8672
9025
  }
8673
9026
  }
@@ -9359,6 +9712,7 @@ export {
9359
9712
  GraphNodeSchema,
9360
9713
  GraphEdgeSchema,
9361
9714
  saveGraph,
9715
+ loadGraphMetadata,
9362
9716
  loadGraph,
9363
9717
  GraphStore,
9364
9718
  VectorStore,
@@ -9368,6 +9722,8 @@ export {
9368
9722
  project,
9369
9723
  classifyNodeCategory,
9370
9724
  groupNodesByImpact,
9725
+ DEFAULT_SKIP_DIRS,
9726
+ resolveSkipDirs,
9371
9727
  CodeIngestor,
9372
9728
  GitIngestor,
9373
9729
  TopologicalLinker,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  init_dist
3
- } from "./chunk-RFYF7TJS.js";
3
+ } from "./chunk-K4WRQTEF.js";
4
4
  import {
5
5
  Err,
6
6
  Ok
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  resolveConfig
3
- } from "./chunk-KJD4J54I.js";
3
+ } from "./chunk-LO7ZLV7S.js";
4
4
  import {
5
5
  init_dist
6
- } from "./chunk-RFYF7TJS.js";
6
+ } from "./chunk-K4WRQTEF.js";
7
7
  import {
8
8
  Ok
9
9
  } from "./chunk-RL4VMEXL.js";