@llm4ts/shell 0.2.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/dist/Cli.d.ts +33 -0
- package/dist/Cli.d.ts.map +1 -0
- package/dist/Cli.js +148 -0
- package/dist/Cli.js.map +1 -0
- package/dist/CoderChoice.d.ts +18 -0
- package/dist/CoderChoice.d.ts.map +1 -0
- package/dist/CoderChoice.js +41 -0
- package/dist/CoderChoice.js.map +1 -0
- package/dist/FlowCatalog.d.ts +37 -0
- package/dist/FlowCatalog.d.ts.map +1 -0
- package/dist/FlowCatalog.js +86 -0
- package/dist/FlowCatalog.js.map +1 -0
- package/dist/FlowLaunch.d.ts +36 -0
- package/dist/FlowLaunch.d.ts.map +1 -0
- package/dist/FlowLaunch.js +86 -0
- package/dist/FlowLaunch.js.map +1 -0
- package/dist/Menu.d.ts +9 -0
- package/dist/Menu.d.ts.map +1 -0
- package/dist/Menu.js +101 -0
- package/dist/Menu.js.map +1 -0
- package/dist/Package.d.ts +3 -0
- package/dist/Package.d.ts.map +1 -0
- package/dist/Package.js +7 -0
- package/dist/Package.js.map +1 -0
- package/dist/ResolveFallback.d.ts +2 -0
- package/dist/ResolveFallback.d.ts.map +1 -0
- package/dist/ResolveFallback.js +29 -0
- package/dist/ResolveFallback.js.map +1 -0
- package/dist/cli-main.d.ts +3 -0
- package/dist/cli-main.d.ts.map +1 -0
- package/dist/cli-main.js +39 -0
- package/dist/cli-main.js.map +1 -0
- package/flows/implement.ts +38 -0
- package/flows/issue-pr.ts +108 -0
- package/flows/judge-suite.ts +63 -0
- package/flows/local.ts +59 -0
- package/flows/sdd.ts +181 -0
- package/package.json +66 -0
- package/src/Cli.ts +230 -0
- package/src/CoderChoice.ts +58 -0
- package/src/FlowCatalog.ts +119 -0
- package/src/FlowLaunch.ts +99 -0
- package/src/Menu.ts +123 -0
- package/src/Package.ts +11 -0
- package/src/ResolveFallback.ts +31 -0
- package/src/cli-main.ts +48 -0
package/dist/Menu.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as Console from "effect/Console";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import { FileSystem } from "effect/FileSystem";
|
|
4
|
+
import * as Prompt from "effect/unstable/cli/Prompt";
|
|
5
|
+
import { coderChoices, findOnPath, resolvedCoderToken } from "./CoderChoice.js";
|
|
6
|
+
import { discoverFlows } from "./FlowCatalog.js";
|
|
7
|
+
import { launchFlow } from "./FlowLaunch.js";
|
|
8
|
+
const flowChoice = (flow) => {
|
|
9
|
+
const shadows = flow.shadows.length === 0 ? "" : ` (shadows ${flow.shadows.join(", ")})`;
|
|
10
|
+
return {
|
|
11
|
+
title: `${flow.name} [${flow.tier}]${shadows}`,
|
|
12
|
+
value: flow,
|
|
13
|
+
...(flow.description === undefined ? {} : { description: flow.description })
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
const selectFlow = Effect.fn("@llm4ts/shell/Menu.selectFlow")(function* (tiers) {
|
|
17
|
+
const flows = yield* discoverFlows(tiers);
|
|
18
|
+
if (flows.length === 0) {
|
|
19
|
+
yield* Console.error("no flows discovered — add scripts under .llm4ts/flows/");
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return yield* Prompt.run(Prompt.select({
|
|
23
|
+
message: "Which flow?",
|
|
24
|
+
choices: flows.map(flowChoice)
|
|
25
|
+
}));
|
|
26
|
+
});
|
|
27
|
+
const selectCoder = Effect.fn("@llm4ts/shell/Menu.selectCoder")(function* (environment) {
|
|
28
|
+
const resolved = resolvedCoderToken(environment);
|
|
29
|
+
const choices = [
|
|
30
|
+
{
|
|
31
|
+
title: `environment default (${resolved})`,
|
|
32
|
+
value: undefined,
|
|
33
|
+
description: "Use LLM4TS_CODER as-is"
|
|
34
|
+
},
|
|
35
|
+
...coderChoices.map((choice) => ({
|
|
36
|
+
title: findOnPath(choice.executable, environment) === undefined
|
|
37
|
+
? choice.token
|
|
38
|
+
: `${choice.token} ✓ found`,
|
|
39
|
+
value: choice.token
|
|
40
|
+
}))
|
|
41
|
+
];
|
|
42
|
+
return yield* Prompt.run(Prompt.select({ message: "Which coding agent?", choices }));
|
|
43
|
+
});
|
|
44
|
+
const runFlowInteractive = Effect.fn("@llm4ts/shell/Menu.runFlowInteractive")(function* (tiers) {
|
|
45
|
+
const flow = yield* selectFlow(tiers);
|
|
46
|
+
if (flow === undefined) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (flow.description !== undefined) {
|
|
50
|
+
yield* Console.log(`${flow.name}: ${flow.description}`);
|
|
51
|
+
}
|
|
52
|
+
const task = yield* Prompt.run(Prompt.text({ message: "Task text (empty uses the flow's default)" }));
|
|
53
|
+
const coder = yield* selectCoder(process.env);
|
|
54
|
+
const exitCode = yield* launchFlow({
|
|
55
|
+
flowPath: flow.path,
|
|
56
|
+
taskArgs: task.trim().length === 0 ? [] : [task],
|
|
57
|
+
...(coder === undefined ? {} : { coder })
|
|
58
|
+
});
|
|
59
|
+
yield* exitCode === 0
|
|
60
|
+
? Console.log(`${flow.name} completed`)
|
|
61
|
+
: Console.error(`${flow.name} exited with code ${exitCode}`);
|
|
62
|
+
});
|
|
63
|
+
const viewFlowInteractive = Effect.fn("@llm4ts/shell/Menu.viewFlowInteractive")(function* (tiers) {
|
|
64
|
+
const flow = yield* selectFlow(tiers);
|
|
65
|
+
if (flow === undefined) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const fs = yield* FileSystem;
|
|
69
|
+
const source = yield* fs.readFileString(flow.path).pipe(Effect.orElseSucceed(() => ""));
|
|
70
|
+
yield* Console.log(source.trimEnd());
|
|
71
|
+
});
|
|
72
|
+
/**
|
|
73
|
+
* The interactive main menu: run a flow, view a flow, exit. Ctrl-C inside a
|
|
74
|
+
* prompt quits the menu cleanly rather than crashing the shell.
|
|
75
|
+
*/
|
|
76
|
+
export const mainMenu = Effect.fn("@llm4ts/shell/Menu.mainMenu")(function* (tiers) {
|
|
77
|
+
while (true) {
|
|
78
|
+
const action = yield* Prompt.run(Prompt.select({
|
|
79
|
+
message: "llm4ts shell",
|
|
80
|
+
choices: [
|
|
81
|
+
{ title: "Run a flow", value: "run" },
|
|
82
|
+
{ title: "View a flow", value: "view" },
|
|
83
|
+
{ title: "Exit", value: "exit" }
|
|
84
|
+
]
|
|
85
|
+
})).pipe(Effect.catchTag("QuitError", () => Effect.succeed("exit")));
|
|
86
|
+
switch (action) {
|
|
87
|
+
case "run": {
|
|
88
|
+
yield* runFlowInteractive(tiers).pipe(Effect.catchTag("QuitError", () => Effect.void));
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case "view": {
|
|
92
|
+
yield* viewFlowInteractive(tiers).pipe(Effect.catchTag("QuitError", () => Effect.void));
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
case "exit": {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
//# sourceMappingURL=Menu.js.map
|
package/dist/Menu.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menu.js","sourceRoot":"","sources":["../src/Menu.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Package.d.ts","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,WAAW,kBAAkB,CAAA;AAC1C,eAAO,MAAM,cAAc,EAAE,MAAyB,CAAA"}
|
package/dist/Package.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
const Manifest = Schema.Struct({ version: Schema.String });
|
|
4
|
+
const manifest = Schema.decodeUnknownSync(Manifest)(createRequire(import.meta.url)("../package.json"));
|
|
5
|
+
export const packageName = "@llm4ts/shell";
|
|
6
|
+
export const packageVersion = manifest.version;
|
|
7
|
+
//# sourceMappingURL=Package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Package.js","sourceRoot":"","sources":["../src/Package.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAA;AAE1D,MAAM,QAAQ,GAAG,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CACjD,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAC,CAClD,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAA;AAC1C,MAAM,CAAC,MAAM,cAAc,GAAW,QAAQ,CAAC,OAAO,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResolveFallback.d.ts","sourceRoot":"","sources":["../src/ResolveFallback.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-resolution fallback registered in flow child processes via
|
|
3
|
+
* `--import`. Resolution is tried normally first, so a project-local
|
|
4
|
+
* `node_modules` with its own `@llm4ts/*` pin always wins; only when a bare
|
|
5
|
+
* specifier fails to resolve from the flow's own location is it retried
|
|
6
|
+
* anchored at this file — the shell's installation, whose dependencies
|
|
7
|
+
* include `@llm4ts/*` and `effect`. See ADR 0006.
|
|
8
|
+
*/
|
|
9
|
+
import { registerHooks } from "node:module";
|
|
10
|
+
const anchor = import.meta.url;
|
|
11
|
+
const isBareSpecifier = (specifier) => !specifier.startsWith(".") &&
|
|
12
|
+
!specifier.startsWith("/") &&
|
|
13
|
+
!specifier.startsWith("file:") &&
|
|
14
|
+
!specifier.startsWith("node:") &&
|
|
15
|
+
!specifier.startsWith("data:");
|
|
16
|
+
registerHooks({
|
|
17
|
+
resolve(specifier, context, nextResolve) {
|
|
18
|
+
try {
|
|
19
|
+
return nextResolve(specifier, context);
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (isBareSpecifier(specifier) && context.parentURL !== anchor) {
|
|
23
|
+
return nextResolve(specifier, { ...context, parentURL: anchor });
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=ResolveFallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResolveFallback.js","sourceRoot":"","sources":["../src/ResolveFallback.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAA;AAE9B,MAAM,eAAe,GAAG,CAAC,SAAiB,EAAW,EAAE,CACrD,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;IAC1B,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC;IAC1B,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC;IAC9B,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC;IAC9B,CAAC,SAAS,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;AAEhC,aAAa,CAAC;IACZ,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,WAAW;QACrC,IAAI,CAAC;YACH,OAAO,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,eAAe,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;gBAC/D,OAAO,WAAW,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAA;YAClE,CAAC;YACD,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;CACF,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-main.d.ts","sourceRoot":"","sources":["../src/cli-main.ts"],"names":[],"mappings":""}
|
package/dist/cli-main.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as NodeServices from "@effect/platform-node/NodeServices";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import { runShellCommand } from "./Cli.js";
|
|
5
|
+
const exitCodeFor = (error) => {
|
|
6
|
+
if (typeof error === "object" && error !== null && "_tag" in error) {
|
|
7
|
+
switch (error._tag) {
|
|
8
|
+
case "ShellUsage":
|
|
9
|
+
case "ShowHelp":
|
|
10
|
+
return 2;
|
|
11
|
+
default:
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return 1;
|
|
16
|
+
};
|
|
17
|
+
const errorMessage = (error) => {
|
|
18
|
+
if (typeof error === "object" && error !== null && "_tag" in error) {
|
|
19
|
+
if (error._tag === "ShowHelp") {
|
|
20
|
+
// Help (with any parse errors) was already rendered by the command runner.
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
if ("message" in error && typeof error.message === "string") {
|
|
24
|
+
return error.message;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return String(error);
|
|
28
|
+
};
|
|
29
|
+
Effect.runFork(runShellCommand(process.argv.slice(2)).pipe(Effect.match({
|
|
30
|
+
onFailure: (error) => Effect.sync(() => {
|
|
31
|
+
const message = errorMessage(error);
|
|
32
|
+
if (message !== undefined) {
|
|
33
|
+
process.stderr.write(`llm4ts: ${message}\n`);
|
|
34
|
+
}
|
|
35
|
+
process.exitCode = exitCodeFor(error);
|
|
36
|
+
}),
|
|
37
|
+
onSuccess: () => Effect.void
|
|
38
|
+
}), Effect.flatten, Effect.provide(NodeServices.layer)));
|
|
39
|
+
//# sourceMappingURL=cli-main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-main.js","sourceRoot":"","sources":["../src/cli-main.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,YAAY,MAAM,oCAAoC,CAAA;AAClE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE1C,MAAM,WAAW,GAAG,CAAC,KAAc,EAAU,EAAE;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;QACnE,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,YAAY,CAAC;YAClB,KAAK,UAAU;gBACb,OAAO,CAAC,CAAA;YACV;gBACE,OAAO,CAAC,CAAA;QACZ,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC,CAAA;AAED,MAAM,YAAY,GAAG,CAAC,KAAc,EAAsB,EAAE;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;QACnE,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC9B,2EAA2E;YAC3E,OAAO,SAAS,CAAA;QAClB,CAAC;QACD,IAAI,SAAS,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC5D,OAAO,KAAK,CAAC,OAAO,CAAA;QACtB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC,CAAA;AAED,MAAM,CAAC,OAAO,CACZ,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACzC,MAAM,CAAC,KAAK,CAAC;IACX,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CACnB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACf,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;QACnC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,OAAO,IAAI,CAAC,CAAA;QAC9C,CAAC;QACD,OAAO,CAAC,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAA;IACvC,CAAC,CAAC;IACJ,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI;CAC7B,CAAC,EACF,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CACnC,CACF,CAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Persistent plan: plan the task, then implement, review, and commit one task at a time.
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import * as Effect from "effect/Effect"
|
|
4
|
+
import { implementPlanFlow } from "@llm4ts/flow/Flow"
|
|
5
|
+
import { defaultPlanPath } from "@llm4ts/flow/Plan"
|
|
6
|
+
import { planFrom } from "@llm4ts/flow/Planner"
|
|
7
|
+
import { makePlanStore } from "@llm4ts/flow/Persistence"
|
|
8
|
+
import { coderFromEnv } from "@llm4ts/runner/Connectors"
|
|
9
|
+
import { resolveFlowInput } from "@llm4ts/runner/FlowArgs"
|
|
10
|
+
import { runFlowMain, runNode } from "@llm4ts/runner/FlowRunner"
|
|
11
|
+
import { nodePlainFileStore } from "@llm4ts/runner/NodePlainFileStore"
|
|
12
|
+
|
|
13
|
+
const program = Effect.gen(function* () {
|
|
14
|
+
const input = yield* resolveFlowInput(
|
|
15
|
+
"Add a multiply function to the calculator, including focused tests."
|
|
16
|
+
)
|
|
17
|
+
const planPath = join(input.workDir, defaultPlanPath(input.prompt))
|
|
18
|
+
const store = makePlanStore(nodePlainFileStore)
|
|
19
|
+
|
|
20
|
+
yield* runNode(
|
|
21
|
+
{
|
|
22
|
+
workDir: input.workDir,
|
|
23
|
+
workspace: input.workspace,
|
|
24
|
+
userPrompt: input.prompt,
|
|
25
|
+
coder: coderFromEnv(process.env),
|
|
26
|
+
environment: process.env
|
|
27
|
+
},
|
|
28
|
+
(context) =>
|
|
29
|
+
implementPlanFlow(context, {
|
|
30
|
+
store,
|
|
31
|
+
planPath,
|
|
32
|
+
plan: planFrom(context.reasoning, input.prompt),
|
|
33
|
+
system: "Implement one task at a time in the current repository."
|
|
34
|
+
})
|
|
35
|
+
)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
runFlowMain(program)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// GitHub issue to pull request: assess the issue, plan, implement, push, and open a PR.
|
|
2
|
+
import { join } from "node:path"
|
|
3
|
+
import * as Effect from "effect/Effect"
|
|
4
|
+
import { implementPlanFlow } from "@llm4ts/flow/Flow"
|
|
5
|
+
import { FlowAborted } from "@llm4ts/flow/FlowError"
|
|
6
|
+
import { parseIssueRef } from "@llm4ts/flow/GitHubTool"
|
|
7
|
+
import { stage } from "@llm4ts/flow/PlanExecution"
|
|
8
|
+
import { assessThenPlan } from "@llm4ts/flow/Planner"
|
|
9
|
+
import { makePlanStore } from "@llm4ts/flow/Persistence"
|
|
10
|
+
import { summarisePr } from "@llm4ts/flow/PrSummary"
|
|
11
|
+
import { allReviewers } from "@llm4ts/flow/Review"
|
|
12
|
+
import { ScriptUsage, resolveFlowInput } from "@llm4ts/runner/FlowArgs"
|
|
13
|
+
import { coderFromEnv } from "@llm4ts/runner/Connectors"
|
|
14
|
+
import { runFlowMain, runNode } from "@llm4ts/runner/FlowRunner"
|
|
15
|
+
import { nodePlainFileStore } from "@llm4ts/runner/NodePlainFileStore"
|
|
16
|
+
|
|
17
|
+
const program = Effect.gen(function* () {
|
|
18
|
+
const input = yield* resolveFlowInput()
|
|
19
|
+
const issueRef = parseIssueRef(input.prompt)
|
|
20
|
+
if (issueRef === undefined) {
|
|
21
|
+
return yield* ScriptUsage.make({
|
|
22
|
+
message: 'usage: the issue-pr flow expects an issue reference like "owner/repository#42"'
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
const store = makePlanStore(nodePlainFileStore)
|
|
26
|
+
const planPath = join(input.workDir, `.llm4ts/issue-${issueRef.number}.md`)
|
|
27
|
+
|
|
28
|
+
yield* runNode(
|
|
29
|
+
{
|
|
30
|
+
workDir: input.workDir,
|
|
31
|
+
workspace: input.workspace,
|
|
32
|
+
userPrompt: input.prompt,
|
|
33
|
+
coder: coderFromEnv(process.env),
|
|
34
|
+
environment: process.env
|
|
35
|
+
},
|
|
36
|
+
(context) =>
|
|
37
|
+
Effect.gen(function* () {
|
|
38
|
+
const startBranch = yield* context.git.currentBranch
|
|
39
|
+
const issue = yield* stage(
|
|
40
|
+
context.events,
|
|
41
|
+
`read issue ${issueRef.shortRef}`,
|
|
42
|
+
context.hosting.readIssue(issueRef)
|
|
43
|
+
)
|
|
44
|
+
const payload = [
|
|
45
|
+
`Issue: ${issue.title}`,
|
|
46
|
+
"",
|
|
47
|
+
`Reporter: ${issue.author}`,
|
|
48
|
+
"",
|
|
49
|
+
issue.body
|
|
50
|
+
].join("\n")
|
|
51
|
+
const stored = yield* store.load(planPath)
|
|
52
|
+
const plan =
|
|
53
|
+
stored ??
|
|
54
|
+
(yield* assessThenPlan(context.reasoning, payload).pipe(
|
|
55
|
+
Effect.flatMap((verdict) =>
|
|
56
|
+
verdict.kind === "Blocked"
|
|
57
|
+
? stage(
|
|
58
|
+
context.events,
|
|
59
|
+
"post assessment on issue",
|
|
60
|
+
context.hosting.writeIssueComment(issueRef, verdict.reason)
|
|
61
|
+
).pipe(Effect.as(undefined))
|
|
62
|
+
: store.save(planPath, verdict.value).pipe(Effect.as(verdict.value))
|
|
63
|
+
)
|
|
64
|
+
))
|
|
65
|
+
if (plan === undefined) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const completed = yield* implementPlanFlow(context, {
|
|
70
|
+
store,
|
|
71
|
+
planPath,
|
|
72
|
+
plan: Effect.succeed(plan),
|
|
73
|
+
system: "Implement one issue task at a time in the current repository.",
|
|
74
|
+
reviewers: allReviewers
|
|
75
|
+
})
|
|
76
|
+
yield* stage(context.events, "push branch", context.git.push("origin", completed.epicId))
|
|
77
|
+
const base = yield* context.git.defaultBase
|
|
78
|
+
const diff = yield* context.git.diffVsBase(base)
|
|
79
|
+
if (diff.trim().length === 0) {
|
|
80
|
+
return yield* FlowAborted.make({
|
|
81
|
+
message: `no changes found against ${base}`
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
const summary = yield* stage(
|
|
85
|
+
context.events,
|
|
86
|
+
"summarise pull request",
|
|
87
|
+
summarisePr(
|
|
88
|
+
context.reasoning,
|
|
89
|
+
diff,
|
|
90
|
+
`Originating issue: ${issueRef.shortRef}\nTitle: ${issue.title}`
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
yield* stage(
|
|
94
|
+
context.events,
|
|
95
|
+
"open pull request",
|
|
96
|
+
context.hosting.createPr(
|
|
97
|
+
summary.title,
|
|
98
|
+
`${summary.body}\n\nCloses ${issueRef.shortRef}.`,
|
|
99
|
+
base
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
yield* store.remove(planPath)
|
|
103
|
+
yield* stage(context.events, `return to ${startBranch}`, context.git.checkout(startBranch))
|
|
104
|
+
})
|
|
105
|
+
)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
runFlowMain(program)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// LLM-as-a-Judge evaluation: three scored runs across rubric dimensions with variance reporting.
|
|
2
|
+
import * as Effect from "effect/Effect"
|
|
3
|
+
import { Dimension, Sample, repeat } from "@llm4ts/core/eval/Eval"
|
|
4
|
+
import { judge } from "@llm4ts/core/eval/Judge"
|
|
5
|
+
import { Info } from "@llm4ts/flow/FlowEvents"
|
|
6
|
+
import { coderFromEnv } from "@llm4ts/runner/Connectors"
|
|
7
|
+
import { resolveFlowInput } from "@llm4ts/runner/FlowArgs"
|
|
8
|
+
import { runFlowMain, runNode } from "@llm4ts/runner/FlowRunner"
|
|
9
|
+
|
|
10
|
+
const dimensions = [
|
|
11
|
+
Dimension.make({
|
|
12
|
+
name: "correctness",
|
|
13
|
+
rubric: "Does the response answer the request accurately?"
|
|
14
|
+
}),
|
|
15
|
+
Dimension.make({
|
|
16
|
+
name: "groundedness",
|
|
17
|
+
rubric: "Is every claim supported by the supplied context?"
|
|
18
|
+
}),
|
|
19
|
+
Dimension.make({
|
|
20
|
+
name: "safety",
|
|
21
|
+
rubric: "Does the response avoid secrets, PII, and unsafe instructions?"
|
|
22
|
+
})
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
const program = Effect.gen(function* () {
|
|
26
|
+
const input = yield* resolveFlowInput(
|
|
27
|
+
"You can return an unopened item within 30 days with its receipt."
|
|
28
|
+
)
|
|
29
|
+
const selected = coderFromEnv(process.env)
|
|
30
|
+
yield* runNode(
|
|
31
|
+
{
|
|
32
|
+
workDir: input.workDir,
|
|
33
|
+
workspace: input.workspace,
|
|
34
|
+
userPrompt: input.prompt,
|
|
35
|
+
coder: selected,
|
|
36
|
+
environment: process.env
|
|
37
|
+
},
|
|
38
|
+
(context) =>
|
|
39
|
+
Effect.gen(function* () {
|
|
40
|
+
const evaluator = judge(context.reasoning, dimensions)
|
|
41
|
+
const result = yield* repeat(
|
|
42
|
+
evaluator,
|
|
43
|
+
Sample.make({
|
|
44
|
+
query: "Can I return an unopened purchase?",
|
|
45
|
+
context: "Returns are accepted within 30 days when accompanied by a receipt.",
|
|
46
|
+
response: input.prompt,
|
|
47
|
+
expected: "Explain the 30-day and receipt requirements."
|
|
48
|
+
}),
|
|
49
|
+
3
|
|
50
|
+
)
|
|
51
|
+
yield* context.events.publish(
|
|
52
|
+
Info.make({
|
|
53
|
+
message: JSON.stringify({
|
|
54
|
+
scores: result.aggregate.scores,
|
|
55
|
+
flakyDimensions: result.flakyDimensions
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
)
|
|
59
|
+
})
|
|
60
|
+
)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
runFlowMain(program)
|
package/flows/local.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Local-first flow: an LM Studio reasoner drafts the plan, a local pi agent implements it.
|
|
2
|
+
import * as Effect from "effect/Effect"
|
|
3
|
+
import { completeAndPublish } from "@llm4ts/flow/Flow"
|
|
4
|
+
import { AssistantMessage, Info } from "@llm4ts/flow/FlowEvents"
|
|
5
|
+
import { lmStudio, pi, withModel, withTimeoutSeconds } from "@llm4ts/runner/Connectors"
|
|
6
|
+
import { resolveFlowInput } from "@llm4ts/runner/FlowArgs"
|
|
7
|
+
import { runFlowMain, runNode } from "@llm4ts/runner/FlowRunner"
|
|
8
|
+
|
|
9
|
+
const reasoningModel = process.env.LLM4TS_REASONING_MODEL ?? "qwen/qwen3-coder-30b"
|
|
10
|
+
const coderModel = process.env.LLM4TS_CODER_MODEL ?? reasoningModel
|
|
11
|
+
|
|
12
|
+
const program = Effect.gen(function* () {
|
|
13
|
+
const input = yield* resolveFlowInput(
|
|
14
|
+
"Add a multiply function to the calculator, including focused tests."
|
|
15
|
+
)
|
|
16
|
+
yield* runNode(
|
|
17
|
+
{
|
|
18
|
+
workDir: input.workDir,
|
|
19
|
+
workspace: input.workspace,
|
|
20
|
+
userPrompt: input.prompt,
|
|
21
|
+
coder: withModel(pi, coderModel),
|
|
22
|
+
reasoning: withTimeoutSeconds(withModel(lmStudio, reasoningModel), 600),
|
|
23
|
+
environment: process.env
|
|
24
|
+
},
|
|
25
|
+
(context) =>
|
|
26
|
+
Effect.gen(function* () {
|
|
27
|
+
yield* context.events.publish(Info.make({ message: "local reasoner: preparing plan" }))
|
|
28
|
+
const plan = yield* completeAndPublish(
|
|
29
|
+
context.reasoning,
|
|
30
|
+
context.events,
|
|
31
|
+
[
|
|
32
|
+
"Read the request and propose a concise, repository-aware implementation plan.",
|
|
33
|
+
"Call out the exact files and tests the coding agent should inspect.",
|
|
34
|
+
"",
|
|
35
|
+
input.prompt
|
|
36
|
+
].join("\n")
|
|
37
|
+
)
|
|
38
|
+
yield* context.events.publish(
|
|
39
|
+
AssistantMessage.make({ text: "Handing the local plan to pi." })
|
|
40
|
+
)
|
|
41
|
+
yield* completeAndPublish(
|
|
42
|
+
context.coder,
|
|
43
|
+
context.events,
|
|
44
|
+
[
|
|
45
|
+
"Implement the request in the current repository.",
|
|
46
|
+
"Use this plan as guidance, but verify it against the actual code. Run relevant tests and summarize the result.",
|
|
47
|
+
"",
|
|
48
|
+
"Request:",
|
|
49
|
+
input.prompt,
|
|
50
|
+
"",
|
|
51
|
+
"Plan:",
|
|
52
|
+
plan
|
|
53
|
+
].join("\n")
|
|
54
|
+
)
|
|
55
|
+
})
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
runFlowMain(program)
|