@open-agent-toolkit/cli 0.2.50 → 0.2.51
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/bundle-metadata.json +1 -1
- package/assets/docs/reference/cli-reference.md +20 -5
- package/assets/docs/reference/file-locations.md +8 -2
- package/assets/docs/reference/oat-directory-structure.md +14 -5
- package/assets/docs/workflows/projects/artifacts.md +9 -4
- package/assets/docs/workflows/projects/lifecycle.md +17 -5
- package/assets/docs/workflows/projects/picking-up-projects.md +26 -9
- package/assets/docs/workflows/projects/pr-flow.md +5 -3
- package/assets/docs/workflows/projects/reviewing-oat-prs.md +12 -8
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +2 -2
- package/assets/skills/oat-project-complete/SKILL.md +193 -26
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +306 -0
- package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +125 -0
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +106 -0
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +110 -0
- package/assets/skills/oat-project-complete/scripts/resolve-synced-archive-entry.mjs +164 -0
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +2 -2
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +2 -2
- package/dist/commands/project/archive/archive-utils.d.ts +8 -1
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +147 -50
- package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
- package/dist/commands/project/archive/push-runner.js +15 -2
- package/dist/commands/project/links/compute.d.ts +1 -0
- package/dist/commands/project/links/compute.d.ts.map +1 -1
- package/dist/commands/project/links/compute.js +5 -4
- package/dist/commands/project/links/index.d.ts +9 -0
- package/dist/commands/project/links/index.d.ts.map +1 -1
- package/dist/commands/project/links/index.js +71 -2
- package/dist/commands/project/list.d.ts +10 -2
- package/dist/commands/project/list.d.ts.map +1 -1
- package/dist/commands/project/list.js +205 -29
- package/dist/commands/project/open/index.d.ts +2 -0
- package/dist/commands/project/open/index.d.ts.map +1 -1
- package/dist/commands/project/open/index.js +16 -1
- package/dist/commands/project/prune/index.d.ts +10 -4
- package/dist/commands/project/prune/index.d.ts.map +1 -1
- package/dist/commands/project/prune/index.js +85 -15
- package/dist/commands/project/pull/index.d.ts +4 -0
- package/dist/commands/project/pull/index.d.ts.map +1 -1
- package/dist/commands/project/pull/index.js +82 -3
- package/dist/commands/project/sync/git.d.ts +9 -0
- package/dist/commands/project/sync/git.d.ts.map +1 -1
- package/dist/commands/project/sync/git.js +18 -0
- package/dist/commands/project/sync/ref-sync.d.ts +16 -1
- package/dist/commands/project/sync/ref-sync.d.ts.map +1 -1
- package/dist/commands/project/sync/ref-sync.js +288 -17
- package/dist/commands/project/sync/resolve-target.d.ts +10 -0
- package/dist/commands/project/sync/resolve-target.d.ts.map +1 -1
- package/dist/commands/project/sync/resolve-target.js +56 -1
- package/dist/commands/shared/project-scope.d.ts +2 -0
- package/dist/commands/shared/project-scope.d.ts.map +1 -1
- package/dist/commands/shared/project-scope.js +5 -0
- package/dist/commands/state/generate.d.ts +2 -0
- package/dist/commands/state/generate.d.ts.map +1 -1
- package/dist/commands/state/generate.js +93 -28
- package/package.json +2 -2
|
@@ -2,10 +2,65 @@ import { realpath, stat } from 'node:fs/promises';
|
|
|
2
2
|
import { isAbsolute, relative, resolve, sep } from 'node:path';
|
|
3
3
|
import { defaultGitRunner } from '../../project/sync/git.js';
|
|
4
4
|
import { resolveProjectsRoot } from '../../shared/oat-paths.js';
|
|
5
|
-
import { assertValidProjectSlug, resolveProjectScope, resolveScopeRoot, syncedRecordPath, } from '../../shared/project-scope.js';
|
|
5
|
+
import { assertValidProjectSlug, completedSyncedRefName, resolveProjectScope, resolveScopeRoot, syncedRecordPath, } from '../../shared/project-scope.js';
|
|
6
6
|
import { readOatLocalConfig } from '../../../config/oat-config.js';
|
|
7
7
|
import { CliError } from '../../../errors/cli-error.js';
|
|
8
8
|
import { assertCanonicalSyncTargetIdentity, buildSyncTarget, classifyRemoteRefLookup, classifyAdoptionRecord, } from './ref-sync.js';
|
|
9
|
+
async function remoteTerminalRefShas(target, activeRef, completedRef, git) {
|
|
10
|
+
const result = await git.run(['ls-remote', '--exit-code', target.remote, activeRef, completedRef], { cwd: target.repoRoot, allowFailure: true });
|
|
11
|
+
if (classifyRemoteRefLookup(result, target.remote, `${activeRef} and ${completedRef}`) === 'absent') {
|
|
12
|
+
return { activeSha: null, completedSha: null };
|
|
13
|
+
}
|
|
14
|
+
const requestedRefs = new Set([activeRef, completedRef]);
|
|
15
|
+
const advertised = new Map();
|
|
16
|
+
const rows = result.stdout.split('\n').filter((row) => row.length > 0);
|
|
17
|
+
if (rows.length === 0) {
|
|
18
|
+
throw new CliError(`Unable to verify terminal refs for ${target.slug}: git ls-remote returned a malformed empty advertisement.`, 2);
|
|
19
|
+
}
|
|
20
|
+
for (const row of rows) {
|
|
21
|
+
const fields = row.trim().split(/\s+/);
|
|
22
|
+
if (fields.length !== 2 ||
|
|
23
|
+
!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i.test(fields[0] ?? '')) {
|
|
24
|
+
throw new CliError(`Unable to verify terminal refs for ${target.slug}: git ls-remote returned a malformed row.`, 2);
|
|
25
|
+
}
|
|
26
|
+
const [sha, ref] = fields;
|
|
27
|
+
if (!requestedRefs.has(ref)) {
|
|
28
|
+
throw new CliError(`Unable to verify terminal refs for ${target.slug}: git ls-remote returned unexpected ref ${ref}.`, 2);
|
|
29
|
+
}
|
|
30
|
+
if (advertised.has(ref)) {
|
|
31
|
+
throw new CliError(`Unable to verify terminal refs for ${target.slug}: git ls-remote returned duplicate rows for ${ref}.`, 2);
|
|
32
|
+
}
|
|
33
|
+
advertised.set(ref, sha);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
activeSha: advertised.get(activeRef) ?? null,
|
|
37
|
+
completedSha: advertised.get(completedRef) ?? null,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export async function probeSyncedTerminalRefs(target, expectedSha, git) {
|
|
41
|
+
const activeRef = target.ref;
|
|
42
|
+
const completedRef = completedSyncedRefName(target.slug);
|
|
43
|
+
const { activeSha, completedSha } = await remoteTerminalRefShas(target, activeRef, completedRef, git);
|
|
44
|
+
const wrongSha = (activeSha !== null && activeSha !== expectedSha) ||
|
|
45
|
+
(completedSha !== null && completedSha !== expectedSha);
|
|
46
|
+
const state = wrongSha
|
|
47
|
+
? 'wrong-sha'
|
|
48
|
+
: activeSha !== null && completedSha !== null
|
|
49
|
+
? 'both'
|
|
50
|
+
: activeSha !== null
|
|
51
|
+
? 'active-only'
|
|
52
|
+
: completedSha !== null
|
|
53
|
+
? 'completed-only'
|
|
54
|
+
: 'absent';
|
|
55
|
+
return {
|
|
56
|
+
state,
|
|
57
|
+
activeRef,
|
|
58
|
+
completedRef,
|
|
59
|
+
expectedSha,
|
|
60
|
+
activeSha,
|
|
61
|
+
completedSha,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
9
64
|
async function pathExists(path) {
|
|
10
65
|
try {
|
|
11
66
|
await stat(path);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type ProjectScope = 'shared' | 'local' | 'synced';
|
|
2
2
|
export declare const PROJECT_SCOPES: readonly ["shared", "local", "synced"];
|
|
3
3
|
export declare const SYNCED_REF_NAMESPACE = "refs/oat/projects";
|
|
4
|
+
export declare const COMPLETED_SYNCED_REF_NAMESPACE = "refs/oat/completed";
|
|
4
5
|
export declare const SYNCED_REMOTE = "origin";
|
|
5
6
|
export declare function canonicalizePath(path: string): string;
|
|
6
7
|
interface EffectiveConfigResult {
|
|
@@ -16,6 +17,7 @@ export declare function resolveScopeRoot(repoRoot: string, projectsRoot: string,
|
|
|
16
17
|
export declare function resolveProjectScope(projectPath: string, projectsRoot: string, repoRoot: string): ProjectScope | null;
|
|
17
18
|
export declare function assertValidProjectSlug(slug: string, exitCode?: 1 | 2): void;
|
|
18
19
|
export declare function syncedRefName(slug: string): string;
|
|
20
|
+
export declare function completedSyncedRefName(slug: string): string;
|
|
19
21
|
export declare function syncedRecordPath(scopeRoot: string, slug: string): string;
|
|
20
22
|
export declare function isSyncedCheckout(projectPath: string): Promise<boolean>;
|
|
21
23
|
export declare function resolveDefaultScope(repoRoot: string, env: NodeJS.ProcessEnv, dependencies?: Partial<ProjectScopeDependencies>): Promise<ProjectScope>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-scope.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/project-scope.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEzD,eAAO,MAAM,cAAc,wCAIiB,CAAC;AAC7C,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AACxD,eAAO,MAAM,aAAa,WAAW,CAAC;AAItC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBrD;AAED,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9C;AAED,UAAU,wBAAwB;IAChC,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACrC;AAMD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,CAKR;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,YAAY,GAClB,MAAM,CAOR;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,YAAY,GAAG,IAAI,CAmBrB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,CAAC,GAAG,CAAK,GAClB,IAAI,CAON;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa5E;AAeD,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,YAAY,GAAE,OAAO,CAAC,wBAAwB,CAAM,GACnD,OAAO,CAAC,YAAY,CAAC,CAqBvB"}
|
|
1
|
+
{"version":3,"file":"project-scope.d.ts","sourceRoot":"","sources":["../../../src/commands/shared/project-scope.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEzD,eAAO,MAAM,cAAc,wCAIiB,CAAC;AAC7C,eAAO,MAAM,oBAAoB,sBAAsB,CAAC;AACxD,eAAO,MAAM,8BAA8B,uBAAuB,CAAC;AACnE,eAAO,MAAM,aAAa,WAAW,CAAC;AAItC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoBrD;AAED,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9C;AAED,UAAU,wBAAwB;IAChC,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACrC;AAMD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,MAAM,CAKR;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,YAAY,GAClB,MAAM,CAOR;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GACf,YAAY,GAAG,IAAI,CAmBrB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,CAAC,GAAG,CAAK,GAClB,IAAI,CAON;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGlD;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED,wBAAsB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa5E;AAeD,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,EACtB,YAAY,GAAE,OAAO,CAAC,wBAAwB,CAAM,GACnD,OAAO,CAAC,YAAY,CAAC,CAqBvB"}
|
|
@@ -10,6 +10,7 @@ export const PROJECT_SCOPES = [
|
|
|
10
10
|
'synced',
|
|
11
11
|
];
|
|
12
12
|
export const SYNCED_REF_NAMESPACE = 'refs/oat/projects';
|
|
13
|
+
export const COMPLETED_SYNCED_REF_NAMESPACE = 'refs/oat/completed';
|
|
13
14
|
export const SYNCED_REMOTE = 'origin';
|
|
14
15
|
const PROJECT_SLUG_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
15
16
|
export function canonicalizePath(path) {
|
|
@@ -69,6 +70,10 @@ export function syncedRefName(slug) {
|
|
|
69
70
|
assertValidProjectSlug(slug);
|
|
70
71
|
return `${SYNCED_REF_NAMESPACE}/${slug}`;
|
|
71
72
|
}
|
|
73
|
+
export function completedSyncedRefName(slug) {
|
|
74
|
+
assertValidProjectSlug(slug);
|
|
75
|
+
return `${COMPLETED_SYNCED_REF_NAMESPACE}/${slug}`;
|
|
76
|
+
}
|
|
72
77
|
export function syncedRecordPath(scopeRoot, slug) {
|
|
73
78
|
assertValidProjectSlug(slug);
|
|
74
79
|
return join(scopeRoot, `${slug}.json`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type GitRunner } from '../project/sync/git.js';
|
|
1
2
|
export interface GitOperations {
|
|
2
3
|
isGitRepo(root: string): boolean;
|
|
3
4
|
diffFileCount(root: string, sha: string): number;
|
|
@@ -7,6 +8,7 @@ export interface GenerateStateOptions {
|
|
|
7
8
|
env?: NodeJS.ProcessEnv;
|
|
8
9
|
today?: string;
|
|
9
10
|
git?: GitOperations;
|
|
11
|
+
gitRunner?: GitRunner;
|
|
10
12
|
}
|
|
11
13
|
export interface GenerateStateResult {
|
|
12
14
|
dashboardPath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"
|
|
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;AAiHD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvE;AAsgBD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAkF9B"}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { execSync } from 'node:child_process';
|
|
2
2
|
import { readdir, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { basename, join } from 'node:path';
|
|
3
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
4
|
+
import { classifyLegacySyncedRecord, probeAuthoritativeCompletedRef, } from '../project/list.js';
|
|
5
|
+
import { defaultGitRunner } from '../project/sync/git.js';
|
|
6
|
+
import { listSyncedRecords } from '../project/sync/record.js';
|
|
7
|
+
import { buildSyncTarget } from '../project/sync/ref-sync.js';
|
|
8
|
+
import { probeSyncedTerminalRefs } from '../project/sync/resolve-target.js';
|
|
4
9
|
import { parseFrontmatterField } from '../shared/frontmatter.js';
|
|
5
10
|
import { resolveProjectsRoot } from '../shared/oat-paths.js';
|
|
11
|
+
import { resolveScopeRoot } from '../shared/project-scope.js';
|
|
6
12
|
import { readOatLocalConfig } from '../../config/oat-config.js';
|
|
7
13
|
import { ensureDir, fileExists } from '../../fs/io.js';
|
|
8
14
|
const defaultGit = {
|
|
@@ -310,22 +316,41 @@ function isTerminalCoordinationProject(kind, phase, phaseStatus) {
|
|
|
310
316
|
phase === 'decomposition' &&
|
|
311
317
|
phaseStatus === 'complete');
|
|
312
318
|
}
|
|
313
|
-
async function listAvailableProjects(repoRoot, projectsRoot) {
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
};
|
|
319
|
+
async function listAvailableProjects(repoRoot, projectsRoot, gitRunner) {
|
|
320
|
+
const lines = [];
|
|
321
|
+
const decompositions = [];
|
|
322
|
+
const materializedSynced = new Set();
|
|
323
|
+
for (const scope of ['shared', 'synced', 'local']) {
|
|
324
|
+
const scopeRoot = resolveScopeRoot(repoRoot, projectsRoot, scope);
|
|
325
|
+
let entries;
|
|
326
|
+
try {
|
|
327
|
+
entries = await readdir(scopeRoot, { withFileTypes: true });
|
|
323
328
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
329
|
+
catch {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
for (const dir of entries.filter((entry) => entry.isDirectory())) {
|
|
333
|
+
const stateFile = join(scopeRoot, dir.name, 'state.md');
|
|
328
334
|
if (await fileExists(stateFile)) {
|
|
335
|
+
if (scope === 'synced') {
|
|
336
|
+
materializedSynced.add(dir.name);
|
|
337
|
+
const target = buildSyncTarget(repoRoot, projectsRoot, dir.name);
|
|
338
|
+
const terminal = await probeAuthoritativeCompletedRef(target, gitRunner);
|
|
339
|
+
if (terminal) {
|
|
340
|
+
const row = classifyLegacySyncedRecord({
|
|
341
|
+
schemaVersion: 1,
|
|
342
|
+
slug: dir.name,
|
|
343
|
+
scope: 'synced',
|
|
344
|
+
ref: target.ref,
|
|
345
|
+
remote: 'origin',
|
|
346
|
+
status: 'active',
|
|
347
|
+
createdAt: new Date(0).toISOString(),
|
|
348
|
+
completedAt: null,
|
|
349
|
+
}, target.projectPath, terminal);
|
|
350
|
+
lines.push(`- **${dir.name}** - ${row.recommendation.reason}`);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
329
354
|
const phase = (await parseFrontmatterField(stateFile, 'oat_phase')) || 'unknown';
|
|
330
355
|
const phaseStatus = (await parseFrontmatterField(stateFile, 'oat_phase_status')) ||
|
|
331
356
|
'in_progress';
|
|
@@ -340,19 +365,29 @@ async function listAvailableProjects(repoRoot, projectsRoot) {
|
|
|
340
365
|
}
|
|
341
366
|
}
|
|
342
367
|
}
|
|
343
|
-
return {
|
|
344
|
-
active: lines.length > 0 ? lines.join('\n') : '*(No projects found)*',
|
|
345
|
-
decompositions: decompositions.length > 0
|
|
346
|
-
? decompositions.join('\n')
|
|
347
|
-
: '*(No decompositions found)*',
|
|
348
|
-
};
|
|
349
368
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
369
|
+
const syncedRoot = resolveScopeRoot(repoRoot, projectsRoot, 'synced');
|
|
370
|
+
const records = await listSyncedRecords(syncedRoot);
|
|
371
|
+
for (const record of records) {
|
|
372
|
+
if (materializedSynced.has(record.slug))
|
|
373
|
+
continue;
|
|
374
|
+
const target = buildSyncTarget(repoRoot, projectsRoot, record.slug);
|
|
375
|
+
const authoritative = await probeAuthoritativeCompletedRef(target, gitRunner);
|
|
376
|
+
const probe = authoritative ??
|
|
377
|
+
(record.archiveSourceRefSha
|
|
378
|
+
? await probeSyncedTerminalRefs(target, record.archiveSourceRefSha, gitRunner)
|
|
379
|
+
: undefined);
|
|
380
|
+
const row = classifyLegacySyncedRecord(record, target.projectPath, probe);
|
|
381
|
+
lines.push(row.kind === 'recorded-absent'
|
|
382
|
+
? `- **${record.slug}** - checkout absent; oat project pull ${record.slug}`
|
|
383
|
+
: `- **${record.slug}** - ${row.recommendation.reason}`);
|
|
355
384
|
}
|
|
385
|
+
return {
|
|
386
|
+
active: lines.length > 0 ? lines.join('\n') : '*(No projects found)*',
|
|
387
|
+
decompositions: decompositions.length > 0
|
|
388
|
+
? decompositions.join('\n')
|
|
389
|
+
: '*(No decompositions found)*',
|
|
390
|
+
};
|
|
356
391
|
}
|
|
357
392
|
function buildDashboardMarkdown(project, state, knowledge, staleness, nextStep, projectsList, generatedDate) {
|
|
358
393
|
const timestamp = `${generatedDate}`;
|
|
@@ -453,18 +488,48 @@ export async function generateStateDashboard(options) {
|
|
|
453
488
|
const env = options.env ?? process.env;
|
|
454
489
|
const today = options.today ?? new Date().toISOString().slice(0, 10);
|
|
455
490
|
const git = options.git ?? defaultGit;
|
|
491
|
+
const gitRunner = options.gitRunner ?? defaultGitRunner;
|
|
456
492
|
const dashboardPath = join(repoRoot, '.oat', 'state.md');
|
|
457
493
|
const projectsRoot = await resolveProjectsRoot(repoRoot, env);
|
|
458
494
|
const hasProjects = await hasAnyProjects(join(repoRoot, projectsRoot));
|
|
459
495
|
const project = await readActiveProject(repoRoot);
|
|
496
|
+
let terminalNextStep = null;
|
|
497
|
+
if (project.status === 'active' && project.path) {
|
|
498
|
+
const activePath = resolve(repoRoot, project.path);
|
|
499
|
+
const syncedRoot = resolveScopeRoot(repoRoot, projectsRoot, 'synced');
|
|
500
|
+
if (dirname(activePath) === syncedRoot) {
|
|
501
|
+
const target = buildSyncTarget(repoRoot, projectsRoot, project.name);
|
|
502
|
+
const terminal = await probeAuthoritativeCompletedRef(target, gitRunner);
|
|
503
|
+
if (terminal) {
|
|
504
|
+
const row = classifyLegacySyncedRecord({
|
|
505
|
+
schemaVersion: 1,
|
|
506
|
+
slug: project.name,
|
|
507
|
+
scope: 'synced',
|
|
508
|
+
ref: target.ref,
|
|
509
|
+
remote: 'origin',
|
|
510
|
+
status: 'active',
|
|
511
|
+
createdAt: new Date(0).toISOString(),
|
|
512
|
+
completedAt: null,
|
|
513
|
+
}, activePath, terminal);
|
|
514
|
+
project.status =
|
|
515
|
+
row.kind === 'terminal-invalid'
|
|
516
|
+
? 'terminal ref mismatch'
|
|
517
|
+
: 'terminal cleanup pending';
|
|
518
|
+
terminalNextStep = {
|
|
519
|
+
step: 'oat-project-complete',
|
|
520
|
+
reason: row.recommendation.reason,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
460
525
|
let state = null;
|
|
461
526
|
if (project.status === 'active') {
|
|
462
527
|
state = await readProjectState(repoRoot, project.path);
|
|
463
528
|
}
|
|
464
529
|
const knowledge = await readKnowledgeStatus(repoRoot);
|
|
465
530
|
const staleness = calculateStaleness(knowledge, git, repoRoot, today);
|
|
466
|
-
const nextStep = computeNextStep(project, hasProjects, state);
|
|
467
|
-
const projectsList = await listAvailableProjects(repoRoot, projectsRoot);
|
|
531
|
+
const nextStep = terminalNextStep ?? computeNextStep(project, hasProjects, state);
|
|
532
|
+
const projectsList = await listAvailableProjects(repoRoot, projectsRoot, gitRunner);
|
|
468
533
|
const markdown = buildDashboardMarkdown(project, state, knowledge, staleness, nextStep, projectsList, today);
|
|
469
534
|
await ensureDir(join(repoRoot, '.oat'));
|
|
470
535
|
await writeFile(dashboardPath, markdown, 'utf8');
|
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.51",
|
|
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.51"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.0",
|