forge-workflow 0.0.5 → 0.0.7
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/.claude/commands/dev.md +6 -1
- package/.claude/commands/plan.md +59 -14
- package/.claude/commands/premerge.md +10 -0
- package/.claude/commands/review.md +7 -1
- package/.claude/commands/ship.md +95 -47
- package/.claude/commands/status.md +42 -0
- package/.claude/commands/validate.md +7 -1
- package/.claude/commands/verify.md +52 -4
- package/.claude/rules/workflow.md +16 -0
- package/.claude/scripts/greptile-resolve.sh +32 -0
- package/.cline/workflows/dev.md +6 -1
- package/.cline/workflows/plan.md +59 -14
- package/.cline/workflows/premerge.md +10 -0
- package/.cline/workflows/review.md +7 -1
- package/.cline/workflows/ship.md +95 -47
- package/.cline/workflows/status.md +42 -0
- package/.cline/workflows/validate.md +7 -1
- package/.cline/workflows/verify.md +52 -4
- package/.codex/skills/dev/SKILL.md +6 -1
- package/.codex/skills/plan/SKILL.md +59 -14
- package/.codex/skills/premerge/SKILL.md +10 -0
- package/.codex/skills/review/SKILL.md +7 -1
- package/.codex/skills/ship/SKILL.md +95 -47
- package/.codex/skills/status/SKILL.md +42 -0
- package/.codex/skills/validate/SKILL.md +7 -1
- package/.codex/skills/verify/SKILL.md +52 -4
- package/.cursor/commands/dev.md +6 -1
- package/.cursor/commands/plan.md +59 -14
- package/.cursor/commands/premerge.md +10 -0
- package/.cursor/commands/review.md +7 -1
- package/.cursor/commands/ship.md +95 -47
- package/.cursor/commands/status.md +42 -0
- package/.cursor/commands/validate.md +7 -1
- package/.cursor/commands/verify.md +52 -4
- package/.cursorrules +149 -0
- package/.github/prompts/dev.prompt.md +6 -1
- package/.github/prompts/plan.prompt.md +59 -14
- package/.github/prompts/premerge.prompt.md +10 -0
- package/.github/prompts/review.prompt.md +7 -1
- package/.github/prompts/ship.prompt.md +95 -47
- package/.github/prompts/status.prompt.md +42 -0
- package/.github/prompts/validate.prompt.md +7 -1
- package/.github/prompts/verify.prompt.md +52 -4
- package/.kilocode/workflows/dev.md +6 -1
- package/.kilocode/workflows/plan.md +59 -14
- package/.kilocode/workflows/premerge.md +10 -0
- package/.kilocode/workflows/review.md +7 -1
- package/.kilocode/workflows/ship.md +95 -47
- package/.kilocode/workflows/status.md +42 -0
- package/.kilocode/workflows/validate.md +7 -1
- package/.kilocode/workflows/verify.md +52 -4
- package/.opencode/commands/dev.md +6 -1
- package/.opencode/commands/plan.md +59 -14
- package/.opencode/commands/premerge.md +10 -0
- package/.opencode/commands/review.md +7 -1
- package/.opencode/commands/ship.md +95 -47
- package/.opencode/commands/status.md +42 -0
- package/.opencode/commands/validate.md +7 -1
- package/.opencode/commands/verify.md +52 -4
- package/.roo/commands/dev.md +6 -1
- package/.roo/commands/plan.md +59 -14
- package/.roo/commands/premerge.md +10 -0
- package/.roo/commands/review.md +7 -1
- package/.roo/commands/ship.md +95 -47
- package/.roo/commands/status.md +42 -0
- package/.roo/commands/validate.md +7 -1
- package/.roo/commands/verify.md +52 -4
- package/AGENTS.md +97 -0
- package/CLAUDE.md +10 -0
- package/README.md +2 -2
- package/bin/forge-cmd.js +5 -1
- package/bin/forge-preflight.js +15 -2
- package/bin/forge.js +211 -9
- package/docs/ENHANCED_ONBOARDING.md +96 -86
- package/docs/ROADMAP.md +2 -2
- package/docs/TOOLCHAIN.md +23 -0
- package/docs/VALIDATION.md +1 -1
- package/lefthook.yml +11 -0
- package/lib/agents/README.md +46 -1
- package/lib/agents/cline.plugin.json +11 -4
- package/lib/agents/codex.plugin.json +2 -2
- package/lib/agents/copilot.plugin.json +5 -5
- package/lib/agents/cursor.plugin.json +1 -1
- package/lib/agents/kilocode.plugin.json +1 -1
- package/lib/agents/opencode.plugin.json +7 -4
- package/lib/agents/roo.plugin.json +10 -3
- package/lib/agents-config.js +129 -81
- package/lib/codex-skills.js +50 -0
- package/lib/commands/_registry.js +173 -0
- package/lib/commands/clean.js +181 -0
- package/lib/commands/commands-reset.js +147 -0
- package/lib/commands/dev.js +84 -0
- package/lib/commands/plan.js +18 -0
- package/lib/commands/push.js +196 -0
- package/lib/commands/recommend.js +1 -1
- package/lib/commands/setup.js +4295 -0
- package/lib/commands/ship.js +20 -0
- package/lib/commands/status.js +210 -44
- package/lib/commands/sync.js +71 -0
- package/lib/commands/team.js +37 -0
- package/lib/commands/test.js +207 -0
- package/lib/commands/validate.js +13 -0
- package/lib/commands/worktree.js +310 -0
- package/lib/detect-agent.js +38 -8
- package/lib/detection-utils.js +405 -0
- package/lib/docs-command.js +51 -0
- package/lib/docs-copy.js +50 -0
- package/lib/file-utils.js +260 -0
- package/lib/forge-context.js +42 -0
- package/lib/freshness-token.js +148 -0
- package/lib/frontmatter.js +79 -0
- package/lib/greptile-match.js +80 -0
- package/lib/husky-migration.js +113 -12
- package/lib/lefthook-check.js +27 -6
- package/lib/plugin-manager.js +225 -72
- package/lib/project-discovery.js +39 -5
- package/lib/reset.js +309 -0
- package/lib/runtime-health.js +305 -0
- package/lib/shell-utils.js +50 -0
- package/lib/task-ownership.js +117 -0
- package/lib/ui-utils.js +43 -0
- package/lib/validation-utils.js +163 -0
- package/lib/workflow/enforce-stage.js +179 -0
- package/lib/workflow/stages.js +201 -0
- package/lib/workflow/state.js +332 -0
- package/opencode.json +67 -0
- package/package.json +16 -6
- package/scripts/beads-context.sh +165 -22
- package/scripts/beads-context.test.js +5 -1
- package/scripts/check-agents.js +103 -0
- package/scripts/check-forge-token.js +98 -0
- package/scripts/conflict-detect.sh +2 -2
- package/scripts/dep-guard.sh +6 -28
- package/scripts/file-index.sh +117 -23
- package/scripts/forge-team/index.sh +86 -0
- package/scripts/forge-team/lib/agent-prompt.sh +52 -0
- package/scripts/forge-team/lib/claim.sh +256 -0
- package/scripts/forge-team/lib/dashboard.sh +341 -0
- package/scripts/forge-team/lib/epic.sh +332 -0
- package/scripts/forge-team/lib/hooks.sh +253 -0
- package/scripts/forge-team/lib/identity.sh +235 -0
- package/scripts/forge-team/lib/sync-github.sh +317 -0
- package/scripts/forge-team/lib/verify.sh +284 -0
- package/scripts/forge-team/lib/workload.sh +296 -0
- package/scripts/forge-team/tests/agent-prompt.test.sh +72 -0
- package/scripts/forge-team/tests/claim.test.sh +179 -0
- package/scripts/forge-team/tests/dashboard.test.sh +170 -0
- package/scripts/forge-team/tests/dispatcher.test.sh +79 -0
- package/scripts/forge-team/tests/epic.test.sh +176 -0
- package/scripts/forge-team/tests/hooks.test.sh +239 -0
- package/scripts/forge-team/tests/identity.test.sh +176 -0
- package/scripts/forge-team/tests/integration.test.sh +371 -0
- package/scripts/forge-team/tests/sync-github.test.sh +209 -0
- package/scripts/forge-team/tests/verify.test.sh +314 -0
- package/scripts/forge-team/tests/workflow-integration.test.sh +43 -0
- package/scripts/forge-team/tests/workload.test.sh +209 -0
- package/scripts/lib/eval-runner.js +39 -0
- package/scripts/lib/jsonl-lock.sh +48 -0
- package/scripts/lib/sanitize.sh +116 -0
- package/scripts/pr-coordinator.sh +756 -0
- package/scripts/smart-status.sh +58 -21
- package/scripts/sync-commands.js +49 -20
- package/scripts/sync-utils.sh +24 -29
- package/scripts/test.js +18 -1
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
WORKFLOW_CLASSIFICATIONS,
|
|
5
|
+
STAGE_IDS,
|
|
6
|
+
STAGE_MODEL,
|
|
7
|
+
getWorkflowPath,
|
|
8
|
+
normalizeStageId,
|
|
9
|
+
} = require('./stages.js');
|
|
10
|
+
|
|
11
|
+
const WORKFLOW_STATE_SCHEMA_VERSION = 2;
|
|
12
|
+
const LEGACY_STANDARD_VERIFY = Symbol('legacy-standard-verify');
|
|
13
|
+
const LEGACY_STANDARD_WORKFLOW_PATH = Object.freeze([
|
|
14
|
+
'plan',
|
|
15
|
+
'dev',
|
|
16
|
+
'validate',
|
|
17
|
+
'ship',
|
|
18
|
+
'review',
|
|
19
|
+
'premerge',
|
|
20
|
+
'verify',
|
|
21
|
+
]);
|
|
22
|
+
const LEGACY_WORKFLOW_STATE_OPTIONS = Object.freeze({
|
|
23
|
+
allowLegacyDefaultClassification: true,
|
|
24
|
+
allowLegacyStandardVerify: true,
|
|
25
|
+
allowLegacyOverrideRecords: true,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function normalizeString(value) {
|
|
29
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeBoolean(value) {
|
|
33
|
+
return value === true || value === 1 || value === '1' || value === 'true';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeStageList(stages) {
|
|
37
|
+
if (!Array.isArray(stages)) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const normalized = [];
|
|
42
|
+
for (const stage of stages) {
|
|
43
|
+
const stageId = normalizeStageId(stage);
|
|
44
|
+
if (!stageId || normalized.includes(stageId)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
normalized.push(stageId);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return normalized;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalizeParallelTrack(track) {
|
|
54
|
+
if (!track || typeof track !== 'object' || Array.isArray(track)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const normalized = {
|
|
59
|
+
name: normalizeString(track.name),
|
|
60
|
+
agent: normalizeString(track.agent),
|
|
61
|
+
status: normalizeString(track.status),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (track.worktree && typeof track.worktree === 'object' && !Array.isArray(track.worktree)) {
|
|
65
|
+
normalized.worktree = {
|
|
66
|
+
path: normalizeString(track.worktree.path),
|
|
67
|
+
branch: normalizeString(track.worktree.branch),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return normalized;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeOverrideRecord(override = {}, options = {}) {
|
|
75
|
+
const type = normalizeString(override.type || override.kind) || 'manual';
|
|
76
|
+
const fromStage = override.fromStage == null ? null : normalizeStageId(override.fromStage);
|
|
77
|
+
const toStage = override.toStage == null ? null : normalizeStageId(override.toStage);
|
|
78
|
+
const reason = normalizeString(override.reason);
|
|
79
|
+
const actor = normalizeString(override.actor) || 'unknown';
|
|
80
|
+
const recordedAt = normalizeString(override.recordedAt || override.at || override.timestamp) || new Date().toISOString();
|
|
81
|
+
|
|
82
|
+
if (!type) {
|
|
83
|
+
throw new Error('Override record must include a type');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!options.allowLegacyOverrideRecords && (!fromStage || !toStage || !reason)) {
|
|
87
|
+
throw new Error('Override record must include fromStage, toStage, and a non-empty reason');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
type,
|
|
92
|
+
fromStage,
|
|
93
|
+
toStage,
|
|
94
|
+
reason,
|
|
95
|
+
actor,
|
|
96
|
+
userOverride: normalizeBoolean(override.userOverride),
|
|
97
|
+
recordedAt,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function normalizeWorkflowOverrides(value = {}, options = {}) {
|
|
102
|
+
const overrides = Array.isArray(value.overrides)
|
|
103
|
+
? value.overrides.map(override => normalizeOverrideRecord(override, options))
|
|
104
|
+
: [];
|
|
105
|
+
const userOverride = normalizeBoolean(value.userOverride);
|
|
106
|
+
|
|
107
|
+
if (userOverride && overrides.length === 0) {
|
|
108
|
+
throw new Error('workflowDecisions.userOverride requires at least one override record');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
overrides,
|
|
113
|
+
userOverride: overrides.length > 0 || userOverride,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function buildWorkflowDecisions(classification, value, overrideState) {
|
|
118
|
+
return {
|
|
119
|
+
classification,
|
|
120
|
+
reason: normalizeString(value.reason),
|
|
121
|
+
userOverride: overrideState.userOverride,
|
|
122
|
+
overrides: overrideState.overrides,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeWorkflowDecisions(value = {}, options = {}) {
|
|
127
|
+
const hasClassification = value && typeof value === 'object'
|
|
128
|
+
? Object.hasOwn(value, 'classification')
|
|
129
|
+
: false;
|
|
130
|
+
const classification = normalizeString(value.classification);
|
|
131
|
+
const overrideState = normalizeWorkflowOverrides(value, options);
|
|
132
|
+
|
|
133
|
+
if (!classification) {
|
|
134
|
+
if (options.allowLegacyDefaultClassification && !hasClassification) {
|
|
135
|
+
return buildWorkflowDecisions('standard', value, overrideState);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
throw new Error(
|
|
139
|
+
'Workflow state is missing a classification field. Delete .forge-state.json to reset, or add "classification": "standard" manually.'
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!WORKFLOW_CLASSIFICATIONS.includes(classification)) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Invalid workflow classification: ${value.classification}. Expected one of: ${WORKFLOW_CLASSIFICATIONS.join(', ')}`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return buildWorkflowDecisions(classification, value, overrideState);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function shouldUseLegacyStandardVerifyPath(classification, input, options = {}) {
|
|
153
|
+
const legacyBySchema = input.schemaVersion == null
|
|
154
|
+
? options.treatMissingSchemaAsCurrent !== true
|
|
155
|
+
: input.schemaVersion < WORKFLOW_STATE_SCHEMA_VERSION;
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
options.allowLegacyStandardVerify === true &&
|
|
159
|
+
classification === 'standard' &&
|
|
160
|
+
(
|
|
161
|
+
input[LEGACY_STANDARD_VERIFY] === true ||
|
|
162
|
+
legacyBySchema
|
|
163
|
+
)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function assertWorkflowPathTransition(previousStage, currentStage, workflowPath, classification) {
|
|
168
|
+
const previousIndex = workflowPath.indexOf(previousStage);
|
|
169
|
+
const currentIndex = workflowPath.indexOf(currentStage);
|
|
170
|
+
const allowed = previousIndex === -1 || previousIndex === workflowPath.length - 1
|
|
171
|
+
? []
|
|
172
|
+
: [workflowPath[previousIndex + 1]];
|
|
173
|
+
|
|
174
|
+
if (previousIndex !== -1 && currentIndex === previousIndex + 1) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const suffix = allowed.length > 0 ? ` Allowed next stages: ${allowed.join(', ')}.` : '';
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Invalid workflow transition: ${previousStage} -> ${currentStage} for ${classification} workflow.${suffix}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeWorkflowState(input = {}, options = {}) {
|
|
185
|
+
const currentStage = normalizeStageId(input.currentStage);
|
|
186
|
+
if (!currentStage) {
|
|
187
|
+
throw new Error(`Invalid current stage: ${input.currentStage}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const workflowDecisions = normalizeWorkflowDecisions(input.workflowDecisions || {}, options);
|
|
191
|
+
const useLegacyStandardVerify = shouldUseLegacyStandardVerifyPath(
|
|
192
|
+
workflowDecisions.classification,
|
|
193
|
+
input,
|
|
194
|
+
options
|
|
195
|
+
);
|
|
196
|
+
const workflowPath = useLegacyStandardVerify
|
|
197
|
+
? LEGACY_STANDARD_WORKFLOW_PATH
|
|
198
|
+
: getWorkflowPath(workflowDecisions.classification);
|
|
199
|
+
|
|
200
|
+
const previousStage = input.previousStage == null ? null : normalizeStageId(input.previousStage);
|
|
201
|
+
if (input.previousStage != null && !previousStage) {
|
|
202
|
+
throw new Error(`Invalid previous stage: ${input.previousStage}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!workflowPath.includes(currentStage)) {
|
|
206
|
+
throw new Error(`Stage ${currentStage} is not valid for ${workflowDecisions.classification} workflow`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (previousStage) {
|
|
210
|
+
assertWorkflowPathTransition(previousStage, currentStage, workflowPath, workflowDecisions.classification);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const completedStages = normalizeStageList(input.completedStages);
|
|
214
|
+
const skippedStages = normalizeStageList(input.skippedStages);
|
|
215
|
+
const invalidCompletedStage = completedStages.find(stage => !workflowPath.includes(stage));
|
|
216
|
+
if (invalidCompletedStage) {
|
|
217
|
+
throw new Error(`Completed stage ${invalidCompletedStage} is not valid for ${workflowDecisions.classification} workflow`);
|
|
218
|
+
}
|
|
219
|
+
const invalidSkippedStage = skippedStages.find(stage => !workflowPath.includes(stage));
|
|
220
|
+
if (invalidSkippedStage) {
|
|
221
|
+
throw new Error(`Skipped stage ${invalidSkippedStage} is not valid for ${workflowDecisions.classification} workflow`);
|
|
222
|
+
}
|
|
223
|
+
const parallelTracks = Array.isArray(input.parallelTracks)
|
|
224
|
+
? input.parallelTracks.map(normalizeParallelTrack).filter(Boolean)
|
|
225
|
+
: [];
|
|
226
|
+
|
|
227
|
+
const payload = {
|
|
228
|
+
schemaVersion: useLegacyStandardVerify ? 1 : WORKFLOW_STATE_SCHEMA_VERSION,
|
|
229
|
+
currentStage,
|
|
230
|
+
completedStages,
|
|
231
|
+
skippedStages,
|
|
232
|
+
workflowDecisions,
|
|
233
|
+
parallelTracks,
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
if (useLegacyStandardVerify) {
|
|
237
|
+
Object.defineProperty(payload, LEGACY_STANDARD_VERIFY, {
|
|
238
|
+
value: true,
|
|
239
|
+
enumerable: false,
|
|
240
|
+
configurable: false,
|
|
241
|
+
writable: false,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return payload;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function serializeWorkflowState(input) {
|
|
249
|
+
return normalizeWorkflowState(input, {
|
|
250
|
+
...LEGACY_WORKFLOW_STATE_OPTIONS,
|
|
251
|
+
treatMissingSchemaAsCurrent: true,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function readWorkflowState(source) {
|
|
256
|
+
if (source == null) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (typeof source === 'string') {
|
|
261
|
+
try {
|
|
262
|
+
return normalizeWorkflowState(JSON.parse(source), LEGACY_WORKFLOW_STATE_OPTIONS);
|
|
263
|
+
} catch (error) {
|
|
264
|
+
throw new Error(`Failed to parse workflow state: ${error.message}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (typeof source !== 'object') {
|
|
269
|
+
throw new Error('Workflow state source must be a string or object');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (source.schemaVersion === WORKFLOW_STATE_SCHEMA_VERSION || source.currentStage) {
|
|
273
|
+
return normalizeWorkflowState(source, LEGACY_WORKFLOW_STATE_OPTIONS);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (source.workflowState) {
|
|
277
|
+
return normalizeWorkflowState(source.workflowState, LEGACY_WORKFLOW_STATE_OPTIONS);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (source.metadata && source.metadata.workflowState) {
|
|
281
|
+
return normalizeWorkflowState(source.metadata.workflowState, LEGACY_WORKFLOW_STATE_OPTIONS);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return normalizeWorkflowState(source, LEGACY_WORKFLOW_STATE_OPTIONS);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function writeWorkflowState(input) {
|
|
288
|
+
const payload = serializeWorkflowState(input);
|
|
289
|
+
|
|
290
|
+
return JSON.stringify(payload, null, 2);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function getWorkflowPathForState(workflowState) {
|
|
294
|
+
if (!workflowState || typeof workflowState !== 'object') {
|
|
295
|
+
return Object.freeze([]);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return workflowState[LEGACY_STANDARD_VERIFY]
|
|
299
|
+
? LEGACY_STANDARD_WORKFLOW_PATH
|
|
300
|
+
: getWorkflowPath(workflowState.workflowDecisions?.classification);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function getAllowedTransitionsForWorkflowState(workflowState) {
|
|
304
|
+
const currentStage = normalizeStageId(workflowState?.currentStage);
|
|
305
|
+
if (!currentStage) {
|
|
306
|
+
return Object.freeze([]);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const workflowPath = getWorkflowPathForState(workflowState);
|
|
310
|
+
const currentIndex = workflowPath.indexOf(currentStage);
|
|
311
|
+
if (currentIndex === -1 || currentIndex === workflowPath.length - 1) {
|
|
312
|
+
return Object.freeze([]);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return Object.freeze([workflowPath[currentIndex + 1]]);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
module.exports = {
|
|
319
|
+
getAllowedTransitionsForWorkflowState,
|
|
320
|
+
getWorkflowPathForState,
|
|
321
|
+
LEGACY_STANDARD_VERIFY,
|
|
322
|
+
WORKFLOW_STATE_SCHEMA_VERSION,
|
|
323
|
+
WORKFLOW_CLASSIFICATIONS,
|
|
324
|
+
normalizeOverrideRecord,
|
|
325
|
+
normalizeWorkflowDecisions,
|
|
326
|
+
normalizeWorkflowState,
|
|
327
|
+
serializeWorkflowState,
|
|
328
|
+
readWorkflowState,
|
|
329
|
+
writeWorkflowState,
|
|
330
|
+
STAGE_IDS,
|
|
331
|
+
STAGE_MODEL,
|
|
332
|
+
};
|
package/opencode.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"permission": {
|
|
4
|
+
"*": "ask",
|
|
5
|
+
"bash": {
|
|
6
|
+
"*": "ask",
|
|
7
|
+
"git status *": "allow",
|
|
8
|
+
"git log *": "allow",
|
|
9
|
+
"git diff *": "allow",
|
|
10
|
+
"git branch *": "allow",
|
|
11
|
+
"git show *": "allow",
|
|
12
|
+
"git remote *": "allow",
|
|
13
|
+
"git stash *": "allow",
|
|
14
|
+
"git tag *": "allow",
|
|
15
|
+
"git rev-parse *": "allow",
|
|
16
|
+
"git describe *": "allow",
|
|
17
|
+
"git ls-files *": "allow",
|
|
18
|
+
"git blame *": "allow",
|
|
19
|
+
"git shortlog *": "allow",
|
|
20
|
+
"git add *": "allow",
|
|
21
|
+
"git commit *": "allow",
|
|
22
|
+
"git push *": "allow",
|
|
23
|
+
"git checkout *": "allow",
|
|
24
|
+
"git switch *": "allow",
|
|
25
|
+
"git merge *": "allow",
|
|
26
|
+
"git rebase *": "ask",
|
|
27
|
+
"git cherry-pick *": "allow",
|
|
28
|
+
"bd *": "allow",
|
|
29
|
+
"gh *": "allow",
|
|
30
|
+
"ls *": "allow",
|
|
31
|
+
"cat *": "allow",
|
|
32
|
+
"grep *": "allow",
|
|
33
|
+
"find *": "allow",
|
|
34
|
+
"pwd *": "allow",
|
|
35
|
+
"which *": "allow",
|
|
36
|
+
"echo *": "allow",
|
|
37
|
+
"head *": "allow",
|
|
38
|
+
"tail *": "allow",
|
|
39
|
+
"sort *": "allow",
|
|
40
|
+
"wc *": "allow",
|
|
41
|
+
"awk *": "allow",
|
|
42
|
+
"sed *": "allow",
|
|
43
|
+
"stat *": "allow",
|
|
44
|
+
"du *": "allow",
|
|
45
|
+
"df *": "allow",
|
|
46
|
+
"file *": "allow",
|
|
47
|
+
"mkdir *": "allow",
|
|
48
|
+
"touch *": "allow",
|
|
49
|
+
"cp *": "allow",
|
|
50
|
+
"mv *": "allow",
|
|
51
|
+
"bun *": "allow",
|
|
52
|
+
"bunx *": "allow",
|
|
53
|
+
"npm *": "allow",
|
|
54
|
+
"npx *": "allow",
|
|
55
|
+
"node *": "allow",
|
|
56
|
+
|
|
57
|
+
"git worktree *": "allow",
|
|
58
|
+
"git reset --hard *": "ask",
|
|
59
|
+
"rm -rf *": "deny",
|
|
60
|
+
"git push --force *": "deny",
|
|
61
|
+
"git push -f *": "deny"
|
|
62
|
+
},
|
|
63
|
+
"edit": {
|
|
64
|
+
"*": "allow"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forge-workflow",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "7-stage TDD workflow for ALL AI coding agents (Claude, Cursor, Cline, OpenCode, Copilot, Kilo Code, Roo Code, Codex)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"forge": "bin/forge.js",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"packages/*"
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
|
-
"test": "bun test",
|
|
13
|
+
"test": "bun test --timeout 15000",
|
|
14
14
|
"test:setup": "bash test-env/automation/setup-fixtures.sh",
|
|
15
15
|
"test:all": "bun run test:setup && bun test",
|
|
16
16
|
"test:coverage": "c8 --check-coverage bun test",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"eslint": "^10.0.2",
|
|
44
44
|
"globals": "^17.3.0",
|
|
45
45
|
"js-yaml": "^4.1.1",
|
|
46
|
-
"lefthook": "^1.
|
|
47
|
-
"yaml": "^2.8.
|
|
46
|
+
"lefthook": "^2.1.4",
|
|
47
|
+
"yaml": "^2.8.3"
|
|
48
48
|
},
|
|
49
49
|
"keywords": [
|
|
50
50
|
"ai",
|
|
@@ -81,11 +81,17 @@
|
|
|
81
81
|
".claude/scripts/",
|
|
82
82
|
".claude/skills/",
|
|
83
83
|
".cursor/",
|
|
84
|
+
"!.cursor/node_modules/",
|
|
84
85
|
".cline/",
|
|
86
|
+
"!.cline/node_modules/",
|
|
85
87
|
".roo/",
|
|
88
|
+
"!.roo/node_modules/",
|
|
86
89
|
".codex/",
|
|
90
|
+
"!.codex/node_modules/",
|
|
87
91
|
".kilocode/",
|
|
92
|
+
"!.kilocode/node_modules/",
|
|
88
93
|
".opencode/",
|
|
94
|
+
"!.opencode/node_modules/",
|
|
89
95
|
"scripts/",
|
|
90
96
|
".github/prompts/",
|
|
91
97
|
".github/PLUGIN_TEMPLATE.json",
|
|
@@ -97,7 +103,9 @@
|
|
|
97
103
|
"lefthook.yml",
|
|
98
104
|
"AGENTS.md",
|
|
99
105
|
"CLAUDE.md",
|
|
100
|
-
".mcp.json.example"
|
|
106
|
+
".mcp.json.example",
|
|
107
|
+
".cursorrules",
|
|
108
|
+
"opencode.json"
|
|
101
109
|
],
|
|
102
110
|
"dependencies": {
|
|
103
111
|
"@babel/parser": "^7.29.2",
|
|
@@ -106,7 +114,9 @@
|
|
|
106
114
|
"overrides": {
|
|
107
115
|
"eslint": "^10.0.2",
|
|
108
116
|
"flatted": "3.4.2",
|
|
109
|
-
"minimatch": "10.2.4"
|
|
117
|
+
"minimatch": "10.2.4",
|
|
118
|
+
"brace-expansion": "5.0.5",
|
|
119
|
+
"lodash": "4.18.1"
|
|
110
120
|
},
|
|
111
121
|
"c8": {
|
|
112
122
|
"lines": 80,
|