@harness-engineering/core 0.14.0 → 0.15.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/README.md +23 -0
- package/dist/architecture/matchers.d.mts +1 -1
- package/dist/architecture/matchers.d.ts +1 -1
- package/dist/index.d.mts +353 -85
- package/dist/index.d.ts +353 -85
- package/dist/index.js +1288 -143
- package/dist/index.mjs +1260 -139
- package/dist/{matchers-D20x48U9.d.mts → matchers-Dj1t5vpg.d.mts} +46 -46
- package/dist/{matchers-D20x48U9.d.ts → matchers-Dj1t5vpg.d.ts} +46 -46
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -63,6 +63,7 @@ __export(index_exports, {
|
|
|
63
63
|
DEFAULT_STATE: () => DEFAULT_STATE,
|
|
64
64
|
DEFAULT_STREAM_INDEX: () => DEFAULT_STREAM_INDEX,
|
|
65
65
|
DepDepthCollector: () => DepDepthCollector,
|
|
66
|
+
EXTENSION_MAP: () => EXTENSION_MAP,
|
|
66
67
|
EmitInteractionInputSchema: () => EmitInteractionInputSchema,
|
|
67
68
|
EntropyAnalyzer: () => EntropyAnalyzer,
|
|
68
69
|
EntropyConfigSchema: () => EntropyConfigSchema,
|
|
@@ -97,6 +98,7 @@ __export(index_exports, {
|
|
|
97
98
|
SharableForbiddenImportSchema: () => SharableForbiddenImportSchema,
|
|
98
99
|
SharableLayerSchema: () => SharableLayerSchema,
|
|
99
100
|
SharableSecurityRulesSchema: () => SharableSecurityRulesSchema,
|
|
101
|
+
SkillEventSchema: () => SkillEventSchema,
|
|
100
102
|
StreamIndexSchema: () => StreamIndexSchema,
|
|
101
103
|
StreamInfoSchema: () => StreamInfoSchema,
|
|
102
104
|
ThresholdConfigSchema: () => ThresholdConfigSchema,
|
|
@@ -105,6 +107,7 @@ __export(index_exports, {
|
|
|
105
107
|
VERSION: () => VERSION,
|
|
106
108
|
ViolationSchema: () => ViolationSchema,
|
|
107
109
|
addProvenance: () => addProvenance,
|
|
110
|
+
agentConfigRules: () => agentConfigRules,
|
|
108
111
|
analyzeDiff: () => analyzeDiff,
|
|
109
112
|
analyzeLearningPatterns: () => analyzeLearningPatterns,
|
|
110
113
|
appendFailure: () => appendFailure,
|
|
@@ -112,6 +115,7 @@ __export(index_exports, {
|
|
|
112
115
|
appendSessionEntry: () => appendSessionEntry,
|
|
113
116
|
applyFixes: () => applyFixes,
|
|
114
117
|
applyHotspotDowngrade: () => applyHotspotDowngrade,
|
|
118
|
+
applySyncChanges: () => applySyncChanges,
|
|
115
119
|
archMatchers: () => archMatchers,
|
|
116
120
|
archModule: () => archModule,
|
|
117
121
|
architecture: () => architecture,
|
|
@@ -126,12 +130,14 @@ __export(index_exports, {
|
|
|
126
130
|
checkEligibility: () => checkEligibility,
|
|
127
131
|
checkEvidenceCoverage: () => checkEvidenceCoverage,
|
|
128
132
|
classifyFinding: () => classifyFinding,
|
|
133
|
+
clearEventHashCache: () => clearEventHashCache,
|
|
129
134
|
clearFailuresCache: () => clearFailuresCache,
|
|
130
135
|
clearLearningsCache: () => clearLearningsCache,
|
|
131
136
|
configureFeedback: () => configureFeedback,
|
|
132
137
|
constraintRuleId: () => constraintRuleId,
|
|
133
138
|
contextBudget: () => contextBudget,
|
|
134
139
|
contextFilter: () => contextFilter,
|
|
140
|
+
countLearningEntries: () => countLearningEntries,
|
|
135
141
|
createBoundaryValidator: () => createBoundaryValidator,
|
|
136
142
|
createCommentedCodeFixes: () => createCommentedCodeFixes,
|
|
137
143
|
createError: () => createError,
|
|
@@ -155,27 +161,34 @@ __export(index_exports, {
|
|
|
155
161
|
detectCouplingViolations: () => detectCouplingViolations,
|
|
156
162
|
detectDeadCode: () => detectDeadCode,
|
|
157
163
|
detectDocDrift: () => detectDocDrift,
|
|
164
|
+
detectLanguage: () => detectLanguage,
|
|
158
165
|
detectPatternViolations: () => detectPatternViolations,
|
|
159
166
|
detectSizeBudgetViolations: () => detectSizeBudgetViolations,
|
|
160
167
|
detectStack: () => detectStack,
|
|
161
168
|
detectStaleConstraints: () => detectStaleConstraints,
|
|
162
169
|
determineAssessment: () => determineAssessment,
|
|
163
170
|
diff: () => diff,
|
|
171
|
+
emitEvent: () => emitEvent,
|
|
164
172
|
executeWorkflow: () => executeWorkflow,
|
|
165
173
|
expressRules: () => expressRules,
|
|
166
174
|
extractBundle: () => extractBundle,
|
|
175
|
+
extractIndexEntry: () => extractIndexEntry,
|
|
167
176
|
extractMarkdownLinks: () => extractMarkdownLinks,
|
|
168
177
|
extractSections: () => extractSections,
|
|
169
178
|
fanOutReview: () => fanOutReview,
|
|
179
|
+
formatEventTimeline: () => formatEventTimeline,
|
|
170
180
|
formatFindingBlock: () => formatFindingBlock,
|
|
171
181
|
formatGitHubComment: () => formatGitHubComment,
|
|
172
182
|
formatGitHubSummary: () => formatGitHubSummary,
|
|
183
|
+
formatOutline: () => formatOutline,
|
|
173
184
|
formatTerminalOutput: () => formatTerminalOutput,
|
|
174
185
|
generateAgentsMap: () => generateAgentsMap,
|
|
175
186
|
generateSuggestions: () => generateSuggestions,
|
|
176
187
|
getActionEmitter: () => getActionEmitter,
|
|
177
188
|
getExitCode: () => getExitCode,
|
|
178
189
|
getFeedbackConfig: () => getFeedbackConfig,
|
|
190
|
+
getOutline: () => getOutline,
|
|
191
|
+
getParser: () => getParser,
|
|
179
192
|
getPhaseCategories: () => getPhaseCategories,
|
|
180
193
|
getStreamForBranch: () => getStreamForBranch,
|
|
181
194
|
getUpdateNotification: () => getUpdateNotification,
|
|
@@ -186,24 +199,30 @@ __export(index_exports, {
|
|
|
186
199
|
listActiveSessions: () => listActiveSessions,
|
|
187
200
|
listStreams: () => listStreams,
|
|
188
201
|
loadBudgetedLearnings: () => loadBudgetedLearnings,
|
|
202
|
+
loadEvents: () => loadEvents,
|
|
189
203
|
loadFailures: () => loadFailures,
|
|
190
204
|
loadHandoff: () => loadHandoff,
|
|
205
|
+
loadIndexEntries: () => loadIndexEntries,
|
|
191
206
|
loadRelevantLearnings: () => loadRelevantLearnings,
|
|
192
207
|
loadSessionSummary: () => loadSessionSummary,
|
|
193
208
|
loadState: () => loadState,
|
|
194
209
|
loadStreamIndex: () => loadStreamIndex,
|
|
195
210
|
logAgentAction: () => logAgentAction,
|
|
211
|
+
mcpRules: () => mcpRules,
|
|
196
212
|
migrateToStreams: () => migrateToStreams,
|
|
197
213
|
networkRules: () => networkRules,
|
|
198
214
|
nodeRules: () => nodeRules,
|
|
199
215
|
parseDateFromEntry: () => parseDateFromEntry,
|
|
200
216
|
parseDiff: () => parseDiff,
|
|
217
|
+
parseFile: () => parseFile,
|
|
218
|
+
parseFrontmatter: () => parseFrontmatter,
|
|
201
219
|
parseManifest: () => parseManifest,
|
|
202
220
|
parseRoadmap: () => parseRoadmap,
|
|
203
221
|
parseSecurityConfig: () => parseSecurityConfig,
|
|
204
222
|
parseSize: () => parseSize,
|
|
205
223
|
pathTraversalRules: () => pathTraversalRules,
|
|
206
224
|
previewFix: () => previewFix,
|
|
225
|
+
promoteSessionLearnings: () => promoteSessionLearnings,
|
|
207
226
|
pruneLearnings: () => pruneLearnings,
|
|
208
227
|
reactRules: () => reactRules,
|
|
209
228
|
readCheckState: () => readCheckState,
|
|
@@ -215,6 +234,7 @@ __export(index_exports, {
|
|
|
215
234
|
requestMultiplePeerReviews: () => requestMultiplePeerReviews,
|
|
216
235
|
requestPeerReview: () => requestPeerReview,
|
|
217
236
|
resetFeedbackConfig: () => resetFeedbackConfig,
|
|
237
|
+
resetParserCache: () => resetParserCache,
|
|
218
238
|
resolveFileToLayer: () => resolveFileToLayer,
|
|
219
239
|
resolveModelTier: () => resolveModelTier,
|
|
220
240
|
resolveRuleSeverity: () => resolveRuleSeverity,
|
|
@@ -236,6 +256,7 @@ __export(index_exports, {
|
|
|
236
256
|
saveState: () => saveState,
|
|
237
257
|
saveStreamIndex: () => saveStreamIndex,
|
|
238
258
|
scopeContext: () => scopeContext,
|
|
259
|
+
searchSymbols: () => searchSymbols,
|
|
239
260
|
secretRules: () => secretRules,
|
|
240
261
|
serializeRoadmap: () => serializeRoadmap,
|
|
241
262
|
setActiveStream: () => setActiveStream,
|
|
@@ -246,6 +267,8 @@ __export(index_exports, {
|
|
|
246
267
|
tagUncitedFindings: () => tagUncitedFindings,
|
|
247
268
|
touchStream: () => touchStream,
|
|
248
269
|
trackAction: () => trackAction,
|
|
270
|
+
unfoldRange: () => unfoldRange,
|
|
271
|
+
unfoldSymbol: () => unfoldSymbol,
|
|
249
272
|
updateSessionEntryStatus: () => updateSessionEntryStatus,
|
|
250
273
|
updateSessionIndex: () => updateSessionIndex,
|
|
251
274
|
validateAgentsMap: () => validateAgentsMap,
|
|
@@ -284,17 +307,17 @@ var import_node_path = require("path");
|
|
|
284
307
|
var import_glob = require("glob");
|
|
285
308
|
var accessAsync = (0, import_util.promisify)(import_fs.access);
|
|
286
309
|
var readFileAsync = (0, import_util.promisify)(import_fs.readFile);
|
|
287
|
-
async function fileExists(
|
|
310
|
+
async function fileExists(path23) {
|
|
288
311
|
try {
|
|
289
|
-
await accessAsync(
|
|
312
|
+
await accessAsync(path23, import_fs.constants.F_OK);
|
|
290
313
|
return true;
|
|
291
314
|
} catch {
|
|
292
315
|
return false;
|
|
293
316
|
}
|
|
294
317
|
}
|
|
295
|
-
async function readFileContent(
|
|
318
|
+
async function readFileContent(path23) {
|
|
296
319
|
try {
|
|
297
|
-
const content = await readFileAsync(
|
|
320
|
+
const content = await readFileAsync(path23, "utf-8");
|
|
298
321
|
return (0, import_types.Ok)(content);
|
|
299
322
|
} catch (error) {
|
|
300
323
|
return (0, import_types.Err)(error);
|
|
@@ -345,15 +368,15 @@ function validateConfig(data, schema) {
|
|
|
345
368
|
let message = "Configuration validation failed";
|
|
346
369
|
const suggestions = [];
|
|
347
370
|
if (firstError) {
|
|
348
|
-
const
|
|
349
|
-
const pathDisplay =
|
|
371
|
+
const path23 = firstError.path.join(".");
|
|
372
|
+
const pathDisplay = path23 ? ` at "${path23}"` : "";
|
|
350
373
|
if (firstError.code === "invalid_type") {
|
|
351
374
|
const received = firstError.received;
|
|
352
375
|
const expected = firstError.expected;
|
|
353
376
|
if (received === "undefined") {
|
|
354
377
|
code = "MISSING_FIELD";
|
|
355
378
|
message = `Missing required field${pathDisplay}: ${firstError.message}`;
|
|
356
|
-
suggestions.push(`Field "${
|
|
379
|
+
suggestions.push(`Field "${path23}" is required and must be of type "${expected}"`);
|
|
357
380
|
} else {
|
|
358
381
|
code = "INVALID_TYPE";
|
|
359
382
|
message = `Invalid type${pathDisplay}: ${firstError.message}`;
|
|
@@ -569,27 +592,27 @@ function extractSections(content) {
|
|
|
569
592
|
}
|
|
570
593
|
return sections.map((section) => buildAgentMapSection(section, lines));
|
|
571
594
|
}
|
|
572
|
-
function isExternalLink(
|
|
573
|
-
return
|
|
595
|
+
function isExternalLink(path23) {
|
|
596
|
+
return path23.startsWith("http://") || path23.startsWith("https://") || path23.startsWith("#") || path23.startsWith("mailto:");
|
|
574
597
|
}
|
|
575
598
|
function resolveLinkPath(linkPath, baseDir) {
|
|
576
599
|
return linkPath.startsWith(".") ? (0, import_path.join)(baseDir, linkPath) : linkPath;
|
|
577
600
|
}
|
|
578
|
-
async function validateAgentsMap(
|
|
579
|
-
const contentResult = await readFileContent(
|
|
601
|
+
async function validateAgentsMap(path23 = "./AGENTS.md") {
|
|
602
|
+
const contentResult = await readFileContent(path23);
|
|
580
603
|
if (!contentResult.ok) {
|
|
581
604
|
return (0, import_types.Err)(
|
|
582
605
|
createError(
|
|
583
606
|
"PARSE_ERROR",
|
|
584
607
|
`Failed to read AGENTS.md: ${contentResult.error.message}`,
|
|
585
|
-
{ path:
|
|
608
|
+
{ path: path23 },
|
|
586
609
|
["Ensure the file exists", "Check file permissions"]
|
|
587
610
|
)
|
|
588
611
|
);
|
|
589
612
|
}
|
|
590
613
|
const content = contentResult.value;
|
|
591
614
|
const sections = extractSections(content);
|
|
592
|
-
const baseDir = (0, import_path.dirname)(
|
|
615
|
+
const baseDir = (0, import_path.dirname)(path23);
|
|
593
616
|
const sectionTitles = sections.map((s) => s.title);
|
|
594
617
|
const missingSections = REQUIRED_SECTIONS.filter(
|
|
595
618
|
(required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
|
|
@@ -730,8 +753,8 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
730
753
|
|
|
731
754
|
// src/context/knowledge-map.ts
|
|
732
755
|
var import_path3 = require("path");
|
|
733
|
-
function suggestFix(
|
|
734
|
-
const targetName = (0, import_path3.basename)(
|
|
756
|
+
function suggestFix(path23, existingFiles) {
|
|
757
|
+
const targetName = (0, import_path3.basename)(path23).toLowerCase();
|
|
735
758
|
const similar = existingFiles.find((file) => {
|
|
736
759
|
const fileName = (0, import_path3.basename)(file).toLowerCase();
|
|
737
760
|
return fileName.includes(targetName) || targetName.includes(fileName);
|
|
@@ -739,7 +762,7 @@ function suggestFix(path22, existingFiles) {
|
|
|
739
762
|
if (similar) {
|
|
740
763
|
return `Did you mean "${similar}"?`;
|
|
741
764
|
}
|
|
742
|
-
return `Create the file "${
|
|
765
|
+
return `Create the file "${path23}" or remove the link`;
|
|
743
766
|
}
|
|
744
767
|
async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
745
768
|
const agentsPath = (0, import_path3.join)(rootDir, "AGENTS.md");
|
|
@@ -1345,8 +1368,8 @@ function createBoundaryValidator(schema, name) {
|
|
|
1345
1368
|
return (0, import_types.Ok)(result.data);
|
|
1346
1369
|
}
|
|
1347
1370
|
const suggestions = result.error.issues.map((issue) => {
|
|
1348
|
-
const
|
|
1349
|
-
return
|
|
1371
|
+
const path23 = issue.path.join(".");
|
|
1372
|
+
return path23 ? `${path23}: ${issue.message}` : issue.message;
|
|
1350
1373
|
});
|
|
1351
1374
|
return (0, import_types.Err)(
|
|
1352
1375
|
createError(
|
|
@@ -1978,11 +2001,11 @@ function processExportListSpecifiers(exportDecl, exports2) {
|
|
|
1978
2001
|
var TypeScriptParser = class {
|
|
1979
2002
|
name = "typescript";
|
|
1980
2003
|
extensions = [".ts", ".tsx", ".mts", ".cts"];
|
|
1981
|
-
async parseFile(
|
|
1982
|
-
const contentResult = await readFileContent(
|
|
2004
|
+
async parseFile(path23) {
|
|
2005
|
+
const contentResult = await readFileContent(path23);
|
|
1983
2006
|
if (!contentResult.ok) {
|
|
1984
2007
|
return (0, import_types.Err)(
|
|
1985
|
-
createParseError("NOT_FOUND", `File not found: ${
|
|
2008
|
+
createParseError("NOT_FOUND", `File not found: ${path23}`, { path: path23 }, [
|
|
1986
2009
|
"Check that the file exists",
|
|
1987
2010
|
"Verify the path is correct"
|
|
1988
2011
|
])
|
|
@@ -1992,7 +2015,7 @@ var TypeScriptParser = class {
|
|
|
1992
2015
|
const ast = (0, import_typescript_estree.parse)(contentResult.value, {
|
|
1993
2016
|
loc: true,
|
|
1994
2017
|
range: true,
|
|
1995
|
-
jsx:
|
|
2018
|
+
jsx: path23.endsWith(".tsx"),
|
|
1996
2019
|
errorOnUnknownASTType: false
|
|
1997
2020
|
});
|
|
1998
2021
|
return (0, import_types.Ok)({
|
|
@@ -2003,7 +2026,7 @@ var TypeScriptParser = class {
|
|
|
2003
2026
|
} catch (e) {
|
|
2004
2027
|
const error = e;
|
|
2005
2028
|
return (0, import_types.Err)(
|
|
2006
|
-
createParseError("SYNTAX_ERROR", `Failed to parse ${
|
|
2029
|
+
createParseError("SYNTAX_ERROR", `Failed to parse ${path23}: ${error.message}`, { path: path23 }, [
|
|
2007
2030
|
"Check for syntax errors in the file",
|
|
2008
2031
|
"Ensure valid TypeScript syntax"
|
|
2009
2032
|
])
|
|
@@ -2188,22 +2211,22 @@ function extractInlineRefs(content) {
|
|
|
2188
2211
|
}
|
|
2189
2212
|
return refs;
|
|
2190
2213
|
}
|
|
2191
|
-
async function parseDocumentationFile(
|
|
2192
|
-
const contentResult = await readFileContent(
|
|
2214
|
+
async function parseDocumentationFile(path23) {
|
|
2215
|
+
const contentResult = await readFileContent(path23);
|
|
2193
2216
|
if (!contentResult.ok) {
|
|
2194
2217
|
return (0, import_types.Err)(
|
|
2195
2218
|
createEntropyError(
|
|
2196
2219
|
"PARSE_ERROR",
|
|
2197
|
-
`Failed to read documentation file: ${
|
|
2198
|
-
{ file:
|
|
2220
|
+
`Failed to read documentation file: ${path23}`,
|
|
2221
|
+
{ file: path23 },
|
|
2199
2222
|
["Check that the file exists"]
|
|
2200
2223
|
)
|
|
2201
2224
|
);
|
|
2202
2225
|
}
|
|
2203
2226
|
const content = contentResult.value;
|
|
2204
|
-
const type =
|
|
2227
|
+
const type = path23.endsWith(".md") ? "markdown" : "text";
|
|
2205
2228
|
return (0, import_types.Ok)({
|
|
2206
|
-
path:
|
|
2229
|
+
path: path23,
|
|
2207
2230
|
type,
|
|
2208
2231
|
content,
|
|
2209
2232
|
codeBlocks: extractCodeBlocks(content),
|
|
@@ -6868,6 +6891,8 @@ var SESSION_INDEX_FILE = "index.md";
|
|
|
6868
6891
|
var SUMMARY_FILE = "summary.md";
|
|
6869
6892
|
var SESSION_STATE_FILE = "session-state.json";
|
|
6870
6893
|
var ARCHIVE_DIR = "archive";
|
|
6894
|
+
var CONTENT_HASHES_FILE = "content-hashes.json";
|
|
6895
|
+
var EVENTS_FILE = "events.jsonl";
|
|
6871
6896
|
|
|
6872
6897
|
// src/state/stream-resolver.ts
|
|
6873
6898
|
var STREAMS_DIR = "streams";
|
|
@@ -7210,6 +7235,85 @@ async function saveState(projectPath, state, stream, session) {
|
|
|
7210
7235
|
// src/state/learnings.ts
|
|
7211
7236
|
var fs9 = __toESM(require("fs"));
|
|
7212
7237
|
var path6 = __toESM(require("path"));
|
|
7238
|
+
var crypto = __toESM(require("crypto"));
|
|
7239
|
+
function parseFrontmatter(line) {
|
|
7240
|
+
const match = line.match(/^<!--\s+hash:([a-f0-9]+)(?:\s+tags:([^\s]+))?\s+-->/);
|
|
7241
|
+
if (!match) return null;
|
|
7242
|
+
const hash = match[1];
|
|
7243
|
+
const tags = match[2] ? match[2].split(",").filter(Boolean) : [];
|
|
7244
|
+
return { hash, tags };
|
|
7245
|
+
}
|
|
7246
|
+
function computeEntryHash(text) {
|
|
7247
|
+
return crypto.createHash("sha256").update(text).digest("hex").slice(0, 8);
|
|
7248
|
+
}
|
|
7249
|
+
function normalizeLearningContent(text) {
|
|
7250
|
+
let normalized = text;
|
|
7251
|
+
normalized = normalized.replace(/\d{4}-\d{2}-\d{2}/g, "");
|
|
7252
|
+
normalized = normalized.replace(/\[skill:[^\]]*\]/g, "");
|
|
7253
|
+
normalized = normalized.replace(/\[outcome:[^\]]*\]/g, "");
|
|
7254
|
+
normalized = normalized.replace(/^[\s]*[-*]\s+/gm, "");
|
|
7255
|
+
normalized = normalized.replace(/\*\*/g, "");
|
|
7256
|
+
normalized = normalized.replace(/:\s*/g, " ");
|
|
7257
|
+
normalized = normalized.toLowerCase();
|
|
7258
|
+
normalized = normalized.replace(/\s+/g, " ").trim();
|
|
7259
|
+
return normalized;
|
|
7260
|
+
}
|
|
7261
|
+
function computeContentHash(text) {
|
|
7262
|
+
return crypto.createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
7263
|
+
}
|
|
7264
|
+
function loadContentHashes(stateDir) {
|
|
7265
|
+
const hashesPath = path6.join(stateDir, CONTENT_HASHES_FILE);
|
|
7266
|
+
if (!fs9.existsSync(hashesPath)) return {};
|
|
7267
|
+
try {
|
|
7268
|
+
const raw = fs9.readFileSync(hashesPath, "utf-8");
|
|
7269
|
+
const parsed = JSON.parse(raw);
|
|
7270
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
7271
|
+
return parsed;
|
|
7272
|
+
} catch {
|
|
7273
|
+
return {};
|
|
7274
|
+
}
|
|
7275
|
+
}
|
|
7276
|
+
function saveContentHashes(stateDir, index) {
|
|
7277
|
+
const hashesPath = path6.join(stateDir, CONTENT_HASHES_FILE);
|
|
7278
|
+
fs9.writeFileSync(hashesPath, JSON.stringify(index, null, 2) + "\n");
|
|
7279
|
+
}
|
|
7280
|
+
function rebuildContentHashes(stateDir) {
|
|
7281
|
+
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
7282
|
+
if (!fs9.existsSync(learningsPath)) return {};
|
|
7283
|
+
const content = fs9.readFileSync(learningsPath, "utf-8");
|
|
7284
|
+
const lines = content.split("\n");
|
|
7285
|
+
const index = {};
|
|
7286
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7287
|
+
const line = lines[i];
|
|
7288
|
+
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
7289
|
+
if (isDatedBullet) {
|
|
7290
|
+
const learningMatch = line.match(/:\*\*\s*(.+)$/);
|
|
7291
|
+
if (learningMatch?.[1]) {
|
|
7292
|
+
const normalized = normalizeLearningContent(learningMatch[1]);
|
|
7293
|
+
const hash = computeContentHash(normalized);
|
|
7294
|
+
const dateMatch = line.match(/(\d{4}-\d{2}-\d{2})/);
|
|
7295
|
+
index[hash] = { date: dateMatch?.[1] ?? "", line: i + 1 };
|
|
7296
|
+
}
|
|
7297
|
+
}
|
|
7298
|
+
}
|
|
7299
|
+
saveContentHashes(stateDir, index);
|
|
7300
|
+
return index;
|
|
7301
|
+
}
|
|
7302
|
+
function extractIndexEntry(entry) {
|
|
7303
|
+
const lines = entry.split("\n");
|
|
7304
|
+
const summary = lines[0] ?? entry;
|
|
7305
|
+
const tags = [];
|
|
7306
|
+
const skillMatch = entry.match(/\[skill:([^\]]+)\]/);
|
|
7307
|
+
if (skillMatch?.[1]) tags.push(skillMatch[1]);
|
|
7308
|
+
const outcomeMatch = entry.match(/\[outcome:([^\]]+)\]/);
|
|
7309
|
+
if (outcomeMatch?.[1]) tags.push(outcomeMatch[1]);
|
|
7310
|
+
return {
|
|
7311
|
+
hash: computeEntryHash(entry),
|
|
7312
|
+
tags,
|
|
7313
|
+
summary,
|
|
7314
|
+
fullText: entry
|
|
7315
|
+
};
|
|
7316
|
+
}
|
|
7213
7317
|
var learningsCacheMap = /* @__PURE__ */ new Map();
|
|
7214
7318
|
function clearLearningsCache() {
|
|
7215
7319
|
learningsCacheMap.clear();
|
|
@@ -7221,27 +7325,55 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream,
|
|
|
7221
7325
|
const stateDir = dirResult.value;
|
|
7222
7326
|
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
7223
7327
|
fs9.mkdirSync(stateDir, { recursive: true });
|
|
7328
|
+
const normalizedContent = normalizeLearningContent(learning);
|
|
7329
|
+
const contentHash = computeContentHash(normalizedContent);
|
|
7330
|
+
const hashesPath = path6.join(stateDir, CONTENT_HASHES_FILE);
|
|
7331
|
+
let contentHashes;
|
|
7332
|
+
if (fs9.existsSync(hashesPath)) {
|
|
7333
|
+
contentHashes = loadContentHashes(stateDir);
|
|
7334
|
+
if (Object.keys(contentHashes).length === 0 && fs9.existsSync(learningsPath)) {
|
|
7335
|
+
contentHashes = rebuildContentHashes(stateDir);
|
|
7336
|
+
}
|
|
7337
|
+
} else if (fs9.existsSync(learningsPath)) {
|
|
7338
|
+
contentHashes = rebuildContentHashes(stateDir);
|
|
7339
|
+
} else {
|
|
7340
|
+
contentHashes = {};
|
|
7341
|
+
}
|
|
7342
|
+
if (contentHashes[contentHash]) {
|
|
7343
|
+
return (0, import_types.Ok)(void 0);
|
|
7344
|
+
}
|
|
7224
7345
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
7225
|
-
|
|
7346
|
+
const fmTags = [];
|
|
7347
|
+
if (skillName) fmTags.push(skillName);
|
|
7348
|
+
if (outcome) fmTags.push(outcome);
|
|
7349
|
+
let bulletLine;
|
|
7226
7350
|
if (skillName && outcome) {
|
|
7227
|
-
|
|
7228
|
-
- **${timestamp} [skill:${skillName}] [outcome:${outcome}]:** ${learning}
|
|
7229
|
-
`;
|
|
7351
|
+
bulletLine = `- **${timestamp} [skill:${skillName}] [outcome:${outcome}]:** ${learning}`;
|
|
7230
7352
|
} else if (skillName) {
|
|
7231
|
-
|
|
7232
|
-
- **${timestamp} [skill:${skillName}]:** ${learning}
|
|
7233
|
-
`;
|
|
7353
|
+
bulletLine = `- **${timestamp} [skill:${skillName}]:** ${learning}`;
|
|
7234
7354
|
} else {
|
|
7235
|
-
|
|
7236
|
-
- **${timestamp}:** ${learning}
|
|
7237
|
-
`;
|
|
7355
|
+
bulletLine = `- **${timestamp}:** ${learning}`;
|
|
7238
7356
|
}
|
|
7357
|
+
const hash = crypto.createHash("sha256").update(bulletLine).digest("hex").slice(0, 8);
|
|
7358
|
+
const tagsStr = fmTags.length > 0 ? ` tags:${fmTags.join(",")}` : "";
|
|
7359
|
+
const frontmatter = `<!-- hash:${hash}${tagsStr} -->`;
|
|
7360
|
+
const entry = `
|
|
7361
|
+
${frontmatter}
|
|
7362
|
+
${bulletLine}
|
|
7363
|
+
`;
|
|
7364
|
+
let existingLineCount;
|
|
7239
7365
|
if (!fs9.existsSync(learningsPath)) {
|
|
7240
7366
|
fs9.writeFileSync(learningsPath, `# Learnings
|
|
7241
7367
|
${entry}`);
|
|
7368
|
+
existingLineCount = 1;
|
|
7242
7369
|
} else {
|
|
7370
|
+
const existingContent = fs9.readFileSync(learningsPath, "utf-8");
|
|
7371
|
+
existingLineCount = existingContent.split("\n").length;
|
|
7243
7372
|
fs9.appendFileSync(learningsPath, entry);
|
|
7244
7373
|
}
|
|
7374
|
+
const bulletLine_lineNum = existingLineCount + 2;
|
|
7375
|
+
contentHashes[contentHash] = { date: timestamp ?? "", line: bulletLine_lineNum };
|
|
7376
|
+
saveContentHashes(stateDir, contentHashes);
|
|
7245
7377
|
learningsCacheMap.delete(learningsPath);
|
|
7246
7378
|
return (0, import_types.Ok)(void 0);
|
|
7247
7379
|
} catch (error) {
|
|
@@ -7289,7 +7421,30 @@ function analyzeLearningPatterns(entries) {
|
|
|
7289
7421
|
return patterns.sort((a, b) => b.count - a.count);
|
|
7290
7422
|
}
|
|
7291
7423
|
async function loadBudgetedLearnings(projectPath, options) {
|
|
7292
|
-
const { intent, tokenBudget = 1e3, skill, session, stream } = options;
|
|
7424
|
+
const { intent, tokenBudget = 1e3, skill, session, stream, depth = "summary" } = options;
|
|
7425
|
+
if (depth === "index") {
|
|
7426
|
+
const indexEntries = [];
|
|
7427
|
+
if (session) {
|
|
7428
|
+
const sessionResult = await loadIndexEntries(projectPath, skill, stream, session);
|
|
7429
|
+
if (sessionResult.ok) indexEntries.push(...sessionResult.value);
|
|
7430
|
+
}
|
|
7431
|
+
const globalResult2 = await loadIndexEntries(projectPath, skill, stream);
|
|
7432
|
+
if (globalResult2.ok) {
|
|
7433
|
+
const sessionHashes = new Set(indexEntries.map((e) => e.hash));
|
|
7434
|
+
const uniqueGlobal = globalResult2.value.filter((e) => !sessionHashes.has(e.hash));
|
|
7435
|
+
indexEntries.push(...uniqueGlobal);
|
|
7436
|
+
}
|
|
7437
|
+
const budgeted2 = [];
|
|
7438
|
+
let totalTokens2 = 0;
|
|
7439
|
+
for (const entry of indexEntries) {
|
|
7440
|
+
const separator = budgeted2.length > 0 ? "\n" : "";
|
|
7441
|
+
const entryCost = estimateTokens(entry.summary + separator);
|
|
7442
|
+
if (totalTokens2 + entryCost > tokenBudget) break;
|
|
7443
|
+
budgeted2.push(entry.summary);
|
|
7444
|
+
totalTokens2 += entryCost;
|
|
7445
|
+
}
|
|
7446
|
+
return (0, import_types.Ok)(budgeted2);
|
|
7447
|
+
}
|
|
7293
7448
|
const sortByRecencyAndRelevance = (entries) => {
|
|
7294
7449
|
return [...entries].sort((a, b) => {
|
|
7295
7450
|
const dateA = parseDateFromEntry(a) ?? "0000-00-00";
|
|
@@ -7308,7 +7463,9 @@ async function loadBudgetedLearnings(projectPath, options) {
|
|
|
7308
7463
|
}
|
|
7309
7464
|
const globalResult = await loadRelevantLearnings(projectPath, skill, stream);
|
|
7310
7465
|
if (globalResult.ok) {
|
|
7311
|
-
allEntries.
|
|
7466
|
+
const sessionSet = new Set(allEntries.map((e) => e.trim()));
|
|
7467
|
+
const uniqueGlobal = globalResult.value.filter((e) => !sessionSet.has(e.trim()));
|
|
7468
|
+
allEntries.push(...sortByRecencyAndRelevance(uniqueGlobal));
|
|
7312
7469
|
}
|
|
7313
7470
|
const budgeted = [];
|
|
7314
7471
|
let totalTokens = 0;
|
|
@@ -7321,6 +7478,68 @@ async function loadBudgetedLearnings(projectPath, options) {
|
|
|
7321
7478
|
}
|
|
7322
7479
|
return (0, import_types.Ok)(budgeted);
|
|
7323
7480
|
}
|
|
7481
|
+
async function loadIndexEntries(projectPath, skillName, stream, session) {
|
|
7482
|
+
try {
|
|
7483
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7484
|
+
if (!dirResult.ok) return dirResult;
|
|
7485
|
+
const stateDir = dirResult.value;
|
|
7486
|
+
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
7487
|
+
if (!fs9.existsSync(learningsPath)) {
|
|
7488
|
+
return (0, import_types.Ok)([]);
|
|
7489
|
+
}
|
|
7490
|
+
const content = fs9.readFileSync(learningsPath, "utf-8");
|
|
7491
|
+
const lines = content.split("\n");
|
|
7492
|
+
const indexEntries = [];
|
|
7493
|
+
let pendingFrontmatter = null;
|
|
7494
|
+
let currentBlock = [];
|
|
7495
|
+
for (const line of lines) {
|
|
7496
|
+
if (line.startsWith("# ")) continue;
|
|
7497
|
+
const fm = parseFrontmatter(line);
|
|
7498
|
+
if (fm) {
|
|
7499
|
+
pendingFrontmatter = fm;
|
|
7500
|
+
continue;
|
|
7501
|
+
}
|
|
7502
|
+
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
7503
|
+
const isHeading = /^## \d{4}-\d{2}-\d{2}/.test(line);
|
|
7504
|
+
if (isDatedBullet || isHeading) {
|
|
7505
|
+
if (pendingFrontmatter) {
|
|
7506
|
+
indexEntries.push({
|
|
7507
|
+
hash: pendingFrontmatter.hash,
|
|
7508
|
+
tags: pendingFrontmatter.tags,
|
|
7509
|
+
summary: line,
|
|
7510
|
+
fullText: ""
|
|
7511
|
+
// Placeholder — full text not loaded in index mode
|
|
7512
|
+
});
|
|
7513
|
+
pendingFrontmatter = null;
|
|
7514
|
+
} else {
|
|
7515
|
+
const idx = extractIndexEntry(line);
|
|
7516
|
+
indexEntries.push({
|
|
7517
|
+
hash: idx.hash,
|
|
7518
|
+
tags: idx.tags,
|
|
7519
|
+
summary: line,
|
|
7520
|
+
fullText: ""
|
|
7521
|
+
});
|
|
7522
|
+
}
|
|
7523
|
+
currentBlock = [line];
|
|
7524
|
+
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
7525
|
+
currentBlock.push(line);
|
|
7526
|
+
}
|
|
7527
|
+
}
|
|
7528
|
+
if (skillName) {
|
|
7529
|
+
const filtered = indexEntries.filter(
|
|
7530
|
+
(e) => e.tags.includes(skillName) || e.summary.includes(`[skill:${skillName}]`)
|
|
7531
|
+
);
|
|
7532
|
+
return (0, import_types.Ok)(filtered);
|
|
7533
|
+
}
|
|
7534
|
+
return (0, import_types.Ok)(indexEntries);
|
|
7535
|
+
} catch (error) {
|
|
7536
|
+
return (0, import_types.Err)(
|
|
7537
|
+
new Error(
|
|
7538
|
+
`Failed to load index entries: ${error instanceof Error ? error.message : String(error)}`
|
|
7539
|
+
)
|
|
7540
|
+
);
|
|
7541
|
+
}
|
|
7542
|
+
}
|
|
7324
7543
|
async function loadRelevantLearnings(projectPath, skillName, stream, session) {
|
|
7325
7544
|
try {
|
|
7326
7545
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
@@ -7343,6 +7562,7 @@ async function loadRelevantLearnings(projectPath, skillName, stream, session) {
|
|
|
7343
7562
|
let currentBlock = [];
|
|
7344
7563
|
for (const line of lines) {
|
|
7345
7564
|
if (line.startsWith("# ")) continue;
|
|
7565
|
+
if (/^<!--\s+hash:[a-f0-9]+/.test(line)) continue;
|
|
7346
7566
|
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
7347
7567
|
const isHeading = /^## \d{4}-\d{2}-\d{2}/.test(line);
|
|
7348
7568
|
if (isDatedBullet || isHeading) {
|
|
@@ -7452,6 +7672,68 @@ async function pruneLearnings(projectPath, stream) {
|
|
|
7452
7672
|
);
|
|
7453
7673
|
}
|
|
7454
7674
|
}
|
|
7675
|
+
var PROMOTABLE_OUTCOMES = ["gotcha", "decision", "observation"];
|
|
7676
|
+
function isGeneralizable(entry) {
|
|
7677
|
+
for (const outcome of PROMOTABLE_OUTCOMES) {
|
|
7678
|
+
if (entry.includes(`[outcome:${outcome}]`)) return true;
|
|
7679
|
+
}
|
|
7680
|
+
return false;
|
|
7681
|
+
}
|
|
7682
|
+
async function promoteSessionLearnings(projectPath, sessionSlug, stream) {
|
|
7683
|
+
try {
|
|
7684
|
+
const sessionResult = await loadRelevantLearnings(projectPath, void 0, stream, sessionSlug);
|
|
7685
|
+
if (!sessionResult.ok) return sessionResult;
|
|
7686
|
+
const sessionEntries = sessionResult.value;
|
|
7687
|
+
if (sessionEntries.length === 0) {
|
|
7688
|
+
return (0, import_types.Ok)({ promoted: 0, skipped: 0 });
|
|
7689
|
+
}
|
|
7690
|
+
const toPromote = [];
|
|
7691
|
+
let skipped = 0;
|
|
7692
|
+
for (const entry of sessionEntries) {
|
|
7693
|
+
if (isGeneralizable(entry)) {
|
|
7694
|
+
toPromote.push(entry);
|
|
7695
|
+
} else {
|
|
7696
|
+
skipped++;
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7699
|
+
if (toPromote.length === 0) {
|
|
7700
|
+
return (0, import_types.Ok)({ promoted: 0, skipped });
|
|
7701
|
+
}
|
|
7702
|
+
const dirResult = await getStateDir(projectPath, stream);
|
|
7703
|
+
if (!dirResult.ok) return dirResult;
|
|
7704
|
+
const stateDir = dirResult.value;
|
|
7705
|
+
const globalPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
7706
|
+
const existingGlobal = fs9.existsSync(globalPath) ? fs9.readFileSync(globalPath, "utf-8") : "";
|
|
7707
|
+
const newEntries = toPromote.filter((entry) => !existingGlobal.includes(entry.trim()));
|
|
7708
|
+
if (newEntries.length === 0) {
|
|
7709
|
+
return (0, import_types.Ok)({ promoted: 0, skipped: skipped + toPromote.length });
|
|
7710
|
+
}
|
|
7711
|
+
const promotedContent = newEntries.join("\n\n") + "\n";
|
|
7712
|
+
if (!existingGlobal) {
|
|
7713
|
+
fs9.writeFileSync(globalPath, `# Learnings
|
|
7714
|
+
|
|
7715
|
+
${promotedContent}`);
|
|
7716
|
+
} else {
|
|
7717
|
+
fs9.appendFileSync(globalPath, "\n\n" + promotedContent);
|
|
7718
|
+
}
|
|
7719
|
+
learningsCacheMap.delete(globalPath);
|
|
7720
|
+
return (0, import_types.Ok)({
|
|
7721
|
+
promoted: newEntries.length,
|
|
7722
|
+
skipped: skipped + (toPromote.length - newEntries.length)
|
|
7723
|
+
});
|
|
7724
|
+
} catch (error) {
|
|
7725
|
+
return (0, import_types.Err)(
|
|
7726
|
+
new Error(
|
|
7727
|
+
`Failed to promote session learnings: ${error instanceof Error ? error.message : String(error)}`
|
|
7728
|
+
)
|
|
7729
|
+
);
|
|
7730
|
+
}
|
|
7731
|
+
}
|
|
7732
|
+
async function countLearningEntries(projectPath, stream) {
|
|
7733
|
+
const loadResult = await loadRelevantLearnings(projectPath, void 0, stream);
|
|
7734
|
+
if (!loadResult.ok) return 0;
|
|
7735
|
+
return loadResult.value.length;
|
|
7736
|
+
}
|
|
7455
7737
|
|
|
7456
7738
|
// src/state/failures.ts
|
|
7457
7739
|
var fs10 = __toESM(require("fs"));
|
|
@@ -7913,6 +8195,151 @@ async function archiveSession(projectPath, sessionSlug) {
|
|
|
7913
8195
|
}
|
|
7914
8196
|
}
|
|
7915
8197
|
|
|
8198
|
+
// src/state/events.ts
|
|
8199
|
+
var fs16 = __toESM(require("fs"));
|
|
8200
|
+
var path13 = __toESM(require("path"));
|
|
8201
|
+
var import_zod6 = require("zod");
|
|
8202
|
+
var SkillEventSchema = import_zod6.z.object({
|
|
8203
|
+
timestamp: import_zod6.z.string(),
|
|
8204
|
+
skill: import_zod6.z.string(),
|
|
8205
|
+
session: import_zod6.z.string().optional(),
|
|
8206
|
+
type: import_zod6.z.enum(["phase_transition", "decision", "gate_result", "handoff", "error", "checkpoint"]),
|
|
8207
|
+
summary: import_zod6.z.string(),
|
|
8208
|
+
data: import_zod6.z.record(import_zod6.z.unknown()).optional(),
|
|
8209
|
+
refs: import_zod6.z.array(import_zod6.z.string()).optional(),
|
|
8210
|
+
contentHash: import_zod6.z.string().optional()
|
|
8211
|
+
});
|
|
8212
|
+
function computeEventHash(event, session) {
|
|
8213
|
+
const identity = `${event.skill}|${event.type}|${event.summary}|${session ?? ""}`;
|
|
8214
|
+
return computeContentHash(identity);
|
|
8215
|
+
}
|
|
8216
|
+
var knownHashesCache = /* @__PURE__ */ new Map();
|
|
8217
|
+
function loadKnownHashes(eventsPath) {
|
|
8218
|
+
const cached = knownHashesCache.get(eventsPath);
|
|
8219
|
+
if (cached) return cached;
|
|
8220
|
+
const hashes = /* @__PURE__ */ new Set();
|
|
8221
|
+
if (fs16.existsSync(eventsPath)) {
|
|
8222
|
+
const content = fs16.readFileSync(eventsPath, "utf-8");
|
|
8223
|
+
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
|
8224
|
+
for (const line of lines) {
|
|
8225
|
+
try {
|
|
8226
|
+
const existing = JSON.parse(line);
|
|
8227
|
+
if (existing.contentHash) {
|
|
8228
|
+
hashes.add(existing.contentHash);
|
|
8229
|
+
}
|
|
8230
|
+
} catch {
|
|
8231
|
+
}
|
|
8232
|
+
}
|
|
8233
|
+
}
|
|
8234
|
+
knownHashesCache.set(eventsPath, hashes);
|
|
8235
|
+
return hashes;
|
|
8236
|
+
}
|
|
8237
|
+
function clearEventHashCache() {
|
|
8238
|
+
knownHashesCache.clear();
|
|
8239
|
+
}
|
|
8240
|
+
async function emitEvent(projectPath, event, options) {
|
|
8241
|
+
try {
|
|
8242
|
+
const dirResult = await getStateDir(projectPath, options?.stream, options?.session);
|
|
8243
|
+
if (!dirResult.ok) return dirResult;
|
|
8244
|
+
const stateDir = dirResult.value;
|
|
8245
|
+
const eventsPath = path13.join(stateDir, EVENTS_FILE);
|
|
8246
|
+
fs16.mkdirSync(stateDir, { recursive: true });
|
|
8247
|
+
const contentHash = computeEventHash(event, options?.session);
|
|
8248
|
+
const knownHashes = loadKnownHashes(eventsPath);
|
|
8249
|
+
if (knownHashes.has(contentHash)) {
|
|
8250
|
+
return (0, import_types.Ok)({ written: false, reason: "duplicate" });
|
|
8251
|
+
}
|
|
8252
|
+
const fullEvent = {
|
|
8253
|
+
...event,
|
|
8254
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8255
|
+
contentHash
|
|
8256
|
+
};
|
|
8257
|
+
if (options?.session) {
|
|
8258
|
+
fullEvent.session = options.session;
|
|
8259
|
+
}
|
|
8260
|
+
fs16.appendFileSync(eventsPath, JSON.stringify(fullEvent) + "\n");
|
|
8261
|
+
knownHashes.add(contentHash);
|
|
8262
|
+
return (0, import_types.Ok)({ written: true });
|
|
8263
|
+
} catch (error) {
|
|
8264
|
+
return (0, import_types.Err)(
|
|
8265
|
+
new Error(`Failed to emit event: ${error instanceof Error ? error.message : String(error)}`)
|
|
8266
|
+
);
|
|
8267
|
+
}
|
|
8268
|
+
}
|
|
8269
|
+
async function loadEvents(projectPath, options) {
|
|
8270
|
+
try {
|
|
8271
|
+
const dirResult = await getStateDir(projectPath, options?.stream, options?.session);
|
|
8272
|
+
if (!dirResult.ok) return dirResult;
|
|
8273
|
+
const stateDir = dirResult.value;
|
|
8274
|
+
const eventsPath = path13.join(stateDir, EVENTS_FILE);
|
|
8275
|
+
if (!fs16.existsSync(eventsPath)) {
|
|
8276
|
+
return (0, import_types.Ok)([]);
|
|
8277
|
+
}
|
|
8278
|
+
const content = fs16.readFileSync(eventsPath, "utf-8");
|
|
8279
|
+
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
|
8280
|
+
const events = [];
|
|
8281
|
+
for (const line of lines) {
|
|
8282
|
+
try {
|
|
8283
|
+
const parsed = JSON.parse(line);
|
|
8284
|
+
const result = SkillEventSchema.safeParse(parsed);
|
|
8285
|
+
if (result.success) {
|
|
8286
|
+
events.push(result.data);
|
|
8287
|
+
}
|
|
8288
|
+
} catch {
|
|
8289
|
+
}
|
|
8290
|
+
}
|
|
8291
|
+
return (0, import_types.Ok)(events);
|
|
8292
|
+
} catch (error) {
|
|
8293
|
+
return (0, import_types.Err)(
|
|
8294
|
+
new Error(`Failed to load events: ${error instanceof Error ? error.message : String(error)}`)
|
|
8295
|
+
);
|
|
8296
|
+
}
|
|
8297
|
+
}
|
|
8298
|
+
function formatPhaseTransition(event) {
|
|
8299
|
+
const data = event.data;
|
|
8300
|
+
const suffix = data?.taskCount ? ` (${data.taskCount} tasks)` : "";
|
|
8301
|
+
return `phase: ${data?.from ?? "?"} -> ${data?.to ?? "?"}${suffix}`;
|
|
8302
|
+
}
|
|
8303
|
+
function formatGateResult(event) {
|
|
8304
|
+
const data = event.data;
|
|
8305
|
+
const status = data?.passed ? "passed" : "failed";
|
|
8306
|
+
const checks = data?.checks?.map((c) => `${c.name} ${c.passed ? "Y" : "N"}`).join(", ");
|
|
8307
|
+
return checks ? `gate: ${status} (${checks})` : `gate: ${status}`;
|
|
8308
|
+
}
|
|
8309
|
+
function formatHandoffDetail(event) {
|
|
8310
|
+
const data = event.data;
|
|
8311
|
+
const direction = data?.toSkill ? ` -> ${data.toSkill}` : "";
|
|
8312
|
+
return `handoff: ${event.summary}${direction}`;
|
|
8313
|
+
}
|
|
8314
|
+
var EVENT_FORMATTERS = {
|
|
8315
|
+
phase_transition: formatPhaseTransition,
|
|
8316
|
+
gate_result: formatGateResult,
|
|
8317
|
+
decision: (event) => `decision: ${event.summary}`,
|
|
8318
|
+
handoff: formatHandoffDetail,
|
|
8319
|
+
error: (event) => `error: ${event.summary}`,
|
|
8320
|
+
checkpoint: (event) => `checkpoint: ${event.summary}`
|
|
8321
|
+
};
|
|
8322
|
+
function formatEventTimeline(events, limit = 20) {
|
|
8323
|
+
if (events.length === 0) return "";
|
|
8324
|
+
const recent = events.slice(-limit);
|
|
8325
|
+
return recent.map((event) => {
|
|
8326
|
+
const time = formatTime(event.timestamp);
|
|
8327
|
+
const formatter = EVENT_FORMATTERS[event.type];
|
|
8328
|
+
const detail = formatter ? formatter(event) : event.summary;
|
|
8329
|
+
return `- ${time} [${event.skill}] ${detail}`;
|
|
8330
|
+
}).join("\n");
|
|
8331
|
+
}
|
|
8332
|
+
function formatTime(timestamp) {
|
|
8333
|
+
try {
|
|
8334
|
+
const date = new Date(timestamp);
|
|
8335
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
8336
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
8337
|
+
return `${hours}:${minutes}`;
|
|
8338
|
+
} catch {
|
|
8339
|
+
return "??:??";
|
|
8340
|
+
}
|
|
8341
|
+
}
|
|
8342
|
+
|
|
7916
8343
|
// src/workflow/runner.ts
|
|
7917
8344
|
async function executeWorkflow(workflow, executor) {
|
|
7918
8345
|
const stepResults = [];
|
|
@@ -8062,7 +8489,8 @@ async function runMultiTurnPipeline(initialContext, turnExecutor, options) {
|
|
|
8062
8489
|
}
|
|
8063
8490
|
|
|
8064
8491
|
// src/security/scanner.ts
|
|
8065
|
-
var
|
|
8492
|
+
var fs18 = __toESM(require("fs/promises"));
|
|
8493
|
+
var import_minimatch5 = require("minimatch");
|
|
8066
8494
|
|
|
8067
8495
|
// src/security/rules/registry.ts
|
|
8068
8496
|
var RuleRegistry = class {
|
|
@@ -8093,7 +8521,7 @@ var RuleRegistry = class {
|
|
|
8093
8521
|
};
|
|
8094
8522
|
|
|
8095
8523
|
// src/security/config.ts
|
|
8096
|
-
var
|
|
8524
|
+
var import_zod7 = require("zod");
|
|
8097
8525
|
|
|
8098
8526
|
// src/security/types.ts
|
|
8099
8527
|
var DEFAULT_SECURITY_CONFIG = {
|
|
@@ -8104,19 +8532,19 @@ var DEFAULT_SECURITY_CONFIG = {
|
|
|
8104
8532
|
};
|
|
8105
8533
|
|
|
8106
8534
|
// src/security/config.ts
|
|
8107
|
-
var RuleOverrideSchema =
|
|
8108
|
-
var SecurityConfigSchema =
|
|
8109
|
-
enabled:
|
|
8110
|
-
strict:
|
|
8111
|
-
rules:
|
|
8112
|
-
exclude:
|
|
8113
|
-
external:
|
|
8114
|
-
semgrep:
|
|
8115
|
-
enabled:
|
|
8116
|
-
rulesets:
|
|
8535
|
+
var RuleOverrideSchema = import_zod7.z.enum(["off", "error", "warning", "info"]);
|
|
8536
|
+
var SecurityConfigSchema = import_zod7.z.object({
|
|
8537
|
+
enabled: import_zod7.z.boolean().default(true),
|
|
8538
|
+
strict: import_zod7.z.boolean().default(false),
|
|
8539
|
+
rules: import_zod7.z.record(import_zod7.z.string(), RuleOverrideSchema).optional().default({}),
|
|
8540
|
+
exclude: import_zod7.z.array(import_zod7.z.string()).optional().default(["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/fixtures/**"]),
|
|
8541
|
+
external: import_zod7.z.object({
|
|
8542
|
+
semgrep: import_zod7.z.object({
|
|
8543
|
+
enabled: import_zod7.z.union([import_zod7.z.literal("auto"), import_zod7.z.boolean()]).default("auto"),
|
|
8544
|
+
rulesets: import_zod7.z.array(import_zod7.z.string()).optional()
|
|
8117
8545
|
}).optional(),
|
|
8118
|
-
gitleaks:
|
|
8119
|
-
enabled:
|
|
8546
|
+
gitleaks: import_zod7.z.object({
|
|
8547
|
+
enabled: import_zod7.z.union([import_zod7.z.literal("auto"), import_zod7.z.boolean()]).default("auto")
|
|
8120
8548
|
}).optional()
|
|
8121
8549
|
}).optional()
|
|
8122
8550
|
});
|
|
@@ -8149,15 +8577,15 @@ function resolveRuleSeverity(ruleId, defaultSeverity, overrides, strict) {
|
|
|
8149
8577
|
}
|
|
8150
8578
|
|
|
8151
8579
|
// src/security/stack-detector.ts
|
|
8152
|
-
var
|
|
8153
|
-
var
|
|
8580
|
+
var fs17 = __toESM(require("fs"));
|
|
8581
|
+
var path14 = __toESM(require("path"));
|
|
8154
8582
|
function detectStack(projectRoot) {
|
|
8155
8583
|
const stacks = [];
|
|
8156
|
-
const pkgJsonPath =
|
|
8157
|
-
if (
|
|
8584
|
+
const pkgJsonPath = path14.join(projectRoot, "package.json");
|
|
8585
|
+
if (fs17.existsSync(pkgJsonPath)) {
|
|
8158
8586
|
stacks.push("node");
|
|
8159
8587
|
try {
|
|
8160
|
-
const pkgJson = JSON.parse(
|
|
8588
|
+
const pkgJson = JSON.parse(fs17.readFileSync(pkgJsonPath, "utf-8"));
|
|
8161
8589
|
const allDeps = {
|
|
8162
8590
|
...pkgJson.dependencies,
|
|
8163
8591
|
...pkgJson.devDependencies
|
|
@@ -8172,13 +8600,13 @@ function detectStack(projectRoot) {
|
|
|
8172
8600
|
} catch {
|
|
8173
8601
|
}
|
|
8174
8602
|
}
|
|
8175
|
-
const goModPath =
|
|
8176
|
-
if (
|
|
8603
|
+
const goModPath = path14.join(projectRoot, "go.mod");
|
|
8604
|
+
if (fs17.existsSync(goModPath)) {
|
|
8177
8605
|
stacks.push("go");
|
|
8178
8606
|
}
|
|
8179
|
-
const requirementsPath =
|
|
8180
|
-
const pyprojectPath =
|
|
8181
|
-
if (
|
|
8607
|
+
const requirementsPath = path14.join(projectRoot, "requirements.txt");
|
|
8608
|
+
const pyprojectPath = path14.join(projectRoot, "pyproject.toml");
|
|
8609
|
+
if (fs17.existsSync(requirementsPath) || fs17.existsSync(pyprojectPath)) {
|
|
8182
8610
|
stacks.push("python");
|
|
8183
8611
|
}
|
|
8184
8612
|
return stacks;
|
|
@@ -8242,6 +8670,72 @@ var secretRules = [
|
|
|
8242
8670
|
message: "Hardcoded JWT token detected",
|
|
8243
8671
|
remediation: "Tokens should be fetched at runtime, not embedded in source",
|
|
8244
8672
|
references: ["CWE-798"]
|
|
8673
|
+
},
|
|
8674
|
+
{
|
|
8675
|
+
id: "SEC-SEC-006",
|
|
8676
|
+
name: "Anthropic API Key",
|
|
8677
|
+
category: "secrets",
|
|
8678
|
+
severity: "error",
|
|
8679
|
+
confidence: "high",
|
|
8680
|
+
patterns: [/sk-ant-api\d{2}-[A-Za-z0-9_-]{20,}/],
|
|
8681
|
+
message: "Hardcoded Anthropic API key detected",
|
|
8682
|
+
remediation: "Use environment variables: process.env.ANTHROPIC_API_KEY",
|
|
8683
|
+
references: ["CWE-798"]
|
|
8684
|
+
},
|
|
8685
|
+
{
|
|
8686
|
+
id: "SEC-SEC-007",
|
|
8687
|
+
name: "OpenAI API Key",
|
|
8688
|
+
category: "secrets",
|
|
8689
|
+
severity: "error",
|
|
8690
|
+
confidence: "high",
|
|
8691
|
+
patterns: [/sk-proj-[A-Za-z0-9_-]{20,}/],
|
|
8692
|
+
message: "Hardcoded OpenAI API key detected",
|
|
8693
|
+
remediation: "Use environment variables: process.env.OPENAI_API_KEY",
|
|
8694
|
+
references: ["CWE-798"]
|
|
8695
|
+
},
|
|
8696
|
+
{
|
|
8697
|
+
id: "SEC-SEC-008",
|
|
8698
|
+
name: "Google API Key",
|
|
8699
|
+
category: "secrets",
|
|
8700
|
+
severity: "error",
|
|
8701
|
+
confidence: "high",
|
|
8702
|
+
patterns: [/AIza[A-Za-z0-9_-]{35}/],
|
|
8703
|
+
message: "Hardcoded Google API key detected",
|
|
8704
|
+
remediation: "Use environment variables or a secrets manager for Google API keys",
|
|
8705
|
+
references: ["CWE-798"]
|
|
8706
|
+
},
|
|
8707
|
+
{
|
|
8708
|
+
id: "SEC-SEC-009",
|
|
8709
|
+
name: "GitHub Personal Access Token",
|
|
8710
|
+
category: "secrets",
|
|
8711
|
+
severity: "error",
|
|
8712
|
+
confidence: "high",
|
|
8713
|
+
patterns: [/gh[pous]_[A-Za-z0-9_]{36,}/],
|
|
8714
|
+
message: "Hardcoded GitHub personal access token detected",
|
|
8715
|
+
remediation: "Use environment variables: process.env.GITHUB_TOKEN",
|
|
8716
|
+
references: ["CWE-798"]
|
|
8717
|
+
},
|
|
8718
|
+
{
|
|
8719
|
+
id: "SEC-SEC-010",
|
|
8720
|
+
name: "Stripe Live Key",
|
|
8721
|
+
category: "secrets",
|
|
8722
|
+
severity: "error",
|
|
8723
|
+
confidence: "high",
|
|
8724
|
+
patterns: [/\b[spr]k_live_[A-Za-z0-9]{24,}/],
|
|
8725
|
+
message: "Hardcoded Stripe live key detected",
|
|
8726
|
+
remediation: "Use environment variables for Stripe keys; never commit live keys",
|
|
8727
|
+
references: ["CWE-798"]
|
|
8728
|
+
},
|
|
8729
|
+
{
|
|
8730
|
+
id: "SEC-SEC-011",
|
|
8731
|
+
name: "Database Connection String with Credentials",
|
|
8732
|
+
category: "secrets",
|
|
8733
|
+
severity: "error",
|
|
8734
|
+
confidence: "high",
|
|
8735
|
+
patterns: [/(?:postgres|mysql|mongodb|redis|amqp|mssql)(?:\+\w+)?:\/\/[^/\s:]+:[^@/\s]+@/i],
|
|
8736
|
+
message: "Database connection string with embedded credentials detected",
|
|
8737
|
+
remediation: "Use environment variables for connection strings; separate credentials from URIs",
|
|
8738
|
+
references: ["CWE-798"]
|
|
8245
8739
|
}
|
|
8246
8740
|
];
|
|
8247
8741
|
|
|
@@ -8428,6 +8922,158 @@ var deserializationRules = [
|
|
|
8428
8922
|
}
|
|
8429
8923
|
];
|
|
8430
8924
|
|
|
8925
|
+
// src/security/rules/agent-config.ts
|
|
8926
|
+
var agentConfigRules = [
|
|
8927
|
+
{
|
|
8928
|
+
id: "SEC-AGT-001",
|
|
8929
|
+
name: "Hidden Unicode Characters",
|
|
8930
|
+
category: "agent-config",
|
|
8931
|
+
severity: "error",
|
|
8932
|
+
confidence: "high",
|
|
8933
|
+
patterns: [/\u200B|\u200C|\u200D|\uFEFF|\u2060/],
|
|
8934
|
+
fileGlob: "**/CLAUDE.md,**/AGENTS.md,**/*.yaml",
|
|
8935
|
+
message: "Hidden zero-width Unicode characters detected in agent configuration",
|
|
8936
|
+
remediation: "Remove invisible Unicode characters; they may hide malicious instructions",
|
|
8937
|
+
references: ["CWE-116"]
|
|
8938
|
+
},
|
|
8939
|
+
{
|
|
8940
|
+
id: "SEC-AGT-002",
|
|
8941
|
+
name: "URL Execution Directives",
|
|
8942
|
+
category: "agent-config",
|
|
8943
|
+
severity: "warning",
|
|
8944
|
+
confidence: "medium",
|
|
8945
|
+
patterns: [/\b(?:curl|wget)\s+\S+/i, /\bfetch\s*\(/i],
|
|
8946
|
+
fileGlob: "**/CLAUDE.md,**/AGENTS.md",
|
|
8947
|
+
message: "URL execution directive found in agent configuration",
|
|
8948
|
+
remediation: "Avoid instructing agents to download and execute remote content",
|
|
8949
|
+
references: ["CWE-94"]
|
|
8950
|
+
},
|
|
8951
|
+
{
|
|
8952
|
+
id: "SEC-AGT-003",
|
|
8953
|
+
name: "Wildcard Tool Permissions",
|
|
8954
|
+
category: "agent-config",
|
|
8955
|
+
severity: "warning",
|
|
8956
|
+
confidence: "high",
|
|
8957
|
+
patterns: [/(?:Bash|Write|Edit)\s*\(\s*\*\s*\)/],
|
|
8958
|
+
fileGlob: "**/.claude/**,**/settings*.json",
|
|
8959
|
+
message: "Wildcard tool permissions grant unrestricted access",
|
|
8960
|
+
remediation: "Scope tool permissions to specific patterns instead of wildcards",
|
|
8961
|
+
references: ["CWE-250"]
|
|
8962
|
+
},
|
|
8963
|
+
{
|
|
8964
|
+
id: "SEC-AGT-004",
|
|
8965
|
+
name: "Auto-approve Patterns",
|
|
8966
|
+
category: "agent-config",
|
|
8967
|
+
severity: "warning",
|
|
8968
|
+
confidence: "high",
|
|
8969
|
+
patterns: [/\bautoApprove\b/i, /\bauto_approve\b/i],
|
|
8970
|
+
fileGlob: "**/.claude/**,**/.mcp.json",
|
|
8971
|
+
message: "Auto-approve configuration bypasses human review of tool calls",
|
|
8972
|
+
remediation: "Review auto-approved tools carefully; prefer explicit approval for destructive operations",
|
|
8973
|
+
references: ["CWE-862"]
|
|
8974
|
+
},
|
|
8975
|
+
{
|
|
8976
|
+
id: "SEC-AGT-005",
|
|
8977
|
+
name: "Prompt Injection Surface",
|
|
8978
|
+
category: "agent-config",
|
|
8979
|
+
severity: "warning",
|
|
8980
|
+
confidence: "medium",
|
|
8981
|
+
patterns: [/\$\{[^}]*\}/, /\{\{[^}]*\}\}/],
|
|
8982
|
+
fileGlob: "**/skill.yaml",
|
|
8983
|
+
message: "Template interpolation syntax in skill YAML may enable prompt injection",
|
|
8984
|
+
remediation: "Avoid dynamic interpolation in skill descriptions; use static text",
|
|
8985
|
+
references: ["CWE-94"]
|
|
8986
|
+
},
|
|
8987
|
+
{
|
|
8988
|
+
id: "SEC-AGT-006",
|
|
8989
|
+
name: "Permission Bypass Flags",
|
|
8990
|
+
category: "agent-config",
|
|
8991
|
+
severity: "error",
|
|
8992
|
+
confidence: "high",
|
|
8993
|
+
patterns: [/--dangerously-skip-permissions/, /--no-verify/],
|
|
8994
|
+
fileGlob: "**/CLAUDE.md,**/AGENTS.md,**/.claude/**",
|
|
8995
|
+
message: "Permission bypass flag detected in agent configuration",
|
|
8996
|
+
remediation: "Remove flags that bypass safety checks; they undermine enforcement",
|
|
8997
|
+
references: ["CWE-863"]
|
|
8998
|
+
},
|
|
8999
|
+
{
|
|
9000
|
+
id: "SEC-AGT-007",
|
|
9001
|
+
name: "Hook Injection Surface",
|
|
9002
|
+
category: "agent-config",
|
|
9003
|
+
severity: "error",
|
|
9004
|
+
confidence: "low",
|
|
9005
|
+
patterns: [/\$\(/, /`[^`]+`/, /\s&&\s/, /\s\|\|\s/],
|
|
9006
|
+
fileGlob: "**/settings*.json,**/hooks.json",
|
|
9007
|
+
message: "Shell metacharacters in hook commands may enable command injection",
|
|
9008
|
+
remediation: "Use simple, single-command hooks without shell operators; chain logic inside the script",
|
|
9009
|
+
references: ["CWE-78"]
|
|
9010
|
+
}
|
|
9011
|
+
];
|
|
9012
|
+
|
|
9013
|
+
// src/security/rules/mcp.ts
|
|
9014
|
+
var mcpRules = [
|
|
9015
|
+
{
|
|
9016
|
+
id: "SEC-MCP-001",
|
|
9017
|
+
name: "Hardcoded MCP Secrets",
|
|
9018
|
+
category: "mcp",
|
|
9019
|
+
severity: "error",
|
|
9020
|
+
confidence: "medium",
|
|
9021
|
+
patterns: [/(?:API_KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL)\s*["']?\s*:\s*["'][^"']{8,}["']/i],
|
|
9022
|
+
fileGlob: "**/.mcp.json",
|
|
9023
|
+
message: "Hardcoded secret detected in MCP server configuration",
|
|
9024
|
+
remediation: "Use environment variable references instead of inline secrets in .mcp.json",
|
|
9025
|
+
references: ["CWE-798"]
|
|
9026
|
+
},
|
|
9027
|
+
{
|
|
9028
|
+
id: "SEC-MCP-002",
|
|
9029
|
+
name: "Shell Injection in MCP Args",
|
|
9030
|
+
category: "mcp",
|
|
9031
|
+
severity: "error",
|
|
9032
|
+
confidence: "medium",
|
|
9033
|
+
patterns: [/\$\(/, /`[^`]+`/],
|
|
9034
|
+
fileGlob: "**/.mcp.json",
|
|
9035
|
+
message: "Shell metacharacters detected in MCP server arguments",
|
|
9036
|
+
remediation: "Use literal argument values; avoid shell interpolation in MCP args",
|
|
9037
|
+
references: ["CWE-78"]
|
|
9038
|
+
},
|
|
9039
|
+
{
|
|
9040
|
+
id: "SEC-MCP-003",
|
|
9041
|
+
name: "Network Exposure",
|
|
9042
|
+
category: "mcp",
|
|
9043
|
+
severity: "warning",
|
|
9044
|
+
confidence: "high",
|
|
9045
|
+
patterns: [/0\.0\.0\.0/, /["']\*["']\s*:\s*\d/, /host["']?\s*:\s*["']\*["']/i],
|
|
9046
|
+
fileGlob: "**/.mcp.json",
|
|
9047
|
+
message: "MCP server binding to all network interfaces (0.0.0.0 or wildcard *)",
|
|
9048
|
+
remediation: "Bind to 127.0.0.1 or localhost to restrict access to local machine",
|
|
9049
|
+
references: ["CWE-668"]
|
|
9050
|
+
},
|
|
9051
|
+
{
|
|
9052
|
+
id: "SEC-MCP-004",
|
|
9053
|
+
name: "Typosquatting Vector",
|
|
9054
|
+
category: "mcp",
|
|
9055
|
+
severity: "warning",
|
|
9056
|
+
confidence: "medium",
|
|
9057
|
+
patterns: [/\bnpx\s+(?:-y|--yes)\b/],
|
|
9058
|
+
fileGlob: "**/.mcp.json",
|
|
9059
|
+
message: "npx -y auto-installs packages without confirmation, enabling typosquatting",
|
|
9060
|
+
remediation: "Pin exact package versions or install packages explicitly before use",
|
|
9061
|
+
references: ["CWE-427"]
|
|
9062
|
+
},
|
|
9063
|
+
{
|
|
9064
|
+
id: "SEC-MCP-005",
|
|
9065
|
+
name: "Unencrypted Transport",
|
|
9066
|
+
category: "mcp",
|
|
9067
|
+
severity: "warning",
|
|
9068
|
+
confidence: "medium",
|
|
9069
|
+
patterns: [/http:\/\/(?!localhost\b|127\.0\.0\.1\b)/],
|
|
9070
|
+
fileGlob: "**/.mcp.json",
|
|
9071
|
+
message: "Unencrypted HTTP transport detected for MCP server connection",
|
|
9072
|
+
remediation: "Use https:// for all non-localhost MCP server connections",
|
|
9073
|
+
references: ["CWE-319"]
|
|
9074
|
+
}
|
|
9075
|
+
];
|
|
9076
|
+
|
|
8431
9077
|
// src/security/rules/stack/node.ts
|
|
8432
9078
|
var nodeRules = [
|
|
8433
9079
|
{
|
|
@@ -8555,7 +9201,9 @@ var SecurityScanner = class {
|
|
|
8555
9201
|
...cryptoRules,
|
|
8556
9202
|
...pathTraversalRules,
|
|
8557
9203
|
...networkRules,
|
|
8558
|
-
...deserializationRules
|
|
9204
|
+
...deserializationRules,
|
|
9205
|
+
...agentConfigRules,
|
|
9206
|
+
...mcpRules
|
|
8559
9207
|
]);
|
|
8560
9208
|
this.registry.registerAll([...nodeRules, ...expressRules, ...reactRules, ...goRules]);
|
|
8561
9209
|
this.activeRules = this.registry.getAll();
|
|
@@ -8564,6 +9212,12 @@ var SecurityScanner = class {
|
|
|
8564
9212
|
const stacks = detectStack(projectRoot);
|
|
8565
9213
|
this.activeRules = this.registry.getForStacks(stacks.length > 0 ? stacks : []);
|
|
8566
9214
|
}
|
|
9215
|
+
/**
|
|
9216
|
+
* Scan raw content against all active rules. Note: this method does NOT apply
|
|
9217
|
+
* fileGlob filtering — every active rule is evaluated regardless of filePath.
|
|
9218
|
+
* If you are scanning a specific file and want fileGlob-based rule filtering,
|
|
9219
|
+
* use {@link scanFile} instead.
|
|
9220
|
+
*/
|
|
8567
9221
|
scanContent(content, filePath, startLine = 1) {
|
|
8568
9222
|
if (!this.config.enabled) return [];
|
|
8569
9223
|
const findings = [];
|
|
@@ -8605,8 +9259,52 @@ var SecurityScanner = class {
|
|
|
8605
9259
|
}
|
|
8606
9260
|
async scanFile(filePath) {
|
|
8607
9261
|
if (!this.config.enabled) return [];
|
|
8608
|
-
const content = await
|
|
8609
|
-
return this.
|
|
9262
|
+
const content = await fs18.readFile(filePath, "utf-8");
|
|
9263
|
+
return this.scanContentForFile(content, filePath, 1);
|
|
9264
|
+
}
|
|
9265
|
+
scanContentForFile(content, filePath, startLine = 1) {
|
|
9266
|
+
if (!this.config.enabled) return [];
|
|
9267
|
+
const findings = [];
|
|
9268
|
+
const lines = content.split("\n");
|
|
9269
|
+
const applicableRules = this.activeRules.filter((rule) => {
|
|
9270
|
+
if (!rule.fileGlob) return true;
|
|
9271
|
+
const globs = rule.fileGlob.split(",").map((g) => g.trim());
|
|
9272
|
+
return globs.some((glob2) => (0, import_minimatch5.minimatch)(filePath, glob2, { dot: true }));
|
|
9273
|
+
});
|
|
9274
|
+
for (const rule of applicableRules) {
|
|
9275
|
+
const resolved = resolveRuleSeverity(
|
|
9276
|
+
rule.id,
|
|
9277
|
+
rule.severity,
|
|
9278
|
+
this.config.rules ?? {},
|
|
9279
|
+
this.config.strict
|
|
9280
|
+
);
|
|
9281
|
+
if (resolved === "off") continue;
|
|
9282
|
+
for (let i = 0; i < lines.length; i++) {
|
|
9283
|
+
const line = lines[i] ?? "";
|
|
9284
|
+
if (line.includes("harness-ignore") && line.includes(rule.id)) continue;
|
|
9285
|
+
for (const pattern of rule.patterns) {
|
|
9286
|
+
pattern.lastIndex = 0;
|
|
9287
|
+
if (pattern.test(line)) {
|
|
9288
|
+
findings.push({
|
|
9289
|
+
ruleId: rule.id,
|
|
9290
|
+
ruleName: rule.name,
|
|
9291
|
+
category: rule.category,
|
|
9292
|
+
severity: resolved,
|
|
9293
|
+
confidence: rule.confidence,
|
|
9294
|
+
file: filePath,
|
|
9295
|
+
line: startLine + i,
|
|
9296
|
+
match: line.trim(),
|
|
9297
|
+
context: line,
|
|
9298
|
+
message: rule.message,
|
|
9299
|
+
remediation: rule.remediation,
|
|
9300
|
+
...rule.references ? { references: rule.references } : {}
|
|
9301
|
+
});
|
|
9302
|
+
break;
|
|
9303
|
+
}
|
|
9304
|
+
}
|
|
9305
|
+
}
|
|
9306
|
+
}
|
|
9307
|
+
return findings;
|
|
8610
9308
|
}
|
|
8611
9309
|
async scanFiles(filePaths) {
|
|
8612
9310
|
const allFindings = [];
|
|
@@ -8630,7 +9328,7 @@ var SecurityScanner = class {
|
|
|
8630
9328
|
};
|
|
8631
9329
|
|
|
8632
9330
|
// src/ci/check-orchestrator.ts
|
|
8633
|
-
var
|
|
9331
|
+
var path15 = __toESM(require("path"));
|
|
8634
9332
|
var ALL_CHECKS = [
|
|
8635
9333
|
"validate",
|
|
8636
9334
|
"deps",
|
|
@@ -8643,7 +9341,7 @@ var ALL_CHECKS = [
|
|
|
8643
9341
|
];
|
|
8644
9342
|
async function runValidateCheck(projectRoot, config) {
|
|
8645
9343
|
const issues = [];
|
|
8646
|
-
const agentsPath =
|
|
9344
|
+
const agentsPath = path15.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
8647
9345
|
const result = await validateAgentsMap(agentsPath);
|
|
8648
9346
|
if (!result.ok) {
|
|
8649
9347
|
issues.push({ severity: "error", message: result.error.message });
|
|
@@ -8700,7 +9398,7 @@ async function runDepsCheck(projectRoot, config) {
|
|
|
8700
9398
|
}
|
|
8701
9399
|
async function runDocsCheck(projectRoot, config) {
|
|
8702
9400
|
const issues = [];
|
|
8703
|
-
const docsDir =
|
|
9401
|
+
const docsDir = path15.join(projectRoot, config.docsDir ?? "docs");
|
|
8704
9402
|
const entropyConfig = config.entropy || {};
|
|
8705
9403
|
const result = await checkDocCoverage("project", {
|
|
8706
9404
|
docsDir,
|
|
@@ -8978,7 +9676,7 @@ async function runCIChecks(input) {
|
|
|
8978
9676
|
}
|
|
8979
9677
|
|
|
8980
9678
|
// src/review/mechanical-checks.ts
|
|
8981
|
-
var
|
|
9679
|
+
var path16 = __toESM(require("path"));
|
|
8982
9680
|
async function runMechanicalChecks(options) {
|
|
8983
9681
|
const { projectRoot, config, skip = [], changedFiles } = options;
|
|
8984
9682
|
const findings = [];
|
|
@@ -8990,7 +9688,7 @@ async function runMechanicalChecks(options) {
|
|
|
8990
9688
|
};
|
|
8991
9689
|
if (!skip.includes("validate")) {
|
|
8992
9690
|
try {
|
|
8993
|
-
const agentsPath =
|
|
9691
|
+
const agentsPath = path16.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
8994
9692
|
const result = await validateAgentsMap(agentsPath);
|
|
8995
9693
|
if (!result.ok) {
|
|
8996
9694
|
statuses.validate = "fail";
|
|
@@ -9027,7 +9725,7 @@ async function runMechanicalChecks(options) {
|
|
|
9027
9725
|
statuses.validate = "fail";
|
|
9028
9726
|
findings.push({
|
|
9029
9727
|
tool: "validate",
|
|
9030
|
-
file:
|
|
9728
|
+
file: path16.join(projectRoot, "AGENTS.md"),
|
|
9031
9729
|
message: err instanceof Error ? err.message : String(err),
|
|
9032
9730
|
severity: "error"
|
|
9033
9731
|
});
|
|
@@ -9091,7 +9789,7 @@ async function runMechanicalChecks(options) {
|
|
|
9091
9789
|
(async () => {
|
|
9092
9790
|
const localFindings = [];
|
|
9093
9791
|
try {
|
|
9094
|
-
const docsDir =
|
|
9792
|
+
const docsDir = path16.join(projectRoot, config.docsDir ?? "docs");
|
|
9095
9793
|
const result = await checkDocCoverage("project", { docsDir });
|
|
9096
9794
|
if (!result.ok) {
|
|
9097
9795
|
statuses["check-docs"] = "warn";
|
|
@@ -9118,7 +9816,7 @@ async function runMechanicalChecks(options) {
|
|
|
9118
9816
|
statuses["check-docs"] = "warn";
|
|
9119
9817
|
localFindings.push({
|
|
9120
9818
|
tool: "check-docs",
|
|
9121
|
-
file:
|
|
9819
|
+
file: path16.join(projectRoot, "docs"),
|
|
9122
9820
|
message: err instanceof Error ? err.message : String(err),
|
|
9123
9821
|
severity: "warning"
|
|
9124
9822
|
});
|
|
@@ -9266,7 +9964,7 @@ function detectChangeType(commitMessage, diff2) {
|
|
|
9266
9964
|
}
|
|
9267
9965
|
|
|
9268
9966
|
// src/review/context-scoper.ts
|
|
9269
|
-
var
|
|
9967
|
+
var path17 = __toESM(require("path"));
|
|
9270
9968
|
var ALL_DOMAINS = ["compliance", "bug", "security", "architecture"];
|
|
9271
9969
|
var SECURITY_PATTERNS = /auth|crypto|password|secret|token|session|cookie|hash|encrypt|decrypt|sql|shell|exec|eval/i;
|
|
9272
9970
|
function computeContextBudget(diffLines) {
|
|
@@ -9274,18 +9972,18 @@ function computeContextBudget(diffLines) {
|
|
|
9274
9972
|
return diffLines;
|
|
9275
9973
|
}
|
|
9276
9974
|
function isWithinProject(absPath, projectRoot) {
|
|
9277
|
-
const resolvedRoot =
|
|
9278
|
-
const resolvedPath =
|
|
9279
|
-
return resolvedPath.startsWith(resolvedRoot) || resolvedPath ===
|
|
9975
|
+
const resolvedRoot = path17.resolve(projectRoot) + path17.sep;
|
|
9976
|
+
const resolvedPath = path17.resolve(absPath);
|
|
9977
|
+
return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path17.resolve(projectRoot);
|
|
9280
9978
|
}
|
|
9281
9979
|
async function readContextFile(projectRoot, filePath, reason) {
|
|
9282
|
-
const absPath =
|
|
9980
|
+
const absPath = path17.isAbsolute(filePath) ? filePath : path17.join(projectRoot, filePath);
|
|
9283
9981
|
if (!isWithinProject(absPath, projectRoot)) return null;
|
|
9284
9982
|
const result = await readFileContent(absPath);
|
|
9285
9983
|
if (!result.ok) return null;
|
|
9286
9984
|
const content = result.value;
|
|
9287
9985
|
const lines = content.split("\n").length;
|
|
9288
|
-
const relPath =
|
|
9986
|
+
const relPath = path17.isAbsolute(filePath) ? relativePosix(projectRoot, filePath) : filePath;
|
|
9289
9987
|
return { path: relPath, content, reason, lines };
|
|
9290
9988
|
}
|
|
9291
9989
|
function extractImportSources2(content) {
|
|
@@ -9300,18 +9998,18 @@ function extractImportSources2(content) {
|
|
|
9300
9998
|
}
|
|
9301
9999
|
async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
9302
10000
|
if (!importSource.startsWith(".")) return null;
|
|
9303
|
-
const fromDir =
|
|
9304
|
-
const basePath =
|
|
10001
|
+
const fromDir = path17.dirname(path17.join(projectRoot, fromFile));
|
|
10002
|
+
const basePath = path17.resolve(fromDir, importSource);
|
|
9305
10003
|
if (!isWithinProject(basePath, projectRoot)) return null;
|
|
9306
10004
|
const relBase = relativePosix(projectRoot, basePath);
|
|
9307
10005
|
const candidates = [
|
|
9308
10006
|
relBase + ".ts",
|
|
9309
10007
|
relBase + ".tsx",
|
|
9310
10008
|
relBase + ".mts",
|
|
9311
|
-
|
|
10009
|
+
path17.join(relBase, "index.ts")
|
|
9312
10010
|
];
|
|
9313
10011
|
for (const candidate of candidates) {
|
|
9314
|
-
const absCandidate =
|
|
10012
|
+
const absCandidate = path17.join(projectRoot, candidate);
|
|
9315
10013
|
if (await fileExists(absCandidate)) {
|
|
9316
10014
|
return candidate;
|
|
9317
10015
|
}
|
|
@@ -9319,7 +10017,7 @@ async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
|
9319
10017
|
return null;
|
|
9320
10018
|
}
|
|
9321
10019
|
async function findTestFiles(projectRoot, sourceFile) {
|
|
9322
|
-
const baseName =
|
|
10020
|
+
const baseName = path17.basename(sourceFile, path17.extname(sourceFile));
|
|
9323
10021
|
const pattern = `**/${baseName}.{test,spec}.{ts,tsx,mts}`;
|
|
9324
10022
|
const results = await findFiles(pattern, projectRoot);
|
|
9325
10023
|
return results.map((f) => relativePosix(projectRoot, f));
|
|
@@ -10128,7 +10826,7 @@ async function fanOutReview(options) {
|
|
|
10128
10826
|
}
|
|
10129
10827
|
|
|
10130
10828
|
// src/review/validate-findings.ts
|
|
10131
|
-
var
|
|
10829
|
+
var path18 = __toESM(require("path"));
|
|
10132
10830
|
var DOWNGRADE_MAP = {
|
|
10133
10831
|
critical: "important",
|
|
10134
10832
|
important: "suggestion",
|
|
@@ -10149,7 +10847,7 @@ function normalizePath(filePath, projectRoot) {
|
|
|
10149
10847
|
let normalized = filePath;
|
|
10150
10848
|
normalized = normalized.replace(/\\/g, "/");
|
|
10151
10849
|
const normalizedRoot = projectRoot.replace(/\\/g, "/");
|
|
10152
|
-
if (
|
|
10850
|
+
if (path18.isAbsolute(normalized)) {
|
|
10153
10851
|
const root = normalizedRoot.endsWith("/") ? normalizedRoot : normalizedRoot + "/";
|
|
10154
10852
|
if (normalized.startsWith(root)) {
|
|
10155
10853
|
normalized = normalized.slice(root.length);
|
|
@@ -10174,12 +10872,12 @@ function followImportChain(fromFile, fileContents, maxDepth = 2) {
|
|
|
10174
10872
|
while ((match = importRegex.exec(content)) !== null) {
|
|
10175
10873
|
const importPath = match[1];
|
|
10176
10874
|
if (!importPath.startsWith(".")) continue;
|
|
10177
|
-
const dir =
|
|
10178
|
-
let resolved =
|
|
10875
|
+
const dir = path18.dirname(current.file);
|
|
10876
|
+
let resolved = path18.join(dir, importPath).replace(/\\/g, "/");
|
|
10179
10877
|
if (!resolved.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
10180
10878
|
resolved += ".ts";
|
|
10181
10879
|
}
|
|
10182
|
-
resolved =
|
|
10880
|
+
resolved = path18.normalize(resolved).replace(/\\/g, "/");
|
|
10183
10881
|
if (!visited.has(resolved) && current.depth + 1 <= maxDepth) {
|
|
10184
10882
|
queue.push({ file: resolved, depth: current.depth + 1 });
|
|
10185
10883
|
}
|
|
@@ -10196,7 +10894,7 @@ async function validateFindings(options) {
|
|
|
10196
10894
|
if (exclusionSet.isExcluded(normalizedFile, finding.lineRange) || exclusionSet.isExcluded(finding.file, finding.lineRange)) {
|
|
10197
10895
|
continue;
|
|
10198
10896
|
}
|
|
10199
|
-
const absoluteFile =
|
|
10897
|
+
const absoluteFile = path18.isAbsolute(finding.file) ? finding.file : path18.join(projectRoot, finding.file).replace(/\\/g, "/");
|
|
10200
10898
|
if (exclusionSet.isExcluded(absoluteFile, finding.lineRange)) {
|
|
10201
10899
|
continue;
|
|
10202
10900
|
}
|
|
@@ -10824,7 +11522,7 @@ function parseRoadmap(markdown) {
|
|
|
10824
11522
|
if (!fmMatch) {
|
|
10825
11523
|
return (0, import_types19.Err)(new Error("Missing or malformed YAML frontmatter"));
|
|
10826
11524
|
}
|
|
10827
|
-
const fmResult =
|
|
11525
|
+
const fmResult = parseFrontmatter2(fmMatch[1]);
|
|
10828
11526
|
if (!fmResult.ok) return fmResult;
|
|
10829
11527
|
const body = markdown.slice(fmMatch[0].length);
|
|
10830
11528
|
const milestonesResult = parseMilestones(body);
|
|
@@ -10834,7 +11532,7 @@ function parseRoadmap(markdown) {
|
|
|
10834
11532
|
milestones: milestonesResult.value
|
|
10835
11533
|
});
|
|
10836
11534
|
}
|
|
10837
|
-
function
|
|
11535
|
+
function parseFrontmatter2(raw) {
|
|
10838
11536
|
const lines = raw.split("\n");
|
|
10839
11537
|
const map = /* @__PURE__ */ new Map();
|
|
10840
11538
|
for (const line of lines) {
|
|
@@ -11000,8 +11698,8 @@ function serializeFeature(feature) {
|
|
|
11000
11698
|
}
|
|
11001
11699
|
|
|
11002
11700
|
// src/roadmap/sync.ts
|
|
11003
|
-
var
|
|
11004
|
-
var
|
|
11701
|
+
var fs19 = __toESM(require("fs"));
|
|
11702
|
+
var path19 = __toESM(require("path"));
|
|
11005
11703
|
var import_types20 = require("@harness-engineering/types");
|
|
11006
11704
|
function inferStatus(feature, projectPath, allFeatures) {
|
|
11007
11705
|
if (feature.blockedBy.length > 0) {
|
|
@@ -11016,10 +11714,10 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
11016
11714
|
const featuresWithPlans = allFeatures.filter((f) => f.plans.length > 0);
|
|
11017
11715
|
const useRootState = featuresWithPlans.length <= 1;
|
|
11018
11716
|
if (useRootState) {
|
|
11019
|
-
const rootStatePath =
|
|
11020
|
-
if (
|
|
11717
|
+
const rootStatePath = path19.join(projectPath, ".harness", "state.json");
|
|
11718
|
+
if (fs19.existsSync(rootStatePath)) {
|
|
11021
11719
|
try {
|
|
11022
|
-
const raw =
|
|
11720
|
+
const raw = fs19.readFileSync(rootStatePath, "utf-8");
|
|
11023
11721
|
const state = JSON.parse(raw);
|
|
11024
11722
|
if (state.progress) {
|
|
11025
11723
|
for (const status of Object.values(state.progress)) {
|
|
@@ -11030,16 +11728,16 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
11030
11728
|
}
|
|
11031
11729
|
}
|
|
11032
11730
|
}
|
|
11033
|
-
const sessionsDir =
|
|
11034
|
-
if (
|
|
11731
|
+
const sessionsDir = path19.join(projectPath, ".harness", "sessions");
|
|
11732
|
+
if (fs19.existsSync(sessionsDir)) {
|
|
11035
11733
|
try {
|
|
11036
|
-
const sessionDirs =
|
|
11734
|
+
const sessionDirs = fs19.readdirSync(sessionsDir, { withFileTypes: true });
|
|
11037
11735
|
for (const entry of sessionDirs) {
|
|
11038
11736
|
if (!entry.isDirectory()) continue;
|
|
11039
|
-
const autopilotPath =
|
|
11040
|
-
if (!
|
|
11737
|
+
const autopilotPath = path19.join(sessionsDir, entry.name, "autopilot-state.json");
|
|
11738
|
+
if (!fs19.existsSync(autopilotPath)) continue;
|
|
11041
11739
|
try {
|
|
11042
|
-
const raw =
|
|
11740
|
+
const raw = fs19.readFileSync(autopilotPath, "utf-8");
|
|
11043
11741
|
const autopilot = JSON.parse(raw);
|
|
11044
11742
|
if (!autopilot.phases) continue;
|
|
11045
11743
|
const linkedPhases = autopilot.phases.filter(
|
|
@@ -11069,17 +11767,26 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
11069
11767
|
if (anyStarted) return "in-progress";
|
|
11070
11768
|
return null;
|
|
11071
11769
|
}
|
|
11770
|
+
var STATUS_RANK = {
|
|
11771
|
+
backlog: 0,
|
|
11772
|
+
planned: 1,
|
|
11773
|
+
blocked: 1,
|
|
11774
|
+
// lateral to planned — sync can move to/from blocked freely
|
|
11775
|
+
"in-progress": 2,
|
|
11776
|
+
done: 3
|
|
11777
|
+
};
|
|
11778
|
+
function isRegression(from, to) {
|
|
11779
|
+
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
11780
|
+
}
|
|
11072
11781
|
function syncRoadmap(options) {
|
|
11073
11782
|
const { projectPath, roadmap, forceSync } = options;
|
|
11074
|
-
const isManuallyEdited = new Date(roadmap.frontmatter.lastManualEdit) > new Date(roadmap.frontmatter.lastSynced);
|
|
11075
|
-
const skipOverride = isManuallyEdited && !forceSync;
|
|
11076
11783
|
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
11077
11784
|
const changes = [];
|
|
11078
11785
|
for (const feature of allFeatures) {
|
|
11079
|
-
if (skipOverride) continue;
|
|
11080
11786
|
const inferred = inferStatus(feature, projectPath, allFeatures);
|
|
11081
11787
|
if (inferred === null) continue;
|
|
11082
11788
|
if (inferred === feature.status) continue;
|
|
11789
|
+
if (!forceSync && isRegression(feature.status, inferred)) continue;
|
|
11083
11790
|
changes.push({
|
|
11084
11791
|
feature: feature.name,
|
|
11085
11792
|
from: feature.status,
|
|
@@ -11088,48 +11795,60 @@ function syncRoadmap(options) {
|
|
|
11088
11795
|
}
|
|
11089
11796
|
return (0, import_types20.Ok)(changes);
|
|
11090
11797
|
}
|
|
11798
|
+
function applySyncChanges(roadmap, changes) {
|
|
11799
|
+
for (const change of changes) {
|
|
11800
|
+
for (const m of roadmap.milestones) {
|
|
11801
|
+
const feature = m.features.find((f) => f.name.toLowerCase() === change.feature.toLowerCase());
|
|
11802
|
+
if (feature) {
|
|
11803
|
+
feature.status = change.to;
|
|
11804
|
+
break;
|
|
11805
|
+
}
|
|
11806
|
+
}
|
|
11807
|
+
}
|
|
11808
|
+
roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
11809
|
+
}
|
|
11091
11810
|
|
|
11092
11811
|
// src/interaction/types.ts
|
|
11093
|
-
var
|
|
11094
|
-
var InteractionTypeSchema =
|
|
11095
|
-
var QuestionSchema =
|
|
11096
|
-
text:
|
|
11097
|
-
options:
|
|
11098
|
-
default:
|
|
11812
|
+
var import_zod8 = require("zod");
|
|
11813
|
+
var InteractionTypeSchema = import_zod8.z.enum(["question", "confirmation", "transition"]);
|
|
11814
|
+
var QuestionSchema = import_zod8.z.object({
|
|
11815
|
+
text: import_zod8.z.string(),
|
|
11816
|
+
options: import_zod8.z.array(import_zod8.z.string()).optional(),
|
|
11817
|
+
default: import_zod8.z.string().optional()
|
|
11099
11818
|
});
|
|
11100
|
-
var ConfirmationSchema =
|
|
11101
|
-
text:
|
|
11102
|
-
context:
|
|
11819
|
+
var ConfirmationSchema = import_zod8.z.object({
|
|
11820
|
+
text: import_zod8.z.string(),
|
|
11821
|
+
context: import_zod8.z.string()
|
|
11103
11822
|
});
|
|
11104
|
-
var TransitionSchema =
|
|
11105
|
-
completedPhase:
|
|
11106
|
-
suggestedNext:
|
|
11107
|
-
reason:
|
|
11108
|
-
artifacts:
|
|
11109
|
-
requiresConfirmation:
|
|
11110
|
-
summary:
|
|
11823
|
+
var TransitionSchema = import_zod8.z.object({
|
|
11824
|
+
completedPhase: import_zod8.z.string(),
|
|
11825
|
+
suggestedNext: import_zod8.z.string(),
|
|
11826
|
+
reason: import_zod8.z.string(),
|
|
11827
|
+
artifacts: import_zod8.z.array(import_zod8.z.string()),
|
|
11828
|
+
requiresConfirmation: import_zod8.z.boolean(),
|
|
11829
|
+
summary: import_zod8.z.string()
|
|
11111
11830
|
});
|
|
11112
|
-
var EmitInteractionInputSchema =
|
|
11113
|
-
path:
|
|
11831
|
+
var EmitInteractionInputSchema = import_zod8.z.object({
|
|
11832
|
+
path: import_zod8.z.string(),
|
|
11114
11833
|
type: InteractionTypeSchema,
|
|
11115
|
-
stream:
|
|
11834
|
+
stream: import_zod8.z.string().optional(),
|
|
11116
11835
|
question: QuestionSchema.optional(),
|
|
11117
11836
|
confirmation: ConfirmationSchema.optional(),
|
|
11118
11837
|
transition: TransitionSchema.optional()
|
|
11119
11838
|
});
|
|
11120
11839
|
|
|
11121
11840
|
// src/blueprint/scanner.ts
|
|
11122
|
-
var
|
|
11123
|
-
var
|
|
11841
|
+
var fs20 = __toESM(require("fs/promises"));
|
|
11842
|
+
var path20 = __toESM(require("path"));
|
|
11124
11843
|
var ProjectScanner = class {
|
|
11125
11844
|
constructor(rootDir) {
|
|
11126
11845
|
this.rootDir = rootDir;
|
|
11127
11846
|
}
|
|
11128
11847
|
async scan() {
|
|
11129
|
-
let projectName =
|
|
11848
|
+
let projectName = path20.basename(this.rootDir);
|
|
11130
11849
|
try {
|
|
11131
|
-
const pkgPath =
|
|
11132
|
-
const pkgRaw = await
|
|
11850
|
+
const pkgPath = path20.join(this.rootDir, "package.json");
|
|
11851
|
+
const pkgRaw = await fs20.readFile(pkgPath, "utf-8");
|
|
11133
11852
|
const pkg = JSON.parse(pkgRaw);
|
|
11134
11853
|
if (pkg.name) projectName = pkg.name;
|
|
11135
11854
|
} catch {
|
|
@@ -11170,8 +11889,8 @@ var ProjectScanner = class {
|
|
|
11170
11889
|
};
|
|
11171
11890
|
|
|
11172
11891
|
// src/blueprint/generator.ts
|
|
11173
|
-
var
|
|
11174
|
-
var
|
|
11892
|
+
var fs21 = __toESM(require("fs/promises"));
|
|
11893
|
+
var path21 = __toESM(require("path"));
|
|
11175
11894
|
var ejs = __toESM(require("ejs"));
|
|
11176
11895
|
|
|
11177
11896
|
// src/blueprint/templates.ts
|
|
@@ -11255,19 +11974,19 @@ var BlueprintGenerator = class {
|
|
|
11255
11974
|
styles: STYLES,
|
|
11256
11975
|
scripts: SCRIPTS
|
|
11257
11976
|
});
|
|
11258
|
-
await
|
|
11259
|
-
await
|
|
11977
|
+
await fs21.mkdir(options.outputDir, { recursive: true });
|
|
11978
|
+
await fs21.writeFile(path21.join(options.outputDir, "index.html"), html);
|
|
11260
11979
|
}
|
|
11261
11980
|
};
|
|
11262
11981
|
|
|
11263
11982
|
// src/update-checker.ts
|
|
11264
|
-
var
|
|
11265
|
-
var
|
|
11983
|
+
var fs22 = __toESM(require("fs"));
|
|
11984
|
+
var path22 = __toESM(require("path"));
|
|
11266
11985
|
var os = __toESM(require("os"));
|
|
11267
11986
|
var import_child_process3 = require("child_process");
|
|
11268
11987
|
function getStatePath() {
|
|
11269
11988
|
const home = process.env["HOME"] || os.homedir();
|
|
11270
|
-
return
|
|
11989
|
+
return path22.join(home, ".harness", "update-check.json");
|
|
11271
11990
|
}
|
|
11272
11991
|
function isUpdateCheckEnabled(configInterval) {
|
|
11273
11992
|
if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
|
|
@@ -11280,7 +11999,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
11280
11999
|
}
|
|
11281
12000
|
function readCheckState() {
|
|
11282
12001
|
try {
|
|
11283
|
-
const raw =
|
|
12002
|
+
const raw = fs22.readFileSync(getStatePath(), "utf-8");
|
|
11284
12003
|
const parsed = JSON.parse(raw);
|
|
11285
12004
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
11286
12005
|
const state = parsed;
|
|
@@ -11297,7 +12016,7 @@ function readCheckState() {
|
|
|
11297
12016
|
}
|
|
11298
12017
|
function spawnBackgroundCheck(currentVersion) {
|
|
11299
12018
|
const statePath = getStatePath();
|
|
11300
|
-
const stateDir =
|
|
12019
|
+
const stateDir = path22.dirname(statePath);
|
|
11301
12020
|
const script = `
|
|
11302
12021
|
const { execSync } = require('child_process');
|
|
11303
12022
|
const fs = require('fs');
|
|
@@ -11350,8 +12069,411 @@ function getUpdateNotification(currentVersion) {
|
|
|
11350
12069
|
Run "harness update" to upgrade.`;
|
|
11351
12070
|
}
|
|
11352
12071
|
|
|
12072
|
+
// src/code-nav/types.ts
|
|
12073
|
+
var EXTENSION_MAP = {
|
|
12074
|
+
".ts": "typescript",
|
|
12075
|
+
".tsx": "typescript",
|
|
12076
|
+
".mts": "typescript",
|
|
12077
|
+
".cts": "typescript",
|
|
12078
|
+
".js": "javascript",
|
|
12079
|
+
".jsx": "javascript",
|
|
12080
|
+
".mjs": "javascript",
|
|
12081
|
+
".cjs": "javascript",
|
|
12082
|
+
".py": "python"
|
|
12083
|
+
};
|
|
12084
|
+
function detectLanguage(filePath) {
|
|
12085
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
12086
|
+
return EXTENSION_MAP[ext] ?? null;
|
|
12087
|
+
}
|
|
12088
|
+
|
|
12089
|
+
// src/code-nav/parser.ts
|
|
12090
|
+
var import_web_tree_sitter = __toESM(require("web-tree-sitter"));
|
|
12091
|
+
var import_meta = {};
|
|
12092
|
+
var parserCache = /* @__PURE__ */ new Map();
|
|
12093
|
+
var initialized = false;
|
|
12094
|
+
var GRAMMAR_MAP = {
|
|
12095
|
+
typescript: "tree-sitter-typescript",
|
|
12096
|
+
javascript: "tree-sitter-javascript",
|
|
12097
|
+
python: "tree-sitter-python"
|
|
12098
|
+
};
|
|
12099
|
+
async function ensureInit() {
|
|
12100
|
+
if (!initialized) {
|
|
12101
|
+
await import_web_tree_sitter.default.init();
|
|
12102
|
+
initialized = true;
|
|
12103
|
+
}
|
|
12104
|
+
}
|
|
12105
|
+
async function resolveWasmPath(grammarName) {
|
|
12106
|
+
const { createRequire } = await import("module");
|
|
12107
|
+
const require2 = createRequire(import_meta.url ?? __filename);
|
|
12108
|
+
const pkgPath = require2.resolve("tree-sitter-wasms/package.json");
|
|
12109
|
+
const path23 = await import("path");
|
|
12110
|
+
const pkgDir = path23.dirname(pkgPath);
|
|
12111
|
+
return path23.join(pkgDir, "out", `${grammarName}.wasm`);
|
|
12112
|
+
}
|
|
12113
|
+
async function loadLanguage(lang) {
|
|
12114
|
+
const grammarName = GRAMMAR_MAP[lang];
|
|
12115
|
+
const wasmPath = await resolveWasmPath(grammarName);
|
|
12116
|
+
return import_web_tree_sitter.default.Language.load(wasmPath);
|
|
12117
|
+
}
|
|
12118
|
+
async function getParser(lang) {
|
|
12119
|
+
const cached = parserCache.get(lang);
|
|
12120
|
+
if (cached) return cached;
|
|
12121
|
+
await ensureInit();
|
|
12122
|
+
const parser = new import_web_tree_sitter.default();
|
|
12123
|
+
const language = await loadLanguage(lang);
|
|
12124
|
+
parser.setLanguage(language);
|
|
12125
|
+
parserCache.set(lang, parser);
|
|
12126
|
+
return parser;
|
|
12127
|
+
}
|
|
12128
|
+
async function parseFile(filePath) {
|
|
12129
|
+
const lang = detectLanguage(filePath);
|
|
12130
|
+
if (!lang) {
|
|
12131
|
+
return (0, import_types.Err)({
|
|
12132
|
+
code: "UNSUPPORTED_LANGUAGE",
|
|
12133
|
+
message: `Unsupported file extension: ${filePath}`
|
|
12134
|
+
});
|
|
12135
|
+
}
|
|
12136
|
+
const contentResult = await readFileContent(filePath);
|
|
12137
|
+
if (!contentResult.ok) {
|
|
12138
|
+
return (0, import_types.Err)({
|
|
12139
|
+
code: "FILE_NOT_FOUND",
|
|
12140
|
+
message: `Cannot read file: ${filePath}`
|
|
12141
|
+
});
|
|
12142
|
+
}
|
|
12143
|
+
try {
|
|
12144
|
+
const parser = await getParser(lang);
|
|
12145
|
+
const tree = parser.parse(contentResult.value);
|
|
12146
|
+
return (0, import_types.Ok)({ tree, language: lang, source: contentResult.value, filePath });
|
|
12147
|
+
} catch (e) {
|
|
12148
|
+
return (0, import_types.Err)({
|
|
12149
|
+
code: "PARSE_FAILED",
|
|
12150
|
+
message: `Tree-sitter parse failed for ${filePath}: ${e.message}`
|
|
12151
|
+
});
|
|
12152
|
+
}
|
|
12153
|
+
}
|
|
12154
|
+
function resetParserCache() {
|
|
12155
|
+
parserCache.clear();
|
|
12156
|
+
initialized = false;
|
|
12157
|
+
}
|
|
12158
|
+
|
|
12159
|
+
// src/code-nav/outline.ts
|
|
12160
|
+
var TOP_LEVEL_TYPES = {
|
|
12161
|
+
typescript: {
|
|
12162
|
+
function_declaration: "function",
|
|
12163
|
+
class_declaration: "class",
|
|
12164
|
+
interface_declaration: "interface",
|
|
12165
|
+
type_alias_declaration: "type",
|
|
12166
|
+
lexical_declaration: "variable",
|
|
12167
|
+
variable_declaration: "variable",
|
|
12168
|
+
export_statement: "export",
|
|
12169
|
+
import_statement: "import",
|
|
12170
|
+
enum_declaration: "type"
|
|
12171
|
+
},
|
|
12172
|
+
javascript: {
|
|
12173
|
+
function_declaration: "function",
|
|
12174
|
+
class_declaration: "class",
|
|
12175
|
+
lexical_declaration: "variable",
|
|
12176
|
+
variable_declaration: "variable",
|
|
12177
|
+
export_statement: "export",
|
|
12178
|
+
import_statement: "import"
|
|
12179
|
+
},
|
|
12180
|
+
python: {
|
|
12181
|
+
function_definition: "function",
|
|
12182
|
+
class_definition: "class",
|
|
12183
|
+
assignment: "variable",
|
|
12184
|
+
import_statement: "import",
|
|
12185
|
+
import_from_statement: "import"
|
|
12186
|
+
}
|
|
12187
|
+
};
|
|
12188
|
+
var METHOD_TYPES = {
|
|
12189
|
+
typescript: ["method_definition", "public_field_definition"],
|
|
12190
|
+
javascript: ["method_definition"],
|
|
12191
|
+
python: ["function_definition"]
|
|
12192
|
+
};
|
|
12193
|
+
var IDENTIFIER_TYPES = /* @__PURE__ */ new Set(["identifier", "property_identifier", "type_identifier"]);
|
|
12194
|
+
function findIdentifier(node) {
|
|
12195
|
+
return node.childForFieldName("name") ?? node.children.find((c) => IDENTIFIER_TYPES.has(c.type)) ?? null;
|
|
12196
|
+
}
|
|
12197
|
+
function getVariableDeclarationName(node) {
|
|
12198
|
+
const declarator = node.children.find((c) => c.type === "variable_declarator");
|
|
12199
|
+
if (!declarator) return null;
|
|
12200
|
+
const id = findIdentifier(declarator);
|
|
12201
|
+
return id?.text ?? null;
|
|
12202
|
+
}
|
|
12203
|
+
function getExportName(node, source) {
|
|
12204
|
+
const decl = node.children.find(
|
|
12205
|
+
(c) => c.type !== "export" && c.type !== "default" && c.type !== "comment"
|
|
12206
|
+
);
|
|
12207
|
+
return decl ? getNodeName(decl, source) : "<anonymous>";
|
|
12208
|
+
}
|
|
12209
|
+
function getAssignmentName(node) {
|
|
12210
|
+
const left = node.childForFieldName("left") ?? node.children[0];
|
|
12211
|
+
return left?.text ?? "<anonymous>";
|
|
12212
|
+
}
|
|
12213
|
+
function getNodeName(node, source) {
|
|
12214
|
+
const id = findIdentifier(node);
|
|
12215
|
+
if (id) return id.text;
|
|
12216
|
+
const isVarDecl = node.type === "lexical_declaration" || node.type === "variable_declaration";
|
|
12217
|
+
if (isVarDecl) return getVariableDeclarationName(node) ?? "<anonymous>";
|
|
12218
|
+
if (node.type === "export_statement") return getExportName(node, source);
|
|
12219
|
+
if (node.type === "assignment") return getAssignmentName(node);
|
|
12220
|
+
return "<anonymous>";
|
|
12221
|
+
}
|
|
12222
|
+
function getSignature(node, source) {
|
|
12223
|
+
const startLine = node.startPosition.row;
|
|
12224
|
+
const lines = source.split("\n");
|
|
12225
|
+
return (lines[startLine] ?? "").trim();
|
|
12226
|
+
}
|
|
12227
|
+
function extractMethods(classNode, language, source, filePath) {
|
|
12228
|
+
const methodTypes = METHOD_TYPES[language] ?? [];
|
|
12229
|
+
const body = classNode.childForFieldName("body") ?? classNode.children.find((c) => c.type === "class_body" || c.type === "block");
|
|
12230
|
+
if (!body) return [];
|
|
12231
|
+
return body.children.filter((child) => methodTypes.includes(child.type)).map((child) => ({
|
|
12232
|
+
name: getNodeName(child, source),
|
|
12233
|
+
kind: "method",
|
|
12234
|
+
file: filePath,
|
|
12235
|
+
line: child.startPosition.row + 1,
|
|
12236
|
+
endLine: child.endPosition.row + 1,
|
|
12237
|
+
signature: getSignature(child, source)
|
|
12238
|
+
}));
|
|
12239
|
+
}
|
|
12240
|
+
function nodeToSymbol(node, kind, source, filePath) {
|
|
12241
|
+
return {
|
|
12242
|
+
name: getNodeName(node, source),
|
|
12243
|
+
kind,
|
|
12244
|
+
file: filePath,
|
|
12245
|
+
line: node.startPosition.row + 1,
|
|
12246
|
+
endLine: node.endPosition.row + 1,
|
|
12247
|
+
signature: getSignature(node, source)
|
|
12248
|
+
};
|
|
12249
|
+
}
|
|
12250
|
+
function processExportStatement(child, topLevelTypes, lang, source, filePath) {
|
|
12251
|
+
const declaration = child.children.find(
|
|
12252
|
+
(c) => c.type !== "export" && c.type !== "default" && c.type !== ";" && c.type !== "comment"
|
|
12253
|
+
);
|
|
12254
|
+
const kind = declaration ? topLevelTypes[declaration.type] : void 0;
|
|
12255
|
+
if (declaration && kind) {
|
|
12256
|
+
const sym = nodeToSymbol(child, kind, source, filePath);
|
|
12257
|
+
sym.name = getNodeName(declaration, source);
|
|
12258
|
+
if (kind === "class") {
|
|
12259
|
+
sym.children = extractMethods(declaration, lang, source, filePath);
|
|
12260
|
+
}
|
|
12261
|
+
return sym;
|
|
12262
|
+
}
|
|
12263
|
+
return nodeToSymbol(child, "export", source, filePath);
|
|
12264
|
+
}
|
|
12265
|
+
function extractSymbols(rootNode, lang, source, filePath) {
|
|
12266
|
+
const symbols = [];
|
|
12267
|
+
const topLevelTypes = TOP_LEVEL_TYPES[lang] ?? {};
|
|
12268
|
+
for (const child of rootNode.children) {
|
|
12269
|
+
if (child.type === "export_statement") {
|
|
12270
|
+
symbols.push(processExportStatement(child, topLevelTypes, lang, source, filePath));
|
|
12271
|
+
continue;
|
|
12272
|
+
}
|
|
12273
|
+
const kind = topLevelTypes[child.type];
|
|
12274
|
+
if (!kind || kind === "import") continue;
|
|
12275
|
+
const sym = nodeToSymbol(child, kind, source, filePath);
|
|
12276
|
+
if (kind === "class") {
|
|
12277
|
+
sym.children = extractMethods(child, lang, source, filePath);
|
|
12278
|
+
}
|
|
12279
|
+
symbols.push(sym);
|
|
12280
|
+
}
|
|
12281
|
+
return symbols;
|
|
12282
|
+
}
|
|
12283
|
+
function buildFailedResult(filePath, lang) {
|
|
12284
|
+
return { file: filePath, language: lang, totalLines: 0, symbols: [], error: "[parse-failed]" };
|
|
12285
|
+
}
|
|
12286
|
+
async function getOutline(filePath) {
|
|
12287
|
+
const lang = detectLanguage(filePath);
|
|
12288
|
+
if (!lang) return buildFailedResult(filePath, "unknown");
|
|
12289
|
+
const result = await parseFile(filePath);
|
|
12290
|
+
if (!result.ok) return buildFailedResult(filePath, lang);
|
|
12291
|
+
const { tree, source } = result.value;
|
|
12292
|
+
const totalLines = source.split("\n").length;
|
|
12293
|
+
const symbols = extractSymbols(tree.rootNode, lang, source, filePath);
|
|
12294
|
+
return { file: filePath, language: lang, totalLines, symbols };
|
|
12295
|
+
}
|
|
12296
|
+
function formatOutline(outline) {
|
|
12297
|
+
if (outline.error) {
|
|
12298
|
+
return `${outline.file} ${outline.error}`;
|
|
12299
|
+
}
|
|
12300
|
+
const lines = [`${outline.file} (${outline.totalLines} lines)`];
|
|
12301
|
+
const last = outline.symbols.length - 1;
|
|
12302
|
+
outline.symbols.forEach((sym, i) => {
|
|
12303
|
+
const prefix = i === last ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
12304
|
+
lines.push(`${prefix} ${sym.signature} :${sym.line}`);
|
|
12305
|
+
if (sym.children) {
|
|
12306
|
+
const childLast = sym.children.length - 1;
|
|
12307
|
+
sym.children.forEach((child, j) => {
|
|
12308
|
+
const childConnector = i === last ? " " : "\u2502 ";
|
|
12309
|
+
const childPrefix = j === childLast ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500";
|
|
12310
|
+
lines.push(`${childConnector}${childPrefix} ${child.signature} :${child.line}`);
|
|
12311
|
+
});
|
|
12312
|
+
}
|
|
12313
|
+
});
|
|
12314
|
+
return lines.join("\n");
|
|
12315
|
+
}
|
|
12316
|
+
|
|
12317
|
+
// src/code-nav/search.ts
|
|
12318
|
+
function buildGlob(directory, fileGlob) {
|
|
12319
|
+
const dir = directory.replaceAll("\\", "/");
|
|
12320
|
+
if (fileGlob) {
|
|
12321
|
+
return `${dir}/**/${fileGlob}`;
|
|
12322
|
+
}
|
|
12323
|
+
const exts = Object.keys(EXTENSION_MAP).map((e) => e.slice(1));
|
|
12324
|
+
return `${dir}/**/*.{${exts.join(",")}}`;
|
|
12325
|
+
}
|
|
12326
|
+
function matchesQuery(name, query) {
|
|
12327
|
+
return name.toLowerCase().includes(query.toLowerCase());
|
|
12328
|
+
}
|
|
12329
|
+
function flattenSymbols(symbols) {
|
|
12330
|
+
const flat = [];
|
|
12331
|
+
for (const sym of symbols) {
|
|
12332
|
+
flat.push(sym);
|
|
12333
|
+
if (sym.children) {
|
|
12334
|
+
flat.push(...sym.children);
|
|
12335
|
+
}
|
|
12336
|
+
}
|
|
12337
|
+
return flat;
|
|
12338
|
+
}
|
|
12339
|
+
async function searchSymbols(query, directory, fileGlob) {
|
|
12340
|
+
const pattern = buildGlob(directory, fileGlob);
|
|
12341
|
+
let files;
|
|
12342
|
+
try {
|
|
12343
|
+
files = await findFiles(pattern, directory);
|
|
12344
|
+
} catch {
|
|
12345
|
+
files = [];
|
|
12346
|
+
}
|
|
12347
|
+
const matches = [];
|
|
12348
|
+
const skipped = [];
|
|
12349
|
+
for (const file of files) {
|
|
12350
|
+
const lang = detectLanguage(file);
|
|
12351
|
+
if (!lang) {
|
|
12352
|
+
skipped.push(file);
|
|
12353
|
+
continue;
|
|
12354
|
+
}
|
|
12355
|
+
const outline = await getOutline(file);
|
|
12356
|
+
if (outline.error) {
|
|
12357
|
+
skipped.push(file);
|
|
12358
|
+
continue;
|
|
12359
|
+
}
|
|
12360
|
+
const allSymbols = flattenSymbols(outline.symbols);
|
|
12361
|
+
for (const sym of allSymbols) {
|
|
12362
|
+
if (matchesQuery(sym.name, query)) {
|
|
12363
|
+
matches.push({
|
|
12364
|
+
symbol: sym,
|
|
12365
|
+
context: sym.signature
|
|
12366
|
+
});
|
|
12367
|
+
}
|
|
12368
|
+
}
|
|
12369
|
+
}
|
|
12370
|
+
return { query, matches, skipped };
|
|
12371
|
+
}
|
|
12372
|
+
|
|
12373
|
+
// src/code-nav/unfold.ts
|
|
12374
|
+
function findSymbolInList(symbols, name) {
|
|
12375
|
+
for (const sym of symbols) {
|
|
12376
|
+
if (sym.name === name) return sym;
|
|
12377
|
+
if (sym.children) {
|
|
12378
|
+
const found = findSymbolInList(sym.children, name);
|
|
12379
|
+
if (found) return found;
|
|
12380
|
+
}
|
|
12381
|
+
}
|
|
12382
|
+
return null;
|
|
12383
|
+
}
|
|
12384
|
+
function extractLines(source, startLine, endLine) {
|
|
12385
|
+
const lines = source.split("\n");
|
|
12386
|
+
const start = Math.max(0, startLine - 1);
|
|
12387
|
+
const end = Math.min(lines.length, endLine);
|
|
12388
|
+
return lines.slice(start, end).join("\n");
|
|
12389
|
+
}
|
|
12390
|
+
function buildFallbackResult(filePath, symbolName, content, language) {
|
|
12391
|
+
const totalLines = content ? content.split("\n").length : 0;
|
|
12392
|
+
return {
|
|
12393
|
+
file: filePath,
|
|
12394
|
+
symbolName,
|
|
12395
|
+
startLine: content ? 1 : 0,
|
|
12396
|
+
endLine: totalLines,
|
|
12397
|
+
content,
|
|
12398
|
+
language,
|
|
12399
|
+
fallback: true,
|
|
12400
|
+
warning: "[fallback: raw content]"
|
|
12401
|
+
};
|
|
12402
|
+
}
|
|
12403
|
+
async function readContentSafe(filePath) {
|
|
12404
|
+
const result = await readFileContent(filePath);
|
|
12405
|
+
return result.ok ? result.value : "";
|
|
12406
|
+
}
|
|
12407
|
+
async function unfoldSymbol(filePath, symbolName) {
|
|
12408
|
+
const lang = detectLanguage(filePath);
|
|
12409
|
+
if (!lang) {
|
|
12410
|
+
const content2 = await readContentSafe(filePath);
|
|
12411
|
+
return buildFallbackResult(filePath, symbolName, content2, "unknown");
|
|
12412
|
+
}
|
|
12413
|
+
const outline = await getOutline(filePath);
|
|
12414
|
+
if (outline.error) {
|
|
12415
|
+
const content2 = await readContentSafe(filePath);
|
|
12416
|
+
return buildFallbackResult(filePath, symbolName, content2, lang);
|
|
12417
|
+
}
|
|
12418
|
+
const symbol = findSymbolInList(outline.symbols, symbolName);
|
|
12419
|
+
if (!symbol) {
|
|
12420
|
+
const content2 = await readContentSafe(filePath);
|
|
12421
|
+
return buildFallbackResult(filePath, symbolName, content2, lang);
|
|
12422
|
+
}
|
|
12423
|
+
const parseResult = await parseFile(filePath);
|
|
12424
|
+
if (!parseResult.ok) {
|
|
12425
|
+
const content2 = await readContentSafe(filePath);
|
|
12426
|
+
return {
|
|
12427
|
+
...buildFallbackResult(
|
|
12428
|
+
filePath,
|
|
12429
|
+
symbolName,
|
|
12430
|
+
extractLines(content2, symbol.line, symbol.endLine),
|
|
12431
|
+
lang
|
|
12432
|
+
),
|
|
12433
|
+
startLine: symbol.line,
|
|
12434
|
+
endLine: symbol.endLine
|
|
12435
|
+
};
|
|
12436
|
+
}
|
|
12437
|
+
const content = extractLines(parseResult.value.source, symbol.line, symbol.endLine);
|
|
12438
|
+
return {
|
|
12439
|
+
file: filePath,
|
|
12440
|
+
symbolName,
|
|
12441
|
+
startLine: symbol.line,
|
|
12442
|
+
endLine: symbol.endLine,
|
|
12443
|
+
content,
|
|
12444
|
+
language: lang,
|
|
12445
|
+
fallback: false
|
|
12446
|
+
};
|
|
12447
|
+
}
|
|
12448
|
+
async function unfoldRange(filePath, startLine, endLine) {
|
|
12449
|
+
const lang = detectLanguage(filePath) ?? "unknown";
|
|
12450
|
+
const contentResult = await readFileContent(filePath);
|
|
12451
|
+
if (!contentResult.ok) {
|
|
12452
|
+
return {
|
|
12453
|
+
file: filePath,
|
|
12454
|
+
startLine: 0,
|
|
12455
|
+
endLine: 0,
|
|
12456
|
+
content: "",
|
|
12457
|
+
language: lang,
|
|
12458
|
+
fallback: true,
|
|
12459
|
+
warning: "[fallback: raw content]"
|
|
12460
|
+
};
|
|
12461
|
+
}
|
|
12462
|
+
const totalLines = contentResult.value.split("\n").length;
|
|
12463
|
+
const clampedEnd = Math.min(endLine, totalLines);
|
|
12464
|
+
const content = extractLines(contentResult.value, startLine, clampedEnd);
|
|
12465
|
+
return {
|
|
12466
|
+
file: filePath,
|
|
12467
|
+
startLine,
|
|
12468
|
+
endLine: clampedEnd,
|
|
12469
|
+
content,
|
|
12470
|
+
language: lang,
|
|
12471
|
+
fallback: false
|
|
12472
|
+
};
|
|
12473
|
+
}
|
|
12474
|
+
|
|
11353
12475
|
// src/index.ts
|
|
11354
|
-
var VERSION = "0.
|
|
12476
|
+
var VERSION = "0.15.0";
|
|
11355
12477
|
// Annotate the CommonJS export names for ESM import in node:
|
|
11356
12478
|
0 && (module.exports = {
|
|
11357
12479
|
AGENT_DESCRIPTORS,
|
|
@@ -11386,6 +12508,7 @@ var VERSION = "0.14.0";
|
|
|
11386
12508
|
DEFAULT_STATE,
|
|
11387
12509
|
DEFAULT_STREAM_INDEX,
|
|
11388
12510
|
DepDepthCollector,
|
|
12511
|
+
EXTENSION_MAP,
|
|
11389
12512
|
EmitInteractionInputSchema,
|
|
11390
12513
|
EntropyAnalyzer,
|
|
11391
12514
|
EntropyConfigSchema,
|
|
@@ -11420,6 +12543,7 @@ var VERSION = "0.14.0";
|
|
|
11420
12543
|
SharableForbiddenImportSchema,
|
|
11421
12544
|
SharableLayerSchema,
|
|
11422
12545
|
SharableSecurityRulesSchema,
|
|
12546
|
+
SkillEventSchema,
|
|
11423
12547
|
StreamIndexSchema,
|
|
11424
12548
|
StreamInfoSchema,
|
|
11425
12549
|
ThresholdConfigSchema,
|
|
@@ -11428,6 +12552,7 @@ var VERSION = "0.14.0";
|
|
|
11428
12552
|
VERSION,
|
|
11429
12553
|
ViolationSchema,
|
|
11430
12554
|
addProvenance,
|
|
12555
|
+
agentConfigRules,
|
|
11431
12556
|
analyzeDiff,
|
|
11432
12557
|
analyzeLearningPatterns,
|
|
11433
12558
|
appendFailure,
|
|
@@ -11435,6 +12560,7 @@ var VERSION = "0.14.0";
|
|
|
11435
12560
|
appendSessionEntry,
|
|
11436
12561
|
applyFixes,
|
|
11437
12562
|
applyHotspotDowngrade,
|
|
12563
|
+
applySyncChanges,
|
|
11438
12564
|
archMatchers,
|
|
11439
12565
|
archModule,
|
|
11440
12566
|
architecture,
|
|
@@ -11449,12 +12575,14 @@ var VERSION = "0.14.0";
|
|
|
11449
12575
|
checkEligibility,
|
|
11450
12576
|
checkEvidenceCoverage,
|
|
11451
12577
|
classifyFinding,
|
|
12578
|
+
clearEventHashCache,
|
|
11452
12579
|
clearFailuresCache,
|
|
11453
12580
|
clearLearningsCache,
|
|
11454
12581
|
configureFeedback,
|
|
11455
12582
|
constraintRuleId,
|
|
11456
12583
|
contextBudget,
|
|
11457
12584
|
contextFilter,
|
|
12585
|
+
countLearningEntries,
|
|
11458
12586
|
createBoundaryValidator,
|
|
11459
12587
|
createCommentedCodeFixes,
|
|
11460
12588
|
createError,
|
|
@@ -11478,27 +12606,34 @@ var VERSION = "0.14.0";
|
|
|
11478
12606
|
detectCouplingViolations,
|
|
11479
12607
|
detectDeadCode,
|
|
11480
12608
|
detectDocDrift,
|
|
12609
|
+
detectLanguage,
|
|
11481
12610
|
detectPatternViolations,
|
|
11482
12611
|
detectSizeBudgetViolations,
|
|
11483
12612
|
detectStack,
|
|
11484
12613
|
detectStaleConstraints,
|
|
11485
12614
|
determineAssessment,
|
|
11486
12615
|
diff,
|
|
12616
|
+
emitEvent,
|
|
11487
12617
|
executeWorkflow,
|
|
11488
12618
|
expressRules,
|
|
11489
12619
|
extractBundle,
|
|
12620
|
+
extractIndexEntry,
|
|
11490
12621
|
extractMarkdownLinks,
|
|
11491
12622
|
extractSections,
|
|
11492
12623
|
fanOutReview,
|
|
12624
|
+
formatEventTimeline,
|
|
11493
12625
|
formatFindingBlock,
|
|
11494
12626
|
formatGitHubComment,
|
|
11495
12627
|
formatGitHubSummary,
|
|
12628
|
+
formatOutline,
|
|
11496
12629
|
formatTerminalOutput,
|
|
11497
12630
|
generateAgentsMap,
|
|
11498
12631
|
generateSuggestions,
|
|
11499
12632
|
getActionEmitter,
|
|
11500
12633
|
getExitCode,
|
|
11501
12634
|
getFeedbackConfig,
|
|
12635
|
+
getOutline,
|
|
12636
|
+
getParser,
|
|
11502
12637
|
getPhaseCategories,
|
|
11503
12638
|
getStreamForBranch,
|
|
11504
12639
|
getUpdateNotification,
|
|
@@ -11509,24 +12644,30 @@ var VERSION = "0.14.0";
|
|
|
11509
12644
|
listActiveSessions,
|
|
11510
12645
|
listStreams,
|
|
11511
12646
|
loadBudgetedLearnings,
|
|
12647
|
+
loadEvents,
|
|
11512
12648
|
loadFailures,
|
|
11513
12649
|
loadHandoff,
|
|
12650
|
+
loadIndexEntries,
|
|
11514
12651
|
loadRelevantLearnings,
|
|
11515
12652
|
loadSessionSummary,
|
|
11516
12653
|
loadState,
|
|
11517
12654
|
loadStreamIndex,
|
|
11518
12655
|
logAgentAction,
|
|
12656
|
+
mcpRules,
|
|
11519
12657
|
migrateToStreams,
|
|
11520
12658
|
networkRules,
|
|
11521
12659
|
nodeRules,
|
|
11522
12660
|
parseDateFromEntry,
|
|
11523
12661
|
parseDiff,
|
|
12662
|
+
parseFile,
|
|
12663
|
+
parseFrontmatter,
|
|
11524
12664
|
parseManifest,
|
|
11525
12665
|
parseRoadmap,
|
|
11526
12666
|
parseSecurityConfig,
|
|
11527
12667
|
parseSize,
|
|
11528
12668
|
pathTraversalRules,
|
|
11529
12669
|
previewFix,
|
|
12670
|
+
promoteSessionLearnings,
|
|
11530
12671
|
pruneLearnings,
|
|
11531
12672
|
reactRules,
|
|
11532
12673
|
readCheckState,
|
|
@@ -11538,6 +12679,7 @@ var VERSION = "0.14.0";
|
|
|
11538
12679
|
requestMultiplePeerReviews,
|
|
11539
12680
|
requestPeerReview,
|
|
11540
12681
|
resetFeedbackConfig,
|
|
12682
|
+
resetParserCache,
|
|
11541
12683
|
resolveFileToLayer,
|
|
11542
12684
|
resolveModelTier,
|
|
11543
12685
|
resolveRuleSeverity,
|
|
@@ -11559,6 +12701,7 @@ var VERSION = "0.14.0";
|
|
|
11559
12701
|
saveState,
|
|
11560
12702
|
saveStreamIndex,
|
|
11561
12703
|
scopeContext,
|
|
12704
|
+
searchSymbols,
|
|
11562
12705
|
secretRules,
|
|
11563
12706
|
serializeRoadmap,
|
|
11564
12707
|
setActiveStream,
|
|
@@ -11569,6 +12712,8 @@ var VERSION = "0.14.0";
|
|
|
11569
12712
|
tagUncitedFindings,
|
|
11570
12713
|
touchStream,
|
|
11571
12714
|
trackAction,
|
|
12715
|
+
unfoldRange,
|
|
12716
|
+
unfoldSymbol,
|
|
11572
12717
|
updateSessionEntryStatus,
|
|
11573
12718
|
updateSessionIndex,
|
|
11574
12719
|
validateAgentsMap,
|