@open-agent-toolkit/cli 0.1.5 → 0.1.7

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 (84) hide show
  1. package/assets/agents/oat-reviewer.md +10 -1
  2. package/assets/docs/workflows/projects/artifacts.md +17 -0
  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 +21 -1
  10. package/assets/skills/oat-project-quick-start/SKILL.md +14 -5
  11. package/assets/skills/oat-project-review-provide/SKILL.md +9 -1
  12. package/assets/skills/oat-project-review-receive/SKILL.md +21 -1
  13. package/assets/skills/oat-project-split/SKILL.md +82 -0
  14. package/assets/skills/oat-project-summary/SKILL.md +15 -13
  15. package/assets/templates/implementation.md +5 -5
  16. package/assets/templates/state.md +6 -1
  17. package/assets/templates/summary.md +2 -1
  18. package/dist/__tests__/skills/split-flow-fixtures.d.ts +50 -0
  19. package/dist/__tests__/skills/split-flow-fixtures.d.ts.map +1 -0
  20. package/dist/__tests__/skills/split-flow-fixtures.js +161 -0
  21. package/dist/commands/init/tools/shared/skill-manifest.d.ts +1 -1
  22. package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
  23. package/dist/commands/init/tools/shared/skill-manifest.js +1 -0
  24. package/dist/commands/project/complete-discovery/index.d.ts +16 -0
  25. package/dist/commands/project/complete-discovery/index.d.ts.map +1 -0
  26. package/dist/commands/project/complete-discovery/index.js +123 -0
  27. package/dist/commands/project/complete-state/index.d.ts.map +1 -1
  28. package/dist/commands/project/complete-state/index.js +5 -0
  29. package/dist/commands/project/index.d.ts.map +1 -1
  30. package/dist/commands/project/index.js +4 -0
  31. package/dist/commands/project/list.d.ts +6 -0
  32. package/dist/commands/project/list.d.ts.map +1 -1
  33. package/dist/commands/project/list.js +37 -4
  34. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  35. package/dist/commands/project/new/scaffold.js +4 -0
  36. package/dist/commands/project/open/index.d.ts.map +1 -1
  37. package/dist/commands/project/open/index.js +9 -3
  38. package/dist/commands/project/pause/index.d.ts.map +1 -1
  39. package/dist/commands/project/pause/index.js +7 -1
  40. package/dist/commands/project/split/evaluate-signals.d.ts +8 -0
  41. package/dist/commands/project/split/evaluate-signals.d.ts.map +1 -0
  42. package/dist/commands/project/split/evaluate-signals.js +47 -0
  43. package/dist/commands/project/split/index.d.ts +3 -0
  44. package/dist/commands/project/split/index.d.ts.map +1 -0
  45. package/dist/commands/project/split/index.js +11 -0
  46. package/dist/commands/project/split/run.d.ts +21 -0
  47. package/dist/commands/project/split/run.d.ts.map +1 -0
  48. package/dist/commands/project/split/run.js +231 -0
  49. package/dist/commands/project/split/validate-plan.d.ts +14 -0
  50. package/dist/commands/project/split/validate-plan.d.ts.map +1 -0
  51. package/dist/commands/project/split/validate-plan.js +62 -0
  52. package/dist/commands/shared/frontmatter.d.ts +9 -0
  53. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  54. package/dist/commands/shared/frontmatter.js +46 -0
  55. package/dist/commands/state/generate.d.ts.map +1 -1
  56. package/dist/commands/state/generate.js +38 -6
  57. package/dist/projects/split/child-plan.d.ts +46 -0
  58. package/dist/projects/split/child-plan.d.ts.map +1 -0
  59. package/dist/projects/split/child-plan.js +107 -0
  60. package/dist/projects/split/document-validation.d.ts +14 -0
  61. package/dist/projects/split/document-validation.d.ts.map +1 -0
  62. package/dist/projects/split/document-validation.js +106 -0
  63. package/dist/projects/split/finalize.d.ts +7 -0
  64. package/dist/projects/split/finalize.d.ts.map +1 -0
  65. package/dist/projects/split/finalize.js +32 -0
  66. package/dist/projects/split/resume.d.ts +19 -0
  67. package/dist/projects/split/resume.d.ts.map +1 -0
  68. package/dist/projects/split/resume.js +107 -0
  69. package/dist/projects/split/seed-children.d.ts +9 -0
  70. package/dist/projects/split/seed-children.d.ts.map +1 -0
  71. package/dist/projects/split/seed-children.js +122 -0
  72. package/dist/projects/split/signals.d.ts +10 -0
  73. package/dist/projects/split/signals.d.ts.map +1 -0
  74. package/dist/projects/split/signals.js +18 -0
  75. package/dist/projects/split/validation.d.ts +14 -0
  76. package/dist/projects/split/validation.d.ts.map +1 -0
  77. package/dist/projects/split/validation.js +104 -0
  78. package/dist/projects/split/write-parent.d.ts +16 -0
  79. package/dist/projects/split/write-parent.d.ts.map +1 -0
  80. package/dist/projects/split/write-parent.js +176 -0
  81. package/dist/validation/project-state.d.ts +50 -0
  82. package/dist/validation/project-state.d.ts.map +1 -0
  83. package/dist/validation/project-state.js +279 -0
  84. package/package.json +2 -2
@@ -1,5 +1,51 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
+ export const PROJECT_STATE_KINDS = ['implementation', 'coordination'];
4
+ export const PROJECT_STATE_PHASES = [
5
+ 'discovery',
6
+ 'spec',
7
+ 'design',
8
+ 'plan',
9
+ 'implement',
10
+ 'decomposition',
11
+ ];
12
+ export const PROJECT_STATE_FRONTMATTER_FIELDS = [
13
+ 'oat_kind',
14
+ 'oat_parent',
15
+ 'oat_siblings',
16
+ 'oat_depends_on',
17
+ 'oat_children',
18
+ 'oat_inherited_context_revalidated',
19
+ 'oat_phase',
20
+ 'oat_phase_status',
21
+ 'oat_status',
22
+ 'oat_workflow_mode',
23
+ 'oat_lifecycle',
24
+ 'oat_current_task',
25
+ 'oat_last_commit',
26
+ 'oat_blockers',
27
+ 'oat_hill_checkpoints',
28
+ 'oat_hill_completed',
29
+ 'oat_parallel_execution',
30
+ 'oat_pr_status',
31
+ 'oat_pr_url',
32
+ 'oat_project_created',
33
+ 'oat_project_completed',
34
+ 'oat_project_state_updated',
35
+ 'oat_docs_updated',
36
+ 'oat_generated',
37
+ 'oat_template',
38
+ 'oat_template_name',
39
+ ];
40
+ export function isProjectStateKind(value) {
41
+ return PROJECT_STATE_KINDS.includes(value);
42
+ }
43
+ export function isProjectStatePhase(value) {
44
+ return PROJECT_STATE_PHASES.includes(value);
45
+ }
46
+ export function isProjectStateFrontmatterField(value) {
47
+ return PROJECT_STATE_FRONTMATTER_FIELDS.includes(value);
48
+ }
3
49
  export function getFrontmatterBlock(content) {
4
50
  const match = content.match(/^---\n([\s\S]*?)\n---/);
5
51
  return match?.[1] ?? null;
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AA4GD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvE;AA8aD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CA2C9B"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/state/generate.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAiHD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvE;AAydD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CA2C9B"}
@@ -305,25 +305,53 @@ function computeNextStep(project, hasProjects, state) {
305
305
  }
306
306
  return { step: 'oat-project-progress', reason: 'Check current progress' };
307
307
  }
308
+ function isTerminalCoordinationProject(kind, phase, phaseStatus) {
309
+ return (kind === 'coordination' &&
310
+ phase === 'decomposition' &&
311
+ phaseStatus === 'complete');
312
+ }
308
313
  async function listAvailableProjects(repoRoot, projectsRoot) {
309
314
  const fullRoot = join(repoRoot, projectsRoot);
310
315
  try {
311
316
  const entries = await readdir(fullRoot, { withFileTypes: true });
312
317
  const dirs = entries.filter((e) => e.isDirectory());
313
- if (dirs.length === 0)
314
- return '*(No projects found)*';
318
+ if (dirs.length === 0) {
319
+ return {
320
+ active: '*(No projects found)*',
321
+ decompositions: '*(No decompositions found)*',
322
+ };
323
+ }
315
324
  const lines = [];
325
+ const decompositions = [];
316
326
  for (const dir of dirs) {
317
327
  const stateFile = join(fullRoot, dir.name, 'state.md');
318
328
  if (await fileExists(stateFile)) {
319
329
  const phase = (await parseFrontmatterField(stateFile, 'oat_phase')) || 'unknown';
320
- lines.push(`- **${dir.name}** - ${phase}`);
330
+ const phaseStatus = (await parseFrontmatterField(stateFile, 'oat_phase_status')) ||
331
+ 'in_progress';
332
+ const kind = (await parseFrontmatterField(stateFile, 'oat_kind')) ||
333
+ 'implementation';
334
+ const line = `- **${dir.name}** - ${phase}`;
335
+ if (isTerminalCoordinationProject(kind, phase, phaseStatus)) {
336
+ decompositions.push(line);
337
+ }
338
+ else {
339
+ lines.push(line);
340
+ }
321
341
  }
322
342
  }
323
- return lines.length > 0 ? lines.join('\n') : '*(No projects found)*';
343
+ return {
344
+ active: lines.length > 0 ? lines.join('\n') : '*(No projects found)*',
345
+ decompositions: decompositions.length > 0
346
+ ? decompositions.join('\n')
347
+ : '*(No decompositions found)*',
348
+ };
324
349
  }
325
350
  catch {
326
- return '*(No projects directory found)*';
351
+ return {
352
+ active: '*(No projects directory found)*',
353
+ decompositions: '*(No decompositions found)*',
354
+ };
327
355
  }
328
356
  }
329
357
  function buildDashboardMarkdown(project, state, knowledge, staleness, nextStep, projectsList, generatedDate) {
@@ -412,7 +440,11 @@ function buildDashboardMarkdown(project, state, knowledge, staleness, nextStep,
412
440
  lines.push('');
413
441
  lines.push('## Available Projects');
414
442
  lines.push('');
415
- lines.push(projectsList);
443
+ lines.push(projectsList.active);
444
+ lines.push('');
445
+ lines.push('## Decompositions');
446
+ lines.push('');
447
+ lines.push(projectsList.decompositions);
416
448
  lines.push('');
417
449
  return lines.join('\n');
418
450
  }
@@ -0,0 +1,46 @@
1
+ export type SplitOrigin = 'declared' | 'detected-mid-stream' | 'detected-convergence' | 'brainstorm-picker';
2
+ export interface SplitChildInput {
3
+ slug: string;
4
+ description?: string;
5
+ inheritedContext?: string;
6
+ knownDependencies?: string[];
7
+ foundation?: boolean;
8
+ }
9
+ export interface SplitPayload {
10
+ origin: SplitOrigin;
11
+ parentSlug?: string;
12
+ declaredChildren?: SplitChildInput[];
13
+ inferredChildren?: SplitChildInput[];
14
+ priorDiscovery?: {
15
+ path: string;
16
+ brainstormSessionId?: string;
17
+ parentSlug?: string;
18
+ children?: SplitChildInput[];
19
+ inheritedContext?: string;
20
+ integrationSketch?: string;
21
+ };
22
+ interactive: boolean;
23
+ foundationChild?: string;
24
+ integrationSketch?: string;
25
+ initialActiveChild?: string;
26
+ }
27
+ export interface ChildPlan {
28
+ parentSlug: string;
29
+ children: Array<{
30
+ slug: string;
31
+ description?: string;
32
+ inheritedContext: string;
33
+ knownDependencies: string[];
34
+ order: number;
35
+ }>;
36
+ foundationChild?: string;
37
+ integrationSketch?: string;
38
+ initialActiveChild: string;
39
+ }
40
+ export interface SplitPlanDocument {
41
+ origin: SplitOrigin;
42
+ interactive: boolean;
43
+ plan: ChildPlan;
44
+ }
45
+ export declare function buildSplitPlanDocument(payload: SplitPayload): SplitPlanDocument;
46
+ //# sourceMappingURL=child-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-plan.d.ts","sourceRoot":"","sources":["../../../src/projects/split/child-plan.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GACnB,UAAU,GACV,qBAAqB,GACrB,sBAAsB,GACtB,mBAAmB,CAAC;AAExB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,cAAc,CAAC,EAAE;QACf,IAAI,EAAE,MAAM,CAAC;QACb,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;QAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAC5B,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,IAAI,EAAE,SAAS,CAAC;CACjB;AA0GD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,YAAY,GACpB,iBAAiB,CAiCnB"}
@@ -0,0 +1,107 @@
1
+ function deriveSlugFromPath(path) {
2
+ const normalized = path.trim().replace(/\/+$/, '');
3
+ const slug = normalized.split('/').at(-1);
4
+ if (!slug) {
5
+ throw new Error('Unable to derive parent slug from priorDiscovery.path');
6
+ }
7
+ return slug;
8
+ }
9
+ function resolveParentSlug(payload) {
10
+ if (payload.parentSlug) {
11
+ return payload.parentSlug;
12
+ }
13
+ if (payload.priorDiscovery?.parentSlug) {
14
+ return payload.priorDiscovery.parentSlug;
15
+ }
16
+ if (payload.priorDiscovery?.path) {
17
+ return deriveSlugFromPath(payload.priorDiscovery.path);
18
+ }
19
+ throw new Error('SplitPayload requires parentSlug or priorDiscovery.path');
20
+ }
21
+ function resolveChildren(payload) {
22
+ const children = payload.declaredChildren ??
23
+ payload.inferredChildren ??
24
+ payload.priorDiscovery?.children;
25
+ if (!children || children.length === 0) {
26
+ throw new Error('SplitPayload requires at least one child');
27
+ }
28
+ return children;
29
+ }
30
+ function resolveFoundationChild(payload, children) {
31
+ return (payload.foundationChild ?? children.find((child) => child.foundation)?.slug);
32
+ }
33
+ function orderChildren(children, foundationChild) {
34
+ const childSlugs = new Set(children.map((child) => child.slug));
35
+ const bySlug = new Map(children.map((child) => [child.slug, child]));
36
+ const inputOrder = new Map(children.map((child, index) => [child.slug, index]));
37
+ const indegree = new Map(children.map((child) => [child.slug, 0]));
38
+ const dependents = new Map();
39
+ for (const child of children) {
40
+ for (const dependency of child.knownDependencies ?? []) {
41
+ if (!childSlugs.has(dependency)) {
42
+ continue;
43
+ }
44
+ indegree.set(child.slug, (indegree.get(child.slug) ?? 0) + 1);
45
+ const existing = dependents.get(dependency) ?? [];
46
+ existing.push(child.slug);
47
+ dependents.set(dependency, existing);
48
+ }
49
+ }
50
+ const priority = (slug) => slug === foundationChild ? -1 : (inputOrder.get(slug) ?? 0);
51
+ const ready = children
52
+ .filter((child) => indegree.get(child.slug) === 0)
53
+ .map((child) => child.slug)
54
+ .sort((left, right) => priority(left) - priority(right));
55
+ const ordered = [];
56
+ while (ready.length > 0) {
57
+ const slug = ready.shift();
58
+ const child = bySlug.get(slug);
59
+ if (child) {
60
+ ordered.push(child);
61
+ }
62
+ for (const dependent of dependents.get(slug) ?? []) {
63
+ indegree.set(dependent, (indegree.get(dependent) ?? 0) - 1);
64
+ if (indegree.get(dependent) === 0) {
65
+ ready.push(dependent);
66
+ ready.sort((left, right) => priority(left) - priority(right));
67
+ }
68
+ }
69
+ }
70
+ if (ordered.length !== children.length) {
71
+ const orderedSlugs = new Set(ordered.map((child) => child.slug));
72
+ ordered.push(...children
73
+ .filter((child) => !orderedSlugs.has(child.slug))
74
+ .sort((left, right) => priority(left.slug) - priority(right.slug)));
75
+ }
76
+ return ordered;
77
+ }
78
+ export function buildSplitPlanDocument(payload) {
79
+ const parentSlug = resolveParentSlug(payload);
80
+ const children = resolveChildren(payload);
81
+ const foundationChild = resolveFoundationChild(payload, children);
82
+ const orderedChildren = orderChildren(children, foundationChild);
83
+ const firstChild = orderedChildren[0]?.slug;
84
+ if (!firstChild) {
85
+ throw new Error('SplitPayload requires at least one child');
86
+ }
87
+ return {
88
+ origin: payload.origin,
89
+ interactive: payload.interactive,
90
+ plan: {
91
+ parentSlug,
92
+ children: orderedChildren.map((child, index) => ({
93
+ slug: child.slug,
94
+ description: child.description,
95
+ inheritedContext: child.inheritedContext ??
96
+ payload.priorDiscovery?.inheritedContext ??
97
+ child.description ??
98
+ '',
99
+ knownDependencies: [...(child.knownDependencies ?? [])],
100
+ order: index + 1,
101
+ })),
102
+ foundationChild,
103
+ integrationSketch: payload.integrationSketch ?? payload.priorDiscovery?.integrationSketch,
104
+ initialActiveChild: payload.initialActiveChild ?? foundationChild ?? firstChild,
105
+ },
106
+ };
107
+ }
@@ -0,0 +1,14 @@
1
+ import type { ChildPlan, SplitPlanDocument } from './child-plan.js';
2
+ export interface DocumentValidationError {
3
+ code: string;
4
+ message: string;
5
+ }
6
+ export declare function normalizePlanForValidation(plan: ChildPlan): ChildPlan;
7
+ export declare function validateSplitPlanDocumentShape(value: unknown): {
8
+ ok: true;
9
+ document: SplitPlanDocument;
10
+ } | {
11
+ ok: false;
12
+ errors: DocumentValidationError[];
13
+ };
14
+ //# sourceMappingURL=document-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-validation.d.ts","sourceRoot":"","sources":["../../../src/projects/split/document-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAe,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAE9E,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAQrE;AAED,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GACzD;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAA;CAAE,GACzC;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,uBAAuB,EAAE,CAAC;CACnC,CAgGJ"}
@@ -0,0 +1,106 @@
1
+ const SPLIT_ORIGINS = [
2
+ 'declared',
3
+ 'detected-mid-stream',
4
+ 'detected-convergence',
5
+ 'brainstorm-picker',
6
+ ];
7
+ function isObject(value) {
8
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
9
+ }
10
+ export function normalizePlanForValidation(plan) {
11
+ return {
12
+ ...plan,
13
+ children: plan.children.map((child) => ({
14
+ ...child,
15
+ knownDependencies: child.knownDependencies ?? [],
16
+ })),
17
+ };
18
+ }
19
+ export function validateSplitPlanDocumentShape(value) {
20
+ const errors = [];
21
+ if (!isObject(value)) {
22
+ return {
23
+ ok: false,
24
+ errors: [{ code: 'invalid-document', message: 'Expected JSON object' }],
25
+ };
26
+ }
27
+ if (typeof value.origin !== 'string' ||
28
+ !SPLIT_ORIGINS.includes(value.origin)) {
29
+ errors.push({
30
+ code: 'invalid-origin',
31
+ message: 'SplitPlanDocument origin is required',
32
+ });
33
+ }
34
+ if (typeof value.interactive !== 'boolean') {
35
+ errors.push({
36
+ code: 'invalid-interactive',
37
+ message: 'SplitPlanDocument interactive boolean is required',
38
+ });
39
+ }
40
+ if (!isObject(value.plan)) {
41
+ errors.push({
42
+ code: 'invalid-plan',
43
+ message: 'SplitPlanDocument plan object is required',
44
+ });
45
+ }
46
+ else {
47
+ if (typeof value.plan.parentSlug !== 'string') {
48
+ errors.push({
49
+ code: 'invalid-parent-slug',
50
+ message: 'ChildPlan parentSlug is required',
51
+ });
52
+ }
53
+ if (!Array.isArray(value.plan.children)) {
54
+ errors.push({
55
+ code: 'invalid-children',
56
+ message: 'ChildPlan children array is required',
57
+ });
58
+ }
59
+ else {
60
+ value.plan.children.forEach((child, index) => {
61
+ if (!isObject(child)) {
62
+ errors.push({
63
+ code: 'invalid-child',
64
+ message: `ChildPlan child ${index + 1} must be an object`,
65
+ });
66
+ return;
67
+ }
68
+ if (typeof child.slug !== 'string') {
69
+ errors.push({
70
+ code: 'invalid-child-slug',
71
+ message: `ChildPlan child ${index + 1} slug is required`,
72
+ });
73
+ }
74
+ if (child.knownDependencies !== undefined &&
75
+ !Array.isArray(child.knownDependencies)) {
76
+ errors.push({
77
+ code: 'invalid-known-dependencies',
78
+ message: `ChildPlan child ${index + 1} knownDependencies must be an array`,
79
+ });
80
+ }
81
+ if (typeof child.order !== 'number') {
82
+ errors.push({
83
+ code: 'invalid-child-order',
84
+ message: `ChildPlan child ${index + 1} order is required`,
85
+ });
86
+ }
87
+ });
88
+ }
89
+ if (typeof value.plan.initialActiveChild !== 'string') {
90
+ errors.push({
91
+ code: 'invalid-initial-active-child',
92
+ message: 'ChildPlan initialActiveChild is required',
93
+ });
94
+ }
95
+ }
96
+ if (errors.length > 0) {
97
+ return { ok: false, errors };
98
+ }
99
+ return {
100
+ ok: true,
101
+ document: {
102
+ ...value,
103
+ plan: normalizePlanForValidation(value.plan),
104
+ },
105
+ };
106
+ }
@@ -0,0 +1,7 @@
1
+ import type { ChildPlan } from './child-plan.js';
2
+ import type { SplitProjectContext } from './write-parent.js';
3
+ export interface FinalizeSplitResult {
4
+ activeProjectPath: string;
5
+ }
6
+ export declare function finalizeSplit(plan: ChildPlan, context: SplitProjectContext): Promise<FinalizeSplitResult>;
7
+ //# sourceMappingURL=finalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finalize.d.ts","sourceRoot":"","sources":["../../../src/projects/split/finalize.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAiB1D,MAAM,WAAW,mBAAmB;IAClC,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,mBAAmB,CAAC,CAoB9B"}
@@ -0,0 +1,32 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { getFrontmatterBlock } from '../../commands/shared/frontmatter.js';
4
+ import { replaceFrontmatter } from '../../commands/shared/frontmatter-write.js';
5
+ import { setActiveProject } from '../../config/oat-config.js';
6
+ import YAML from 'yaml';
7
+ function readObjectFrontmatter(content, filePath) {
8
+ const block = getFrontmatterBlock(content);
9
+ if (!block) {
10
+ throw new Error(`${filePath} is missing frontmatter`);
11
+ }
12
+ const parsed = YAML.parse(block);
13
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
14
+ throw new Error(`${filePath} frontmatter must be an object`);
15
+ }
16
+ return parsed;
17
+ }
18
+ export async function finalizeSplit(plan, context) {
19
+ const projectsRoot = context.projectsRoot ?? '.oat/projects/shared';
20
+ const parentPath = join(projectsRoot, plan.parentSlug).split('\\').join('/');
21
+ const statePath = join(context.repoRoot, parentPath, 'state.md');
22
+ const stateContent = await readFile(statePath, 'utf8');
23
+ const frontmatter = readObjectFrontmatter(stateContent, statePath);
24
+ frontmatter['oat_phase'] = 'decomposition';
25
+ frontmatter['oat_phase_status'] = 'complete';
26
+ await writeFile(statePath, replaceFrontmatter(stateContent, YAML.stringify(frontmatter).trimEnd()), 'utf8');
27
+ const activeProjectPath = join(projectsRoot, plan.initialActiveChild)
28
+ .split('\\')
29
+ .join('/');
30
+ await setActiveProject(context.repoRoot, activeProjectPath);
31
+ return { activeProjectPath };
32
+ }
@@ -0,0 +1,19 @@
1
+ import type { ChildPlan, SplitPlanDocument } from './child-plan.js';
2
+ import type { SplitProjectContext } from './write-parent.js';
3
+ export declare class SplitResumeError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ export interface PartialSplit {
7
+ document: SplitPlanDocument;
8
+ plan: ChildPlan;
9
+ missingChildren: string[];
10
+ parentProjectPath: string;
11
+ projectsRoot: string;
12
+ }
13
+ export interface ResumeSplitOptions {
14
+ confirmed?: boolean;
15
+ }
16
+ export declare function detectPartialSplit(parentPath: string, context: SplitProjectContext): Promise<PartialSplit>;
17
+ export declare function resumeSplit(parentPath: string, context: SplitProjectContext, options?: ResumeSplitOptions): Promise<PartialSplit>;
18
+ export declare function continueSplitResume(partial: PartialSplit, context: SplitProjectContext): Promise<PartialSplit>;
19
+ //# sourceMappingURL=resume.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/projects/split/resume.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAKjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE1D,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA6CD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,CA8DvB;AAED,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,YAAY,CAAC,CAQvB;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAavB"}
@@ -0,0 +1,107 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import { isAbsolute, join, relative } from 'node:path';
3
+ import { getFrontmatterBlock } from '../../commands/shared/frontmatter.js';
4
+ import YAML from 'yaml';
5
+ import { validateSplitPlanDocumentShape } from './document-validation.js';
6
+ import { finalizeSplit } from './finalize.js';
7
+ import { seedChildren } from './seed-children.js';
8
+ import { validateChildPlan } from './validation.js';
9
+ export class SplitResumeError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'SplitResumeError';
13
+ }
14
+ }
15
+ function readObjectFrontmatter(content, filePath) {
16
+ const block = getFrontmatterBlock(content);
17
+ if (!block) {
18
+ throw new SplitResumeError(`${filePath} is missing frontmatter`);
19
+ }
20
+ const parsed = YAML.parse(block);
21
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
22
+ throw new SplitResumeError(`${filePath} frontmatter must be an object`);
23
+ }
24
+ return parsed;
25
+ }
26
+ async function exists(path) {
27
+ try {
28
+ await stat(path);
29
+ return true;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ function resolveParentPaths(parentPath, context) {
36
+ const absoluteParentPath = isAbsolute(parentPath)
37
+ ? parentPath
38
+ : join(context.repoRoot, parentPath);
39
+ const parentProjectPath = relative(context.repoRoot, absoluteParentPath)
40
+ .split('\\')
41
+ .join('/');
42
+ const segments = parentProjectPath.split('/');
43
+ const projectsRoot = segments.slice(0, -1).join('/');
44
+ return { absoluteParentPath, parentProjectPath, projectsRoot };
45
+ }
46
+ export async function detectPartialSplit(parentPath, context) {
47
+ const { absoluteParentPath, parentProjectPath, projectsRoot } = resolveParentPaths(parentPath, context);
48
+ const statePath = join(absoluteParentPath, 'state.md');
49
+ const state = readObjectFrontmatter(await readFile(statePath, 'utf8'), statePath);
50
+ if (state['oat_kind'] !== 'coordination') {
51
+ throw new SplitResumeError('Split resume requires a coordination parent');
52
+ }
53
+ if (state['oat_phase_status'] === 'complete') {
54
+ throw new SplitResumeError('Cannot resume a completed split parent');
55
+ }
56
+ let parsed;
57
+ try {
58
+ parsed = JSON.parse(await readFile(join(absoluteParentPath, 'references', 'split-plan.json'), 'utf8'));
59
+ }
60
+ catch (error) {
61
+ const message = error instanceof Error ? error.message : String(error);
62
+ throw new SplitResumeError(`Cannot resume split without valid references/split-plan.json: ${message}`);
63
+ }
64
+ const documentShape = validateSplitPlanDocumentShape(parsed);
65
+ if (!documentShape.ok) {
66
+ throw new SplitResumeError(`Cannot resume split without valid references/split-plan.json: ${documentShape.errors
67
+ .map((error) => error.message)
68
+ .join('; ')}`);
69
+ }
70
+ const document = documentShape.document;
71
+ const validation = validateChildPlan(document.plan, new Set());
72
+ if (!validation.ok) {
73
+ throw new SplitResumeError(`Cannot resume split with invalid references/split-plan.json: ${validation.errors
74
+ .map((error) => error.message)
75
+ .join('; ')}`);
76
+ }
77
+ const missingChildren = [];
78
+ for (const child of document.plan.children) {
79
+ if (!(await exists(join(context.repoRoot, projectsRoot, child.slug)))) {
80
+ missingChildren.push(child.slug);
81
+ }
82
+ }
83
+ return {
84
+ document,
85
+ plan: document.plan,
86
+ missingChildren,
87
+ parentProjectPath,
88
+ projectsRoot,
89
+ };
90
+ }
91
+ export async function resumeSplit(parentPath, context, options = {}) {
92
+ const partial = await detectPartialSplit(parentPath, context);
93
+ if (options.confirmed !== true) {
94
+ throw new SplitResumeError('Split resume requires explicit confirmation before writing missing children.');
95
+ }
96
+ return continueSplitResume(partial, context);
97
+ }
98
+ export async function continueSplitResume(partial, context) {
99
+ if (partial.missingChildren.length > 0) {
100
+ await seedChildren(partial.plan, { ...context, projectsRoot: partial.projectsRoot }, new Set(partial.missingChildren));
101
+ }
102
+ await finalizeSplit(partial.plan, {
103
+ ...context,
104
+ projectsRoot: partial.projectsRoot,
105
+ });
106
+ return partial;
107
+ }
@@ -0,0 +1,9 @@
1
+ import type { ChildPlan } from './child-plan.js';
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"];
4
+ export interface SeedChildrenResult {
5
+ childProjectPaths: string[];
6
+ }
7
+ export declare function seedChildren(plan: ChildPlan, context: SplitProjectContext, onlySlugs?: Set<string>): Promise<SeedChildrenResult>;
8
+ export { SEEDED_SECTIONS };
9
+ //# sourceMappingURL=seed-children.d.ts.map
@@ -0,0 +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,wJAQX,CAAC;AAoFX,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,CA4C7B;AAED,OAAO,EAAE,eAAe,EAAE,CAAC"}