@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 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
- // Step instruction
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
- // Output rules (always appended)
219
- lines.push("Output rules:");
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("- After completion, update .ai/HANDOFF.md:");
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, fill reason field with needs_clarification");
225
- lines.push("- If Constitution violation found, fill reason field with constitution_violation");
226
- lines.push("- If touching Non-Goals scope, fill reason field with scope_warning");
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.1.1",
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",