@vendoai/vendo 0.4.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/LICENSE +202 -0
- package/README.md +28 -0
- package/bin/vendo.mjs +4 -0
- package/dist/ai-sdk.d.ts +35 -0
- package/dist/ai-sdk.js +62 -0
- package/dist/auth-presets/auth-js.d.ts +14 -0
- package/dist/auth-presets/auth-js.js +91 -0
- package/dist/auth-presets/auth0.d.ts +23 -0
- package/dist/auth-presets/auth0.js +98 -0
- package/dist/auth-presets/clerk.d.ts +23 -0
- package/dist/auth-presets/clerk.js +65 -0
- package/dist/auth-presets/conformance.d.ts +42 -0
- package/dist/auth-presets/conformance.js +139 -0
- package/dist/auth-presets/identity.d.ts +56 -0
- package/dist/auth-presets/identity.js +152 -0
- package/dist/auth-presets/index.d.ts +14 -0
- package/dist/auth-presets/index.js +12 -0
- package/dist/auth-presets/jwt.d.ts +16 -0
- package/dist/auth-presets/jwt.js +54 -0
- package/dist/auth-presets/shared.d.ts +35 -0
- package/dist/auth-presets/shared.js +1 -0
- package/dist/auth-presets/supabase.d.ts +42 -0
- package/dist/auth-presets/supabase.js +229 -0
- package/dist/byo-approvals.d.ts +37 -0
- package/dist/byo-approvals.js +189 -0
- package/dist/capability-misses.d.ts +38 -0
- package/dist/capability-misses.js +172 -0
- package/dist/catalog.d.ts +19 -0
- package/dist/catalog.js +152 -0
- package/dist/cli/cloud/args.d.ts +3 -0
- package/dist/cli/cloud/args.js +32 -0
- package/dist/cli/cloud/auth.d.ts +17 -0
- package/dist/cli/cloud/auth.js +123 -0
- package/dist/cli/cloud/client.d.ts +28 -0
- package/dist/cli/cloud/client.js +133 -0
- package/dist/cli/cloud/command.d.ts +22 -0
- package/dist/cli/cloud/command.js +73 -0
- package/dist/cli/cloud/deploy.d.ts +26 -0
- package/dist/cli/cloud/deploy.js +187 -0
- package/dist/cli/cloud/device-login.d.ts +32 -0
- package/dist/cli/cloud/device-login.js +259 -0
- package/dist/cli/cloud/index.d.ts +5 -0
- package/dist/cli/cloud/index.js +59 -0
- package/dist/cli/cloud/keys.d.ts +2 -0
- package/dist/cli/cloud/keys.js +31 -0
- package/dist/cli/cloud/members.d.ts +3 -0
- package/dist/cli/cloud/members.js +25 -0
- package/dist/cli/cloud/output.d.ts +4 -0
- package/dist/cli/cloud/output.js +12 -0
- package/dist/cli/cloud/pending-claim.d.ts +29 -0
- package/dist/cli/cloud/pending-claim.js +38 -0
- package/dist/cli/cloud/read.d.ts +3 -0
- package/dist/cli/cloud/read.js +12 -0
- package/dist/cli/cloud/services.d.ts +2 -0
- package/dist/cli/cloud/services.js +9 -0
- package/dist/cli/cloud/session.d.ts +13 -0
- package/dist/cli/cloud/session.js +41 -0
- package/dist/cli/cloud-init.d.ts +60 -0
- package/dist/cli/cloud-init.js +149 -0
- package/dist/cli/dep-versions.d.ts +26 -0
- package/dist/cli/dep-versions.js +77 -0
- package/dist/cli/doctor-codes.d.ts +58 -0
- package/dist/cli/doctor-codes.js +60 -0
- package/dist/cli/doctor-live.d.ts +67 -0
- package/dist/cli/doctor-live.js +169 -0
- package/dist/cli/doctor.d.ts +52 -0
- package/dist/cli/doctor.js +467 -0
- package/dist/cli/eject.d.ts +29 -0
- package/dist/cli/eject.js +141 -0
- package/dist/cli/extract/apply.d.ts +26 -0
- package/dist/cli/extract/apply.js +123 -0
- package/dist/cli/extract/claude-cli-harness.d.ts +18 -0
- package/dist/cli/extract/claude-cli-harness.js +132 -0
- package/dist/cli/extract/claude-harness.d.ts +14 -0
- package/dist/cli/extract/claude-harness.js +127 -0
- package/dist/cli/extract/codex-cli-harness.d.ts +31 -0
- package/dist/cli/extract/codex-cli-harness.js +154 -0
- package/dist/cli/extract/delegate.d.ts +21 -0
- package/dist/cli/extract/delegate.js +77 -0
- package/dist/cli/extract/extraction.d.ts +65 -0
- package/dist/cli/extract/extraction.js +300 -0
- package/dist/cli/extract/gateway-fuel.d.ts +69 -0
- package/dist/cli/extract/gateway-fuel.js +69 -0
- package/dist/cli/extract/harness.d.ts +114 -0
- package/dist/cli/extract/harness.js +84 -0
- package/dist/cli/extract/index.d.ts +9 -0
- package/dist/cli/extract/index.js +9 -0
- package/dist/cli/extract/npx-engine-harness.d.ts +73 -0
- package/dist/cli/extract/npx-engine-harness.js +225 -0
- package/dist/cli/extract/stages.d.ts +149 -0
- package/dist/cli/extract/stages.js +398 -0
- package/dist/cli/framework.d.ts +8 -0
- package/dist/cli/framework.js +36 -0
- package/dist/cli/init-auth.d.ts +67 -0
- package/dist/cli/init-auth.js +142 -0
- package/dist/cli/init-scaffolds.d.ts +28 -0
- package/dist/cli/init-scaffolds.js +235 -0
- package/dist/cli/init.d.ts +115 -0
- package/dist/cli/init.js +1008 -0
- package/dist/cli/mcp/index.d.ts +7 -0
- package/dist/cli/mcp/index.js +59 -0
- package/dist/cli/mcp/registry.d.ts +9 -0
- package/dist/cli/mcp/registry.js +117 -0
- package/dist/cli/mcp/server-json.d.ts +12 -0
- package/dist/cli/mcp/server-json.js +65 -0
- package/dist/cli/mcp/server.schema.json +574 -0
- package/dist/cli/mcp/verify-domain.d.ts +11 -0
- package/dist/cli/mcp/verify-domain.js +48 -0
- package/dist/cli/playground/app/embed-entry.d.ts +20 -0
- package/dist/cli/playground/app/embed-entry.js +46 -0
- package/dist/cli/playground/app/fake-client.d.ts +3 -0
- package/dist/cli/playground/app/fake-client.js +215 -0
- package/dist/cli/playground/app/fixtures.d.ts +47 -0
- package/dist/cli/playground/app/fixtures.js +472 -0
- package/dist/cli/playground/app/main.d.ts +1 -0
- package/dist/cli/playground/app/main.js +108 -0
- package/dist/cli/playground/app/scenario-mount.d.ts +13 -0
- package/dist/cli/playground/app/scenario-mount.js +48 -0
- package/dist/cli/playground/app/scenarios.d.ts +25 -0
- package/dist/cli/playground/app/scenarios.js +152 -0
- package/dist/cli/playground/app/theme-editor.d.ts +13 -0
- package/dist/cli/playground/app/theme-editor.js +149 -0
- package/dist/cli/playground/app/theme-state.d.ts +29 -0
- package/dist/cli/playground/app/theme-state.js +151 -0
- package/dist/cli/playground/bundle.gen.d.ts +2 -0
- package/dist/cli/playground/bundle.gen.js +2 -0
- package/dist/cli/playground/embed-bundle.gen.d.ts +2 -0
- package/dist/cli/playground/embed-bundle.gen.js +2 -0
- package/dist/cli/playground.d.ts +33 -0
- package/dist/cli/playground.js +120 -0
- package/dist/cli/pretty.d.ts +48 -0
- package/dist/cli/pretty.js +330 -0
- package/dist/cli/refine.d.ts +43 -0
- package/dist/cli/refine.js +181 -0
- package/dist/cli/semantics.d.ts +31 -0
- package/dist/cli/semantics.js +122 -0
- package/dist/cli/shared.d.ts +68 -0
- package/dist/cli/shared.js +184 -0
- package/dist/cli/sync.d.ts +36 -0
- package/dist/cli/sync.js +202 -0
- package/dist/cli/theme/color.d.ts +15 -0
- package/dist/cli/theme/color.js +188 -0
- package/dist/cli/theme/css-vars.d.ts +13 -0
- package/dist/cli/theme/css-vars.js +40 -0
- package/dist/cli/theme/entry-candidates.d.ts +5 -0
- package/dist/cli/theme/entry-candidates.js +22 -0
- package/dist/cli/theme/extract-theme.d.ts +184 -0
- package/dist/cli/theme/extract-theme.js +358 -0
- package/dist/cli/theme/walk.d.ts +2 -0
- package/dist/cli/theme/walk.js +24 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +333 -0
- package/dist/cloud-apps.d.ts +17 -0
- package/dist/cloud-apps.js +46 -0
- package/dist/cloud-console.d.ts +32 -0
- package/dist/cloud-console.js +72 -0
- package/dist/cloud-tools.d.ts +23 -0
- package/dist/cloud-tools.js +196 -0
- package/dist/connections.d.ts +71 -0
- package/dist/connections.js +192 -0
- package/dist/deployment-identity.d.ts +12 -0
- package/dist/deployment-identity.js +69 -0
- package/dist/dev-creds/model.d.ts +65 -0
- package/dist/dev-creds/model.js +159 -0
- package/dist/dev-creds/resolve.d.ts +36 -0
- package/dist/dev-creds/resolve.js +57 -0
- package/dist/hosted-store.d.ts +52 -0
- package/dist/hosted-store.js +322 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1 -0
- package/dist/mastra.d.ts +46 -0
- package/dist/mastra.js +88 -0
- package/dist/react.d.ts +10 -0
- package/dist/react.js +32 -0
- package/dist/refine.d.ts +244 -0
- package/dist/refine.js +599 -0
- package/dist/remixable.d.ts +18 -0
- package/dist/remixable.js +42 -0
- package/dist/runtime-capture.d.ts +18 -0
- package/dist/runtime-capture.js +111 -0
- package/dist/sandbox-wire.d.ts +101 -0
- package/dist/sandbox-wire.js +101 -0
- package/dist/sandbox.d.ts +38 -0
- package/dist/sandbox.js +389 -0
- package/dist/server.d.ts +216 -0
- package/dist/server.js +1320 -0
- package/dist/sync-impact.d.ts +14 -0
- package/dist/sync-impact.js +75 -0
- package/dist/turn-liveness.d.ts +33 -0
- package/dist/turn-liveness.js +105 -0
- package/dist/wire/approvals.d.ts +10 -0
- package/dist/wire/approvals.js +59 -0
- package/dist/wire/apps.d.ts +5 -0
- package/dist/wire/apps.js +155 -0
- package/dist/wire/automations.d.ts +5 -0
- package/dist/wire/automations.js +57 -0
- package/dist/wire/box.d.ts +3 -0
- package/dist/wire/box.js +268 -0
- package/dist/wire/connections.d.ts +5 -0
- package/dist/wire/connections.js +47 -0
- package/dist/wire/context.d.ts +22 -0
- package/dist/wire/context.js +190 -0
- package/dist/wire/doctor.d.ts +19 -0
- package/dist/wire/doctor.js +116 -0
- package/dist/wire/misc.d.ts +18 -0
- package/dist/wire/misc.js +227 -0
- package/dist/wire/shared.d.ts +158 -0
- package/dist/wire/shared.js +134 -0
- package/dist/wire/threads.d.ts +3 -0
- package/dist/wire/threads.js +68 -0
- package/package.json +151 -0
- package/skills/vendo-setup/SKILL.md +128 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The ExtractionHarness seam (install-dx v1, brainstorm 2026-07-18): Vendo
|
|
4
|
+
* owns the instructions, the deterministic validation, and the artifact
|
|
5
|
+
* contract; a world-class coding agent does the reading. Everything above
|
|
6
|
+
* this seam is vendor-neutral — swapping the harness (Claude Agent SDK today,
|
|
7
|
+
* anything tomorrow) is a config change, not an architecture change.
|
|
8
|
+
*/
|
|
9
|
+
/** One drafted polish entry for an extracted tool. The AI pass proposes;
|
|
10
|
+
* deterministic guards in extraction.ts decide what applies. */
|
|
11
|
+
export declare const draftToolSchema: z.ZodObject<{
|
|
12
|
+
name: z.ZodString;
|
|
13
|
+
description: z.ZodString;
|
|
14
|
+
risk: z.ZodOptional<z.ZodEnum<["read", "write", "destructive"]>>;
|
|
15
|
+
critical: z.ZodOptional<z.ZodBoolean>;
|
|
16
|
+
/** false = wake a statically-unclassifiable tool (needs reasoning). */
|
|
17
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
18
|
+
reasoning: z.ZodOptional<z.ZodString>;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
risk?: "read" | "write" | "destructive" | undefined;
|
|
23
|
+
critical?: boolean | undefined;
|
|
24
|
+
disabled?: boolean | undefined;
|
|
25
|
+
reasoning?: string | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
risk?: "read" | "write" | "destructive" | undefined;
|
|
30
|
+
critical?: boolean | undefined;
|
|
31
|
+
disabled?: boolean | undefined;
|
|
32
|
+
reasoning?: string | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
export type DraftTool = z.infer<typeof draftToolSchema>;
|
|
35
|
+
export declare const extractionDraftSchema: z.ZodObject<{
|
|
36
|
+
/** One-paragraph product brief drafted from the codebase. */
|
|
37
|
+
brief: z.ZodString;
|
|
38
|
+
tools: z.ZodArray<z.ZodObject<{
|
|
39
|
+
name: z.ZodString;
|
|
40
|
+
description: z.ZodString;
|
|
41
|
+
risk: z.ZodOptional<z.ZodEnum<["read", "write", "destructive"]>>;
|
|
42
|
+
critical: z.ZodOptional<z.ZodBoolean>;
|
|
43
|
+
/** false = wake a statically-unclassifiable tool (needs reasoning). */
|
|
44
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
45
|
+
reasoning: z.ZodOptional<z.ZodString>;
|
|
46
|
+
}, "strip", z.ZodTypeAny, {
|
|
47
|
+
name: string;
|
|
48
|
+
description: string;
|
|
49
|
+
risk?: "read" | "write" | "destructive" | undefined;
|
|
50
|
+
critical?: boolean | undefined;
|
|
51
|
+
disabled?: boolean | undefined;
|
|
52
|
+
reasoning?: string | undefined;
|
|
53
|
+
}, {
|
|
54
|
+
name: string;
|
|
55
|
+
description: string;
|
|
56
|
+
risk?: "read" | "write" | "destructive" | undefined;
|
|
57
|
+
critical?: boolean | undefined;
|
|
58
|
+
disabled?: boolean | undefined;
|
|
59
|
+
reasoning?: string | undefined;
|
|
60
|
+
}>, "many">;
|
|
61
|
+
/** API surfaces the static extractor missed — surfaced to the dev, not
|
|
62
|
+
* written anywhere (adding tools is future work on the sync side). */
|
|
63
|
+
missedSurfaces: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
64
|
+
}, "strip", z.ZodTypeAny, {
|
|
65
|
+
tools: {
|
|
66
|
+
name: string;
|
|
67
|
+
description: string;
|
|
68
|
+
risk?: "read" | "write" | "destructive" | undefined;
|
|
69
|
+
critical?: boolean | undefined;
|
|
70
|
+
disabled?: boolean | undefined;
|
|
71
|
+
reasoning?: string | undefined;
|
|
72
|
+
}[];
|
|
73
|
+
brief: string;
|
|
74
|
+
missedSurfaces?: string[] | undefined;
|
|
75
|
+
}, {
|
|
76
|
+
tools: {
|
|
77
|
+
name: string;
|
|
78
|
+
description: string;
|
|
79
|
+
risk?: "read" | "write" | "destructive" | undefined;
|
|
80
|
+
critical?: boolean | undefined;
|
|
81
|
+
disabled?: boolean | undefined;
|
|
82
|
+
reasoning?: string | undefined;
|
|
83
|
+
}[];
|
|
84
|
+
brief: string;
|
|
85
|
+
missedSurfaces?: string[] | undefined;
|
|
86
|
+
}>;
|
|
87
|
+
export type ExtractionDraft = z.infer<typeof extractionDraftSchema>;
|
|
88
|
+
export interface ExtractionRunInput {
|
|
89
|
+
root: string;
|
|
90
|
+
/** The complete staged instructions, composed by extraction.ts. */
|
|
91
|
+
instructions: string;
|
|
92
|
+
env: Record<string, string | undefined>;
|
|
93
|
+
/** Live narration line (surface discoveries, files being read). */
|
|
94
|
+
onProgress?: (line: string) => void;
|
|
95
|
+
}
|
|
96
|
+
export interface ExtractionHarness {
|
|
97
|
+
/** Stable identifier ("claude-agent-sdk"). */
|
|
98
|
+
id: string;
|
|
99
|
+
/** A short human label of the credential this harness would use
|
|
100
|
+
* ("your Claude Code login", "your ANTHROPIC_API_KEY"), or null when the
|
|
101
|
+
* harness cannot run on this machine. */
|
|
102
|
+
availability(input: {
|
|
103
|
+
root: string;
|
|
104
|
+
env: Record<string, string | undefined>;
|
|
105
|
+
}): Promise<string | null>;
|
|
106
|
+
/** Run the instructions and return the agent's final text (extraction.ts
|
|
107
|
+
* parses and validates; the harness never interprets the draft). */
|
|
108
|
+
run(input: ExtractionRunInput): Promise<string>;
|
|
109
|
+
}
|
|
110
|
+
/** Extract a stage artifact from an agent's final text: prefer a fenced
|
|
111
|
+
* block, else try each balanced object span until one validates against the
|
|
112
|
+
* stage's schema. Throws on unparseable output. */
|
|
113
|
+
export declare function parseArtifact<Schema extends z.ZodTypeAny>(text: string, schema: Schema): z.infer<Schema>;
|
|
114
|
+
export declare function parseDraft(text: string): ExtractionDraft;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The ExtractionHarness seam (install-dx v1, brainstorm 2026-07-18): Vendo
|
|
4
|
+
* owns the instructions, the deterministic validation, and the artifact
|
|
5
|
+
* contract; a world-class coding agent does the reading. Everything above
|
|
6
|
+
* this seam is vendor-neutral — swapping the harness (Claude Agent SDK today,
|
|
7
|
+
* anything tomorrow) is a config change, not an architecture change.
|
|
8
|
+
*/
|
|
9
|
+
/** One drafted polish entry for an extracted tool. The AI pass proposes;
|
|
10
|
+
* deterministic guards in extraction.ts decide what applies. */
|
|
11
|
+
export const draftToolSchema = z.object({
|
|
12
|
+
name: z.string().min(1),
|
|
13
|
+
description: z.string().min(1).max(500),
|
|
14
|
+
risk: z.enum(["read", "write", "destructive"]).optional(),
|
|
15
|
+
critical: z.boolean().optional(),
|
|
16
|
+
/** false = wake a statically-unclassifiable tool (needs reasoning). */
|
|
17
|
+
disabled: z.boolean().optional(),
|
|
18
|
+
reasoning: z.string().max(500).optional(),
|
|
19
|
+
});
|
|
20
|
+
export const extractionDraftSchema = z.object({
|
|
21
|
+
/** One-paragraph product brief drafted from the codebase. */
|
|
22
|
+
brief: z.string().min(1).max(4000),
|
|
23
|
+
tools: z.array(draftToolSchema),
|
|
24
|
+
/** API surfaces the static extractor missed — surfaced to the dev, not
|
|
25
|
+
* written anywhere (adding tools is future work on the sync side). */
|
|
26
|
+
missedSurfaces: z.array(z.string().max(300)).optional(),
|
|
27
|
+
});
|
|
28
|
+
/** Walk balanced `{…}` spans (string-aware) starting at each `{`, returning
|
|
29
|
+
* each complete candidate — so prose containing stray braces around the real
|
|
30
|
+
* JSON object cannot poison the parse (Greptile P2 on #363). */
|
|
31
|
+
function* balancedObjectSpans(text) {
|
|
32
|
+
for (let start = text.indexOf("{"); start !== -1; start = text.indexOf("{", start + 1)) {
|
|
33
|
+
let depth = 0;
|
|
34
|
+
let inString = false;
|
|
35
|
+
for (let index = start; index < text.length; index += 1) {
|
|
36
|
+
const char = text[index];
|
|
37
|
+
if (inString) {
|
|
38
|
+
if (char === "\\")
|
|
39
|
+
index += 1;
|
|
40
|
+
else if (char === '"')
|
|
41
|
+
inString = false;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (char === '"')
|
|
45
|
+
inString = true;
|
|
46
|
+
else if (char === "{")
|
|
47
|
+
depth += 1;
|
|
48
|
+
else if (char === "}") {
|
|
49
|
+
depth -= 1;
|
|
50
|
+
if (depth === 0) {
|
|
51
|
+
yield text.slice(start, index + 1);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** Extract a stage artifact from an agent's final text: prefer a fenced
|
|
59
|
+
* block, else try each balanced object span until one validates against the
|
|
60
|
+
* stage's schema. Throws on unparseable output. */
|
|
61
|
+
export function parseArtifact(text, schema) {
|
|
62
|
+
const fenced = text.match(/```(?:json)?\s*\n([\s\S]*?)\n```/);
|
|
63
|
+
if (fenced?.[1] !== undefined) {
|
|
64
|
+
return schema.parse(JSON.parse(fenced[1]));
|
|
65
|
+
}
|
|
66
|
+
let firstError = new Error("no JSON object found in the agent's output");
|
|
67
|
+
let attempts = 0;
|
|
68
|
+
for (const span of balancedObjectSpans(text)) {
|
|
69
|
+
if (attempts >= 20)
|
|
70
|
+
break;
|
|
71
|
+
attempts += 1;
|
|
72
|
+
try {
|
|
73
|
+
return schema.parse(JSON.parse(span));
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (attempts === 1)
|
|
77
|
+
firstError = error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw firstError;
|
|
81
|
+
}
|
|
82
|
+
export function parseDraft(text) {
|
|
83
|
+
return parseArtifact(text, extractionDraftSchema);
|
|
84
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public seam for the extraction pipeline pieces the corpus AI eval matrix
|
|
3
|
+
* consumes (install-dx lane 3). Additive re-exports only — the pipeline
|
|
4
|
+
* itself lives in the sibling modules and is owned by the init flow.
|
|
5
|
+
*/
|
|
6
|
+
export { draftToolSchema, extractionDraftSchema, parseDraft, type DraftTool, type ExtractionDraft, type ExtractionHarness, type ExtractionRunInput, } from "./harness.js";
|
|
7
|
+
export { applyDraft, composeInstructions } from "./extraction.js";
|
|
8
|
+
export { runStagedExtraction, staticToolSchema, type StagedExtractionInput, type StagedExtractionResult, type StaticTool, } from "./stages.js";
|
|
9
|
+
export { claudeHarness, type ClaudeHarnessOptions } from "./claude-harness.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public seam for the extraction pipeline pieces the corpus AI eval matrix
|
|
3
|
+
* consumes (install-dx lane 3). Additive re-exports only — the pipeline
|
|
4
|
+
* itself lives in the sibling modules and is owned by the init flow.
|
|
5
|
+
*/
|
|
6
|
+
export { draftToolSchema, extractionDraftSchema, parseDraft, } from "./harness.js";
|
|
7
|
+
export { applyDraft, composeInstructions } from "./extraction.js";
|
|
8
|
+
export { runStagedExtraction, staticToolSchema, } from "./stages.js";
|
|
9
|
+
export { claudeHarness } from "./claude-harness.js";
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type ExecFileException } from "node:child_process";
|
|
2
|
+
import type { ExtractionHarness } from "./harness.js";
|
|
3
|
+
/**
|
|
4
|
+
* Last-resort extraction harness: nothing Claude-shaped is installed on the
|
|
5
|
+
* dev's machine (no Agent SDK, no `claude` binary, no `codex` binary), but
|
|
6
|
+
* they do have a usable credential — so init fetches Claude Code itself via
|
|
7
|
+
* `npm exec` rather than degrading straight to the honest skip. This is the
|
|
8
|
+
* fourth and final rung of the ladder in extraction.ts.
|
|
9
|
+
*
|
|
10
|
+
* Cross-task contract (a parallel task builds `@vendoai/engine` to this exact
|
|
11
|
+
* shape): `npm exec --yes @vendoai/engine@<PINNED_VERSION> -- run`, job JSON
|
|
12
|
+
* `{ instructions, root }` on the child's stdin, credentials/base-url/headers
|
|
13
|
+
* ride the child's process env (never the job JSON), stdout is EXACTLY the
|
|
14
|
+
* agent's final text, stderr is progress/narration, exit 0 = success. The
|
|
15
|
+
* version is pinned exact (never a range) so this rung's behavior can't
|
|
16
|
+
* drift out from under init on a machine with no local install to pin
|
|
17
|
+
* instead.
|
|
18
|
+
*
|
|
19
|
+
* Availability deliberately never touches npm or the network — it is called
|
|
20
|
+
* eagerly for every rung on every `vendo init`, and a probe here would mean
|
|
21
|
+
* every init pays an npm-registry round trip just to build the "AI polish?"
|
|
22
|
+
* prompt. The ~250MB download surprise is disclosed instead via the
|
|
23
|
+
* run-time notice below, right before the first real network access.
|
|
24
|
+
*
|
|
25
|
+
* Gateway fuel: mirrors claude-cli-harness.ts — when the dev has none of
|
|
26
|
+
* ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, or
|
|
27
|
+
* ANTHROPIC_BASE_URL (the corporate-gateway/custom-endpoint path) but
|
|
28
|
+
* VENDO_API_KEY is set, the child runs against Vendo Cloud's model gateway
|
|
29
|
+
* instead of degrading to unavailable (see gateway-fuel.ts). Own credential
|
|
30
|
+
* always wins — availability() must label these honestly (not as "Vendo
|
|
31
|
+
* Cloud key") since composeGatewayFuel itself refuses to overlay onto any of
|
|
32
|
+
* them; a wrong label here would make the consent prompt lie about what
|
|
33
|
+
* run() actually does.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ENGINE_PACKAGE_NAME = "@vendoai/engine";
|
|
36
|
+
export declare const ENGINE_PACKAGE_VERSION = "0.1.0";
|
|
37
|
+
interface ExecResult {
|
|
38
|
+
stdout: string;
|
|
39
|
+
stderr: string;
|
|
40
|
+
code: number;
|
|
41
|
+
}
|
|
42
|
+
type Exec = (args: string[], options: {
|
|
43
|
+
cwd: string;
|
|
44
|
+
env: NodeJS.ProcessEnv;
|
|
45
|
+
input: string;
|
|
46
|
+
onStderrLine?: (line: string) => void;
|
|
47
|
+
}) => Promise<ExecResult>;
|
|
48
|
+
/** Pure mapping from a completed `execFile` callback into an ExecResult —
|
|
49
|
+
* pulled out of the real spawn so the four ways `error` shows up (a normal
|
|
50
|
+
* nonzero exit, a real spawn-layer failure, the RUN_TIMEOUT_MS kill, or the
|
|
51
|
+
* MAX_BUFFER_BYTES kill) each get direct unit coverage without actually
|
|
52
|
+
* spawning anything (mirrors resolveCodexExecResult in
|
|
53
|
+
* codex-cli-harness.ts). The four are easy to conflate — all leave
|
|
54
|
+
* `error.code` non-numeric — but they need different messages: a killed
|
|
55
|
+
* 15-minute extraction run is not npm being uninstalled, and a >10MB
|
|
56
|
+
* narration stream is not a timeout. */
|
|
57
|
+
export declare function resolveNpmExecResult(error: ExecFileException | null, stdout: string, stderr: string): ExecResult;
|
|
58
|
+
/** Line-buffered splitter for a live-streaming descriptor, extracted out of
|
|
59
|
+
* execNpmEngine so it's directly unit-testable without a real child
|
|
60
|
+
* process: a line split across two `data` chunks, and — the bug this
|
|
61
|
+
* fixes — a trailing line with no final newline, which `flush()` still
|
|
62
|
+
* delivers instead of silently dropping the child's last progress line
|
|
63
|
+
* when the stream ends without one. */
|
|
64
|
+
export declare function createLineSplitter(onLine: (line: string) => void): {
|
|
65
|
+
push(chunk: string): void;
|
|
66
|
+
flush(): void;
|
|
67
|
+
};
|
|
68
|
+
export interface NpxEngineHarnessOptions {
|
|
69
|
+
/** Test seam. */
|
|
70
|
+
exec?: Exec;
|
|
71
|
+
}
|
|
72
|
+
export declare function npxEngineHarness(options?: NpxEngineHarnessOptions): ExtractionHarness;
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { composeGatewayFuel, hasOwnAnthropicEnvOverride } from "./gateway-fuel.js";
|
|
3
|
+
/**
|
|
4
|
+
* Last-resort extraction harness: nothing Claude-shaped is installed on the
|
|
5
|
+
* dev's machine (no Agent SDK, no `claude` binary, no `codex` binary), but
|
|
6
|
+
* they do have a usable credential — so init fetches Claude Code itself via
|
|
7
|
+
* `npm exec` rather than degrading straight to the honest skip. This is the
|
|
8
|
+
* fourth and final rung of the ladder in extraction.ts.
|
|
9
|
+
*
|
|
10
|
+
* Cross-task contract (a parallel task builds `@vendoai/engine` to this exact
|
|
11
|
+
* shape): `npm exec --yes @vendoai/engine@<PINNED_VERSION> -- run`, job JSON
|
|
12
|
+
* `{ instructions, root }` on the child's stdin, credentials/base-url/headers
|
|
13
|
+
* ride the child's process env (never the job JSON), stdout is EXACTLY the
|
|
14
|
+
* agent's final text, stderr is progress/narration, exit 0 = success. The
|
|
15
|
+
* version is pinned exact (never a range) so this rung's behavior can't
|
|
16
|
+
* drift out from under init on a machine with no local install to pin
|
|
17
|
+
* instead.
|
|
18
|
+
*
|
|
19
|
+
* Availability deliberately never touches npm or the network — it is called
|
|
20
|
+
* eagerly for every rung on every `vendo init`, and a probe here would mean
|
|
21
|
+
* every init pays an npm-registry round trip just to build the "AI polish?"
|
|
22
|
+
* prompt. The ~250MB download surprise is disclosed instead via the
|
|
23
|
+
* run-time notice below, right before the first real network access.
|
|
24
|
+
*
|
|
25
|
+
* Gateway fuel: mirrors claude-cli-harness.ts — when the dev has none of
|
|
26
|
+
* ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, or
|
|
27
|
+
* ANTHROPIC_BASE_URL (the corporate-gateway/custom-endpoint path) but
|
|
28
|
+
* VENDO_API_KEY is set, the child runs against Vendo Cloud's model gateway
|
|
29
|
+
* instead of degrading to unavailable (see gateway-fuel.ts). Own credential
|
|
30
|
+
* always wins — availability() must label these honestly (not as "Vendo
|
|
31
|
+
* Cloud key") since composeGatewayFuel itself refuses to overlay onto any of
|
|
32
|
+
* them; a wrong label here would make the consent prompt lie about what
|
|
33
|
+
* run() actually does.
|
|
34
|
+
*/
|
|
35
|
+
export const ENGINE_PACKAGE_NAME = "@vendoai/engine";
|
|
36
|
+
export const ENGINE_PACKAGE_VERSION = "0.1.0";
|
|
37
|
+
// A one-time ~250MB package fetch plus the extraction stages themselves (which
|
|
38
|
+
// can already run for minutes over a real codebase on the other rungs) needs
|
|
39
|
+
// more headroom than the PATH-binary rungs' 10-minute budget.
|
|
40
|
+
const RUN_TIMEOUT_MS = 15 * 60 * 1_000;
|
|
41
|
+
const MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
42
|
+
/** Pure mapping from a completed `execFile` callback into an ExecResult —
|
|
43
|
+
* pulled out of the real spawn so the four ways `error` shows up (a normal
|
|
44
|
+
* nonzero exit, a real spawn-layer failure, the RUN_TIMEOUT_MS kill, or the
|
|
45
|
+
* MAX_BUFFER_BYTES kill) each get direct unit coverage without actually
|
|
46
|
+
* spawning anything (mirrors resolveCodexExecResult in
|
|
47
|
+
* codex-cli-harness.ts). The four are easy to conflate — all leave
|
|
48
|
+
* `error.code` non-numeric — but they need different messages: a killed
|
|
49
|
+
* 15-minute extraction run is not npm being uninstalled, and a >10MB
|
|
50
|
+
* narration stream is not a timeout. */
|
|
51
|
+
export function resolveNpmExecResult(error, stdout, stderr) {
|
|
52
|
+
if (error === null)
|
|
53
|
+
return { stdout, stderr, code: 0 };
|
|
54
|
+
if (typeof error.code === "number")
|
|
55
|
+
return { stdout, stderr, code: error.code };
|
|
56
|
+
if (error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") {
|
|
57
|
+
// execFile's `maxBuffer` kill also sets error.killed, so this must be
|
|
58
|
+
// checked BEFORE the killed branch below — a >10MB narration stream is
|
|
59
|
+
// not the 15-minute timeout, and labeling it as one would send the dev
|
|
60
|
+
// chasing a slowness that isn't there.
|
|
61
|
+
const megabytes = Math.round(MAX_BUFFER_BYTES / (1024 * 1024));
|
|
62
|
+
return {
|
|
63
|
+
stdout: "",
|
|
64
|
+
stderr: `npm exec output exceeded the ${megabytes}MB buffer — the engine's narration stream was `
|
|
65
|
+
+ "larger than expected, so the child was killed before finishing",
|
|
66
|
+
code: 1,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (error.killed === true) {
|
|
70
|
+
// execFile's `timeout` option SIGTERMs a still-running child — a
|
|
71
|
+
// legitimate long extraction run, not a broken npm install. This must be
|
|
72
|
+
// checked before the spawn-failure branch below: a kill leaves
|
|
73
|
+
// error.code null (not a string), so it would otherwise fall into "npm
|
|
74
|
+
// could not be launched" and mislabel the dev's own long-running job as
|
|
75
|
+
// a missing npm.
|
|
76
|
+
const minutes = Math.round(RUN_TIMEOUT_MS / 60_000);
|
|
77
|
+
return {
|
|
78
|
+
stdout: "",
|
|
79
|
+
stderr: `npm exec was killed for exceeding the ${minutes}-minute timeout`
|
|
80
|
+
+ (error.signal !== undefined ? ` (${error.signal})` : ""),
|
|
81
|
+
code: 1,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
if (typeof error.code === "string") {
|
|
85
|
+
// A real spawn-layer failure (error.code is an errno string like
|
|
86
|
+
// "ENOENT" or "EACCES", set when the OS never launched the child at
|
|
87
|
+
// all) — npm itself could not be launched: not installed, not on PATH,
|
|
88
|
+
// or not executable. execFile's own stdout/stderr are empty in this
|
|
89
|
+
// case, so the actionable detail has to come from error.message.
|
|
90
|
+
return {
|
|
91
|
+
stdout: "",
|
|
92
|
+
stderr: `npm could not be launched (${error.message}) — is npm installed and on PATH?`,
|
|
93
|
+
code: 1,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// Any other shape (no numeric exit code, not killed, no string spawn
|
|
97
|
+
// code) — don't fabricate a diagnosis; forward whatever npm itself wrote.
|
|
98
|
+
// An offline registry lands here: npm still launches and exits non-zero
|
|
99
|
+
// with its own descriptive stderr (e.g. "npm error code ENOTFOUND"),
|
|
100
|
+
// which run()'s nonzero-exit handling forwards to the dev verbatim.
|
|
101
|
+
return { stdout, stderr, code: 1 };
|
|
102
|
+
}
|
|
103
|
+
/** Line-buffered splitter for a live-streaming descriptor, extracted out of
|
|
104
|
+
* execNpmEngine so it's directly unit-testable without a real child
|
|
105
|
+
* process: a line split across two `data` chunks, and — the bug this
|
|
106
|
+
* fixes — a trailing line with no final newline, which `flush()` still
|
|
107
|
+
* delivers instead of silently dropping the child's last progress line
|
|
108
|
+
* when the stream ends without one. */
|
|
109
|
+
export function createLineSplitter(onLine) {
|
|
110
|
+
let buffer = "";
|
|
111
|
+
return {
|
|
112
|
+
push(chunk) {
|
|
113
|
+
buffer += chunk;
|
|
114
|
+
let index = buffer.indexOf("\n");
|
|
115
|
+
while (index !== -1) {
|
|
116
|
+
const line = buffer.slice(0, index);
|
|
117
|
+
buffer = buffer.slice(index + 1);
|
|
118
|
+
if (line.length > 0)
|
|
119
|
+
onLine(line);
|
|
120
|
+
index = buffer.indexOf("\n");
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
flush() {
|
|
124
|
+
if (buffer.length > 0)
|
|
125
|
+
onLine(buffer);
|
|
126
|
+
buffer = "";
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function execNpmEngine(args, options) {
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
// stderr is progress/narration per the child protocol — forward it line
|
|
133
|
+
// by line as it streams in, not just once the process exits; flush()
|
|
134
|
+
// in the callback below delivers any trailing partial line too.
|
|
135
|
+
const splitter = createLineSplitter((line) => options.onStderrLine?.(line));
|
|
136
|
+
const child = execFile("npm", args, { cwd: options.cwd, env: options.env, timeout: RUN_TIMEOUT_MS, maxBuffer: MAX_BUFFER_BYTES }, (error, stdout, stderr) => {
|
|
137
|
+
splitter.flush();
|
|
138
|
+
resolve(resolveNpmExecResult(error, stdout, stderr));
|
|
139
|
+
});
|
|
140
|
+
child.stderr?.on("data", (chunk) => splitter.push(chunk.toString()));
|
|
141
|
+
// If the child exits (or is killed by the timeout) before the stdin
|
|
142
|
+
// write drains, Node emits an unhandled EPIPE 'error' on this stream —
|
|
143
|
+
// with no listener, that's an uncaughtException that aborts all of
|
|
144
|
+
// `vendo init`, bypassing extraction.ts's graceful per-rung degradation.
|
|
145
|
+
// The real outcome always arrives via the execFile callback's exit code
|
|
146
|
+
// + stderr above, so this handler is intentionally a no-op.
|
|
147
|
+
child.stdin?.on("error", () => { });
|
|
148
|
+
child.stdin?.write(options.input);
|
|
149
|
+
child.stdin?.end();
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function isSet(value) {
|
|
153
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
154
|
+
}
|
|
155
|
+
// Every label this rung can return carries the download disclosure, since
|
|
156
|
+
// (unlike the PATH-binary rungs) a fetch always happens here.
|
|
157
|
+
const DOWNLOAD_NOTE = "via the Vendo engine, ~250MB one-time download";
|
|
158
|
+
const DOWNLOAD_SUFFIX = ` (${DOWNLOAD_NOTE})`;
|
|
159
|
+
export function npxEngineHarness(options = {}) {
|
|
160
|
+
const exec = options.exec ?? execNpmEngine;
|
|
161
|
+
return {
|
|
162
|
+
id: "npx-engine",
|
|
163
|
+
async availability({ env }) {
|
|
164
|
+
// The child spawns with {...process.env, ...input.env} (see run()), so
|
|
165
|
+
// the label must be judged against that SAME merged view — an ambient
|
|
166
|
+
// (process.env) own credential is the one the child would actually
|
|
167
|
+
// use, even when the caller's partial env carries only VENDO_API_KEY.
|
|
168
|
+
const merged = { ...process.env, ...env };
|
|
169
|
+
if (isSet(merged["ANTHROPIC_API_KEY"]))
|
|
170
|
+
return `your ANTHROPIC_API_KEY${DOWNLOAD_SUFFIX}`;
|
|
171
|
+
// The corporate-gateway/custom-endpoint env vars are an own credential
|
|
172
|
+
// too (see gateway-fuel.ts's INVARIANT) — composeGatewayFuel refuses to
|
|
173
|
+
// overlay onto them regardless of what run() passes, so labeling this
|
|
174
|
+
// rung "your Vendo Cloud key" here would be a lie about what actually
|
|
175
|
+
// runs. Per-var labels (same priority order as claude-cli-harness.ts)
|
|
176
|
+
// so the consent line names the credential that's really in play.
|
|
177
|
+
if (hasOwnAnthropicEnvOverride(merged)) {
|
|
178
|
+
if (isSet(merged["ANTHROPIC_AUTH_TOKEN"]))
|
|
179
|
+
return `your ANTHROPIC_AUTH_TOKEN${DOWNLOAD_SUFFIX}`;
|
|
180
|
+
if (isSet(merged["CLAUDE_CODE_OAUTH_TOKEN"]))
|
|
181
|
+
return `your CLAUDE_CODE_OAUTH_TOKEN${DOWNLOAD_SUFFIX}`;
|
|
182
|
+
return `your ANTHROPIC_BASE_URL${DOWNLOAD_SUFFIX}`;
|
|
183
|
+
}
|
|
184
|
+
if (isSet(merged["VENDO_API_KEY"])) {
|
|
185
|
+
return `your Vendo Cloud key (managed inference, ${DOWNLOAD_NOTE})`;
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
188
|
+
},
|
|
189
|
+
async run(input) {
|
|
190
|
+
// Merge FIRST, then guard — the child is spawned with the caller's env
|
|
191
|
+
// over process.env, so the own-credential verdict and composeGatewayFuel
|
|
192
|
+
// must evaluate that same merged env. Guarding input.env alone would
|
|
193
|
+
// let the overlay clobber an ambient (process.env) BYO endpoint the
|
|
194
|
+
// child would otherwise have used. composeGatewayFuel already refuses
|
|
195
|
+
// to overlay onto these env vars on its own (defense in depth) —
|
|
196
|
+
// computed explicitly here too so this rung's own-credential verdict
|
|
197
|
+
// matches availability()'s exactly, the same belt-and-suspenders style
|
|
198
|
+
// as claude-cli-harness.ts.
|
|
199
|
+
const merged = { ...process.env, ...input.env };
|
|
200
|
+
const hasOwnKey = isSet(merged["ANTHROPIC_API_KEY"]) || hasOwnAnthropicEnvOverride(merged);
|
|
201
|
+
const overlay = composeGatewayFuel({ env: merged, ownCredentialAvailable: hasOwnKey });
|
|
202
|
+
// Visible-never-silent: the ~250MB fetch is a real surprise on a
|
|
203
|
+
// machine with nothing installed, so it's disclosed up front, before
|
|
204
|
+
// the child (and its network access) ever starts.
|
|
205
|
+
input.onProgress?.(`Fetching ${ENGINE_PACKAGE_NAME}@${ENGINE_PACKAGE_VERSION} via npm exec (~250MB one-time download; `
|
|
206
|
+
+ "npm caches it locally, so later runs skip the download)…");
|
|
207
|
+
const job = JSON.stringify({ instructions: input.instructions, root: input.root });
|
|
208
|
+
const args = ["exec", "--yes", `${ENGINE_PACKAGE_NAME}@${ENGINE_PACKAGE_VERSION}`, "--", "run"];
|
|
209
|
+
// Forward the caller's env so a key present only in the passed map
|
|
210
|
+
// (not process.env) still authenticates the child; gateway fuel (if
|
|
211
|
+
// applicable) wins last — mirrors claude-cli-harness.ts.
|
|
212
|
+
const result = await exec(args, {
|
|
213
|
+
cwd: input.root,
|
|
214
|
+
env: { ...merged, ...overlay },
|
|
215
|
+
input: job,
|
|
216
|
+
onStderrLine: input.onProgress,
|
|
217
|
+
});
|
|
218
|
+
if (result.code !== 0) {
|
|
219
|
+
throw new Error(`npm exec ${ENGINE_PACKAGE_NAME}@${ENGINE_PACKAGE_VERSION} exited with code ${result.code}: `
|
|
220
|
+
+ `${result.stderr.trim() || "(no stderr)"}`);
|
|
221
|
+
}
|
|
222
|
+
return result.stdout;
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type ExtractionDraft, type ExtractionHarness } from "./harness.js";
|
|
3
|
+
import { modelThemeSchema, type ThemeSlotValues } from "../theme/extract-theme.js";
|
|
4
|
+
/**
|
|
5
|
+
* The staged extraction pipeline (install-dx, PostHog lesson: narrow stages
|
|
6
|
+
* beat one-shot). Four required passes plus one optional pass, all over the
|
|
7
|
+
* SAME harness seam — each is one `harness.run(instructions)` with its own
|
|
8
|
+
* narrow instructions and its own zod-validated artifact, written to
|
|
9
|
+
* `.vendo/data/extract/<stage>.json` so a failed run is diagnosable stage by
|
|
10
|
+
* stage:
|
|
11
|
+
*
|
|
12
|
+
* 1. survey — map the repo: frameworks, where the API surfaces live, and a
|
|
13
|
+
* grouping of the static tool list into surfaces (cheap/fast; respects a
|
|
14
|
+
* VENDO_EXTRACTION_SURVEY_MODEL override).
|
|
15
|
+
* 2. draft — one focused pass per surface, drafting judgment for just that
|
|
16
|
+
* surface's tools. Surfaces run sequentially (rate-limit friendly); a
|
|
17
|
+
* failed surface is SKIPPED with an honest note, never aborting the run.
|
|
18
|
+
* 3. cross-check — one pass over the combined draft for naming consistency,
|
|
19
|
+
* duplicate coverage, and risk sanity. It may only AMEND entries within
|
|
20
|
+
* the same schema (unknown names ignored, omitted entries stand); a
|
|
21
|
+
* failure degrades to the uncross-checked drafts.
|
|
22
|
+
* 4. brief — drafted from what the survey and drafts learned; a failure
|
|
23
|
+
* keeps the current brief.
|
|
24
|
+
* 5. theme — OPTIONAL, only when the caller supplies `theme` input and the
|
|
25
|
+
* allowlist left brand slots unfilled: fills those slots the same way the
|
|
26
|
+
* old theme model pass did, but over this harness seam (Read/Glob/Grep the
|
|
27
|
+
* repo instead of a fixed evidence-file set). A failure degrades to a note
|
|
28
|
+
* — the exact reads and neutral defaults still stand.
|
|
29
|
+
*
|
|
30
|
+
* The combined output feeds the EXISTING applyDraft guards in extraction.ts
|
|
31
|
+
* unchanged — deterministic verification stays the single gate. Nothing here
|
|
32
|
+
* assumes a vendor; the model override rides VENDO_EXTRACTION_MODEL, which
|
|
33
|
+
* every harness already honors.
|
|
34
|
+
*/
|
|
35
|
+
export declare const BRIEF_TEMPLATE = "Describe this product, its users, and the jobs the agent should help them complete.";
|
|
36
|
+
/** Static facts don't clutter passes that only need names — kept small. */
|
|
37
|
+
export declare const staticToolSchema: z.ZodObject<{
|
|
38
|
+
name: z.ZodString;
|
|
39
|
+
description: z.ZodOptional<z.ZodString>;
|
|
40
|
+
risk: z.ZodOptional<z.ZodEnum<["read", "write", "destructive"]>>;
|
|
41
|
+
disabled: z.ZodOptional<z.ZodBoolean>;
|
|
42
|
+
method: z.ZodOptional<z.ZodString>;
|
|
43
|
+
path: z.ZodOptional<z.ZodString>;
|
|
44
|
+
}, "strip", z.ZodTypeAny, {
|
|
45
|
+
name: string;
|
|
46
|
+
path?: string | undefined;
|
|
47
|
+
method?: string | undefined;
|
|
48
|
+
risk?: "read" | "write" | "destructive" | undefined;
|
|
49
|
+
description?: string | undefined;
|
|
50
|
+
disabled?: boolean | undefined;
|
|
51
|
+
}, {
|
|
52
|
+
name: string;
|
|
53
|
+
path?: string | undefined;
|
|
54
|
+
method?: string | undefined;
|
|
55
|
+
risk?: "read" | "write" | "destructive" | undefined;
|
|
56
|
+
description?: string | undefined;
|
|
57
|
+
disabled?: boolean | undefined;
|
|
58
|
+
}>;
|
|
59
|
+
export type StaticTool = z.infer<typeof staticToolSchema>;
|
|
60
|
+
export declare const surveySchema: z.ZodObject<{
|
|
61
|
+
/** Frameworks/routers the surveyor identified (context for later stages). */
|
|
62
|
+
frameworks: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
63
|
+
surfaces: z.ZodArray<z.ZodObject<{
|
|
64
|
+
name: z.ZodString;
|
|
65
|
+
/** One line on where the surface lives in the code. */
|
|
66
|
+
note: z.ZodOptional<z.ZodString>;
|
|
67
|
+
tools: z.ZodArray<z.ZodString, "many">;
|
|
68
|
+
}, "strip", z.ZodTypeAny, {
|
|
69
|
+
tools: string[];
|
|
70
|
+
name: string;
|
|
71
|
+
note?: string | undefined;
|
|
72
|
+
}, {
|
|
73
|
+
tools: string[];
|
|
74
|
+
name: string;
|
|
75
|
+
note?: string | undefined;
|
|
76
|
+
}>, "many">;
|
|
77
|
+
}, "strip", z.ZodTypeAny, {
|
|
78
|
+
surfaces: {
|
|
79
|
+
tools: string[];
|
|
80
|
+
name: string;
|
|
81
|
+
note?: string | undefined;
|
|
82
|
+
}[];
|
|
83
|
+
frameworks?: string[] | undefined;
|
|
84
|
+
}, {
|
|
85
|
+
surfaces: {
|
|
86
|
+
tools: string[];
|
|
87
|
+
name: string;
|
|
88
|
+
note?: string | undefined;
|
|
89
|
+
}[];
|
|
90
|
+
frameworks?: string[] | undefined;
|
|
91
|
+
}>;
|
|
92
|
+
export type Survey = z.infer<typeof surveySchema>;
|
|
93
|
+
export declare function staticFacts(tools: StaticTool[]): string;
|
|
94
|
+
export declare function composeInstructions(tools: StaticTool[], appName: string, surface?: {
|
|
95
|
+
name: string;
|
|
96
|
+
note?: string;
|
|
97
|
+
}): string;
|
|
98
|
+
/**
|
|
99
|
+
* Instructions for the OPTIONAL theme stage — the SOLE copy of the theme
|
|
100
|
+
* judgment rules (extract-theme.ts's old model pass and its prompt were
|
|
101
|
+
* deleted when the deterministic split landed; that file now owns only the
|
|
102
|
+
* exact pass, validators, and assembly).
|
|
103
|
+
*/
|
|
104
|
+
export declare function composeThemeInstructions(input: {
|
|
105
|
+
needed: string[];
|
|
106
|
+
alreadyExact: Record<string, string>;
|
|
107
|
+
evidencePaths: string[];
|
|
108
|
+
appName: string;
|
|
109
|
+
}): string;
|
|
110
|
+
interface NormalizedSurface {
|
|
111
|
+
name: string;
|
|
112
|
+
slug: string;
|
|
113
|
+
note?: string;
|
|
114
|
+
tools: StaticTool[];
|
|
115
|
+
}
|
|
116
|
+
/** Deterministic normalization of the survey grouping: unknown names are
|
|
117
|
+
* dropped, a tool claimed twice keeps its first surface, unassigned tools
|
|
118
|
+
* land in a catch-all pass, and a runaway surface count is merged down. The
|
|
119
|
+
* drafting stage always covers EVERY static tool exactly once. */
|
|
120
|
+
export declare function normalizeSurfaces(survey: Survey | null, tools: StaticTool[], notes: string[]): NormalizedSurface[];
|
|
121
|
+
export interface StagedExtractionInput {
|
|
122
|
+
root: string;
|
|
123
|
+
env: Record<string, string | undefined>;
|
|
124
|
+
harness: ExtractionHarness;
|
|
125
|
+
tools: StaticTool[];
|
|
126
|
+
appName: string;
|
|
127
|
+
onProgress?: (line: string) => void;
|
|
128
|
+
/** Optional theme stage input — omitted entirely when the caller has no
|
|
129
|
+
* theme extraction to do (e.g. init running without a theme pass). */
|
|
130
|
+
theme?: {
|
|
131
|
+
needed: Array<keyof ThemeSlotValues>;
|
|
132
|
+
alreadyExact: Record<string, string>;
|
|
133
|
+
evidencePaths: string[];
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
export interface StagedExtractionResult {
|
|
137
|
+
draft: ExtractionDraft;
|
|
138
|
+
/** Honest degradation notes (skipped surfaces, failed cross-check, …). */
|
|
139
|
+
notes: string[];
|
|
140
|
+
/** false = the brief stage failed and draft.brief is the pre-existing one. */
|
|
141
|
+
briefFromStage: boolean;
|
|
142
|
+
/** Present only when the theme stage ran and succeeded. */
|
|
143
|
+
theme?: z.infer<typeof modelThemeSchema>;
|
|
144
|
+
}
|
|
145
|
+
/** Orchestrate the staged pipeline. Throws only when no stage produced
|
|
146
|
+
* anything usable (the error names the failed stage); partial failures
|
|
147
|
+
* degrade with notes instead. */
|
|
148
|
+
export declare function runStagedExtraction(input: StagedExtractionInput): Promise<StagedExtractionResult>;
|
|
149
|
+
export {};
|