@open-agent-toolkit/cli 0.0.32 → 0.0.33
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 +2 -0
- package/assets/docs/reference/cli-reference.md +1 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-complete/SKILL.md +8 -30
- package/dist/commands/project/complete-state/index.d.ts +16 -0
- package/dist/commands/project/complete-state/index.d.ts.map +1 -0
- package/dist/commands/project/complete-state/index.js +78 -0
- package/dist/commands/project/complete-state/state-utils.d.ts +7 -0
- package/dist/commands/project/complete-state/state-utils.d.ts.map +1 -0
- package/dist/commands/project/complete-state/state-utils.js +72 -0
- package/dist/commands/project/index.d.ts.map +1 -1
- package/dist/commands/project/index.js +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ Additional useful entry points:
|
|
|
34
34
|
- `oat config dump --json`
|
|
35
35
|
- `oat project status --json`
|
|
36
36
|
- `oat project list --json`
|
|
37
|
+
- `oat project complete-state /path/to/project`
|
|
37
38
|
- `oat project archive sync`
|
|
38
39
|
- `oat doctor`
|
|
39
40
|
|
|
@@ -44,6 +45,7 @@ Use these commands when you want structured runtime/project state out of the CLI
|
|
|
44
45
|
- `oat config dump --json` - emit merged OAT config with per-key source attribution
|
|
45
46
|
- `oat project status --json` - emit the active project's full parsed control-plane state
|
|
46
47
|
- `oat project list --json` - emit summary state for tracked projects under the configured projects root
|
|
48
|
+
- `oat project complete-state <project-path>` - emit the canonical completed lifecycle shape into a tracked project's `state.md`
|
|
47
49
|
|
|
48
50
|
## Requirements
|
|
49
51
|
|
|
@@ -39,6 +39,7 @@ Notable inspection commands introduced in the current CLI surface:
|
|
|
39
39
|
- `oat config dump --json` - merged config with source attribution
|
|
40
40
|
- `oat project status --json` - full parsed state for the active tracked project
|
|
41
41
|
- `oat project list --json` - summary state for tracked projects under the configured projects root
|
|
42
|
+
- `oat project complete-state <project-path>` - apply the canonical completed-state mutation to a project's `state.md`; used by `oat-project-complete` during lifecycle closeout
|
|
42
43
|
|
|
43
44
|
## `oat config` surface flags
|
|
44
45
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-project-complete
|
|
3
|
-
version: 1.4.
|
|
3
|
+
version: 1.4.1
|
|
4
4
|
description: Use when all implementation work is finished and the project is ready to close. Marks the OAT project lifecycle as complete.
|
|
5
5
|
disable-model-invocation: true
|
|
6
6
|
user-invocable: true
|
|
@@ -254,41 +254,19 @@ Rules:
|
|
|
254
254
|
|
|
255
255
|
### Step 5: Set Lifecycle Complete
|
|
256
256
|
|
|
257
|
-
|
|
257
|
+
Delegate the canonical `state.md` completion mutation to the CLI:
|
|
258
258
|
|
|
259
259
|
```bash
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
# Check if oat_lifecycle already exists
|
|
264
|
-
if grep -q "^oat_lifecycle:" "$STATE_FILE"; then
|
|
265
|
-
# Update existing (portable approach using temp file)
|
|
266
|
-
sed 's/^oat_lifecycle:.*/oat_lifecycle: complete/' "$STATE_FILE" > "$STATE_FILE.tmp"
|
|
267
|
-
mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
268
|
-
else
|
|
269
|
-
# Add after oat_phase_status line using awk (more portable for multi-line inserts)
|
|
270
|
-
awk '/^oat_phase_status:/ {print; print "oat_lifecycle: complete"; next} 1' "$STATE_FILE" > "$STATE_FILE.tmp"
|
|
271
|
-
mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
260
|
+
COMPLETE_STATE_ARGS=("$PROJECT_PATH")
|
|
261
|
+
if [[ "$SHOULD_ARCHIVE" == "true" && "$IS_SHARED_PROJECT" == "true" ]]; then
|
|
262
|
+
COMPLETE_STATE_ARGS+=("--archived")
|
|
272
263
|
fi
|
|
273
264
|
|
|
274
|
-
|
|
275
|
-
sed -E "s/^oat_project_completed:.*/oat_project_completed: \"$NOW_UTC\"/" "$STATE_FILE" > "$STATE_FILE.tmp"
|
|
276
|
-
mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
277
|
-
sed -E "s/^oat_project_state_updated:.*/oat_project_state_updated: \"$NOW_UTC\"/" "$STATE_FILE" > "$STATE_FILE.tmp"
|
|
278
|
-
mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
265
|
+
oat project complete-state "${COMPLETE_STATE_ARGS[@]}"
|
|
279
266
|
```
|
|
280
267
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
- Set `**Status:** Complete`
|
|
284
|
-
- Set `**Last Updated:**` to the completion date in `YYYY-MM-DD`
|
|
285
|
-
- In `## Current Phase`, replace the body with:
|
|
286
|
-
- `Lifecycle complete; archived locally` when the project is archived in Step 8
|
|
287
|
-
- `Lifecycle complete` when the project is completed without archive
|
|
288
|
-
- In `## Progress`, preserve the existing completed workflow/review bullets and add `- ✓ Project lifecycle complete` if it is not already present
|
|
289
|
-
- In `## Next Milestone`, replace the body with `None. Project complete.`
|
|
290
|
-
|
|
291
|
-
Do not infer these body mutations from other archived projects. Apply them directly as part of this skill.
|
|
268
|
+
The CLI command owns both the frontmatter completion fields and the canonical markdown body updates for `state.md`.
|
|
269
|
+
It must set `oat_lifecycle: complete`, completion timestamps, `**Status:** Complete`, `**Last Updated:**`, the canonical `## Current Phase` body, normalized `## Progress`, and `## Next Milestone`.
|
|
292
270
|
|
|
293
271
|
### Step 6: Clear Active Project Pointer
|
|
294
272
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { readFile as defaultReadFile, writeFile as defaultWriteFile } from 'node:fs/promises';
|
|
2
|
+
import { buildCommandContext, type CommandContext } from '../../../app/command-context.js';
|
|
3
|
+
import { dirExists, fileExists } from '../../../fs/io.js';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
interface ProjectCompleteStateDependencies {
|
|
6
|
+
buildCommandContext: (options: Parameters<typeof buildCommandContext>[0]) => CommandContext;
|
|
7
|
+
resolveProjectRoot: (cwd: string) => Promise<string>;
|
|
8
|
+
readFile: typeof defaultReadFile;
|
|
9
|
+
writeFile: typeof defaultWriteFile;
|
|
10
|
+
dirExists: typeof dirExists;
|
|
11
|
+
fileExists: typeof fileExists;
|
|
12
|
+
now: () => Date;
|
|
13
|
+
}
|
|
14
|
+
export declare function createProjectCompleteStateCommand(overrides?: Partial<ProjectCompleteStateDependencies>): Command;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/complete-state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,UAAU,gCAAgC;IACxC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAsED,wBAAgB,iCAAiC,CAC/C,SAAS,GAAE,OAAO,CAAC,gCAAgC,CAAM,GACxD,OAAO,CA2BT"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { readFile as defaultReadFile, writeFile as defaultWriteFile, } from 'node:fs/promises';
|
|
2
|
+
import { isAbsolute, join } from 'node:path';
|
|
3
|
+
import { buildCommandContext } from '../../../app/command-context.js';
|
|
4
|
+
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
5
|
+
import { CliError } from '../../../errors/cli-error.js';
|
|
6
|
+
import { dirExists, fileExists } from '../../../fs/io.js';
|
|
7
|
+
import { resolveProjectRoot } from '../../../fs/paths.js';
|
|
8
|
+
import { Command } from 'commander';
|
|
9
|
+
import { renderCompletedProjectState } from './state-utils.js';
|
|
10
|
+
const DEFAULT_DEPENDENCIES = {
|
|
11
|
+
buildCommandContext,
|
|
12
|
+
resolveProjectRoot,
|
|
13
|
+
readFile: defaultReadFile,
|
|
14
|
+
writeFile: defaultWriteFile,
|
|
15
|
+
dirExists,
|
|
16
|
+
fileExists,
|
|
17
|
+
now: () => new Date(),
|
|
18
|
+
};
|
|
19
|
+
function resolveTargetProjectPath(repoRoot, projectPath) {
|
|
20
|
+
return isAbsolute(projectPath) ? projectPath : join(repoRoot, projectPath);
|
|
21
|
+
}
|
|
22
|
+
async function runProjectCompleteState(projectPath, options, context, dependencies) {
|
|
23
|
+
try {
|
|
24
|
+
const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
25
|
+
const targetProjectPath = resolveTargetProjectPath(repoRoot, projectPath);
|
|
26
|
+
if (!(await dependencies.dirExists(targetProjectPath))) {
|
|
27
|
+
throw new CliError(`Project not found: ${projectPath}`, 1);
|
|
28
|
+
}
|
|
29
|
+
const statePath = join(targetProjectPath, 'state.md');
|
|
30
|
+
if (!(await dependencies.fileExists(statePath))) {
|
|
31
|
+
throw new CliError(`Project state.md not found: ${statePath}`, 1);
|
|
32
|
+
}
|
|
33
|
+
const now = dependencies.now();
|
|
34
|
+
const content = await dependencies.readFile(statePath, 'utf8');
|
|
35
|
+
const updatedContent = renderCompletedProjectState(content, {
|
|
36
|
+
archived: options.archived ?? false,
|
|
37
|
+
nowUtc: now.toISOString(),
|
|
38
|
+
today: now.toISOString().slice(0, 10),
|
|
39
|
+
});
|
|
40
|
+
await dependencies.writeFile(statePath, updatedContent, 'utf8');
|
|
41
|
+
if (context.json) {
|
|
42
|
+
context.logger.json({
|
|
43
|
+
status: 'ok',
|
|
44
|
+
projectPath,
|
|
45
|
+
statePath,
|
|
46
|
+
archived: options.archived ?? false,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
context.logger.info(`Updated completed project state: ${projectPath}`);
|
|
51
|
+
}
|
|
52
|
+
process.exitCode = 0;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
56
|
+
if (context.json) {
|
|
57
|
+
context.logger.json({ status: 'error', message });
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
context.logger.error(message);
|
|
61
|
+
}
|
|
62
|
+
process.exitCode = error instanceof CliError ? error.exitCode : 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function createProjectCompleteStateCommand(overrides = {}) {
|
|
66
|
+
const dependencies = {
|
|
67
|
+
...DEFAULT_DEPENDENCIES,
|
|
68
|
+
...overrides,
|
|
69
|
+
};
|
|
70
|
+
return new Command('complete-state')
|
|
71
|
+
.description('Update a project state.md to the completed lifecycle shape')
|
|
72
|
+
.argument('<project-path>', 'Project path to update')
|
|
73
|
+
.option('--archived', 'Mark the completed project as archived locally')
|
|
74
|
+
.action(async (projectPath, options, command) => {
|
|
75
|
+
const context = dependencies.buildCommandContext(readGlobalOptions(command));
|
|
76
|
+
await runProjectCompleteState(projectPath, options, context, dependencies);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface CompleteProjectStateOptions {
|
|
2
|
+
archived: boolean;
|
|
3
|
+
nowUtc: string;
|
|
4
|
+
today: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function renderCompletedProjectState(content: string, options: CompleteProjectStateOptions): string;
|
|
7
|
+
//# sourceMappingURL=state-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-utils.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/complete-state/state-utils.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAoED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,2BAA2B,GACnC,MAAM,CA8DR"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { getFrontmatterBlock, getFrontmatterField, } from '../../shared/frontmatter.js';
|
|
2
|
+
import { replaceFrontmatter, upsertFrontmatterField, } from '../../shared/frontmatter-write.js';
|
|
3
|
+
function replaceLine(content, pattern, nextLine) {
|
|
4
|
+
return pattern.test(content) ? content.replace(pattern, nextLine) : content;
|
|
5
|
+
}
|
|
6
|
+
function findSectionBounds(content, heading) {
|
|
7
|
+
const marker = `## ${heading}\n\n`;
|
|
8
|
+
const start = content.indexOf(marker);
|
|
9
|
+
if (start === -1) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const bodyStart = start + marker.length;
|
|
13
|
+
const nextHeading = content.indexOf('\n## ', bodyStart);
|
|
14
|
+
return {
|
|
15
|
+
start,
|
|
16
|
+
bodyStart,
|
|
17
|
+
end: nextHeading === -1 ? content.length : nextHeading,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function readSectionBody(content, heading) {
|
|
21
|
+
const bounds = findSectionBounds(content, heading);
|
|
22
|
+
if (!bounds) {
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
return content.slice(bounds.bodyStart, bounds.end).trim();
|
|
26
|
+
}
|
|
27
|
+
function replaceSection(content, heading, body) {
|
|
28
|
+
const bounds = findSectionBounds(content, heading);
|
|
29
|
+
if (!bounds) {
|
|
30
|
+
return content;
|
|
31
|
+
}
|
|
32
|
+
return [
|
|
33
|
+
content.slice(0, bounds.start),
|
|
34
|
+
`## ${heading}\n\n${body.trim()}\n`,
|
|
35
|
+
content.slice(bounds.end),
|
|
36
|
+
].join('');
|
|
37
|
+
}
|
|
38
|
+
function renderCompletedProgress(content) {
|
|
39
|
+
const existingLines = readSectionBody(content, 'Progress')
|
|
40
|
+
.split('\n')
|
|
41
|
+
.map((line) => line.trim())
|
|
42
|
+
.filter((line) => line.length > 0 && line.startsWith('- ✓'));
|
|
43
|
+
if (!existingLines.includes('- ✓ Project lifecycle complete')) {
|
|
44
|
+
existingLines.push('- ✓ Project lifecycle complete');
|
|
45
|
+
}
|
|
46
|
+
return existingLines.join('\n');
|
|
47
|
+
}
|
|
48
|
+
export function renderCompletedProjectState(content, options) {
|
|
49
|
+
const frontmatter = getFrontmatterBlock(content);
|
|
50
|
+
if (!frontmatter) {
|
|
51
|
+
throw new Error('state.md is missing frontmatter');
|
|
52
|
+
}
|
|
53
|
+
let nextBlock = upsertFrontmatterField(frontmatter, 'oat_lifecycle', 'complete', true).nextBlock;
|
|
54
|
+
nextBlock = upsertFrontmatterField(nextBlock, 'oat_project_completed', `"${options.nowUtc}"`, true).nextBlock;
|
|
55
|
+
nextBlock = upsertFrontmatterField(nextBlock, 'oat_project_state_updated', `"${options.nowUtc}"`, true).nextBlock;
|
|
56
|
+
let nextContent = nextBlock === frontmatter
|
|
57
|
+
? content
|
|
58
|
+
: replaceFrontmatter(content, nextBlock);
|
|
59
|
+
nextContent = replaceLine(nextContent, /^\*\*Status:\*\*.*$/m, '**Status:** Complete');
|
|
60
|
+
nextContent = replaceLine(nextContent, /^\*\*Last Updated:\*\*.*$/m, `**Last Updated:** ${options.today}`);
|
|
61
|
+
const currentPhase = options.archived
|
|
62
|
+
? 'Lifecycle complete; archived locally'
|
|
63
|
+
: 'Lifecycle complete';
|
|
64
|
+
nextContent = replaceSection(nextContent, 'Current Phase', currentPhase);
|
|
65
|
+
nextContent = replaceSection(nextContent, 'Progress', renderCompletedProgress(nextContent));
|
|
66
|
+
nextContent = replaceSection(nextContent, 'Next Milestone', 'None. Project complete.');
|
|
67
|
+
const currentLifecycle = getFrontmatterField(nextBlock, 'oat_lifecycle');
|
|
68
|
+
if (currentLifecycle !== 'complete') {
|
|
69
|
+
throw new Error('Failed to set oat_lifecycle: complete');
|
|
70
|
+
}
|
|
71
|
+
return nextContent;
|
|
72
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,oBAAoB,IAAI,OAAO,CAW9C"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { createProjectArchiveCommand } from './archive/index.js';
|
|
3
|
+
import { createProjectCompleteStateCommand } from './complete-state/index.js';
|
|
3
4
|
import { createProjectListCommand } from './list.js';
|
|
4
5
|
import { createProjectNewCommand } from './new/index.js';
|
|
5
6
|
import { createProjectOpenCommand } from './open/index.js';
|
|
@@ -10,6 +11,7 @@ export function createProjectCommand() {
|
|
|
10
11
|
return new Command('project')
|
|
11
12
|
.description('Manage OAT project workflows')
|
|
12
13
|
.addCommand(createProjectArchiveCommand())
|
|
14
|
+
.addCommand(createProjectCompleteStateCommand())
|
|
13
15
|
.addCommand(createProjectListCommand())
|
|
14
16
|
.addCommand(createProjectNewCommand())
|
|
15
17
|
.addCommand(createProjectOpenCommand())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-agent-toolkit/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
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",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"ora": "^9.0.0",
|
|
34
34
|
"yaml": "2.8.2",
|
|
35
35
|
"zod": "^3.25.76",
|
|
36
|
-
"@open-agent-toolkit/control-plane": "0.0.
|
|
36
|
+
"@open-agent-toolkit/control-plane": "0.0.33"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^22.10.0",
|