@agentic-coding-framework/orchestrator-core 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto.d.ts +105 -0
- package/dist/auto.js +212 -0
- package/dist/cli.js +54 -0
- package/dist/dispatch.js +13 -7
- package/dist/index.d.ts +2 -1
- package/dist/index.js +7 -1
- package/dist/state.d.ts +13 -0
- package/dist/state.js +68 -0
- package/package.json +1 -1
package/dist/auto.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auto.ts — Unified entry point for OpenClaw integration
|
|
3
|
+
*
|
|
4
|
+
* Classifies a raw user message into an action (query, dispatch, approve, etc.)
|
|
5
|
+
* using keyword-based matching (zero LLM tokens), then routes to the appropriate
|
|
6
|
+
* orchestrator function.
|
|
7
|
+
*
|
|
8
|
+
* OpenClaw only needs to call:
|
|
9
|
+
* orchestrator auto <project-root> "<user message>"
|
|
10
|
+
*
|
|
11
|
+
* and gets back a JSON result with an `action` field telling it what happened.
|
|
12
|
+
*/
|
|
13
|
+
import type { Reason } from "./state";
|
|
14
|
+
import { type ProjectStatus, type FrameworkDetection, type ProjectEntry } from "./dispatch";
|
|
15
|
+
export type AutoResult = {
|
|
16
|
+
action: "query";
|
|
17
|
+
data: ProjectStatus;
|
|
18
|
+
memory?: string;
|
|
19
|
+
handoff?: string;
|
|
20
|
+
} | {
|
|
21
|
+
action: "dispatched";
|
|
22
|
+
step: string;
|
|
23
|
+
attempt: number;
|
|
24
|
+
prompt: string;
|
|
25
|
+
fw_lv: number;
|
|
26
|
+
} | {
|
|
27
|
+
action: "done";
|
|
28
|
+
story: string;
|
|
29
|
+
summary: string;
|
|
30
|
+
} | {
|
|
31
|
+
action: "needs_human";
|
|
32
|
+
step: string;
|
|
33
|
+
message: string;
|
|
34
|
+
} | {
|
|
35
|
+
action: "blocked";
|
|
36
|
+
step: string;
|
|
37
|
+
reason: string;
|
|
38
|
+
} | {
|
|
39
|
+
action: "already_running";
|
|
40
|
+
step: string;
|
|
41
|
+
elapsed_min: number;
|
|
42
|
+
} | {
|
|
43
|
+
action: "timeout";
|
|
44
|
+
step: string;
|
|
45
|
+
elapsed_min: number;
|
|
46
|
+
} | {
|
|
47
|
+
action: "approved";
|
|
48
|
+
note?: string;
|
|
49
|
+
} | {
|
|
50
|
+
action: "rejected";
|
|
51
|
+
reason: string;
|
|
52
|
+
note?: string;
|
|
53
|
+
} | {
|
|
54
|
+
action: "detected";
|
|
55
|
+
framework: FrameworkDetection;
|
|
56
|
+
} | {
|
|
57
|
+
action: "listed";
|
|
58
|
+
projects: ProjectEntry[];
|
|
59
|
+
} | {
|
|
60
|
+
action: "error";
|
|
61
|
+
message: string;
|
|
62
|
+
};
|
|
63
|
+
type Intent = {
|
|
64
|
+
type: "query";
|
|
65
|
+
} | {
|
|
66
|
+
type: "approve";
|
|
67
|
+
note?: string;
|
|
68
|
+
} | {
|
|
69
|
+
type: "reject";
|
|
70
|
+
reason: Reason;
|
|
71
|
+
note?: string;
|
|
72
|
+
} | {
|
|
73
|
+
type: "start_story";
|
|
74
|
+
storyId: string;
|
|
75
|
+
} | {
|
|
76
|
+
type: "continue";
|
|
77
|
+
} | {
|
|
78
|
+
type: "detect";
|
|
79
|
+
} | {
|
|
80
|
+
type: "list";
|
|
81
|
+
} | {
|
|
82
|
+
type: "custom";
|
|
83
|
+
instruction: string;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Classify a raw user message into an intent.
|
|
87
|
+
* Pure keyword/regex matching — zero LLM tokens.
|
|
88
|
+
*
|
|
89
|
+
* Priority order matters: more specific patterns are checked first.
|
|
90
|
+
*/
|
|
91
|
+
export declare function classify(message: string): Intent;
|
|
92
|
+
/**
|
|
93
|
+
* Unified orchestrator entry point.
|
|
94
|
+
*
|
|
95
|
+
* Takes a project root and a raw user message, classifies the intent,
|
|
96
|
+
* and routes to the appropriate function. Returns a JSON-serializable result.
|
|
97
|
+
*
|
|
98
|
+
* Usage:
|
|
99
|
+
* orchestrator auto ./project "目前狀態如何"
|
|
100
|
+
* orchestrator auto ./project "繼續"
|
|
101
|
+
* orchestrator auto ./project "幫我 refactor auth module"
|
|
102
|
+
* orchestrator auto ./project "approve"
|
|
103
|
+
*/
|
|
104
|
+
export declare function auto(projectRoot: string, message: string): AutoResult;
|
|
105
|
+
export {};
|
package/dist/auto.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* auto.ts — Unified entry point for OpenClaw integration
|
|
4
|
+
*
|
|
5
|
+
* Classifies a raw user message into an action (query, dispatch, approve, etc.)
|
|
6
|
+
* using keyword-based matching (zero LLM tokens), then routes to the appropriate
|
|
7
|
+
* orchestrator function.
|
|
8
|
+
*
|
|
9
|
+
* OpenClaw only needs to call:
|
|
10
|
+
* orchestrator auto <project-root> "<user message>"
|
|
11
|
+
*
|
|
12
|
+
* and gets back a JSON result with an `action` field telling it what happened.
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.classify = classify;
|
|
16
|
+
exports.auto = auto;
|
|
17
|
+
const fs_1 = require("fs");
|
|
18
|
+
const path_1 = require("path");
|
|
19
|
+
const dispatch_1 = require("./dispatch");
|
|
20
|
+
/**
|
|
21
|
+
* Classify a raw user message into an intent.
|
|
22
|
+
* Pure keyword/regex matching — zero LLM tokens.
|
|
23
|
+
*
|
|
24
|
+
* Priority order matters: more specific patterns are checked first.
|
|
25
|
+
*/
|
|
26
|
+
function classify(message) {
|
|
27
|
+
const msg = message.trim();
|
|
28
|
+
const lower = msg.toLowerCase();
|
|
29
|
+
// ── Approve ──
|
|
30
|
+
if (/^(approve|核准|lgtm|通過|approved|ok\s*$)/i.test(msg)) {
|
|
31
|
+
const note = msg.replace(/^(approve|核准|lgtm|通過|approved|ok)\s*/i, "").trim() || undefined;
|
|
32
|
+
return { type: "approve", note };
|
|
33
|
+
}
|
|
34
|
+
// ── Reject ──
|
|
35
|
+
if (/^(reject|退回|不行|rejected)/i.test(msg)) {
|
|
36
|
+
const rest = msg.replace(/^(reject|退回|不行|rejected)\s*/i, "").trim();
|
|
37
|
+
const { reason, note } = extractRejectReason(rest);
|
|
38
|
+
return { type: "reject", reason, note };
|
|
39
|
+
}
|
|
40
|
+
// ── Start Story (must be before query — "start US-007" is not a query) ──
|
|
41
|
+
const storyMatch = msg.match(/(?:start\s*story|開始\s*story|開新\s*story|start)\s+(US-\d+)/i)
|
|
42
|
+
|| msg.match(/^(US-\d+)/i);
|
|
43
|
+
if (storyMatch) {
|
|
44
|
+
return { type: "start_story", storyId: storyMatch[1] };
|
|
45
|
+
}
|
|
46
|
+
// ── List projects ──
|
|
47
|
+
if (/列出|list.*project|所有專案|all\s*projects/i.test(lower)) {
|
|
48
|
+
return { type: "list" };
|
|
49
|
+
}
|
|
50
|
+
// ── Detect framework ──
|
|
51
|
+
if (/framework|有沒有用|adoption|detect/i.test(lower)) {
|
|
52
|
+
return { type: "detect" };
|
|
53
|
+
}
|
|
54
|
+
// ── Query (information requests — no code changes) ──
|
|
55
|
+
if (isQuery(lower)) {
|
|
56
|
+
return { type: "query" };
|
|
57
|
+
}
|
|
58
|
+
// ── Continue / Dispatch ──
|
|
59
|
+
if (/^(繼續|continue|dispatch|next|下一步|run|執行|go|proceed)\s*$/i.test(msg)) {
|
|
60
|
+
return { type: "continue" };
|
|
61
|
+
}
|
|
62
|
+
// ── Fallback: Custom task ──
|
|
63
|
+
return { type: "custom", instruction: msg };
|
|
64
|
+
}
|
|
65
|
+
/** Check if the message is a read-only query */
|
|
66
|
+
function isQuery(lower) {
|
|
67
|
+
const queryPatterns = [
|
|
68
|
+
/狀態/, /status/, /什麼步驟/, /what\s*step/, /which\s*step/,
|
|
69
|
+
/測試/, /tests?/, /進度/, /progress/,
|
|
70
|
+
/還有什麼/, /what's\s*next/, /what\s*next/, /what's\s*left/,
|
|
71
|
+
/history/, /歷史/, /上次/, /last\s*(session|time)/,
|
|
72
|
+
/怎麼樣/, /how.*project/, /how.*going/,
|
|
73
|
+
/目前/, /current/, /看一下/, /看看/, /查一下/, /check\s*status/,
|
|
74
|
+
/哪個步驟/, /卡住/, /blocked/, /why.*block/,
|
|
75
|
+
/summary/, /摘要/, /report/, /報告/,
|
|
76
|
+
/打開/, /open\s*project/,
|
|
77
|
+
];
|
|
78
|
+
return queryPatterns.some((p) => p.test(lower));
|
|
79
|
+
}
|
|
80
|
+
/** Extract reject reason from the rest of the message */
|
|
81
|
+
function extractRejectReason(rest) {
|
|
82
|
+
const reasonMap = {
|
|
83
|
+
clarification: "needs_clarification",
|
|
84
|
+
需要說明: "needs_clarification",
|
|
85
|
+
不清楚: "needs_clarification",
|
|
86
|
+
constitution: "constitution_violation",
|
|
87
|
+
架構違反: "constitution_violation",
|
|
88
|
+
scope: "scope_warning",
|
|
89
|
+
範圍: "scope_warning",
|
|
90
|
+
nfr: "nfr_missing",
|
|
91
|
+
timeout: "test_timeout",
|
|
92
|
+
超時: "test_timeout",
|
|
93
|
+
};
|
|
94
|
+
for (const [keyword, reason] of Object.entries(reasonMap)) {
|
|
95
|
+
if (rest.toLowerCase().includes(keyword)) {
|
|
96
|
+
const note = rest.replace(new RegExp(keyword, "i"), "").trim() || undefined;
|
|
97
|
+
return { reason, note };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Default reason
|
|
101
|
+
return { reason: "needs_clarification", note: rest || undefined };
|
|
102
|
+
}
|
|
103
|
+
// ─── Main Entry Point ───────────────────────────────────────────────────────
|
|
104
|
+
/**
|
|
105
|
+
* Unified orchestrator entry point.
|
|
106
|
+
*
|
|
107
|
+
* Takes a project root and a raw user message, classifies the intent,
|
|
108
|
+
* and routes to the appropriate function. Returns a JSON-serializable result.
|
|
109
|
+
*
|
|
110
|
+
* Usage:
|
|
111
|
+
* orchestrator auto ./project "目前狀態如何"
|
|
112
|
+
* orchestrator auto ./project "繼續"
|
|
113
|
+
* orchestrator auto ./project "幫我 refactor auth module"
|
|
114
|
+
* orchestrator auto ./project "approve"
|
|
115
|
+
*/
|
|
116
|
+
function auto(projectRoot, message) {
|
|
117
|
+
const intent = classify(message);
|
|
118
|
+
try {
|
|
119
|
+
switch (intent.type) {
|
|
120
|
+
case "query":
|
|
121
|
+
return handleQuery(projectRoot);
|
|
122
|
+
case "approve":
|
|
123
|
+
return handleApprove(projectRoot, intent.note);
|
|
124
|
+
case "reject":
|
|
125
|
+
return handleReject(projectRoot, intent.reason, intent.note);
|
|
126
|
+
case "start_story":
|
|
127
|
+
return handleStartStory(projectRoot, intent.storyId);
|
|
128
|
+
case "continue":
|
|
129
|
+
return handleDispatch(projectRoot);
|
|
130
|
+
case "detect":
|
|
131
|
+
return { action: "detected", framework: (0, dispatch_1.detectFramework)(projectRoot) };
|
|
132
|
+
case "list":
|
|
133
|
+
// Use projectRoot as workspace root for listing
|
|
134
|
+
return { action: "listed", projects: (0, dispatch_1.listProjects)(projectRoot) };
|
|
135
|
+
case "custom":
|
|
136
|
+
return handleCustom(projectRoot, intent.instruction);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
return { action: "error", message: err.message ?? String(err) };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// ─── Intent Handlers ────────────────────────────────────────────────────────
|
|
144
|
+
function handleQuery(projectRoot) {
|
|
145
|
+
const data = (0, dispatch_1.queryProjectStatus)(projectRoot);
|
|
146
|
+
// Attach memory and handoff content if available
|
|
147
|
+
let memory;
|
|
148
|
+
let handoff;
|
|
149
|
+
const memoryPath = (0, path_1.join)(projectRoot, "PROJECT_MEMORY.md");
|
|
150
|
+
if ((0, fs_1.existsSync)(memoryPath)) {
|
|
151
|
+
memory = (0, fs_1.readFileSync)(memoryPath, "utf-8").slice(0, 2000);
|
|
152
|
+
}
|
|
153
|
+
const handoffPath = (0, path_1.join)(projectRoot, ".ai", "HANDOFF.md");
|
|
154
|
+
if ((0, fs_1.existsSync)(handoffPath)) {
|
|
155
|
+
handoff = (0, fs_1.readFileSync)(handoffPath, "utf-8").slice(0, 2000);
|
|
156
|
+
}
|
|
157
|
+
return { action: "query", data, memory, handoff };
|
|
158
|
+
}
|
|
159
|
+
function handleApprove(projectRoot, note) {
|
|
160
|
+
try {
|
|
161
|
+
(0, dispatch_1.approveReview)(projectRoot, note);
|
|
162
|
+
return { action: "approved", note };
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
return { action: "error", message: err.message };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function handleReject(projectRoot, reason, note) {
|
|
169
|
+
try {
|
|
170
|
+
(0, dispatch_1.rejectReview)(projectRoot, reason, note);
|
|
171
|
+
return { action: "rejected", reason, note };
|
|
172
|
+
}
|
|
173
|
+
catch (err) {
|
|
174
|
+
return { action: "error", message: err.message };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function handleStartStory(projectRoot, storyId) {
|
|
178
|
+
(0, dispatch_1.startStory)(projectRoot, storyId);
|
|
179
|
+
return wrapDispatchResult((0, dispatch_1.dispatch)(projectRoot));
|
|
180
|
+
}
|
|
181
|
+
function handleDispatch(projectRoot) {
|
|
182
|
+
return wrapDispatchResult((0, dispatch_1.dispatch)(projectRoot));
|
|
183
|
+
}
|
|
184
|
+
function handleCustom(projectRoot, instruction) {
|
|
185
|
+
// Check if there's an agent-teams keyword
|
|
186
|
+
const agentTeams = /agent.?teams?|平行|多\s*agent|agents/i.test(instruction);
|
|
187
|
+
(0, dispatch_1.startCustom)(projectRoot, instruction, { agentTeams });
|
|
188
|
+
return wrapDispatchResult((0, dispatch_1.dispatch)(projectRoot));
|
|
189
|
+
}
|
|
190
|
+
/** Convert DispatchResult to AutoResult */
|
|
191
|
+
function wrapDispatchResult(result) {
|
|
192
|
+
switch (result.type) {
|
|
193
|
+
case "dispatched":
|
|
194
|
+
return {
|
|
195
|
+
action: "dispatched",
|
|
196
|
+
step: result.step,
|
|
197
|
+
attempt: result.attempt,
|
|
198
|
+
prompt: result.prompt,
|
|
199
|
+
fw_lv: result.fw_lv,
|
|
200
|
+
};
|
|
201
|
+
case "done":
|
|
202
|
+
return { action: "done", story: result.story, summary: result.summary };
|
|
203
|
+
case "needs_human":
|
|
204
|
+
return { action: "needs_human", step: result.step, message: result.message };
|
|
205
|
+
case "blocked":
|
|
206
|
+
return { action: "blocked", step: result.step, reason: result.reason };
|
|
207
|
+
case "already_running":
|
|
208
|
+
return { action: "already_running", step: result.step, elapsed_min: result.elapsed_min };
|
|
209
|
+
case "timeout":
|
|
210
|
+
return { action: "timeout", step: result.step, elapsed_min: result.elapsed_min };
|
|
211
|
+
}
|
|
212
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -18,11 +18,13 @@ const path_1 = require("path");
|
|
|
18
18
|
const child_process_1 = require("child_process");
|
|
19
19
|
const state_1 = require("./state");
|
|
20
20
|
const dispatch_1 = require("./dispatch");
|
|
21
|
+
const auto_1 = require("./auto");
|
|
21
22
|
const [, , command, ...args] = process.argv;
|
|
22
23
|
function usage() {
|
|
23
24
|
console.error(`Usage: orchestrator <command> [args]
|
|
24
25
|
|
|
25
26
|
Commands:
|
|
27
|
+
auto <project-root> <message...> ★ Unified entry — classify message & route automatically
|
|
26
28
|
init <project-root> <project-name> Initialize .ai/STATE.json
|
|
27
29
|
start-story <project-root> <story-id> Begin a new User Story (micro-waterfall)
|
|
28
30
|
start-custom <project-root> <instruction> Begin a custom ad-hoc task
|
|
@@ -47,6 +49,53 @@ function resolveRoot(raw) {
|
|
|
47
49
|
}
|
|
48
50
|
try {
|
|
49
51
|
switch (command) {
|
|
52
|
+
case "auto": {
|
|
53
|
+
const projectRoot = resolveRoot(args[0]);
|
|
54
|
+
const message = args.slice(1).join(" ");
|
|
55
|
+
if (!message) {
|
|
56
|
+
console.error("Error: <message> is required");
|
|
57
|
+
console.error('Example: orchestrator auto ./project "目前狀態如何"');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const result = (0, auto_1.auto)(projectRoot, message);
|
|
61
|
+
// JSON to stdout (machine-readable)
|
|
62
|
+
console.log(JSON.stringify(result, null, 2));
|
|
63
|
+
// Human-friendly summary to stderr
|
|
64
|
+
switch (result.action) {
|
|
65
|
+
case "query":
|
|
66
|
+
console.error(`[auto] Query: ${result.data.project} — step=${result.data.step}, status=${result.data.status}`);
|
|
67
|
+
break;
|
|
68
|
+
case "dispatched":
|
|
69
|
+
console.error(`[auto] Dispatched: step=${result.step}, attempt=${result.attempt}, fw_lv=${result.fw_lv}`);
|
|
70
|
+
break;
|
|
71
|
+
case "done":
|
|
72
|
+
console.error(`[auto] Done: ${result.summary}`);
|
|
73
|
+
break;
|
|
74
|
+
case "needs_human":
|
|
75
|
+
console.error(`[auto] Needs human: ${result.message}`);
|
|
76
|
+
break;
|
|
77
|
+
case "blocked":
|
|
78
|
+
console.error(`[auto] Blocked: ${result.reason}`);
|
|
79
|
+
break;
|
|
80
|
+
case "approved":
|
|
81
|
+
console.error(`[auto] Approved${result.note ? ` (note: ${result.note})` : ""}`);
|
|
82
|
+
break;
|
|
83
|
+
case "rejected":
|
|
84
|
+
console.error(`[auto] Rejected: ${result.reason}${result.note ? ` — ${result.note}` : ""}`);
|
|
85
|
+
break;
|
|
86
|
+
case "detected":
|
|
87
|
+
console.error(`[auto] Framework level: ${result.framework.level}`);
|
|
88
|
+
break;
|
|
89
|
+
case "listed":
|
|
90
|
+
console.error(`[auto] Found ${result.projects.length} project(s)`);
|
|
91
|
+
break;
|
|
92
|
+
case "error":
|
|
93
|
+
console.error(`[auto] Error: ${result.message}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
50
99
|
case "init": {
|
|
51
100
|
const projectRoot = resolveRoot(args[0]);
|
|
52
101
|
const projectName = args[1];
|
|
@@ -61,6 +110,11 @@ try {
|
|
|
61
110
|
else {
|
|
62
111
|
console.log(`STATE.json already exists (step: ${state.step}, status: ${state.status})`);
|
|
63
112
|
}
|
|
113
|
+
// Always ensure CLAUDE.md exists (idempotent, won't overwrite)
|
|
114
|
+
const claudeCreated = (0, state_1.writeClaudeMd)(projectRoot, projectName);
|
|
115
|
+
if (claudeCreated) {
|
|
116
|
+
console.log(`Created CLAUDE.md (CC will auto-detect ACF on session start)`);
|
|
117
|
+
}
|
|
64
118
|
break;
|
|
65
119
|
}
|
|
66
120
|
case "start-story": {
|
package/dist/dispatch.js
CHANGED
|
@@ -212,18 +212,24 @@ function buildPrompt(state, rule) {
|
|
|
212
212
|
lines.push("===================");
|
|
213
213
|
lines.push("");
|
|
214
214
|
}
|
|
215
|
-
//
|
|
215
|
+
// === PRIMARY TASK (this is the actual work CC must do) ===
|
|
216
|
+
lines.push("=== YOUR PRIMARY TASK ===");
|
|
216
217
|
lines.push(rule.step_instruction);
|
|
217
218
|
lines.push("");
|
|
218
|
-
|
|
219
|
-
lines.push("
|
|
219
|
+
lines.push("Focus on completing the task above FIRST. Modify source files, create");
|
|
220
|
+
lines.push("tests, update documents — whatever the task requires. Do NOT stop after");
|
|
221
|
+
lines.push("just updating .ai/HANDOFF.md — that is only the final bookkeeping step.");
|
|
222
|
+
lines.push("=========================");
|
|
223
|
+
lines.push("");
|
|
224
|
+
// === POST-TASK BOOKKEEPING (only after the real work is done) ===
|
|
225
|
+
lines.push("After you have completed ALL the work above, do this final bookkeeping:");
|
|
220
226
|
lines.push("- Only modify affected files and paragraphs, don't rewrite unrelated content");
|
|
221
|
-
lines.push("-
|
|
227
|
+
lines.push("- Update .ai/HANDOFF.md as a summary of what you did:");
|
|
222
228
|
lines.push(" - YAML front matter: fill in story, step, attempt, status, reason, files_changed, tests values");
|
|
223
229
|
lines.push(" - Markdown body: record what was done, what's unresolved, what next session should note");
|
|
224
|
-
lines.push("- If requirements unclear,
|
|
225
|
-
lines.push("- If Constitution violation found,
|
|
226
|
-
lines.push("- If touching Non-Goals scope,
|
|
230
|
+
lines.push("- If requirements unclear, set status: failing and reason: needs_clarification");
|
|
231
|
+
lines.push("- If Constitution violation found, set status: failing and reason: constitution_violation");
|
|
232
|
+
lines.push("- If touching Non-Goals scope, set status: failing and reason: scope_warning");
|
|
227
233
|
return lines.join("\n");
|
|
228
234
|
}
|
|
229
235
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* rules.ts → Step transition rules table (pure data)
|
|
7
7
|
* dispatch.ts → State machine + prompt builder + handoff parser
|
|
8
8
|
*/
|
|
9
|
-
export { type State, type Step, type Status, type Reason, type TaskType, type TestResult, createInitialState, readState, writeState, initState, validate, isTimedOut, isMaxedOut, markRunning, markCompleted, } from "./state";
|
|
9
|
+
export { type State, type Step, type Status, type Reason, type TaskType, type TestResult, createInitialState, readState, writeState, initState, validate, isTimedOut, isMaxedOut, markRunning, markCompleted, generateClaudeMd, writeClaudeMd, } from "./state";
|
|
10
10
|
export { type StepRule, type FailRouting, type TeamRole, type TeamRoles, type DispatchMode, STEP_RULES, BOOTSTRAP_RULE, DEFAULT_TEAM_ROLES, DISPATCH_MODES, getRule, resolvePaths, getDispatchMode, getFailTarget, getStepSequence, } from "./rules";
|
|
11
|
+
export { type AutoResult, auto, classify, } from "./auto";
|
|
11
12
|
export { type DispatchResult, type HandoffResult, dispatch, buildPrompt, parseHandoff, applyHandoff, runPostCheck, approveReview, rejectReview, startStory, startCustom, type ProjectStatus, type FrameworkDetection, type ProjectEntry, detectFramework, queryProjectStatus, listProjects, } from "./dispatch";
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* dispatch.ts → State machine + prompt builder + handoff parser
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.listProjects = exports.queryProjectStatus = exports.detectFramework = exports.startCustom = exports.startStory = exports.rejectReview = exports.approveReview = exports.runPostCheck = exports.applyHandoff = exports.parseHandoff = exports.buildPrompt = exports.dispatch = exports.getStepSequence = exports.getFailTarget = exports.getDispatchMode = exports.resolvePaths = exports.getRule = exports.DISPATCH_MODES = exports.DEFAULT_TEAM_ROLES = exports.BOOTSTRAP_RULE = exports.STEP_RULES = exports.markCompleted = exports.markRunning = exports.isMaxedOut = exports.isTimedOut = exports.validate = exports.initState = exports.writeState = exports.readState = exports.createInitialState = void 0;
|
|
11
|
+
exports.listProjects = exports.queryProjectStatus = exports.detectFramework = exports.startCustom = exports.startStory = exports.rejectReview = exports.approveReview = exports.runPostCheck = exports.applyHandoff = exports.parseHandoff = exports.buildPrompt = exports.dispatch = exports.classify = exports.auto = exports.getStepSequence = exports.getFailTarget = exports.getDispatchMode = exports.resolvePaths = exports.getRule = exports.DISPATCH_MODES = exports.DEFAULT_TEAM_ROLES = exports.BOOTSTRAP_RULE = exports.STEP_RULES = exports.writeClaudeMd = exports.generateClaudeMd = exports.markCompleted = exports.markRunning = exports.isMaxedOut = exports.isTimedOut = exports.validate = exports.initState = exports.writeState = exports.readState = exports.createInitialState = void 0;
|
|
12
12
|
// State
|
|
13
13
|
var state_1 = require("./state");
|
|
14
14
|
Object.defineProperty(exports, "createInitialState", { enumerable: true, get: function () { return state_1.createInitialState; } });
|
|
@@ -20,6 +20,8 @@ Object.defineProperty(exports, "isTimedOut", { enumerable: true, get: function (
|
|
|
20
20
|
Object.defineProperty(exports, "isMaxedOut", { enumerable: true, get: function () { return state_1.isMaxedOut; } });
|
|
21
21
|
Object.defineProperty(exports, "markRunning", { enumerable: true, get: function () { return state_1.markRunning; } });
|
|
22
22
|
Object.defineProperty(exports, "markCompleted", { enumerable: true, get: function () { return state_1.markCompleted; } });
|
|
23
|
+
Object.defineProperty(exports, "generateClaudeMd", { enumerable: true, get: function () { return state_1.generateClaudeMd; } });
|
|
24
|
+
Object.defineProperty(exports, "writeClaudeMd", { enumerable: true, get: function () { return state_1.writeClaudeMd; } });
|
|
23
25
|
// Rules
|
|
24
26
|
var rules_1 = require("./rules");
|
|
25
27
|
Object.defineProperty(exports, "STEP_RULES", { enumerable: true, get: function () { return rules_1.STEP_RULES; } });
|
|
@@ -31,6 +33,10 @@ Object.defineProperty(exports, "resolvePaths", { enumerable: true, get: function
|
|
|
31
33
|
Object.defineProperty(exports, "getDispatchMode", { enumerable: true, get: function () { return rules_1.getDispatchMode; } });
|
|
32
34
|
Object.defineProperty(exports, "getFailTarget", { enumerable: true, get: function () { return rules_1.getFailTarget; } });
|
|
33
35
|
Object.defineProperty(exports, "getStepSequence", { enumerable: true, get: function () { return rules_1.getStepSequence; } });
|
|
36
|
+
// Auto (unified entry point)
|
|
37
|
+
var auto_1 = require("./auto");
|
|
38
|
+
Object.defineProperty(exports, "auto", { enumerable: true, get: function () { return auto_1.auto; } });
|
|
39
|
+
Object.defineProperty(exports, "classify", { enumerable: true, get: function () { return auto_1.classify; } });
|
|
34
40
|
// Dispatch
|
|
35
41
|
var dispatch_1 = require("./dispatch");
|
|
36
42
|
Object.defineProperty(exports, "dispatch", { enumerable: true, get: function () { return dispatch_1.dispatch; } });
|
package/dist/state.d.ts
CHANGED
|
@@ -71,6 +71,19 @@ export declare function initState(projectRoot: string, project: string): {
|
|
|
71
71
|
created: boolean;
|
|
72
72
|
state: State;
|
|
73
73
|
};
|
|
74
|
+
/** Path to CLAUDE.md in project root */
|
|
75
|
+
export declare function claudeMdPath(projectRoot: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Generate CLAUDE.md content for ACF-enabled projects.
|
|
78
|
+
* CC reads this file automatically on every session start,
|
|
79
|
+
* ensuring it follows the ACF workflow even without dispatch.
|
|
80
|
+
*/
|
|
81
|
+
export declare function generateClaudeMd(project: string): string;
|
|
82
|
+
/**
|
|
83
|
+
* Write CLAUDE.md to project root.
|
|
84
|
+
* Returns true if created, false if already exists (no overwrite).
|
|
85
|
+
*/
|
|
86
|
+
export declare function writeClaudeMd(projectRoot: string, project: string, force?: boolean): boolean;
|
|
74
87
|
/** Validate a State object. Throws on invalid fields. */
|
|
75
88
|
export declare function validate(state: State): void;
|
|
76
89
|
/** Check if a step has exceeded its timeout */
|
package/dist/state.js
CHANGED
|
@@ -11,6 +11,9 @@ exports.statePath = statePath;
|
|
|
11
11
|
exports.readState = readState;
|
|
12
12
|
exports.writeState = writeState;
|
|
13
13
|
exports.initState = initState;
|
|
14
|
+
exports.claudeMdPath = claudeMdPath;
|
|
15
|
+
exports.generateClaudeMd = generateClaudeMd;
|
|
16
|
+
exports.writeClaudeMd = writeClaudeMd;
|
|
14
17
|
exports.validate = validate;
|
|
15
18
|
exports.isTimedOut = isTimedOut;
|
|
16
19
|
exports.isMaxedOut = isMaxedOut;
|
|
@@ -78,6 +81,71 @@ function initState(projectRoot, project) {
|
|
|
78
81
|
writeState(projectRoot, state);
|
|
79
82
|
return { created: true, state };
|
|
80
83
|
}
|
|
84
|
+
// ─── CLAUDE.md Generation ─────────────────────────────────────────────────
|
|
85
|
+
/** Path to CLAUDE.md in project root */
|
|
86
|
+
function claudeMdPath(projectRoot) {
|
|
87
|
+
return (0, path_1.join)(projectRoot, "CLAUDE.md");
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Generate CLAUDE.md content for ACF-enabled projects.
|
|
91
|
+
* CC reads this file automatically on every session start,
|
|
92
|
+
* ensuring it follows the ACF workflow even without dispatch.
|
|
93
|
+
*/
|
|
94
|
+
function generateClaudeMd(project) {
|
|
95
|
+
return `# ${project} — Agentic Coding Framework
|
|
96
|
+
|
|
97
|
+
This project uses the **Agentic Coding Framework (ACF)** with an orchestrator-driven
|
|
98
|
+
micro-waterfall pipeline. You MUST follow the ACF workflow — do NOT work freestyle.
|
|
99
|
+
|
|
100
|
+
## How to Work in This Project
|
|
101
|
+
|
|
102
|
+
1. **Check current state first:**
|
|
103
|
+
Read \`.ai/STATE.json\` to understand which step and status the project is in.
|
|
104
|
+
|
|
105
|
+
2. **Follow the orchestrator — never skip steps:**
|
|
106
|
+
The pipeline is: bootstrap → bdd → sdd-delta → contract → review → scaffold → impl → verify → update-memory → done.
|
|
107
|
+
Each step has specific inputs, outputs, and acceptance criteria.
|
|
108
|
+
|
|
109
|
+
3. **Use the orchestrator CLI** (preferred):
|
|
110
|
+
\`\`\`bash
|
|
111
|
+
orchestrator dispatch . # Get your current task prompt
|
|
112
|
+
orchestrator status . # Check pipeline status
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
4. **Always update .ai/HANDOFF.md when done:**
|
|
116
|
+
After completing your work, write a YAML front-matter summary to \`.ai/HANDOFF.md\`
|
|
117
|
+
with: story, step, attempt, status (pass/failing), reason, files_changed, and tests.
|
|
118
|
+
The orchestrator hook reads this to advance the pipeline.
|
|
119
|
+
|
|
120
|
+
## Key Rules
|
|
121
|
+
|
|
122
|
+
- **Do NOT modify .ai/STATE.json directly** — the orchestrator manages it.
|
|
123
|
+
- **Do NOT skip to a different step** — always complete the current step first.
|
|
124
|
+
- If requirements are unclear, set status: \`failing\` and reason: \`needs_clarification\` in HANDOFF.md.
|
|
125
|
+
- If you find a Constitution violation, set reason: \`constitution_violation\`.
|
|
126
|
+
- Read \`.ai/PROJECT_MEMORY.md\` for cross-session context and architectural decisions.
|
|
127
|
+
|
|
128
|
+
## Files
|
|
129
|
+
|
|
130
|
+
| File | Purpose | Who writes |
|
|
131
|
+
|------|---------|-----------|
|
|
132
|
+
| \`.ai/STATE.json\` | Pipeline state machine | Orchestrator only |
|
|
133
|
+
| \`.ai/HANDOFF.md\` | Executor ↔ Orchestrator bridge | You (CC) |
|
|
134
|
+
| \`.ai/PROJECT_MEMORY.md\` | Cross-session knowledge | You (at update-memory step) |
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Write CLAUDE.md to project root.
|
|
139
|
+
* Returns true if created, false if already exists (no overwrite).
|
|
140
|
+
*/
|
|
141
|
+
function writeClaudeMd(projectRoot, project, force = false) {
|
|
142
|
+
const path = claudeMdPath(projectRoot);
|
|
143
|
+
if ((0, fs_1.existsSync)(path) && !force) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
(0, fs_1.writeFileSync)(path, generateClaudeMd(project), "utf-8");
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
81
149
|
// ─── Validation ──────────────────────────────────────────────────────────────
|
|
82
150
|
const VALID_STEPS = new Set([
|
|
83
151
|
"bootstrap",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentic-coding-framework/orchestrator-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Zero-token orchestrator for the Agentic Coding Protocol — state machine, rules table, dispatch logic, CC integration",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "dist/index.js",
|