@gr8ful/spf 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/LICENSE +21 -0
- package/README.md +321 -0
- package/assets/defaults/spf.config.yaml +141 -0
- package/assets/prompts/builder/system.md +13 -0
- package/assets/prompts/builder/user.md +34 -0
- package/assets/prompts/documenter/system.md +17 -0
- package/assets/prompts/documenter/user.md +48 -0
- package/assets/prompts/planner/system.md +21 -0
- package/assets/prompts/planner/user.md +45 -0
- package/assets/prompts/reviewer/system.md +16 -0
- package/assets/prompts/reviewer/user.md +44 -0
- package/assets/prompts/scout/system.md +20 -0
- package/assets/prompts/scout/user.md +34 -0
- package/assets/skill/SKILL.md +80 -0
- package/assets/skill/cookbooks/authoring_chains.md +193 -0
- package/assets/skill/cookbooks/how_to_prompt_for_the_eng.md +109 -0
- package/assets/skill/cookbooks/roster.md +197 -0
- package/assets/skill/cookbooks/run_adw.md +92 -0
- package/assets/skill/cookbooks/spf_overview.md +111 -0
- package/assets/skill/references/config.md +188 -0
- package/assets/skill/references/handoff.md +162 -0
- package/assets/skill/references/observability.md +184 -0
- package/dist/chains/adw_build.d.ts +12 -0
- package/dist/chains/adw_build.js +27 -0
- package/dist/chains/adw_build_review.d.ts +21 -0
- package/dist/chains/adw_build_review.js +55 -0
- package/dist/chains/adw_build_test.d.ts +21 -0
- package/dist/chains/adw_build_test.js +67 -0
- package/dist/chains/adw_document.d.ts +23 -0
- package/dist/chains/adw_document.js +59 -0
- package/dist/chains/adw_plan.d.ts +12 -0
- package/dist/chains/adw_plan.js +27 -0
- package/dist/chains/adw_plan_build.d.ts +12 -0
- package/dist/chains/adw_plan_build.js +30 -0
- package/dist/chains/adw_plan_build_test.d.ts +16 -0
- package/dist/chains/adw_plan_build_test.js +65 -0
- package/dist/chains/adw_plan_build_test_quality.d.ts +18 -0
- package/dist/chains/adw_plan_build_test_quality.js +66 -0
- package/dist/chains/adw_prompt.d.ts +12 -0
- package/dist/chains/adw_prompt.js +25 -0
- package/dist/chains/adw_quality.d.ts +12 -0
- package/dist/chains/adw_quality.js +32 -0
- package/dist/chains/adw_scout.d.ts +12 -0
- package/dist/chains/adw_scout.js +27 -0
- package/dist/chains/adw_simple_sdlc.d.ts +43 -0
- package/dist/chains/adw_simple_sdlc.js +147 -0
- package/dist/chains/context.d.ts +19 -0
- package/dist/chains/context.js +1 -0
- package/dist/chains/index.d.ts +19 -0
- package/dist/chains/index.js +116 -0
- package/dist/cli/bin.d.ts +15 -0
- package/dist/cli/bin.js +24 -0
- package/dist/cli/commands/abort.d.ts +1 -0
- package/dist/cli/commands/abort.js +42 -0
- package/dist/cli/commands/doctor.d.ts +1 -0
- package/dist/cli/commands/doctor.js +165 -0
- package/dist/cli/commands/eject.d.ts +1 -0
- package/dist/cli/commands/eject.js +55 -0
- package/dist/cli/commands/events.d.ts +1 -0
- package/dist/cli/commands/events.js +49 -0
- package/dist/cli/commands/init.d.ts +1 -0
- package/dist/cli/commands/init.js +62 -0
- package/dist/cli/commands/install-skill.d.ts +1 -0
- package/dist/cli/commands/install-skill.js +122 -0
- package/dist/cli/commands/list.d.ts +1 -0
- package/dist/cli/commands/list.js +13 -0
- package/dist/cli/commands/migrate.d.ts +1 -0
- package/dist/cli/commands/migrate.js +167 -0
- package/dist/cli/commands/phases.d.ts +1 -0
- package/dist/cli/commands/phases.js +25 -0
- package/dist/cli/commands/run.d.ts +3 -0
- package/dist/cli/commands/run.js +27 -0
- package/dist/cli/commands/sessions.d.ts +1 -0
- package/dist/cli/commands/sessions.js +20 -0
- package/dist/cli/commands/trace.d.ts +8 -0
- package/dist/cli/commands/trace.js +10 -0
- package/dist/cli/commands/ui.d.ts +1 -0
- package/dist/cli/commands/ui.js +35 -0
- package/dist/cli/commands/version.d.ts +1 -0
- package/dist/cli/commands/version.js +8 -0
- package/dist/cli/commands/watch.d.ts +8 -0
- package/dist/cli/commands/watch.js +210 -0
- package/dist/cli/gitignore.d.ts +1 -0
- package/dist/cli/gitignore.js +14 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +156 -0
- package/dist/core/agent_cc.d.ts +75 -0
- package/dist/core/agent_cc.js +322 -0
- package/dist/core/agent_flue.d.ts +66 -0
- package/dist/core/agent_flue.js +321 -0
- package/dist/core/agents.d.ts +64 -0
- package/dist/core/agents.js +456 -0
- package/dist/core/changes.d.ts +35 -0
- package/dist/core/changes.js +98 -0
- package/dist/core/console.d.ts +36 -0
- package/dist/core/console.js +156 -0
- package/dist/core/data_types.d.ts +562 -0
- package/dist/core/data_types.js +382 -0
- package/dist/core/gates.d.ts +33 -0
- package/dist/core/gates.js +144 -0
- package/dist/core/git_helper.d.ts +59 -0
- package/dist/core/git_helper.js +115 -0
- package/dist/core/issues/github_provider.d.ts +52 -0
- package/dist/core/issues/github_provider.js +211 -0
- package/dist/core/issues/provider.d.ts +90 -0
- package/dist/core/issues/provider.js +14 -0
- package/dist/core/paths.d.ts +78 -0
- package/dist/core/paths.js +108 -0
- package/dist/core/permissions.d.ts +78 -0
- package/dist/core/permissions.js +187 -0
- package/dist/core/prompts.d.ts +4 -0
- package/dist/core/prompts.js +17 -0
- package/dist/core/quality.d.ts +65 -0
- package/dist/core/quality.js +194 -0
- package/dist/core/runner.d.ts +72 -0
- package/dist/core/runner.js +168 -0
- package/dist/core/session.d.ts +16 -0
- package/dist/core/session.js +60 -0
- package/dist/core/sqlite.d.ts +55 -0
- package/dist/core/sqlite.js +106 -0
- package/dist/core/tracer.d.ts +56 -0
- package/dist/core/tracer.js +246 -0
- package/dist/core/utils.d.ts +40 -0
- package/dist/core/utils.js +108 -0
- package/dist/core/watch.d.ts +44 -0
- package/dist/core/watch.js +194 -0
- package/dist/test/agent_cc.test.d.ts +1 -0
- package/dist/test/agent_cc.test.js +95 -0
- package/dist/test/agent_flue.test.d.ts +1 -0
- package/dist/test/agent_flue.test.js +83 -0
- package/dist/test/data_types.test.d.ts +10 -0
- package/dist/test/data_types.test.js +49 -0
- package/dist/test/ui_server.test.d.ts +1 -0
- package/dist/test/ui_server.test.js +119 -0
- package/dist/test/watch.test.d.ts +1 -0
- package/dist/test/watch.test.js +227 -0
- package/dist/ui/server/app.d.ts +3 -0
- package/dist/ui/server/app.js +98 -0
- package/dist/ui/server/db.d.ts +82 -0
- package/dist/ui/server/db.js +333 -0
- package/dist/ui/server/serve.d.ts +13 -0
- package/dist/ui/server/serve.js +80 -0
- package/dist/ui/server/static.d.ts +5 -0
- package/dist/ui/server/static.js +55 -0
- package/dist/ui/shared/types.d.ts +263 -0
- package/dist/ui/shared/types.js +8 -0
- package/package.json +52 -0
- package/web/assets/index-C7nF068F.css +1 -0
- package/web/assets/index-mzSArcnQ.js +11 -0
- package/web/assets/play-latin-400-normal-GKW-4YV7.woff2 +0 -0
- package/web/assets/play-latin-700-normal-DyPlLDbb.woff2 +0 -0
- package/web/index.html +14 -0
- package/web/logo.svg +6 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Plan — one-shot planning workflow.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf plan "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner
|
|
8
|
+
*/
|
|
9
|
+
import * as agents from "../core/agents.js";
|
|
10
|
+
import * as gates from "../core/gates.js";
|
|
11
|
+
import * as session from "../core/session.js";
|
|
12
|
+
import { PlanOutput, makeAgentCall, makePhaseParams } from "../core/data_types.js";
|
|
13
|
+
export const REQUIRED_AGENTS = ["planner"];
|
|
14
|
+
export const REQUIRED_SUITES = [];
|
|
15
|
+
export async function main(ctx) {
|
|
16
|
+
const { prompt, config_paths, adw_id, cwd } = ctx;
|
|
17
|
+
const cfg = agents.loadConfig(config_paths);
|
|
18
|
+
agents.validate(cfg, REQUIRED_AGENTS, REQUIRED_SUITES, cwd);
|
|
19
|
+
const run = session.ensure(cfg, adw_id, cwd);
|
|
20
|
+
await run.phase(makePhaseParams({ name: "request", kind: "engineer", owner: run.engineer, description: "Capture the incoming ask" }), async (ph) => {
|
|
21
|
+
ph.log({ input: prompt });
|
|
22
|
+
});
|
|
23
|
+
await run.phase(makePhaseParams({ name: "plan", kind: "agent", owner: "planner", description: "Turn the request into an implementable plan" }), async (ph) => {
|
|
24
|
+
await ph.call(makeAgentCall({ output_type: PlanOutput, prompt, gates: [gates.artifactsExist, gates.filesNonEmpty] }));
|
|
25
|
+
});
|
|
26
|
+
return run.finish();
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Plan Build — two-agent chain: planner -> envelope -> builder.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf plan-build "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner -> builder -> git(commit)
|
|
8
|
+
*/
|
|
9
|
+
import type { ChainContext } from "./context.ts";
|
|
10
|
+
export declare const REQUIRED_AGENTS: string[];
|
|
11
|
+
export declare const REQUIRED_SUITES: string[];
|
|
12
|
+
export declare function main(ctx: ChainContext): Promise<number>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Plan Build — two-agent chain: planner -> envelope -> builder.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf plan-build "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner -> builder -> git(commit)
|
|
8
|
+
*/
|
|
9
|
+
import * as agents from "../core/agents.js";
|
|
10
|
+
import * as gates from "../core/gates.js";
|
|
11
|
+
import * as session from "../core/session.js";
|
|
12
|
+
import { BuildOutput, PlanOutput, makeAgentCall, makePhaseParams } from "../core/data_types.js";
|
|
13
|
+
export const REQUIRED_AGENTS = ["planner", "builder"];
|
|
14
|
+
export const REQUIRED_SUITES = [];
|
|
15
|
+
export async function main(ctx) {
|
|
16
|
+
const { prompt, config_paths, adw_id, cwd } = ctx;
|
|
17
|
+
const cfg = agents.loadConfig(config_paths);
|
|
18
|
+
agents.validate(cfg, REQUIRED_AGENTS, REQUIRED_SUITES, cwd);
|
|
19
|
+
const run = session.ensure(cfg, adw_id, cwd);
|
|
20
|
+
await run.phase(makePhaseParams({ name: "request", kind: "engineer", owner: run.engineer, description: "Capture the incoming ask" }), async (ph) => {
|
|
21
|
+
ph.log({ input: prompt });
|
|
22
|
+
});
|
|
23
|
+
const plan = await run.phase(makePhaseParams({ name: "plan", kind: "agent", owner: "planner", description: "Turn the request into an implementable plan" }), (ph) => ph.call(makeAgentCall({ output_type: PlanOutput, prompt, gates: [gates.artifactsExist, gates.filesNonEmpty] })));
|
|
24
|
+
const build = await run.phase(makePhaseParams({ name: "build", kind: "agent", owner: "builder", description: "Implement the plan exactly" }), (ph) => ph.call(makeAgentCall({ output_type: BuildOutput, prompt, previous: plan, gates: [gates.diffMatchesClaims] })));
|
|
25
|
+
await run.phase(makePhaseParams({ name: "commit", kind: "code", owner: "git", description: "Land the builder's changes, using the message it wrote" }), async (ph) => {
|
|
26
|
+
const message = build.commit_message || `spf(${run.adw_id}): ${build.summary}`;
|
|
27
|
+
ph.log({ sha: run.git.commitAll(message), message });
|
|
28
|
+
});
|
|
29
|
+
return run.finish();
|
|
30
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Plan Build Test — the full starter chain.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf plan-build-test "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner -> builder -> code(test) [-> builder(fix) -> code(test) ... bounded] -> git(commit)
|
|
8
|
+
*
|
|
9
|
+
* Testing is CODE: the suite's command lives in core/quality.ts, so no
|
|
10
|
+
* agent spends a context window rediscovering it. Failures flow back to the
|
|
11
|
+
* builder as an envelope, and only an exhausted fix loop fails the run.
|
|
12
|
+
*/
|
|
13
|
+
import type { ChainContext } from "./context.ts";
|
|
14
|
+
export declare const REQUIRED_AGENTS: string[];
|
|
15
|
+
export declare const REQUIRED_SUITES: string[];
|
|
16
|
+
export declare function main(ctx: ChainContext): Promise<number>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Plan Build Test — the full starter chain.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf plan-build-test "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner -> builder -> code(test) [-> builder(fix) -> code(test) ... bounded] -> git(commit)
|
|
8
|
+
*
|
|
9
|
+
* Testing is CODE: the suite's command lives in core/quality.ts, so no
|
|
10
|
+
* agent spends a context window rediscovering it. Failures flow back to the
|
|
11
|
+
* builder as an envelope, and only an exhausted fix loop fails the run.
|
|
12
|
+
*/
|
|
13
|
+
import * as agents from "../core/agents.js";
|
|
14
|
+
import * as gates from "../core/gates.js";
|
|
15
|
+
import * as quality from "../core/quality.js";
|
|
16
|
+
import * as session from "../core/session.js";
|
|
17
|
+
import { BuildOutput, PlanOutput, makeAgentCall, makePhaseParams } from "../core/data_types.js";
|
|
18
|
+
export const REQUIRED_AGENTS = ["planner", "builder"];
|
|
19
|
+
export const REQUIRED_SUITES = ["test"];
|
|
20
|
+
const MAX_FIX_LOOPS = 3;
|
|
21
|
+
function record(ph, result) {
|
|
22
|
+
const passed = result.checks.filter((c) => c.passed).length;
|
|
23
|
+
ph.log({ passed: result.passed, checks: `${passed}/${result.checks.length}`, artifacts: result.artifacts.join(", ") });
|
|
24
|
+
}
|
|
25
|
+
export async function main(ctx) {
|
|
26
|
+
const { prompt, config_paths, adw_id, cwd } = ctx;
|
|
27
|
+
const cfg = agents.loadConfig(config_paths);
|
|
28
|
+
agents.validate(cfg, REQUIRED_AGENTS, REQUIRED_SUITES, cwd);
|
|
29
|
+
const run = session.ensure(cfg, adw_id, cwd);
|
|
30
|
+
await run.phase(makePhaseParams({ name: "request", kind: "engineer", owner: run.engineer, description: "Capture the incoming ask" }), async (ph) => {
|
|
31
|
+
ph.log({ input: prompt });
|
|
32
|
+
});
|
|
33
|
+
const plan = await run.phase(makePhaseParams({ name: "plan", kind: "agent", owner: "planner", description: "Turn the request into an implementable plan" }), (ph) => ph.call(makeAgentCall({ output_type: PlanOutput, prompt, gates: [gates.artifactsExist, gates.filesNonEmpty] })));
|
|
34
|
+
let previous = await run.phase(makePhaseParams({ name: "build", kind: "agent", owner: "builder", description: "Implement the plan exactly" }), (ph) => ph.call(makeAgentCall({ output_type: BuildOutput, prompt, previous: plan, gates: [gates.artifactsExist] })));
|
|
35
|
+
let test = null;
|
|
36
|
+
for (let i = 1; i <= MAX_FIX_LOOPS; i++) {
|
|
37
|
+
test = await run.phase(makePhaseParams({
|
|
38
|
+
name: `test_${i}`,
|
|
39
|
+
kind: "code",
|
|
40
|
+
owner: "quality",
|
|
41
|
+
description: "Run the suite — a known command, so code runs it and no agent has to rediscover it",
|
|
42
|
+
}), async (ph) => {
|
|
43
|
+
const result = quality.runTests(run);
|
|
44
|
+
record(ph, result);
|
|
45
|
+
return result;
|
|
46
|
+
});
|
|
47
|
+
if (test.passed)
|
|
48
|
+
break;
|
|
49
|
+
previous = await run.phase(makePhaseParams({
|
|
50
|
+
name: `fix_${i}`,
|
|
51
|
+
kind: "agent",
|
|
52
|
+
owner: "builder",
|
|
53
|
+
retries: 1,
|
|
54
|
+
description: "Repair what the suite reported, from its verbatim output",
|
|
55
|
+
}), (ph) => ph.call(makeAgentCall({ output_type: BuildOutput, prompt, previous: quality.asEnvelope(test, "tests"), gates: [gates.artifactsExist] })));
|
|
56
|
+
}
|
|
57
|
+
// Only tested work gets committed — a red suite leaves the tree uncommitted.
|
|
58
|
+
if (test !== null && test.passed) {
|
|
59
|
+
await run.phase(makePhaseParams({ name: "commit", kind: "code", owner: "git", description: "Land the code only after the suite came back green" }), async (ph) => {
|
|
60
|
+
const message = previous.commit_message || `spf(${run.adw_id}): ${previous.summary}`;
|
|
61
|
+
ph.log({ sha: run.git.commitAll(message), message });
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return run.finish(test !== null && test.passed, `the suite still failed after ${MAX_FIX_LOOPS} fix attempt(s)`);
|
|
65
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Plan Build Test Quality — full agent chain plus deterministic quality.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf plan-build-test-quality "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner -> builder -> [code(verify) -> code(test) -> builder(fix)] bounded -> git(commit)
|
|
8
|
+
*
|
|
9
|
+
* Verify and test are CODE, not agents. Their commands are known, so running them
|
|
10
|
+
* needs no judgement — only repairing them does. A failing block does not fail its
|
|
11
|
+
* phase: the runner did its job, the code is what failed. The failure becomes an
|
|
12
|
+
* envelope and flows back into the builder, and only an exhausted repair loop
|
|
13
|
+
* fails the run.
|
|
14
|
+
*/
|
|
15
|
+
import type { ChainContext } from "./context.ts";
|
|
16
|
+
export declare const REQUIRED_AGENTS: string[];
|
|
17
|
+
export declare const REQUIRED_SUITES: string[];
|
|
18
|
+
export declare function main(ctx: ChainContext): Promise<number>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Plan Build Test Quality — full agent chain plus deterministic quality.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf plan-build-test-quality "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner -> builder -> [code(verify) -> code(test) -> builder(fix)] bounded -> git(commit)
|
|
8
|
+
*
|
|
9
|
+
* Verify and test are CODE, not agents. Their commands are known, so running them
|
|
10
|
+
* needs no judgement — only repairing them does. A failing block does not fail its
|
|
11
|
+
* phase: the runner did its job, the code is what failed. The failure becomes an
|
|
12
|
+
* envelope and flows back into the builder, and only an exhausted repair loop
|
|
13
|
+
* fails the run.
|
|
14
|
+
*/
|
|
15
|
+
import * as agents from "../core/agents.js";
|
|
16
|
+
import * as gates from "../core/gates.js";
|
|
17
|
+
import * as quality from "../core/quality.js";
|
|
18
|
+
import * as session from "../core/session.js";
|
|
19
|
+
import { BuildOutput, PlanOutput, makeAgentCall, makePhaseParams } from "../core/data_types.js";
|
|
20
|
+
export const REQUIRED_AGENTS = ["planner", "builder"];
|
|
21
|
+
export const REQUIRED_SUITES = ["all"];
|
|
22
|
+
const MAX_FIX_LOOPS = 3;
|
|
23
|
+
function record(ph, result) {
|
|
24
|
+
const passed = result.checks.filter((c) => c.passed).length;
|
|
25
|
+
ph.log({ passed: result.passed, checks: `${passed}/${result.checks.length}`, artifacts: result.artifacts.join(", ") });
|
|
26
|
+
}
|
|
27
|
+
export async function main(ctx) {
|
|
28
|
+
const { prompt, config_paths, adw_id, cwd } = ctx;
|
|
29
|
+
const cfg = agents.loadConfig(config_paths);
|
|
30
|
+
agents.validate(cfg, REQUIRED_AGENTS, REQUIRED_SUITES, cwd);
|
|
31
|
+
const run = session.ensure(cfg, adw_id, cwd);
|
|
32
|
+
await run.phase(makePhaseParams({ name: "request", kind: "engineer", owner: run.engineer, description: "Capture the incoming ask" }), async (ph) => {
|
|
33
|
+
ph.log({ input: prompt });
|
|
34
|
+
});
|
|
35
|
+
const plan = await run.phase(makePhaseParams({ name: "plan", kind: "agent", owner: "planner", description: "Turn the request into an implementable plan" }), (ph) => ph.call(makeAgentCall({ output_type: PlanOutput, prompt, gates: [gates.artifactsExist, gates.filesNonEmpty] })));
|
|
36
|
+
let previous = await run.phase(makePhaseParams({ name: "build", kind: "agent", owner: "builder", description: "Implement the plan exactly" }), (ph) => ph.call(makeAgentCall({ output_type: BuildOutput, prompt, previous: plan, gates: [gates.diffMatchesClaims] })));
|
|
37
|
+
let testResult = null;
|
|
38
|
+
let qualityResult = null;
|
|
39
|
+
for (let i = 1; i <= MAX_FIX_LOOPS; i++) {
|
|
40
|
+
qualityResult = await run.phase(makePhaseParams({ name: `verify_${i}`, kind: "code", owner: "quality", description: "Lint, typecheck, and build before testing" }), async (ph) => {
|
|
41
|
+
const result = quality.runQuality(run);
|
|
42
|
+
record(ph, result);
|
|
43
|
+
return result;
|
|
44
|
+
});
|
|
45
|
+
// runQuality() already includes the test block; a repo that wants tests
|
|
46
|
+
// in their own phase can split them out the way this comment does.
|
|
47
|
+
testResult = qualityResult;
|
|
48
|
+
if (qualityResult.passed && testResult.passed)
|
|
49
|
+
break;
|
|
50
|
+
if (i === MAX_FIX_LOOPS)
|
|
51
|
+
break;
|
|
52
|
+
// Whichever block failed becomes the builder's spec — verbatim command
|
|
53
|
+
// output, no parser standing between the failure and the fix.
|
|
54
|
+
const broken = !qualityResult.passed ? qualityResult : testResult;
|
|
55
|
+
const what = !qualityResult.passed ? "verification" : "tests";
|
|
56
|
+
previous = await run.phase(makePhaseParams({ name: `fix_${i}`, kind: "agent", owner: "builder", retries: 1, description: `Resolve the reported ${what} failures` }), (ph) => ph.call(makeAgentCall({ output_type: BuildOutput, prompt, previous: quality.asEnvelope(broken, what), gates: [gates.diffMatchesClaims] })));
|
|
57
|
+
}
|
|
58
|
+
const verified = qualityResult !== null && qualityResult.passed && testResult !== null && testResult.passed;
|
|
59
|
+
if (verified) {
|
|
60
|
+
await run.phase(makePhaseParams({ name: "commit", kind: "code", owner: "git", description: "Commit the tested and quality-verified working tree" }), async (ph) => {
|
|
61
|
+
const message = previous.commit_message || `spf(${run.adw_id}): ${previous.summary}`;
|
|
62
|
+
ph.log({ sha: run.git.commitAll(message), message });
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return run.finish(verified, `verify/test never came back clean after ${MAX_FIX_LOOPS} fix attempt(s)`);
|
|
66
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Prompt — the smallest ADW: one agent, one prompt, traced end-to-end.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf prompt "<prompt or path/to/prompt.md>" [--agent builder] [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> <agent>
|
|
8
|
+
*/
|
|
9
|
+
import type { ChainContext } from "./context.ts";
|
|
10
|
+
export declare function main(ctx: ChainContext, options?: {
|
|
11
|
+
agent?: string;
|
|
12
|
+
}): Promise<number>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Prompt — the smallest ADW: one agent, one prompt, traced end-to-end.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf prompt "<prompt or path/to/prompt.md>" [--agent builder] [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> <agent>
|
|
8
|
+
*/
|
|
9
|
+
import * as agents from "../core/agents.js";
|
|
10
|
+
import * as session from "../core/session.js";
|
|
11
|
+
import { GenericOutput, makeAgentCall, makePhaseParams } from "../core/data_types.js";
|
|
12
|
+
export async function main(ctx, options = {}) {
|
|
13
|
+
const { prompt, config_paths, adw_id, cwd } = ctx;
|
|
14
|
+
const agent = options.agent ?? "builder";
|
|
15
|
+
const cfg = agents.loadConfig(config_paths);
|
|
16
|
+
agents.validate(cfg, [agent], [], cwd);
|
|
17
|
+
const run = session.ensure(cfg, adw_id, cwd);
|
|
18
|
+
await run.phase(makePhaseParams({ name: "request", kind: "engineer", owner: run.engineer, description: "Capture the incoming ask" }), async (ph) => {
|
|
19
|
+
ph.log({ input: prompt });
|
|
20
|
+
});
|
|
21
|
+
await run.phase(makePhaseParams({ name: "prompt", kind: "agent", owner: agent, description: `Send the request straight to ${agent} and parse its envelope` }), async (ph) => {
|
|
22
|
+
await ph.call(makeAgentCall({ output_type: GenericOutput, prompt }));
|
|
23
|
+
});
|
|
24
|
+
return run.finish();
|
|
25
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Quality — lint, typecheck, and build the project.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf quality "<reason for the quality run>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> code(quality)
|
|
8
|
+
*/
|
|
9
|
+
import type { ChainContext } from "./context.ts";
|
|
10
|
+
export declare const REQUIRED_AGENTS: string[];
|
|
11
|
+
export declare const REQUIRED_SUITES: string[];
|
|
12
|
+
export declare function main(ctx: ChainContext): Promise<number>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Quality — lint, typecheck, and build the project.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf quality "<reason for the quality run>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> code(quality)
|
|
8
|
+
*/
|
|
9
|
+
import * as agents from "../core/agents.js";
|
|
10
|
+
import * as quality from "../core/quality.js";
|
|
11
|
+
import * as session from "../core/session.js";
|
|
12
|
+
import { makePhaseParams } from "../core/data_types.js";
|
|
13
|
+
export const REQUIRED_AGENTS = [];
|
|
14
|
+
export const REQUIRED_SUITES = ["all"];
|
|
15
|
+
export async function main(ctx) {
|
|
16
|
+
const { prompt, config_paths, adw_id, cwd } = ctx;
|
|
17
|
+
const cfg = agents.loadConfig(config_paths);
|
|
18
|
+
agents.validate(cfg, REQUIRED_AGENTS, REQUIRED_SUITES, cwd);
|
|
19
|
+
const run = session.ensure(cfg, adw_id, cwd);
|
|
20
|
+
await run.phase(makePhaseParams({ name: "request", kind: "engineer", owner: run.engineer, description: "Capture why quality verification was requested" }), async (ph) => {
|
|
21
|
+
ph.log({ input: prompt });
|
|
22
|
+
});
|
|
23
|
+
await run.phase(makePhaseParams({ name: "quality", kind: "code", owner: "quality", description: "Run the deterministic quality blocks" }), async (ph) => {
|
|
24
|
+
const result = quality.runQuality(run);
|
|
25
|
+
const passed = result.checks.filter((c) => c.passed).length;
|
|
26
|
+
ph.log({ passed: result.passed, checks: `${passed}/${result.checks.length}`, artifacts: result.artifacts.join(", ") });
|
|
27
|
+
if (!result.passed) {
|
|
28
|
+
throw new Error("quality failed: " + result.failures.join("; "));
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return run.finish();
|
|
32
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Scout — read-only recon workflow. Just looking for stuff.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf scout "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> scout
|
|
8
|
+
*/
|
|
9
|
+
import type { ChainContext } from "./context.ts";
|
|
10
|
+
export declare const REQUIRED_AGENTS: string[];
|
|
11
|
+
export declare const REQUIRED_SUITES: string[];
|
|
12
|
+
export declare function main(ctx: ChainContext): Promise<number>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Scout — read-only recon workflow. Just looking for stuff.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf scout "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> scout
|
|
8
|
+
*/
|
|
9
|
+
import * as agents from "../core/agents.js";
|
|
10
|
+
import * as gates from "../core/gates.js";
|
|
11
|
+
import * as session from "../core/session.js";
|
|
12
|
+
import { makeAgentCall, makePhaseParams, ScoutOutput } from "../core/data_types.js";
|
|
13
|
+
export const REQUIRED_AGENTS = ["scout"];
|
|
14
|
+
export const REQUIRED_SUITES = [];
|
|
15
|
+
export async function main(ctx) {
|
|
16
|
+
const { prompt, config_paths, adw_id, cwd } = ctx;
|
|
17
|
+
const cfg = agents.loadConfig(config_paths);
|
|
18
|
+
agents.validate(cfg, REQUIRED_AGENTS, REQUIRED_SUITES, cwd);
|
|
19
|
+
const run = session.ensure(cfg, adw_id, cwd);
|
|
20
|
+
await run.phase(makePhaseParams({ name: "request", kind: "engineer", owner: run.engineer, description: "Capture the incoming ask" }), async (ph) => {
|
|
21
|
+
ph.log({ input: prompt });
|
|
22
|
+
});
|
|
23
|
+
await run.phase(makePhaseParams({ name: "scout", kind: "agent", owner: "scout", description: "Find and report where things live — change nothing" }), async (ph) => {
|
|
24
|
+
await ph.call(makeAgentCall({ output_type: ScoutOutput, prompt, gates: [gates.artifactsExist] }));
|
|
25
|
+
});
|
|
26
|
+
return run.finish();
|
|
27
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Simple SDLC — plan, build, test, review, document, committing as it goes.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf simple-sdlc "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner -> git(commit_plan)
|
|
8
|
+
* -> builder -> code(test) [-> builder(fix) -> code(test) ... bounded]
|
|
9
|
+
* -> reviewer [-> builder(revise) -> reviewer ... bounded]
|
|
10
|
+
* -> code(retest, only if a revision changed code)
|
|
11
|
+
* -> git(commit_build) -> code(changes) -> documenter -> git(commit_docs)
|
|
12
|
+
*
|
|
13
|
+
* Three commits, three work products, three authors. The plan, the code, and the
|
|
14
|
+
* write-up each land in their own commit, and each commit message is the words of
|
|
15
|
+
* the agent that produced it — `commit_message` on PlanOutput describes the spec,
|
|
16
|
+
* on BuildOutput the code, on DocumentOutput the write-up. No agent's sentence is
|
|
17
|
+
* ever reused for another agent's diff.
|
|
18
|
+
*
|
|
19
|
+
* Testing is CODE, not an agent. `bun test` is a command, not a judgement call:
|
|
20
|
+
* an agent rediscovering it every run costs a million tokens to learn what a
|
|
21
|
+
* subprocess already knows. Failures travel back to the builder as an envelope,
|
|
22
|
+
* so the repair loop is unchanged — only the runner became free and repeatable.
|
|
23
|
+
*
|
|
24
|
+
* Two different questions still get asked, in order. The suite asks "does it
|
|
25
|
+
* run"; the reviewer asks "is this what was asked for", against `plan.md` — and
|
|
26
|
+
* neither can answer the other's. A revision that closes a review finding
|
|
27
|
+
* re-enters the suite, so the tree that gets committed is the tree that was both
|
|
28
|
+
* tested and approved.
|
|
29
|
+
*
|
|
30
|
+
* The code commit lands after verification, not straight after the build: fixes
|
|
31
|
+
* and revisions are part of the same work product, and red code has no business
|
|
32
|
+
* on the branch. A run that fails verification therefore leaves the plan
|
|
33
|
+
* committed and the working tree dirty — the spec is a real artifact either way,
|
|
34
|
+
* and the unfinished code stays where the engineer can see it.
|
|
35
|
+
*
|
|
36
|
+
* The documenter measures against the commit this run STARTED from, not against
|
|
37
|
+
* `main`, because by then the run has moved `main` itself. That baseline is
|
|
38
|
+
* pinned before the first commit phase and printed in the request phase.
|
|
39
|
+
*/
|
|
40
|
+
import type { ChainContext } from "./context.ts";
|
|
41
|
+
export declare const REQUIRED_AGENTS: string[];
|
|
42
|
+
export declare const REQUIRED_SUITES: string[];
|
|
43
|
+
export declare function main(ctx: ChainContext): Promise<number>;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADW Simple SDLC — plan, build, test, review, document, committing as it goes.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* spf simple-sdlc "<prompt or path/to/prompt.md>" [--config <path>] [--adw-id a1b2c3d4]
|
|
6
|
+
*
|
|
7
|
+
* Phases: engineer(request) -> planner -> git(commit_plan)
|
|
8
|
+
* -> builder -> code(test) [-> builder(fix) -> code(test) ... bounded]
|
|
9
|
+
* -> reviewer [-> builder(revise) -> reviewer ... bounded]
|
|
10
|
+
* -> code(retest, only if a revision changed code)
|
|
11
|
+
* -> git(commit_build) -> code(changes) -> documenter -> git(commit_docs)
|
|
12
|
+
*
|
|
13
|
+
* Three commits, three work products, three authors. The plan, the code, and the
|
|
14
|
+
* write-up each land in their own commit, and each commit message is the words of
|
|
15
|
+
* the agent that produced it — `commit_message` on PlanOutput describes the spec,
|
|
16
|
+
* on BuildOutput the code, on DocumentOutput the write-up. No agent's sentence is
|
|
17
|
+
* ever reused for another agent's diff.
|
|
18
|
+
*
|
|
19
|
+
* Testing is CODE, not an agent. `bun test` is a command, not a judgement call:
|
|
20
|
+
* an agent rediscovering it every run costs a million tokens to learn what a
|
|
21
|
+
* subprocess already knows. Failures travel back to the builder as an envelope,
|
|
22
|
+
* so the repair loop is unchanged — only the runner became free and repeatable.
|
|
23
|
+
*
|
|
24
|
+
* Two different questions still get asked, in order. The suite asks "does it
|
|
25
|
+
* run"; the reviewer asks "is this what was asked for", against `plan.md` — and
|
|
26
|
+
* neither can answer the other's. A revision that closes a review finding
|
|
27
|
+
* re-enters the suite, so the tree that gets committed is the tree that was both
|
|
28
|
+
* tested and approved.
|
|
29
|
+
*
|
|
30
|
+
* The code commit lands after verification, not straight after the build: fixes
|
|
31
|
+
* and revisions are part of the same work product, and red code has no business
|
|
32
|
+
* on the branch. A run that fails verification therefore leaves the plan
|
|
33
|
+
* committed and the working tree dirty — the spec is a real artifact either way,
|
|
34
|
+
* and the unfinished code stays where the engineer can see it.
|
|
35
|
+
*
|
|
36
|
+
* The documenter measures against the commit this run STARTED from, not against
|
|
37
|
+
* `main`, because by then the run has moved `main` itself. That baseline is
|
|
38
|
+
* pinned before the first commit phase and printed in the request phase.
|
|
39
|
+
*/
|
|
40
|
+
import * as agents from "../core/agents.js";
|
|
41
|
+
import * as changes from "../core/changes.js";
|
|
42
|
+
import * as gates from "../core/gates.js";
|
|
43
|
+
import * as quality from "../core/quality.js";
|
|
44
|
+
import * as session from "../core/session.js";
|
|
45
|
+
import { BuildOutput, DocumentOutput, PlanOutput, ReviewOutput, makeAgentCall, makeChangeCapture, makePhaseParams, } from "../core/data_types.js";
|
|
46
|
+
export const REQUIRED_AGENTS = ["planner", "builder", "reviewer", "documenter"];
|
|
47
|
+
export const REQUIRED_SUITES = ["test"];
|
|
48
|
+
const MAX_FIX_LOOPS = 3;
|
|
49
|
+
const MAX_REVISION_LOOPS = 2;
|
|
50
|
+
const DOCUMENT_NOTES = "Read diff_path in full before writing. Document only what the diff shows, then copy the write-up into app_docs/ as your task describes.";
|
|
51
|
+
/** Commit what the preceding phase produced, in that agent's own words. */
|
|
52
|
+
function commit(run, ph, envelope) {
|
|
53
|
+
const message = envelope.commit_message || `spf(${run.adw_id}): ${envelope.summary}`;
|
|
54
|
+
ph.log({ sha: run.git.commitAll(message), message });
|
|
55
|
+
}
|
|
56
|
+
/** Log a deterministic block's verdict — the same shape every ADW uses. */
|
|
57
|
+
function record(ph, result) {
|
|
58
|
+
const passed = result.checks.filter((c) => c.passed).length;
|
|
59
|
+
ph.log({ passed: result.passed, checks: `${passed}/${result.checks.length}`, artifacts: result.artifacts.join(", ") });
|
|
60
|
+
}
|
|
61
|
+
export async function main(ctx) {
|
|
62
|
+
const { prompt, config_paths, adw_id, cwd } = ctx;
|
|
63
|
+
const cfg = agents.loadConfig(config_paths);
|
|
64
|
+
agents.validate(cfg, REQUIRED_AGENTS, REQUIRED_SUITES, cwd);
|
|
65
|
+
const run = session.ensure(cfg, adw_id, cwd);
|
|
66
|
+
const baseline = run.git.rev("HEAD"); // pinned before this run commits anything
|
|
67
|
+
await run.phase(makePhaseParams({ name: "request", kind: "engineer", owner: run.engineer, description: "Capture the incoming ask" }), async (ph) => {
|
|
68
|
+
ph.log({ input: prompt, baseline: run.git.shortSha(baseline) });
|
|
69
|
+
});
|
|
70
|
+
const plan = await run.phase(makePhaseParams({ name: "plan", kind: "agent", owner: "planner", description: "Turn the request into an implementable plan" }), (ph) => ph.call(makeAgentCall({ output_type: PlanOutput, prompt, gates: [gates.artifactsExist, gates.filesNonEmpty] })));
|
|
71
|
+
await run.phase(makePhaseParams({ name: "commit_plan", kind: "code", owner: "git", description: "Put the spec on record before any code exists to blur it" }), async (ph) => commit(run, ph, plan));
|
|
72
|
+
let build = await run.phase(makePhaseParams({ name: "build", kind: "agent", owner: "builder", description: "Implement the plan exactly" }), (ph) => ph.call(makeAgentCall({ output_type: BuildOutput, prompt, previous: plan, gates: [gates.diffMatchesClaims] })));
|
|
73
|
+
let test = null;
|
|
74
|
+
for (let i = 1; i <= MAX_FIX_LOOPS; i++) {
|
|
75
|
+
test = await run.phase(makePhaseParams({
|
|
76
|
+
name: `test_${i}`,
|
|
77
|
+
kind: "code",
|
|
78
|
+
owner: "quality",
|
|
79
|
+
description: "Run the suite — a known command, so code runs it and no agent has to rediscover it",
|
|
80
|
+
}), async (ph) => {
|
|
81
|
+
const result = quality.runTests(run);
|
|
82
|
+
record(ph, result);
|
|
83
|
+
return result;
|
|
84
|
+
});
|
|
85
|
+
if (test.passed)
|
|
86
|
+
break;
|
|
87
|
+
build = await run.phase(makePhaseParams({
|
|
88
|
+
name: `fix_${i}`,
|
|
89
|
+
kind: "agent",
|
|
90
|
+
owner: "builder",
|
|
91
|
+
retries: 1,
|
|
92
|
+
description: "Repair what the suite reported, from its verbatim output",
|
|
93
|
+
}), (ph) => ph.call(makeAgentCall({ output_type: BuildOutput, prompt, previous: quality.asEnvelope(test, "tests"), gates: [gates.diffMatchesClaims] })));
|
|
94
|
+
}
|
|
95
|
+
let review = null;
|
|
96
|
+
let revised = false;
|
|
97
|
+
for (let i = 1; i <= MAX_REVISION_LOOPS; i++) {
|
|
98
|
+
review = await run.phase(makePhaseParams({ name: `review_${i}`, kind: "agent", owner: "reviewer", description: "Confirm the build matches the plan" }), (ph) => ph.call(makeAgentCall({ output_type: ReviewOutput, prompt, previous: build, gates: [gates.artifactsExist, gates.verdictConsistent] })));
|
|
99
|
+
if (review.approved || i === MAX_REVISION_LOOPS)
|
|
100
|
+
break;
|
|
101
|
+
build = await run.phase(makePhaseParams({ name: `revise_${i}`, kind: "agent", owner: "builder", retries: 1, description: "Close the reviewer's blocking findings" }), (ph) => ph.call(makeAgentCall({ output_type: BuildOutput, prompt, previous: review, gates: [gates.diffMatchesClaims] })));
|
|
102
|
+
revised = true;
|
|
103
|
+
}
|
|
104
|
+
// A revision edited code after the suite last ran, so the green light is
|
|
105
|
+
// stale. Re-run it rather than commit on a result that predates the change.
|
|
106
|
+
if (revised && review !== null && review.approved) {
|
|
107
|
+
test = await run.phase(makePhaseParams({
|
|
108
|
+
name: "retest",
|
|
109
|
+
kind: "code",
|
|
110
|
+
owner: "quality",
|
|
111
|
+
description: "Re-run the suite — the revision changed code after the last green result",
|
|
112
|
+
}), async (ph) => {
|
|
113
|
+
const result = quality.runTests(run);
|
|
114
|
+
record(ph, result);
|
|
115
|
+
return result;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
// Red tests or a rejected review stop the chain here: the code stays
|
|
119
|
+
// uncommitted and nothing is documented, because there is nothing worth
|
|
120
|
+
// describing yet. The plan commit stands — it is a record of what was asked.
|
|
121
|
+
const verified = test !== null && test.passed && review !== null && review.approved;
|
|
122
|
+
if (verified) {
|
|
123
|
+
await run.phase(makePhaseParams({ name: "commit_build", kind: "code", owner: "git", description: "Land the code only now: green suite, approved review" }), async (ph) => commit(run, ph, build));
|
|
124
|
+
const changeset = await run.phase(makePhaseParams({ name: "changes", kind: "code", owner: "git", description: "Diff the whole run against its pinned baseline, for the documenter" }), async (ph) => {
|
|
125
|
+
const result = changes.capture(run, makeChangeCapture({ base: baseline }));
|
|
126
|
+
ph.log({
|
|
127
|
+
base: `${result.base.label} @ ${result.base.commit.slice(0, 7)}`,
|
|
128
|
+
reason: result.base.reason,
|
|
129
|
+
files: result.files.length + result.untracked.length,
|
|
130
|
+
lines: `+${result.insertions} -${result.deletions}`,
|
|
131
|
+
diff: result.diff_path,
|
|
132
|
+
});
|
|
133
|
+
if (result.empty) {
|
|
134
|
+
throw new Error(`nothing changed since ${result.base.label} (${result.base.reason}) — there is nothing to document.`);
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
});
|
|
138
|
+
const document = await run.phase(makePhaseParams({ name: "document", kind: "agent", owner: "documenter", retries: 1, description: "Write up the completed change" }), (ph) => ph.call(makeAgentCall({
|
|
139
|
+
output_type: DocumentOutput,
|
|
140
|
+
prompt,
|
|
141
|
+
previous: changes.asEnvelope(changeset, DOCUMENT_NOTES),
|
|
142
|
+
gates: [gates.artifactsExist, gates.filesNonEmpty],
|
|
143
|
+
})));
|
|
144
|
+
await run.phase(makePhaseParams({ name: "commit_docs", kind: "code", owner: "git", description: "Ship the write-up in its own commit, beside the code it describes" }), async (ph) => commit(run, ph, document));
|
|
145
|
+
}
|
|
146
|
+
return run.finish(verified, "the suite or the review never came back clean");
|
|
147
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every chain's `main()` receives. The CLI (or, later, the chain
|
|
3
|
+
* registry) resolves every path here — a chain never has its own opinion
|
|
4
|
+
* about where the config file or the working directory are, and none of the
|
|
5
|
+
* historical per-chain hardcoded config-path defaults survive this.
|
|
6
|
+
*/
|
|
7
|
+
export interface ChainContext {
|
|
8
|
+
prompt: string;
|
|
9
|
+
/**
|
|
10
|
+
* Absolute paths, in load order (agents.loadConfig merges them: later
|
|
11
|
+
* overrides earlier). The built-in packaged default, then an optional
|
|
12
|
+
* discovered `.spf/spf.config.yaml` — or just the one explicit `--config`
|
|
13
|
+
* path, standalone, with no built-in underneath it.
|
|
14
|
+
*/
|
|
15
|
+
config_paths: string[];
|
|
16
|
+
adw_id: string | null;
|
|
17
|
+
/** Absolute. The anchor session.ensure() resolves repo_root and data_dir from. */
|
|
18
|
+
cwd: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chain registry — the one place a CLI-facing short name maps to a
|
|
3
|
+
* chain module. `spf list` reads this; `spf <name>`/`spf run <name>` dispatch
|
|
4
|
+
* through it. Replaces guessing a module filename from the CLI argument.
|
|
5
|
+
*/
|
|
6
|
+
import type { ChainContext } from "./context.ts";
|
|
7
|
+
export interface ChainDefinition {
|
|
8
|
+
/** The name typed on the CLI: `spf <name> "..."`. */
|
|
9
|
+
name: string;
|
|
10
|
+
describe: string;
|
|
11
|
+
phases: string;
|
|
12
|
+
/** Static for most chains; adw_prompt's depends on --agent, so it's a function there. */
|
|
13
|
+
requiredAgents: string[] | ((options: Record<string, string>) => string[]);
|
|
14
|
+
requiredSuites: string[];
|
|
15
|
+
run: (ctx: ChainContext, options?: Record<string, string>) => Promise<number>;
|
|
16
|
+
}
|
|
17
|
+
export declare const CHAINS: ChainDefinition[];
|
|
18
|
+
export declare function findChain(name: string): ChainDefinition | undefined;
|
|
19
|
+
export declare function resolveRequiredAgents(chain: ChainDefinition, options: Record<string, string>): string[];
|