@bluewombat/slot-kit 0.1.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/finish.js ADDED
@@ -0,0 +1,67 @@
1
+ import { readResult } from "./agent.js";
2
+ import { emitFailure } from "./emit.js";
3
+ /**
4
+ * Failures no further Attempt can fix: the CLI is not usable as invoked.
5
+ * Everything else is the agent's work failing, which is what Attempts are for.
6
+ */
7
+ const BLOCKING = [
8
+ /not (logged in|authenticated)|please (log ?in|sign ?in)|\blogin\b|unauthori[sz]ed|forbidden/i,
9
+ /failed to authenticate|authentication failed|oauth|session expired|token expired/i,
10
+ /invalid api key|missing api key|api key not/i,
11
+ /credit balance|usage limit reached/i,
12
+ /unknown (option|argument|flag|command)|unrecogni[sz]ed (option|argument)|^usage:/im,
13
+ /unknown model|unsupported model|no such model/i,
14
+ ];
15
+ /**
16
+ * How a finished run reads: what to answer, and the report to answer it with.
17
+ *
18
+ * Separate from `finishRun` because the classification is the part worth
19
+ * checking, and `finishRun` ends the process.
20
+ */
21
+ export function failureOf(run, bin, name) {
22
+ if (run.error !== undefined) {
23
+ return {
24
+ outcome: "fail-blocking",
25
+ report: `Could not start ${name} at ${bin}: ${run.error.message}`,
26
+ };
27
+ }
28
+ const detail = failureDetail(run, readResult(run.stdout), name);
29
+ return {
30
+ outcome: BLOCKING.some((pattern) => pattern.test(detail)) ? "fail-blocking" : "fail-retryable",
31
+ report: detail,
32
+ };
33
+ }
34
+ /**
35
+ * Answer the Builder contract for a finished run, then leave.
36
+ *
37
+ * Every agent Builder ends here so they classify the same way: exit 0 is a
38
+ * completed pass, and everything else is `fail-blocking` when the CLI itself is
39
+ * unusable, `fail-retryable` when the agent failed at the work.
40
+ */
41
+ export function finishRun({ run, bin, name }) {
42
+ if (run.error === undefined && run.code === 0 && readResult(run.stdout)?.is_error !== true) {
43
+ process.exit(0);
44
+ }
45
+ const failure = failureOf(run, bin, name);
46
+ emitFailure(failure.outcome, failure.report);
47
+ process.exit(1);
48
+ }
49
+ function failureDetail(run, result, name) {
50
+ if (result !== undefined) {
51
+ for (const field of ["error", "message", "result"]) {
52
+ const value = result[field];
53
+ if (typeof value === "string" && value.trim().length > 0) {
54
+ return value.trim().slice(-2000);
55
+ }
56
+ }
57
+ }
58
+ const output = run.output.trim();
59
+ if (output.length > 0) {
60
+ return output.slice(-2000);
61
+ }
62
+ if (run.signal !== null && run.signal !== undefined) {
63
+ return `${name} was killed by ${run.signal}.`;
64
+ }
65
+ return `${name} exited ${run.code} without output.`;
66
+ }
67
+ //# sourceMappingURL=finish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finish.js","sourceRoot":"","sources":["../src/finish.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC;;;GAGG;AACH,MAAM,QAAQ,GAAG;IACf,8FAA8F;IAC9F,mFAAmF;IACnF,8CAA8C;IAC9C,qCAAqC;IACrC,oFAAoF;IACpF,gDAAgD;CACjD,CAAC;AAIF;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CACvB,GAAa,EACb,GAAW,EACX,IAAY;IAEZ,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO;YACL,OAAO,EAAE,eAAe;YACxB,MAAM,EAAE,mBAAmB,IAAI,OAAO,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE;SAClE,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,EAAE,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAChE,OAAO;QACL,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,gBAAgB;QAC9F,MAAM,EAAE,MAAM;KACf,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAe;IACvD,IAAI,GAAG,CAAC,KAAK,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC3F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC1C,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,aAAa,CACpB,GAAa,EACb,MAA2C,EAC3C,IAAY;IAEZ,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,KAAK,MAAM,KAAK,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,CAAC;YACnD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzD,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACpD,OAAO,GAAG,IAAI,kBAAkB,GAAG,CAAC,MAAM,GAAG,CAAC;IAChD,CAAC;IACD,OAAO,GAAG,IAAI,WAAW,GAAG,CAAC,IAAI,kBAAkB,CAAC;AACtD,CAAC"}
package/dist/glob.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ /** Path glob: `*` one segment, `**` any depth. */
2
+ export declare function pathMatchesGlob(path: string, pattern: string): boolean;
3
+ //# sourceMappingURL=glob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAkCtE"}
package/dist/glob.js ADDED
@@ -0,0 +1,37 @@
1
+ /** Path glob: `*` one segment, `**` any depth. */
2
+ export function pathMatchesGlob(path, pattern) {
3
+ const posix = path.replaceAll("\\", "/");
4
+ let source = "^";
5
+ const spec = pattern.replaceAll("\\", "/");
6
+ for (let i = 0; i < spec.length;) {
7
+ if (spec.startsWith("**/", i)) {
8
+ source += "(?:.*/)?";
9
+ i += 3;
10
+ continue;
11
+ }
12
+ if (spec.startsWith("**", i) && i + 2 === spec.length) {
13
+ source += ".*";
14
+ i += 2;
15
+ continue;
16
+ }
17
+ const char = spec[i];
18
+ if (char === undefined) {
19
+ break;
20
+ }
21
+ if (char === "*") {
22
+ source += "[^/]*";
23
+ i += 1;
24
+ continue;
25
+ }
26
+ if (char === "?") {
27
+ source += "[^/]";
28
+ i += 1;
29
+ continue;
30
+ }
31
+ source += char.replace(/[.+^${}()|[\]\\]/g, "\\$&");
32
+ i += 1;
33
+ }
34
+ source += "$";
35
+ return new RegExp(source).test(posix);
36
+ }
37
+ //# sourceMappingURL=glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.js","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe;IAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACzC,IAAI,MAAM,GAAG,GAAG,CAAC;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,GAAI,CAAC;QAClC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,UAAU,CAAC;YACrB,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YACtD,MAAM,IAAI,IAAI,CAAC;YACf,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM;QACR,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,OAAO,CAAC;YAClB,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,MAAM,IAAI,MAAM,CAAC;YACjB,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;QACpD,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,MAAM,IAAI,GAAG,CAAC;IACd,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * What a slot needs to answer the Transformer that spawned it.
3
+ *
4
+ * A slot is an opaque command: Implementer spawns a Builder and its Gates,
5
+ * FeatureBreakdown spawns a Planner, and each one reads argv and writes one
6
+ * JSON line on stdout. Nothing here is required to fill a slot — a shell script
7
+ * that prints the right line is a Gate. This is the plumbing that every slot
8
+ * written in Node would otherwise write again.
9
+ */
10
+ export type { AgentInvocation, AgentRun, SerializedRun } from "./agent.js";
11
+ export { deserializeRun, findExecutable, readResult, runAgent, serializeRun } from "./agent.js";
12
+ export { BUILDER_FLAGS, GATE_FLAGS, MESSAGE_FLAGS, ownArgv, PLANNER_FLAGS, PUBLISHER_FLAGS, REFRESHER_FLAGS, splitRunner, stageOf, take, } from "./argv.js";
13
+ export type { Failure, Verdict, Write } from "./emit.js";
14
+ export { emitFailure, emitMessage, emitPlan, emitPublication, emitPublishRefusal, emitRefreshed, emitRefreshRefusal, emitRefusal, emitVerdict, } from "./emit.js";
15
+ export type { FinishedRun } from "./finish.js";
16
+ export { failureOf, finishRun } from "./finish.js";
17
+ export { pathMatchesGlob } from "./glob.js";
18
+ export type { OptionRule, OptionSpec, OptionValues, ParsedOptions } from "./options.js";
19
+ export { listOption, parseOptions, textOption } from "./options.js";
20
+ export type { CheckedPlan, PlanSubtask } from "./plan.js";
21
+ export { checkPlan } from "./plan.js";
22
+ export type { ReadTemplate, Rendered, TranscriptSubject } from "./prompt.js";
23
+ export { AGENT_OPTIONS, PROMPT_RULES, readAgentPrompt, readTemplate, renderPrompt, transcriptFor, } from "./prompt.js";
24
+ export type { AgentAbout, ParsedRole, SpawnedAgent, TranscriptExtras } from "./role.js";
25
+ export { fillPrompt, loadPrompt, loadRules, parseRole, ROLE_OPTIONS, spawnFilled, transcriptArgs, } from "./role.js";
26
+ export type { Transcript, TranscriptPart, TranscriptSpec } from "./transcript.js";
27
+ export { openTranscript, TRANSCRIPT_PARTS } from "./transcript.js";
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAChG,OAAO,EACL,aAAa,EACb,UAAU,EACV,aAAa,EACb,OAAO,EACP,aAAa,EACb,eAAe,EACf,eAAe,EACf,WAAW,EACX,OAAO,EACP,IAAI,GACL,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EACL,WAAW,EACX,WAAW,EACX,QAAQ,EACR,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,WAAW,GACZ,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACxF,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EACL,aAAa,EACb,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACxF,OAAO,EACL,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,cAAc,GACf,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * What a slot needs to answer the Transformer that spawned it.
3
+ *
4
+ * A slot is an opaque command: Implementer spawns a Builder and its Gates,
5
+ * FeatureBreakdown spawns a Planner, and each one reads argv and writes one
6
+ * JSON line on stdout. Nothing here is required to fill a slot — a shell script
7
+ * that prints the right line is a Gate. This is the plumbing that every slot
8
+ * written in Node would otherwise write again.
9
+ */
10
+ export { deserializeRun, findExecutable, readResult, runAgent, serializeRun } from "./agent.js";
11
+ export { BUILDER_FLAGS, GATE_FLAGS, MESSAGE_FLAGS, ownArgv, PLANNER_FLAGS, PUBLISHER_FLAGS, REFRESHER_FLAGS, splitRunner, stageOf, take, } from "./argv.js";
12
+ export { emitFailure, emitMessage, emitPlan, emitPublication, emitPublishRefusal, emitRefreshed, emitRefreshRefusal, emitRefusal, emitVerdict, } from "./emit.js";
13
+ export { failureOf, finishRun } from "./finish.js";
14
+ export { pathMatchesGlob } from "./glob.js";
15
+ export { listOption, parseOptions, textOption } from "./options.js";
16
+ export { checkPlan } from "./plan.js";
17
+ export { AGENT_OPTIONS, PROMPT_RULES, readAgentPrompt, readTemplate, renderPrompt, transcriptFor, } from "./prompt.js";
18
+ export { fillPrompt, loadPrompt, loadRules, parseRole, ROLE_OPTIONS, spawnFilled, transcriptArgs, } from "./role.js";
19
+ export { openTranscript, TRANSCRIPT_PARTS } from "./transcript.js";
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAChG,OAAO,EACL,aAAa,EACb,UAAU,EACV,aAAa,EACb,OAAO,EACP,aAAa,EACb,eAAe,EACf,eAAe,EACf,WAAW,EACX,OAAO,EACP,IAAI,GACL,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,WAAW,EACX,WAAW,EACX,QAAQ,EACR,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,WAAW,GACZ,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EACL,aAAa,EACb,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,UAAU,EACV,UAAU,EACV,SAAS,EACT,SAAS,EACT,YAAY,EACZ,WAAW,EACX,cAAc,GACf,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,36 @@
1
+ export type OptionRule = {
2
+ /** Collect every occurrence instead of keeping the last. */
3
+ many?: boolean | undefined;
4
+ /** Takes no value: its presence is the value. */
5
+ flag?: boolean | undefined;
6
+ /** The only values accepted. */
7
+ choices?: readonly string[] | undefined;
8
+ /** Used when the Project gave none. */
9
+ fallback?: string | undefined;
10
+ };
11
+ /** Keyed by the flag as written, e.g. `"--model"`. */
12
+ export type OptionSpec = Record<string, OptionRule>;
13
+ /** Keyed by the flag in camel case, e.g. `model` for `--model`. */
14
+ export type OptionValues = Record<string, string | string[] | boolean | undefined>;
15
+ export type ParsedOptions = {
16
+ ok: true;
17
+ values: OptionValues;
18
+ } | {
19
+ ok: false;
20
+ reason: string;
21
+ };
22
+ /**
23
+ * Read a slot's own options from a spec of flags.
24
+ *
25
+ * `{ many: true }` collects a repeatable flag, `{ choices }` validates, and a
26
+ * flag not in the spec is refused rather than passed on: an option meant for
27
+ * the agent CLI that lands here is a config mistake, and a silent one costs a
28
+ * whole run. Keys are the flag name in camel case, and `name` is how the slot
29
+ * calls itself in that refusal.
30
+ */
31
+ export declare function parseOptions(tokens: readonly string[], spec: OptionSpec, name: string): ParsedOptions;
32
+ /** The value of a one-shot flag, or nothing: a `many` or `flag` option is not text. */
33
+ export declare function textOption(values: OptionValues, key: string): string | undefined;
34
+ /** Every value of a `many` flag; empty for anything else. */
35
+ export declare function listOption(values: OptionValues, key: string): string[];
36
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG;IACvB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,iDAAiD;IACjD,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC3B,gCAAgC;IAChC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACxC,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEpD,mEAAmE;AACnE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;AAEnF,MAAM,MAAM,aAAa,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/F;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,GACX,aAAa,CAwCf;AAaD,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGhF;AAED,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAGtE"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Read a slot's own options from a spec of flags.
3
+ *
4
+ * `{ many: true }` collects a repeatable flag, `{ choices }` validates, and a
5
+ * flag not in the spec is refused rather than passed on: an option meant for
6
+ * the agent CLI that lands here is a config mistake, and a silent one costs a
7
+ * whole run. Keys are the flag name in camel case, and `name` is how the slot
8
+ * calls itself in that refusal.
9
+ */
10
+ export function parseOptions(tokens, spec, name) {
11
+ const values = {};
12
+ for (const [flag, rule] of Object.entries(spec)) {
13
+ if (rule.many === true) {
14
+ values[optionKey(flag)] = [];
15
+ }
16
+ else if (rule.fallback !== undefined) {
17
+ values[optionKey(flag)] = rule.fallback;
18
+ }
19
+ }
20
+ for (let i = 0; i < tokens.length; i++) {
21
+ const token = tokens[i];
22
+ if (token === undefined) {
23
+ continue;
24
+ }
25
+ const rule = spec[token];
26
+ if (rule === undefined) {
27
+ return { ok: false, reason: `The ${name} does not take "${token}".` };
28
+ }
29
+ if (rule.flag === true) {
30
+ values[optionKey(token)] = true;
31
+ continue;
32
+ }
33
+ const value = tokens[i + 1];
34
+ if (value === undefined) {
35
+ return { ok: false, reason: `The ${name} needs a value after ${token}.` };
36
+ }
37
+ i += 1;
38
+ if (rule.choices !== undefined && !rule.choices.includes(value)) {
39
+ return { ok: false, reason: `${token} is ${orList(rule.choices)}, not "${value}".` };
40
+ }
41
+ if (rule.many === true) {
42
+ const collected = values[optionKey(token)];
43
+ if (Array.isArray(collected)) {
44
+ collected.push(value);
45
+ }
46
+ }
47
+ else {
48
+ values[optionKey(token)] = value;
49
+ }
50
+ }
51
+ return { ok: true, values };
52
+ }
53
+ function optionKey(flag) {
54
+ return flag.slice(2).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
55
+ }
56
+ function orList(choices) {
57
+ if (choices.length < 2) {
58
+ return `${choices[0]}`;
59
+ }
60
+ return `${choices.slice(0, -1).join(", ")} or ${choices.at(-1)}`;
61
+ }
62
+ /** The value of a one-shot flag, or nothing: a `many` or `flag` option is not text. */
63
+ export function textOption(values, key) {
64
+ const value = values[key];
65
+ return typeof value === "string" ? value : undefined;
66
+ }
67
+ /** Every value of a `many` flag; empty for anything else. */
68
+ export function listOption(values, key) {
69
+ const value = values[key];
70
+ return Array.isArray(value) ? value : [];
71
+ }
72
+ //# sourceMappingURL=options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
package/dist/plan.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ export type PlanSubtask = {
2
+ id: string;
3
+ intention: string;
4
+ definition_of_done: string;
5
+ depends_on: string[];
6
+ };
7
+ export type CheckedPlan = {
8
+ ok: true;
9
+ subtasks: PlanSubtask[];
10
+ } | {
11
+ ok: false;
12
+ reason: string;
13
+ };
14
+ /**
15
+ * Whether this is a Plan the rest of the system can act on — and, when it is,
16
+ * the subtasks typed as one.
17
+ *
18
+ * Checked here rather than trusted, because a Planner that hands back a broken
19
+ * graph has failed at its one job, and the failure should read as this slot's,
20
+ * not as a refusal of the feature.
21
+ */
22
+ export declare function checkPlan(subtasks: unknown, maxUnits?: number): CheckedPlan;
23
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,WAAW,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhG;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,WAAW,CAkD3E"}
package/dist/plan.js ADDED
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Whether this is a Plan the rest of the system can act on — and, when it is,
3
+ * the subtasks typed as one.
4
+ *
5
+ * Checked here rather than trusted, because a Planner that hands back a broken
6
+ * graph has failed at its one job, and the failure should read as this slot's,
7
+ * not as a refusal of the feature.
8
+ */
9
+ export function checkPlan(subtasks, maxUnits) {
10
+ if (!Array.isArray(subtasks) || subtasks.length === 0) {
11
+ return { ok: false, reason: "The plan has no subtasks." };
12
+ }
13
+ if (maxUnits !== undefined && Number.isInteger(maxUnits) && subtasks.length > maxUnits) {
14
+ return {
15
+ ok: false,
16
+ reason: `The plan has ${subtasks.length} subtasks, over the ${maxUnits} allowed.`,
17
+ };
18
+ }
19
+ const ids = new Set();
20
+ for (const subtask of subtasks) {
21
+ if (subtask === null || typeof subtask !== "object") {
22
+ return { ok: false, reason: "A subtask is not an object." };
23
+ }
24
+ const record = subtask;
25
+ for (const field of ["id", "intention", "definition_of_done"]) {
26
+ const value = record[field];
27
+ if (typeof value !== "string" || value.trim().length === 0) {
28
+ return { ok: false, reason: `A subtask has no "${field}".` };
29
+ }
30
+ }
31
+ const id = record.id;
32
+ if (ids.has(id)) {
33
+ return { ok: false, reason: `Two subtasks share the id "${id}".` };
34
+ }
35
+ ids.add(id);
36
+ if (!Array.isArray(record.depends_on)) {
37
+ return { ok: false, reason: `Subtask "${id}" has no depends_on list.` };
38
+ }
39
+ }
40
+ const plan = subtasks;
41
+ for (const subtask of plan) {
42
+ for (const dependency of subtask.depends_on) {
43
+ if (dependency === subtask.id) {
44
+ return { ok: false, reason: `Subtask "${subtask.id}" depends on itself.` };
45
+ }
46
+ if (!ids.has(dependency)) {
47
+ return {
48
+ ok: false,
49
+ reason: `Subtask "${subtask.id}" depends on "${dependency}", which is not in the plan.`,
50
+ };
51
+ }
52
+ }
53
+ }
54
+ const cycle = findCycle(plan);
55
+ if (cycle !== undefined) {
56
+ return { ok: false, reason: `The plan has a cycle: ${cycle.join(" → ")}.` };
57
+ }
58
+ return { ok: true, subtasks: plan };
59
+ }
60
+ /** Depth-first, so the answer names the loop rather than only reporting one. */
61
+ function findCycle(subtasks) {
62
+ const edges = new Map(subtasks.map((subtask) => [subtask.id, subtask.depends_on]));
63
+ const done = new Set();
64
+ const path = [];
65
+ const open = new Set();
66
+ const walk = (id) => {
67
+ if (done.has(id)) {
68
+ return undefined;
69
+ }
70
+ if (open.has(id)) {
71
+ return [...path.slice(path.indexOf(id)), id];
72
+ }
73
+ open.add(id);
74
+ path.push(id);
75
+ for (const next of edges.get(id) ?? []) {
76
+ const found = walk(next);
77
+ if (found !== undefined) {
78
+ return found;
79
+ }
80
+ }
81
+ path.pop();
82
+ open.delete(id);
83
+ done.add(id);
84
+ return undefined;
85
+ };
86
+ for (const subtask of subtasks) {
87
+ const found = walk(subtask.id);
88
+ if (found !== undefined) {
89
+ return found;
90
+ }
91
+ }
92
+ return undefined;
93
+ }
94
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import { type OptionSpec, type OptionValues } from "./options.js";
2
+ import { type Transcript } from "./transcript.js";
3
+ /**
4
+ * The bounds of a Builder slot: what `{{rules}}` says unless the Project writes
5
+ * its own.
6
+ *
7
+ * The last three are not a matter of taste. The checks that run after the agent
8
+ * read the working tree of this one directory, so an agent that commits,
9
+ * stashes or writes elsewhere produces nothing they can see, and the pass is
10
+ * refused for having done nothing. A Project that replaces these keeps that
11
+ * obligation whether or not it repeats the words.
12
+ */
13
+ export declare const PROMPT_RULES: string;
14
+ export type Rendered = {
15
+ ok: true;
16
+ prompt: string;
17
+ } | {
18
+ ok: false;
19
+ reason: string;
20
+ };
21
+ /**
22
+ * Fill a prompt template from a map of names the caller owns.
23
+ *
24
+ * An unknown name is refused rather than dropped: a typo that silently removes
25
+ * the task is a run spent on nothing. `required` names must appear in the
26
+ * template; an empty value still counts as placed. Blank lines an emptied
27
+ * placeholder leaves are collapsed.
28
+ */
29
+ export declare function renderPrompt(template: string, values: Record<string, string>, required?: readonly string[]): Rendered;
30
+ export type ReadTemplate = {
31
+ ok: true;
32
+ value: string;
33
+ } | {
34
+ ok: false;
35
+ reason: string;
36
+ };
37
+ /** The file at `path`, or `fallback` when there is no path. */
38
+ export declare function readTemplate(path: string | undefined, fallback: string, what?: string): ReadTemplate;
39
+ /**
40
+ * Options every vendor agent CLI takes: the prompt is already filled, and
41
+ * nothing here is a placeholder.
42
+ */
43
+ export declare const AGENT_OPTIONS: OptionSpec;
44
+ /**
45
+ * The already-filled prompt an agent CLI was handed. `--prompt` and
46
+ * `--prompt-file` are the same text, two ways in; both, or neither, is a
47
+ * mistake. Nothing here interpolates.
48
+ */
49
+ export declare function readAgentPrompt(options: OptionValues): {
50
+ ok: true;
51
+ prompt: string;
52
+ } | {
53
+ ok: false;
54
+ reason: string;
55
+ };
56
+ export type TranscriptSubject = {
57
+ slot?: string | undefined;
58
+ bin?: string | undefined;
59
+ args?: readonly string[] | undefined;
60
+ prompt?: string | undefined;
61
+ };
62
+ /** The turn's transcript, filed under what this Task belongs to. */
63
+ export declare function transcriptFor(argv: readonly string[], options: OptionValues, about: TranscriptSubject): Transcript;
64
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAc,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEpF;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,QAab,CAAC;AAIb,MAAM,MAAM,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,QAAQ,GAAE,SAAS,MAAM,EAAO,GAC/B,QAAQ,CAyBV;AAED,MAAM,MAAM,YAAY,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvF,+DAA+D;AAC/D,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,QAAQ,EAAE,MAAM,EAChB,IAAI,SAAoB,GACvB,YAAY,CAqBd;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,UAY3B,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAoB9D;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7B,CAAC;AAEF,oEAAoE;AACpE,wBAAgB,aAAa,CAC3B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,iBAAiB,GACvB,UAAU,CAUZ"}
package/dist/prompt.js ADDED
@@ -0,0 +1,142 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { take } from "./argv.js";
3
+ import { textOption } from "./options.js";
4
+ import { openTranscript, TRANSCRIPT_PARTS } from "./transcript.js";
5
+ /**
6
+ * The bounds of a Builder slot: what `{{rules}}` says unless the Project writes
7
+ * its own.
8
+ *
9
+ * The last three are not a matter of taste. The checks that run after the agent
10
+ * read the working tree of this one directory, so an agent that commits,
11
+ * stashes or writes elsewhere produces nothing they can see, and the pass is
12
+ * refused for having done nothing. A Project that replaces these keeps that
13
+ * obligation whether or not it repeats the words.
14
+ */
15
+ export const PROMPT_RULES = [
16
+ "- Work only inside this directory. Do not read or edit anything outside it.",
17
+ "- Follow the conventions already in this repository: its structure, its naming,",
18
+ " its tests. Read the code around what you touch before you write.",
19
+ "- Change only what the task needs.",
20
+ "- If the project has checks of its own, run them and leave them passing.",
21
+ "- Nobody is watching this run and nobody will answer a question. Where the task",
22
+ " is ambiguous, take the reading most consistent with the existing code, and say",
23
+ " which one you took in your final message.",
24
+ "- Leave your work uncommitted in the working tree: do not commit, branch, stash,",
25
+ " or reset. The changed files in this directory are the deliverable, and they are",
26
+ " read back from here exactly as you leave them.",
27
+ "- Do not push, do not open a pull request, do not touch a remote.",
28
+ ].join("\n");
29
+ const PLACEHOLDER = /\{\{\s*([a-z_]+)\s*\}\}/g;
30
+ /**
31
+ * Fill a prompt template from a map of names the caller owns.
32
+ *
33
+ * An unknown name is refused rather than dropped: a typo that silently removes
34
+ * the task is a run spent on nothing. `required` names must appear in the
35
+ * template; an empty value still counts as placed. Blank lines an emptied
36
+ * placeholder leaves are collapsed.
37
+ */
38
+ export function renderPrompt(template, values, required = []) {
39
+ const unknown = new Set();
40
+ const seen = new Set();
41
+ const filled = template.replace(PLACEHOLDER, (match, name) => {
42
+ const value = values[name];
43
+ if (value === undefined) {
44
+ unknown.add(name);
45
+ return match;
46
+ }
47
+ seen.add(name);
48
+ return value;
49
+ });
50
+ if (unknown.size > 0) {
51
+ return {
52
+ ok: false,
53
+ reason: `The prompt template uses {{${[...unknown].join("}}, {{")}}}, which is not a placeholder. Known: {{${Object.keys(values).join("}}, {{")}}}.`,
54
+ };
55
+ }
56
+ for (const name of required) {
57
+ if (!seen.has(name)) {
58
+ return { ok: false, reason: `The prompt template must place {{${name}}} somewhere.` };
59
+ }
60
+ }
61
+ return { ok: true, prompt: `${filled.replace(/\n{3,}/g, "\n\n").trim()}\n` };
62
+ }
63
+ /** The file at `path`, or `fallback` when there is no path. */
64
+ export function readTemplate(path, fallback, what = "prompt template") {
65
+ if (path === undefined) {
66
+ return { ok: true, value: fallback };
67
+ }
68
+ let text;
69
+ try {
70
+ text = readFileSync(path, "utf8");
71
+ }
72
+ catch (error) {
73
+ return {
74
+ ok: false,
75
+ reason: `Cannot read the ${what} at ${path}: ${error instanceof Error ? error.message : String(error)}`,
76
+ };
77
+ }
78
+ // An empty file is a truncated one far more often than it is a deliberate
79
+ // silence, and a template with nothing in it wastes a whole run. A producing
80
+ // template must place `{{rules}}`; a Project that wants other rules writes
81
+ // them in a `--rules-file`.
82
+ if (text.trim().length === 0) {
83
+ return { ok: false, reason: `The ${what} at ${path} is empty.` };
84
+ }
85
+ return { ok: true, value: text };
86
+ }
87
+ /**
88
+ * Options every vendor agent CLI takes: the prompt is already filled, and
89
+ * nothing here is a placeholder.
90
+ */
91
+ export const AGENT_OPTIONS = {
92
+ "--bin": {},
93
+ "--model": {},
94
+ "--output-format": { choices: ["json", "text", "stream-json"], fallback: "json" },
95
+ "--prompt": {},
96
+ "--prompt-file": {},
97
+ "--transcript-dir": {},
98
+ "--transcript-part": { many: true, choices: TRANSCRIPT_PARTS },
99
+ "--agent-arg": { many: true },
100
+ "--id": {},
101
+ "--attempt": {},
102
+ "--context": {},
103
+ };
104
+ /**
105
+ * The already-filled prompt an agent CLI was handed. `--prompt` and
106
+ * `--prompt-file` are the same text, two ways in; both, or neither, is a
107
+ * mistake. Nothing here interpolates.
108
+ */
109
+ export function readAgentPrompt(options) {
110
+ const inline = textOption(options, "prompt");
111
+ const fromFile = textOption(options, "promptFile");
112
+ if (inline !== undefined && fromFile !== undefined) {
113
+ return { ok: false, reason: "Pass --prompt or --prompt-file, not both." };
114
+ }
115
+ if (inline !== undefined) {
116
+ if (inline.trim().length === 0) {
117
+ return { ok: false, reason: "The --prompt is empty." };
118
+ }
119
+ return { ok: true, prompt: inline };
120
+ }
121
+ if (fromFile === undefined) {
122
+ return { ok: false, reason: "The agent command needs --prompt or --prompt-file." };
123
+ }
124
+ const loaded = readTemplate(fromFile, "");
125
+ if (!loaded.ok) {
126
+ return loaded;
127
+ }
128
+ return { ok: true, prompt: loaded.value };
129
+ }
130
+ /** The turn's transcript, filed under what this Task belongs to. */
131
+ export function transcriptFor(argv, options, about) {
132
+ const parts = options.transcriptPart;
133
+ return openTranscript({
134
+ dir: textOption(options, "transcriptDir"),
135
+ ...(Array.isArray(parts) && parts.length > 0 ? { parts } : {}),
136
+ context: take(argv, "--context"),
137
+ id: take(argv, "--id"),
138
+ attempt: take(argv, "--attempt"),
139
+ ...about,
140
+ });
141
+ }
142
+ //# sourceMappingURL=prompt.js.map