@open-agent-toolkit/cli 0.2.41 → 0.2.46
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 +11 -1
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +11 -1
- package/assets/docs/cli-utilities/tool-packs.md +55 -9
- package/assets/docs/quickstart.md +9 -0
- package/assets/docs/reference/cli-reference.md +56 -1
- package/assets/docs/reference/file-locations.md +18 -1
- package/assets/docs/reference/oat-directory-structure.md +20 -4
- package/assets/docs/reference/troubleshooting.md +18 -11
- package/assets/docs/workflows/projects/artifacts.md +27 -1
- package/assets/docs/workflows/projects/implementation-execution.md +22 -0
- package/assets/docs/workflows/projects/index.md +6 -0
- package/assets/docs/workflows/projects/lifecycle.md +19 -5
- package/assets/docs/workflows/projects/picking-up-projects.md +75 -0
- package/assets/docs/workflows/projects/pr-flow.md +22 -1
- package/assets/docs/workflows/projects/reviewing-oat-prs.md +60 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-brainstorm/SKILL.md +135 -32
- package/assets/skills/oat-brainstorm/references/destinations.md +9 -6
- package/assets/skills/oat-cursor-cloud-projects/SKILL.md +11 -1
- package/assets/skills/oat-doctor/SKILL.md +8 -1
- package/assets/skills/oat-project-autonomous/SKILL.md +45 -1
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +8 -8
- package/assets/skills/oat-project-capture/SKILL.md +29 -9
- package/assets/skills/oat-project-complete/SKILL.md +610 -38
- package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +265 -0
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +764 -0
- package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +134 -0
- package/assets/skills/oat-project-complete/scripts/validate-nonarchive-lifecycle-receipt.mjs +160 -0
- package/assets/skills/oat-project-design/SKILL.md +33 -9
- package/assets/skills/oat-project-discover/SKILL.md +16 -3
- package/assets/skills/oat-project-document/SKILL.md +10 -4
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +8 -8
- package/assets/skills/oat-project-implement/SKILL.md +14 -3
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +18 -6
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +8 -8
- package/assets/skills/oat-project-implement/references/phase-execution.md +9 -3
- package/assets/skills/oat-project-implement/references/plan-and-resume.md +3 -0
- package/assets/skills/oat-project-import-plan/SKILL.md +15 -9
- package/assets/skills/oat-project-new/SKILL.md +7 -5
- package/assets/skills/oat-project-next/SKILL.md +32 -10
- package/assets/skills/oat-project-plan/SKILL.md +18 -3
- package/assets/skills/oat-project-pr-final/SKILL.md +23 -2
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +8 -8
- package/assets/skills/oat-project-pr-progress/SKILL.md +23 -2
- package/assets/skills/oat-project-progress/SKILL.md +23 -9
- package/assets/skills/oat-project-promote-spec-driven/SKILL.md +20 -2
- package/assets/skills/oat-project-quick-start/SKILL.md +48 -18
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +8 -8
- package/assets/skills/oat-project-reconcile/SKILL.md +14 -10
- package/assets/skills/oat-project-retro/SKILL.md +3 -3
- package/assets/skills/oat-project-retro/references/apply-procedure.md +102 -6
- package/assets/skills/oat-project-retro-file/SKILL.md +57 -6
- package/assets/skills/oat-project-review-provide/SKILL.md +45 -4
- package/assets/skills/oat-project-review-receive/SKILL.md +16 -7
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +9 -3
- package/assets/skills/oat-project-revise/SKILL.md +12 -3
- package/assets/skills/oat-project-spec/SKILL.md +16 -3
- package/assets/skills/oat-project-summary/SKILL.md +113 -13
- package/assets/skills/oat-wave-execute/SKILL.md +34 -2
- package/assets/skills/oat-worktree-bootstrap/SKILL.md +13 -1
- package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +13 -1
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +34 -3
- package/dist/commands/doctor/index.d.ts +2 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +27 -3
- package/dist/commands/doctor/synced-projects.d.ts +12 -0
- package/dist/commands/doctor/synced-projects.d.ts.map +1 -0
- package/dist/commands/doctor/synced-projects.js +208 -0
- package/dist/commands/init/gitattributes.d.ts +4 -0
- package/dist/commands/init/gitattributes.d.ts.map +1 -0
- package/dist/commands/init/gitattributes.js +12 -0
- package/dist/commands/init/gitignore.d.ts +8 -1
- package/dist/commands/init/gitignore.d.ts.map +1 -1
- package/dist/commands/init/gitignore.js +146 -41
- package/dist/commands/init/index.d.ts +2 -0
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +3 -0
- package/dist/commands/init/managed-block.d.ts +10 -0
- package/dist/commands/init/managed-block.d.ts.map +1 -0
- package/dist/commands/init/managed-block.js +40 -0
- package/dist/commands/init/tools/index.d.ts +1 -0
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +37 -9
- package/dist/commands/init/tools/workflows/install-workflows.js +1 -1
- package/dist/commands/local/index.d.ts.map +1 -1
- package/dist/commands/local/index.js +5 -1
- package/dist/commands/local/sync.d.ts +4 -0
- package/dist/commands/local/sync.d.ts.map +1 -1
- package/dist/commands/local/sync.js +123 -2
- package/dist/commands/pjm/adoption.d.ts +2 -0
- package/dist/commands/pjm/adoption.d.ts.map +1 -1
- package/dist/commands/pjm/adoption.js +1 -1
- package/dist/commands/project/archive/archive-utils.d.ts +24 -0
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +349 -22
- package/dist/commands/project/archive/index.d.ts.map +1 -1
- package/dist/commands/project/archive/index.js +1 -0
- package/dist/commands/project/archive/push-runner.d.ts +5 -1
- package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
- package/dist/commands/project/archive/push-runner.js +33 -4
- package/dist/commands/project/complete-state/index.d.ts.map +1 -1
- package/dist/commands/project/complete-state/index.js +3 -1
- package/dist/commands/project/index.d.ts.map +1 -1
- package/dist/commands/project/index.js +12 -0
- package/dist/commands/project/links/compute.d.ts +9 -0
- package/dist/commands/project/links/compute.d.ts.map +1 -0
- package/dist/commands/project/links/compute.js +27 -0
- package/dist/commands/project/links/index.d.ts +17 -0
- package/dist/commands/project/links/index.d.ts.map +1 -0
- package/dist/commands/project/links/index.js +72 -0
- package/dist/commands/project/links/refresh.d.ts +26 -0
- package/dist/commands/project/links/refresh.d.ts.map +1 -0
- package/dist/commands/project/links/refresh.js +92 -0
- package/dist/commands/project/links/render.d.ts +24 -0
- package/dist/commands/project/links/render.d.ts.map +1 -0
- package/dist/commands/project/links/render.js +68 -0
- package/dist/commands/project/list.d.ts +5 -0
- package/dist/commands/project/list.d.ts.map +1 -1
- package/dist/commands/project/list.js +182 -9
- package/dist/commands/project/migrate/index.d.ts +22 -0
- package/dist/commands/project/migrate/index.d.ts.map +1 -0
- package/dist/commands/project/migrate/index.js +79 -0
- package/dist/commands/project/new/index.d.ts +2 -0
- package/dist/commands/project/new/index.d.ts.map +1 -1
- package/dist/commands/project/new/index.js +14 -2
- package/dist/commands/project/new/scaffold.d.ts +22 -1
- package/dist/commands/project/new/scaffold.d.ts.map +1 -1
- package/dist/commands/project/new/scaffold.js +287 -69
- package/dist/commands/project/open/index.d.ts +16 -0
- package/dist/commands/project/open/index.d.ts.map +1 -1
- package/dist/commands/project/open/index.js +151 -19
- package/dist/commands/project/pause/index.d.ts +8 -0
- package/dist/commands/project/pause/index.d.ts.map +1 -1
- package/dist/commands/project/pause/index.js +129 -19
- package/dist/commands/project/prune/index.d.ts +21 -0
- package/dist/commands/project/prune/index.d.ts.map +1 -0
- package/dist/commands/project/prune/index.js +95 -0
- package/dist/commands/project/pull/index.d.ts +28 -0
- package/dist/commands/project/pull/index.d.ts.map +1 -0
- package/dist/commands/project/pull/index.js +157 -0
- package/dist/commands/project/push/index.d.ts +22 -0
- package/dist/commands/project/push/index.d.ts.map +1 -0
- package/dist/commands/project/push/index.js +100 -0
- package/dist/commands/project/scope/index.d.ts +17 -0
- package/dist/commands/project/scope/index.d.ts.map +1 -0
- package/dist/commands/project/scope/index.js +107 -0
- package/dist/commands/project/split/run.d.ts +4 -0
- package/dist/commands/project/split/run.d.ts.map +1 -1
- package/dist/commands/project/split/run.js +48 -10
- package/dist/commands/project/sync/git.d.ts +18 -0
- package/dist/commands/project/sync/git.d.ts.map +1 -0
- package/dist/commands/project/sync/git.js +60 -0
- package/dist/commands/project/sync/record.d.ts +22 -0
- package/dist/commands/project/sync/record.d.ts.map +1 -0
- package/dist/commands/project/sync/record.js +133 -0
- package/dist/commands/project/sync/ref-sync.d.ts +120 -0
- package/dist/commands/project/sync/ref-sync.d.ts.map +1 -0
- package/dist/commands/project/sync/ref-sync.js +962 -0
- package/dist/commands/project/sync/resolve-target.d.ts +27 -0
- package/dist/commands/project/sync/resolve-target.d.ts.map +1 -0
- package/dist/commands/project/sync/resolve-target.js +159 -0
- package/dist/commands/shared/oat-paths.d.ts +4 -1
- package/dist/commands/shared/oat-paths.d.ts.map +1 -1
- package/dist/commands/shared/oat-paths.js +2 -2
- package/dist/commands/shared/project-scope.d.ts +23 -0
- package/dist/commands/shared/project-scope.d.ts.map +1 -0
- package/dist/commands/shared/project-scope.js +106 -0
- package/dist/commands/tools/migrate/index.js +1 -1
- package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-inventory.js +79 -8
- package/dist/commands/tools/shared/pack-lifecycle.js +1 -1
- package/dist/commands/tools/shared/pack-reconcile.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-reconcile.js +4 -1
- package/dist/commands/tools/shared/project-tools-config.d.ts +6 -2
- package/dist/commands/tools/shared/project-tools-config.d.ts.map +1 -1
- package/dist/commands/tools/shared/project-tools-config.js +7 -5
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +38 -14
- package/dist/commands/tools/update/update-tools.d.ts +2 -0
- package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
- package/dist/config/oat-config.d.ts +6 -3
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +41 -7
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +2 -1
- package/dist/fs/io.d.ts +2 -1
- package/dist/fs/io.d.ts.map +1 -1
- package/dist/fs/io.js +5 -2
- package/dist/projects/split/finalize.d.ts.map +1 -1
- package/dist/projects/split/finalize.js +55 -2
- package/dist/projects/split/resume.d.ts +3 -0
- package/dist/projects/split/resume.d.ts.map +1 -1
- package/dist/projects/split/resume.js +34 -7
- package/dist/projects/split/seed-children.d.ts.map +1 -1
- package/dist/projects/split/seed-children.js +6 -0
- package/dist/projects/split/write-parent.d.ts +7 -0
- package/dist/projects/split/write-parent.d.ts.map +1 -1
- package/dist/projects/split/write-parent.js +7 -4
- package/dist/release/public-package-contract.d.ts.map +1 -1
- package/dist/release/public-package-contract.js +1 -0
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +434 -0
- package/package.json +4 -4
- package/dist/__tests__/skills/split-flow-fixtures.d.ts +0 -50
- package/dist/__tests__/skills/split-flow-fixtures.d.ts.map +0 -1
- package/dist/__tests__/skills/split-flow-fixtures.js +0 -161
- package/dist/commands/__tests__/helpers.d.ts +0 -12
- package/dist/commands/__tests__/helpers.d.ts.map +0 -1
- package/dist/commands/__tests__/helpers.js +0 -36
|
@@ -3,6 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { scaffoldProject as defaultScaffoldProject, } from '../../commands/project/new/scaffold.js';
|
|
4
4
|
import { getFrontmatterBlock } from '../../commands/shared/frontmatter.js';
|
|
5
5
|
import { replaceFrontmatter } from '../../commands/shared/frontmatter-write.js';
|
|
6
|
+
import { resolveProjectScope, } from '../../commands/shared/project-scope.js';
|
|
6
7
|
import { assertValidProjectStateFilesystemContent } from '../../validation/project-state.js';
|
|
7
8
|
import YAML from 'yaml';
|
|
8
9
|
function readObjectFrontmatter(content, filePath) {
|
|
@@ -22,9 +23,6 @@ async function writeObjectFrontmatter(filePath, updates) {
|
|
|
22
23
|
updates(frontmatter);
|
|
23
24
|
await writeFile(filePath, replaceFrontmatter(content, YAML.stringify(frontmatter).trimEnd()), 'utf8');
|
|
24
25
|
}
|
|
25
|
-
function projectPathFor(projectsRoot, slug) {
|
|
26
|
-
return join(projectsRoot, slug).split('\\').join('/');
|
|
27
|
-
}
|
|
28
26
|
function renderParentDiscovery(document) {
|
|
29
27
|
const { plan } = document;
|
|
30
28
|
const orderedChildren = plan.children
|
|
@@ -138,9 +136,14 @@ async function replaceMarkdownBody(filePath, body) {
|
|
|
138
136
|
}
|
|
139
137
|
export async function writeCoordinationParent(document, context) {
|
|
140
138
|
const scaffoldProject = context.scaffoldProject ?? defaultScaffoldProject;
|
|
139
|
+
const projectsRoot = context.projectsRoot ?? '.oat/projects/shared';
|
|
140
|
+
const scope = context.scope ??
|
|
141
|
+
resolveProjectScope(join(projectsRoot, document.plan.parentSlug), projectsRoot, context.repoRoot) ??
|
|
142
|
+
'shared';
|
|
141
143
|
const scaffold = await scaffoldProject({
|
|
142
144
|
repoRoot: context.repoRoot,
|
|
143
145
|
projectName: document.plan.parentSlug,
|
|
146
|
+
scope,
|
|
144
147
|
mode: 'quick',
|
|
145
148
|
setActive: false,
|
|
146
149
|
refreshDashboard: false,
|
|
@@ -170,7 +173,7 @@ export async function writeCoordinationParent(document, context) {
|
|
|
170
173
|
projectPath: parentRoot,
|
|
171
174
|
});
|
|
172
175
|
return {
|
|
173
|
-
parentProjectPath:
|
|
176
|
+
parentProjectPath: scaffold.projectPath,
|
|
174
177
|
scaffold,
|
|
175
178
|
};
|
|
176
179
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-package-contract.d.ts","sourceRoot":"","sources":["../../src/release/public-package-contract.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,GAAG,cAAc,GAAG,iBAAiB,CAAC;IACjD,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uBAAuB,EAAE,yBAAyB,EAAE,CAAC;IACrD,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,4BAA4B,EAAE,MAAM,EAAE,CAAC;IACvC,2BAA2B,EAAE,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;
|
|
1
|
+
{"version":3,"file":"public-package-contract.d.ts","sourceRoot":"","sources":["../../src/release/public-package-contract.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,GAAG,cAAc,GAAG,iBAAiB,CAAC;IACjD,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uBAAuB,EAAE,yBAAyB,EAAE,CAAC;IACrD,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,4BAA4B,EAAE,MAAM,EAAE,CAAC;IACvC,2BAA2B,EAAE,MAAM,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AA6KD,wBAAgB,yBAAyB,IAAI,qBAAqB,EAAE,CAenE;AAED,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,EAAE,CAIV;AAED,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,EAAE,CAIV;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,EAAE,CAMV;AAED,wBAAgB,6BAA6B,CAC3C,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACjD,QAAQ,EAAE,qBAAqB,GAC9B,MAAM,EAAE,CAoBV;AAED,wBAAgB,oCAAoC,CAClD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,EAAE,CAmBV;AAED,wBAAgB,qCAAqC,CACnD,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,kBAAkB,EAAE,SAAS,MAAM,EAAE,EACrC,qBAAqB,EAAE,SAAS,MAAM,EAAE,GACvC,MAAM,EAAE,CA0BV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/validation/skills.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,uCAAuC;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sCAAsC;IACrD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAChD,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,UAAU,6BAA6B;IACrC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/validation/skills.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,uCAAuC;IACtD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sCAAsC;IACrD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,KAChD,OAAO,CAAC,cAAc,CAAC,CAAC;AAE7B,UAAU,6BAA6B;IACrC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AA04BD,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,uCAAuC,EAChD,YAAY,GAAE,6BAAkC,GAC/C,OAAO,CAAC,sCAAsC,CAAC,CAoBjD;AAED,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,wBAA6B,EACtC,YAAY,GAAE,6BAAkC,GAC/C,OAAO,CAAC,uBAAuB,CAAC,CAqIlC"}
|
|
@@ -13,6 +13,439 @@ async function isDirectory(path) {
|
|
|
13
13
|
return false;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
async function isFile(path) {
|
|
17
|
+
try {
|
|
18
|
+
const s = await stat(path);
|
|
19
|
+
return s.isFile();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function listMarkdownFiles(root) {
|
|
26
|
+
if (!(await isDirectory(root))) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
const files = [];
|
|
30
|
+
for (const entry of await readdir(root, { withFileTypes: true })) {
|
|
31
|
+
const path = join(root, entry.name);
|
|
32
|
+
if (entry.isDirectory()) {
|
|
33
|
+
files.push(...(await listMarkdownFiles(path)));
|
|
34
|
+
}
|
|
35
|
+
else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
36
|
+
files.push(path);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return files.sort();
|
|
40
|
+
}
|
|
41
|
+
function countOccurrences(content, anchor) {
|
|
42
|
+
if (anchor.length === 0) {
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
let count = 0;
|
|
46
|
+
let offset = 0;
|
|
47
|
+
while ((offset = content.indexOf(anchor, offset)) !== -1) {
|
|
48
|
+
count += 1;
|
|
49
|
+
offset += anchor.length;
|
|
50
|
+
}
|
|
51
|
+
return count;
|
|
52
|
+
}
|
|
53
|
+
function isLifecycleSafetyFile(file) {
|
|
54
|
+
return (/\/\.agents\/skills\/(?:oat-project-[^/]+|oat-worktree-[^/]+|oat-brainstorm|oat-wave-execute)\//.test(file) || file.endsWith('/.agents/agents/oat-phase-implementer.md'));
|
|
55
|
+
}
|
|
56
|
+
function referencesProjectArtifactVariable(line, artifactArrays = new Set()) {
|
|
57
|
+
if (/\$PROJECT_PATH|\$\{PROJECT_PATH\}|\{PROJECT_PATH\}|\$ARTIFACT_PATH|\$\{ARTIFACT_PATH\}|\$ACTIVE_PROJECT(?!_PATH)|\$\{ACTIVE_PROJECT\}|\$REVIEW_PATH|\$\{REVIEW_PATH\}|\$\{(?:PROJECT|RETRO)_[A-Z0-9_]*(?:PATHS|FILES)\[@\]\}/.test(line)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return [...artifactArrays].some((name) => line.includes(`\${${name}[@]}`));
|
|
61
|
+
}
|
|
62
|
+
function projectArtifactArrays(content) {
|
|
63
|
+
const arrays = new Set();
|
|
64
|
+
let changed = true;
|
|
65
|
+
while (changed) {
|
|
66
|
+
changed = false;
|
|
67
|
+
for (const { content: line } of logicalLines(content)) {
|
|
68
|
+
const assignment = /^\s*([A-Z][A-Z0-9_]*)(?:\+)?=\((.*)\)\s*$/.exec(line);
|
|
69
|
+
const name = assignment?.[1];
|
|
70
|
+
const value = assignment?.[2];
|
|
71
|
+
if (name &&
|
|
72
|
+
value &&
|
|
73
|
+
!arrays.has(name) &&
|
|
74
|
+
referencesProjectArtifactVariable(value, arrays)) {
|
|
75
|
+
arrays.add(name);
|
|
76
|
+
changed = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return arrays;
|
|
81
|
+
}
|
|
82
|
+
function executesCommand(line, command) {
|
|
83
|
+
const prefix = '(?:(?:&&|\\|\\||;|\\bthen\\b|\\belse\\b|\\bdo\\b|\\bif\\b)\\s+)';
|
|
84
|
+
const modifiers = '(?:!\\s+)?(?:command\\s+)?(?:env\\s+)?(?:[A-Z][A-Z0-9_]*=\\S+\\s+)*';
|
|
85
|
+
return new RegExp(`^\\s*(?:(?:[A-Z][A-Z0-9_]*)=\\$\\()?${command}\\b|${prefix}${modifiers}${command}\\b`).test(line);
|
|
86
|
+
}
|
|
87
|
+
function executesGitCommand(line) {
|
|
88
|
+
return executesCommand(line, 'git');
|
|
89
|
+
}
|
|
90
|
+
function logicalLines(content) {
|
|
91
|
+
const physicalLines = content.split('\n');
|
|
92
|
+
const logical = [];
|
|
93
|
+
let offset = 0;
|
|
94
|
+
for (let index = 0; index < physicalLines.length; index += 1) {
|
|
95
|
+
const startLine = index;
|
|
96
|
+
const startOffset = offset;
|
|
97
|
+
let combined = physicalLines[index] ?? '';
|
|
98
|
+
offset += combined.length + 1;
|
|
99
|
+
while (/\\\s*$/.test(combined) && index + 1 < physicalLines.length) {
|
|
100
|
+
combined = combined.replace(/\\\s*$/, ' ');
|
|
101
|
+
index += 1;
|
|
102
|
+
const continuation = physicalLines[index] ?? '';
|
|
103
|
+
combined += continuation.trimStart();
|
|
104
|
+
offset += continuation.length + 1;
|
|
105
|
+
}
|
|
106
|
+
logical.push({
|
|
107
|
+
content: combined,
|
|
108
|
+
endLine: index,
|
|
109
|
+
offset: startOffset,
|
|
110
|
+
startLine,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return logical;
|
|
114
|
+
}
|
|
115
|
+
function isProjectArtifactWriterLine(line, artifactArrays = new Set()) {
|
|
116
|
+
const trimmed = line.trim();
|
|
117
|
+
const executesGitOrOat = executesGitCommand(trimmed) || executesCommand(trimmed, 'oat');
|
|
118
|
+
const writesProjectArtifact = executesGitOrOat &&
|
|
119
|
+
/\bgit\s+(?:add|commit)\b|\boat\s+project\s+push\b/.test(trimmed) &&
|
|
120
|
+
referencesProjectArtifactVariable(line, artifactArrays);
|
|
121
|
+
const writesActiveProject = executesGitOrOat && /\boat\s+config\s+set\s+activeProject\b/.test(trimmed);
|
|
122
|
+
return writesProjectArtifact || writesActiveProject;
|
|
123
|
+
}
|
|
124
|
+
function fencedContentSites(content) {
|
|
125
|
+
const lines = content.split('\n');
|
|
126
|
+
const sites = [];
|
|
127
|
+
let offset = 0;
|
|
128
|
+
let opening = null;
|
|
129
|
+
for (const line of lines) {
|
|
130
|
+
const lineEnd = offset + line.length;
|
|
131
|
+
const fenceMatch = line.match(/^\s*(`{3,}|~{3,})(?:[^`~]*)$/);
|
|
132
|
+
if (fenceMatch) {
|
|
133
|
+
const marker = fenceMatch[1] ?? '';
|
|
134
|
+
if (opening === null) {
|
|
135
|
+
opening = { marker, start: offset };
|
|
136
|
+
}
|
|
137
|
+
else if (marker[0] === opening.marker[0] &&
|
|
138
|
+
marker.length >= opening.marker.length &&
|
|
139
|
+
/^\s*[`~]+\s*$/.test(line)) {
|
|
140
|
+
const end = lineEnd;
|
|
141
|
+
sites.push({
|
|
142
|
+
start: opening.start,
|
|
143
|
+
end,
|
|
144
|
+
content: content.slice(opening.start, end),
|
|
145
|
+
});
|
|
146
|
+
opening = null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
offset = lineEnd + 1;
|
|
150
|
+
}
|
|
151
|
+
return sites;
|
|
152
|
+
}
|
|
153
|
+
function contentSiteAt(content, offset, fencedSites) {
|
|
154
|
+
const fenced = fencedSites.find((site) => offset >= site.start && offset < site.end);
|
|
155
|
+
if (fenced)
|
|
156
|
+
return fenced;
|
|
157
|
+
const preceding = content.slice(0, offset);
|
|
158
|
+
const headings = [...preceding.matchAll(/^([#]{1,6})\s+.+$/gm)];
|
|
159
|
+
const heading = headings.at(-1);
|
|
160
|
+
if (heading?.index !== undefined) {
|
|
161
|
+
const level = heading[1]?.length ?? 6;
|
|
162
|
+
const following = content.slice(offset);
|
|
163
|
+
const nextHeading = [...following.matchAll(/^([#]{1,6})\s+.+$/gm)].find((candidate) => (candidate[1]?.length ?? 7) <= level);
|
|
164
|
+
const end = nextHeading?.index === undefined
|
|
165
|
+
? content.length
|
|
166
|
+
: offset + nextHeading.index;
|
|
167
|
+
return {
|
|
168
|
+
start: heading.index,
|
|
169
|
+
end,
|
|
170
|
+
content: content.slice(heading.index, end),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const start = content.lastIndexOf('\n', Math.max(0, offset - 1)) + 1;
|
|
174
|
+
const nextNewline = content.indexOf('\n', offset);
|
|
175
|
+
const end = nextNewline === -1 ? content.length : nextNewline;
|
|
176
|
+
return { start, end, content: content.slice(start, end) };
|
|
177
|
+
}
|
|
178
|
+
function projectArtifactWriterSites(content) {
|
|
179
|
+
const fencedSites = fencedContentSites(content);
|
|
180
|
+
const sites = new Map();
|
|
181
|
+
const artifactArrays = projectArtifactArrays(content);
|
|
182
|
+
for (const line of logicalLines(content)) {
|
|
183
|
+
if (isProjectArtifactWriterLine(line.content, artifactArrays)) {
|
|
184
|
+
const site = contentSiteAt(content, line.offset, fencedSites);
|
|
185
|
+
sites.set(`${site.start}:${site.end}`, site);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return [...sites.values()];
|
|
189
|
+
}
|
|
190
|
+
function collectSyncedContentFindings(file, content, findings) {
|
|
191
|
+
for (const match of content.matchAll(/PROJECT_SCOPE=\$\(oat project scope[^\n]+\) \|\| exit 1\n\s*\[\s*"?\$PROJECT_SCOPE"?\s*=\s*"?synced"?\s*\]\s*&&\s*oat\s+project\s+pull\b/g)) {
|
|
192
|
+
const lineNumber = content.slice(0, match.index).split('\n').length + 1;
|
|
193
|
+
findings.push({
|
|
194
|
+
file,
|
|
195
|
+
message: `Line ${lineNumber}: Synced arrival pull must use an if block so shared and local arrival exits successfully`,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
const logical = logicalLines(content);
|
|
199
|
+
const artifactArrays = projectArtifactArrays(content);
|
|
200
|
+
for (const [logicalIndex, site] of logical.entries()) {
|
|
201
|
+
const line = site.content;
|
|
202
|
+
const lineNumber = site.startLine + 1;
|
|
203
|
+
if (/\bgit\s+add\b[^\n]*\.oat\/projects\/synced(?:\/|\b)/.test(line)) {
|
|
204
|
+
findings.push({
|
|
205
|
+
file,
|
|
206
|
+
message: `Line ${lineNumber}: Never stage a path under .oat/projects/synced/; use oat project push`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
if (/\boat\s+project\s+scope\b/.test(line) &&
|
|
210
|
+
(/--json\b/.test(line) || /\|[^\n]*\bjq\b/.test(line))) {
|
|
211
|
+
findings.push({
|
|
212
|
+
file,
|
|
213
|
+
message: `Line ${lineNumber}: Resolve project scope with --format value; do not parse --json or pipe into jq`,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (isLifecycleSafetyFile(file) &&
|
|
217
|
+
executesGitCommand(line) &&
|
|
218
|
+
/\bgit\s+add\s+-A(?:\s*$|\s*[;&|])/.test(line)) {
|
|
219
|
+
findings.push({
|
|
220
|
+
file,
|
|
221
|
+
message: `Line ${lineNumber}: Project-artifact staging must use exact pathspecs; broad git add -A is forbidden`,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (isLifecycleSafetyFile(file) &&
|
|
225
|
+
executesGitCommand(line) &&
|
|
226
|
+
/\bgit\s+add\b/.test(line) &&
|
|
227
|
+
/(?:\$PROJECT_PATH|\$\{PROJECT_PATH\})(?:\/?(?:\*|\*\*)?)?(?:["'}\s]|$)/.test(line) &&
|
|
228
|
+
!/\$PROJECT_PATH\/[A-Za-z0-9_.-]+|\$\{PROJECT_PATH\}\/[A-Za-z0-9_.-]+/.test(line)) {
|
|
229
|
+
findings.push({
|
|
230
|
+
file,
|
|
231
|
+
message: `Line ${lineNumber}: Project-artifact staging must name exact files, not the project directory or a glob`,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
const pushReceipt = /^\s*([A-Z][A-Z0-9_]*)=\$\(oat project push\b[^\n]*--json\b/.exec(line);
|
|
235
|
+
if (pushReceipt) {
|
|
236
|
+
const outputVariable = pushReceipt[1] ?? '';
|
|
237
|
+
const validationLines = logical
|
|
238
|
+
.slice(logicalIndex + 1, logicalIndex + 5)
|
|
239
|
+
.map((candidate) => candidate.content);
|
|
240
|
+
const parserNeedle = `parse_synced_push_receipt "$${outputVariable}"`;
|
|
241
|
+
const parserOffset = validationLines.findIndex((candidate) => candidate.includes(parserNeedle));
|
|
242
|
+
if (parserOffset === -1) {
|
|
243
|
+
findings.push({
|
|
244
|
+
file,
|
|
245
|
+
message: `Line ${lineNumber}: Synced push JSON receipt must validate status as pushed or up-to-date and require a full SHA before use`,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
for (const [offset, candidate] of validationLines
|
|
250
|
+
.slice(0, parserOffset)
|
|
251
|
+
.entries()) {
|
|
252
|
+
if (candidate.includes(`$${outputVariable}`) &&
|
|
253
|
+
/(?:\.sha\b|\[['"]sha['"]\])/.test(candidate)) {
|
|
254
|
+
findings.push({
|
|
255
|
+
file,
|
|
256
|
+
message: `Line ${lineNumber + offset + 1}: Synced push JSON receipt must not extract or use .sha before parse_synced_push_receipt validation`,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (/\boat\s+project\s+scope\b/.test(line) &&
|
|
263
|
+
/\|\|\s*echo\s+["']?shared\b/.test(line)) {
|
|
264
|
+
findings.push({
|
|
265
|
+
file,
|
|
266
|
+
message: `Line ${lineNumber}: Project scope resolution must fail closed; do not fall back to shared`,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const isPushCommand = executesCommand(line, 'oat') && /\boat\s+project\s+push\b/.test(line);
|
|
270
|
+
const pushFailureHandled = /\|\|\s*(?:\{|exit\b)/.test(line) ||
|
|
271
|
+
((line.match(/"/g)?.length ?? 0) % 2 === 1 &&
|
|
272
|
+
logical
|
|
273
|
+
.slice(logicalIndex + 1, logicalIndex + 16)
|
|
274
|
+
.some((candidate) => /"\s*\|\|\s*(?:\{|exit\b)/.test(candidate.content)));
|
|
275
|
+
if (isPushCommand && !pushFailureHandled) {
|
|
276
|
+
findings.push({
|
|
277
|
+
file,
|
|
278
|
+
message: `Line ${lineNumber}: Project push must handle a nonzero exit explicitly and stop bookkeeping`,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const lines = content.split('\n');
|
|
283
|
+
const logicalByStart = new Map(logical.map((line) => [line.startLine, line.content]));
|
|
284
|
+
const continuedLines = new Set(logical.flatMap((line) => Array.from({ length: line.endLine - line.startLine }, (_, offset) => line.startLine + offset + 1)));
|
|
285
|
+
let fenceMarker = null;
|
|
286
|
+
let scopeGuardSeen = false;
|
|
287
|
+
for (const [index, line] of lines.entries()) {
|
|
288
|
+
const lineNumber = index + 1;
|
|
289
|
+
const fenceMatch = line.match(/^\s*(`{3,}|~{3,})(?:[^`~]*)$/);
|
|
290
|
+
if (fenceMatch) {
|
|
291
|
+
const marker = fenceMatch[1] ?? '';
|
|
292
|
+
if (fenceMarker === null) {
|
|
293
|
+
fenceMarker = marker;
|
|
294
|
+
scopeGuardSeen = false;
|
|
295
|
+
}
|
|
296
|
+
else if (marker[0] === fenceMarker[0] &&
|
|
297
|
+
marker.length >= fenceMarker.length &&
|
|
298
|
+
/^\s*[`~]+\s*$/.test(line)) {
|
|
299
|
+
fenceMarker = null;
|
|
300
|
+
scopeGuardSeen = false;
|
|
301
|
+
}
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
if (continuedLines.has(index))
|
|
305
|
+
continue;
|
|
306
|
+
const inspectedLine = logicalByStart.get(index) ?? line;
|
|
307
|
+
if (fenceMarker === null || !isLifecycleSafetyFile(file)) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (/\boat\s+project\s+scope\b/.test(inspectedLine) &&
|
|
311
|
+
/--format\s+value\b/.test(inspectedLine)) {
|
|
312
|
+
scopeGuardSeen = true;
|
|
313
|
+
}
|
|
314
|
+
if (/\bPROJECT_SCOPE\b/.test(inspectedLine) &&
|
|
315
|
+
/\bsynced\b/.test(inspectedLine)) {
|
|
316
|
+
scopeGuardSeen = true;
|
|
317
|
+
}
|
|
318
|
+
if (/\bgit\s+(?:add|commit)\b/.test(inspectedLine) &&
|
|
319
|
+
referencesProjectArtifactVariable(inspectedLine, artifactArrays) &&
|
|
320
|
+
!scopeGuardSeen) {
|
|
321
|
+
findings.push({
|
|
322
|
+
file,
|
|
323
|
+
message: `Line ${lineNumber}: Project-artifact git writes require an oat project scope --format value guard earlier in the same fenced block`,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function collectSyncedBookkeepingInventoryFindings(repoRoot, safetyFiles, findings) {
|
|
329
|
+
const inventoryPath = join(repoRoot, 'packages', 'cli', 'src', 'validation', 'synced-bookkeeping-sites.json');
|
|
330
|
+
if (!(await isFile(inventoryPath))) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
let sites;
|
|
334
|
+
try {
|
|
335
|
+
const parsed = JSON.parse(await readFile(inventoryPath, 'utf8'));
|
|
336
|
+
if (!Array.isArray(parsed)) {
|
|
337
|
+
throw new Error('expected a JSON array');
|
|
338
|
+
}
|
|
339
|
+
sites = parsed;
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
findings.push({
|
|
343
|
+
file: inventoryPath,
|
|
344
|
+
message: `Invalid synced-bookkeeping inventory: ${error instanceof Error ? error.message : String(error)}`,
|
|
345
|
+
});
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
const inventoriedWriterSites = new Map();
|
|
349
|
+
for (const site of sites) {
|
|
350
|
+
if (typeof site?.file !== 'string' ||
|
|
351
|
+
typeof site?.anchor !== 'string' ||
|
|
352
|
+
typeof site?.guard !== 'string' ||
|
|
353
|
+
!['resolve', 'arrival', 'write'].includes(site?.kind)) {
|
|
354
|
+
findings.push({
|
|
355
|
+
file: inventoryPath,
|
|
356
|
+
message: 'Each synced-bookkeeping inventory entry requires file, unique anchor, companion guard, and kind (resolve | arrival | write)',
|
|
357
|
+
});
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
const sitePath = join(repoRoot, site.file);
|
|
361
|
+
let content;
|
|
362
|
+
try {
|
|
363
|
+
content = await readFile(sitePath, 'utf8');
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
findings.push({
|
|
367
|
+
file: inventoryPath,
|
|
368
|
+
message: `Missing synced-bookkeeping inventory file: ${sitePath}`,
|
|
369
|
+
});
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
const occurrences = countOccurrences(content, site.anchor);
|
|
373
|
+
if (occurrences === 0) {
|
|
374
|
+
findings.push({
|
|
375
|
+
file: inventoryPath,
|
|
376
|
+
message: `Stale synced-bookkeeping inventory anchor in ${sitePath}: ${site.anchor}`,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
else if (occurrences > 1) {
|
|
380
|
+
findings.push({
|
|
381
|
+
file: inventoryPath,
|
|
382
|
+
message: `Synced-bookkeeping inventory anchor is not unique in ${sitePath}: ${site.anchor}`,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
if (occurrences === 1) {
|
|
386
|
+
const fencedSites = fencedContentSites(content);
|
|
387
|
+
const anchorOffset = content.indexOf(site.anchor);
|
|
388
|
+
const anchorSite = contentSiteAt(content, anchorOffset, fencedSites);
|
|
389
|
+
const companionGuardPresent = site.kind === 'write' && site.guard === 'project-scope'
|
|
390
|
+
? /\boat\s+project\s+scope\b[^\n]*--format\s+value\b|\bPROJECT_SCOPE\b[^\n]*\bsynced\b/.test(anchorSite.content)
|
|
391
|
+
: site.kind === 'arrival' && site.guard === 'materialized-arrival'
|
|
392
|
+
? /\boat\s+project\s+pull\b|pulled and materialized/i.test(anchorSite.content)
|
|
393
|
+
: site.kind === 'resolve' && site.guard === 'canonical-resolution'
|
|
394
|
+
? /\bresolve(?:Project|NamedProject|SyncedTarget|ProjectScope|ActiveProject)\b|\bprojectScope\s*===\s*['"]synced['"]|options\.remote[^\n]*synced|\boat\s+project\s+new\b[^\n]*--scope\b|project:synced_tracked_artifacts|\boat\s+config\s+get\s+activeProject\b/.test(anchorSite.content)
|
|
395
|
+
: anchorSite.content.includes(site.guard);
|
|
396
|
+
if (!companionGuardPresent) {
|
|
397
|
+
findings.push({
|
|
398
|
+
file: inventoryPath,
|
|
399
|
+
message: `Synced-bookkeeping ${site.kind} site lacks its companion guard in the same function or fenced block: ${sitePath}: ${site.anchor}`,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
if (site.kind === 'write') {
|
|
403
|
+
const fileSites = inventoriedWriterSites.get(sitePath) ?? [];
|
|
404
|
+
fileSites.push(anchorSite);
|
|
405
|
+
inventoriedWriterSites.set(sitePath, fileSites);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
for (const file of safetyFiles) {
|
|
410
|
+
const content = await readFile(file, 'utf8');
|
|
411
|
+
if (!isLifecycleSafetyFile(file))
|
|
412
|
+
continue;
|
|
413
|
+
const inventoriedSites = inventoriedWriterSites.get(file) ?? [];
|
|
414
|
+
const writerSites = projectArtifactWriterSites(content);
|
|
415
|
+
const artifactArrays = projectArtifactArrays(content);
|
|
416
|
+
if (writerSites.length > 0 && inventoriedSites.length === 0) {
|
|
417
|
+
findings.push({
|
|
418
|
+
file: inventoryPath,
|
|
419
|
+
message: `Lifecycle project-artifact writer is missing from synced-bookkeeping inventory: ${file}`,
|
|
420
|
+
});
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
for (const writerSite of writerSites) {
|
|
424
|
+
if (inventoriedSites.some((site) => site.start === writerSite.start && site.end === writerSite.end)) {
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
const writer = writerSite.content
|
|
428
|
+
.split('\n')
|
|
429
|
+
.find((line) => isProjectArtifactWriterLine(line, artifactArrays))
|
|
430
|
+
?.trim();
|
|
431
|
+
findings.push({
|
|
432
|
+
file: inventoryPath,
|
|
433
|
+
message: `Lifecycle project-artifact writer site is missing from synced-bookkeeping inventory: ${file}: ${writer ?? '(unknown writer)'}`,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async function collectSyncedSafetyFindings(repoRoot, oatSkillDirs, findings) {
|
|
439
|
+
const skillFiles = (await Promise.all(oatSkillDirs.map((dir) => listMarkdownFiles(join(repoRoot, '.agents', 'skills', dir))))).flat();
|
|
440
|
+
const phaseImplementerPath = join(repoRoot, '.agents', 'agents', 'oat-phase-implementer.md');
|
|
441
|
+
const safetyFiles = (await isFile(phaseImplementerPath))
|
|
442
|
+
? [...skillFiles, phaseImplementerPath]
|
|
443
|
+
: skillFiles;
|
|
444
|
+
for (const file of safetyFiles) {
|
|
445
|
+
collectSyncedContentFindings(file, await readFile(file, 'utf8'), findings);
|
|
446
|
+
}
|
|
447
|
+
await collectSyncedBookkeepingInventoryFindings(repoRoot, safetyFiles, findings);
|
|
448
|
+
}
|
|
16
449
|
function frontmatterHasKey(frontmatter, key) {
|
|
17
450
|
const re = new RegExp(`^${key}:`, 'm');
|
|
18
451
|
return re.test(frontmatter);
|
|
@@ -299,6 +732,7 @@ export async function validateOatSkills(repoRoot, options = {}, dependencies = {
|
|
|
299
732
|
validateQuickStartSemantics(skillPath, content, findings);
|
|
300
733
|
}
|
|
301
734
|
}
|
|
735
|
+
await collectSyncedSafetyFindings(repoRoot, oatSkillDirs, findings);
|
|
302
736
|
await collectGateabilityFindings(skillsRoot, options.gateSkillNames, findings);
|
|
303
737
|
if (options.baseRef) {
|
|
304
738
|
const changedSkillFiles = await listChangedSkillFiles(repoRoot, options.baseRef, dependencies);
|
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.46",
|
|
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.46"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"node": ">=22.17.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
|
-
"build": "bash scripts/bundle-assets.sh && tsc && tsc-alias && chmod +x dist/index.js && touch dist/index.js",
|
|
51
|
+
"build": "bash scripts/bundle-assets.sh && { [ ! -d dist ] || find dist -type d -name __tests__ -prune -exec rm -rf {} +; } && tsc && tsc-alias && chmod +x dist/index.js && touch dist/index.js",
|
|
52
52
|
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
53
53
|
"dev": "tsx watch src/index.ts",
|
|
54
54
|
"check": "oxlint . && oxlint --type-aware -A all -D typescript/no-floating-promises -D typescript/no-misused-promises --ignore-pattern '**/*.test.ts' --ignore-pattern '**/*.spec.ts' . && oxfmt --check .",
|
|
@@ -60,6 +60,6 @@
|
|
|
60
60
|
"test": "vitest run",
|
|
61
61
|
"test:watch": "vitest",
|
|
62
62
|
"test:coverage": "vitest run --coverage",
|
|
63
|
-
"type-check": "tsc --noEmit"
|
|
63
|
+
"type-check": "tsc --noEmit && tsc -p tsconfig.test-support.json"
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { type SplitChildInput, type SplitPayload, type SplitPlanDocument } from '../../projects/split/child-plan.js';
|
|
2
|
-
import { type Signal } from '../../projects/split/signals.js';
|
|
3
|
-
export type TranscriptSpeaker = 'assistant' | 'user';
|
|
4
|
-
export interface TranscriptTurn {
|
|
5
|
-
speaker: TranscriptSpeaker;
|
|
6
|
-
text: string;
|
|
7
|
-
}
|
|
8
|
-
export type StubbedUserChoice = 'broad-first' | 'children-known' | 'decompose-children' | 'inline-only' | 'keep-one-project' | 'promote-n-projects' | 'split-now';
|
|
9
|
-
export interface StubbedQuestion {
|
|
10
|
-
id: string;
|
|
11
|
-
prompt: string;
|
|
12
|
-
options: string[];
|
|
13
|
-
response: StubbedUserChoice;
|
|
14
|
-
}
|
|
15
|
-
export interface TranscriptFixture {
|
|
16
|
-
transcript: TranscriptTurn[];
|
|
17
|
-
parentSlug: string;
|
|
18
|
-
children: SplitChildInput[];
|
|
19
|
-
discoveryPath?: string;
|
|
20
|
-
inheritedContext?: string;
|
|
21
|
-
integrationSketch?: string;
|
|
22
|
-
}
|
|
23
|
-
export declare const SPLIT_HANDOFF_TARGET: {
|
|
24
|
-
readonly skill: 'oat-project-split';
|
|
25
|
-
readonly hookCreatesProjects: false;
|
|
26
|
-
readonly responsibilities: readonly ['normalize-split-plan', 'write-coordination-parent', 'scaffold-children', 'activate-initial-child'];
|
|
27
|
-
};
|
|
28
|
-
export declare function inferSplitSignals(transcript: TranscriptTurn[]): Signal[];
|
|
29
|
-
export declare function runDiscoverDetectionFixture(fixture: TranscriptFixture, responses: Record<string, StubbedUserChoice>): {
|
|
30
|
-
asked: StubbedQuestion[];
|
|
31
|
-
confidence: 'below' | 'high' | 'soft';
|
|
32
|
-
decision?: StubbedUserChoice;
|
|
33
|
-
document?: SplitPlanDocument;
|
|
34
|
-
payload?: SplitPayload;
|
|
35
|
-
triggered: boolean;
|
|
36
|
-
};
|
|
37
|
-
export declare function runDeclaredBrainstormFixture(fixture: TranscriptFixture, responses: Record<string, StubbedUserChoice>): {
|
|
38
|
-
asked: StubbedQuestion[];
|
|
39
|
-
document?: SplitPlanDocument;
|
|
40
|
-
handoffTarget: typeof SPLIT_HANDOFF_TARGET;
|
|
41
|
-
payload?: SplitPayload;
|
|
42
|
-
};
|
|
43
|
-
export declare function runBrainstormPickerFixture(fixture: TranscriptFixture, responses: Record<string, StubbedUserChoice>): {
|
|
44
|
-
asked: StubbedQuestion[];
|
|
45
|
-
document?: SplitPlanDocument;
|
|
46
|
-
handoffTarget: typeof SPLIT_HANDOFF_TARGET;
|
|
47
|
-
options: string[];
|
|
48
|
-
payload?: SplitPayload;
|
|
49
|
-
};
|
|
50
|
-
//# sourceMappingURL=split-flow-fixtures.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"split-flow-fixtures.d.ts","sourceRoot":"","sources":["../../../src/__tests__/skills/split-flow-fixtures.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,8BAA8B,CAAC;AAE5E,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,iBAAiB,GACzB,aAAa,GACb,gBAAgB,GAChB,oBAAoB,GACpB,aAAa,GACb,kBAAkB,GAClB,oBAAoB,GACpB,WAAW,CAAC;AAEhB,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,iBAAiB,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,oBAAoB;aAC/B,KAAK,EAAE,mBAAmB;aAC1B,mBAAmB;aACnB,gBAAgB,YACd,sBAAsB,EACtB,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB;CAElB,CAAC;AAsBX,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE,CA+BxE;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,iBAAiB,EAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC3C;IACD,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB,CAqDA;AAED,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,iBAAiB,EAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC3C;IACD,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,aAAa,EAAE,OAAO,oBAAoB,CAAC;IAC3C,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,CAmCA;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iBAAiB,EAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC3C;IACD,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,aAAa,EAAE,OAAO,oBAAoB,CAAC;IAC3C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,CA4CA"}
|