patram 0.6.2 → 0.8.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.
Files changed (55) hide show
  1. package/lib/build-graph-identity.d.ts +40 -0
  2. package/lib/build-graph.d.ts +11 -0
  3. package/lib/build-graph.types.d.ts +24 -0
  4. package/lib/claim-helpers.d.ts +20 -0
  5. package/lib/cli-help-metadata.js +48 -9
  6. package/lib/command-output.js +1 -0
  7. package/lib/document-node-identity.d.ts +47 -0
  8. package/lib/inspect-reverse-references.js +184 -0
  9. package/lib/layout-incoming-references.js +105 -0
  10. package/lib/layout-incoming-summary-lines.js +21 -0
  11. package/lib/list-source-files.d.ts +29 -0
  12. package/lib/load-patram-config.d.ts +384 -0
  13. package/lib/load-patram-config.types.d.ts +45 -0
  14. package/lib/load-project-graph.d.ts +35 -0
  15. package/lib/output-view.types.d.ts +88 -0
  16. package/lib/output-view.types.ts +19 -2
  17. package/lib/overlay-graph.d.ts +43 -0
  18. package/lib/overlay-graph.js +191 -0
  19. package/lib/parse-claims.d.ts +40 -0
  20. package/lib/parse-claims.types.d.ts +31 -0
  21. package/lib/parse-cli-arguments-helpers.js +11 -4
  22. package/lib/parse-cli-arguments.types.ts +8 -2
  23. package/lib/parse-jsdoc-blocks.d.ts +15 -0
  24. package/lib/parse-jsdoc-claims.d.ts +9 -0
  25. package/lib/parse-jsdoc-prose.d.ts +28 -0
  26. package/lib/parse-markdown-claims.d.ts +14 -0
  27. package/lib/parse-markdown-directives.d.ts +34 -0
  28. package/lib/parse-where-clause.d.ts +75 -0
  29. package/lib/parse-where-clause.js +157 -37
  30. package/lib/parse-where-clause.types.d.ts +63 -0
  31. package/lib/parse-yaml-claims.d.ts +38 -0
  32. package/lib/patram-cli.js +66 -1
  33. package/lib/patram-config.d.ts +106 -0
  34. package/lib/patram-config.types.d.ts +14 -0
  35. package/lib/patram.d.ts +73 -0
  36. package/lib/patram.js +3 -0
  37. package/lib/query-graph.d.ts +68 -0
  38. package/lib/query-graph.js +27 -24
  39. package/lib/query-inspection.d.ts +86 -0
  40. package/lib/render-cli-help.js +1 -1
  41. package/lib/render-json-output.js +91 -62
  42. package/lib/render-output-view.js +58 -3
  43. package/lib/render-plain-output.js +46 -3
  44. package/lib/render-rich-output.js +50 -5
  45. package/lib/resolve-patram-graph-config.d.ts +9 -0
  46. package/lib/reverse-reference-test-helpers.js +76 -0
  47. package/lib/show-document.js +44 -6
  48. package/lib/source-file-defaults.d.ts +5 -0
  49. package/lib/tagged-fenced-block-error.d.ts +10 -0
  50. package/lib/tagged-fenced-block-markdown.d.ts +47 -0
  51. package/lib/tagged-fenced-block-metadata.d.ts +26 -0
  52. package/lib/tagged-fenced-block-parser.d.ts +61 -0
  53. package/lib/tagged-fenced-blocks.d.ts +39 -0
  54. package/lib/tagged-fenced-blocks.types.d.ts +32 -0
  55. package/package.json +13 -2
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Resolve the node key for one mapped claim.
3
+ *
4
+ * @param {{ field: string, key?: 'path' | 'value', class: string }} node_mapping
5
+ * @param {PatramClaim} claim
6
+ * @param {Map<string, string>} document_entity_keys
7
+ * @returns {string}
8
+ */
9
+ export function resolveNodeKey(node_mapping: {
10
+ field: string;
11
+ key?: "path" | "value";
12
+ class: string;
13
+ }, claim: PatramClaim, document_entity_keys: Map<string, string>): string;
14
+ /**
15
+ * Resolve one edge target key and canonical path.
16
+ *
17
+ * @param {string} target_class
18
+ * @param {'path' | 'value'} target_type
19
+ * @param {PatramClaim} claim
20
+ * @param {Map<string, string>} document_entity_keys
21
+ * @param {Map<string, DocumentNodeReference>} document_node_references
22
+ * @param {Set<string>} document_paths
23
+ * @returns {{ class_name: string, key: string, path?: string }}
24
+ */
25
+ export function resolveTargetReference(target_class: string, target_type: "path" | "value", claim: PatramClaim, document_entity_keys: Map<string, string>, document_node_references: Map<string, DocumentNodeReference>, document_paths: Set<string>): {
26
+ class_name: string;
27
+ key: string;
28
+ path?: string;
29
+ };
30
+ /**
31
+ * Attach one canonical path to a graph node.
32
+ *
33
+ * @param {GraphNode} graph_node
34
+ * @param {string | undefined} source_path
35
+ */
36
+ export function setCanonicalPath(graph_node: GraphNode, source_path: string | undefined): void;
37
+ import type { PatramClaim } from './parse-claims.types.ts';
38
+ import type { DocumentNodeReference } from './document-node-identity.js';
39
+ import type { GraphNode } from './build-graph.types.ts';
40
+ export { collectDocumentEntityKeys, collectDocumentNodeReferences, normalizeRepoRelativePath, resolveDocumentNodeId } from "./document-node-identity.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Build a Patram graph from semantic config and parsed claims.
3
+ *
4
+ * @param {PatramConfig} patram_config
5
+ * @param {PatramClaim[]} claims
6
+ * @returns {BuildGraphResult}
7
+ */
8
+ export function buildGraph(patram_config: PatramConfig, claims: PatramClaim[]): BuildGraphResult;
9
+ import type { PatramConfig } from './patram-config.types.ts';
10
+ import type { PatramClaim } from './parse-claims.types.ts';
11
+ import type { BuildGraphResult } from './build-graph.types.ts';
@@ -0,0 +1,24 @@
1
+ import type { ClaimOrigin } from './parse-claims.types.ts';
2
+ export interface GraphNode {
3
+ $class?: string;
4
+ $id?: string;
5
+ $path?: string;
6
+ id: string;
7
+ kind?: string;
8
+ key?: string;
9
+ path?: string;
10
+ title?: string;
11
+ [field: string]: string | string[] | undefined;
12
+ }
13
+ export interface GraphEdge {
14
+ from: string;
15
+ id: string;
16
+ origin: ClaimOrigin;
17
+ relation: string;
18
+ to: string;
19
+ }
20
+ export interface BuildGraphResult {
21
+ document_node_ids?: Record<string, string>;
22
+ edges: GraphEdge[];
23
+ nodes: Record<string, GraphNode>;
24
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @param {string} target_value
3
+ * @returns {boolean}
4
+ */
5
+ export function isPathLikeTarget(target_value: string): boolean;
6
+ /**
7
+ * @param {string} file_path
8
+ * @param {number} claim_number
9
+ * @param {string} claim_type
10
+ * @param {PatramClaimFields} claim_fields
11
+ * @returns {PatramClaim}
12
+ */
13
+ export function createClaim(file_path: string, claim_number: number, claim_type: string, claim_fields: PatramClaimFields): PatramClaim;
14
+ /**
15
+ * @param {string} file_path
16
+ * @returns {string}
17
+ */
18
+ export function getFileExtension(file_path: string): string;
19
+ import type { PatramClaimFields } from './parse-claims.types.ts';
20
+ import type { PatramClaim } from './parse-claims.types.ts';
@@ -50,6 +50,7 @@ export const COMMAND_NAMES = /** @type {const} */ ([
50
50
  'fields',
51
51
  'query',
52
52
  'queries',
53
+ 'refs',
53
54
  'show',
54
55
  ]);
55
56
 
@@ -146,12 +147,12 @@ const COMMAND_DEFINITIONS = {
146
147
  ]),
147
148
  examples: [
148
149
  'patram query active-plans',
149
- 'patram query --where "tracked_in=doc:docs/plans/v0/worktracking-agent-guidance.md"',
150
- 'patram query --where "status not in [done, dropped, superseded]"',
151
- 'patram query --where "$class=plan and none(in:tracked_in, $class=decision)"',
152
- 'patram query --where "count(in:decided_by, $class=task) = 0"',
150
+ "patram query --where 'tracked_in=doc:docs/plans/v0/worktracking-agent-guidance.md'",
151
+ "patram query --where 'status not in [done, dropped, superseded]'",
152
+ "patram query --where '$class=plan and none(in:tracked_in, $class=decision)'",
153
+ "patram query --where 'count(in:decided_by, $class=task) = 0'",
153
154
  'patram query ready-tasks --explain',
154
- 'patram query --where "$class=decision and status=accepted and count(in:decided_by, $class=task) = 0" --lint',
155
+ "patram query --where '$class=decision and status=accepted and count(in:decided_by, $class=task) = 0' --lint",
155
156
  'patram query active-plans --limit 10 --offset 20',
156
157
  ],
157
158
  extra_positionals_message:
@@ -161,12 +162,12 @@ const COMMAND_DEFINITIONS = {
161
162
  min_positionals: 0,
162
163
  missing_argument_examples: [
163
164
  'patram query active-plans',
164
- 'patram query --where "tracked_in=doc:docs/plans/v0/worktracking-agent-guidance.md"',
165
+ "patram query --where 'tracked_in=doc:docs/plans/v0/worktracking-agent-guidance.md'",
165
166
  ],
166
- missing_argument_label: '<name> or --where "<clause>"',
167
+ missing_argument_label: "<name> or --where '<clause>'",
167
168
  missing_usage_lines: [
168
169
  'patram query <name> [options]',
169
- 'patram query --where "<clause>" [options]',
170
+ "patram query --where '<clause>' [options]",
170
171
  ],
171
172
  option_column_width: 19,
172
173
  options: [
@@ -214,7 +215,7 @@ const COMMAND_DEFINITIONS = {
214
215
  ],
215
216
  usage_lines: [
216
217
  'patram query <name> [options]',
217
- 'patram query --where "<clause>" [options]',
218
+ "patram query --where '<clause>' [options]",
218
219
  ],
219
220
  },
220
221
  queries: {
@@ -243,6 +244,44 @@ const COMMAND_DEFINITIONS = {
243
244
  summary: 'List the stored queries defined in the project configuration.',
244
245
  usage_lines: ['patram queries [options]'],
245
246
  },
247
+ refs: {
248
+ allowed_option_names: new Set(['where']),
249
+ examples: [
250
+ 'patram refs docs/decisions/query-language.md',
251
+ "patram refs docs/decisions/query-language.md --where '$class=document'",
252
+ 'patram refs docs/decisions/query-language.md --json',
253
+ ],
254
+ extra_positionals_message: 'Refs accepts exactly one file path.',
255
+ help_topics: ['query-language'],
256
+ max_positionals: 1,
257
+ min_positionals: 1,
258
+ missing_argument_examples: [
259
+ 'patram refs docs/decisions/query-language.md',
260
+ "patram refs docs/patram.md --where '$class=document'",
261
+ ],
262
+ missing_argument_label: '<file>',
263
+ missing_usage_lines: ['patram refs <file>'],
264
+ option_column_width: 19,
265
+ options: [
266
+ {
267
+ description: 'Filter incoming source nodes with a where clause',
268
+ label: '--where <clause>',
269
+ },
270
+ {
271
+ description: 'Print plain text output',
272
+ label: '--plain',
273
+ },
274
+ {
275
+ description: 'Print JSON output',
276
+ label: '--json',
277
+ },
278
+ ],
279
+ related: ['show', 'query'],
280
+ root_summary: 'Inspect incoming graph references for one file',
281
+ summary:
282
+ 'Inspect incoming graph references for one file, grouped by relation.',
283
+ usage_lines: ['patram refs <file> [options]'],
284
+ },
246
285
  show: {
247
286
  allowed_option_names: new Set(),
248
287
  examples: ['patram show docs/patram.md', 'patram show lib/patram-cli.js'],
@@ -78,6 +78,7 @@ export function shouldPageCommandOutput(parsed_command, output_stream) {
78
78
  output_stream.isTTY === true &&
79
79
  (parsed_command.command_name === 'fields' ||
80
80
  parsed_command.command_name === 'query' ||
81
+ parsed_command.command_name === 'refs' ||
81
82
  parsed_command.command_name === 'show')
82
83
  );
83
84
  }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @typedef {{
3
+ * class_name: string,
4
+ * id: string,
5
+ * key: string,
6
+ * path: string,
7
+ * }} DocumentNodeReference
8
+ */
9
+ /**
10
+ * Collect semantic entity keys defined by canonical documents.
11
+ *
12
+ * @param {Record<string, MappingDefinition>} mappings
13
+ * @param {PatramClaim[]} claims
14
+ * @returns {Map<string, string>}
15
+ */
16
+ export function collectDocumentEntityKeys(mappings: Record<string, MappingDefinition>, claims: PatramClaim[]): Map<string, string>;
17
+ /**
18
+ * Collect canonical graph identities for document-backed source paths.
19
+ *
20
+ * @param {Record<string, MappingDefinition>} mappings
21
+ * @param {PatramClaim[]} claims
22
+ * @returns {Map<string, DocumentNodeReference>}
23
+ */
24
+ export function collectDocumentNodeReferences(mappings: Record<string, MappingDefinition>, claims: PatramClaim[]): Map<string, DocumentNodeReference>;
25
+ /**
26
+ * Resolve the canonical node id for a source document path.
27
+ *
28
+ * @param {Record<string, string> | undefined} document_node_ids
29
+ * @param {string} document_path
30
+ * @returns {string}
31
+ */
32
+ export function resolveDocumentNodeId(document_node_ids: Record<string, string> | undefined, document_path: string): string;
33
+ /**
34
+ * Normalize one repo-relative source path.
35
+ *
36
+ * @param {string} source_path
37
+ * @returns {string}
38
+ */
39
+ export function normalizeRepoRelativePath(source_path: string): string;
40
+ export type DocumentNodeReference = {
41
+ class_name: string;
42
+ id: string;
43
+ key: string;
44
+ path: string;
45
+ };
46
+ import type { MappingDefinition } from './patram-config.types.ts';
47
+ import type { PatramClaim } from './parse-claims.types.ts';
@@ -0,0 +1,184 @@
1
+ /**
2
+ * @import { BuildGraphResult, GraphNode } from './build-graph.types.ts';
3
+ * @import { PatramDiagnostic, PatramRepoConfig } from './load-patram-config.types.ts';
4
+ */
5
+
6
+ import {
7
+ normalizeRepoRelativePath,
8
+ resolveDocumentNodeId,
9
+ } from './document-node-identity.js';
10
+ import { queryGraph } from './query-graph.js';
11
+
12
+ /**
13
+ * Inspect incoming graph edges for one canonical target file.
14
+ *
15
+ * @param {BuildGraphResult} graph
16
+ * @param {string} target_file_path
17
+ * @param {PatramRepoConfig | undefined} repo_config
18
+ * @param {string | undefined} where_clause
19
+ * @returns {{
20
+ * diagnostics: PatramDiagnostic[],
21
+ * incoming: Record<string, GraphNode[]>,
22
+ * node: GraphNode,
23
+ * }}
24
+ */
25
+ export function inspectReverseReferences(
26
+ graph,
27
+ target_file_path,
28
+ repo_config,
29
+ where_clause,
30
+ ) {
31
+ const normalized_target_path = normalizeRepoRelativePath(target_file_path);
32
+ const target_node_id = resolveDocumentNodeId(
33
+ graph.document_node_ids,
34
+ normalized_target_path,
35
+ );
36
+ const target_node =
37
+ graph.nodes[target_node_id] ??
38
+ createFallbackTargetNode(target_node_id, normalized_target_path);
39
+ const allowed_source_node_ids = where_clause
40
+ ? resolveAllowedSourceNodeIds(graph, where_clause, repo_config)
41
+ : null;
42
+
43
+ if (allowed_source_node_ids && 'diagnostics' in allowed_source_node_ids) {
44
+ return {
45
+ diagnostics: allowed_source_node_ids.diagnostics,
46
+ incoming: {},
47
+ node: target_node,
48
+ };
49
+ }
50
+
51
+ return {
52
+ diagnostics: [],
53
+ incoming: collectIncomingGroups(
54
+ graph,
55
+ target_node.id,
56
+ allowed_source_node_ids,
57
+ ),
58
+ node: target_node,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * @param {BuildGraphResult} graph
64
+ * @param {string} target_node_id
65
+ * @param {Set<string> | null} allowed_source_node_ids
66
+ * @returns {Record<string, GraphNode[]>}
67
+ */
68
+ function collectIncomingGroups(graph, target_node_id, allowed_source_node_ids) {
69
+ /** @type {Map<string, Map<string, GraphNode>>} */
70
+ const grouped_incoming = new Map();
71
+
72
+ for (const graph_edge of graph.edges) {
73
+ if (graph_edge.to !== target_node_id) {
74
+ continue;
75
+ }
76
+
77
+ if (
78
+ allowed_source_node_ids &&
79
+ !allowed_source_node_ids.has(graph_edge.from)
80
+ ) {
81
+ continue;
82
+ }
83
+
84
+ const source_node = graph.nodes[graph_edge.from];
85
+
86
+ if (!source_node) {
87
+ continue;
88
+ }
89
+
90
+ let relation_sources = grouped_incoming.get(graph_edge.relation);
91
+
92
+ if (!relation_sources) {
93
+ relation_sources = new Map();
94
+ grouped_incoming.set(graph_edge.relation, relation_sources);
95
+ }
96
+
97
+ relation_sources.set(source_node.id, source_node);
98
+ }
99
+
100
+ return formatIncomingGroups(grouped_incoming);
101
+ }
102
+
103
+ /**
104
+ * @param {BuildGraphResult} graph
105
+ * @param {string} where_clause
106
+ * @param {PatramRepoConfig | undefined} repo_config
107
+ * @returns {{ diagnostics: PatramDiagnostic[] } | Set<string>}
108
+ */
109
+ function resolveAllowedSourceNodeIds(graph, where_clause, repo_config) {
110
+ const query_result = queryGraph(
111
+ graph,
112
+ where_clause,
113
+ repo_config ?? {
114
+ fields: {},
115
+ include: [],
116
+ queries: {},
117
+ },
118
+ );
119
+
120
+ if (query_result.diagnostics.length > 0) {
121
+ return {
122
+ diagnostics: query_result.diagnostics,
123
+ };
124
+ }
125
+
126
+ return new Set(query_result.nodes.map((graph_node) => graph_node.id));
127
+ }
128
+
129
+ /**
130
+ * @param {Map<string, Map<string, GraphNode>>} grouped_incoming
131
+ * @returns {Record<string, GraphNode[]>}
132
+ */
133
+ function formatIncomingGroups(grouped_incoming) {
134
+ /** @type {Record<string, GraphNode[]>} */
135
+ const incoming = {};
136
+
137
+ for (const relation_name of [...grouped_incoming.keys()].sort(
138
+ compareStrings,
139
+ )) {
140
+ const relation_sources = /** @type {Map<string, GraphNode>} */ (
141
+ grouped_incoming.get(relation_name)
142
+ );
143
+
144
+ incoming[relation_name] = [...relation_sources.values()].sort(
145
+ compareGraphNodes,
146
+ );
147
+ }
148
+
149
+ return incoming;
150
+ }
151
+
152
+ /**
153
+ * @param {string} node_id
154
+ * @param {string} target_path
155
+ * @returns {GraphNode}
156
+ */
157
+ function createFallbackTargetNode(node_id, target_path) {
158
+ return {
159
+ $class: 'document',
160
+ $id: node_id,
161
+ $path: target_path,
162
+ id: node_id,
163
+ path: target_path,
164
+ title: target_path,
165
+ };
166
+ }
167
+
168
+ /**
169
+ * @param {GraphNode} left_node
170
+ * @param {GraphNode} right_node
171
+ * @returns {number}
172
+ */
173
+ function compareGraphNodes(left_node, right_node) {
174
+ return compareStrings(left_node.id, right_node.id);
175
+ }
176
+
177
+ /**
178
+ * @param {string} left_value
179
+ * @param {string} right_value
180
+ * @returns {number}
181
+ */
182
+ function compareStrings(left_value, right_value) {
183
+ return left_value.localeCompare(right_value, 'en');
184
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @import { OutputNodeItem } from './output-view.types.ts';
3
+ */
4
+
5
+ import { formatOutputNodeMetadataRows } from './format-output-metadata.js';
6
+ import { formatNodeHeader } from './format-node-header.js';
7
+ import { formatOutputItemBlock } from './format-output-item-block.js';
8
+
9
+ /**
10
+ * Layout grouped incoming references as plain text lines.
11
+ *
12
+ * @param {Record<string, OutputNodeItem[]>} incoming
13
+ * @param {{
14
+ * format_node_header?: (output_item: OutputNodeItem) => string,
15
+ * format_relation_header?: (relation_name: string, relation_count: number) => string,
16
+ * }} layout_options
17
+ * @returns {string[]}
18
+ */
19
+ export function layoutIncomingReferenceLines(incoming, layout_options = {}) {
20
+ const format_node_header =
21
+ layout_options.format_node_header ?? defaultNodeHeaderFormatter;
22
+ const format_relation_header =
23
+ layout_options.format_relation_header ?? defaultRelationHeaderFormatter;
24
+ /** @type {string[]} */
25
+ const output_lines = [];
26
+
27
+ for (const relation_name of Object.keys(incoming)) {
28
+ if (output_lines.length > 0) {
29
+ output_lines.push('');
30
+ }
31
+
32
+ const relation_sources = incoming[relation_name];
33
+
34
+ output_lines.push(
35
+ format_relation_header(relation_name, relation_sources.length),
36
+ );
37
+ output_lines.push(
38
+ ...layoutIncomingRelationSources(relation_sources, format_node_header),
39
+ );
40
+ }
41
+
42
+ return output_lines;
43
+ }
44
+
45
+ /**
46
+ * @param {OutputNodeItem} output_item
47
+ * @returns {string}
48
+ */
49
+ function defaultNodeHeaderFormatter(output_item) {
50
+ return formatNodeHeader(output_item);
51
+ }
52
+
53
+ /**
54
+ * @param {string} relation_name
55
+ * @param {number} relation_count
56
+ * @returns {string}
57
+ */
58
+ function defaultRelationHeaderFormatter(relation_name, relation_count) {
59
+ return `${relation_name} (${relation_count})`;
60
+ }
61
+
62
+ /**
63
+ * @param {OutputNodeItem[]} relation_sources
64
+ * @param {(output_item: OutputNodeItem) => string} format_node_header
65
+ * @returns {string[]}
66
+ */
67
+ function layoutIncomingRelationSources(relation_sources, format_node_header) {
68
+ /** @type {string[]} */
69
+ const output_lines = [];
70
+
71
+ for (const [item_index, output_item] of relation_sources.entries()) {
72
+ if (item_index > 0) {
73
+ output_lines.push('');
74
+ }
75
+
76
+ output_lines.push(
77
+ ...indentIncomingNodeBlock(
78
+ formatIncomingNodeBlock(output_item, format_node_header),
79
+ ),
80
+ );
81
+ }
82
+
83
+ return output_lines;
84
+ }
85
+
86
+ /**
87
+ * @param {OutputNodeItem} output_item
88
+ * @param {(output_item: OutputNodeItem) => string} format_node_header
89
+ * @returns {string}
90
+ */
91
+ function formatIncomingNodeBlock(output_item, format_node_header) {
92
+ return formatOutputItemBlock({
93
+ header: format_node_header(output_item),
94
+ metadata_rows: formatOutputNodeMetadataRows(output_item),
95
+ title: output_item.title,
96
+ });
97
+ }
98
+
99
+ /**
100
+ * @param {string} block
101
+ * @returns {string[]}
102
+ */
103
+ function indentIncomingNodeBlock(block) {
104
+ return block.split('\n').map((line) => (line.length > 0 ? ` ${line}` : ''));
105
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Layout compact incoming-summary lines for `show`.
3
+ *
4
+ * @param {Record<string, number>} incoming_summary
5
+ * @returns {string[]}
6
+ */
7
+ export function layoutIncomingSummaryLines(incoming_summary) {
8
+ const relation_names = Object.keys(incoming_summary);
9
+ const output_lines = ['incoming refs:'];
10
+
11
+ if (relation_names.length === 0) {
12
+ output_lines.push(' none');
13
+ return output_lines;
14
+ }
15
+
16
+ for (const relation_name of relation_names) {
17
+ output_lines.push(` ${relation_name}: ${incoming_summary[relation_name]}`);
18
+ }
19
+
20
+ return output_lines;
21
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Source file scanning.
3
+ *
4
+ * Expands include globs into stable repo-relative file lists for indexing and
5
+ * broken-link validation.
6
+ *
7
+ * Kind: scan
8
+ * Status: active
9
+ * Tracked in: ../docs/plans/v0/source-anchor-dogfooding.md
10
+ * Decided by: ../docs/decisions/source-scan.md
11
+ * @patram
12
+ * @see {@link ./load-project-graph.js}
13
+ * @see {@link ../docs/decisions/source-scan.md}
14
+ */
15
+ /**
16
+ * List source files matched by Patram include globs.
17
+ *
18
+ * @param {string[]} include_patterns
19
+ * @param {string} [project_directory]
20
+ * @returns {Promise<string[]>}
21
+ */
22
+ export function listSourceFiles(include_patterns: string[], project_directory?: string): Promise<string[]>;
23
+ /**
24
+ * List repo files available for broken-link validation.
25
+ *
26
+ * @param {string} [project_directory]
27
+ * @returns {Promise<string[]>}
28
+ */
29
+ export function listRepoFiles(project_directory?: string): Promise<string[]>;