@shenlee/devcrew 0.1.1 → 0.1.3
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/README.md +30 -8
- package/README.zh-CN.md +27 -8
- package/dist/packages/adapters/src/index.js +62 -8
- package/dist/packages/core/src/artifacts.js +14 -4
- package/dist/packages/core/src/config.js +1 -1
- package/dist/packages/core/src/paths.js +10 -6
- package/dist/packages/core/src/store.js +36 -0
- package/dist/packages/core/src/types.js +5 -1
- package/dist/packages/core/src/validation.js +7 -1
- package/dist/packages/core/src/version.js +1 -1
- package/dist/packages/core/src/workflow.js +120 -31
- package/dist/packages/orchestrator/src/index.js +339 -160
- package/dist/packages/orchestrator/src/worktree.js +198 -0
- package/dist/packages/plugins/src/index.js +2 -32
- package/dist/packages/service/src/stdio.js +19 -3
- package/dist/packages/service/src/tools.js +45 -6
- package/docs/claude-code.md +5 -3
- package/docs/codex.md +12 -4
- package/docs/quickstart.md +1 -1
- package/docs/superpowers/plans/2026-07-10-p0-foundation-repair.md +939 -0
- package/docs/superpowers/plans/2026-07-13-safety-semantics.md +313 -0
- package/docs/superpowers/specs/2026-07-10-p0-foundation-repair-design.md +182 -0
- package/docs/superpowers/specs/2026-07-13-execution-boundaries-design.md +126 -0
- package/docs/workflow.md +36 -4
- package/package.json +1 -1
- package/packages/adapters/src/index.ts +87 -11
- package/packages/core/src/artifacts.ts +16 -4
- package/packages/core/src/config.ts +1 -1
- package/packages/core/src/paths.ts +11 -6
- package/packages/core/src/store.ts +41 -1
- package/packages/core/src/types.ts +53 -2
- package/packages/core/src/validation.ts +10 -0
- package/packages/core/src/version.ts +1 -1
- package/packages/core/src/workflow.ts +136 -30
- package/packages/orchestrator/src/index.ts +377 -182
- package/packages/orchestrator/src/worktree.ts +269 -0
- package/packages/plugins/src/index.ts +2 -44
- package/packages/service/src/stdio.ts +27 -6
- package/packages/service/src/tools.ts +46 -5
- package/plugins/devcrew-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/devcrew-codex/.mcp.json +1 -1
- package/plugins/devcrew-codex/assets/logo.png +0 -0
- package/plugins/devcrew-codex/skills/devcrew/SKILL.md +8 -7
- package/scripts/smoke-codex-plugin.mjs +1 -1
- package/plugins/devcrew-codex/agents/architect.toml +0 -6
- package/plugins/devcrew-codex/agents/implementer.toml +0 -6
- package/plugins/devcrew-codex/agents/pm.toml +0 -6
- package/plugins/devcrew-codex/agents/tester.toml +0 -6
|
@@ -3,43 +3,67 @@ import { ensureConfig } from "./config.js";
|
|
|
3
3
|
import { readArtifact, writeArtifact } from "./artifacts.js";
|
|
4
4
|
import { discoverStandards } from "./standards.js";
|
|
5
5
|
import { loadState, saveState } from "./store.js";
|
|
6
|
-
import { parseAnswer, parseArtifactName, parseBackend, parseCwd, parseExecutionMode, parseFeedback, parseGate, parseHost, parseOptionalNote, parseRequest, parseRunId, parseWorkflowMode, } from "./validation.js";
|
|
6
|
+
import { parseAnswer, parseArtifactName, parseBackend, parseCwd, parseExecutionMode, parseExecutionPolicy, parseFeedback, parseGate, parseHost, parseOptionalNote, parseRequest, parseRunId, parseWaiverReason, parseWorkflowMode, } from "./validation.js";
|
|
7
|
+
import { GATES } from "./types.js";
|
|
7
8
|
function now() {
|
|
8
9
|
return new Date().toISOString();
|
|
9
10
|
}
|
|
10
11
|
function newRunId() {
|
|
11
12
|
return `dc-${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`;
|
|
12
13
|
}
|
|
13
|
-
export function nextPhaseAfterGate(gate) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
export function nextPhaseAfterGate(state, gate) {
|
|
15
|
+
switch (gate) {
|
|
16
|
+
case "requirements":
|
|
17
|
+
return "architecture";
|
|
18
|
+
case "architecture":
|
|
19
|
+
return "implementation";
|
|
20
|
+
case "implementation":
|
|
21
|
+
return state.executionMode === "apply" ? "execution" : "testing";
|
|
22
|
+
case "implementation-review":
|
|
23
|
+
return "testing";
|
|
24
|
+
case "testing":
|
|
25
|
+
return "acceptance";
|
|
26
|
+
}
|
|
21
27
|
}
|
|
22
28
|
export function artifactForPhase(phase) {
|
|
23
29
|
const artifactByPhase = {
|
|
24
30
|
requirements: "requirements",
|
|
25
31
|
architecture: "architecture",
|
|
26
32
|
implementation: "implementation-plan",
|
|
33
|
+
execution: "implementation-review",
|
|
34
|
+
review: "architecture-review",
|
|
27
35
|
testing: "test-report",
|
|
28
36
|
acceptance: "acceptance",
|
|
29
37
|
complete: "acceptance",
|
|
30
38
|
};
|
|
31
39
|
return artifactByPhase[phase];
|
|
32
40
|
}
|
|
33
|
-
export function gateForPhase(phase) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
export function gateForPhase(phase, enabledGates) {
|
|
42
|
+
const gate = phase === "review"
|
|
43
|
+
? "implementation-review"
|
|
44
|
+
: (phase === "requirements" ||
|
|
45
|
+
phase === "architecture" ||
|
|
46
|
+
phase === "implementation" ||
|
|
47
|
+
phase === "testing"
|
|
48
|
+
? phase
|
|
49
|
+
: undefined);
|
|
50
|
+
return gate && (!enabledGates || enabledGates.includes(gate)) ? gate : undefined;
|
|
51
|
+
}
|
|
52
|
+
function configuredGates(gates) {
|
|
53
|
+
const configured = new Set(gates.filter((gate) => GATES.includes(gate)));
|
|
54
|
+
configured.add("implementation-review");
|
|
55
|
+
configured.add("testing");
|
|
56
|
+
return [...configured];
|
|
38
57
|
}
|
|
39
|
-
function
|
|
40
|
-
const expected = gateForPhase(state.phase);
|
|
41
|
-
if (expected
|
|
42
|
-
throw new Error(
|
|
58
|
+
function assertPendingCurrentGate(state, gate) {
|
|
59
|
+
const expected = gateForPhase(state.phase, state.enabledGates);
|
|
60
|
+
if (expected !== gate) {
|
|
61
|
+
throw new Error(expected
|
|
62
|
+
? `Cannot act on ${gate} while current gate is ${expected}`
|
|
63
|
+
: `Cannot act on ${gate} while workflow phase is ${state.phase}`);
|
|
64
|
+
}
|
|
65
|
+
if (state.status !== "awaiting_approval" || state.gates[gate] !== "pending") {
|
|
66
|
+
throw new Error(`Gate ${gate} is not pending approval`);
|
|
43
67
|
}
|
|
44
68
|
}
|
|
45
69
|
async function writeCurrentArtifact(state) {
|
|
@@ -56,6 +80,11 @@ export async function startWorkflow(input, options = {}) {
|
|
|
56
80
|
const config = await ensureConfig(cwd);
|
|
57
81
|
const backend = input.backend ? parseBackend(input.backend) : config.defaultBackend === "host-preferred" ? host : config.defaultBackend;
|
|
58
82
|
const executionMode = input.executionMode ? parseExecutionMode(input.executionMode) : config.executionMode;
|
|
83
|
+
const executionPolicy = input.executionPolicy ? parseExecutionPolicy(input.executionPolicy) : "interactive-host";
|
|
84
|
+
const enabledGates = configuredGates(config.workflow.gates);
|
|
85
|
+
if (executionMode === "apply" && backend === "local") {
|
|
86
|
+
throw new Error("DevCrew apply mode requires a codex or claude backend; local is plan-only");
|
|
87
|
+
}
|
|
59
88
|
const createdAt = now();
|
|
60
89
|
const state = {
|
|
61
90
|
version: 1,
|
|
@@ -64,20 +93,25 @@ export async function startWorkflow(input, options = {}) {
|
|
|
64
93
|
host,
|
|
65
94
|
mode,
|
|
66
95
|
executionMode,
|
|
96
|
+
executionPolicy,
|
|
97
|
+
enabledGates,
|
|
98
|
+
artifactDirectory: config.workflow.artifactDirectory,
|
|
67
99
|
backend,
|
|
68
100
|
request,
|
|
69
101
|
phase: "requirements",
|
|
70
|
-
status: "awaiting_approval",
|
|
102
|
+
status: enabledGates.includes("requirements") ? "awaiting_approval" : "ready",
|
|
71
103
|
createdAt,
|
|
72
104
|
updatedAt: createdAt,
|
|
73
105
|
gates: {
|
|
74
|
-
requirements: "pending",
|
|
106
|
+
requirements: enabledGates.includes("requirements") ? "pending" : "not_started",
|
|
75
107
|
architecture: "not_started",
|
|
76
108
|
implementation: "not_started",
|
|
109
|
+
"implementation-review": "not_started",
|
|
77
110
|
testing: "not_started",
|
|
78
111
|
},
|
|
79
112
|
artifacts: {},
|
|
80
113
|
roles: [],
|
|
114
|
+
pendingQuestions: [],
|
|
81
115
|
answers: [],
|
|
82
116
|
approvals: [],
|
|
83
117
|
feedback: [],
|
|
@@ -85,6 +119,7 @@ export async function startWorkflow(input, options = {}) {
|
|
|
85
119
|
changedFiles: [],
|
|
86
120
|
implementationDiff: "",
|
|
87
121
|
verification: [],
|
|
122
|
+
verificationStatus: "not_run",
|
|
88
123
|
lintResults: [],
|
|
89
124
|
};
|
|
90
125
|
if (!options.skipArtifactWrite) {
|
|
@@ -97,7 +132,10 @@ export async function getWorkflowStatus(input) {
|
|
|
97
132
|
}
|
|
98
133
|
export async function continueWorkflow(input) {
|
|
99
134
|
const state = await getWorkflowStatus(input);
|
|
100
|
-
if (state.status === "awaiting_approval" ||
|
|
135
|
+
if (state.status === "awaiting_approval" ||
|
|
136
|
+
state.status === "awaiting_input" ||
|
|
137
|
+
state.status === "awaiting_execution" ||
|
|
138
|
+
state.status === "complete") {
|
|
101
139
|
return state;
|
|
102
140
|
}
|
|
103
141
|
if (state.phase === "acceptance") {
|
|
@@ -106,8 +144,17 @@ export async function continueWorkflow(input) {
|
|
|
106
144
|
state.status = "complete";
|
|
107
145
|
return saveState(state);
|
|
108
146
|
}
|
|
109
|
-
|
|
147
|
+
if (state.phase === "execution") {
|
|
148
|
+
throw new Error("DevCrew execution phase requires orchestrated continuation");
|
|
149
|
+
}
|
|
150
|
+
const configuredGate = gateForPhase(state.phase);
|
|
151
|
+
const gate = gateForPhase(state.phase, state.enabledGates);
|
|
110
152
|
if (!gate) {
|
|
153
|
+
if (configuredGate) {
|
|
154
|
+
state.phase = nextPhaseAfterGate(state, configuredGate);
|
|
155
|
+
state.status = "ready";
|
|
156
|
+
return saveState(state);
|
|
157
|
+
}
|
|
111
158
|
state.status = "complete";
|
|
112
159
|
return saveState(state);
|
|
113
160
|
}
|
|
@@ -116,17 +163,29 @@ export async function continueWorkflow(input) {
|
|
|
116
163
|
await writeCurrentArtifact(state);
|
|
117
164
|
return saveState(state);
|
|
118
165
|
}
|
|
119
|
-
export async function
|
|
166
|
+
export async function validateWorkflowApproval(input) {
|
|
120
167
|
const state = await getWorkflowStatus(input);
|
|
121
168
|
const gate = parseGate(input.gate);
|
|
122
|
-
|
|
169
|
+
if (state.gates[gate] === "approved") {
|
|
170
|
+
return state;
|
|
171
|
+
}
|
|
172
|
+
assertPendingCurrentGate(state, gate);
|
|
173
|
+
parseOptionalNote(input.note);
|
|
174
|
+
return state;
|
|
175
|
+
}
|
|
176
|
+
export async function approveWorkflow(input) {
|
|
177
|
+
const state = await validateWorkflowApproval(input);
|
|
178
|
+
const gate = parseGate(input.gate);
|
|
179
|
+
if (state.gates[gate] === "approved") {
|
|
180
|
+
return state;
|
|
181
|
+
}
|
|
123
182
|
state.gates[gate] = "approved";
|
|
124
183
|
state.approvals.push({
|
|
125
184
|
gate,
|
|
126
185
|
note: parseOptionalNote(input.note),
|
|
127
186
|
createdAt: now(),
|
|
128
187
|
});
|
|
129
|
-
const nextPhase = nextPhaseAfterGate(gate);
|
|
188
|
+
const nextPhase = nextPhaseAfterGate(state, gate);
|
|
130
189
|
state.phase = nextPhase;
|
|
131
190
|
state.status = "ready";
|
|
132
191
|
return saveState(state);
|
|
@@ -134,7 +193,10 @@ export async function approveWorkflow(input) {
|
|
|
134
193
|
export async function rejectWorkflow(input) {
|
|
135
194
|
const state = await getWorkflowStatus(input);
|
|
136
195
|
const gate = parseGate(input.gate);
|
|
137
|
-
|
|
196
|
+
if (state.gates[gate] === "rejected") {
|
|
197
|
+
return state;
|
|
198
|
+
}
|
|
199
|
+
assertPendingCurrentGate(state, gate);
|
|
138
200
|
state.gates[gate] = "rejected";
|
|
139
201
|
state.status = "awaiting_input";
|
|
140
202
|
state.feedback.push({
|
|
@@ -146,20 +208,47 @@ export async function rejectWorkflow(input) {
|
|
|
146
208
|
}
|
|
147
209
|
export async function answerWorkflow(input, options = {}) {
|
|
148
210
|
const state = await getWorkflowStatus(input);
|
|
211
|
+
const gate = gateForPhase(state.phase, state.enabledGates);
|
|
212
|
+
if (state.status === "awaiting_input" && state.pendingQuestions.length > 0 && state.phase === "requirements") {
|
|
213
|
+
state.answers.push({
|
|
214
|
+
answer: parseAnswer(input.answer),
|
|
215
|
+
createdAt: now(),
|
|
216
|
+
});
|
|
217
|
+
state.pendingQuestions = [];
|
|
218
|
+
state.status = "ready";
|
|
219
|
+
return saveState(state);
|
|
220
|
+
}
|
|
221
|
+
if (state.status !== "awaiting_input" || !gate || state.gates[gate] !== "rejected") {
|
|
222
|
+
throw new Error("Workflow must be awaiting_input at a rejected current gate before recording an answer");
|
|
223
|
+
}
|
|
149
224
|
state.answers.push({
|
|
150
225
|
answer: parseAnswer(input.answer),
|
|
151
226
|
createdAt: now(),
|
|
152
227
|
});
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
state.gates[gate] = "pending";
|
|
156
|
-
state.status = "awaiting_approval";
|
|
157
|
-
}
|
|
228
|
+
state.gates[gate] = "pending";
|
|
229
|
+
state.status = "awaiting_approval";
|
|
158
230
|
if (!options.skipArtifactWrite) {
|
|
159
231
|
await writeCurrentArtifact(state);
|
|
160
232
|
}
|
|
161
233
|
return saveState(state);
|
|
162
234
|
}
|
|
235
|
+
export async function waiveVerificationWorkflow(input) {
|
|
236
|
+
const state = await getWorkflowStatus(input);
|
|
237
|
+
if (state.executionMode !== "apply" ||
|
|
238
|
+
state.phase !== "testing" ||
|
|
239
|
+
state.status !== "awaiting_input" ||
|
|
240
|
+
state.gates.testing !== "rejected" ||
|
|
241
|
+
state.verificationStatus !== "failed") {
|
|
242
|
+
throw new Error("A verification waiver is only available after failed apply-mode testing");
|
|
243
|
+
}
|
|
244
|
+
state.verificationWaiver = {
|
|
245
|
+
reason: parseWaiverReason(input.reason),
|
|
246
|
+
createdAt: now(),
|
|
247
|
+
};
|
|
248
|
+
state.gates.testing = "pending";
|
|
249
|
+
state.status = "awaiting_approval";
|
|
250
|
+
return saveState(state);
|
|
251
|
+
}
|
|
163
252
|
export async function getArtifact(input) {
|
|
164
253
|
const state = await getWorkflowStatus(input);
|
|
165
254
|
return readArtifact(state, parseArtifactName(input.name));
|