prodex 2.0.0 → 2.1.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 (119) hide show
  1. package/README.md +369 -146
  2. package/dist/app/dependency-source-provider.d.ts +2 -0
  3. package/dist/app/dependency-source-provider.js +66 -0
  4. package/dist/app/entry-resolver.d.ts +7 -0
  5. package/dist/app/entry-resolver.js +26 -0
  6. package/dist/app/execute-run.d.ts +2 -2
  7. package/dist/app/execute-run.js +138 -36
  8. package/dist/app/git-source-provider.d.ts +4 -0
  9. package/dist/app/git-source-provider.js +551 -0
  10. package/dist/app/grep-source-provider.d.ts +2 -0
  11. package/dist/app/grep-source-provider.js +226 -0
  12. package/dist/app/planner.d.ts +12 -0
  13. package/dist/app/planner.js +106 -0
  14. package/dist/app/planners/attachment-options.d.ts +2 -0
  15. package/dist/app/planners/attachment-options.js +39 -0
  16. package/dist/app/planners/git-plan.d.ts +13 -0
  17. package/dist/app/planners/git-plan.js +141 -0
  18. package/dist/app/planners/grep-plan.d.ts +13 -0
  19. package/dist/app/planners/grep-plan.js +115 -0
  20. package/dist/app/planners/list-utils.d.ts +1 -0
  21. package/dist/app/planners/list-utils.js +6 -0
  22. package/dist/app/planners/pack-plan.d.ts +13 -0
  23. package/dist/app/planners/pack-plan.js +65 -0
  24. package/dist/app/planners/scope-plan.d.ts +17 -0
  25. package/dist/app/planners/scope-plan.js +143 -0
  26. package/dist/app/planners/trace-plan.d.ts +13 -0
  27. package/dist/app/planners/trace-plan.js +77 -0
  28. package/dist/app/source-collector.d.ts +5 -0
  29. package/dist/app/source-collector.js +33 -0
  30. package/dist/app/target-resolver.d.ts +7 -0
  31. package/dist/app/target-resolver.js +110 -0
  32. package/dist/cli/cli-input.js +70 -19
  33. package/dist/cli/flag-specs.d.ts +7 -3
  34. package/dist/cli/flag-specs.js +58 -7
  35. package/dist/cli/help-specs.d.ts +205 -0
  36. package/dist/cli/help-specs.js +136 -0
  37. package/dist/cli/help.js +107 -28
  38. package/dist/cli/report-command.js +59 -26
  39. package/dist/commands/{run-command.d.ts → git-command.d.ts} +3 -3
  40. package/dist/commands/git-command.js +7 -0
  41. package/dist/commands/grep-command.d.ts +11 -0
  42. package/dist/commands/grep-command.js +7 -0
  43. package/dist/commands/pack-command.d.ts +11 -0
  44. package/dist/commands/pack-command.js +7 -0
  45. package/dist/commands/scope-command.d.ts +12 -0
  46. package/dist/commands/scope-command.js +7 -0
  47. package/dist/commands/shared-runner.d.ts +12 -0
  48. package/dist/commands/shared-runner.js +30 -0
  49. package/dist/commands/trace-command.d.ts +11 -0
  50. package/dist/commands/trace-command.js +7 -0
  51. package/dist/config/default-config.js +7 -11
  52. package/dist/config/load.d.ts +1 -0
  53. package/dist/config/load.js +269 -4
  54. package/dist/config/migration/detect.d.ts +2 -1
  55. package/dist/config/migration/detect.js +10 -2
  56. package/dist/config/migration/messages.js +12 -11
  57. package/dist/config/migration/transform.js +77 -32
  58. package/dist/filesystem/binary.d.ts +2 -0
  59. package/dist/filesystem/binary.js +30 -0
  60. package/dist/filesystem/entry-discovery.d.ts +14 -0
  61. package/dist/filesystem/entry-discovery.js +78 -0
  62. package/dist/filesystem/file-set.d.ts +7 -0
  63. package/dist/filesystem/file-set.js +57 -0
  64. package/dist/filesystem/glob-scan.d.ts +6 -2
  65. package/dist/filesystem/glob-scan.js +12 -7
  66. package/dist/filesystem/path-patterns.d.ts +20 -0
  67. package/dist/filesystem/path-patterns.js +70 -0
  68. package/dist/index.js +51 -29
  69. package/dist/output/markdown-sections.d.ts +5 -0
  70. package/dist/output/markdown-sections.js +103 -0
  71. package/dist/output/markdown-toc.d.ts +31 -0
  72. package/dist/output/markdown-toc.js +287 -0
  73. package/dist/output/markdown.d.ts +5 -8
  74. package/dist/output/markdown.js +80 -127
  75. package/dist/output/produce-output.d.ts +4 -1
  76. package/dist/output/produce-output.js +6 -7
  77. package/dist/output/render-helpers.d.ts +4 -0
  78. package/dist/output/render-helpers.js +14 -0
  79. package/dist/output/text.d.ts +2 -2
  80. package/dist/output/text.js +47 -8
  81. package/dist/resolvers/js/resolve-alias.js +10 -41
  82. package/dist/resolvers/php/bindings.js +6 -6
  83. package/dist/resolvers/php/extract-imports.d.ts +19 -7
  84. package/dist/resolvers/php/extract-imports.js +146 -26
  85. package/dist/resolvers/php/php-resolver.js +55 -24
  86. package/dist/resolvers/php/psr4.d.ts +1 -1
  87. package/dist/resolvers/php/psr4.js +7 -1
  88. package/dist/runtime/shell-command-runner.d.ts +5 -0
  89. package/dist/runtime/shell-command-runner.js +118 -0
  90. package/dist/tracing/collect-trace.d.ts +10 -0
  91. package/dist/tracing/collect-trace.js +23 -0
  92. package/dist/tracing/exclude.d.ts +0 -5
  93. package/dist/tracing/exclude.js +16 -12
  94. package/dist/tracing/follow-chain.js +2 -2
  95. package/dist/types/app.types.d.ts +38 -8
  96. package/dist/types/artifact.types.d.ts +50 -0
  97. package/dist/types/artifact.types.js +2 -0
  98. package/dist/types/cli.types.d.ts +52 -4
  99. package/dist/types/config.types.d.ts +37 -20
  100. package/dist/types/index.d.ts +2 -0
  101. package/dist/types/index.js +2 -0
  102. package/dist/types/output.types.d.ts +12 -3
  103. package/dist/types/planner.types.d.ts +76 -0
  104. package/dist/types/planner.types.js +2 -0
  105. package/dist/types/resolver.types.d.ts +1 -1
  106. package/dist/types/tracing.types.d.ts +0 -11
  107. package/package.json +2 -2
  108. package/schema/prodex.schema.json +59 -41
  109. package/dist/app/run-plans.d.ts +0 -11
  110. package/dist/app/run-plans.js +0 -61
  111. package/dist/commands/profiles-command.d.ts +0 -6
  112. package/dist/commands/profiles-command.js +0 -16
  113. package/dist/commands/run-command.js +0 -17
  114. package/dist/config/build-config.d.ts +0 -13
  115. package/dist/config/build-config.js +0 -127
  116. package/dist/tracing/include-files.d.ts +0 -2
  117. package/dist/tracing/include-files.js +0 -36
  118. package/dist/tracing/trace-run.d.ts +0 -2
  119. package/dist/tracing/trace-run.js +0 -21
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getDynamicFence = getDynamicFence;
7
+ exports.renderFileSection = renderFileSection;
8
+ exports.renderGenericSection = renderGenericSection;
9
+ exports.renderMdCmdResults = renderMdCmdResults;
10
+ const path_1 = __importDefault(require("path"));
11
+ const render_constants_1 = require("./render-constants");
12
+ const read_file_1 = require("../filesystem/read-file");
13
+ const render_helpers_1 = require("./render-helpers");
14
+ function getDynamicFence(output) {
15
+ const matches = output.match(/`+/g);
16
+ if (!matches)
17
+ return "```";
18
+ const maxLength = Math.max(...matches.map((m) => m.length));
19
+ if (maxLength >= 3) {
20
+ return "`".repeat(maxLength + 1);
21
+ }
22
+ return "```";
23
+ }
24
+ function renderFileSection(file, index, root, navLine) {
25
+ const relativePath = (0, read_file_1.rel)(file.path, root);
26
+ const ext = path_1.default.extname(file.path).toLowerCase();
27
+ const lang = render_constants_1.LANG_MAP[ext] || "txt";
28
+ const code = file.readError ? `Error reading file: ${file.readError}` : file.content.trimEnd();
29
+ const fence = getDynamicFence(code);
30
+ return [
31
+ `---\n#### ${index + 1}`,
32
+ "\n",
33
+ `\` File: ${relativePath} \` ${navLine}`,
34
+ "",
35
+ fence + lang,
36
+ code,
37
+ fence,
38
+ "",
39
+ ].join("\n");
40
+ }
41
+ function renderGenericSection(sec, index, navLine) {
42
+ const lang = sec.language || "txt";
43
+ const fence = getDynamicFence(sec.content);
44
+ return [
45
+ `---\n<a id="sec-${index + 1}"></a>`,
46
+ `## ${sec.title}`,
47
+ navLine,
48
+ "",
49
+ fence + lang,
50
+ sec.content.trimEnd(),
51
+ fence,
52
+ "",
53
+ ].join("\n");
54
+ }
55
+ function renderMdCmdResults(cmdResults, root = process.cwd(), totalCount = 0, sectionCount = 0, orderedAnchors) {
56
+ if (!cmdResults || cmdResults.length === 0)
57
+ return "";
58
+ const lines = ["\n---", "# Command Outputs", ""];
59
+ const cmdCount = cmdResults.length;
60
+ for (let i = 0; i < cmdCount; i++) {
61
+ const res = cmdResults[i];
62
+ const relativeDir = (0, read_file_1.rel)(res.cwd, root) || ".";
63
+ const durationSec = (res.durationMs / 1000).toFixed(1);
64
+ const fence = getDynamicFence(res.combinedOutput);
65
+ const anchorId = `cmd-${i + 1}`;
66
+ let navLine;
67
+ if (orderedAnchors) {
68
+ const idx = orderedAnchors.indexOf(anchorId);
69
+ const navParts = [];
70
+ if (idx > 0) {
71
+ navParts.push(`[Previous](#${orderedAnchors[idx - 1]})`);
72
+ }
73
+ navParts.push(`[Back to top](#index)`);
74
+ if (idx < orderedAnchors.length - 1) {
75
+ navParts.push(`[Next](#${orderedAnchors[idx + 1]})`);
76
+ }
77
+ navLine = navParts.join(" | ");
78
+ }
79
+ else {
80
+ const navParts = [];
81
+ if (i > 0) {
82
+ navParts.push(`[Previous](#cmd-${i})`);
83
+ }
84
+ else if (totalCount > 0) {
85
+ navParts.push(`[Previous](#${totalCount})`);
86
+ }
87
+ else if (sectionCount > 0) {
88
+ navParts.push(`[Previous](#sec-${sectionCount})`);
89
+ }
90
+ navParts.push(`[Back to top](#index)`);
91
+ if (i < cmdCount - 1) {
92
+ navParts.push(`[Next](#cmd-${i + 2})`);
93
+ }
94
+ navLine = navParts.join(" | ");
95
+ }
96
+ lines.push(`---`, `<a id="cmd-${i + 1}"></a>`, `## Command ${i + 1}: ${res.command}`, navLine, "", `- Status: ${res.status}`, `- Exit code: ${(0, render_helpers_1.formatExitCode)(res.exitCode)}`, `- Duration: ${durationSec}s`, `- Timed out: ${(0, render_helpers_1.formatTimeout)(res.timedOut)}`, `- Working directory: ${relativeDir}`, "");
97
+ if (res.errorMessage) {
98
+ lines.push(`- Error message: ${res.errorMessage}`, "");
99
+ }
100
+ lines.push(fence + "text", res.combinedOutput, fence, "");
101
+ }
102
+ return lines.join("\n");
103
+ }
@@ -0,0 +1,31 @@
1
+ import type { ArtifactMetadata, ArtifactSection, CommandOutputResult, MdTraceEntry } from "../types";
2
+ export declare function getLineBlocks(lines: string[]): boolean[];
3
+ export declare function analyzeTrace(content: string, count: number, sectionCount: number, cmdCount: number): {
4
+ listingStart: number;
5
+ listingEnd: number;
6
+ trace: MdTraceEntry[];
7
+ sectionTrace: MdTraceEntry[];
8
+ cmdTrace: MdTraceEntry[];
9
+ };
10
+ export declare function analyzeListingRange(lines: string[], startMarkerIndex: number, endMarkerIndex: number, count: number): {
11
+ listingStart: number;
12
+ listingEnd: number;
13
+ };
14
+ export declare function findFooterStartIndex(lines: string[], insideCodeFence: boolean[]): number;
15
+ export declare function findSectionStartIndexes(lines: string[], count: number, fallbackIndex: number, insideCodeFence: boolean[]): number[];
16
+ export declare function findGenericSectionStartIndexes(lines: string[], sectionCount: number, fallbackIndex: number, insideCodeFence: boolean[]): number[];
17
+ export declare function findCmdSectionStartIndexes(lines: string[], cmdCount: number, fallbackIndex: number, insideCodeFence: boolean[]): number[];
18
+ export declare function buildToc(opts: {
19
+ files: string[];
20
+ root: string;
21
+ count: number;
22
+ listingStart: number;
23
+ listingEnd: number;
24
+ trace: MdTraceEntry[] | null;
25
+ sectionTrace: MdTraceEntry[] | null;
26
+ cmdTrace: MdTraceEntry[] | null;
27
+ sections?: ArtifactSection[];
28
+ commandOutputs?: CommandOutputResult[];
29
+ withRanges: boolean;
30
+ metadata?: ArtifactMetadata;
31
+ }): string;
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getLineBlocks = getLineBlocks;
4
+ exports.analyzeTrace = analyzeTrace;
5
+ exports.analyzeListingRange = analyzeListingRange;
6
+ exports.findFooterStartIndex = findFooterStartIndex;
7
+ exports.findSectionStartIndexes = findSectionStartIndexes;
8
+ exports.findGenericSectionStartIndexes = findGenericSectionStartIndexes;
9
+ exports.findCmdSectionStartIndexes = findCmdSectionStartIndexes;
10
+ exports.buildToc = buildToc;
11
+ const render_constants_1 = require("./render-constants");
12
+ const read_file_1 = require("../filesystem/read-file");
13
+ const render_helpers_1 = require("./render-helpers");
14
+ function getLineBlocks(lines) {
15
+ const inside = new Array(lines.length).fill(false);
16
+ let activeFenceChar = null;
17
+ let activeFenceLength = 0;
18
+ for (let i = 0; i < lines.length; i++) {
19
+ const line = lines[i];
20
+ const trimmed = line.trim();
21
+ if (activeFenceChar) {
22
+ inside[i] = true;
23
+ const match = trimmed.match(/^([`~]{3,})\s*$/);
24
+ if (match) {
25
+ const char = match[1][0];
26
+ const length = match[1].length;
27
+ if (char === activeFenceChar && length >= activeFenceLength) {
28
+ activeFenceChar = null;
29
+ activeFenceLength = 0;
30
+ }
31
+ }
32
+ }
33
+ else {
34
+ const match = trimmed.match(/^([`~]{3,})/);
35
+ if (match) {
36
+ activeFenceChar = match[1][0];
37
+ activeFenceLength = match[1].length;
38
+ inside[i] = true;
39
+ }
40
+ }
41
+ }
42
+ return inside;
43
+ }
44
+ function analyzeTrace(content, count, sectionCount, cmdCount) {
45
+ const lines = content.split("\n");
46
+ const insideCodeFence = getLineBlocks(lines);
47
+ const startMarkerIndex = lines.findIndex((line) => line.trim() === "<!-- PRODEX_INDEX_LIST_START -->");
48
+ const endMarkerIndex = lines.findIndex((line) => line.trim() === "<!-- PRODEX_INDEX_LIST_END -->");
49
+ const totalItems = count + sectionCount + cmdCount;
50
+ const { listingStart, listingEnd } = analyzeListingRange(lines, startMarkerIndex, endMarkerIndex, totalItems);
51
+ const footerStartIndex = findFooterStartIndex(lines, insideCodeFence);
52
+ const sectionStarts = findSectionStartIndexes(lines, count, footerStartIndex, insideCodeFence);
53
+ const genericStarts = findGenericSectionStartIndexes(lines, sectionCount, footerStartIndex, insideCodeFence);
54
+ const cmdStarts = findCmdSectionStartIndexes(lines, cmdCount, footerStartIndex, insideCodeFence);
55
+ const allStarts = [
56
+ ...sectionStarts,
57
+ ...genericStarts,
58
+ ...cmdStarts,
59
+ footerStartIndex
60
+ ];
61
+ const uniqueStarts = [...new Set(allStarts)].sort((a, b) => a - b);
62
+ const getEndLine = (start) => {
63
+ if (start >= footerStartIndex)
64
+ return start;
65
+ const nextStart = uniqueStarts.find(val => val > start) ?? footerStartIndex;
66
+ return Math.max(start, nextStart - 1);
67
+ };
68
+ return {
69
+ listingStart,
70
+ listingEnd,
71
+ sectionTrace: genericStarts.map((startIndex, index) => {
72
+ const endIndex = getEndLine(startIndex);
73
+ return {
74
+ file: "",
75
+ anchor: index + 1,
76
+ startLine: startIndex + 1,
77
+ endLine: endIndex + 1,
78
+ };
79
+ }),
80
+ trace: sectionStarts.map((startIndex, index) => {
81
+ const endIndex = getEndLine(startIndex);
82
+ return {
83
+ file: "",
84
+ anchor: index + 1,
85
+ startLine: startIndex + 1,
86
+ endLine: endIndex + 1,
87
+ };
88
+ }),
89
+ cmdTrace: cmdStarts.map((startIndex, index) => {
90
+ const endIndex = getEndLine(startIndex);
91
+ return {
92
+ file: "",
93
+ anchor: index + 1,
94
+ startLine: startIndex + 1,
95
+ endLine: endIndex + 1,
96
+ };
97
+ }),
98
+ };
99
+ }
100
+ function analyzeListingRange(lines, startMarkerIndex, endMarkerIndex, count) {
101
+ if (startMarkerIndex < 0 || endMarkerIndex <= startMarkerIndex)
102
+ return { listingStart: 0, listingEnd: 0 };
103
+ const itemIndexes = [];
104
+ for (let index = startMarkerIndex + 1; index < endMarkerIndex; index++) {
105
+ if (lines[index].trim().startsWith("- "))
106
+ itemIndexes.push(index);
107
+ }
108
+ if (!itemIndexes.length) {
109
+ const markerLine = startMarkerIndex + 2;
110
+ return { listingStart: markerLine, listingEnd: markerLine };
111
+ }
112
+ const cappedCount = count > 0 ? Math.min(itemIndexes.length, count) : itemIndexes.length;
113
+ return {
114
+ listingStart: itemIndexes[0] + 1,
115
+ listingEnd: itemIndexes[cappedCount - 1] + 1,
116
+ };
117
+ }
118
+ function findFooterStartIndex(lines, insideCodeFence) {
119
+ let footerMarker = -1;
120
+ for (let i = 0; i < lines.length; i++) {
121
+ if (insideCodeFence[i])
122
+ continue;
123
+ const line = lines[i];
124
+ if (line.includes("<!-- PRODEx " + "v") || line.includes("*Generated with " + "[Prodex]")) {
125
+ footerMarker = i;
126
+ break;
127
+ }
128
+ }
129
+ let footerStart = footerMarker >= 0 ? footerMarker : lines.length;
130
+ if (footerStart > 0 && lines[footerStart - 1].trim() === "---")
131
+ footerStart--;
132
+ return footerStart;
133
+ }
134
+ function findSectionStartIndexes(lines, count, fallbackIndex, insideCodeFence) {
135
+ const markerByAnchor = new Map();
136
+ for (let index = 0; index < lines.length; index++) {
137
+ if (insideCodeFence[index])
138
+ continue;
139
+ const match = lines[index].trim().match(/^####\s+(\d+)\s*$/);
140
+ if (!match)
141
+ continue;
142
+ const anchor = Number(match[1]);
143
+ if (Number.isFinite(anchor) && anchor >= 1 && anchor <= count && !markerByAnchor.has(anchor)) {
144
+ markerByAnchor.set(anchor, index);
145
+ }
146
+ }
147
+ const starts = [];
148
+ for (let anchor = 1; anchor <= count; anchor++) {
149
+ const markerIndex = markerByAnchor.get(anchor);
150
+ if (markerIndex == null) {
151
+ starts.push(fallbackIndex);
152
+ continue;
153
+ }
154
+ starts.push(markerIndex > 0 && lines[markerIndex - 1].trim() === "---" ? markerIndex - 1 : markerIndex);
155
+ }
156
+ return starts;
157
+ }
158
+ function findGenericSectionStartIndexes(lines, sectionCount, fallbackIndex, insideCodeFence) {
159
+ const markerBySec = new Map();
160
+ for (let index = 0; index < lines.length; index++) {
161
+ if (insideCodeFence[index])
162
+ continue;
163
+ const match = lines[index].trim().match(/<a id="sec-(\d+)"><\/a>/);
164
+ if (!match)
165
+ continue;
166
+ const secIdx = Number(match[1]);
167
+ if (Number.isFinite(secIdx) && secIdx >= 1 && secIdx <= sectionCount && !markerBySec.has(secIdx)) {
168
+ let startIndex = index;
169
+ if (startIndex > 0 && lines[startIndex - 1].trim() === "---") {
170
+ startIndex--;
171
+ }
172
+ markerBySec.set(secIdx, startIndex);
173
+ }
174
+ }
175
+ const starts = [];
176
+ for (let secIdx = 1; secIdx <= sectionCount; secIdx++) {
177
+ const markerIndex = markerBySec.get(secIdx);
178
+ starts.push(markerIndex !== undefined ? markerIndex : fallbackIndex);
179
+ }
180
+ return starts;
181
+ }
182
+ function findCmdSectionStartIndexes(lines, cmdCount, fallbackIndex, insideCodeFence) {
183
+ const markerByCmd = new Map();
184
+ for (let index = 0; index < lines.length; index++) {
185
+ if (insideCodeFence[index])
186
+ continue;
187
+ const match = lines[index].trim().match(/<a id="cmd-(\d+)"><\/a>/);
188
+ if (!match)
189
+ continue;
190
+ const cmdIdx = Number(match[1]);
191
+ if (Number.isFinite(cmdIdx) && cmdIdx >= 1 && cmdIdx <= cmdCount && !markerByCmd.has(cmdIdx)) {
192
+ let startIndex = index;
193
+ if (startIndex > 0 && lines[startIndex - 1].trim() === "---") {
194
+ startIndex--;
195
+ }
196
+ markerByCmd.set(cmdIdx, startIndex);
197
+ }
198
+ }
199
+ const starts = [];
200
+ for (let cmdIdx = 1; cmdIdx <= cmdCount; cmdIdx++) {
201
+ const markerIndex = markerByCmd.get(cmdIdx);
202
+ starts.push(markerIndex !== undefined ? markerIndex : fallbackIndex);
203
+ }
204
+ return starts;
205
+ }
206
+ function buildToc(opts) {
207
+ const sectionCount = opts.sections?.length ?? 0;
208
+ const cmdCount = opts.commandOutputs?.length ?? 0;
209
+ const fileCount = opts.files.length;
210
+ const indexRange = opts.withRanges && opts.listingStart && opts.listingEnd
211
+ ? `L${opts.listingStart}-L${opts.listingEnd}`
212
+ : render_constants_1.INDEX_RANGE_PLACEHOLDER;
213
+ const headers = [
214
+ render_constants_1.MD_HEADER,
215
+ render_constants_1.LLM_NOTE,
216
+ "",
217
+ "# Index",
218
+ ];
219
+ if (opts.metadata?.commandKind === "trace" && opts.metadata.targets && opts.metadata.targets.length > 0) {
220
+ const relativeEntries = opts.metadata.entries.map(e => (0, read_file_1.rel)(e, opts.root));
221
+ headers.push("", "> **Trace Target Context**", `> - **Requested Target(s):** ${opts.metadata.targets.map(t => `\`${t}\``).join(", ")}`, `> - **Resolved Starting Point(s):** ${relativeEntries.map(e => `\`${e}\``).join(", ") || "none"}`, `> - **Traversal Depth:** \`${opts.metadata.depth !== undefined ? opts.metadata.depth : "none"}\``, "");
222
+ }
223
+ headers.push(`<!-- PRODEX_INDEX_RANGE: ${indexRange} -->`, `<!-- PRODEX_FILE_COUNT: ${opts.count} -->`);
224
+ if (sectionCount > 0) {
225
+ headers.push(`<!-- PRODEX_SECTION_COUNT: ${sectionCount} -->`);
226
+ }
227
+ if (cmdCount > 0) {
228
+ headers.push(`<!-- PRODEX_COMMAND_OUTPUT_COUNT: ${cmdCount} -->`);
229
+ }
230
+ headers.push("<!-- PRODEX_INDEX_LIST_START -->");
231
+ const layoutOrder = (0, render_helpers_1.getLayoutOrder)(opts.metadata?.commandKind);
232
+ const isFileFirst = layoutOrder === "files-first";
233
+ const secItems = (sectionCount > 0 && opts.sections) ? opts.sections.map((sec, index) => {
234
+ const label = sec.title;
235
+ if (!opts.withRanges || !opts.sectionTrace) {
236
+ return `- [${label}](#sec-${index + 1})`;
237
+ }
238
+ const trace = opts.sectionTrace[index];
239
+ return `- [${label}](#sec-${index + 1}) L${trace.startLine}-L${trace.endLine}`;
240
+ }) : [];
241
+ const fileItems = fileCount > 0 ? opts.files.map((file, index) => {
242
+ const relativePath = (0, read_file_1.rel)(file, opts.root);
243
+ if (!opts.withRanges || !opts.trace) {
244
+ return `- [${relativePath}](#${index + 1})`;
245
+ }
246
+ const trace = opts.trace[index];
247
+ return `- [${relativePath}](#${index + 1}) L${trace.startLine}-L${trace.endLine}`;
248
+ }) : [];
249
+ const cmdItems = (cmdCount > 0 && opts.commandOutputs) ? opts.commandOutputs.map((cmd, index) => {
250
+ const label = `Command ${index + 1}: ${cmd.command}`;
251
+ if (!opts.withRanges || !opts.cmdTrace) {
252
+ return `- [${label}](#cmd-${index + 1})`;
253
+ }
254
+ const trace = opts.cmdTrace[index];
255
+ return `- [${label}](#cmd-${index + 1}) L${trace.startLine}-L${trace.endLine}`;
256
+ }) : [];
257
+ const categoryCount = (sectionCount > 0 ? 1 : 0) + (fileCount > 0 ? 1 : 0) + (cmdCount > 0 ? 1 : 0);
258
+ const showHeaders = categoryCount > 1;
259
+ const parts = [];
260
+ if (isFileFirst) {
261
+ if (fileCount > 0)
262
+ parts.push({ header: "## Files", items: fileItems });
263
+ if (cmdCount > 0)
264
+ parts.push({ header: "## Command Outputs", items: cmdItems });
265
+ if (sectionCount > 0)
266
+ parts.push({ header: "## Metadata Sections", items: secItems });
267
+ }
268
+ else {
269
+ if (sectionCount > 0)
270
+ parts.push({ header: "## Metadata Sections", items: secItems });
271
+ if (fileCount > 0)
272
+ parts.push({ header: "## Files", items: fileItems });
273
+ if (cmdCount > 0)
274
+ parts.push({ header: "## Command Outputs", items: cmdItems });
275
+ }
276
+ parts.forEach((part, pIdx) => {
277
+ if (pIdx > 0) {
278
+ headers.push("");
279
+ }
280
+ if (showHeaders) {
281
+ headers.push(part.header);
282
+ }
283
+ headers.push(...part.items);
284
+ });
285
+ headers.push("<!-- PRODEX_INDEX_LIST_END -->", "", "---");
286
+ return headers.join("\n");
287
+ }
@@ -1,12 +1,9 @@
1
- export interface MdTraceEntry {
2
- file: string;
3
- anchor: number;
4
- startLine: number;
5
- endLine: number;
6
- }
7
- export declare function renderTraceMd(files: string[], root?: string): {
1
+ import type { ArtifactPayload } from "../types";
2
+ export { MdTraceEntry } from "../types";
3
+ export { renderMdCmdResults } from "./markdown-sections";
4
+ export declare function renderTraceMd(payload: ArtifactPayload): {
8
5
  content: string;
9
- trace: MdTraceEntry[];
6
+ trace: import("../types").MdTraceEntry[];
10
7
  listingStart: number;
11
8
  listingEnd: number;
12
9
  };
@@ -1,36 +1,99 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderMdCmdResults = void 0;
6
4
  exports.renderTraceMd = renderTraceMd;
7
- const path_1 = __importDefault(require("path"));
8
5
  const render_constants_1 = require("./render-constants");
9
- const read_file_1 = require("../filesystem/read-file");
10
- function renderTraceMd(files, root = process.cwd()) {
11
- const sections = files.map((file, index) => renderMdSection(file, index, root));
12
- const firstPassToc = buildToc({
13
- files,
6
+ const render_helpers_1 = require("./render-helpers");
7
+ const markdown_toc_1 = require("./markdown-toc");
8
+ const markdown_sections_1 = require("./markdown-sections");
9
+ var markdown_sections_2 = require("./markdown-sections");
10
+ Object.defineProperty(exports, "renderMdCmdResults", { enumerable: true, get: function () { return markdown_sections_2.renderMdCmdResults; } });
11
+ function renderTraceMd(payload) {
12
+ const root = payload.root;
13
+ const sorted = [...payload.files].sort((a, b) => a.path.localeCompare(b.path));
14
+ const filesList = sorted.map((f) => f.path);
15
+ const cmdCount = payload.commandOutputs?.length ?? 0;
16
+ const sectionCount = payload.sections?.length ?? 0;
17
+ const layoutOrder = (0, render_helpers_1.getLayoutOrder)(payload.metadata?.commandKind);
18
+ const isFileFirst = layoutOrder === "files-first";
19
+ // Build exact sequence of anchors to generate correct sequentially-flowing navigation
20
+ const orderedAnchors = [];
21
+ if (isFileFirst) {
22
+ for (let i = 1; i <= sorted.length; i++)
23
+ orderedAnchors.push(`${i}`);
24
+ for (let i = 1; i <= cmdCount; i++)
25
+ orderedAnchors.push(`cmd-${i}`);
26
+ for (let i = 1; i <= sectionCount; i++)
27
+ orderedAnchors.push(`sec-${i}`);
28
+ }
29
+ else {
30
+ for (let i = 1; i <= sectionCount; i++)
31
+ orderedAnchors.push(`sec-${i}`);
32
+ for (let i = 1; i <= sorted.length; i++)
33
+ orderedAnchors.push(`${i}`);
34
+ for (let i = 1; i <= cmdCount; i++)
35
+ orderedAnchors.push(`cmd-${i}`);
36
+ }
37
+ const getNavLine = (anchorId) => {
38
+ const idx = orderedAnchors.indexOf(anchorId);
39
+ if (idx === -1)
40
+ return `[Back to top](#index)`;
41
+ const navParts = [];
42
+ if (idx > 0) {
43
+ navParts.push(`[Previous](#${orderedAnchors[idx - 1]})`);
44
+ }
45
+ navParts.push(`[Back to top](#index)`);
46
+ if (idx < orderedAnchors.length - 1) {
47
+ navParts.push(`[Next](#${orderedAnchors[idx + 1]})`);
48
+ }
49
+ return navParts.join(" | ");
50
+ };
51
+ // Render generic sections
52
+ const genericSections = (payload.sections ?? []).map((sec, index) => {
53
+ const navLine = getNavLine(`sec-${index + 1}`);
54
+ return (0, markdown_sections_1.renderGenericSection)(sec, index, navLine);
55
+ });
56
+ // Render file snapshots
57
+ const fileSections = sorted.map((file, index) => {
58
+ const navLine = getNavLine(`${index + 1}`);
59
+ return (0, markdown_sections_1.renderFileSection)(file, index, root, navLine);
60
+ });
61
+ const firstPassToc = (0, markdown_toc_1.buildToc)({
62
+ files: filesList,
14
63
  root,
15
- count: files.length,
64
+ count: sorted.length,
16
65
  listingStart: 0,
17
66
  listingEnd: 0,
18
67
  trace: null,
68
+ sectionTrace: null,
69
+ cmdTrace: null,
70
+ sections: payload.sections,
71
+ commandOutputs: payload.commandOutputs,
19
72
  withRanges: false,
73
+ metadata: payload.metadata,
20
74
  });
21
- const firstPassContent = [firstPassToc, ...sections, render_constants_1.MD_FOOTER].join("\n");
22
- const firstPassAnalysis = analyzeTrace(firstPassContent, files.length);
23
- const finalToc = buildToc({
24
- files,
75
+ const cmdSections = (0, markdown_sections_1.renderMdCmdResults)(payload.commandOutputs, root, sorted.length, sectionCount, orderedAnchors);
76
+ const bodySections = isFileFirst
77
+ ? [...fileSections, cmdSections, ...genericSections]
78
+ : [...genericSections, ...fileSections, cmdSections];
79
+ const firstPassContent = [firstPassToc, ...bodySections.filter(Boolean), render_constants_1.MD_FOOTER].join("\n");
80
+ const firstPassAnalysis = (0, markdown_toc_1.analyzeTrace)(firstPassContent, sorted.length, sectionCount, cmdCount);
81
+ const finalToc = (0, markdown_toc_1.buildToc)({
82
+ files: filesList,
25
83
  root,
26
- count: files.length,
84
+ count: sorted.length,
27
85
  listingStart: firstPassAnalysis.listingStart,
28
86
  listingEnd: firstPassAnalysis.listingEnd,
29
87
  trace: firstPassAnalysis.trace,
88
+ sectionTrace: firstPassAnalysis.sectionTrace,
89
+ cmdTrace: firstPassAnalysis.cmdTrace,
90
+ sections: payload.sections,
91
+ commandOutputs: payload.commandOutputs,
30
92
  withRanges: true,
93
+ metadata: payload.metadata,
31
94
  });
32
- const content = [finalToc, ...sections, render_constants_1.MD_FOOTER].join("\n");
33
- const analysis = analyzeTrace(content, files.length);
95
+ const content = [finalToc, ...bodySections.filter(Boolean), render_constants_1.MD_FOOTER].join("\n");
96
+ const analysis = (0, markdown_toc_1.analyzeTrace)(content, sorted.length, sectionCount, cmdCount);
34
97
  return {
35
98
  content,
36
99
  trace: analysis.trace,
@@ -38,113 +101,3 @@ function renderTraceMd(files, root = process.cwd()) {
38
101
  listingEnd: analysis.listingEnd,
39
102
  };
40
103
  }
41
- function buildToc(opts) {
42
- const indexRange = opts.withRanges && opts.listingStart && opts.listingEnd
43
- ? `L${opts.listingStart}-L${opts.listingEnd}`
44
- : render_constants_1.INDEX_RANGE_PLACEHOLDER;
45
- const items = opts.files.map((file, index) => {
46
- const relativePath = (0, read_file_1.rel)(file, opts.root);
47
- if (!opts.withRanges || !opts.trace)
48
- return `- [${relativePath}](#${index + 1})`;
49
- const trace = opts.trace[index];
50
- return `- [${relativePath}](#${index + 1}) L${trace.startLine}-L${trace.endLine}`;
51
- });
52
- return [
53
- render_constants_1.MD_HEADER,
54
- render_constants_1.LLM_NOTE,
55
- "",
56
- "# Index",
57
- `<!-- PRODEX_INDEX_RANGE: ${indexRange} -->`,
58
- `<!-- PRODEX_FILE_COUNT: ${opts.count} -->`,
59
- "<!-- PRODEX_INDEX_LIST_START -->",
60
- ...items,
61
- "<!-- PRODEX_INDEX_LIST_END -->",
62
- "",
63
- "---",
64
- ].join("\n");
65
- }
66
- function analyzeTrace(content, count) {
67
- const lines = content.split("\n");
68
- const startMarkerIndex = lines.findIndex((line) => line.trim() === "<!-- PRODEX_INDEX_LIST_START -->");
69
- const endMarkerIndex = lines.findIndex((line) => line.trim() === "<!-- PRODEX_INDEX_LIST_END -->");
70
- const { listingStart, listingEnd } = analyzeListingRange(lines, startMarkerIndex, endMarkerIndex, count);
71
- const footerStartIndex = findFooterStartIndex(lines);
72
- const sectionStarts = findSectionStartIndexes(lines, count, footerStartIndex);
73
- return {
74
- listingStart,
75
- listingEnd,
76
- trace: sectionStarts.map((startIndex, index) => {
77
- const nextStart = index < count - 1 ? sectionStarts[index + 1] : footerStartIndex;
78
- const endIndex = Math.max(startIndex, nextStart - 1);
79
- return {
80
- file: "",
81
- anchor: index + 1,
82
- startLine: startIndex + 1,
83
- endLine: endIndex + 1,
84
- };
85
- }),
86
- };
87
- }
88
- function analyzeListingRange(lines, startMarkerIndex, endMarkerIndex, count) {
89
- if (startMarkerIndex < 0 || endMarkerIndex <= startMarkerIndex)
90
- return { listingStart: 0, listingEnd: 0 };
91
- const itemIndexes = [];
92
- for (let index = startMarkerIndex + 1; index < endMarkerIndex; index++) {
93
- if (lines[index].trim().startsWith("- "))
94
- itemIndexes.push(index);
95
- }
96
- if (!itemIndexes.length) {
97
- const markerLine = startMarkerIndex + 2;
98
- return { listingStart: markerLine, listingEnd: markerLine };
99
- }
100
- const cappedCount = count > 0 ? Math.min(itemIndexes.length, count) : itemIndexes.length;
101
- return {
102
- listingStart: itemIndexes[0] + 1,
103
- listingEnd: itemIndexes[cappedCount - 1] + 1,
104
- };
105
- }
106
- function findFooterStartIndex(lines) {
107
- const footerMarker = lines.findIndex((line) => line.includes("<!-- PRODEx v") || line.includes("*Generated with [Prodex]"));
108
- let footerStart = footerMarker >= 0 ? footerMarker : lines.length;
109
- if (footerStart > 0 && lines[footerStart - 1].trim() === "---")
110
- footerStart--;
111
- return footerStart;
112
- }
113
- function findSectionStartIndexes(lines, count, fallbackIndex) {
114
- const markerByAnchor = new Map();
115
- for (let index = 0; index < lines.length; index++) {
116
- const match = lines[index].trim().match(/^####\s+(\d+)\s*$/);
117
- if (!match)
118
- continue;
119
- const anchor = Number(match[1]);
120
- if (Number.isFinite(anchor) && anchor >= 1 && anchor <= count && !markerByAnchor.has(anchor)) {
121
- markerByAnchor.set(anchor, index);
122
- }
123
- }
124
- const starts = [];
125
- for (let anchor = 1; anchor <= count; anchor++) {
126
- const markerIndex = markerByAnchor.get(anchor);
127
- if (markerIndex == null) {
128
- starts.push(fallbackIndex);
129
- continue;
130
- }
131
- starts.push(markerIndex > 0 && lines[markerIndex - 1].trim() === "---" ? markerIndex - 1 : markerIndex);
132
- }
133
- return starts;
134
- }
135
- function renderMdSection(filePath, index, root) {
136
- const relativePath = (0, read_file_1.rel)(filePath, root);
137
- const ext = path_1.default.extname(filePath).toLowerCase();
138
- const lang = render_constants_1.LANG_MAP[ext] || "txt";
139
- const code = (0, read_file_1.readFileSafe)(filePath).trimEnd();
140
- return [
141
- `---\n#### ${index + 1}`,
142
- "\n",
143
- "` File: " + relativePath + "` [Back to top](#index)",
144
- "",
145
- "```" + lang,
146
- code,
147
- "```",
148
- "",
149
- ].join("\n");
150
- }
@@ -1,2 +1,5 @@
1
1
  import type { OutputParams } from "../types";
2
- export declare function produceOutput({ name, files, cfg }: OutputParams): Promise<string>;
2
+ export declare function produceOutput({ name, payload, format, dir, versioned, }: OutputParams): Promise<{
3
+ outputPath: string;
4
+ outputSizeBytes: number;
5
+ }>;