@open-agent-toolkit/cli 0.2.59 → 0.2.62
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/assets/agents/oat-phase-implementer.md +27 -4
- package/assets/agents/oat-reviewer.md +25 -14
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/workflow-gates.md +3 -3
- package/assets/docs/reference/cli-reference.md +10 -3
- package/assets/docs/reference/oat-directory-structure.md +1 -0
- package/assets/docs/workflows/ideas/index.md +1 -1
- package/assets/docs/workflows/index.md +1 -0
- package/assets/docs/workflows/projects/artifacts.md +24 -3
- package/assets/docs/workflows/projects/hill-checkpoints.md +21 -0
- package/assets/docs/workflows/projects/implementation-execution.md +5 -0
- package/assets/docs/workflows/projects/lifecycle.md +124 -7
- package/assets/docs/workflows/projects/pr-flow.md +2 -0
- package/assets/docs/workflows/projects/reviews.md +1 -1
- package/assets/docs/workflows/projects/state-machine.md +20 -0
- package/assets/docs/workflows/skills/index.md +2 -1
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-brainstorm/SKILL.md +61 -12
- package/assets/skills/oat-docs/SKILL.md +13 -8
- package/assets/skills/oat-doctor/SKILL.md +3 -3
- package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -2
- package/assets/skills/oat-project-autonomous/SKILL.md +14 -9
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +56 -42
- package/assets/skills/oat-project-capture/SKILL.md +2 -2
- package/assets/skills/oat-project-discover/SKILL.md +6 -5
- package/assets/skills/oat-project-document/SKILL.md +11 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +56 -42
- package/assets/skills/oat-project-implement/SKILL.md +3 -3
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +106 -15
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +56 -42
- package/assets/skills/oat-project-implement/references/phase-execution.md +16 -1
- package/assets/skills/oat-project-implement/references/plan-and-resume.md +19 -1
- package/assets/skills/oat-project-import-plan/SKILL.md +47 -3
- package/assets/skills/oat-project-lite/SKILL.md +467 -0
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +331 -0
- package/assets/skills/oat-project-next/SKILL.md +26 -10
- package/assets/skills/oat-project-plan/SKILL.md +14 -2
- package/assets/skills/oat-project-plan-writing/SKILL.md +6 -3
- package/assets/skills/oat-project-pr-final/SKILL.md +33 -7
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +56 -42
- package/assets/skills/oat-project-pr-progress/SKILL.md +3 -3
- package/assets/skills/oat-project-progress/SKILL.md +16 -3
- package/assets/skills/oat-project-promote-spec-driven/SKILL.md +3 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +56 -42
- package/assets/skills/oat-project-review-provide/SKILL.md +25 -6
- package/assets/skills/oat-project-summary/SKILL.md +27 -3
- package/assets/templates/plan-lite.md +165 -0
- package/assets/templates/plan.md +1 -1
- package/assets/templates/state.md +1 -1
- package/dist/commands/project/index.d.ts.map +1 -1
- package/dist/commands/project/index.js +2 -0
- package/dist/commands/project/new/index.d.ts +3 -2
- package/dist/commands/project/new/index.d.ts.map +1 -1
- package/dist/commands/project/new/index.js +2 -1
- package/dist/commands/project/new/scaffold.d.ts +5 -3
- package/dist/commands/project/new/scaffold.d.ts.map +1 -1
- package/dist/commands/project/new/scaffold.js +38 -10
- package/dist/commands/project/promote/index.d.ts +2 -0
- package/dist/commands/project/promote/index.d.ts.map +1 -0
- package/dist/commands/project/promote/index.js +1 -0
- package/dist/commands/project/promote/promote.d.ts +47 -0
- package/dist/commands/project/promote/promote.d.ts.map +1 -0
- package/dist/commands/project/promote/promote.js +322 -0
- package/dist/commands/project/split/run.d.ts.map +1 -1
- package/dist/commands/project/split/run.js +8 -1
- package/dist/commands/project/validate-plan/index.d.ts.map +1 -1
- package/dist/commands/project/validate-plan/index.js +25 -1
- package/dist/commands/project/validate-plan/validate-plan.d.ts +8 -0
- package/dist/commands/project/validate-plan/validate-plan.d.ts.map +1 -1
- package/dist/commands/project/validate-plan/validate-plan.js +49 -0
- package/dist/commands/shared/frontmatter.d.ts +1 -1
- package/dist/commands/shared/frontmatter.d.ts.map +1 -1
- package/dist/commands/shared/frontmatter.js +1 -0
- package/dist/commands/state/generate.d.ts.map +1 -1
- package/dist/commands/state/generate.js +23 -0
- package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-manifest.js +2 -0
- package/package.json +2 -2
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { mkdir as defaultMkdir, readFile as defaultReadFile, rename as defaultRename, writeFile as defaultWriteFile, } from 'node:fs/promises';
|
|
2
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { buildCommandContext, } from '../../../app/command-context.js';
|
|
4
|
+
import { applyTemplateReplacements, resolveTemplateSource as defaultResolveTemplateSource, } from '../../project/new/scaffold.js';
|
|
5
|
+
import { defaultGitRunner } from '../../project/sync/git.js';
|
|
6
|
+
import { buildSyncTarget, pushSynced as defaultPushSynced, } from '../../project/sync/ref-sync.js';
|
|
7
|
+
import { getFrontmatterBlock } from '../../shared/frontmatter.js';
|
|
8
|
+
import { replaceFrontmatter } from '../../shared/frontmatter-write.js';
|
|
9
|
+
import { resolveProjectsRoot as defaultResolveProjectsRoot } from '../../shared/oat-paths.js';
|
|
10
|
+
import { resolveProjectScope } from '../../shared/project-scope.js';
|
|
11
|
+
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
12
|
+
import { fileExists as defaultFileExists } from '../../../fs/io.js';
|
|
13
|
+
import { resolveProjectRoot } from '../../../fs/paths.js';
|
|
14
|
+
import { Command } from 'commander';
|
|
15
|
+
import YAML from 'yaml';
|
|
16
|
+
const PROMOTED_FILES = [
|
|
17
|
+
'discovery.md',
|
|
18
|
+
'references/lite-plan.md',
|
|
19
|
+
'plan.md',
|
|
20
|
+
'state.md',
|
|
21
|
+
];
|
|
22
|
+
const LITE_SECTION_HEADINGS = [
|
|
23
|
+
['Summary', 'summary'],
|
|
24
|
+
['Decisions', 'decisions'],
|
|
25
|
+
['Assumptions', 'assumptions'],
|
|
26
|
+
['Out of Scope', 'outOfScope'],
|
|
27
|
+
['Validation Criteria', 'validationCriteria'],
|
|
28
|
+
];
|
|
29
|
+
const DEFAULT_DEPENDENCIES = {
|
|
30
|
+
buildCommandContext,
|
|
31
|
+
resolveProjectRoot,
|
|
32
|
+
resolveProjectsRoot: defaultResolveProjectsRoot,
|
|
33
|
+
resolveTemplateSource: defaultResolveTemplateSource,
|
|
34
|
+
readFile: defaultReadFile,
|
|
35
|
+
writeFile: defaultWriteFile,
|
|
36
|
+
mkdir: defaultMkdir,
|
|
37
|
+
rename: defaultRename,
|
|
38
|
+
fileExists: defaultFileExists,
|
|
39
|
+
gitRunner: defaultGitRunner,
|
|
40
|
+
pushSynced: defaultPushSynced,
|
|
41
|
+
processEnv: process.env,
|
|
42
|
+
now: () => new Date(),
|
|
43
|
+
};
|
|
44
|
+
function extractSection(content, heading) {
|
|
45
|
+
const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
46
|
+
const headingMatch = new RegExp(`^## ${escapedHeading}[ \\t]*$`, 'm').exec(content);
|
|
47
|
+
if (!headingMatch)
|
|
48
|
+
return null;
|
|
49
|
+
const bodyStart = headingMatch.index + headingMatch[0].length;
|
|
50
|
+
const remaining = content.slice(bodyStart);
|
|
51
|
+
const nextHeading = /^##\s+/m.exec(remaining);
|
|
52
|
+
const bodyEnd = nextHeading ? bodyStart + nextHeading.index : content.length;
|
|
53
|
+
const body = content.slice(bodyStart, bodyEnd).trim();
|
|
54
|
+
return body.length > 0 ? body : null;
|
|
55
|
+
}
|
|
56
|
+
function extractLitePlanSections(planContent) {
|
|
57
|
+
const sections = {};
|
|
58
|
+
for (const [heading, key] of LITE_SECTION_HEADINGS) {
|
|
59
|
+
const body = extractSection(planContent, heading);
|
|
60
|
+
if (!body)
|
|
61
|
+
return null;
|
|
62
|
+
sections[key] = body;
|
|
63
|
+
}
|
|
64
|
+
const productBehavior = extractSection(planContent, 'Product Behavior');
|
|
65
|
+
const technicalDesign = extractSection(planContent, 'Technical Design');
|
|
66
|
+
if (productBehavior)
|
|
67
|
+
sections.productBehavior = productBehavior;
|
|
68
|
+
if (technicalDesign)
|
|
69
|
+
sections.technicalDesign = technicalDesign;
|
|
70
|
+
return sections;
|
|
71
|
+
}
|
|
72
|
+
function containsUnresolvedTemplateContent(sections, templateSections) {
|
|
73
|
+
const containsTemplateMarkers = (key) => {
|
|
74
|
+
const content = sections[key];
|
|
75
|
+
const templateContent = templateSections[key];
|
|
76
|
+
if (!content || !templateContent)
|
|
77
|
+
return false;
|
|
78
|
+
if (content === templateContent)
|
|
79
|
+
return true;
|
|
80
|
+
const markers = templateContent
|
|
81
|
+
.match(/\[[^\]\n]+\]|\{[^{}\n]+\}/g)
|
|
82
|
+
?.filter((marker) => !/^\[\s*\]$/.test(marker)) ?? [];
|
|
83
|
+
const instructionalComments = templateContent.match(/<!--[\s\S]*?-->/g) ?? [];
|
|
84
|
+
return [...markers, ...instructionalComments].some((marker) => content.includes(marker));
|
|
85
|
+
};
|
|
86
|
+
return (LITE_SECTION_HEADINGS.some(([, key]) => containsTemplateMarkers(key)) ||
|
|
87
|
+
['productBehavior', 'technicalDesign'].some((key) => containsTemplateMarkers(key)));
|
|
88
|
+
}
|
|
89
|
+
export function parseLitePlanSections(planContent, liteTemplateContent) {
|
|
90
|
+
const sections = extractLitePlanSections(planContent);
|
|
91
|
+
if (!sections || !liteTemplateContent)
|
|
92
|
+
return sections;
|
|
93
|
+
const templateSections = extractLitePlanSections(liteTemplateContent);
|
|
94
|
+
if (!templateSections ||
|
|
95
|
+
containsUnresolvedTemplateContent(sections, templateSections)) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return sections;
|
|
99
|
+
}
|
|
100
|
+
function readObjectFrontmatter(content, filePath) {
|
|
101
|
+
const block = getFrontmatterBlock(content);
|
|
102
|
+
if (!block) {
|
|
103
|
+
throw new Error(`${filePath} is missing frontmatter`);
|
|
104
|
+
}
|
|
105
|
+
const parsed = YAML.parse(block);
|
|
106
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
107
|
+
throw new Error(`${filePath} frontmatter must be a YAML object`);
|
|
108
|
+
}
|
|
109
|
+
return parsed;
|
|
110
|
+
}
|
|
111
|
+
function replaceSection(content, heading, body) {
|
|
112
|
+
const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
113
|
+
const headingMatch = new RegExp(`^## ${escapedHeading}[ \\t]*$`, 'm').exec(content);
|
|
114
|
+
if (!headingMatch) {
|
|
115
|
+
throw new Error(`discovery template is missing ## ${heading}`);
|
|
116
|
+
}
|
|
117
|
+
const bodyStart = headingMatch.index + headingMatch[0].length;
|
|
118
|
+
const remaining = content.slice(bodyStart);
|
|
119
|
+
const nextHeading = /^##\s+/m.exec(remaining);
|
|
120
|
+
const bodyEnd = nextHeading ? bodyStart + nextHeading.index : content.length;
|
|
121
|
+
const suffix = content.slice(bodyEnd).replace(/^\s*/, '');
|
|
122
|
+
return `${content.slice(0, bodyStart)}\n\n${body.trim()}\n\n${suffix}`;
|
|
123
|
+
}
|
|
124
|
+
function renderDiscovery(template, projectName, today, nowUtc, sections) {
|
|
125
|
+
let rendered = applyTemplateReplacements(template, projectName, today, nowUtc, 'quick');
|
|
126
|
+
const frontmatter = readObjectFrontmatter(rendered, 'rendered discovery.md');
|
|
127
|
+
frontmatter['oat_ready_for'] = 'oat-project-quick-start';
|
|
128
|
+
rendered = replaceFrontmatter(rendered, YAML.stringify(frontmatter).trimEnd());
|
|
129
|
+
const successCriteria = sections.productBehavior
|
|
130
|
+
? `### Product Behavior (from Lite plan)\n\n${sections.productBehavior}\n\n### Validation Criteria (from Lite plan)\n\n${sections.validationCriteria}`
|
|
131
|
+
: sections.validationCriteria;
|
|
132
|
+
for (const [heading, body] of [
|
|
133
|
+
['Initial Request', sections.summary],
|
|
134
|
+
['Key Decisions', sections.decisions],
|
|
135
|
+
['Assumptions', sections.assumptions],
|
|
136
|
+
['Out of Scope', sections.outOfScope],
|
|
137
|
+
['Success Criteria', successCriteria],
|
|
138
|
+
]) {
|
|
139
|
+
rendered = replaceSection(rendered, heading, body);
|
|
140
|
+
}
|
|
141
|
+
if (sections.technicalDesign) {
|
|
142
|
+
const nextSteps = /^## Next Steps[ \t]*$/m.exec(rendered);
|
|
143
|
+
if (!nextSteps) {
|
|
144
|
+
throw new Error('discovery template is missing ## Next Steps');
|
|
145
|
+
}
|
|
146
|
+
const carriedForward = [
|
|
147
|
+
'## Carried-Forward Technical Design',
|
|
148
|
+
'',
|
|
149
|
+
'> Prior implementation context carried forward from the Lite plan; this is not a new discovery deliverable.',
|
|
150
|
+
'',
|
|
151
|
+
sections.technicalDesign,
|
|
152
|
+
'',
|
|
153
|
+
].join('\n');
|
|
154
|
+
rendered = `${rendered.slice(0, nextSteps.index)}${carriedForward}${rendered.slice(nextSteps.index)}`;
|
|
155
|
+
}
|
|
156
|
+
return rendered;
|
|
157
|
+
}
|
|
158
|
+
function renderQuickState(stateContent, statePath, nowUtc) {
|
|
159
|
+
const frontmatter = readObjectFrontmatter(stateContent, statePath);
|
|
160
|
+
frontmatter['oat_workflow_mode'] = 'quick';
|
|
161
|
+
frontmatter['oat_phase'] = 'discovery';
|
|
162
|
+
frontmatter['oat_phase_status'] = 'complete';
|
|
163
|
+
frontmatter['oat_ready_for'] = 'oat-project-quick-start';
|
|
164
|
+
frontmatter['oat_project_state_updated'] = nowUtc;
|
|
165
|
+
return replaceFrontmatter(stateContent, YAML.stringify(frontmatter).trimEnd());
|
|
166
|
+
}
|
|
167
|
+
function repoRelativePath(repoRoot, path) {
|
|
168
|
+
return relative(repoRoot, path).split(sep).join('/');
|
|
169
|
+
}
|
|
170
|
+
async function persistPromotion(repoRoot, projectsRoot, projectRoot, projectName, scope, dependencies) {
|
|
171
|
+
const message = `chore(oat): promote ${projectName} to quick`;
|
|
172
|
+
if (scope === 'synced') {
|
|
173
|
+
const target = buildSyncTarget(repoRoot, projectsRoot, projectName);
|
|
174
|
+
const result = await dependencies.pushSynced(target, dependencies.gitRunner, { message });
|
|
175
|
+
if (result.status !== 'pushed' && result.status !== 'up-to-date') {
|
|
176
|
+
throw new Error(`synced promotion push ${result.status}`);
|
|
177
|
+
}
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (scope === 'local') {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const pathspecs = PROMOTED_FILES.map((file) => repoRelativePath(repoRoot, join(projectRoot, file)));
|
|
184
|
+
await dependencies.gitRunner.run(['add', '--', ...pathspecs], {
|
|
185
|
+
cwd: repoRoot,
|
|
186
|
+
});
|
|
187
|
+
await dependencies.gitRunner.run(['commit', '-m', message, '--', ...pathspecs], { cwd: repoRoot });
|
|
188
|
+
}
|
|
189
|
+
async function promoteProject(projectPath, options, context, dependencies) {
|
|
190
|
+
if (options.to !== 'quick') {
|
|
191
|
+
return { status: 'refused', reason: 'unsupported-target', files: [] };
|
|
192
|
+
}
|
|
193
|
+
const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
194
|
+
const projectRoot = isAbsolute(projectPath)
|
|
195
|
+
? resolve(projectPath)
|
|
196
|
+
: resolve(repoRoot, projectPath);
|
|
197
|
+
const projectName = basename(projectRoot);
|
|
198
|
+
const statePath = join(projectRoot, 'state.md');
|
|
199
|
+
const planPath = join(projectRoot, 'plan.md');
|
|
200
|
+
const referencePath = join(projectRoot, 'references', 'lite-plan.md');
|
|
201
|
+
let stateContent;
|
|
202
|
+
let planContent;
|
|
203
|
+
try {
|
|
204
|
+
[stateContent, planContent] = await Promise.all([
|
|
205
|
+
dependencies.readFile(statePath, 'utf8'),
|
|
206
|
+
dependencies.readFile(planPath, 'utf8'),
|
|
207
|
+
]);
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return { status: 'refused', reason: 'project-unreadable', files: [] };
|
|
211
|
+
}
|
|
212
|
+
let stateFrontmatter;
|
|
213
|
+
try {
|
|
214
|
+
stateFrontmatter = readObjectFrontmatter(stateContent, statePath);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
return { status: 'refused', reason: 'project-unreadable', files: [] };
|
|
218
|
+
}
|
|
219
|
+
if (stateFrontmatter['oat_workflow_mode'] !== 'lite') {
|
|
220
|
+
return { status: 'refused', reason: 'not-lite', files: [] };
|
|
221
|
+
}
|
|
222
|
+
if (await dependencies.fileExists(referencePath)) {
|
|
223
|
+
return {
|
|
224
|
+
status: 'refused',
|
|
225
|
+
reason: 'lite-plan-reference-exists',
|
|
226
|
+
files: [],
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const userOatRoot = join(context.home, '.oat');
|
|
230
|
+
let liteTemplateContent;
|
|
231
|
+
try {
|
|
232
|
+
const liteTemplatePath = await dependencies.resolveTemplateSource(userOatRoot, repoRoot, 'plan-lite.md');
|
|
233
|
+
liteTemplateContent = await dependencies.readFile(liteTemplatePath, 'utf8');
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return { status: 'refused', reason: 'template-unreadable', files: [] };
|
|
237
|
+
}
|
|
238
|
+
const sections = parseLitePlanSections(planContent, liteTemplateContent);
|
|
239
|
+
if (!sections) {
|
|
240
|
+
return { status: 'refused', reason: 'invalid-lite-plan', files: [] };
|
|
241
|
+
}
|
|
242
|
+
const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
|
|
243
|
+
const scope = resolveProjectScope(projectRoot, projectsRoot, repoRoot);
|
|
244
|
+
if (!scope) {
|
|
245
|
+
return { status: 'refused', reason: 'scope-unresolved', files: [] };
|
|
246
|
+
}
|
|
247
|
+
const now = dependencies.now();
|
|
248
|
+
const nowUtc = now.toISOString();
|
|
249
|
+
const today = nowUtc.slice(0, 10);
|
|
250
|
+
let discoveryContent;
|
|
251
|
+
let quickPlanContent;
|
|
252
|
+
let quickStateContent;
|
|
253
|
+
try {
|
|
254
|
+
const [discoveryTemplatePath, quickPlanTemplatePath] = await Promise.all([
|
|
255
|
+
dependencies.resolveTemplateSource(userOatRoot, repoRoot, 'discovery.md'),
|
|
256
|
+
dependencies.resolveTemplateSource(userOatRoot, repoRoot, 'plan.md'),
|
|
257
|
+
]);
|
|
258
|
+
const [discoveryTemplate, quickPlanTemplate] = await Promise.all([
|
|
259
|
+
dependencies.readFile(discoveryTemplatePath, 'utf8'),
|
|
260
|
+
dependencies.readFile(quickPlanTemplatePath, 'utf8'),
|
|
261
|
+
]);
|
|
262
|
+
discoveryContent = renderDiscovery(discoveryTemplate, projectName, today, nowUtc, sections);
|
|
263
|
+
quickPlanContent = applyTemplateReplacements(quickPlanTemplate, projectName, today, nowUtc, 'quick');
|
|
264
|
+
quickStateContent = renderQuickState(stateContent, statePath, nowUtc);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
return { status: 'refused', reason: 'template-unreadable', files: [] };
|
|
268
|
+
}
|
|
269
|
+
const writtenFiles = [];
|
|
270
|
+
try {
|
|
271
|
+
await dependencies.mkdir(join(projectRoot, 'references'), {
|
|
272
|
+
recursive: true,
|
|
273
|
+
});
|
|
274
|
+
await dependencies.writeFile(join(projectRoot, 'discovery.md'), discoveryContent, 'utf8');
|
|
275
|
+
writtenFiles.push('discovery.md');
|
|
276
|
+
await dependencies.rename(planPath, referencePath);
|
|
277
|
+
writtenFiles.push('references/lite-plan.md');
|
|
278
|
+
await dependencies.writeFile(planPath, quickPlanContent, 'utf8');
|
|
279
|
+
writtenFiles.push('plan.md');
|
|
280
|
+
await dependencies.writeFile(statePath, quickStateContent, 'utf8');
|
|
281
|
+
writtenFiles.push('state.md');
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return { status: 'refused', reason: 'write-failed', files: writtenFiles };
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
await persistPromotion(repoRoot, projectsRoot, projectRoot, projectName, scope, dependencies);
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return {
|
|
291
|
+
status: 'refused',
|
|
292
|
+
reason: 'persistence-failed',
|
|
293
|
+
files: writtenFiles,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
status: 'promoted',
|
|
298
|
+
reason: 'promoted',
|
|
299
|
+
files: [...PROMOTED_FILES],
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
export function createProjectPromoteCommand(overrides = {}) {
|
|
303
|
+
const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
|
|
304
|
+
return new Command('promote')
|
|
305
|
+
.description('Promote an authored lite project to the quick workflow')
|
|
306
|
+
.argument('<project-path>', 'Lite project path to promote')
|
|
307
|
+
.requiredOption('--to <mode>', 'Target workflow mode')
|
|
308
|
+
.action(async (projectPath, options, command) => {
|
|
309
|
+
const context = dependencies.buildCommandContext(readGlobalOptions(command));
|
|
310
|
+
const result = await promoteProject(projectPath, options, context, dependencies);
|
|
311
|
+
if (context.json) {
|
|
312
|
+
context.logger.json(result);
|
|
313
|
+
}
|
|
314
|
+
else if (result.status === 'promoted') {
|
|
315
|
+
context.logger.info(`Promoted ${projectPath} to quick.`);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
context.logger.error(`Promotion refused: ${result.reason}.`);
|
|
319
|
+
}
|
|
320
|
+
process.exitCode = result.status === 'promoted' ? 0 : 1;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/split/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAOlF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAMhE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,UAAU,oBAAoB;IAC5B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,gBAAgB,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,OAAO,iBAAiB,CAAC;CACtC;
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/split/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAOlF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAMhE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,UAAU,oBAAoB;IAC5B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,gBAAgB,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,OAAO,iBAAiB,CAAC;CACtC;AAqND,wBAAgB,4BAA4B,CAC1C,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,OAAO,CA2LT"}
|
|
@@ -63,6 +63,9 @@ async function recordDetectedRecommendation(repoRoot, document, dependencies) {
|
|
|
63
63
|
throw new Error('Cannot record detected split recommendation without an active project');
|
|
64
64
|
}
|
|
65
65
|
const discoveryPath = join(repoRoot, localConfig.activeProject, 'discovery.md');
|
|
66
|
+
if (!(await exists(discoveryPath, dependencies))) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
66
69
|
await dependencies.appendFile(discoveryPath, [
|
|
67
70
|
'',
|
|
68
71
|
'## Detected Split Recommendation',
|
|
@@ -74,6 +77,7 @@ async function recordDetectedRecommendation(repoRoot, document, dependencies) {
|
|
|
74
77
|
.join(', ')}`,
|
|
75
78
|
'',
|
|
76
79
|
].join('\n'), 'utf8');
|
|
80
|
+
return true;
|
|
77
81
|
}
|
|
78
82
|
function isEffectivelyNonInteractive(options, context, processEnv) {
|
|
79
83
|
return (options.nonInteractive === true ||
|
|
@@ -180,7 +184,10 @@ export function createProjectSplitRunCommand(overrides = {}) {
|
|
|
180
184
|
if (isEffectivelyNonInteractive(options, context, dependencies.processEnv) &&
|
|
181
185
|
(document.origin === 'detected-mid-stream' ||
|
|
182
186
|
document.origin === 'detected-convergence')) {
|
|
183
|
-
await recordDetectedRecommendation(repoRoot, document, dependencies);
|
|
187
|
+
const recorded = await recordDetectedRecommendation(repoRoot, document, dependencies);
|
|
188
|
+
if (!recorded) {
|
|
189
|
+
context.logger.info('skipped split recommendation: no discovery.md for this project');
|
|
190
|
+
}
|
|
184
191
|
context.logger.error('Detected split requires interactive confirmation; recommendation recorded.');
|
|
185
192
|
process.exitCode = 1;
|
|
186
193
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/validate-plan/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/validate-plan/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,UAAU,+BAA+B;IACvC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;CACjE;AAMD,wBAAgB,gCAAgC,CAC9C,SAAS,GAAE,OAAO,CAAC,+BAA+B,CAAM,GACvD,OAAO,CAsGT"}
|
|
@@ -3,7 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { buildCommandContext, } from '../../../app/command-context.js';
|
|
4
4
|
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
5
5
|
import { Command } from 'commander';
|
|
6
|
-
import { extractPhaseIdsFromPlan, parseFrontmatterFromContent, validateParallelGroups, } from './validate-plan.js';
|
|
6
|
+
import { extractPhaseIdsFromPlan, parseFrontmatterFromContent, validateLitePlan, validateParallelGroups, } from './validate-plan.js';
|
|
7
7
|
const DEFAULT_DEPENDENCIES = {
|
|
8
8
|
buildCommandContext,
|
|
9
9
|
};
|
|
@@ -47,6 +47,30 @@ export function createProjectValidatePlanCommand(overrides = {}) {
|
|
|
47
47
|
}
|
|
48
48
|
const groups = frontmatterResult.data['oat_plan_parallel_groups'];
|
|
49
49
|
const phaseIds = extractPhaseIdsFromPlan(content);
|
|
50
|
+
let workflowMode = null;
|
|
51
|
+
try {
|
|
52
|
+
const stateContent = readFileSync(join(options.projectPath, 'state.md'), 'utf-8');
|
|
53
|
+
const stateFrontmatter = parseFrontmatterFromContent(stateContent);
|
|
54
|
+
const mode = stateFrontmatter.kind === 'invalid'
|
|
55
|
+
? null
|
|
56
|
+
: stateFrontmatter.data['oat_workflow_mode'];
|
|
57
|
+
workflowMode = typeof mode === 'string' ? mode : null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
workflowMode = null;
|
|
61
|
+
}
|
|
62
|
+
const liteResult = validateLitePlan(content, workflowMode);
|
|
63
|
+
if (!liteResult.ok) {
|
|
64
|
+
if (context.json) {
|
|
65
|
+
context.logger.json({ valid: false, errors: [liteResult.message] });
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
context.logger.error('Plan validation failed:');
|
|
69
|
+
context.logger.error(` - ${liteResult.message}`);
|
|
70
|
+
}
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
50
74
|
const result = validateParallelGroups(groups, phaseIds);
|
|
51
75
|
if (result.valid) {
|
|
52
76
|
if (context.json) {
|
|
@@ -15,6 +15,14 @@ export type ValidationResult = {
|
|
|
15
15
|
valid: false;
|
|
16
16
|
errors: string[];
|
|
17
17
|
};
|
|
18
|
+
export type LitePlanValidationResult = {
|
|
19
|
+
ok: true;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
code: 'lite-multi-phase' | 'lite-parallel-groups' | 'lite-criterion-without-command';
|
|
23
|
+
message: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function validateLitePlan(planContent: string, workflowMode: string | null | undefined): LitePlanValidationResult;
|
|
18
26
|
export declare function validateParallelGroups(groups: unknown, phaseIds: readonly string[]): ValidationResult;
|
|
19
27
|
export declare function extractPhaseIdsFromPlan(planContent: string): string[];
|
|
20
28
|
//# sourceMappingURL=validate-plan.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-plan.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/validate-plan/validate-plan.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GACd,iBAAiB,CAyBnB;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GACf;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEvC,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,gBAAgB,CAgDlB;AAED,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAOrE"}
|
|
1
|
+
{"version":3,"file":"validate-plan.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/validate-plan/validate-plan.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzC,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GACd,iBAAiB,CAyBnB;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GACf;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEvC,MAAM,MAAM,wBAAwB,GAChC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IACE,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EACA,kBAAkB,GAClB,sBAAsB,GACtB,gCAAgC,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAeN,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACtC,wBAAwB,CA8C1B;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,gBAAgB,CAgDlB;AAED,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAOrE"}
|
|
@@ -25,6 +25,55 @@ export function parseFrontmatterFromContent(content) {
|
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
function validationCriteria(planContent) {
|
|
29
|
+
const heading = /^## Validation Criteria[ \t]*$/m.exec(planContent);
|
|
30
|
+
if (!heading)
|
|
31
|
+
return null;
|
|
32
|
+
const bodyStart = heading.index + heading[0].length;
|
|
33
|
+
const remaining = planContent.slice(bodyStart);
|
|
34
|
+
const nextHeading = /^##\s+/m.exec(remaining);
|
|
35
|
+
const body = remaining.slice(0, nextHeading?.index ?? remaining.length);
|
|
36
|
+
return body
|
|
37
|
+
.split('\n')
|
|
38
|
+
.filter((line) => /^\s*-\s+/.test(line))
|
|
39
|
+
.map((line) => line.replace(/^\s*-\s+(?:\[[ xX]\]\s*)?/, '').trim());
|
|
40
|
+
}
|
|
41
|
+
export function validateLitePlan(planContent, workflowMode) {
|
|
42
|
+
if (workflowMode !== 'lite') {
|
|
43
|
+
return { ok: true };
|
|
44
|
+
}
|
|
45
|
+
const phaseCount = planContent.match(/^## Phase\b.*$/gm)?.length ?? 0;
|
|
46
|
+
if (phaseCount !== 1) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
code: 'lite-multi-phase',
|
|
50
|
+
message: `Lite plans must contain exactly one phase; found ${phaseCount}.`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const frontmatter = parseFrontmatterFromContent(planContent);
|
|
54
|
+
const groups = frontmatter.kind === 'invalid'
|
|
55
|
+
? undefined
|
|
56
|
+
: frontmatter.data['oat_plan_parallel_groups'];
|
|
57
|
+
if (groups !== undefined &&
|
|
58
|
+
groups !== null &&
|
|
59
|
+
!(Array.isArray(groups) && groups.length === 0)) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
code: 'lite-parallel-groups',
|
|
63
|
+
message: 'Lite plans must declare no parallel groups.',
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const criteria = validationCriteria(planContent);
|
|
67
|
+
const invalidCriterion = criteria?.find((criterion) => !criterion.startsWith('manual:') && !/`[^`\n]+`/.test(criterion));
|
|
68
|
+
if (!criteria || criteria.length === 0 || invalidCriterion !== undefined) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
code: 'lite-criterion-without-command',
|
|
72
|
+
message: 'Every lite Validation Criteria bullet must name a command in backticks or start with manual: after the bullet marker.',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { ok: true };
|
|
76
|
+
}
|
|
28
77
|
export function validateParallelGroups(groups, phaseIds) {
|
|
29
78
|
// Missing or empty → valid (sequential execution, no parallelism declared)
|
|
30
79
|
if (groups === undefined || groups === null) {
|
|
@@ -24,7 +24,7 @@ export declare const SKILL_GATE_OVERRIDE_SOURCE = "state.md:oat_skill_gate_overr
|
|
|
24
24
|
* `skills.test.ts` pins this constant against the live `oat_gateable: true`
|
|
25
25
|
* declarations so the two cannot drift apart.
|
|
26
26
|
*/
|
|
27
|
-
export declare const GATE_AWARE_SKILLS: readonly ['oat-project-implement', 'oat-project-import-plan', 'oat-project-plan', 'oat-project-quick-start'];
|
|
27
|
+
export declare const GATE_AWARE_SKILLS: readonly ['oat-project-implement', 'oat-project-import-plan', 'oat-project-lite', 'oat-project-plan', 'oat-project-quick-start'];
|
|
28
28
|
export type SkillGateOverrideValue = typeof SKILL_GATE_OVERRIDE_DISABLED;
|
|
29
29
|
export interface ProjectSkillGateOverrides {
|
|
30
30
|
/** False when the key is absent entirely: absence means follow configuration. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/frontmatter.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/frontmatter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAU9E,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAU9E,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAkHD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvE;AAgiBD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAkF9B"}
|
|
@@ -88,6 +88,8 @@ async function readProjectState(repoRoot, projectPath) {
|
|
|
88
88
|
const stateFile = join(repoRoot, projectPath, 'state.md');
|
|
89
89
|
const phase = (await parseFrontmatterField(stateFile, 'oat_phase')) || 'unknown';
|
|
90
90
|
const phaseStatus = (await parseFrontmatterField(stateFile, 'oat_phase_status')) || 'unknown';
|
|
91
|
+
const readyForRaw = await parseFrontmatterField(stateFile, 'oat_ready_for');
|
|
92
|
+
const readyFor = readyForRaw && readyForRaw !== 'null' ? readyForRaw : '';
|
|
91
93
|
let currentTask = await parseFrontmatterField(stateFile, 'oat_current_task');
|
|
92
94
|
if (!currentTask || currentTask === 'null')
|
|
93
95
|
currentTask = '-';
|
|
@@ -115,6 +117,7 @@ async function readProjectState(repoRoot, projectPath) {
|
|
|
115
117
|
return {
|
|
116
118
|
phase,
|
|
117
119
|
phaseStatus,
|
|
120
|
+
readyFor,
|
|
118
121
|
currentTask,
|
|
119
122
|
lifecycle,
|
|
120
123
|
pauseTimestamp,
|
|
@@ -230,6 +233,15 @@ function computeNextStep(project, hasProjects, state) {
|
|
|
230
233
|
reason: `Complete ${state.phase} HiLL approval before advancing`,
|
|
231
234
|
};
|
|
232
235
|
}
|
|
236
|
+
if (state.workflowMode === 'quick' &&
|
|
237
|
+
state.phase === 'discovery' &&
|
|
238
|
+
state.phaseStatus === 'complete' &&
|
|
239
|
+
state.readyFor === 'oat-project-quick-start') {
|
|
240
|
+
return {
|
|
241
|
+
step: 'oat-project-quick-start',
|
|
242
|
+
reason: 'Continue the promoted quick workflow',
|
|
243
|
+
};
|
|
244
|
+
}
|
|
233
245
|
// Workflow mode routing
|
|
234
246
|
const key = `${state.workflowMode}:${state.phase}:${state.phaseStatus}`;
|
|
235
247
|
const routeMap = {
|
|
@@ -271,6 +283,10 @@ function computeNextStep(project, hasProjects, state) {
|
|
|
271
283
|
step: 'oat-project-import-plan',
|
|
272
284
|
reason: 'Continue import-plan normalization',
|
|
273
285
|
},
|
|
286
|
+
'lite:plan:in_progress': {
|
|
287
|
+
step: 'oat-project-lite',
|
|
288
|
+
reason: 'Continue lite planning',
|
|
289
|
+
},
|
|
274
290
|
};
|
|
275
291
|
if (routeMap[key])
|
|
276
292
|
return routeMap[key];
|
|
@@ -298,6 +314,12 @@ function computeNextStep(project, hasProjects, state) {
|
|
|
298
314
|
return sharedMap[sharedKey];
|
|
299
315
|
// Implementation complete — recommend docs sync if not yet done, then PR
|
|
300
316
|
if (state.phase === 'implement' && state.phaseStatus === 'complete') {
|
|
317
|
+
if (state.workflowMode === 'lite') {
|
|
318
|
+
return {
|
|
319
|
+
step: 'oat-project-pr-final',
|
|
320
|
+
reason: 'Generate final PR description from plan and implementation',
|
|
321
|
+
};
|
|
322
|
+
}
|
|
301
323
|
if (!state.docsUpdated || state.docsUpdated === '') {
|
|
302
324
|
return {
|
|
303
325
|
step: 'oat-project-document',
|
|
@@ -469,6 +491,7 @@ function buildDashboardMarkdown(project, state, knowledge, staleness, nextStep,
|
|
|
469
491
|
lines.push('- `oat-project-new` - Create a spec-driven project');
|
|
470
492
|
lines.push('- `oat-project-quick-start` - Create a quick workflow project');
|
|
471
493
|
lines.push('- `oat-project-import-plan` - Import an external provider plan');
|
|
494
|
+
lines.push('- `oat-project-lite` - Create a lite workflow project');
|
|
472
495
|
lines.push('- `oat project open <name>` - Open or resume a project');
|
|
473
496
|
lines.push('- `oat project pause [name]` - Pause active or named project');
|
|
474
497
|
lines.push('- `oat-project-complete` - Mark project complete');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pack-manifest.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/shared/pack-manifest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,EACd,QAAQ,EACT,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,GACf,CAAC;AAEF,eAAO,MAAM,UAAU,4GASiB,CAAC;AAEzC,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;
|
|
1
|
+
{"version":3,"file":"pack-manifest.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/shared/pack-manifest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,EACd,QAAQ,EACT,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,kBAAkB,EAClB,wBAAwB,EACxB,cAAc,GACf,CAAC;AAEF,eAAO,MAAM,UAAU,4GASiB,CAAC;AAEzC,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,CAE3D;AAmID,eAAO,MAAM,aAAa,EAAE,SAAS,cAAc,EAyLlD,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,CAMhE;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,QAAQ,EACd,IAAI,CAAC,EAAE,aAAa,GACnB,SAAS,mBAAmB,EAAE,CAGhC;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAC9C,MAAM,EAAE,CAQV;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,CAIlE;AAoJD,wBAAgB,oBAAoB,CAClC,QAAQ,GAAE,SAAS,cAAc,EAAkB,GAClD,IAAI,CAiEN"}
|
|
@@ -86,6 +86,7 @@ const WORKFLOW_SKILL_NAMES = [
|
|
|
86
86
|
'oat-project-document',
|
|
87
87
|
'oat-project-implement',
|
|
88
88
|
'oat-project-import-plan',
|
|
89
|
+
'oat-project-lite',
|
|
89
90
|
'oat-project-new',
|
|
90
91
|
'oat-project-next',
|
|
91
92
|
'oat-project-open',
|
|
@@ -189,6 +190,7 @@ export const PACK_MANIFEST = [
|
|
|
189
190
|
'spec.md',
|
|
190
191
|
'design.md',
|
|
191
192
|
'plan.md',
|
|
193
|
+
'plan-lite.md',
|
|
192
194
|
'implementation.md',
|
|
193
195
|
'summary.md',
|
|
194
196
|
'project-log.md',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-agent-toolkit/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.62",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Open Agent Toolkit CLI",
|
|
6
6
|
"homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"ora": "^9.0.0",
|
|
35
35
|
"yaml": "2.8.2",
|
|
36
36
|
"zod": "^3.25.76",
|
|
37
|
-
"@open-agent-toolkit/control-plane": "0.2.
|
|
37
|
+
"@open-agent-toolkit/control-plane": "0.2.62"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.0",
|