@moda-ai/cli 1.22.1 → 1.22.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/cli-g8qs2tj9.js +9777 -0
  2. package/dist/cli-pwbvtes8.js +2312 -0
  3. package/dist/cli-qszcpda3.js +284 -0
  4. package/dist/cli.js +4775 -1159
  5. package/dist/index-6h0nc1r5.js +2765 -0
  6. package/dist/provision-j5gq8cvx.js +72 -0
  7. package/package.json +3 -2
  8. package/skills/integration/index.json +2 -2
  9. package/dist/agent-schemas.d.ts +0 -3
  10. package/dist/agent-schemas.js +0 -358
  11. package/dist/agent.d.ts +0 -5
  12. package/dist/agent.js +0 -477
  13. package/dist/analyst-tui.d.ts +0 -81
  14. package/dist/analyst-tui.js +0 -314
  15. package/dist/api-client.d.ts +0 -46
  16. package/dist/api-client.js +0 -120
  17. package/dist/ask-stream.d.ts +0 -146
  18. package/dist/ask-stream.js +0 -473
  19. package/dist/auth-storage.d.ts +0 -35
  20. package/dist/auth-storage.js +0 -159
  21. package/dist/auth.d.ts +0 -50
  22. package/dist/auth.js +0 -694
  23. package/dist/cli-session.d.ts +0 -26
  24. package/dist/cli-session.js +0 -169
  25. package/dist/cli.d.ts +0 -44
  26. package/dist/command-context.d.ts +0 -62
  27. package/dist/command-context.js +0 -57
  28. package/dist/command-registry.d.ts +0 -22
  29. package/dist/command-registry.js +0 -15
  30. package/dist/config.d.ts +0 -60
  31. package/dist/config.js +0 -110
  32. package/dist/doctor.d.ts +0 -79
  33. package/dist/doctor.js +0 -1182
  34. package/dist/errors.d.ts +0 -50
  35. package/dist/errors.js +0 -64
  36. package/dist/exit-funnel.d.ts +0 -14
  37. package/dist/exit-funnel.js +0 -53
  38. package/dist/harness-artifact-bodies.d.ts +0 -28
  39. package/dist/harness-artifact-bodies.js +0 -276
  40. package/dist/harness-candidates.d.ts +0 -77
  41. package/dist/harness-candidates.js +0 -482
  42. package/dist/harness-remote.d.ts +0 -135
  43. package/dist/harness-remote.js +0 -540
  44. package/dist/harness-report.d.ts +0 -145
  45. package/dist/harness-report.js +0 -1579
  46. package/dist/harness-sync-limits.d.ts +0 -8
  47. package/dist/harness-sync-limits.js +0 -21
  48. package/dist/harness.d.ts +0 -463
  49. package/dist/harness.js +0 -4009
  50. package/dist/init/index.d.ts +0 -78
  51. package/dist/init/index.js +0 -1442
  52. package/dist/init/mcp.d.ts +0 -2
  53. package/dist/init/mcp.js +0 -27
  54. package/dist/init/progress-board.d.ts +0 -80
  55. package/dist/init/progress-board.js +0 -225
  56. package/dist/init/rules.d.ts +0 -6
  57. package/dist/init/rules.js +0 -171
  58. package/dist/init/sdk-catalog.d.ts +0 -10
  59. package/dist/init/sdk-catalog.js +0 -80
  60. package/dist/init/sdk-guide.d.ts +0 -19
  61. package/dist/init/sdk-guide.js +0 -185
  62. package/dist/init/sdk-integration.d.ts +0 -53
  63. package/dist/init/sdk-integration.js +0 -835
  64. package/dist/init/sdk-protocol.d.ts +0 -135
  65. package/dist/init/sdk-protocol.js +0 -152
  66. package/dist/init/sdk-verification.d.ts +0 -27
  67. package/dist/init/sdk-verification.js +0 -123
  68. package/dist/init/tenant.d.ts +0 -67
  69. package/dist/init/tenant.js +0 -421
  70. package/dist/intelligence-schema.d.ts +0 -349
  71. package/dist/intelligence-schema.js +0 -174
  72. package/dist/keys.d.ts +0 -47
  73. package/dist/keys.js +0 -136
  74. package/dist/manifest.d.ts +0 -5
  75. package/dist/manifest.js +0 -114
  76. package/dist/markdown-terminal.d.ts +0 -49
  77. package/dist/markdown-terminal.js +0 -212
  78. package/dist/output.d.ts +0 -191
  79. package/dist/output.js +0 -624
  80. package/dist/production-intelligence.d.ts +0 -175
  81. package/dist/production-intelligence.js +0 -1430
  82. package/dist/profile-commands.d.ts +0 -10
  83. package/dist/profile-commands.js +0 -342
  84. package/dist/profiles.d.ts +0 -112
  85. package/dist/profiles.js +0 -573
  86. package/dist/prompts-ab.d.ts +0 -71
  87. package/dist/prompts-ab.js +0 -313
  88. package/dist/prompts-propose.d.ts +0 -15
  89. package/dist/prompts-propose.js +0 -212
  90. package/dist/prompts.d.ts +0 -58
  91. package/dist/prompts.js +0 -418
  92. package/dist/provision.d.ts +0 -28
  93. package/dist/provision.js +0 -104
  94. package/dist/redact.d.ts +0 -2
  95. package/dist/redact.js +0 -90
  96. package/dist/schemas.d.ts +0 -225
  97. package/dist/schemas.js +0 -126
  98. package/dist/skills-bundled.d.ts +0 -35
  99. package/dist/skills-bundled.js +0 -157
  100. package/dist/skills-manifest.d.ts +0 -33
  101. package/dist/skills-manifest.js +0 -244
  102. package/dist/skills.d.ts +0 -57
  103. package/dist/skills.js +0 -783
  104. package/dist/sync.d.ts +0 -5
  105. package/dist/sync.js +0 -36
  106. package/dist/telemetry.d.ts +0 -56
  107. package/dist/telemetry.js +0 -167
  108. package/dist/transforms.d.ts +0 -25
  109. package/dist/transforms.js +0 -102
  110. package/dist/tui.d.ts +0 -48
  111. package/dist/tui.js +0 -393
  112. package/dist/update-check.d.ts +0 -31
  113. package/dist/update-check.js +0 -141
  114. package/dist/workspace.d.ts +0 -12
  115. package/dist/workspace.js +0 -1294
package/dist/errors.d.ts DELETED
@@ -1,50 +0,0 @@
1
- export declare class CliInputError extends Error {
2
- readonly usage?: string | undefined;
3
- constructor(message: string, usage?: string | undefined);
4
- }
5
- /**
6
- * The authenticated user cannot perform the requested action with their
7
- * current access (no organizations, or insufficient role). This is an access
8
- * problem, not missing auth — the dispatcher maps it to EXIT_ERROR (1),
9
- * never EXIT_AUTH_REQUIRED.
10
- */
11
- export declare class CliAccessError extends Error {
12
- /** Optional follow-up line telling the user how to obtain access. */
13
- readonly guidance?: string;
14
- constructor(message: string, options?: {
15
- guidance?: string;
16
- });
17
- }
18
- export interface AgentInputChoice {
19
- value: string;
20
- label: string;
21
- resume_flags: string[];
22
- }
23
- export interface AgentInputRequest {
24
- id: string;
25
- question: string;
26
- kind: 'select' | 'confirm' | 'text';
27
- choices?: AgentInputChoice[];
28
- resume_command?: string[];
29
- }
30
- export interface AgentAuthRequest {
31
- reason: 'no_stored_credentials' | 'token_expired' | 'token_revoked' | 'renew_failed';
32
- login_command: string[];
33
- resume_command?: string[];
34
- }
35
- export declare class CliInputRequiredError extends Error {
36
- readonly input_request: AgentInputRequest;
37
- constructor(message: string, input_request: AgentInputRequest, usage?: string);
38
- readonly usage?: string;
39
- }
40
- export declare class CliAuthError extends Error {
41
- readonly auth: AgentAuthRequest;
42
- constructor(message: string, auth: AgentAuthRequest, usage?: string);
43
- readonly usage?: string;
44
- }
45
- export declare class CliCancellationError extends Error {
46
- constructor(message: string);
47
- }
48
- export declare function isNetworkLikeError(error: unknown): boolean;
49
- export declare function isApiLikeError(error: unknown): boolean;
50
- //# sourceMappingURL=errors.d.ts.map
package/dist/errors.js DELETED
@@ -1,64 +0,0 @@
1
- export class CliInputError extends Error {
2
- usage;
3
- constructor(message, usage) {
4
- super(message);
5
- this.usage = usage;
6
- this.name = 'CliInputError';
7
- }
8
- }
9
- /**
10
- * The authenticated user cannot perform the requested action with their
11
- * current access (no organizations, or insufficient role). This is an access
12
- * problem, not missing auth — the dispatcher maps it to EXIT_ERROR (1),
13
- * never EXIT_AUTH_REQUIRED.
14
- */
15
- export class CliAccessError extends Error {
16
- /** Optional follow-up line telling the user how to obtain access. */
17
- guidance;
18
- constructor(message, options = {}) {
19
- super(message);
20
- this.name = 'CliAccessError';
21
- this.guidance = options.guidance;
22
- }
23
- }
24
- export class CliInputRequiredError extends Error {
25
- input_request;
26
- constructor(message, input_request, usage) {
27
- super(message);
28
- this.input_request = input_request;
29
- this.name = 'CliInputRequiredError';
30
- if (usage)
31
- this.usage = usage;
32
- }
33
- usage;
34
- }
35
- export class CliAuthError extends Error {
36
- auth;
37
- constructor(message, auth, usage) {
38
- super(message);
39
- this.auth = auth;
40
- this.name = 'CliAuthError';
41
- if (usage)
42
- this.usage = usage;
43
- }
44
- usage;
45
- }
46
- export class CliCancellationError extends Error {
47
- constructor(message) {
48
- super(message);
49
- this.name = 'CliCancellationError';
50
- }
51
- }
52
- export function isNetworkLikeError(error) {
53
- return error instanceof Error &&
54
- (error.message.startsWith('Network error:') ||
55
- error.message.startsWith('Request timed out') ||
56
- error.name === 'AbortError' ||
57
- /(?:ECONNRESET|ECONNREFUSED|ETIMEDOUT|ENOTFOUND|socket hang up)/i.test(error.message));
58
- }
59
- export function isApiLikeError(error) {
60
- return error instanceof Error &&
61
- (error.name === 'ApiError' ||
62
- ('statusCode' in error && typeof error.statusCode === 'number'));
63
- }
64
- //# sourceMappingURL=errors.js.map
@@ -1,14 +0,0 @@
1
- import type { CommandContext } from './command-context.js';
2
- export type RunOutcome = {
3
- kind: 'completed';
4
- exitCode?: number;
5
- } | {
6
- kind: 'error';
7
- error: unknown;
8
- } | {
9
- kind: 'cancelled';
10
- reason?: string;
11
- };
12
- export declare function finishRun(context: CommandContext, outcome: RunOutcome): Promise<number>;
13
- export declare function exitCodeForError(error: unknown): number;
14
- //# sourceMappingURL=exit-funnel.d.ts.map
@@ -1,53 +0,0 @@
1
- import { CliAccessError, CliAuthError, CliCancellationError, CliInputRequiredError, } from './errors.js';
2
- import { EXIT_AUTH_REQUIRED, EXIT_CANCELLED, EXIT_ERROR, EXIT_INPUT_REQUIRED, EXIT_OK, wasOutputCancelled, } from './output.js';
3
- const finishedContexts = new WeakSet();
4
- export async function finishRun(context, outcome) {
5
- if (finishedContexts.has(context)) {
6
- return exitCodeForOutcome(outcome);
7
- }
8
- finishedContexts.add(context);
9
- if (outcome.kind === 'completed') {
10
- // Guarantee the stream contract structurally: exactly one terminal event
11
- // ends every stream. A handler that returned without writing one gets a
12
- // synthetic `completed` (a no-op in non-stream modes and when a terminal
13
- // event was already emitted).
14
- if (!context.output.hasTerminalOutput()) {
15
- context.output.writeEvent({ event: 'completed', status: 'ok' });
16
- }
17
- context.output.close();
18
- if (wasOutputCancelled())
19
- return EXIT_CANCELLED;
20
- return outcome.exitCode ?? EXIT_OK;
21
- }
22
- if (outcome.kind === 'cancelled') {
23
- context.output.writeCancelled(outcome.reason ?? 'Command cancelled.');
24
- context.output.close();
25
- return EXIT_CANCELLED;
26
- }
27
- context.output.writeError(outcome.error);
28
- context.output.close();
29
- if (wasOutputCancelled())
30
- return EXIT_CANCELLED;
31
- return exitCodeForError(outcome.error);
32
- }
33
- export function exitCodeForError(error) {
34
- if (error instanceof CliAuthError)
35
- return EXIT_AUTH_REQUIRED;
36
- if (error instanceof CliInputRequiredError)
37
- return EXIT_INPUT_REQUIRED;
38
- if (error instanceof CliCancellationError)
39
- return EXIT_CANCELLED;
40
- // An access problem (missing org / insufficient role) is a hard failure,
41
- // never auth-required — it stays EXIT_ERROR (the default), made explicit here.
42
- if (error instanceof CliAccessError)
43
- return EXIT_ERROR;
44
- return EXIT_ERROR;
45
- }
46
- function exitCodeForOutcome(outcome) {
47
- if (outcome.kind === 'completed')
48
- return outcome.exitCode ?? EXIT_OK;
49
- if (outcome.kind === 'cancelled')
50
- return EXIT_CANCELLED;
51
- return exitCodeForError(outcome.error);
52
- }
53
- //# sourceMappingURL=exit-funnel.js.map
@@ -1,28 +0,0 @@
1
- import type { HarnessArtifactType, HarnessEvidence, HarnessGraph } from './harness.js';
2
- export interface ArtifactBodyAnnotation {
3
- values: Record<string, string[]>;
4
- evidence: HarnessEvidence;
5
- sourceAbsPath: string;
6
- rootDir: string;
7
- }
8
- export interface ArtifactBodyFileItem {
9
- path: string;
10
- rootDir: string;
11
- text?: string;
12
- }
13
- export declare function extractToolBodyFromText(text: string, toolName: string): string | undefined;
14
- export declare function annotatedSourceCandidates(sourcePath: string, annotation: ArtifactBodyAnnotation): string[];
15
- /**
16
- * Resolve an annotated artifact `path=` to a repo-relative sourcePath.
17
- * Prefer the nested annotation-relative file when present so later whole-file
18
- * hydration and content hashing look in the right place.
19
- */
20
- export declare function resolveAnnotatedSourcePath(sourcePath: string, annotation: ArtifactBodyAnnotation): string;
21
- export declare function readPromptOrToolBody(type: HarnessArtifactType, sourcePath: string, annotation: ArtifactBodyAnnotation): string | undefined;
22
- export declare function extractToolBody(file: ArtifactBodyFileItem, tool: {
23
- name: string;
24
- reason: string;
25
- }): string | undefined;
26
- export declare function hydratePromptAndToolBodies(graph: HarnessGraph, rootDir: string): void;
27
- export declare function enrichScanCaptureMetadata(graph: HarnessGraph): void;
28
- //# sourceMappingURL=harness-artifact-bodies.d.ts.map
@@ -1,276 +0,0 @@
1
- import { existsSync, lstatSync, readFileSync, realpathSync } from 'node:fs';
2
- import { basename, dirname, isAbsolute, relative, resolve } from 'node:path';
3
- import { artifactHasBody, firstEvidencePath, requiresArtifactBody, } from './harness-report.js';
4
- const MAX_SCANNED_FILE_BYTES = 512 * 1024;
5
- const SNAPSHOT_BUDGET_SKIP_REASONS = new Set([
6
- 'file too large for harness scan',
7
- 'scan text budget exceeded',
8
- ]);
9
- function first(values) {
10
- return values?.[0];
11
- }
12
- function normalizePath(path) {
13
- return path.replace(/\\/g, '/');
14
- }
15
- function isTextFile(path) {
16
- return /\.(ts|tsx|js|jsx|mjs|cjs|py|md|json|ya?ml|toml|txt|sh|rb|go|rs|swift|kt|java|cs|php|sql|graphql|gql|proto|css|scss|less|html|vue|svelte)$/i.test(path);
17
- }
18
- function isSecretishPath(path) {
19
- const normalized = normalizePath(path).toLowerCase();
20
- return normalized.includes('.env') ||
21
- normalized.endsWith('.pem') ||
22
- normalized.endsWith('.key') ||
23
- normalized.includes('/secrets/') ||
24
- normalized.includes('/credentials/');
25
- }
26
- function isOutsideRoot(rel) {
27
- return rel === '' || rel === '..' || rel.startsWith('../') || isAbsolute(rel);
28
- }
29
- /** Walk from `fromAbs` up to `rootAbs` (inclusive), bounded by `maxDepth`. */
30
- function walkAncestorDirs(fromAbs, rootAbs, maxDepth = 8) {
31
- const dirs = [];
32
- let cursor = fromAbs;
33
- for (let depth = 0; depth < maxDepth; depth += 1) {
34
- dirs.push(cursor);
35
- if (normalizePath(cursor) === normalizePath(rootAbs))
36
- break;
37
- const parent = dirname(cursor);
38
- if (parent === cursor)
39
- break;
40
- cursor = parent;
41
- }
42
- return dirs;
43
- }
44
- function readBoundedRepoText(absPath, rootDir) {
45
- if (!existsSync(absPath))
46
- return undefined;
47
- const rel = normalizePath(relative(rootDir, absPath));
48
- // Containment: never read files that resolve outside the scan root. Guards against
49
- // `..` traversal or absolute paths (e.g. via a crafted `path=` annotation / sourcePath /
50
- // evidence path) being hydrated into synced artifact bodies.
51
- if (isOutsideRoot(rel))
52
- return undefined;
53
- if (isSecretishPath(rel))
54
- return undefined;
55
- let stat;
56
- try {
57
- stat = lstatSync(absPath);
58
- }
59
- catch {
60
- return undefined;
61
- }
62
- if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_SCANNED_FILE_BYTES) {
63
- return undefined;
64
- }
65
- if (!isTextFile(rel))
66
- return undefined;
67
- // Realpath containment: the lexical `rel` check is purely textual and `lstatSync` only
68
- // inspects the final component, so an intermediate directory symlink (e.g.
69
- // `tools/outside -> /tmp/public`) could still let `readFileSync` follow the link out of
70
- // the repo. Canonicalize both sides and re-check before reading.
71
- try {
72
- if (isOutsideRoot(normalizePath(relative(realpathSync(rootDir), realpathSync(absPath))))) {
73
- return undefined;
74
- }
75
- }
76
- catch {
77
- return undefined;
78
- }
79
- try {
80
- return readFileSync(absPath, 'utf8');
81
- }
82
- catch {
83
- return undefined;
84
- }
85
- }
86
- function readRepoRelativeText(rootDir, relPath) {
87
- return readBoundedRepoText(resolve(rootDir, relPath), rootDir);
88
- }
89
- function isDedicatedToolFilePath(path) {
90
- return /(^|\/)tools?\//i.test(path) && /\.(ts|tsx|js|mjs|py)$/i.test(path);
91
- }
92
- function firstEvidenceLineNumber(evidence) {
93
- if (!Array.isArray(evidence))
94
- return undefined;
95
- for (const entry of evidence) {
96
- if (typeof entry.line === 'number' && Number.isFinite(entry.line) && entry.line > 0) {
97
- return Math.floor(entry.line);
98
- }
99
- }
100
- return undefined;
101
- }
102
- function extractSnippetAroundLine(text, line, radius) {
103
- const lines = text.split(/\r?\n/);
104
- const start = Math.max(0, line - 1 - radius);
105
- // `line` is 1-indexed and slice's end is exclusive, so include through
106
- // (line-1)+radius: end = line + radius. Using line-1+radius dropped the tail line.
107
- const end = Math.min(lines.length, line + radius);
108
- return lines.slice(start, end).join('\n');
109
- }
110
- export function extractToolBodyFromText(text, toolName) {
111
- const lines = text.split(/\r?\n/);
112
- const normalizedName = toolName.replace(/\s+/g, '').toLowerCase();
113
- for (let index = 0; index < lines.length; index += 1) {
114
- const line = lines[index];
115
- const compact = line.replace(/\s+/g, '').toLowerCase();
116
- const mentionsTool = compact.includes(normalizedName) || compact.includes(`def${normalizedName}`);
117
- if (!mentionsTool)
118
- continue;
119
- if (line.includes('function_tool') ||
120
- line.includes('tool(') ||
121
- line.includes('createTool') ||
122
- line.includes('functionTool') ||
123
- line.includes('dynamicTool') ||
124
- line.includes('new Tool') ||
125
- line.includes('def ')) {
126
- return extractSnippetAroundLine(text, index + 1, 30);
127
- }
128
- }
129
- return undefined;
130
- }
131
- export function annotatedSourceCandidates(sourcePath, annotation) {
132
- const explicitSourcePath = normalizePath(sourcePath) !== normalizePath(annotation.evidence.path);
133
- // Relative `path=` annotations in nested packages usually mean "relative to the
134
- // package/harness root that owns the annotated file", not the file's own directory.
135
- // Example: harnesses/boardy-ops/src/ops.ts with path=prompts/ops-system.prompt.md
136
- // resolves to harnesses/boardy-ops/prompts/ops-system.prompt.md (one level up from
137
- // src/), not harnesses/boardy-ops/src/prompts/... Walk parents between the
138
- // annotated file and the scan root, then fall back to the scan-root candidate
139
- // for flat repos that annotate from the repository root.
140
- if (!explicitSourcePath) {
141
- return [
142
- resolve(annotation.rootDir, sourcePath),
143
- resolve(dirname(annotation.sourceAbsPath), sourcePath),
144
- annotation.sourceAbsPath,
145
- ];
146
- }
147
- const rootAbs = resolve(annotation.rootDir);
148
- const candidates = walkAncestorDirs(dirname(annotation.sourceAbsPath), rootAbs)
149
- .map((dir) => resolve(dir, sourcePath));
150
- candidates.push(resolve(annotation.rootDir, sourcePath));
151
- return candidates;
152
- }
153
- function readAnnotatedSourceText(sourcePath, annotation) {
154
- const seen = new Set();
155
- for (const candidate of annotatedSourceCandidates(sourcePath, annotation)) {
156
- if (seen.has(candidate))
157
- continue;
158
- seen.add(candidate);
159
- const text = readBoundedRepoText(candidate, annotation.rootDir);
160
- if (text !== undefined)
161
- return text;
162
- }
163
- return undefined;
164
- }
165
- /**
166
- * Resolve an annotated artifact `path=` to a repo-relative sourcePath.
167
- * Prefer the nested annotation-relative file when present so later whole-file
168
- * hydration and content hashing look in the right place.
169
- */
170
- export function resolveAnnotatedSourcePath(sourcePath, annotation) {
171
- const seen = new Set();
172
- for (const candidate of annotatedSourceCandidates(sourcePath, annotation)) {
173
- if (seen.has(candidate))
174
- continue;
175
- seen.add(candidate);
176
- if (readBoundedRepoText(candidate, annotation.rootDir) === undefined)
177
- continue;
178
- const rel = normalizePath(relative(annotation.rootDir, candidate));
179
- if (!isOutsideRoot(rel))
180
- return rel;
181
- }
182
- return sourcePath;
183
- }
184
- export function readPromptOrToolBody(type, sourcePath, annotation) {
185
- if (!requiresArtifactBody(type))
186
- return undefined;
187
- const text = readAnnotatedSourceText(sourcePath, annotation);
188
- if (text === undefined)
189
- return undefined;
190
- if (type === 'prompt' || isDedicatedToolFilePath(sourcePath))
191
- return text;
192
- return extractToolBodyFromText(text, first(annotation.values.name) ?? basename(sourcePath)) ?? text;
193
- }
194
- export function extractToolBody(file, tool) {
195
- const text = file.text ?? readRepoRelativeText(file.rootDir, file.path);
196
- if (!text)
197
- return undefined;
198
- if (isDedicatedToolFilePath(file.path))
199
- return text;
200
- return extractToolBodyFromText(text, tool.name);
201
- }
202
- function hydrateArtifactBody(artifact, rootDir) {
203
- if (artifact.sourcePath) {
204
- const wholeFile = readRepoRelativeText(rootDir, artifact.sourcePath)
205
- ?? readSourcePathRelativeToEvidence(artifact, rootDir);
206
- if (wholeFile !== undefined) {
207
- if (artifact.type === 'prompt' || isDedicatedToolFilePath(artifact.sourcePath)) {
208
- return wholeFile;
209
- }
210
- if (artifact.type === 'tool') {
211
- return extractToolBodyFromText(wholeFile, artifact.name) ?? wholeFile;
212
- }
213
- }
214
- }
215
- const evidenceLine = firstEvidenceLineNumber(artifact.evidence);
216
- const evidencePath = firstEvidencePath(artifact);
217
- if (evidencePath) {
218
- const text = readRepoRelativeText(rootDir, evidencePath);
219
- if (text !== undefined) {
220
- if (evidenceLine !== undefined) {
221
- return extractSnippetAroundLine(text, evidenceLine, artifact.type === 'tool' ? 30 : 80);
222
- }
223
- // No line number (common for agent/hosted analyst citations): fall back to
224
- // the whole evidence file so the body is still captured in full.
225
- if (artifact.type === 'prompt' || isDedicatedToolFilePath(evidencePath))
226
- return text;
227
- if (artifact.type === 'tool')
228
- return extractToolBodyFromText(text, artifact.name) ?? text;
229
- }
230
- }
231
- return undefined;
232
- }
233
- /**
234
- * Fallback for nested relative `sourcePath` values that were recorded before the
235
- * annotation-relative resolver existed: walk parent dirs beside the evidence file
236
- * looking for the package-local prompt/tool path.
237
- */
238
- function readSourcePathRelativeToEvidence(artifact, rootDir) {
239
- if (!artifact.sourcePath)
240
- return undefined;
241
- const evidencePath = firstEvidencePath(artifact);
242
- if (!evidencePath)
243
- return undefined;
244
- const rootAbs = resolve(rootDir);
245
- for (const dir of walkAncestorDirs(dirname(resolve(rootDir, evidencePath)), rootAbs)) {
246
- const text = readBoundedRepoText(resolve(dir, artifact.sourcePath), rootDir);
247
- if (text !== undefined)
248
- return text;
249
- }
250
- return undefined;
251
- }
252
- export function hydratePromptAndToolBodies(graph, rootDir) {
253
- for (const artifact of graph.artifacts) {
254
- if (!requiresArtifactBody(artifact.type) || artifactHasBody(artifact)) {
255
- continue;
256
- }
257
- const hydrated = hydrateArtifactBody(artifact, rootDir);
258
- if (hydrated)
259
- artifact.body = hydrated;
260
- }
261
- }
262
- function countIncompletePromptToolBodies(graph) {
263
- return graph.artifacts.filter((artifact) => requiresArtifactBody(artifact.type) &&
264
- !artifactHasBody(artifact)).length;
265
- }
266
- export function enrichScanCaptureMetadata(graph) {
267
- const budgetSkips = graph.scan.skipped
268
- .map((skip) => skip.reason)
269
- .filter((reason) => SNAPSHOT_BUDGET_SKIP_REASONS.has(reason));
270
- const truncationReasons = [...new Set(budgetSkips)];
271
- graph.scan.truncated = truncationReasons.length > 0;
272
- graph.scan.truncationReasons = truncationReasons.length > 0 ? truncationReasons : undefined;
273
- const incomplete = countIncompletePromptToolBodies(graph);
274
- graph.scan.incompleteArtifactCount = incomplete > 0 ? incomplete : undefined;
275
- }
276
- //# sourceMappingURL=harness-artifact-bodies.js.map
@@ -1,77 +0,0 @@
1
- /**
2
- * Static harness-candidate pre-pass (Phase 2 prototype).
3
- *
4
- * A deterministic, LLM-free detector that walks a repo and extracts
5
- * *candidate* harness surfaces — LLM SDK call sites, agent-framework
6
- * constructs, tool definitions/schemas, prompt files and prompt-like string
7
- * literals, and model config assignments — each with a file:line citation,
8
- * a confidence heuristic, and a short excerpt.
9
- *
10
- * Purpose: today `harness analyze` (local or hosted) has a single agent read
11
- * everything, which caps out around ~450 files / ~20 minutes. This pre-pass is
12
- * pure regex/heuristics (no LLM calls, no network), runs in milliseconds per
13
- * thousand files, and produces a prioritized read plan so the LLM
14
- * classification stage only needs to open the files that plausibly matter.
15
- *
16
- * Output contract: `harness_candidates.v0.1` — see {@link HarnessCandidateReport}.
17
- * Candidates are recall-oriented: false positives are expected and cheap (the
18
- * LLM stage filters them); false negatives are the expensive failure mode.
19
- */
20
- import { type FileInventoryItem, type HarnessScanMetadata } from './harness.js';
21
- export type HarnessCandidateKind = 'llm_call_site' | 'framework_construct' | 'tool_definition' | 'tool_schema' | 'message_construction' | 'prompt_file' | 'prompt_literal' | 'model_config';
22
- export type HarnessCandidateConfidence = 'high' | 'medium' | 'low';
23
- export interface HarnessCandidate {
24
- kind: HarnessCandidateKind;
25
- /** Repo-relative, forward-slash path. */
26
- path: string;
27
- /** 1-based inclusive line range. */
28
- startLine: number;
29
- endLine: number;
30
- confidence: HarnessCandidateConfidence;
31
- /** Which detector fired, e.g. `openai.chat_completions_create`. */
32
- signal: string;
33
- /** Normalized single-line excerpt, capped at {@link EXCERPT_MAX_CHARS}. */
34
- excerpt: string;
35
- }
36
- export interface HarnessCandidateFileSummary {
37
- path: string;
38
- /** Sum of per-candidate confidence weights; higher = read first. */
39
- score: number;
40
- kinds: HarnessCandidateKind[];
41
- candidateCount: number;
42
- }
43
- export interface HarnessCandidateStats {
44
- filesScanned: number;
45
- filesWithCandidates: number;
46
- candidateCount: number;
47
- textBytesScanned: number;
48
- durationMs: number;
49
- filesPerSecond: number;
50
- }
51
- export interface HarnessCandidateReport {
52
- schemaVersion: 'harness_candidates.v0.1';
53
- generatedAt: string;
54
- rootDir: string;
55
- stats: HarnessCandidateStats;
56
- candidates: HarnessCandidate[];
57
- /** Per-file rollup sorted by descending score — the LLM-stage read plan. */
58
- files: HarnessCandidateFileSummary[];
59
- skipped: HarnessScanMetadata['skipped'];
60
- }
61
- /**
62
- * Pure detector core: run every heuristic over an already-materialized file
63
- * inventory. No filesystem access — unit tests and the walker wrapper both
64
- * call this.
65
- */
66
- export declare function detectHarnessCandidatesInFiles(files: Array<Pick<FileInventoryItem, 'path' | 'text'>>): {
67
- candidates: HarnessCandidate[];
68
- files: HarnessCandidateFileSummary[];
69
- };
70
- /**
71
- * Walk `rootDir` with the harness inventory walker (same skip/secret/symlink
72
- * budget rules as `harness scan`, widened to include all prompt-doc files)
73
- * and run the static detector over it.
74
- */
75
- export declare function detectHarnessCandidates(rootDir: string): HarnessCandidateReport;
76
- export declare function writeHarnessCandidatesReport(report: HarnessCandidateReport, outPath: string): string;
77
- //# sourceMappingURL=harness-candidates.d.ts.map