mustflow 2.75.1 → 2.75.2
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.
|
@@ -14,15 +14,15 @@ export const SCRIPT_PACKS = [
|
|
|
14
14
|
summaryKey: 'scriptPack.script.codeOutline.summary',
|
|
15
15
|
actions: ['scan'],
|
|
16
16
|
useWhen: [
|
|
17
|
-
'Scan TypeScript or JavaScript files for symbol headers before reading large source files chunk by chunk.',
|
|
18
|
-
'Build a bounded source outline with file paths, line ranges, signatures, export flags, and content hashes for codebase orientation.',
|
|
17
|
+
'Scan TypeScript or JavaScript files for symbol headers and source anchors before reading large source files chunk by chunk.',
|
|
18
|
+
'Build a bounded source outline with file paths, line ranges, signatures, export flags, source-anchor metadata, and content hashes for codebase orientation.',
|
|
19
19
|
],
|
|
20
20
|
phases: ['before_change', 'during_change', 'review'],
|
|
21
21
|
readOnly: true,
|
|
22
22
|
mutates: false,
|
|
23
23
|
network: false,
|
|
24
24
|
inputs: ['path', 'max_files', 'max_file_bytes'],
|
|
25
|
-
outputs: ['human_summary', 'json_report', 'symbol_outline'],
|
|
25
|
+
outputs: ['human_summary', 'json_report', 'symbol_outline', 'source_anchors'],
|
|
26
26
|
relatedSkills: [
|
|
27
27
|
'codebase-orientation',
|
|
28
28
|
'javascript-code-change',
|
|
@@ -2,6 +2,7 @@ import { createHash } from 'node:crypto';
|
|
|
2
2
|
import { existsSync, lstatSync, readdirSync } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { ensureInside, ensureInsideWithoutSymlinks, readFileInsideWithoutSymlinks } from './safe-filesystem.js';
|
|
5
|
+
import { parseSourceAnchorsInContent, sourceAnchorTextContainsSecretLike, splitSourceAnchorList, } from './source-anchors.js';
|
|
5
6
|
export const CODE_PACK_ID = 'code';
|
|
6
7
|
export const CODE_OUTLINE_SCRIPT_ID = 'outline';
|
|
7
8
|
export const CODE_OUTLINE_SCRIPT_REF = `${CODE_PACK_ID}/${CODE_OUTLINE_SCRIPT_ID}`;
|
|
@@ -455,7 +456,60 @@ function extractSymbols(relativePath, language, contentSha256, text) {
|
|
|
455
456
|
}
|
|
456
457
|
return symbols;
|
|
457
458
|
}
|
|
458
|
-
function
|
|
459
|
+
function sourceAnchorEndLine(lineStart, rawText) {
|
|
460
|
+
return lineStart + rawText.split(/\r\n|\r|\n/u).length - 1;
|
|
461
|
+
}
|
|
462
|
+
function canBridgeAnchorToSymbol(lines, anchorEndLine, symbolStartLine) {
|
|
463
|
+
if (symbolStartLine <= anchorEndLine) {
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
for (let lineNumber = anchorEndLine + 1; lineNumber < symbolStartLine; lineNumber += 1) {
|
|
467
|
+
const trimmed = (lines[lineNumber - 1] ?? '').trim();
|
|
468
|
+
if (trimmed.length === 0 || trimmed.startsWith('//') || trimmed.startsWith('*') || trimmed.startsWith('@')) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
function findAnchorTargetSymbol(relativePath, lines, anchorEndLine, symbols) {
|
|
476
|
+
const followingSymbols = symbols
|
|
477
|
+
.filter((symbol) => symbol.path === relativePath && symbol.start_line > anchorEndLine)
|
|
478
|
+
.sort((left, right) => left.start_line - right.start_line);
|
|
479
|
+
const target = followingSymbols[0] ?? null;
|
|
480
|
+
if (!target || !canBridgeAnchorToSymbol(lines, anchorEndLine, target.start_line)) {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
return target;
|
|
484
|
+
}
|
|
485
|
+
function extractAnchors(relativePath, lines, text, symbols) {
|
|
486
|
+
const anchors = [];
|
|
487
|
+
for (const anchor of parseSourceAnchorsInContent(relativePath, text)) {
|
|
488
|
+
if (!anchor.idValid || sourceAnchorTextContainsSecretLike(anchor.rawText)) {
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
const lineEnd = sourceAnchorEndLine(anchor.lineStart, anchor.rawText);
|
|
492
|
+
const target = findAnchorTargetSymbol(relativePath, lines, lineEnd, symbols);
|
|
493
|
+
anchors.push({
|
|
494
|
+
id: anchor.rawId,
|
|
495
|
+
path: relativePath,
|
|
496
|
+
line_start: anchor.lineStart,
|
|
497
|
+
line_end: lineEnd,
|
|
498
|
+
purpose: anchor.fields.get('purpose') ?? null,
|
|
499
|
+
search: splitSourceAnchorList(anchor.fields.get('search')),
|
|
500
|
+
invariant: anchor.fields.get('invariant') ?? null,
|
|
501
|
+
risk: splitSourceAnchorList(anchor.fields.get('risk')),
|
|
502
|
+
navigation_only: true,
|
|
503
|
+
can_instruct_agent: false,
|
|
504
|
+
target_symbol_id: target?.id ?? null,
|
|
505
|
+
target_kind: target?.kind ?? null,
|
|
506
|
+
target_name: target?.name ?? null,
|
|
507
|
+
target_start_line: target?.start_line ?? null,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return anchors;
|
|
511
|
+
}
|
|
512
|
+
function createOutlineInputHash(policy, files, anchors, findings) {
|
|
459
513
|
const inputState = {
|
|
460
514
|
policy,
|
|
461
515
|
files: files.map((file) => ({
|
|
@@ -464,6 +518,13 @@ function createOutlineInputHash(policy, files, findings) {
|
|
|
464
518
|
size_bytes: file.size_bytes,
|
|
465
519
|
symbol_count: file.symbol_count,
|
|
466
520
|
})),
|
|
521
|
+
anchors: anchors.map((anchor) => ({
|
|
522
|
+
id: anchor.id,
|
|
523
|
+
path: anchor.path,
|
|
524
|
+
line_start: anchor.line_start,
|
|
525
|
+
line_end: anchor.line_end,
|
|
526
|
+
target_symbol_id: anchor.target_symbol_id,
|
|
527
|
+
})),
|
|
467
528
|
input_errors: findings
|
|
468
529
|
.filter((finding) => ERROR_OUTLINE_CODES.has(finding.code))
|
|
469
530
|
.map((finding) => ({ code: finding.code, path: finding.path })),
|
|
@@ -486,6 +547,7 @@ export function inspectCodeOutline(projectRoot, options) {
|
|
|
486
547
|
ignored_directories: [...IGNORED_DIRECTORIES],
|
|
487
548
|
};
|
|
488
549
|
const files = [];
|
|
550
|
+
const anchors = [];
|
|
489
551
|
const symbols = [];
|
|
490
552
|
const findings = [];
|
|
491
553
|
const issues = [];
|
|
@@ -506,15 +568,18 @@ export function inspectCodeOutline(projectRoot, options) {
|
|
|
506
568
|
}
|
|
507
569
|
const contentSha256 = sha256Tagged(buffer);
|
|
508
570
|
const text = buffer.toString('utf8');
|
|
571
|
+
const lines = text.split(/\r\n|\r|\n/u);
|
|
509
572
|
const fileSymbols = extractSymbols(candidate.relativePath, candidate.language, contentSha256, text);
|
|
573
|
+
const fileAnchors = extractAnchors(candidate.relativePath, lines, text, fileSymbols);
|
|
510
574
|
symbols.push(...fileSymbols);
|
|
575
|
+
anchors.push(...fileAnchors);
|
|
511
576
|
files.push({
|
|
512
577
|
kind: 'source_file',
|
|
513
578
|
path: candidate.relativePath,
|
|
514
579
|
language: candidate.language,
|
|
515
580
|
sha256: contentSha256,
|
|
516
581
|
size_bytes: buffer.byteLength,
|
|
517
|
-
line_count:
|
|
582
|
+
line_count: lines.length,
|
|
518
583
|
symbol_count: fileSymbols.length,
|
|
519
584
|
});
|
|
520
585
|
}
|
|
@@ -530,8 +595,9 @@ export function inspectCodeOutline(projectRoot, options) {
|
|
|
530
595
|
ok: status === 'passed',
|
|
531
596
|
mustflow_root: root,
|
|
532
597
|
policy,
|
|
533
|
-
input_hash: createOutlineInputHash(policy, files, findings),
|
|
598
|
+
input_hash: createOutlineInputHash(policy, files, anchors, findings),
|
|
534
599
|
files,
|
|
600
|
+
anchors: anchors.sort((left, right) => left.path.localeCompare(right.path) || left.line_start - right.line_start),
|
|
535
601
|
symbols: symbols.sort((left, right) => left.path.localeCompare(right.path) || left.start_line - right.start_line),
|
|
536
602
|
findings,
|
|
537
603
|
issues,
|
package/package.json
CHANGED
package/schemas/README.md
CHANGED
|
@@ -67,7 +67,8 @@ Current schemas:
|
|
|
67
67
|
- `code-outline-report.schema.json`: output of
|
|
68
68
|
`mf script-pack run code/outline scan <path...> --json`, containing a bounded TypeScript and
|
|
69
69
|
JavaScript source outline with file hashes, symbol names, declaration kinds, line ranges,
|
|
70
|
-
signatures, export flags, static return metadata, and stable
|
|
70
|
+
signatures, export flags, source-anchor navigation metadata, static return metadata, and stable
|
|
71
|
+
input-limit finding codes
|
|
71
72
|
- `code-symbol-read-report.schema.json`: output of
|
|
72
73
|
`mf script-pack run code/symbol-read read <path> --start-line <line> --json`, containing a
|
|
73
74
|
focused source snippet selected by outline symbol line or explicit line range with file hash,
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"policy",
|
|
18
18
|
"input_hash",
|
|
19
19
|
"files",
|
|
20
|
+
"anchors",
|
|
20
21
|
"symbols",
|
|
21
22
|
"findings",
|
|
22
23
|
"issues"
|
|
@@ -37,6 +38,10 @@
|
|
|
37
38
|
"type": "array",
|
|
38
39
|
"items": { "$ref": "#/$defs/file" }
|
|
39
40
|
},
|
|
41
|
+
"anchors": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"items": { "$ref": "#/$defs/anchor" }
|
|
44
|
+
},
|
|
40
45
|
"symbols": {
|
|
41
46
|
"type": "array",
|
|
42
47
|
"items": { "$ref": "#/$defs/symbol" }
|
|
@@ -90,6 +95,47 @@
|
|
|
90
95
|
"symbol_count": { "type": "integer", "minimum": 0 }
|
|
91
96
|
}
|
|
92
97
|
},
|
|
98
|
+
"anchor": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"additionalProperties": false,
|
|
101
|
+
"required": [
|
|
102
|
+
"id",
|
|
103
|
+
"path",
|
|
104
|
+
"line_start",
|
|
105
|
+
"line_end",
|
|
106
|
+
"purpose",
|
|
107
|
+
"search",
|
|
108
|
+
"invariant",
|
|
109
|
+
"risk",
|
|
110
|
+
"navigation_only",
|
|
111
|
+
"can_instruct_agent",
|
|
112
|
+
"target_symbol_id",
|
|
113
|
+
"target_kind",
|
|
114
|
+
"target_name",
|
|
115
|
+
"target_start_line"
|
|
116
|
+
],
|
|
117
|
+
"properties": {
|
|
118
|
+
"id": { "type": "string" },
|
|
119
|
+
"path": { "type": "string" },
|
|
120
|
+
"line_start": { "type": "integer", "minimum": 1 },
|
|
121
|
+
"line_end": { "type": "integer", "minimum": 1 },
|
|
122
|
+
"purpose": { "type": ["string", "null"] },
|
|
123
|
+
"search": { "$ref": "#/$defs/stringArray" },
|
|
124
|
+
"invariant": { "type": ["string", "null"] },
|
|
125
|
+
"risk": { "$ref": "#/$defs/stringArray" },
|
|
126
|
+
"navigation_only": { "const": true },
|
|
127
|
+
"can_instruct_agent": { "const": false },
|
|
128
|
+
"target_symbol_id": { "type": ["string", "null"] },
|
|
129
|
+
"target_kind": {
|
|
130
|
+
"anyOf": [
|
|
131
|
+
{ "$ref": "#/$defs/symbolKind" },
|
|
132
|
+
{ "type": "null" }
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
"target_name": { "type": ["string", "null"] },
|
|
136
|
+
"target_start_line": { "type": ["integer", "null"], "minimum": 1 }
|
|
137
|
+
}
|
|
138
|
+
},
|
|
93
139
|
"symbol": {
|
|
94
140
|
"type": "object",
|
|
95
141
|
"additionalProperties": false,
|