@harness-engineering/graph 0.8.0 → 0.10.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/dist/index.d.mts CHANGED
@@ -497,6 +497,16 @@ declare function resolveSkipDirs(options?: {
497
497
  skipDirs?: Iterable<string>;
498
498
  additionalSkipDirs?: Iterable<string>;
499
499
  }): ReadonlySet<string>;
500
+ /**
501
+ * Convert a skip-dirs set into minimatch glob patterns of the form
502
+ * `**\/<name>/\**`. Use for tools that exclude via globs (security scan, doc
503
+ * coverage, entropy analysis) rather than by reading directory names during
504
+ * traversal. Defaults to {@link DEFAULT_SKIP_DIRS}.
505
+ *
506
+ * Returns a fresh array each call so callers may freely mutate/append
507
+ * domain-specific patterns (e.g. test/fixture file globs).
508
+ */
509
+ declare function skipDirGlobs(skipDirs?: ReadonlySet<string>): string[];
500
510
 
501
511
  type GitRunner = (rootDir: string, args: string[]) => Promise<string>;
502
512
  /**
@@ -544,11 +554,13 @@ declare class KnowledgeIngestor {
544
554
  ingestADRs(adrDir: string): Promise<IngestResult>;
545
555
  ingestLearnings(projectPath: string): Promise<IngestResult>;
546
556
  ingestFailures(projectPath: string): Promise<IngestResult>;
557
+ ingestGeneralDocs(projectPath: string): Promise<IngestResult>;
547
558
  ingestAll(projectPath: string, opts?: {
548
559
  adrDir?: string;
549
560
  }): Promise<IngestResult>;
550
561
  private linkToCode;
551
562
  private findMarkdownFiles;
563
+ private scanDocsDir;
552
564
  }
553
565
 
554
566
  declare class BusinessKnowledgeIngestor {
@@ -1766,6 +1778,28 @@ interface DesignViolation {
1766
1778
  suggestion?: string;
1767
1779
  }
1768
1780
  type DesignStrictness = 'strict' | 'standard' | 'permissive';
1781
+ /**
1782
+ * Generic finding shape accepted by `recordFindings()` — covers ANAT-*
1783
+ * (audit-component-anatomy, design-pipeline #2) and CRAFT-*
1784
+ * (harness-design-craft, design-pipeline #6) namespaces. Skills convert
1785
+ * their internal finding types into this shape before recording.
1786
+ */
1787
+ interface CraftFindingRecord {
1788
+ /** Finding code, e.g. "ANAT-D023", "CRAFT-C001", "CRAFT-P001" */
1789
+ code: string;
1790
+ /** Project-relative file path (becomes the `file` node id) */
1791
+ file: string;
1792
+ /** Line number, if known */
1793
+ line?: number;
1794
+ /** Human-readable message */
1795
+ message: string;
1796
+ /** Severity at emission time */
1797
+ severity: 'error' | 'warn' | 'info';
1798
+ /** Optional evidence snippet */
1799
+ evidence?: string;
1800
+ /** Optional run identifier so #4 verifier can detect fixpoint across runs */
1801
+ runId?: string;
1802
+ }
1769
1803
  declare class DesignConstraintAdapter {
1770
1804
  private readonly store;
1771
1805
  constructor(store: GraphStore);
@@ -1773,6 +1807,30 @@ declare class DesignConstraintAdapter {
1773
1807
  checkForHardcodedFonts(source: string, file: string, strictness?: DesignStrictness): DesignViolation[];
1774
1808
  checkAll(source: string, file: string, strictness?: DesignStrictness): DesignViolation[];
1775
1809
  private mapSeverity;
1810
+ /**
1811
+ * Record externally-computed craft findings (audit-component-anatomy,
1812
+ * harness-design-craft, etc.) as graph state. Idempotent — re-running on
1813
+ * the same findings produces no duplicate nodes or edges thanks to
1814
+ * GraphStore's keyed merge semantics.
1815
+ *
1816
+ * Each finding becomes:
1817
+ * • a `design_constraint` node keyed by finding code (created lazily
1818
+ * if absent; metadata merged on re-record so the most recent message
1819
+ * / severity wins)
1820
+ * • a `violates_design` edge from the source file (id = file path) to
1821
+ * the constraint node, with per-finding metadata (line, severity,
1822
+ * message, evidence, runId)
1823
+ *
1824
+ * The file node is NOT created here — it's assumed to already exist in
1825
+ * the graph from a prior ingest. If it does not, the edge will still be
1826
+ * created (graph stores edges by key, not by referential integrity) and
1827
+ * a subsequent ingest will populate the file node.
1828
+ */
1829
+ recordFindings(findings: readonly CraftFindingRecord[]): {
1830
+ constraintsAdded: number;
1831
+ edgesAdded: number;
1832
+ };
1833
+ private labelForCode;
1776
1834
  }
1777
1835
 
1778
1836
  interface GraphImpactData {
@@ -1969,6 +2027,6 @@ declare class CascadeSimulator {
1969
2027
  private buildResult;
1970
2028
  }
1971
2029
 
1972
- declare const VERSION = "0.6.0";
2030
+ declare const VERSION = "0.9.0";
1973
2031
 
1974
- export { ALL_EXTRACTORS, type AggregateResult, type AnomalyDetectionOptions, type AnomalyReport, ApiPathExtractor, type ArticulationPoint, type AskGraphResult, type AssembledContext, Assembler, BusinessKnowledgeIngestor, CIConnector, CURRENT_SCHEMA_VERSION, type PackedEnvelope as CacheableEnvelope, type CascadeLayer, type CascadeNode, type CascadeResult, type CascadeSimulationOptions, CascadeSimulator, type ClassificationResult, CodeIngestor, type CodeIngestorOptions, CompositeProbabilityStrategy, type ConflictDetail, type ConflictPrediction, ConflictPredictor, type ConflictSeverity, type ConflictType, ConfluenceConnector, type ConnectorConfig, ContextQL, type ContextQLParams, type ContextQLResult, type Contradiction, ContradictionDetector, type ContradictionEntry, type ContradictionResult, type CoverageReport, CoverageScorer, D2Parser, DEFAULT_BLOCKLIST, DEFAULT_PATTERNS, DEFAULT_SKIP_DIRS, DecisionIngestor, DesignConstraintAdapter, DesignIngestor, type DesignStrictness, type DesignViolation, type DetectedElement, type DiagramEntity, type DiagramFormatParser, type DiagramParseResult, DiagramParser, type DiagramRelationship, type DomainCoverage, type DomainCoverageScore, type DomainInferenceOptions, type DriftClassification, type DriftDetector, type DriftFinding, type DriftResult, EDGE_TYPES, type EdgeQuery, type EdgeType, EntityExtractor, EntityResolver, EnumConstantExtractor, type ExtractionCounts, type ExtractionRecord, ExtractionRunner, FigmaConnector, FusionLayer, type FusionResult, type GapEntry, type GapReport, GitIngestor, type GitRunner, GraphAnomalyAdapter, type GraphBudget, GraphComplexityAdapter, type GraphComplexityHotspot, type GraphComplexityResult, type GraphConnector, GraphConstraintAdapter, GraphCouplingAdapter, type GraphCouplingFileData, type GraphCouplingResult, type GraphCoverageReport, type GraphDeadCodeData, type GraphDependencyData, type GraphDriftData, type GraphEdge, GraphEdgeSchema, GraphEntropyAdapter, GraphFeedbackAdapter, type GraphFilterResult, type GraphHarnessCheckData, type GraphImpactData, type GraphLayerViolation, type GraphMetadata, type GraphNode, GraphNodeSchema, type GraphSnapshotSummary, type GraphStabilityTier, GraphStore, type HttpClient, INTENTS, ImageAnalysisExtractor, type ImageAnalysisExtractorOptions, type AnalysisProvider as ImageAnalysisProvider, type ImageAnalysisResult, type ImpactGroups, type IndependenceCheckParams, type IndependenceResult, type IngestResult, type Intent, IntentClassifier, JiraConnector, KnowledgeDocMaterializer, KnowledgeIngestor, type KnowledgePipelineOptions, type KnowledgePipelineResult, KnowledgePipelineRunner, type KnowledgeSnapshot, type KnowledgeSnapshotEntry, KnowledgeStagingAggregator, type Language, type LinkResult, type LoadGraphResult, type LoadMetadataResult, type MaterializeOptions, type MaterializeResult, type MaterializedDoc, MermaidParser, MiroConnector, NODE_STABILITY, NODE_TYPES, type NodeCategory, type NodeQuery, type NodeType, OBSERVABILITY_TYPES, type OverlapDetail, PackedSummaryCache, type PairResult, PlantUmlParser, type ProbabilityStrategy, type ProjectionSpec, type RequirementCoverage, RequirementIngestor, type ResolvedEntity, ResponseFormatter, type SignalExtractor, type SkippedEntry, SlackConnector, type SourceLocation, type StagedEntry, type StatisticalOutlier, StructuralDriftDetector, SyncManager, type SyncMetadata, type TaskDefinition, TaskIndependenceAnalyzer, TestDescriptionExtractor, TopologicalLinker, type TraceabilityOptions, type TraceabilityResult, type TracedFile, VERSION, ValidationRuleExtractor, type VectorSearchResult, VectorStore, askGraph, classifyNodeCategory, createExtractionRunner, detectLanguage, groupNodesByImpact, inferDomain, linkToCode, loadGraph, loadGraphMetadata, normalizeIntent, project, queryTraceability, resolveSkipDirs, saveGraph };
2032
+ export { ALL_EXTRACTORS, type AggregateResult, type AnomalyDetectionOptions, type AnomalyReport, ApiPathExtractor, type ArticulationPoint, type AskGraphResult, type AssembledContext, Assembler, BusinessKnowledgeIngestor, CIConnector, CURRENT_SCHEMA_VERSION, type PackedEnvelope as CacheableEnvelope, type CascadeLayer, type CascadeNode, type CascadeResult, type CascadeSimulationOptions, CascadeSimulator, type ClassificationResult, CodeIngestor, type CodeIngestorOptions, CompositeProbabilityStrategy, type ConflictDetail, type ConflictPrediction, ConflictPredictor, type ConflictSeverity, type ConflictType, ConfluenceConnector, type ConnectorConfig, ContextQL, type ContextQLParams, type ContextQLResult, type Contradiction, ContradictionDetector, type ContradictionEntry, type ContradictionResult, type CoverageReport, CoverageScorer, type CraftFindingRecord, D2Parser, DEFAULT_BLOCKLIST, DEFAULT_PATTERNS, DEFAULT_SKIP_DIRS, DecisionIngestor, DesignConstraintAdapter, DesignIngestor, type DesignStrictness, type DesignViolation, type DetectedElement, type DiagramEntity, type DiagramFormatParser, type DiagramParseResult, DiagramParser, type DiagramRelationship, type DomainCoverage, type DomainCoverageScore, type DomainInferenceOptions, type DriftClassification, type DriftDetector, type DriftFinding, type DriftResult, EDGE_TYPES, type EdgeQuery, type EdgeType, EntityExtractor, EntityResolver, EnumConstantExtractor, type ExtractionCounts, type ExtractionRecord, ExtractionRunner, FigmaConnector, FusionLayer, type FusionResult, type GapEntry, type GapReport, GitIngestor, type GitRunner, GraphAnomalyAdapter, type GraphBudget, GraphComplexityAdapter, type GraphComplexityHotspot, type GraphComplexityResult, type GraphConnector, GraphConstraintAdapter, GraphCouplingAdapter, type GraphCouplingFileData, type GraphCouplingResult, type GraphCoverageReport, type GraphDeadCodeData, type GraphDependencyData, type GraphDriftData, type GraphEdge, GraphEdgeSchema, GraphEntropyAdapter, GraphFeedbackAdapter, type GraphFilterResult, type GraphHarnessCheckData, type GraphImpactData, type GraphLayerViolation, type GraphMetadata, type GraphNode, GraphNodeSchema, type GraphSnapshotSummary, type GraphStabilityTier, GraphStore, type HttpClient, INTENTS, ImageAnalysisExtractor, type ImageAnalysisExtractorOptions, type AnalysisProvider as ImageAnalysisProvider, type ImageAnalysisResult, type ImpactGroups, type IndependenceCheckParams, type IndependenceResult, type IngestResult, type Intent, IntentClassifier, JiraConnector, KnowledgeDocMaterializer, KnowledgeIngestor, type KnowledgePipelineOptions, type KnowledgePipelineResult, KnowledgePipelineRunner, type KnowledgeSnapshot, type KnowledgeSnapshotEntry, KnowledgeStagingAggregator, type Language, type LinkResult, type LoadGraphResult, type LoadMetadataResult, type MaterializeOptions, type MaterializeResult, type MaterializedDoc, MermaidParser, MiroConnector, NODE_STABILITY, NODE_TYPES, type NodeCategory, type NodeQuery, type NodeType, OBSERVABILITY_TYPES, type OverlapDetail, PackedSummaryCache, type PairResult, PlantUmlParser, type ProbabilityStrategy, type ProjectionSpec, type RequirementCoverage, RequirementIngestor, type ResolvedEntity, ResponseFormatter, type SignalExtractor, type SkippedEntry, SlackConnector, type SourceLocation, type StagedEntry, type StatisticalOutlier, StructuralDriftDetector, SyncManager, type SyncMetadata, type TaskDefinition, TaskIndependenceAnalyzer, TestDescriptionExtractor, TopologicalLinker, type TraceabilityOptions, type TraceabilityResult, type TracedFile, VERSION, ValidationRuleExtractor, type VectorSearchResult, VectorStore, askGraph, classifyNodeCategory, createExtractionRunner, detectLanguage, groupNodesByImpact, inferDomain, linkToCode, loadGraph, loadGraphMetadata, normalizeIntent, project, queryTraceability, resolveSkipDirs, saveGraph, skipDirGlobs };
package/dist/index.d.ts CHANGED
@@ -497,6 +497,16 @@ declare function resolveSkipDirs(options?: {
497
497
  skipDirs?: Iterable<string>;
498
498
  additionalSkipDirs?: Iterable<string>;
499
499
  }): ReadonlySet<string>;
500
+ /**
501
+ * Convert a skip-dirs set into minimatch glob patterns of the form
502
+ * `**\/<name>/\**`. Use for tools that exclude via globs (security scan, doc
503
+ * coverage, entropy analysis) rather than by reading directory names during
504
+ * traversal. Defaults to {@link DEFAULT_SKIP_DIRS}.
505
+ *
506
+ * Returns a fresh array each call so callers may freely mutate/append
507
+ * domain-specific patterns (e.g. test/fixture file globs).
508
+ */
509
+ declare function skipDirGlobs(skipDirs?: ReadonlySet<string>): string[];
500
510
 
501
511
  type GitRunner = (rootDir: string, args: string[]) => Promise<string>;
502
512
  /**
@@ -544,11 +554,13 @@ declare class KnowledgeIngestor {
544
554
  ingestADRs(adrDir: string): Promise<IngestResult>;
545
555
  ingestLearnings(projectPath: string): Promise<IngestResult>;
546
556
  ingestFailures(projectPath: string): Promise<IngestResult>;
557
+ ingestGeneralDocs(projectPath: string): Promise<IngestResult>;
547
558
  ingestAll(projectPath: string, opts?: {
548
559
  adrDir?: string;
549
560
  }): Promise<IngestResult>;
550
561
  private linkToCode;
551
562
  private findMarkdownFiles;
563
+ private scanDocsDir;
552
564
  }
553
565
 
554
566
  declare class BusinessKnowledgeIngestor {
@@ -1766,6 +1778,28 @@ interface DesignViolation {
1766
1778
  suggestion?: string;
1767
1779
  }
1768
1780
  type DesignStrictness = 'strict' | 'standard' | 'permissive';
1781
+ /**
1782
+ * Generic finding shape accepted by `recordFindings()` — covers ANAT-*
1783
+ * (audit-component-anatomy, design-pipeline #2) and CRAFT-*
1784
+ * (harness-design-craft, design-pipeline #6) namespaces. Skills convert
1785
+ * their internal finding types into this shape before recording.
1786
+ */
1787
+ interface CraftFindingRecord {
1788
+ /** Finding code, e.g. "ANAT-D023", "CRAFT-C001", "CRAFT-P001" */
1789
+ code: string;
1790
+ /** Project-relative file path (becomes the `file` node id) */
1791
+ file: string;
1792
+ /** Line number, if known */
1793
+ line?: number;
1794
+ /** Human-readable message */
1795
+ message: string;
1796
+ /** Severity at emission time */
1797
+ severity: 'error' | 'warn' | 'info';
1798
+ /** Optional evidence snippet */
1799
+ evidence?: string;
1800
+ /** Optional run identifier so #4 verifier can detect fixpoint across runs */
1801
+ runId?: string;
1802
+ }
1769
1803
  declare class DesignConstraintAdapter {
1770
1804
  private readonly store;
1771
1805
  constructor(store: GraphStore);
@@ -1773,6 +1807,30 @@ declare class DesignConstraintAdapter {
1773
1807
  checkForHardcodedFonts(source: string, file: string, strictness?: DesignStrictness): DesignViolation[];
1774
1808
  checkAll(source: string, file: string, strictness?: DesignStrictness): DesignViolation[];
1775
1809
  private mapSeverity;
1810
+ /**
1811
+ * Record externally-computed craft findings (audit-component-anatomy,
1812
+ * harness-design-craft, etc.) as graph state. Idempotent — re-running on
1813
+ * the same findings produces no duplicate nodes or edges thanks to
1814
+ * GraphStore's keyed merge semantics.
1815
+ *
1816
+ * Each finding becomes:
1817
+ * • a `design_constraint` node keyed by finding code (created lazily
1818
+ * if absent; metadata merged on re-record so the most recent message
1819
+ * / severity wins)
1820
+ * • a `violates_design` edge from the source file (id = file path) to
1821
+ * the constraint node, with per-finding metadata (line, severity,
1822
+ * message, evidence, runId)
1823
+ *
1824
+ * The file node is NOT created here — it's assumed to already exist in
1825
+ * the graph from a prior ingest. If it does not, the edge will still be
1826
+ * created (graph stores edges by key, not by referential integrity) and
1827
+ * a subsequent ingest will populate the file node.
1828
+ */
1829
+ recordFindings(findings: readonly CraftFindingRecord[]): {
1830
+ constraintsAdded: number;
1831
+ edgesAdded: number;
1832
+ };
1833
+ private labelForCode;
1776
1834
  }
1777
1835
 
1778
1836
  interface GraphImpactData {
@@ -1969,6 +2027,6 @@ declare class CascadeSimulator {
1969
2027
  private buildResult;
1970
2028
  }
1971
2029
 
1972
- declare const VERSION = "0.6.0";
2030
+ declare const VERSION = "0.9.0";
1973
2031
 
1974
- export { ALL_EXTRACTORS, type AggregateResult, type AnomalyDetectionOptions, type AnomalyReport, ApiPathExtractor, type ArticulationPoint, type AskGraphResult, type AssembledContext, Assembler, BusinessKnowledgeIngestor, CIConnector, CURRENT_SCHEMA_VERSION, type PackedEnvelope as CacheableEnvelope, type CascadeLayer, type CascadeNode, type CascadeResult, type CascadeSimulationOptions, CascadeSimulator, type ClassificationResult, CodeIngestor, type CodeIngestorOptions, CompositeProbabilityStrategy, type ConflictDetail, type ConflictPrediction, ConflictPredictor, type ConflictSeverity, type ConflictType, ConfluenceConnector, type ConnectorConfig, ContextQL, type ContextQLParams, type ContextQLResult, type Contradiction, ContradictionDetector, type ContradictionEntry, type ContradictionResult, type CoverageReport, CoverageScorer, D2Parser, DEFAULT_BLOCKLIST, DEFAULT_PATTERNS, DEFAULT_SKIP_DIRS, DecisionIngestor, DesignConstraintAdapter, DesignIngestor, type DesignStrictness, type DesignViolation, type DetectedElement, type DiagramEntity, type DiagramFormatParser, type DiagramParseResult, DiagramParser, type DiagramRelationship, type DomainCoverage, type DomainCoverageScore, type DomainInferenceOptions, type DriftClassification, type DriftDetector, type DriftFinding, type DriftResult, EDGE_TYPES, type EdgeQuery, type EdgeType, EntityExtractor, EntityResolver, EnumConstantExtractor, type ExtractionCounts, type ExtractionRecord, ExtractionRunner, FigmaConnector, FusionLayer, type FusionResult, type GapEntry, type GapReport, GitIngestor, type GitRunner, GraphAnomalyAdapter, type GraphBudget, GraphComplexityAdapter, type GraphComplexityHotspot, type GraphComplexityResult, type GraphConnector, GraphConstraintAdapter, GraphCouplingAdapter, type GraphCouplingFileData, type GraphCouplingResult, type GraphCoverageReport, type GraphDeadCodeData, type GraphDependencyData, type GraphDriftData, type GraphEdge, GraphEdgeSchema, GraphEntropyAdapter, GraphFeedbackAdapter, type GraphFilterResult, type GraphHarnessCheckData, type GraphImpactData, type GraphLayerViolation, type GraphMetadata, type GraphNode, GraphNodeSchema, type GraphSnapshotSummary, type GraphStabilityTier, GraphStore, type HttpClient, INTENTS, ImageAnalysisExtractor, type ImageAnalysisExtractorOptions, type AnalysisProvider as ImageAnalysisProvider, type ImageAnalysisResult, type ImpactGroups, type IndependenceCheckParams, type IndependenceResult, type IngestResult, type Intent, IntentClassifier, JiraConnector, KnowledgeDocMaterializer, KnowledgeIngestor, type KnowledgePipelineOptions, type KnowledgePipelineResult, KnowledgePipelineRunner, type KnowledgeSnapshot, type KnowledgeSnapshotEntry, KnowledgeStagingAggregator, type Language, type LinkResult, type LoadGraphResult, type LoadMetadataResult, type MaterializeOptions, type MaterializeResult, type MaterializedDoc, MermaidParser, MiroConnector, NODE_STABILITY, NODE_TYPES, type NodeCategory, type NodeQuery, type NodeType, OBSERVABILITY_TYPES, type OverlapDetail, PackedSummaryCache, type PairResult, PlantUmlParser, type ProbabilityStrategy, type ProjectionSpec, type RequirementCoverage, RequirementIngestor, type ResolvedEntity, ResponseFormatter, type SignalExtractor, type SkippedEntry, SlackConnector, type SourceLocation, type StagedEntry, type StatisticalOutlier, StructuralDriftDetector, SyncManager, type SyncMetadata, type TaskDefinition, TaskIndependenceAnalyzer, TestDescriptionExtractor, TopologicalLinker, type TraceabilityOptions, type TraceabilityResult, type TracedFile, VERSION, ValidationRuleExtractor, type VectorSearchResult, VectorStore, askGraph, classifyNodeCategory, createExtractionRunner, detectLanguage, groupNodesByImpact, inferDomain, linkToCode, loadGraph, loadGraphMetadata, normalizeIntent, project, queryTraceability, resolveSkipDirs, saveGraph };
2032
+ export { ALL_EXTRACTORS, type AggregateResult, type AnomalyDetectionOptions, type AnomalyReport, ApiPathExtractor, type ArticulationPoint, type AskGraphResult, type AssembledContext, Assembler, BusinessKnowledgeIngestor, CIConnector, CURRENT_SCHEMA_VERSION, type PackedEnvelope as CacheableEnvelope, type CascadeLayer, type CascadeNode, type CascadeResult, type CascadeSimulationOptions, CascadeSimulator, type ClassificationResult, CodeIngestor, type CodeIngestorOptions, CompositeProbabilityStrategy, type ConflictDetail, type ConflictPrediction, ConflictPredictor, type ConflictSeverity, type ConflictType, ConfluenceConnector, type ConnectorConfig, ContextQL, type ContextQLParams, type ContextQLResult, type Contradiction, ContradictionDetector, type ContradictionEntry, type ContradictionResult, type CoverageReport, CoverageScorer, type CraftFindingRecord, D2Parser, DEFAULT_BLOCKLIST, DEFAULT_PATTERNS, DEFAULT_SKIP_DIRS, DecisionIngestor, DesignConstraintAdapter, DesignIngestor, type DesignStrictness, type DesignViolation, type DetectedElement, type DiagramEntity, type DiagramFormatParser, type DiagramParseResult, DiagramParser, type DiagramRelationship, type DomainCoverage, type DomainCoverageScore, type DomainInferenceOptions, type DriftClassification, type DriftDetector, type DriftFinding, type DriftResult, EDGE_TYPES, type EdgeQuery, type EdgeType, EntityExtractor, EntityResolver, EnumConstantExtractor, type ExtractionCounts, type ExtractionRecord, ExtractionRunner, FigmaConnector, FusionLayer, type FusionResult, type GapEntry, type GapReport, GitIngestor, type GitRunner, GraphAnomalyAdapter, type GraphBudget, GraphComplexityAdapter, type GraphComplexityHotspot, type GraphComplexityResult, type GraphConnector, GraphConstraintAdapter, GraphCouplingAdapter, type GraphCouplingFileData, type GraphCouplingResult, type GraphCoverageReport, type GraphDeadCodeData, type GraphDependencyData, type GraphDriftData, type GraphEdge, GraphEdgeSchema, GraphEntropyAdapter, GraphFeedbackAdapter, type GraphFilterResult, type GraphHarnessCheckData, type GraphImpactData, type GraphLayerViolation, type GraphMetadata, type GraphNode, GraphNodeSchema, type GraphSnapshotSummary, type GraphStabilityTier, GraphStore, type HttpClient, INTENTS, ImageAnalysisExtractor, type ImageAnalysisExtractorOptions, type AnalysisProvider as ImageAnalysisProvider, type ImageAnalysisResult, type ImpactGroups, type IndependenceCheckParams, type IndependenceResult, type IngestResult, type Intent, IntentClassifier, JiraConnector, KnowledgeDocMaterializer, KnowledgeIngestor, type KnowledgePipelineOptions, type KnowledgePipelineResult, KnowledgePipelineRunner, type KnowledgeSnapshot, type KnowledgeSnapshotEntry, KnowledgeStagingAggregator, type Language, type LinkResult, type LoadGraphResult, type LoadMetadataResult, type MaterializeOptions, type MaterializeResult, type MaterializedDoc, MermaidParser, MiroConnector, NODE_STABILITY, NODE_TYPES, type NodeCategory, type NodeQuery, type NodeType, OBSERVABILITY_TYPES, type OverlapDetail, PackedSummaryCache, type PairResult, PlantUmlParser, type ProbabilityStrategy, type ProjectionSpec, type RequirementCoverage, RequirementIngestor, type ResolvedEntity, ResponseFormatter, type SignalExtractor, type SkippedEntry, SlackConnector, type SourceLocation, type StagedEntry, type StatisticalOutlier, StructuralDriftDetector, SyncManager, type SyncMetadata, type TaskDefinition, TaskIndependenceAnalyzer, TestDescriptionExtractor, TopologicalLinker, type TraceabilityOptions, type TraceabilityResult, type TracedFile, VERSION, ValidationRuleExtractor, type VectorSearchResult, VectorStore, askGraph, classifyNodeCategory, createExtractionRunner, detectLanguage, groupNodesByImpact, inferDomain, linkToCode, loadGraph, loadGraphMetadata, normalizeIntent, project, queryTraceability, resolveSkipDirs, saveGraph, skipDirGlobs };
package/dist/index.js CHANGED
@@ -108,7 +108,8 @@ __export(index_exports, {
108
108
  project: () => project,
109
109
  queryTraceability: () => queryTraceability,
110
110
  resolveSkipDirs: () => resolveSkipDirs,
111
- saveGraph: () => saveGraph
111
+ saveGraph: () => saveGraph,
112
+ skipDirGlobs: () => skipDirGlobs
112
113
  });
113
114
  module.exports = __toCommonJS(index_exports);
114
115
 
@@ -978,6 +979,9 @@ function resolveSkipDirs(options) {
978
979
  }
979
980
  return base;
980
981
  }
982
+ function skipDirGlobs(skipDirs = DEFAULT_SKIP_DIRS) {
983
+ return Array.from(skipDirs, (name) => `**/${name}/**`);
984
+ }
981
985
 
982
986
  // src/ingest/CodeIngestor.ts
983
987
  var SKIP_METHOD_NAMES = /* @__PURE__ */ new Set(["constructor", "if", "for", "while", "switch"]);
@@ -2005,6 +2009,7 @@ function emptyResult(durationMs = 0) {
2005
2009
 
2006
2010
  // src/ingest/KnowledgeIngestor.ts
2007
2011
  var CODE_NODE_TYPES = ["file", "function", "class", "method", "interface", "variable"];
2012
+ var DOCS_OWNED_BY_OTHER_INGESTORS = /* @__PURE__ */ new Set(["adr", "knowledge", "changes", "solutions"]);
2008
2013
  var KnowledgeIngestor = class {
2009
2014
  constructor(store) {
2010
2015
  this.store = store;
@@ -2076,15 +2081,53 @@ var KnowledgeIngestor = class {
2076
2081
  }
2077
2082
  return buildResult(nodesAdded, edgesAdded, [], start);
2078
2083
  }
2084
+ async ingestGeneralDocs(projectPath) {
2085
+ const start = Date.now();
2086
+ const errors = [];
2087
+ let nodesAdded = 0;
2088
+ let edgesAdded = 0;
2089
+ const files = /* @__PURE__ */ new Set();
2090
+ try {
2091
+ const entries = await fs2.readdir(projectPath, { withFileTypes: true });
2092
+ for (const entry of entries) {
2093
+ if (entry.isFile() && entry.name.endsWith(".md")) {
2094
+ files.add(path3.join(projectPath, entry.name));
2095
+ }
2096
+ }
2097
+ } catch {
2098
+ return emptyResult(Date.now() - start);
2099
+ }
2100
+ const docsRoot = path3.join(projectPath, "docs");
2101
+ try {
2102
+ const scanned = await this.scanDocsDir(docsRoot);
2103
+ for (const f of scanned) files.add(f);
2104
+ } catch {
2105
+ }
2106
+ for (const filePath of files) {
2107
+ try {
2108
+ const content = await fs2.readFile(filePath, "utf-8");
2109
+ const relPath = path3.relative(projectPath, filePath).replaceAll("\\", "/");
2110
+ const nodeId = `doc:${relPath}`;
2111
+ if (this.store.getNode(nodeId)) continue;
2112
+ this.store.addNode(parseDocumentNode(nodeId, filePath, relPath, content));
2113
+ nodesAdded++;
2114
+ edgesAdded += this.linkToCode(content, nodeId, "documents");
2115
+ } catch (err) {
2116
+ errors.push(`${filePath}: ${err instanceof Error ? err.message : String(err)}`);
2117
+ }
2118
+ }
2119
+ return buildResult(nodesAdded, edgesAdded, errors, start);
2120
+ }
2079
2121
  async ingestAll(projectPath, opts) {
2080
2122
  const start = Date.now();
2081
2123
  const adrDir = opts?.adrDir ?? path3.join(projectPath, "docs", "adr");
2082
- const [adrResult, learningsResult, failuresResult] = await Promise.all([
2124
+ const [adrResult, learningsResult, failuresResult, docsResult] = await Promise.all([
2083
2125
  this.ingestADRs(adrDir),
2084
2126
  this.ingestLearnings(projectPath),
2085
- this.ingestFailures(projectPath)
2127
+ this.ingestFailures(projectPath),
2128
+ this.ingestGeneralDocs(projectPath)
2086
2129
  ]);
2087
- const merged = mergeResults(adrResult, learningsResult, failuresResult);
2130
+ const merged = mergeResults(adrResult, learningsResult, failuresResult, docsResult);
2088
2131
  return { ...merged, durationMs: Date.now() - start };
2089
2132
  }
2090
2133
  linkToCode(content, sourceNodeId, edgeType) {
@@ -2119,7 +2162,22 @@ var KnowledgeIngestor = class {
2119
2162
  const entries = await fs2.readdir(dir, { withFileTypes: true });
2120
2163
  for (const entry of entries) {
2121
2164
  const fullPath = path3.join(dir, entry.name);
2122
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "target" && entry.name !== "build" && entry.name !== ".git" && entry.name !== ".gradle" && entry.name !== ".harness" && entry.name !== "vendor" && entry.name !== "bin" && entry.name !== "obj" && entry.name !== "venv" && entry.name !== "_build" && entry.name !== "deps" && entry.name !== "coverage") {
2165
+ if (entry.isDirectory() && !DEFAULT_SKIP_DIRS.has(entry.name)) {
2166
+ results.push(...await this.findMarkdownFiles(fullPath));
2167
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
2168
+ results.push(fullPath);
2169
+ }
2170
+ }
2171
+ return results;
2172
+ }
2173
+ async scanDocsDir(docsRoot) {
2174
+ const results = [];
2175
+ const rootEntries = await fs2.readdir(docsRoot, { withFileTypes: true });
2176
+ for (const entry of rootEntries) {
2177
+ const fullPath = path3.join(docsRoot, entry.name);
2178
+ if (entry.isDirectory()) {
2179
+ if (DEFAULT_SKIP_DIRS.has(entry.name)) continue;
2180
+ if (DOCS_OWNED_BY_OTHER_INGESTORS.has(entry.name)) continue;
2123
2181
  results.push(...await this.findMarkdownFiles(fullPath));
2124
2182
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
2125
2183
  results.push(fullPath);
@@ -2145,6 +2203,17 @@ function buildResult(nodesAdded, edgesAdded, errors, start) {
2145
2203
  durationMs: Date.now() - start
2146
2204
  };
2147
2205
  }
2206
+ function parseDocumentNode(nodeId, filePath, relPath, content) {
2207
+ const titleMatch = content.match(/^#\s+(.+)$/m);
2208
+ const title = titleMatch ? titleMatch[1].trim() : path3.basename(filePath, ".md");
2209
+ return {
2210
+ id: nodeId,
2211
+ type: "document",
2212
+ name: title,
2213
+ path: filePath,
2214
+ metadata: { relPath }
2215
+ };
2216
+ }
2148
2217
  function parseADRNode(nodeId, filePath, filename, content) {
2149
2218
  const titleMatch = content.match(/^#\s+(.+)$/m);
2150
2219
  const title = titleMatch ? titleMatch[1].trim() : filename;
@@ -2431,7 +2500,7 @@ var BusinessKnowledgeIngestor = class {
2431
2500
  const entries = await fs3.readdir(dir, { withFileTypes: true });
2432
2501
  for (const entry of entries) {
2433
2502
  const fullPath = path4.join(dir, entry.name);
2434
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "target" && entry.name !== "build" && entry.name !== ".git" && entry.name !== ".gradle" && entry.name !== ".harness" && entry.name !== "vendor" && entry.name !== "bin" && entry.name !== "obj" && entry.name !== "venv" && entry.name !== "_build" && entry.name !== "deps" && entry.name !== "coverage") {
2503
+ if (entry.isDirectory() && !DEFAULT_SKIP_DIRS.has(entry.name)) {
2435
2504
  results.push(...await this.findMarkdownFiles(fullPath));
2436
2505
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
2437
2506
  results.push(fullPath);
@@ -3275,7 +3344,6 @@ var PlantUmlParser = class {
3275
3344
 
3276
3345
  // src/ingest/DiagramParser.ts
3277
3346
  var DIAGRAM_EXTENSIONS = /* @__PURE__ */ new Set([".mmd", ".mermaid", ".d2", ".puml", ".plantuml"]);
3278
- var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".harness"]);
3279
3347
  var DiagramParser = class {
3280
3348
  constructor(store) {
3281
3349
  this.store = store;
@@ -3380,7 +3448,7 @@ var DiagramParser = class {
3380
3448
  }
3381
3449
  for (const entry of entries) {
3382
3450
  if (entry.isDirectory()) {
3383
- if (!SKIP_DIRS.has(entry.name)) {
3451
+ if (!DEFAULT_SKIP_DIRS.has(entry.name)) {
3384
3452
  await walk(path7.join(currentDir, entry.name));
3385
3453
  }
3386
3454
  } else if (entry.isFile()) {
@@ -3904,31 +3972,6 @@ var KnowledgeStagingAggregator = class {
3904
3972
  // src/ingest/extractors/ExtractionRunner.ts
3905
3973
  var fs9 = __toESM(require("fs/promises"));
3906
3974
  var path10 = __toESM(require("path"));
3907
- var SKIP_DIRS2 = /* @__PURE__ */ new Set([
3908
- "node_modules",
3909
- "dist",
3910
- "target",
3911
- "build",
3912
- ".git",
3913
- "__pycache__",
3914
- ".venv",
3915
- ".turbo",
3916
- ".harness",
3917
- ".next",
3918
- ".vscode",
3919
- ".idea",
3920
- "vendor",
3921
- "out",
3922
- ".gradle",
3923
- ".gradle-home",
3924
- "bin",
3925
- "obj",
3926
- "venv",
3927
- "_build",
3928
- "deps",
3929
- "coverage",
3930
- ".nyc_output"
3931
- ]);
3932
3975
  var EXT_TO_LANGUAGE = {
3933
3976
  ".ts": "typescript",
3934
3977
  ".tsx": "typescript",
@@ -4107,7 +4150,7 @@ var ExtractionRunner = class {
4107
4150
  }
4108
4151
  for (const entry of entries) {
4109
4152
  const fullPath = path10.join(dir, entry.name);
4110
- if (entry.isDirectory() && !SKIP_DIRS2.has(entry.name)) {
4153
+ if (entry.isDirectory() && !DEFAULT_SKIP_DIRS.has(entry.name)) {
4111
4154
  results.push(...await this.findSourceFiles(fullPath));
4112
4155
  } else if (entry.isFile() && detectLanguage(fullPath) !== void 0) {
4113
4156
  results.push(fullPath);
@@ -9340,6 +9383,16 @@ var DesignIngestor = class {
9340
9383
  };
9341
9384
 
9342
9385
  // src/constraints/DesignConstraintAdapter.ts
9386
+ var CODE_PREFIX_LABELS = {
9387
+ "ANAT-D": "Component anatomy (definition)",
9388
+ "ANAT-P": "Component anatomy (pattern presence)",
9389
+ "ANAT-U": "Component anatomy (usage)",
9390
+ "CRAFT-C": "Design craft (critique)",
9391
+ "CRAFT-P": "Design craft (polish)",
9392
+ "CRAFT-B": "Design craft (benchmark)",
9393
+ "DESIGN-": "Design constraint (legacy)",
9394
+ "A11Y-": "Accessibility"
9395
+ };
9343
9396
  var DesignConstraintAdapter = class {
9344
9397
  constructor(store) {
9345
9398
  this.store = store;
@@ -9421,6 +9474,76 @@ var DesignConstraintAdapter = class {
9421
9474
  return "error";
9422
9475
  }
9423
9476
  }
9477
+ /**
9478
+ * Record externally-computed craft findings (audit-component-anatomy,
9479
+ * harness-design-craft, etc.) as graph state. Idempotent — re-running on
9480
+ * the same findings produces no duplicate nodes or edges thanks to
9481
+ * GraphStore's keyed merge semantics.
9482
+ *
9483
+ * Each finding becomes:
9484
+ * • a `design_constraint` node keyed by finding code (created lazily
9485
+ * if absent; metadata merged on re-record so the most recent message
9486
+ * / severity wins)
9487
+ * • a `violates_design` edge from the source file (id = file path) to
9488
+ * the constraint node, with per-finding metadata (line, severity,
9489
+ * message, evidence, runId)
9490
+ *
9491
+ * The file node is NOT created here — it's assumed to already exist in
9492
+ * the graph from a prior ingest. If it does not, the edge will still be
9493
+ * created (graph stores edges by key, not by referential integrity) and
9494
+ * a subsequent ingest will populate the file node.
9495
+ */
9496
+ recordFindings(findings) {
9497
+ let constraintsAdded = 0;
9498
+ let edgesAdded = 0;
9499
+ const seenConstraintIds = /* @__PURE__ */ new Set();
9500
+ for (const finding of findings) {
9501
+ const constraintId = `design_constraint:${finding.code}`;
9502
+ if (!seenConstraintIds.has(finding.code)) {
9503
+ const existing = this.store.getNode(constraintId);
9504
+ if (!existing) constraintsAdded += 1;
9505
+ this.store.addNode({
9506
+ id: constraintId,
9507
+ type: "design_constraint",
9508
+ name: finding.code,
9509
+ metadata: {
9510
+ code: finding.code,
9511
+ label: this.labelForCode(finding.code),
9512
+ mostRecentMessage: finding.message,
9513
+ mostRecentSeverity: finding.severity
9514
+ }
9515
+ });
9516
+ seenConstraintIds.add(finding.code);
9517
+ }
9518
+ const fileId = finding.file;
9519
+ const edgeMetadata = {
9520
+ message: finding.message,
9521
+ severity: finding.severity
9522
+ };
9523
+ if (finding.line !== void 0) edgeMetadata.line = finding.line;
9524
+ if (finding.evidence !== void 0) edgeMetadata.evidence = finding.evidence;
9525
+ if (finding.runId !== void 0) edgeMetadata.runId = finding.runId;
9526
+ const before = this.store.getEdges({
9527
+ from: fileId,
9528
+ to: constraintId,
9529
+ type: "violates_design"
9530
+ });
9531
+ this.store.addEdge({
9532
+ from: fileId,
9533
+ to: constraintId,
9534
+ type: "violates_design",
9535
+ metadata: edgeMetadata
9536
+ });
9537
+ if (before.length === 0) edgesAdded += 1;
9538
+ }
9539
+ return { constraintsAdded, edgesAdded };
9540
+ }
9541
+ labelForCode(code) {
9542
+ for (const prefix of Object.keys(CODE_PREFIX_LABELS)) {
9543
+ if (code.startsWith(prefix)) return CODE_PREFIX_LABELS[prefix];
9544
+ }
9545
+ return "Design constraint";
9546
+ }
9424
9547
  };
9425
9548
 
9426
9549
  // src/feedback/GraphFeedbackAdapter.ts
@@ -9948,7 +10071,7 @@ var ConflictPredictor = class {
9948
10071
  };
9949
10072
 
9950
10073
  // src/index.ts
9951
- var VERSION = "0.6.0";
10074
+ var VERSION = "0.9.0";
9952
10075
  // Annotate the CommonJS export names for ESM import in node:
9953
10076
  0 && (module.exports = {
9954
10077
  ALL_EXTRACTORS,
@@ -10029,5 +10152,6 @@ var VERSION = "0.6.0";
10029
10152
  project,
10030
10153
  queryTraceability,
10031
10154
  resolveSkipDirs,
10032
- saveGraph
10155
+ saveGraph,
10156
+ skipDirGlobs
10033
10157
  });
package/dist/index.mjs CHANGED
@@ -864,6 +864,9 @@ function resolveSkipDirs(options) {
864
864
  }
865
865
  return base;
866
866
  }
867
+ function skipDirGlobs(skipDirs = DEFAULT_SKIP_DIRS) {
868
+ return Array.from(skipDirs, (name) => `**/${name}/**`);
869
+ }
867
870
 
868
871
  // src/ingest/CodeIngestor.ts
869
872
  var SKIP_METHOD_NAMES = /* @__PURE__ */ new Set(["constructor", "if", "for", "while", "switch"]);
@@ -1891,6 +1894,7 @@ function emptyResult(durationMs = 0) {
1891
1894
 
1892
1895
  // src/ingest/KnowledgeIngestor.ts
1893
1896
  var CODE_NODE_TYPES = ["file", "function", "class", "method", "interface", "variable"];
1897
+ var DOCS_OWNED_BY_OTHER_INGESTORS = /* @__PURE__ */ new Set(["adr", "knowledge", "changes", "solutions"]);
1894
1898
  var KnowledgeIngestor = class {
1895
1899
  constructor(store) {
1896
1900
  this.store = store;
@@ -1962,15 +1966,53 @@ var KnowledgeIngestor = class {
1962
1966
  }
1963
1967
  return buildResult(nodesAdded, edgesAdded, [], start);
1964
1968
  }
1969
+ async ingestGeneralDocs(projectPath) {
1970
+ const start = Date.now();
1971
+ const errors = [];
1972
+ let nodesAdded = 0;
1973
+ let edgesAdded = 0;
1974
+ const files = /* @__PURE__ */ new Set();
1975
+ try {
1976
+ const entries = await fs2.readdir(projectPath, { withFileTypes: true });
1977
+ for (const entry of entries) {
1978
+ if (entry.isFile() && entry.name.endsWith(".md")) {
1979
+ files.add(path3.join(projectPath, entry.name));
1980
+ }
1981
+ }
1982
+ } catch {
1983
+ return emptyResult(Date.now() - start);
1984
+ }
1985
+ const docsRoot = path3.join(projectPath, "docs");
1986
+ try {
1987
+ const scanned = await this.scanDocsDir(docsRoot);
1988
+ for (const f of scanned) files.add(f);
1989
+ } catch {
1990
+ }
1991
+ for (const filePath of files) {
1992
+ try {
1993
+ const content = await fs2.readFile(filePath, "utf-8");
1994
+ const relPath = path3.relative(projectPath, filePath).replaceAll("\\", "/");
1995
+ const nodeId = `doc:${relPath}`;
1996
+ if (this.store.getNode(nodeId)) continue;
1997
+ this.store.addNode(parseDocumentNode(nodeId, filePath, relPath, content));
1998
+ nodesAdded++;
1999
+ edgesAdded += this.linkToCode(content, nodeId, "documents");
2000
+ } catch (err) {
2001
+ errors.push(`${filePath}: ${err instanceof Error ? err.message : String(err)}`);
2002
+ }
2003
+ }
2004
+ return buildResult(nodesAdded, edgesAdded, errors, start);
2005
+ }
1965
2006
  async ingestAll(projectPath, opts) {
1966
2007
  const start = Date.now();
1967
2008
  const adrDir = opts?.adrDir ?? path3.join(projectPath, "docs", "adr");
1968
- const [adrResult, learningsResult, failuresResult] = await Promise.all([
2009
+ const [adrResult, learningsResult, failuresResult, docsResult] = await Promise.all([
1969
2010
  this.ingestADRs(adrDir),
1970
2011
  this.ingestLearnings(projectPath),
1971
- this.ingestFailures(projectPath)
2012
+ this.ingestFailures(projectPath),
2013
+ this.ingestGeneralDocs(projectPath)
1972
2014
  ]);
1973
- const merged = mergeResults(adrResult, learningsResult, failuresResult);
2015
+ const merged = mergeResults(adrResult, learningsResult, failuresResult, docsResult);
1974
2016
  return { ...merged, durationMs: Date.now() - start };
1975
2017
  }
1976
2018
  linkToCode(content, sourceNodeId, edgeType) {
@@ -2005,7 +2047,22 @@ var KnowledgeIngestor = class {
2005
2047
  const entries = await fs2.readdir(dir, { withFileTypes: true });
2006
2048
  for (const entry of entries) {
2007
2049
  const fullPath = path3.join(dir, entry.name);
2008
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "target" && entry.name !== "build" && entry.name !== ".git" && entry.name !== ".gradle" && entry.name !== ".harness" && entry.name !== "vendor" && entry.name !== "bin" && entry.name !== "obj" && entry.name !== "venv" && entry.name !== "_build" && entry.name !== "deps" && entry.name !== "coverage") {
2050
+ if (entry.isDirectory() && !DEFAULT_SKIP_DIRS.has(entry.name)) {
2051
+ results.push(...await this.findMarkdownFiles(fullPath));
2052
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
2053
+ results.push(fullPath);
2054
+ }
2055
+ }
2056
+ return results;
2057
+ }
2058
+ async scanDocsDir(docsRoot) {
2059
+ const results = [];
2060
+ const rootEntries = await fs2.readdir(docsRoot, { withFileTypes: true });
2061
+ for (const entry of rootEntries) {
2062
+ const fullPath = path3.join(docsRoot, entry.name);
2063
+ if (entry.isDirectory()) {
2064
+ if (DEFAULT_SKIP_DIRS.has(entry.name)) continue;
2065
+ if (DOCS_OWNED_BY_OTHER_INGESTORS.has(entry.name)) continue;
2009
2066
  results.push(...await this.findMarkdownFiles(fullPath));
2010
2067
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
2011
2068
  results.push(fullPath);
@@ -2031,6 +2088,17 @@ function buildResult(nodesAdded, edgesAdded, errors, start) {
2031
2088
  durationMs: Date.now() - start
2032
2089
  };
2033
2090
  }
2091
+ function parseDocumentNode(nodeId, filePath, relPath, content) {
2092
+ const titleMatch = content.match(/^#\s+(.+)$/m);
2093
+ const title = titleMatch ? titleMatch[1].trim() : path3.basename(filePath, ".md");
2094
+ return {
2095
+ id: nodeId,
2096
+ type: "document",
2097
+ name: title,
2098
+ path: filePath,
2099
+ metadata: { relPath }
2100
+ };
2101
+ }
2034
2102
  function parseADRNode(nodeId, filePath, filename, content) {
2035
2103
  const titleMatch = content.match(/^#\s+(.+)$/m);
2036
2104
  const title = titleMatch ? titleMatch[1].trim() : filename;
@@ -2317,7 +2385,7 @@ var BusinessKnowledgeIngestor = class {
2317
2385
  const entries = await fs3.readdir(dir, { withFileTypes: true });
2318
2386
  for (const entry of entries) {
2319
2387
  const fullPath = path4.join(dir, entry.name);
2320
- if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist" && entry.name !== "target" && entry.name !== "build" && entry.name !== ".git" && entry.name !== ".gradle" && entry.name !== ".harness" && entry.name !== "vendor" && entry.name !== "bin" && entry.name !== "obj" && entry.name !== "venv" && entry.name !== "_build" && entry.name !== "deps" && entry.name !== "coverage") {
2388
+ if (entry.isDirectory() && !DEFAULT_SKIP_DIRS.has(entry.name)) {
2321
2389
  results.push(...await this.findMarkdownFiles(fullPath));
2322
2390
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
2323
2391
  results.push(fullPath);
@@ -3161,7 +3229,6 @@ var PlantUmlParser = class {
3161
3229
 
3162
3230
  // src/ingest/DiagramParser.ts
3163
3231
  var DIAGRAM_EXTENSIONS = /* @__PURE__ */ new Set([".mmd", ".mermaid", ".d2", ".puml", ".plantuml"]);
3164
- var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".harness"]);
3165
3232
  var DiagramParser = class {
3166
3233
  constructor(store) {
3167
3234
  this.store = store;
@@ -3266,7 +3333,7 @@ var DiagramParser = class {
3266
3333
  }
3267
3334
  for (const entry of entries) {
3268
3335
  if (entry.isDirectory()) {
3269
- if (!SKIP_DIRS.has(entry.name)) {
3336
+ if (!DEFAULT_SKIP_DIRS.has(entry.name)) {
3270
3337
  await walk(path7.join(currentDir, entry.name));
3271
3338
  }
3272
3339
  } else if (entry.isFile()) {
@@ -3790,31 +3857,6 @@ var KnowledgeStagingAggregator = class {
3790
3857
  // src/ingest/extractors/ExtractionRunner.ts
3791
3858
  import * as fs9 from "fs/promises";
3792
3859
  import * as path10 from "path";
3793
- var SKIP_DIRS2 = /* @__PURE__ */ new Set([
3794
- "node_modules",
3795
- "dist",
3796
- "target",
3797
- "build",
3798
- ".git",
3799
- "__pycache__",
3800
- ".venv",
3801
- ".turbo",
3802
- ".harness",
3803
- ".next",
3804
- ".vscode",
3805
- ".idea",
3806
- "vendor",
3807
- "out",
3808
- ".gradle",
3809
- ".gradle-home",
3810
- "bin",
3811
- "obj",
3812
- "venv",
3813
- "_build",
3814
- "deps",
3815
- "coverage",
3816
- ".nyc_output"
3817
- ]);
3818
3860
  var EXT_TO_LANGUAGE = {
3819
3861
  ".ts": "typescript",
3820
3862
  ".tsx": "typescript",
@@ -3993,7 +4035,7 @@ var ExtractionRunner = class {
3993
4035
  }
3994
4036
  for (const entry of entries) {
3995
4037
  const fullPath = path10.join(dir, entry.name);
3996
- if (entry.isDirectory() && !SKIP_DIRS2.has(entry.name)) {
4038
+ if (entry.isDirectory() && !DEFAULT_SKIP_DIRS.has(entry.name)) {
3997
4039
  results.push(...await this.findSourceFiles(fullPath));
3998
4040
  } else if (entry.isFile() && detectLanguage(fullPath) !== void 0) {
3999
4041
  results.push(fullPath);
@@ -9003,7 +9045,7 @@ function queryTraceability(store, options) {
9003
9045
 
9004
9046
  // src/constraints/GraphConstraintAdapter.ts
9005
9047
  import { minimatch as minimatch2 } from "minimatch";
9006
- import { relative as relative5 } from "path";
9048
+ import { relative as relative6 } from "path";
9007
9049
  var GraphConstraintAdapter = class {
9008
9050
  constructor(store) {
9009
9051
  this.store = store;
@@ -9032,8 +9074,8 @@ var GraphConstraintAdapter = class {
9032
9074
  const { edges } = this.computeDependencyGraph();
9033
9075
  const violations = [];
9034
9076
  for (const edge of edges) {
9035
- const fromRelative = relative5(rootDir, edge.from).replaceAll("\\", "/");
9036
- const toRelative = relative5(rootDir, edge.to).replaceAll("\\", "/");
9077
+ const fromRelative = relative6(rootDir, edge.from).replaceAll("\\", "/");
9078
+ const toRelative = relative6(rootDir, edge.to).replaceAll("\\", "/");
9037
9079
  const fromLayer = this.resolveLayer(fromRelative, layers);
9038
9080
  const toLayer = this.resolveLayer(toRelative, layers);
9039
9081
  if (!fromLayer || !toLayer) continue;
@@ -9226,6 +9268,16 @@ var DesignIngestor = class {
9226
9268
  };
9227
9269
 
9228
9270
  // src/constraints/DesignConstraintAdapter.ts
9271
+ var CODE_PREFIX_LABELS = {
9272
+ "ANAT-D": "Component anatomy (definition)",
9273
+ "ANAT-P": "Component anatomy (pattern presence)",
9274
+ "ANAT-U": "Component anatomy (usage)",
9275
+ "CRAFT-C": "Design craft (critique)",
9276
+ "CRAFT-P": "Design craft (polish)",
9277
+ "CRAFT-B": "Design craft (benchmark)",
9278
+ "DESIGN-": "Design constraint (legacy)",
9279
+ "A11Y-": "Accessibility"
9280
+ };
9229
9281
  var DesignConstraintAdapter = class {
9230
9282
  constructor(store) {
9231
9283
  this.store = store;
@@ -9307,6 +9359,76 @@ var DesignConstraintAdapter = class {
9307
9359
  return "error";
9308
9360
  }
9309
9361
  }
9362
+ /**
9363
+ * Record externally-computed craft findings (audit-component-anatomy,
9364
+ * harness-design-craft, etc.) as graph state. Idempotent — re-running on
9365
+ * the same findings produces no duplicate nodes or edges thanks to
9366
+ * GraphStore's keyed merge semantics.
9367
+ *
9368
+ * Each finding becomes:
9369
+ * • a `design_constraint` node keyed by finding code (created lazily
9370
+ * if absent; metadata merged on re-record so the most recent message
9371
+ * / severity wins)
9372
+ * • a `violates_design` edge from the source file (id = file path) to
9373
+ * the constraint node, with per-finding metadata (line, severity,
9374
+ * message, evidence, runId)
9375
+ *
9376
+ * The file node is NOT created here — it's assumed to already exist in
9377
+ * the graph from a prior ingest. If it does not, the edge will still be
9378
+ * created (graph stores edges by key, not by referential integrity) and
9379
+ * a subsequent ingest will populate the file node.
9380
+ */
9381
+ recordFindings(findings) {
9382
+ let constraintsAdded = 0;
9383
+ let edgesAdded = 0;
9384
+ const seenConstraintIds = /* @__PURE__ */ new Set();
9385
+ for (const finding of findings) {
9386
+ const constraintId = `design_constraint:${finding.code}`;
9387
+ if (!seenConstraintIds.has(finding.code)) {
9388
+ const existing = this.store.getNode(constraintId);
9389
+ if (!existing) constraintsAdded += 1;
9390
+ this.store.addNode({
9391
+ id: constraintId,
9392
+ type: "design_constraint",
9393
+ name: finding.code,
9394
+ metadata: {
9395
+ code: finding.code,
9396
+ label: this.labelForCode(finding.code),
9397
+ mostRecentMessage: finding.message,
9398
+ mostRecentSeverity: finding.severity
9399
+ }
9400
+ });
9401
+ seenConstraintIds.add(finding.code);
9402
+ }
9403
+ const fileId = finding.file;
9404
+ const edgeMetadata = {
9405
+ message: finding.message,
9406
+ severity: finding.severity
9407
+ };
9408
+ if (finding.line !== void 0) edgeMetadata.line = finding.line;
9409
+ if (finding.evidence !== void 0) edgeMetadata.evidence = finding.evidence;
9410
+ if (finding.runId !== void 0) edgeMetadata.runId = finding.runId;
9411
+ const before = this.store.getEdges({
9412
+ from: fileId,
9413
+ to: constraintId,
9414
+ type: "violates_design"
9415
+ });
9416
+ this.store.addEdge({
9417
+ from: fileId,
9418
+ to: constraintId,
9419
+ type: "violates_design",
9420
+ metadata: edgeMetadata
9421
+ });
9422
+ if (before.length === 0) edgesAdded += 1;
9423
+ }
9424
+ return { constraintsAdded, edgesAdded };
9425
+ }
9426
+ labelForCode(code) {
9427
+ for (const prefix of Object.keys(CODE_PREFIX_LABELS)) {
9428
+ if (code.startsWith(prefix)) return CODE_PREFIX_LABELS[prefix];
9429
+ }
9430
+ return "Design constraint";
9431
+ }
9310
9432
  };
9311
9433
 
9312
9434
  // src/feedback/GraphFeedbackAdapter.ts
@@ -9834,7 +9956,7 @@ var ConflictPredictor = class {
9834
9956
  };
9835
9957
 
9836
9958
  // src/index.ts
9837
- var VERSION = "0.6.0";
9959
+ var VERSION = "0.9.0";
9838
9960
  export {
9839
9961
  ALL_EXTRACTORS,
9840
9962
  ApiPathExtractor,
@@ -9914,5 +10036,6 @@ export {
9914
10036
  project,
9915
10037
  queryTraceability,
9916
10038
  resolveSkipDirs,
9917
- saveGraph
10039
+ saveGraph,
10040
+ skipDirGlobs
9918
10041
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harness-engineering/graph",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "license": "MIT",
5
5
  "description": "Knowledge graph for context assembly in Harness Engineering",
6
6
  "main": "./dist/index.js",