@open-agent-toolkit/cli 0.1.14 → 0.1.16
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/public-package-versions.json +4 -4
- package/assets/skills/oat-project-split/SKILL.md +17 -2
- package/assets/skills/oat-review-provide/SKILL.md +2 -2
- package/assets/skills/oat-review-provide/scripts/resolve-review-output.sh +16 -6
- package/dist/commands/project/new/index.d.ts +1 -0
- package/dist/commands/project/new/index.d.ts.map +1 -1
- package/dist/commands/project/new/index.js +23 -0
- package/dist/commands/project/new/scaffold.d.ts +17 -0
- package/dist/commands/project/new/scaffold.d.ts.map +1 -1
- package/dist/commands/project/new/scaffold.js +74 -0
- package/dist/projects/split/seed-children.d.ts +1 -1
- package/dist/projects/split/seed-children.d.ts.map +1 -1
- package/dist/projects/split/seed-children.js +34 -3
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-project-split
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.1.0
|
|
4
4
|
description: Use when a discovery or brainstorm should split one broad scope into coordinated OAT child projects.
|
|
5
5
|
argument-hint: '--plan-file <path>'
|
|
6
6
|
disable-model-invocation: true
|
|
@@ -73,10 +73,25 @@ The run command marks the parent `oat_phase: decomposition` and `oat_phase_statu
|
|
|
73
73
|
|
|
74
74
|
If a previous split wrote a coordination parent but did not finish all children, the run command resumes from `references/split-plan.json`. Do not reconstruct missing child seed data from slugs alone.
|
|
75
75
|
|
|
76
|
+
## Children Resume At Discovery (Revalidation Contract)
|
|
77
|
+
|
|
78
|
+
A split seeds children with inherited context, but that context is only a **starting point**. By the time an agent resumes a child it may be stale, so children must never be treated as discovered, planned, or implementation-ready just because the split wrote their files.
|
|
79
|
+
|
|
80
|
+
What the run command guarantees, and what reviewers should expect:
|
|
81
|
+
|
|
82
|
+
- **Child `state.md` routes to discovery.** Each child is written with `oat_phase: discovery` and `oat_phase_status: in_progress`, plus preserved `oat_parent`, `oat_siblings`, and `oat_depends_on` links. Resume, progress, and quick-start all key off `state.md`, so they pick the child back up at discovery — not at plan or implementation.
|
|
83
|
+
- **Child `discovery.md` is in progress with an unmet revalidation gate.** It carries `oat_status: in_progress` and `oat_inherited_context_revalidated: false`, an explicit "Inherited Context Revalidation Gate" section, and language stating that parent-derived scope is a seed, not a final decision. The discovery-completion gate blocks marking discovery complete until `oat_inherited_context_revalidated: true`.
|
|
84
|
+
- **Child discovery is never marked complete during split generation.** Seeding only provides a provisional starting point.
|
|
85
|
+
- **Placeholder `plan.md` cannot be mistaken for a real plan.** The seeded `plan.md` is re-marked as a not-started template (`oat_template: true`, `oat_template_name: plan`). Routing decisions read `state.md` (`oat_phase: discovery`), and `oat-project-next` treats `oat_template: true` as a still-a-template signal, so the placeholder never reads as plan-ready.
|
|
86
|
+
|
|
87
|
+
A child sitting at `discovery` / `in_progress` after a split is **expected work-in-progress**, not stale bookkeeping. Do not flag it as drift or "skipped discovery" — revalidating inherited context is the next intended step.
|
|
88
|
+
|
|
76
89
|
## Success Criteria
|
|
77
90
|
|
|
78
91
|
- Coordination parent exists and has no `spec.md`, `design.md`, `plan.md`, or `implementation.md`.
|
|
79
92
|
- Parent `state.md` records `oat_kind: coordination`, ordered `oat_children`, and terminal decomposition status.
|
|
80
93
|
- `references/split-plan.json` contains the full `SplitPlanDocument`.
|
|
81
|
-
- Every child
|
|
94
|
+
- Every child `state.md` records `oat_phase: discovery` and `oat_phase_status: in_progress` so resume/progress/quick-start route it back to discovery.
|
|
95
|
+
- Every child has seeded discovery content, parent/sibling/dependency links, `oat_inherited_context_revalidated: false`, and an explicit revalidation gate; no child discovery is marked complete by the split.
|
|
96
|
+
- Every child `plan.md` is a not-started template placeholder (`oat_template: true`) that never reads as plan-ready.
|
|
82
97
|
- `.oat/config.local.json.activeProject` points at the repo-relative initial child path.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oat-review-provide
|
|
3
|
-
version: 1.2.
|
|
3
|
+
version: 1.2.1
|
|
4
4
|
description: Use when you need an ad-hoc review outside an active OAT project lifecycle. Reviews code or artifacts without project phase state, unlike oat-project-review-provide.
|
|
5
5
|
argument-hint: '[unstaged|staged|base_branch=<branch>|base_sha=<sha>|<sha1>..<sha2>|--files <path1,path2,...>] [--output <path>] [--mode auto|local|tracked|inline]'
|
|
6
6
|
disable-model-invocation: true
|
|
@@ -137,7 +137,7 @@ bash .agents/skills/oat-review-provide/scripts/resolve-review-output.sh --mode a
|
|
|
137
137
|
|
|
138
138
|
Policy:
|
|
139
139
|
|
|
140
|
-
- If `.oat/repo/reviews` exists and
|
|
140
|
+
- If `.oat/repo/reviews` exists and new review artifacts under it are not gitignored, assume user wants tracked active artifacts there.
|
|
141
141
|
- Otherwise default to active local `.oat/projects/local/orphan-reviews`.
|
|
142
142
|
- Do **not** write new review artifacts directly into any `archived/` directory; those are historical locations used after `oat-review-receive` processes a review.
|
|
143
143
|
- If user preference is unclear, ask and recommend local-only.
|
|
@@ -18,7 +18,7 @@ Policy:
|
|
|
18
18
|
- If --output is provided, use it directly.
|
|
19
19
|
- If mode=inline, no artifact file is written.
|
|
20
20
|
- In auto mode:
|
|
21
|
-
- If .oat/repo/reviews exists and
|
|
21
|
+
- If .oat/repo/reviews exists and new review artifacts under it are NOT gitignored, use it (tracked convention).
|
|
22
22
|
- Otherwise, use .oat/projects/local/orphan-reviews (local-only default).
|
|
23
23
|
USAGE
|
|
24
24
|
}
|
|
@@ -67,12 +67,22 @@ is_gitignored() {
|
|
|
67
67
|
fi
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
artifact_probe_path() {
|
|
71
|
+
local output_dir="$1"
|
|
72
|
+
printf '%s/%s\n' "${output_dir%/}" "ad-hoc-review-gitignore-probe.md"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
is_output_gitignored() {
|
|
76
|
+
local output_dir="$1"
|
|
77
|
+
is_gitignored "$(artifact_probe_path "$output_dir")"
|
|
78
|
+
}
|
|
79
|
+
|
|
70
80
|
# Resolve explicit output first
|
|
71
81
|
if [[ -n "$OUTPUT" ]]; then
|
|
72
82
|
echo "review_mode=file"
|
|
73
83
|
echo "output_dir=$OUTPUT"
|
|
74
84
|
echo "output_kind=custom"
|
|
75
|
-
echo "output_gitignored=$(
|
|
85
|
+
echo "output_gitignored=$(is_output_gitignored "$OUTPUT")"
|
|
76
86
|
echo "reason=explicit_output"
|
|
77
87
|
exit 0
|
|
78
88
|
fi
|
|
@@ -93,7 +103,7 @@ if [[ "$MODE" == "tracked" ]]; then
|
|
|
93
103
|
echo "review_mode=file"
|
|
94
104
|
echo "output_dir=$TRACKED_DIR"
|
|
95
105
|
echo "output_kind=tracked"
|
|
96
|
-
echo "output_gitignored=$(
|
|
106
|
+
echo "output_gitignored=$(is_output_gitignored "$TRACKED_DIR")"
|
|
97
107
|
echo "reason=forced_tracked"
|
|
98
108
|
exit 0
|
|
99
109
|
fi
|
|
@@ -102,13 +112,13 @@ if [[ "$MODE" == "local" ]]; then
|
|
|
102
112
|
echo "review_mode=file"
|
|
103
113
|
echo "output_dir=$LOCAL_DIR"
|
|
104
114
|
echo "output_kind=local"
|
|
105
|
-
echo "output_gitignored=$(
|
|
115
|
+
echo "output_gitignored=$(is_output_gitignored "$LOCAL_DIR")"
|
|
106
116
|
echo "reason=forced_local"
|
|
107
117
|
exit 0
|
|
108
118
|
fi
|
|
109
119
|
|
|
110
120
|
# auto mode
|
|
111
|
-
if [[ -d "$TRACKED_DIR" ]] && [[ "$(
|
|
121
|
+
if [[ -d "$TRACKED_DIR" ]] && [[ "$(is_output_gitignored "$TRACKED_DIR")" == "false" ]]; then
|
|
112
122
|
echo "review_mode=file"
|
|
113
123
|
echo "output_dir=$TRACKED_DIR"
|
|
114
124
|
echo "output_kind=tracked"
|
|
@@ -120,5 +130,5 @@ fi
|
|
|
120
130
|
echo "review_mode=file"
|
|
121
131
|
echo "output_dir=$LOCAL_DIR"
|
|
122
132
|
echo "output_kind=local"
|
|
123
|
-
echo "output_gitignored=$(
|
|
133
|
+
echo "output_gitignored=$(is_output_gitignored "$LOCAL_DIR")"
|
|
124
134
|
echo "reason=default_local_only"
|
|
@@ -10,6 +10,7 @@ interface ProjectNewDependencies {
|
|
|
10
10
|
force: boolean;
|
|
11
11
|
setActive: boolean;
|
|
12
12
|
refreshDashboard: boolean;
|
|
13
|
+
commit: boolean;
|
|
13
14
|
}) => Promise<ScaffoldProjectResult>;
|
|
14
15
|
}
|
|
15
16
|
export declare function createProjectNewCommand(overrides?: Partial<ProjectNewDependencies>): Command;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAE5C,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAE5C,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AAgBpB,UAAU,sBAAsB;IAC9B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,eAAe,EAAE,CAAC,OAAO,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,mBAAmB,CAAC;QAC1B,KAAK,EAAE,OAAO,CAAC;QACf,SAAS,EAAE,OAAO,CAAC;QACnB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,MAAM,EAAE,OAAO,CAAC;KACjB,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtC;AAoFD,wBAAgB,uBAAuB,CACrC,SAAS,GAAE,OAAO,CAAC,sBAAsB,CAAM,GAC9C,OAAO,CAkCT"}
|
|
@@ -2,6 +2,11 @@ import { buildCommandContext, } from '../../../app/command-context.js';
|
|
|
2
2
|
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
3
3
|
import { Command, Option } from 'commander';
|
|
4
4
|
import { scaffoldProject as defaultScaffoldProject, } from './scaffold.js';
|
|
5
|
+
const COMMIT_STATUS_MESSAGES = {
|
|
6
|
+
skipped_disabled: 'Scaffold commit: skipped (--no-commit)',
|
|
7
|
+
skipped_no_worktree: 'Scaffold commit: skipped (not a git work tree)',
|
|
8
|
+
skipped_nothing: 'Scaffold commit: skipped (nothing to commit)',
|
|
9
|
+
};
|
|
5
10
|
const DEFAULT_DEPENDENCIES = {
|
|
6
11
|
buildCommandContext,
|
|
7
12
|
scaffoldProject: defaultScaffoldProject,
|
|
@@ -18,6 +23,10 @@ function reportSuccess(context, projectName, result) {
|
|
|
18
23
|
skippedFiles: result.skippedFiles,
|
|
19
24
|
activePointerUpdated: result.activePointerUpdated,
|
|
20
25
|
dashboardRefreshed: result.dashboardRefreshed,
|
|
26
|
+
committed: result.committed,
|
|
27
|
+
commitSha: result.commitSha,
|
|
28
|
+
commitStatus: result.commitStatus,
|
|
29
|
+
commitError: result.commitError,
|
|
21
30
|
});
|
|
22
31
|
return;
|
|
23
32
|
}
|
|
@@ -26,6 +35,18 @@ function reportSuccess(context, projectName, result) {
|
|
|
26
35
|
if (result.activePointerUpdated) {
|
|
27
36
|
context.logger.info('Active project updated in local config: .oat/config.local.json');
|
|
28
37
|
}
|
|
38
|
+
if (result.commitStatus === 'committed') {
|
|
39
|
+
context.logger.info(`Scaffold commit: ${result.commitSha?.slice(0, 7) ?? 'committed'}`);
|
|
40
|
+
}
|
|
41
|
+
else if (result.commitStatus === 'failed') {
|
|
42
|
+
// The scaffold itself succeeded, so this is a warning, not an error: do not
|
|
43
|
+
// change the process exit code. Make clear the baseline was NOT committed.
|
|
44
|
+
const detail = result.commitError ? `: ${result.commitError}` : '';
|
|
45
|
+
context.logger.warn(`Warning: scaffold commit failed${detail}. The scaffolded files were written but NOT committed.`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
context.logger.info(COMMIT_STATUS_MESSAGES[result.commitStatus]);
|
|
49
|
+
}
|
|
29
50
|
}
|
|
30
51
|
async function runProjectNew(projectName, options, context, dependencies) {
|
|
31
52
|
try {
|
|
@@ -36,6 +57,7 @@ async function runProjectNew(projectName, options, context, dependencies) {
|
|
|
36
57
|
force: options.force,
|
|
37
58
|
setActive: options.setActive,
|
|
38
59
|
refreshDashboard: options.dashboard,
|
|
60
|
+
commit: options.commit,
|
|
39
61
|
});
|
|
40
62
|
reportSuccess(context, projectName, result);
|
|
41
63
|
process.exitCode = 0;
|
|
@@ -65,6 +87,7 @@ export function createProjectNewCommand(overrides = {}) {
|
|
|
65
87
|
.option('--force', 'Non-destructive scaffold; create missing files only')
|
|
66
88
|
.option('--no-set-active', 'Do not update active project in local config')
|
|
67
89
|
.option('--no-dashboard', 'Do not refresh .oat/state.md after scaffold')
|
|
90
|
+
.option('--no-commit', 'Do not git-commit the scaffolded project directory')
|
|
68
91
|
.action(async (name, options, command) => {
|
|
69
92
|
if (name.startsWith('-')) {
|
|
70
93
|
command.help();
|
|
@@ -6,11 +6,24 @@ export interface ScaffoldProjectOptions {
|
|
|
6
6
|
force?: boolean;
|
|
7
7
|
setActive?: boolean;
|
|
8
8
|
refreshDashboard?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Commit the freshly scaffolded project directory so the artifact baseline is
|
|
11
|
+
* git-tracked from t=0. Opt-in (default false) so library callers that manage
|
|
12
|
+
* their own commits (e.g. the project-split flow) are unaffected; only the
|
|
13
|
+
* `oat project new` command enables it by default.
|
|
14
|
+
*/
|
|
15
|
+
commit?: boolean;
|
|
9
16
|
env?: NodeJS.ProcessEnv;
|
|
10
17
|
today?: string;
|
|
11
18
|
nowUtc?: string;
|
|
12
19
|
refreshDashboardCallback?: (repoRoot: string) => void | Promise<void>;
|
|
13
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Classified outcome of the scoped scaffold commit. Distinguishing the skip
|
|
23
|
+
* reasons (and `failed`) lets the CLI surface accurate, distinct messaging
|
|
24
|
+
* instead of collapsing every non-commit into a single benign "skipped" line.
|
|
25
|
+
*/
|
|
26
|
+
export type CommitScaffoldStatus = 'committed' | 'skipped_disabled' | 'skipped_no_worktree' | 'skipped_nothing' | 'failed';
|
|
14
27
|
export interface ScaffoldProjectResult {
|
|
15
28
|
mode: ProjectScaffoldMode;
|
|
16
29
|
projectsRoot: string;
|
|
@@ -19,6 +32,10 @@ export interface ScaffoldProjectResult {
|
|
|
19
32
|
skippedFiles: string[];
|
|
20
33
|
activePointerUpdated: boolean;
|
|
21
34
|
dashboardRefreshed: boolean;
|
|
35
|
+
committed: boolean;
|
|
36
|
+
commitSha?: string;
|
|
37
|
+
commitStatus: CommitScaffoldStatus;
|
|
38
|
+
commitError?: string;
|
|
22
39
|
}
|
|
23
40
|
export declare function scaffoldProject(options: ScaffoldProjectOptions): Promise<ScaffoldProjectResult>;
|
|
24
41
|
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/scaffold.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/scaffold.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GACX,kBAAkB,GAClB,qBAAqB,GACrB,iBAAiB,GACjB,QAAQ,CAAC;AAEb,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,oBAAoB,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAoRD,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CA4EhC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
1
2
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
import { resolveProjectsRoot } from '../../shared/oat-paths.js';
|
|
@@ -105,6 +106,62 @@ function applyTemplateReplacements(template, projectName, today, nowUtc, mode) {
|
|
|
105
106
|
async function defaultRefreshDashboard(repoRoot) {
|
|
106
107
|
await generateStateDashboard({ repoRoot });
|
|
107
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Scoped, fail-safe commit of just the files this run created.
|
|
111
|
+
*
|
|
112
|
+
* Stages and commits only the pathspecs derived from `createdFiles` (under
|
|
113
|
+
* `projectPath`) so unrelated working-tree changes — including pre-existing
|
|
114
|
+
* dirty edits inside the same project directory on a re-run, and the
|
|
115
|
+
* `.oat/state.md` dashboard outside it — are never swept in. The returned
|
|
116
|
+
* status distinguishes a clean commit from each skip reason and from a genuine
|
|
117
|
+
* git failure; on failure the captured git stderr is surfaced via `error`. This
|
|
118
|
+
* never throws: any git error is classified as `failed`, not propagated.
|
|
119
|
+
*/
|
|
120
|
+
function commitScaffold(cwd, projectPath, projectName, createdFiles) {
|
|
121
|
+
const run = (args) => execFileSync('git', args, {
|
|
122
|
+
cwd,
|
|
123
|
+
encoding: 'utf8',
|
|
124
|
+
// Capture stderr instead of inheriting it so deliberate skip/failure
|
|
125
|
+
// probes (e.g. tests) do not leak raw `git fatal:` lines to the terminal.
|
|
126
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
127
|
+
}).trim();
|
|
128
|
+
try {
|
|
129
|
+
run(['rev-parse', '--is-inside-work-tree']);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return { status: 'skipped_no_worktree', committed: false };
|
|
133
|
+
}
|
|
134
|
+
// Only commit files this run created. Nothing created => nothing to commit,
|
|
135
|
+
// which guarantees a re-run never touches unrelated working-tree edits.
|
|
136
|
+
if (createdFiles.length === 0) {
|
|
137
|
+
return { status: 'skipped_nothing', committed: false };
|
|
138
|
+
}
|
|
139
|
+
const pathspecs = createdFiles.map((file) => join(projectPath, file));
|
|
140
|
+
try {
|
|
141
|
+
run(['add', '--', ...pathspecs]);
|
|
142
|
+
const staged = run(['diff', '--cached', '--name-only', '--', ...pathspecs]);
|
|
143
|
+
if (staged.length === 0) {
|
|
144
|
+
return { status: 'skipped_nothing', committed: false };
|
|
145
|
+
}
|
|
146
|
+
run([
|
|
147
|
+
'commit',
|
|
148
|
+
'-m',
|
|
149
|
+
`chore(oat): scaffold ${projectName}`,
|
|
150
|
+
'--',
|
|
151
|
+
...pathspecs,
|
|
152
|
+
]);
|
|
153
|
+
const commitSha = run(['rev-parse', 'HEAD']);
|
|
154
|
+
return { status: 'committed', committed: true, commitSha };
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const stderr = error && typeof error === 'object' && 'stderr' in error
|
|
158
|
+
? error.stderr
|
|
159
|
+
: undefined;
|
|
160
|
+
const message = (stderr != null ? stderr.toString().trim() : '') ||
|
|
161
|
+
(error instanceof Error ? error.message : String(error));
|
|
162
|
+
return { status: 'failed', committed: false, error: message };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
108
165
|
async function scaffoldModeTemplates(repoRoot, projectPath, projectName, mode, today, nowUtc) {
|
|
109
166
|
const templatesDir = join(repoRoot, '.oat', 'templates');
|
|
110
167
|
const createdFiles = [];
|
|
@@ -169,6 +226,19 @@ export async function scaffoldProject(options) {
|
|
|
169
226
|
console.error(`Warning: dashboard refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
170
227
|
}
|
|
171
228
|
}
|
|
229
|
+
let committed = false;
|
|
230
|
+
let commitSha;
|
|
231
|
+
let commitStatus = 'skipped_disabled';
|
|
232
|
+
let commitError;
|
|
233
|
+
if (options.commit) {
|
|
234
|
+
// `projectPath` is relative to `repoRoot`, so git must run there for the
|
|
235
|
+
// pathspecs to resolve to the scaffolded files.
|
|
236
|
+
const commitResult = commitScaffold(options.repoRoot, projectPath, options.projectName, createdFiles);
|
|
237
|
+
committed = commitResult.committed;
|
|
238
|
+
commitSha = commitResult.commitSha;
|
|
239
|
+
commitStatus = commitResult.status;
|
|
240
|
+
commitError = commitResult.error;
|
|
241
|
+
}
|
|
172
242
|
return {
|
|
173
243
|
mode,
|
|
174
244
|
projectsRoot,
|
|
@@ -177,5 +247,9 @@ export async function scaffoldProject(options) {
|
|
|
177
247
|
skippedFiles,
|
|
178
248
|
activePointerUpdated: setActive,
|
|
179
249
|
dashboardRefreshed,
|
|
250
|
+
committed,
|
|
251
|
+
commitSha,
|
|
252
|
+
commitStatus,
|
|
253
|
+
commitError,
|
|
180
254
|
};
|
|
181
255
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ChildPlan } from './child-plan.js';
|
|
2
2
|
import type { SplitProjectContext } from './write-parent.js';
|
|
3
|
-
declare const SEEDED_SECTIONS: readonly ["Origin", "Inherited Context", "Child Scope", "Known Dependencies", "Assumptions To Revalidate", "Likely Workflow Mode", "Sibling Projects"];
|
|
3
|
+
declare const SEEDED_SECTIONS: readonly ["Origin", "Inherited Context", "Inherited Context Revalidation Gate", "Child Scope", "Known Dependencies", "Assumptions To Revalidate", "Likely Workflow Mode", "Sibling Projects"];
|
|
4
4
|
export interface SeedChildrenResult {
|
|
5
5
|
childProjectPaths: string[];
|
|
6
6
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seed-children.d.ts","sourceRoot":"","sources":["../../../src/projects/split/seed-children.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,QAAA,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"seed-children.d.ts","sourceRoot":"","sources":["../../../src/projects/split/seed-children.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,QAAA,MAAM,eAAe,+LASX,CAAC;AA0GX,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,wBAAsB,YAAY,CAChC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,mBAAmB,EAC5B,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,OAAO,CAAC,kBAAkB,CAAC,CAoD7B;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -7,6 +7,7 @@ import YAML from 'yaml';
|
|
|
7
7
|
const SEEDED_SECTIONS = [
|
|
8
8
|
'Origin',
|
|
9
9
|
'Inherited Context',
|
|
10
|
+
'Inherited Context Revalidation Gate',
|
|
10
11
|
'Child Scope',
|
|
11
12
|
'Known Dependencies',
|
|
12
13
|
'Assumptions To Revalidate',
|
|
@@ -53,16 +54,38 @@ function renderSeededDiscovery(plan, child) {
|
|
|
53
54
|
.map((candidate) => candidate.slug)
|
|
54
55
|
.filter((slug) => slug !== child.slug);
|
|
55
56
|
const sections = [
|
|
56
|
-
[
|
|
57
|
+
[
|
|
58
|
+
'Origin',
|
|
59
|
+
`Split from coordination parent \`${plan.parentSlug}\`. This child was seeded by \`oat-project-split\`; it is **not** discovered, planned, or implementation-ready yet and resumes at discovery.`,
|
|
60
|
+
],
|
|
57
61
|
[
|
|
58
62
|
'Inherited Context',
|
|
59
|
-
|
|
63
|
+
[
|
|
64
|
+
child.inheritedContext || 'No inherited context provided.',
|
|
65
|
+
'',
|
|
66
|
+
'> Provisional seed. This context was inherited from the parent split decision. It is a **starting point, not a final scope decision** — treat every inherited claim as an assumption until it is revalidated below.',
|
|
67
|
+
].join('\n'),
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
'Inherited Context Revalidation Gate',
|
|
71
|
+
[
|
|
72
|
+
'This project resumes at **discovery**, not planning or implementation. Inherited context may be stale by the time work resumes, so it must be revalidated before this discovery is completed.',
|
|
73
|
+
'',
|
|
74
|
+
'- [ ] Re-read the inherited context against the current codebase and sibling progress.',
|
|
75
|
+
'- [ ] Confirm or correct the child scope, dependencies, and assumptions below.',
|
|
76
|
+
'- [ ] When revalidation is complete, set `oat_inherited_context_revalidated: true` in this file.',
|
|
77
|
+
'',
|
|
78
|
+
'Discovery cannot be marked complete — and planning must not begin — while `oat_inherited_context_revalidated: false`. A child left in this state is expected work-in-progress, not stale bookkeeping.',
|
|
79
|
+
].join('\n'),
|
|
60
80
|
],
|
|
61
81
|
['Child Scope', child.description ?? child.slug],
|
|
62
82
|
['Known Dependencies', bulletList(child.knownDependencies)],
|
|
63
83
|
[
|
|
64
84
|
'Assumptions To Revalidate',
|
|
65
|
-
|
|
85
|
+
[
|
|
86
|
+
'- Revalidate inherited context before completing discovery.',
|
|
87
|
+
'- Confirm the child scope still matches reality before generating a plan.',
|
|
88
|
+
].join('\n'),
|
|
66
89
|
],
|
|
67
90
|
['Likely Workflow Mode', 'quick'],
|
|
68
91
|
['Sibling Projects', bulletList(siblingSlugs)],
|
|
@@ -111,8 +134,16 @@ export async function seedChildren(plan, context, onlySlugs) {
|
|
|
111
134
|
oat_siblings: siblings,
|
|
112
135
|
oat_depends_on: child.knownDependencies,
|
|
113
136
|
});
|
|
137
|
+
// Children resume at discovery, so their scaffolded plan.md is a
|
|
138
|
+
// placeholder only. Re-mark it as a not-started template (the scaffold
|
|
139
|
+
// strips these markers when rendering) so neither tooling nor agents can
|
|
140
|
+
// mistake the seed for an active or validated plan; routing keys off
|
|
141
|
+
// state.md (oat_phase: discovery) and oat-project-next treats
|
|
142
|
+
// oat_template: true as a still-a-template signal.
|
|
114
143
|
await updateFrontmatter(join(childRoot, 'plan.md'), {
|
|
115
144
|
oat_plan_source: 'quick',
|
|
145
|
+
oat_template: true,
|
|
146
|
+
oat_template_name: 'plan',
|
|
116
147
|
});
|
|
117
148
|
await writeFile(join(childRoot, 'discovery.md'), renderSeededDiscovery(plan, child), 'utf8');
|
|
118
149
|
childProjectPaths.push(scaffold.projectPath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-agent-toolkit/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
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.1.
|
|
37
|
+
"@open-agent-toolkit/control-plane": "0.1.16"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.0",
|