@open-agent-toolkit/cli 0.1.4 → 0.1.6

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.
Files changed (79) hide show
  1. package/assets/docs/workflows/projects/artifacts.md +17 -0
  2. package/assets/docs/workflows/projects/implementation-execution.md +2 -2
  3. package/assets/docs/workflows/projects/index.md +3 -0
  4. package/assets/docs/workflows/projects/splitting.md +79 -0
  5. package/assets/docs/workflows/skills/index.md +2 -0
  6. package/assets/public-package-versions.json +4 -4
  7. package/assets/skills/oat-brainstorm/SKILL.md +43 -3
  8. package/assets/skills/oat-project-discover/SKILL.md +72 -8
  9. package/assets/skills/oat-project-implement/SKILL.md +8 -4
  10. package/assets/skills/oat-project-quick-start/SKILL.md +14 -5
  11. package/assets/skills/oat-project-split/SKILL.md +82 -0
  12. package/assets/templates/state.md +6 -1
  13. package/dist/__tests__/skills/split-flow-fixtures.d.ts +50 -0
  14. package/dist/__tests__/skills/split-flow-fixtures.d.ts.map +1 -0
  15. package/dist/__tests__/skills/split-flow-fixtures.js +161 -0
  16. package/dist/commands/init/tools/shared/skill-manifest.d.ts +1 -1
  17. package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
  18. package/dist/commands/init/tools/shared/skill-manifest.js +1 -0
  19. package/dist/commands/project/complete-discovery/index.d.ts +16 -0
  20. package/dist/commands/project/complete-discovery/index.d.ts.map +1 -0
  21. package/dist/commands/project/complete-discovery/index.js +123 -0
  22. package/dist/commands/project/complete-state/index.d.ts.map +1 -1
  23. package/dist/commands/project/complete-state/index.js +5 -0
  24. package/dist/commands/project/index.d.ts.map +1 -1
  25. package/dist/commands/project/index.js +4 -0
  26. package/dist/commands/project/list.d.ts +6 -0
  27. package/dist/commands/project/list.d.ts.map +1 -1
  28. package/dist/commands/project/list.js +37 -4
  29. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  30. package/dist/commands/project/new/scaffold.js +4 -0
  31. package/dist/commands/project/open/index.d.ts.map +1 -1
  32. package/dist/commands/project/open/index.js +9 -3
  33. package/dist/commands/project/pause/index.d.ts.map +1 -1
  34. package/dist/commands/project/pause/index.js +7 -1
  35. package/dist/commands/project/split/evaluate-signals.d.ts +8 -0
  36. package/dist/commands/project/split/evaluate-signals.d.ts.map +1 -0
  37. package/dist/commands/project/split/evaluate-signals.js +47 -0
  38. package/dist/commands/project/split/index.d.ts +3 -0
  39. package/dist/commands/project/split/index.d.ts.map +1 -0
  40. package/dist/commands/project/split/index.js +11 -0
  41. package/dist/commands/project/split/run.d.ts +21 -0
  42. package/dist/commands/project/split/run.d.ts.map +1 -0
  43. package/dist/commands/project/split/run.js +231 -0
  44. package/dist/commands/project/split/validate-plan.d.ts +14 -0
  45. package/dist/commands/project/split/validate-plan.d.ts.map +1 -0
  46. package/dist/commands/project/split/validate-plan.js +62 -0
  47. package/dist/commands/shared/frontmatter.d.ts +9 -0
  48. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  49. package/dist/commands/shared/frontmatter.js +46 -0
  50. package/dist/commands/state/generate.d.ts.map +1 -1
  51. package/dist/commands/state/generate.js +38 -6
  52. package/dist/projects/split/child-plan.d.ts +46 -0
  53. package/dist/projects/split/child-plan.d.ts.map +1 -0
  54. package/dist/projects/split/child-plan.js +107 -0
  55. package/dist/projects/split/document-validation.d.ts +14 -0
  56. package/dist/projects/split/document-validation.d.ts.map +1 -0
  57. package/dist/projects/split/document-validation.js +106 -0
  58. package/dist/projects/split/finalize.d.ts +7 -0
  59. package/dist/projects/split/finalize.d.ts.map +1 -0
  60. package/dist/projects/split/finalize.js +32 -0
  61. package/dist/projects/split/resume.d.ts +19 -0
  62. package/dist/projects/split/resume.d.ts.map +1 -0
  63. package/dist/projects/split/resume.js +107 -0
  64. package/dist/projects/split/seed-children.d.ts +9 -0
  65. package/dist/projects/split/seed-children.d.ts.map +1 -0
  66. package/dist/projects/split/seed-children.js +122 -0
  67. package/dist/projects/split/signals.d.ts +10 -0
  68. package/dist/projects/split/signals.d.ts.map +1 -0
  69. package/dist/projects/split/signals.js +18 -0
  70. package/dist/projects/split/validation.d.ts +14 -0
  71. package/dist/projects/split/validation.d.ts.map +1 -0
  72. package/dist/projects/split/validation.js +104 -0
  73. package/dist/projects/split/write-parent.d.ts +16 -0
  74. package/dist/projects/split/write-parent.d.ts.map +1 -0
  75. package/dist/projects/split/write-parent.js +176 -0
  76. package/dist/validation/project-state.d.ts +50 -0
  77. package/dist/validation/project-state.d.ts.map +1 -0
  78. package/dist/validation/project-state.js +279 -0
  79. package/package.json +2 -2
@@ -0,0 +1,161 @@
1
+ import { buildSplitPlanDocument, } from '../../projects/split/child-plan.js';
2
+ import { evaluateSignals } from '../../projects/split/signals.js';
3
+ export const SPLIT_HANDOFF_TARGET = {
4
+ skill: 'oat-project-split',
5
+ hookCreatesProjects: false,
6
+ responsibilities: [
7
+ 'normalize-split-plan',
8
+ 'write-coordination-parent',
9
+ 'scaffold-children',
10
+ 'activate-initial-child',
11
+ ],
12
+ };
13
+ class AskUserQuestionStub {
14
+ responses;
15
+ asked = [];
16
+ constructor(responses) {
17
+ this.responses = responses;
18
+ }
19
+ ask(id, prompt, options) {
20
+ const response = this.responses[id];
21
+ if (!response) {
22
+ throw new Error(`Missing stubbed response for AskUserQuestion ${id}`);
23
+ }
24
+ this.asked.push({ id, prompt, options, response });
25
+ return response;
26
+ }
27
+ }
28
+ function transcriptText(transcript) {
29
+ return transcript.map((turn) => `${turn.speaker}: ${turn.text}`).join('\n');
30
+ }
31
+ export function inferSplitSignals(transcript) {
32
+ const text = transcriptText(transcript).toLowerCase();
33
+ const signals = new Set();
34
+ if (text.includes('independently shippable') ||
35
+ text.includes('ship independently')) {
36
+ signals.add('independently-shippable');
37
+ }
38
+ if (text.includes('no shared design surface') ||
39
+ text.includes('separate design surfaces')) {
40
+ signals.add('no-shared-design-surface');
41
+ }
42
+ if (text.includes('separate pr') || text.includes('separate pull request')) {
43
+ signals.add('expect-separate-prs');
44
+ }
45
+ if (text.includes('distinct subsystem') ||
46
+ text.includes('different package') ||
47
+ text.includes('different packages')) {
48
+ signals.add('distinct-subsystems');
49
+ }
50
+ return [...signals];
51
+ }
52
+ export function runDiscoverDetectionFixture(fixture, responses) {
53
+ const evaluation = evaluateSignals({
54
+ fired: inferSplitSignals(fixture.transcript),
55
+ });
56
+ const askUser = new AskUserQuestionStub(responses);
57
+ if (!evaluation.triggered) {
58
+ return {
59
+ asked: askUser.asked,
60
+ confidence: evaluation.confidence,
61
+ triggered: false,
62
+ };
63
+ }
64
+ const prompt = evaluation.confidence === 'high'
65
+ ? 'This looks like multiple independent projects. Split now, do one round of broad cross-cutting discovery first, or keep this as one project?'
66
+ : 'This may be multiple projects. Split, do one round of broad cross-cutting discovery first, or keep it as one project?';
67
+ const decision = askUser.ask('discover-split-offer', prompt, [
68
+ 'split-now',
69
+ 'broad-first',
70
+ 'keep-one-project',
71
+ ]);
72
+ if (decision !== 'split-now') {
73
+ return {
74
+ asked: askUser.asked,
75
+ confidence: evaluation.confidence,
76
+ decision,
77
+ triggered: true,
78
+ };
79
+ }
80
+ const payload = {
81
+ origin: 'detected-mid-stream',
82
+ interactive: true,
83
+ inferredChildren: fixture.children,
84
+ priorDiscovery: {
85
+ path: fixture.discoveryPath ?? `.oat/projects/shared/${fixture.parentSlug}`,
86
+ inheritedContext: fixture.inheritedContext,
87
+ integrationSketch: fixture.integrationSketch,
88
+ },
89
+ };
90
+ return {
91
+ asked: askUser.asked,
92
+ confidence: evaluation.confidence,
93
+ decision,
94
+ document: buildSplitPlanDocument(payload),
95
+ payload,
96
+ triggered: true,
97
+ };
98
+ }
99
+ export function runDeclaredBrainstormFixture(fixture, responses) {
100
+ const text = transcriptText(fixture.transcript).toLowerCase();
101
+ const declared = text.includes('multiple projects') ||
102
+ text.includes('umbrella project') ||
103
+ text.includes('several sub-projects');
104
+ if (!declared) {
105
+ return { asked: [], handoffTarget: SPLIT_HANDOFF_TARGET };
106
+ }
107
+ const askUser = new AskUserQuestionStub(responses);
108
+ const decision = askUser.ask('declared-brainstorm-boundary', 'Do you already know the child projects, or should we decompose the scope together?', ['children-known', 'decompose-children']);
109
+ if (decision !== 'children-known') {
110
+ return { asked: askUser.asked, handoffTarget: SPLIT_HANDOFF_TARGET };
111
+ }
112
+ const payload = {
113
+ origin: 'declared',
114
+ parentSlug: fixture.parentSlug,
115
+ declaredChildren: fixture.children,
116
+ interactive: true,
117
+ integrationSketch: fixture.integrationSketch,
118
+ };
119
+ return {
120
+ asked: askUser.asked,
121
+ document: buildSplitPlanDocument(payload),
122
+ handoffTarget: SPLIT_HANDOFF_TARGET,
123
+ payload,
124
+ };
125
+ }
126
+ export function runBrainstormPickerFixture(fixture, responses) {
127
+ const evaluation = evaluateSignals({
128
+ fired: inferSplitSignals(fixture.transcript),
129
+ });
130
+ const options = ['inline-only', 'doc-to-path', 'promote-to-new-oat-project'];
131
+ if (evaluation.triggered) {
132
+ options.push('promote-n-projects');
133
+ }
134
+ const askUser = new AskUserQuestionStub(responses);
135
+ const decision = askUser.ask('brainstorm-terminal-state', 'Where should this brainstorm land?', options);
136
+ if (decision !== 'promote-n-projects' || !evaluation.triggered) {
137
+ return {
138
+ asked: askUser.asked,
139
+ handoffTarget: SPLIT_HANDOFF_TARGET,
140
+ options,
141
+ };
142
+ }
143
+ const payload = {
144
+ origin: 'brainstorm-picker',
145
+ parentSlug: fixture.parentSlug,
146
+ inferredChildren: fixture.children,
147
+ interactive: true,
148
+ priorDiscovery: {
149
+ path: `brainstorm/${fixture.parentSlug}`,
150
+ inheritedContext: fixture.inheritedContext,
151
+ integrationSketch: fixture.integrationSketch,
152
+ },
153
+ };
154
+ return {
155
+ asked: askUser.asked,
156
+ document: buildSplitPlanDocument(payload),
157
+ handoffTarget: SPLIT_HANDOFF_TARGET,
158
+ options,
159
+ payload,
160
+ };
161
+ }
@@ -11,7 +11,7 @@ export interface PackMetadata {
11
11
  }
12
12
  export declare const PACK_METADATA: Record<string, PackMetadata>;
13
13
  export declare function resolvePackDefaultScope(packName: string): 'user' | 'project';
14
- export declare const WORKFLOW_SKILLS: readonly ["oat-project-capture", "oat-project-clear-active", "oat-project-complete", "oat-project-design", "oat-project-discover", "oat-project-document", "oat-project-implement", "oat-project-import-plan", "oat-project-new", "oat-project-next", "oat-project-open", "oat-project-plan", "oat-project-plan-writing", "oat-project-pr-final", "oat-project-pr-progress", "oat-project-progress", "oat-project-promote-spec-driven", "oat-project-quick-start", "oat-project-reconcile", "oat-project-revise", "oat-project-review-provide", "oat-project-review-receive", "oat-project-review-receive-remote", "oat-project-spec", "oat-project-summary", "oat-repo-knowledge-index", "oat-worktree-bootstrap", "oat-worktree-bootstrap-auto", "oat-wrap-up"];
14
+ export declare const WORKFLOW_SKILLS: readonly ["oat-project-capture", "oat-project-clear-active", "oat-project-complete", "oat-project-design", "oat-project-discover", "oat-project-document", "oat-project-implement", "oat-project-import-plan", "oat-project-new", "oat-project-next", "oat-project-open", "oat-project-plan", "oat-project-plan-writing", "oat-project-pr-final", "oat-project-pr-progress", "oat-project-progress", "oat-project-promote-spec-driven", "oat-project-quick-start", "oat-project-reconcile", "oat-project-revise", "oat-project-review-provide", "oat-project-review-receive", "oat-project-review-receive-remote", "oat-project-spec", "oat-project-split", "oat-project-summary", "oat-repo-knowledge-index", "oat-worktree-bootstrap", "oat-worktree-bootstrap-auto", "oat-wrap-up"];
15
15
  export declare const WORKFLOW_AGENTS: readonly ["oat-codebase-mapper.md", "oat-phase-implementer.md", "oat-reviewer.md"];
16
16
  export declare const WORKFLOW_TEMPLATES: readonly ["state.md", "discovery.md", "spec.md", "design.md", "plan.md", "implementation.md", "summary.md"];
17
17
  export declare const WORKFLOW_SCRIPTS: readonly ["generate-oat-state.sh", "generate-thin-index.sh", "resolve-tracking.sh"];
@@ -1 +1 @@
1
- {"version":3,"file":"skill-manifest.d.ts","sourceRoot":"","sources":["../../../../../src/commands/init/tools/shared/skill-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgBH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAKtD,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE5E;AAID,eAAO,MAAM,eAAe,muBA8BlB,CAAC;AAEX,eAAO,MAAM,eAAe,oFAIlB,CAAC;AAEX,eAAO,MAAM,kBAAkB,6GAQrB,CAAC;AAEX,eAAO,MAAM,gBAAgB,qFAInB,CAAC;AAIX,eAAO,MAAM,WAAW,2FAKd,CAAC;AAIX,eAAO,MAAM,WAAW,qCAAsC,CAAC;AAI/D,eAAO,MAAM,WAAW,yIAMd,CAAC;AAEX,eAAO,MAAM,YAAY,kCAAmC,CAAC;AAI7D,eAAO,MAAM,cAAc,gJAMjB,CAAC;AAIX,eAAO,MAAM,yBAAyB,kGAI5B,CAAC;AAEX,eAAO,MAAM,4BAA4B,4CAG/B,CAAC;AAEX,eAAO,MAAM,0BAA0B,aAAc,CAAC;AAItD,eAAO,MAAM,iBAAiB,6BAA8B,CAAC;AAI7D,eAAO,MAAM,eAAe,2EAMlB,CAAC;AAEX,eAAO,MAAM,eAAe,qCAAsC,CAAC"}
1
+ {"version":3,"file":"skill-manifest.d.ts","sourceRoot":"","sources":["../../../../../src/commands/init/tools/shared/skill-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgBH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAKtD,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE5E;AAID,eAAO,MAAM,eAAe,wvBA+BlB,CAAC;AAEX,eAAO,MAAM,eAAe,oFAIlB,CAAC;AAEX,eAAO,MAAM,kBAAkB,6GAQrB,CAAC;AAEX,eAAO,MAAM,gBAAgB,qFAInB,CAAC;AAIX,eAAO,MAAM,WAAW,2FAKd,CAAC;AAIX,eAAO,MAAM,WAAW,qCAAsC,CAAC;AAI/D,eAAO,MAAM,WAAW,yIAMd,CAAC;AAEX,eAAO,MAAM,YAAY,kCAAmC,CAAC;AAI7D,eAAO,MAAM,cAAc,gJAMjB,CAAC;AAIX,eAAO,MAAM,yBAAyB,kGAI5B,CAAC;AAEX,eAAO,MAAM,4BAA4B,4CAG/B,CAAC;AAEX,eAAO,MAAM,0BAA0B,aAAc,CAAC;AAItD,eAAO,MAAM,iBAAiB,6BAA8B,CAAC;AAI7D,eAAO,MAAM,eAAe,2EAMlB,CAAC;AAEX,eAAO,MAAM,eAAe,qCAAsC,CAAC"}
@@ -40,6 +40,7 @@ export const WORKFLOW_SKILLS = [
40
40
  'oat-project-review-receive',
41
41
  'oat-project-review-receive-remote',
42
42
  'oat-project-spec',
43
+ 'oat-project-split',
43
44
  'oat-project-summary',
44
45
  'oat-repo-knowledge-index',
45
46
  'oat-worktree-bootstrap',
@@ -0,0 +1,16 @@
1
+ import { readFile as defaultReadFile, writeFile as defaultWriteFile } from 'node:fs/promises';
2
+ import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
3
+ import { dirExists, fileExists } from '../../../fs/io.js';
4
+ import { Command } from 'commander';
5
+ interface ProjectCompleteDiscoveryDependencies {
6
+ buildCommandContext: (options: GlobalOptions) => CommandContext;
7
+ resolveProjectRoot: (cwd: string) => Promise<string>;
8
+ readFile: typeof defaultReadFile;
9
+ writeFile: typeof defaultWriteFile;
10
+ dirExists: typeof dirExists;
11
+ fileExists: typeof fileExists;
12
+ now: () => Date;
13
+ }
14
+ export declare function createProjectCompleteDiscoveryCommand(overrides?: Partial<ProjectCompleteDiscoveryDependencies>): Command;
15
+ export {};
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/complete-discovery/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,UAAU,oCAAoC;IAC5C,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAkJD,wBAAgB,qCAAqC,CACnD,SAAS,GAAE,OAAO,CAAC,oCAAoC,CAAM,GAC5D,OAAO,CA+BT"}
@@ -0,0 +1,123 @@
1
+ import { readFile as defaultReadFile, writeFile as defaultWriteFile, } from 'node:fs/promises';
2
+ import { isAbsolute, join } from 'node:path';
3
+ import { buildCommandContext, } from '../../../app/command-context.js';
4
+ import { getFrontmatterBlock } from '../../shared/frontmatter.js';
5
+ import { readGlobalOptions } from '../../shared/shared.utils.js';
6
+ import { CliError } from '../../../errors/cli-error.js';
7
+ import { dirExists, fileExists } from '../../../fs/io.js';
8
+ import { resolveProjectRoot } from '../../../fs/paths.js';
9
+ import { assertValidProjectStateFilesystemContent } from '../../../validation/project-state.js';
10
+ import { Command } from 'commander';
11
+ import YAML from 'yaml';
12
+ const DEFAULT_DEPENDENCIES = {
13
+ buildCommandContext,
14
+ resolveProjectRoot,
15
+ readFile: defaultReadFile,
16
+ writeFile: defaultWriteFile,
17
+ dirExists,
18
+ fileExists,
19
+ now: () => new Date(),
20
+ };
21
+ function resolveTargetProjectPath(repoRoot, projectPath) {
22
+ return isAbsolute(projectPath) ? projectPath : join(repoRoot, projectPath);
23
+ }
24
+ function parseFrontmatterObject(content, filePath) {
25
+ const frontmatter = getFrontmatterBlock(content);
26
+ if (!frontmatter) {
27
+ throw new CliError(`${filePath} is missing frontmatter`, 1);
28
+ }
29
+ const parsed = YAML.parse(frontmatter);
30
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
31
+ throw new CliError(`${filePath} frontmatter must be a YAML object`, 1);
32
+ }
33
+ return parsed;
34
+ }
35
+ function renderDiscoveryComplete(content, filePath, options) {
36
+ const frontmatter = parseFrontmatterObject(content, filePath);
37
+ frontmatter['oat_status'] = 'complete';
38
+ frontmatter['oat_ready_for'] = options.readyFor;
39
+ frontmatter['oat_last_updated'] = options.today;
40
+ const renderedFrontmatter = YAML.stringify(frontmatter).trimEnd();
41
+ return content.replace(/^---\n[\s\S]*?\n---/, `---\n${renderedFrontmatter}\n---`);
42
+ }
43
+ function renderFrontmatterDocument(frontmatter) {
44
+ return `---\n${YAML.stringify(frontmatter).trimEnd()}\n---\n`;
45
+ }
46
+ async function buildDiscoveryValidationContent(content, options) {
47
+ const discoveryFrontmatter = parseFrontmatterObject(content, options.discoveryPath);
48
+ const statePath = join(options.projectPath, 'state.md');
49
+ if (!(await options.dependencies.fileExists(statePath))) {
50
+ return content;
51
+ }
52
+ const stateContent = await options.dependencies.readFile(statePath, 'utf8');
53
+ const stateFrontmatter = parseFrontmatterObject(stateContent, statePath);
54
+ return renderFrontmatterDocument({
55
+ ...stateFrontmatter,
56
+ ...discoveryFrontmatter,
57
+ });
58
+ }
59
+ async function runProjectCompleteDiscovery(projectPath, options, context, dependencies) {
60
+ try {
61
+ const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
62
+ const targetProjectPath = resolveTargetProjectPath(repoRoot, projectPath);
63
+ if (!(await dependencies.dirExists(targetProjectPath))) {
64
+ throw new CliError(`Project not found: ${projectPath}`, 1);
65
+ }
66
+ const discoveryPath = join(targetProjectPath, 'discovery.md');
67
+ if (!(await dependencies.fileExists(discoveryPath))) {
68
+ throw new CliError(`Project discovery.md not found: ${discoveryPath}`, 1);
69
+ }
70
+ const now = dependencies.now();
71
+ const content = await dependencies.readFile(discoveryPath, 'utf8');
72
+ const updatedContent = renderDiscoveryComplete(content, discoveryPath, {
73
+ readyFor: options.readyFor ?? 'oat-project-design',
74
+ today: now.toISOString().slice(0, 10),
75
+ });
76
+ const validationContent = await buildDiscoveryValidationContent(updatedContent, {
77
+ discoveryPath,
78
+ projectPath: targetProjectPath,
79
+ dependencies,
80
+ });
81
+ await assertValidProjectStateFilesystemContent(validationContent, {
82
+ filePath: discoveryPath,
83
+ projectPath: targetProjectPath,
84
+ });
85
+ await dependencies.writeFile(discoveryPath, updatedContent, 'utf8');
86
+ if (context.json) {
87
+ context.logger.json({
88
+ status: 'ok',
89
+ projectPath,
90
+ discoveryPath,
91
+ readyFor: options.readyFor ?? 'oat-project-design',
92
+ });
93
+ }
94
+ else {
95
+ context.logger.info(`Completed project discovery: ${projectPath}`);
96
+ }
97
+ process.exitCode = 0;
98
+ }
99
+ catch (error) {
100
+ const message = error instanceof Error ? error.message : String(error);
101
+ if (context.json) {
102
+ context.logger.json({ status: 'error', message });
103
+ }
104
+ else {
105
+ context.logger.error(message);
106
+ }
107
+ process.exitCode = error instanceof CliError ? error.exitCode : 1;
108
+ }
109
+ }
110
+ export function createProjectCompleteDiscoveryCommand(overrides = {}) {
111
+ const dependencies = {
112
+ ...DEFAULT_DEPENDENCIES,
113
+ ...overrides,
114
+ };
115
+ return new Command('complete-discovery')
116
+ .description('Validate and mark a project discovery.md complete')
117
+ .argument('<project-path>', 'Project path to update')
118
+ .option('--ready-for <skill>', 'Value to write to discovery.md oat_ready_for', 'oat-project-design')
119
+ .action(async (projectPath, options, command) => {
120
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
121
+ await runProjectCompleteDiscovery(projectPath, options, context, dependencies);
122
+ });
123
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/complete-state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,UAAU,gCAAgC;IACxC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAsED,wBAAgB,iCAAiC,CAC/C,SAAS,GAAE,OAAO,CAAC,gCAAgC,CAAM,GACxD,OAAO,CA2BT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/complete-state/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,UAAU,gCAAgC;IACxC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AA0ED,wBAAgB,iCAAiC,CAC/C,SAAS,GAAE,OAAO,CAAC,gCAAgC,CAAM,GACxD,OAAO,CA2BT"}
@@ -5,6 +5,7 @@ import { readGlobalOptions } from '../../shared/shared.utils.js';
5
5
  import { CliError } from '../../../errors/cli-error.js';
6
6
  import { dirExists, fileExists } from '../../../fs/io.js';
7
7
  import { resolveProjectRoot } from '../../../fs/paths.js';
8
+ import { assertValidProjectStateFilesystemContent } from '../../../validation/project-state.js';
8
9
  import { Command } from 'commander';
9
10
  import { renderCompletedProjectState } from './state-utils.js';
10
11
  const DEFAULT_DEPENDENCIES = {
@@ -37,6 +38,10 @@ async function runProjectCompleteState(projectPath, options, context, dependenci
37
38
  nowUtc: now.toISOString(),
38
39
  today: now.toISOString().slice(0, 10),
39
40
  });
41
+ await assertValidProjectStateFilesystemContent(updatedContent, {
42
+ filePath: statePath,
43
+ projectPath: targetProjectPath,
44
+ });
40
45
  await dependencies.writeFile(statePath, updatedContent, 'utf8');
41
46
  if (context.json) {
42
47
  context.logger.json({
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,oBAAoB,IAAI,OAAO,CAY9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/project/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,oBAAoB,IAAI,OAAO,CAc9C"}
@@ -1,23 +1,27 @@
1
1
  import { Command } from 'commander';
2
2
  import { createProjectArchiveCommand } from './archive/index.js';
3
+ import { createProjectCompleteDiscoveryCommand } from './complete-discovery/index.js';
3
4
  import { createProjectCompleteStateCommand } from './complete-state/index.js';
4
5
  import { createProjectListCommand } from './list.js';
5
6
  import { createProjectNewCommand } from './new/index.js';
6
7
  import { createProjectOpenCommand } from './open/index.js';
7
8
  import { createProjectPauseCommand } from './pause/index.js';
8
9
  import { createProjectSetModeCommand } from './set-mode/index.js';
10
+ import { createProjectSplitCommand } from './split/index.js';
9
11
  import { createProjectStatusCommand } from './status.js';
10
12
  import { createProjectValidatePlanCommand } from './validate-plan/index.js';
11
13
  export function createProjectCommand() {
12
14
  return new Command('project')
13
15
  .description('Manage OAT project workflows')
14
16
  .addCommand(createProjectArchiveCommand())
17
+ .addCommand(createProjectCompleteDiscoveryCommand())
15
18
  .addCommand(createProjectCompleteStateCommand())
16
19
  .addCommand(createProjectListCommand())
17
20
  .addCommand(createProjectNewCommand())
18
21
  .addCommand(createProjectOpenCommand())
19
22
  .addCommand(createProjectPauseCommand())
20
23
  .addCommand(createProjectSetModeCommand())
24
+ .addCommand(createProjectSplitCommand())
21
25
  .addCommand(createProjectStatusCommand())
22
26
  .addCommand(createProjectValidatePlanCommand());
23
27
  }
@@ -6,8 +6,14 @@ interface ProjectListDependencies {
6
6
  resolveProjectRoot: (cwd: string) => Promise<string>;
7
7
  resolveProjectsRoot: (repoRoot: string, env: NodeJS.ProcessEnv) => Promise<string>;
8
8
  listProjects: (projectsRoot: string) => Promise<ProjectSummary[]>;
9
+ readProjectMetadata: (projectPath: string) => Promise<ProjectListMetadata>;
9
10
  processEnv: NodeJS.ProcessEnv;
10
11
  }
12
+ interface ProjectListMetadata {
13
+ kind: string;
14
+ phase: string;
15
+ phaseStatus: string;
16
+ }
11
17
  export declare function createProjectListCommand(overrides?: Partial<ProjectListDependencies>): Command;
12
18
  export {};
13
19
  //# sourceMappingURL=list.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/project/list.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAClE,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAgGD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAcT"}
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/project/list.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAK9B,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,YAAY,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAClE,mBAAmB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC3E,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAED,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAwJD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAkBT"}
@@ -1,5 +1,6 @@
1
1
  import { isAbsolute, join } from 'node:path';
2
2
  import { buildCommandContext, } from '../../app/command-context.js';
3
+ import { parseFrontmatterField } from '../shared/frontmatter.js';
3
4
  import { resolveProjectsRoot } from '../shared/oat-paths.js';
4
5
  import { readGlobalOptions } from '../shared/shared.utils.js';
5
6
  import { resolveProjectRoot } from '../../fs/paths.js';
@@ -10,8 +11,39 @@ const DEFAULT_DEPENDENCIES = {
10
11
  resolveProjectRoot,
11
12
  resolveProjectsRoot,
12
13
  listProjects,
14
+ readProjectMetadata,
13
15
  processEnv: process.env,
14
16
  };
17
+ async function readProjectMetadata(projectPath) {
18
+ const stateFile = join(projectPath, 'state.md');
19
+ const [kind, phase, phaseStatus] = await Promise.all([
20
+ parseFrontmatterField(stateFile, 'oat_kind'),
21
+ parseFrontmatterField(stateFile, 'oat_phase'),
22
+ parseFrontmatterField(stateFile, 'oat_phase_status'),
23
+ ]);
24
+ return {
25
+ kind: kind || 'implementation',
26
+ phase: phase || 'discovery',
27
+ phaseStatus: phaseStatus || 'in_progress',
28
+ };
29
+ }
30
+ function isTerminalCoordinationProject(metadata) {
31
+ return (metadata.kind === 'coordination' &&
32
+ metadata.phase === 'decomposition' &&
33
+ metadata.phaseStatus === 'complete');
34
+ }
35
+ async function filterProjectsForList(projects, projectsRoot, includeCoordination, dependencies) {
36
+ if (includeCoordination)
37
+ return projects;
38
+ const filtered = [];
39
+ for (const project of projects) {
40
+ const metadata = await dependencies.readProjectMetadata(join(projectsRoot, project.name));
41
+ if (!isTerminalCoordinationProject(metadata)) {
42
+ filtered.push(project);
43
+ }
44
+ }
45
+ return filtered;
46
+ }
15
47
  function formatProjectTable(projects) {
16
48
  if (projects.length === 0) {
17
49
  return ['No tracked projects found.'];
@@ -48,14 +80,14 @@ function formatProjectTable(projects) {
48
80
  ].join(' '));
49
81
  return [header, divider, ...lines];
50
82
  }
51
- async function runProjectList(context, dependencies) {
83
+ async function runProjectList(context, dependencies, options) {
52
84
  try {
53
85
  const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
54
86
  const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
55
87
  const absoluteProjectsRoot = isAbsolute(projectsRoot)
56
88
  ? projectsRoot
57
89
  : join(repoRoot, projectsRoot);
58
- const projects = await dependencies.listProjects(absoluteProjectsRoot);
90
+ const projects = await filterProjectsForList(await dependencies.listProjects(absoluteProjectsRoot), absoluteProjectsRoot, options.includeCoordination ?? false, dependencies);
59
91
  if (context.json) {
60
92
  context.logger.json({ status: 'ok', projects });
61
93
  }
@@ -84,8 +116,9 @@ export function createProjectListCommand(overrides = {}) {
84
116
  };
85
117
  return new Command('list')
86
118
  .description('List tracked OAT projects')
87
- .action(async (_options, command) => {
119
+ .option('--include-coordination', 'Include completed coordination parent projects')
120
+ .action(async (options, command) => {
88
121
  const context = dependencies.buildCommandContext(readGlobalOptions(command));
89
- await runProjectList(context, dependencies);
122
+ await runProjectList(context, dependencies, options);
90
123
  });
91
124
  }
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/scaffold.ts"],"names":[],"mappings":"AAQA,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,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,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;CAC7B;AAqMD,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAqDhC"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/scaffold.ts"],"names":[],"mappings":"AASA,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,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,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;CAC7B;AAwMD,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAqDhC"}
@@ -4,6 +4,7 @@ import { resolveProjectsRoot } from '../../shared/oat-paths.js';
4
4
  import { generateStateDashboard } from '../../state/generate.js';
5
5
  import { setActiveProject } from '../../../config/oat-config.js';
6
6
  import { fileExists } from '../../../fs/io.js';
7
+ import { assertValidProjectStateContent } from '../../../validation/project-state.js';
7
8
  const TEMPLATES_BY_MODE = {
8
9
  'spec-driven': [
9
10
  'state.md',
@@ -117,6 +118,9 @@ async function scaffoldModeTemplates(repoRoot, projectPath, projectName, mode, t
117
118
  }
118
119
  const template = await readFile(src, 'utf8');
119
120
  const rendered = applyTemplateReplacements(template, projectName, today, nowUtc, mode);
121
+ if (templateFile === 'state.md') {
122
+ assertValidProjectStateContent(rendered, { filePath: dest });
123
+ }
120
124
  await writeFile(dest, rendered, 'utf8');
121
125
  createdFiles.push(templateFile);
122
126
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/open/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAYhF,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,sBAAsB,EAAE,CAAC,OAAO,EAAE;QAChC,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AA+JD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAkBT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/open/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAYhF,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,sBAAsB,EAAE,CAAC,OAAO,EAAE;QAChC,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAkKD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAkBT"}
@@ -9,6 +9,7 @@ import { generateStateDashboard as defaultGenerateStateDashboard, } from '../../
9
9
  import { readOatLocalConfig, setActiveProject, writeOatLocalConfig, } from '../../../config/oat-config.js';
10
10
  import { dirExists, fileExists } from '../../../fs/io.js';
11
11
  import { resolveProjectRoot } from '../../../fs/paths.js';
12
+ import { assertValidProjectStateFilesystemContent } from '../../../validation/project-state.js';
12
13
  import { Command } from 'commander';
13
14
  const DEFAULT_DEPENDENCIES = {
14
15
  buildCommandContext,
@@ -25,7 +26,7 @@ const DEFAULT_DEPENDENCIES = {
25
26
  processEnv: process.env,
26
27
  now: () => new Date(),
27
28
  };
28
- async function maybeResumePausedProject(statePath, dependencies) {
29
+ async function maybeResumePausedProject(statePath, projectPath, dependencies) {
29
30
  const stateContent = await dependencies.readFile(statePath, 'utf8');
30
31
  const frontmatter = getFrontmatterBlock(stateContent);
31
32
  if (!frontmatter) {
@@ -39,7 +40,12 @@ async function maybeResumePausedProject(statePath, dependencies) {
39
40
  nextBlock = removeFrontmatterField(nextBlock, 'oat_pause_reason');
40
41
  nextBlock = upsertFrontmatterField(nextBlock, 'oat_project_state_updated', dependencies.now().toISOString(), true).nextBlock;
41
42
  if (nextBlock !== frontmatter) {
42
- await dependencies.writeFile(statePath, replaceFrontmatter(stateContent, nextBlock), 'utf8');
43
+ const nextContent = replaceFrontmatter(stateContent, nextBlock);
44
+ await assertValidProjectStateFilesystemContent(nextContent, {
45
+ filePath: statePath,
46
+ projectPath,
47
+ });
48
+ await dependencies.writeFile(statePath, nextContent, 'utf8');
43
49
  }
44
50
  return true;
45
51
  }
@@ -76,7 +82,7 @@ async function runProjectOpen(projectName, options, context, dependencies) {
76
82
  process.exitCode = 0;
77
83
  return;
78
84
  }
79
- const resumedFromPaused = await maybeResumePausedProject(statePath, dependencies);
85
+ const resumedFromPaused = await maybeResumePausedProject(statePath, fullProjectPath, dependencies);
80
86
  await dependencies.setActiveProject(repoRoot, projectPath);
81
87
  if (localConfig.lastPausedProject === projectPath) {
82
88
  const updatedConfig = await dependencies.readOatLocalConfig(repoRoot);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/pause/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAShF,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,UAAU,wBAAwB;IAChC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,KAC9B,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,sBAAsB,EAAE,CAAC,OAAO,EAAE;QAChC,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAyID,wBAAgB,yBAAyB,CACvC,SAAS,GAAE,OAAO,CAAC,wBAAwB,CAAM,GAChD,OAAO,CAsBT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/pause/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAShF,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,UAAU,wBAAwB;IAChC,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,kBAAkB,EAAE,CAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,KAC9B,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,sBAAsB,EAAE,CAAC,OAAO,EAAE;QAChC,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AA0ID,wBAAgB,yBAAyB,CACvC,SAAS,GAAE,OAAO,CAAC,wBAAwB,CAAM,GAChD,OAAO,CAsBT"}
@@ -9,6 +9,7 @@ import { generateStateDashboard as defaultGenerateStateDashboard, } from '../../
9
9
  import { clearActiveProject, readOatLocalConfig, } from '../../../config/oat-config.js';
10
10
  import { dirExists, fileExists } from '../../../fs/io.js';
11
11
  import { resolveProjectRoot } from '../../../fs/paths.js';
12
+ import { assertValidProjectStateFilesystemContent } from '../../../validation/project-state.js';
12
13
  import { Command } from 'commander';
13
14
  const DEFAULT_DEPENDENCIES = {
14
15
  buildCommandContext,
@@ -67,7 +68,12 @@ async function runProjectPause(projectName, options, context, dependencies) {
67
68
  nextBlock = removeFrontmatterField(nextBlock, 'oat_pause_reason');
68
69
  }
69
70
  if (nextBlock !== frontmatter) {
70
- await dependencies.writeFile(statePath, replaceFrontmatter(content, nextBlock), 'utf8');
71
+ const nextContent = replaceFrontmatter(content, nextBlock);
72
+ await assertValidProjectStateFilesystemContent(nextContent, {
73
+ filePath: statePath,
74
+ projectPath: fullProjectPath,
75
+ });
76
+ await dependencies.writeFile(statePath, nextContent, 'utf8');
71
77
  }
72
78
  const pointerCleared = activeProject === projectPath;
73
79
  if (pointerCleared) {
@@ -0,0 +1,8 @@
1
+ import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
2
+ import { Command } from 'commander';
3
+ interface EvaluateSignalsDependencies {
4
+ buildCommandContext: (options: GlobalOptions) => CommandContext;
5
+ }
6
+ export declare function createEvaluateSignalsCommand(overrides?: Partial<EvaluateSignalsDependencies>): Command;
7
+ export {};
8
+ //# sourceMappingURL=evaluate-signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate-signals.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/split/evaluate-signals.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,UAAU,2BAA2B;IACnC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;CACjE;AAqBD,wBAAgB,4BAA4B,CAC1C,SAAS,GAAE,OAAO,CAAC,2BAA2B,CAAM,GACnD,OAAO,CAyBT"}