@agentic-coding-framework/orchestrator-core 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/cli.d.ts +15 -0
- package/dist/cli.js +199 -0
- package/dist/dispatch.d.ts +192 -0
- package/dist/dispatch.js +729 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +47 -0
- package/dist/rules.d.ts +65 -0
- package/dist/rules.js +366 -0
- package/dist/state.d.ts +81 -0
- package/dist/state.js +160 -0
- package/package.json +41 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cli.ts — CLI entry point for the Agentic Coding Orchestrator
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* init <project-root> <project-name> Initialize .ai/STATE.json
|
|
7
|
+
* start-story <project-root> <story-id> Begin a new User Story (micro-waterfall)
|
|
8
|
+
* start-custom <project-root> <instruction> Begin a custom ad-hoc task
|
|
9
|
+
* dispatch <project-root> Dispatch next step (prints prompt)
|
|
10
|
+
* apply-handoff <project-root> Parse HANDOFF.md → update STATE
|
|
11
|
+
* approve <project-root> [note] Approve review step
|
|
12
|
+
* reject <project-root> <reason> [note] Reject review step
|
|
13
|
+
* status <project-root> Print current STATE.json
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* cli.ts — CLI entry point for the Agentic Coding Orchestrator
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* init <project-root> <project-name> Initialize .ai/STATE.json
|
|
8
|
+
* start-story <project-root> <story-id> Begin a new User Story (micro-waterfall)
|
|
9
|
+
* start-custom <project-root> <instruction> Begin a custom ad-hoc task
|
|
10
|
+
* dispatch <project-root> Dispatch next step (prints prompt)
|
|
11
|
+
* apply-handoff <project-root> Parse HANDOFF.md → update STATE
|
|
12
|
+
* approve <project-root> [note] Approve review step
|
|
13
|
+
* reject <project-root> <reason> [note] Reject review step
|
|
14
|
+
* status <project-root> Print current STATE.json
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
const path_1 = require("path");
|
|
18
|
+
const child_process_1 = require("child_process");
|
|
19
|
+
const state_1 = require("./state");
|
|
20
|
+
const dispatch_1 = require("./dispatch");
|
|
21
|
+
const [, , command, ...args] = process.argv;
|
|
22
|
+
function usage() {
|
|
23
|
+
console.error(`Usage: orchestrator <command> [args]
|
|
24
|
+
|
|
25
|
+
Commands:
|
|
26
|
+
init <project-root> <project-name> Initialize .ai/STATE.json
|
|
27
|
+
start-story <project-root> <story-id> Begin a new User Story (micro-waterfall)
|
|
28
|
+
start-custom <project-root> <instruction> Begin a custom ad-hoc task
|
|
29
|
+
dispatch <project-root> Dispatch next step (prints prompt to stdout)
|
|
30
|
+
apply-handoff <project-root> Parse HANDOFF.md → update STATE.json
|
|
31
|
+
post-check <project-root> Run step's post_check command
|
|
32
|
+
approve <project-root> [note] Approve review step
|
|
33
|
+
reject <project-root> <reason> [note] Reject review step
|
|
34
|
+
status <project-root> Print current STATE.json
|
|
35
|
+
query <project-root> Project status summary (for OpenClaw)
|
|
36
|
+
detect <project-root> Check if project uses the framework
|
|
37
|
+
list-projects <workspace-root> List all projects in workspace
|
|
38
|
+
`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
function resolveRoot(raw) {
|
|
42
|
+
if (!raw) {
|
|
43
|
+
console.error("Error: <project-root> is required");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
return (0, path_1.resolve)(raw);
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
switch (command) {
|
|
50
|
+
case "init": {
|
|
51
|
+
const projectRoot = resolveRoot(args[0]);
|
|
52
|
+
const projectName = args[1];
|
|
53
|
+
if (!projectName) {
|
|
54
|
+
console.error("Error: <project-name> is required");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const { created, state } = (0, state_1.initState)(projectRoot, projectName);
|
|
58
|
+
if (created) {
|
|
59
|
+
console.log(`Initialized .ai/STATE.json for "${projectName}"`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(`STATE.json already exists (step: ${state.step}, status: ${state.status})`);
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case "start-story": {
|
|
67
|
+
const projectRoot = resolveRoot(args[0]);
|
|
68
|
+
const storyId = args[1];
|
|
69
|
+
if (!storyId) {
|
|
70
|
+
console.error("Error: <story-id> is required");
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const state = (0, dispatch_1.startStory)(projectRoot, storyId);
|
|
74
|
+
console.log(`Started story ${storyId} (step: ${state.step}, attempt: ${state.attempt})`);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case "start-custom": {
|
|
78
|
+
const projectRoot = resolveRoot(args[0]);
|
|
79
|
+
const instruction = args[1];
|
|
80
|
+
if (!instruction) {
|
|
81
|
+
console.error("Error: <instruction> is required");
|
|
82
|
+
console.error('Example: orchestrator start-custom ./project "Refactor auth module into separate package"');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
const label = args[2] || undefined; // optional label
|
|
86
|
+
const state = (0, dispatch_1.startCustom)(projectRoot, instruction, label);
|
|
87
|
+
console.log(`Started custom task: "${instruction}"`);
|
|
88
|
+
console.log(` label: ${state.story}, step: ${state.step}, task_type: ${state.task_type}`);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case "dispatch": {
|
|
92
|
+
const projectRoot = resolveRoot(args[0]);
|
|
93
|
+
const result = (0, dispatch_1.dispatch)(projectRoot);
|
|
94
|
+
switch (result.type) {
|
|
95
|
+
case "dispatched":
|
|
96
|
+
// Print prompt to stdout (can be piped to claude -p)
|
|
97
|
+
console.log(result.prompt);
|
|
98
|
+
// Print metadata to stderr so it doesn't pollute prompt
|
|
99
|
+
console.error(`[dispatch] step=${result.step} attempt=${result.attempt}`);
|
|
100
|
+
break;
|
|
101
|
+
case "done":
|
|
102
|
+
console.error(`[dispatch] DONE: ${result.summary}`);
|
|
103
|
+
break;
|
|
104
|
+
case "needs_human":
|
|
105
|
+
console.error(`[dispatch] NEEDS HUMAN REVIEW`);
|
|
106
|
+
console.error(result.message);
|
|
107
|
+
break;
|
|
108
|
+
case "blocked":
|
|
109
|
+
console.error(`[dispatch] BLOCKED: ${result.reason}`);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
break;
|
|
112
|
+
case "already_running":
|
|
113
|
+
console.error(`[dispatch] Already running (${result.elapsed_min} min)`);
|
|
114
|
+
break;
|
|
115
|
+
case "timeout":
|
|
116
|
+
console.error(`[dispatch] TIMEOUT at ${result.step} (${result.elapsed_min} min)`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
case "apply-handoff": {
|
|
123
|
+
const projectRoot = resolveRoot(args[0]);
|
|
124
|
+
const state = (0, dispatch_1.applyHandoff)(projectRoot);
|
|
125
|
+
console.log(`Applied HANDOFF → step: ${state.step}, status: ${state.status}, reason: ${state.reason ?? "(none)"}`);
|
|
126
|
+
if (state.tests) {
|
|
127
|
+
console.log(` tests: pass=${state.tests.pass} fail=${state.tests.fail} skip=${state.tests.skip}`);
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case "post-check": {
|
|
132
|
+
const projectRoot = resolveRoot(args[0]);
|
|
133
|
+
const passed = (0, dispatch_1.runPostCheck)(projectRoot, child_process_1.execSync);
|
|
134
|
+
console.log(passed ? "post_check: PASSED" : "post_check: FAILED");
|
|
135
|
+
if (!passed)
|
|
136
|
+
process.exit(1);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "approve": {
|
|
140
|
+
const projectRoot = resolveRoot(args[0]);
|
|
141
|
+
const note = args[1] || undefined;
|
|
142
|
+
(0, dispatch_1.approveReview)(projectRoot, note);
|
|
143
|
+
console.log(`Review approved${note ? ` (note: "${note}")` : ""}`);
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case "reject": {
|
|
147
|
+
const projectRoot = resolveRoot(args[0]);
|
|
148
|
+
const reason = args[1];
|
|
149
|
+
if (!reason) {
|
|
150
|
+
console.error("Error: <reason> is required (constitution_violation, needs_clarification, nfr_missing, scope_warning, test_timeout)");
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
const note = args[2] || undefined;
|
|
154
|
+
(0, dispatch_1.rejectReview)(projectRoot, reason, note);
|
|
155
|
+
console.log(`Review rejected (reason: ${reason}${note ? `, note: "${note}"` : ""})`);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "status": {
|
|
159
|
+
const projectRoot = resolveRoot(args[0]);
|
|
160
|
+
const state = (0, state_1.readState)(projectRoot);
|
|
161
|
+
console.log(JSON.stringify(state, null, 2));
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case "query": {
|
|
165
|
+
const projectRoot = resolveRoot(args[0]);
|
|
166
|
+
const status = (0, dispatch_1.queryProjectStatus)(projectRoot);
|
|
167
|
+
console.log(JSON.stringify(status, null, 2));
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case "detect": {
|
|
171
|
+
const projectRoot = resolveRoot(args[0]);
|
|
172
|
+
const result = (0, dispatch_1.detectFramework)(projectRoot);
|
|
173
|
+
console.log(JSON.stringify(result, null, 2));
|
|
174
|
+
const levelNames = ["Not adopted", "Partial adoption", "Full adoption"];
|
|
175
|
+
console.error(`Framework adoption: Level ${result.level} — ${levelNames[result.level]}`);
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
case "list-projects": {
|
|
179
|
+
const workspaceRoot = resolveRoot(args[0]);
|
|
180
|
+
const projects = (0, dispatch_1.listProjects)(workspaceRoot);
|
|
181
|
+
if (projects.length === 0) {
|
|
182
|
+
console.log("No projects found with .ai/STATE.json");
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
console.log(JSON.stringify(projects, null, 2));
|
|
186
|
+
}
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
default:
|
|
190
|
+
if (command) {
|
|
191
|
+
console.error(`Unknown command: ${command}`);
|
|
192
|
+
}
|
|
193
|
+
usage();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
console.error(`Error: ${err.message}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dispatch.ts — Orchestrator State Machine + Prompt Builder + Handoff Parser
|
|
3
|
+
*
|
|
4
|
+
* The only file with logic. Combines state.ts (read/write) and rules.ts (lookup)
|
|
5
|
+
* to drive the micro-waterfall pipeline. All decisions are deterministic —
|
|
6
|
+
* zero LLM tokens.
|
|
7
|
+
*
|
|
8
|
+
* Main entry point: dispatch(projectRoot)
|
|
9
|
+
*/
|
|
10
|
+
import { type State, type Step, type Status, type Reason } from "./state";
|
|
11
|
+
import { type StepRule } from "./rules";
|
|
12
|
+
export type DispatchResult = {
|
|
13
|
+
type: "dispatched";
|
|
14
|
+
step: Step;
|
|
15
|
+
attempt: number;
|
|
16
|
+
prompt: string;
|
|
17
|
+
fw_lv: 0 | 1 | 2;
|
|
18
|
+
} | {
|
|
19
|
+
type: "blocked";
|
|
20
|
+
step: Step;
|
|
21
|
+
reason: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: "needs_human";
|
|
24
|
+
step: Step;
|
|
25
|
+
message: string;
|
|
26
|
+
} | {
|
|
27
|
+
type: "done";
|
|
28
|
+
story: string;
|
|
29
|
+
summary: string;
|
|
30
|
+
} | {
|
|
31
|
+
type: "already_running";
|
|
32
|
+
step: Step;
|
|
33
|
+
elapsed_min: number;
|
|
34
|
+
} | {
|
|
35
|
+
type: "timeout";
|
|
36
|
+
step: Step;
|
|
37
|
+
elapsed_min: number;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Core dispatch logic — direct translation of Protocol's dispatch(project).
|
|
41
|
+
*
|
|
42
|
+
* Reads STATE.json, applies the step rules table, and returns a DispatchResult
|
|
43
|
+
* telling the caller what to do next. The caller is responsible for actually
|
|
44
|
+
* invoking the executor (e.g., spawning a Claude Code session).
|
|
45
|
+
*
|
|
46
|
+
* This function updates STATE.json as a side effect (marks running, advances
|
|
47
|
+
* steps, etc.).
|
|
48
|
+
*/
|
|
49
|
+
export declare function dispatch(projectRoot: string): DispatchResult;
|
|
50
|
+
/**
|
|
51
|
+
* Build the dispatch prompt from the template.
|
|
52
|
+
* Pure template filling — zero LLM reasoning.
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildPrompt(state: State, rule: StepRule): string;
|
|
55
|
+
/** Parsed result from HANDOFF.md's YAML front matter */
|
|
56
|
+
export interface HandoffResult {
|
|
57
|
+
story: string | null;
|
|
58
|
+
step: string | null;
|
|
59
|
+
attempt: number | null;
|
|
60
|
+
status: Status | null;
|
|
61
|
+
reason: Reason | null;
|
|
62
|
+
files_changed: string[];
|
|
63
|
+
tests_pass: number | null;
|
|
64
|
+
tests_fail: number | null;
|
|
65
|
+
tests_skip: number | null;
|
|
66
|
+
/** The markdown body (everything after the second ---) */
|
|
67
|
+
body: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Parse HANDOFF.md — prioritize YAML front matter, fallback to grep.
|
|
71
|
+
* Direct translation of Protocol's hook pseudocode.
|
|
72
|
+
*/
|
|
73
|
+
export declare function parseHandoff(projectRoot: string): HandoffResult | null;
|
|
74
|
+
/**
|
|
75
|
+
* After executor exits, read HANDOFF.md and update STATE.json accordingly.
|
|
76
|
+
* This is the TypeScript equivalent of the Protocol's post-execution hook.
|
|
77
|
+
*
|
|
78
|
+
* Call this after the executor process exits, before the next dispatch().
|
|
79
|
+
*/
|
|
80
|
+
export declare function applyHandoff(projectRoot: string): State;
|
|
81
|
+
/**
|
|
82
|
+
* Run the post_check command for the current step.
|
|
83
|
+
* Returns true if check passed (or no check defined), false if failed.
|
|
84
|
+
*
|
|
85
|
+
* This is a synchronous shell execution — zero LLM tokens.
|
|
86
|
+
*/
|
|
87
|
+
export declare function runPostCheck(projectRoot: string, execSync: (cmd: string, opts: object) => Buffer): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Mark the review step as approved by human.
|
|
90
|
+
* Optionally attach a human note (modification requests, clarifications).
|
|
91
|
+
*/
|
|
92
|
+
export declare function approveReview(projectRoot: string, humanNote?: string): void;
|
|
93
|
+
/**
|
|
94
|
+
* Reject the review with a reason and optional note.
|
|
95
|
+
*/
|
|
96
|
+
export declare function rejectReview(projectRoot: string, reason: Reason, humanNote?: string): void;
|
|
97
|
+
/**
|
|
98
|
+
* Begin a new User Story. Resets state to bdd step with attempt 1.
|
|
99
|
+
* Auto-initializes STATE.json if the project hasn't adopted the framework yet.
|
|
100
|
+
*/
|
|
101
|
+
export declare function startStory(projectRoot: string, storyId: string): State;
|
|
102
|
+
/**
|
|
103
|
+
* Get a human-friendly project status summary.
|
|
104
|
+
* OpenClaw calls this when the user asks "how's the project?" or "open project X".
|
|
105
|
+
*
|
|
106
|
+
* Returns structured data that OpenClaw LLM can translate to natural language.
|
|
107
|
+
*/
|
|
108
|
+
export interface ProjectStatus {
|
|
109
|
+
project: string;
|
|
110
|
+
task_type: string;
|
|
111
|
+
story: string | null;
|
|
112
|
+
step: string;
|
|
113
|
+
status: string;
|
|
114
|
+
attempt: number;
|
|
115
|
+
max_attempts: number;
|
|
116
|
+
reason: string | null;
|
|
117
|
+
tests: {
|
|
118
|
+
pass: number;
|
|
119
|
+
fail: number;
|
|
120
|
+
skip: number;
|
|
121
|
+
} | null;
|
|
122
|
+
lint_pass: boolean | null;
|
|
123
|
+
files_changed: string[];
|
|
124
|
+
blocked_by: string[];
|
|
125
|
+
human_note: string | null;
|
|
126
|
+
memory_summary: string | null;
|
|
127
|
+
has_framework: FrameworkDetection;
|
|
128
|
+
}
|
|
129
|
+
export interface FrameworkDetection {
|
|
130
|
+
has_state: boolean;
|
|
131
|
+
has_memory: boolean;
|
|
132
|
+
has_context: boolean;
|
|
133
|
+
has_constitution: boolean;
|
|
134
|
+
has_sdd: boolean;
|
|
135
|
+
has_handoff: boolean;
|
|
136
|
+
has_history: boolean;
|
|
137
|
+
/** Adoption level: 0 = none, 1 = partial (some files), 2 = full (all core files) */
|
|
138
|
+
level: 0 | 1 | 2;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Detect whether a project uses the Agentic Coding Framework.
|
|
142
|
+
* OpenClaw calls this when the user asks "is this project using the framework?"
|
|
143
|
+
*/
|
|
144
|
+
export declare function detectFramework(projectRoot: string): FrameworkDetection;
|
|
145
|
+
/**
|
|
146
|
+
* Get comprehensive project status for OpenClaw to summarize to the user.
|
|
147
|
+
* Works for ANY project — with or without the Agentic Coding Framework.
|
|
148
|
+
*
|
|
149
|
+
* - Framework project (has STATE.json): returns full state + memory summary
|
|
150
|
+
* - Non-framework project: returns framework detection + whatever files exist
|
|
151
|
+
*/
|
|
152
|
+
export declare function queryProjectStatus(projectRoot: string): ProjectStatus;
|
|
153
|
+
/** Project entry returned by listProjects */
|
|
154
|
+
export interface ProjectEntry {
|
|
155
|
+
/** Project name (from STATE.json, package.json, or directory name) */
|
|
156
|
+
name: string;
|
|
157
|
+
/** Directory name relative to workspace root */
|
|
158
|
+
dir: string;
|
|
159
|
+
/** Current step ("none" if not using framework) */
|
|
160
|
+
step: string;
|
|
161
|
+
/** Current status ("not_initialized" if not using framework) */
|
|
162
|
+
status: string;
|
|
163
|
+
/** Current story ID */
|
|
164
|
+
story: string | null;
|
|
165
|
+
/** Whether this project uses the Agentic Coding Framework */
|
|
166
|
+
has_framework: boolean;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Scan a workspace directory for all projects — both framework and non-framework.
|
|
170
|
+
* OpenClaw calls this when the user asks "list my projects" or "switch project".
|
|
171
|
+
*
|
|
172
|
+
* A directory is considered a "project" if it contains any of:
|
|
173
|
+
* - .ai/STATE.json (framework project)
|
|
174
|
+
* - package.json (Node.js project)
|
|
175
|
+
* - go.mod (Go project)
|
|
176
|
+
* - Cargo.toml (Rust project)
|
|
177
|
+
* - pyproject.toml or setup.py (Python project)
|
|
178
|
+
* - .git/ (any git repo)
|
|
179
|
+
*/
|
|
180
|
+
export declare function listProjects(workspaceRoot: string): ProjectEntry[];
|
|
181
|
+
/**
|
|
182
|
+
* Begin a custom (ad-hoc) task. The orchestrator forwards the instruction
|
|
183
|
+
* to Claude Code with full project context, without going through the
|
|
184
|
+
* micro-waterfall pipeline.
|
|
185
|
+
*
|
|
186
|
+
* Pipeline: custom → update-memory → done
|
|
187
|
+
* Auto-initializes STATE.json if the project hasn't adopted the framework yet.
|
|
188
|
+
*
|
|
189
|
+
* Use cases: refactoring, code review, bug fix, DevOps, documentation,
|
|
190
|
+
* testing, migration, performance optimization, security, cleanup, etc.
|
|
191
|
+
*/
|
|
192
|
+
export declare function startCustom(projectRoot: string, instruction: string, label?: string): State;
|