@open-agent-toolkit/cli 0.2.59 → 0.2.63

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 (120) hide show
  1. package/assets/agents/oat-phase-implementer.md +27 -4
  2. package/assets/agents/oat-reviewer.md +25 -14
  3. package/assets/bundle-metadata.json +1 -1
  4. package/assets/docs/cli-utilities/config-and-local-state.md +1 -0
  5. package/assets/docs/cli-utilities/configuration.md +10 -8
  6. package/assets/docs/cli-utilities/project-log.md +16 -0
  7. package/assets/docs/cli-utilities/workflow-gates.md +127 -19
  8. package/assets/docs/provider-sync/commands.md +3 -0
  9. package/assets/docs/provider-sync/instruction-sync.md +107 -0
  10. package/assets/docs/reference/cli-reference.md +26 -9
  11. package/assets/docs/reference/oat-directory-structure.md +33 -31
  12. package/assets/docs/workflows/ideas/index.md +1 -1
  13. package/assets/docs/workflows/index.md +1 -0
  14. package/assets/docs/workflows/projects/artifacts.md +24 -3
  15. package/assets/docs/workflows/projects/hill-checkpoints.md +21 -0
  16. package/assets/docs/workflows/projects/implementation-execution.md +5 -0
  17. package/assets/docs/workflows/projects/lifecycle.md +169 -7
  18. package/assets/docs/workflows/projects/picking-up-projects.md +14 -0
  19. package/assets/docs/workflows/projects/pr-flow.md +2 -0
  20. package/assets/docs/workflows/projects/reviews.md +1 -1
  21. package/assets/docs/workflows/projects/state-machine.md +20 -0
  22. package/assets/docs/workflows/skills/explainer-kit.md +10 -4
  23. package/assets/docs/workflows/skills/index.md +2 -1
  24. package/assets/docs/workflows/skills/repo-improve.md +15 -1
  25. package/assets/public-package-versions.json +4 -4
  26. package/assets/skills/oat-brainstorm/SKILL.md +61 -12
  27. package/assets/skills/oat-docs/SKILL.md +13 -8
  28. package/assets/skills/oat-doctor/SKILL.md +3 -3
  29. package/assets/skills/oat-explainer-kit/SKILL.md +14 -1
  30. package/assets/skills/oat-explainer-kit/references/config-contract.md +12 -0
  31. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +57 -10
  32. package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +28 -5
  33. package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +371 -0
  34. package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +123 -1
  35. package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -2
  36. package/assets/skills/oat-project-autonomous/SKILL.md +23 -9
  37. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +164 -150
  38. package/assets/skills/oat-project-capture/SKILL.md +2 -2
  39. package/assets/skills/oat-project-complete/SKILL.md +134 -4
  40. package/assets/skills/oat-project-discover/SKILL.md +6 -5
  41. package/assets/skills/oat-project-document/SKILL.md +11 -1
  42. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +164 -150
  43. package/assets/skills/oat-project-implement/SKILL.md +3 -3
  44. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +144 -21
  45. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +164 -150
  46. package/assets/skills/oat-project-implement/references/phase-execution.md +16 -1
  47. package/assets/skills/oat-project-implement/references/plan-and-resume.md +19 -1
  48. package/assets/skills/oat-project-import-plan/SKILL.md +47 -3
  49. package/assets/skills/oat-project-lite/SKILL.md +467 -0
  50. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +331 -0
  51. package/assets/skills/oat-project-next/SKILL.md +75 -16
  52. package/assets/skills/oat-project-plan/SKILL.md +44 -6
  53. package/assets/skills/oat-project-plan-writing/SKILL.md +6 -3
  54. package/assets/skills/oat-project-pr-final/SKILL.md +33 -7
  55. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +164 -150
  56. package/assets/skills/oat-project-pr-progress/SKILL.md +3 -3
  57. package/assets/skills/oat-project-progress/SKILL.md +27 -5
  58. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +3 -1
  59. package/assets/skills/oat-project-quick-start/SKILL.md +264 -4
  60. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +164 -150
  61. package/assets/skills/oat-project-review-provide/SKILL.md +25 -6
  62. package/assets/skills/oat-project-summary/SKILL.md +61 -26
  63. package/assets/skills/oat-repo-improve/SKILL.md +40 -5
  64. package/assets/skills/oat-repo-improve/references/plan-template.md +84 -7
  65. package/assets/templates/plan-lite.md +165 -0
  66. package/assets/templates/plan.md +1 -1
  67. package/assets/templates/state.md +1 -1
  68. package/dist/commands/config/index.d.ts +1 -0
  69. package/dist/commands/config/index.d.ts.map +1 -1
  70. package/dist/commands/config/index.js +401 -18
  71. package/dist/commands/gate/index.d.ts +3 -1
  72. package/dist/commands/gate/index.d.ts.map +1 -1
  73. package/dist/commands/gate/index.js +699 -211
  74. package/dist/commands/init/tools/shared/skill-script-references.d.ts +126 -0
  75. package/dist/commands/init/tools/shared/skill-script-references.d.ts.map +1 -0
  76. package/dist/commands/init/tools/shared/skill-script-references.js +311 -0
  77. package/dist/commands/instructions/instructions.types.d.ts +63 -0
  78. package/dist/commands/instructions/instructions.types.d.ts.map +1 -1
  79. package/dist/commands/instructions/instructions.utils.d.ts +51 -2
  80. package/dist/commands/instructions/instructions.utils.d.ts.map +1 -1
  81. package/dist/commands/instructions/instructions.utils.js +201 -4
  82. package/dist/commands/instructions/sync/sync.d.ts.map +1 -1
  83. package/dist/commands/instructions/sync/sync.js +12 -1
  84. package/dist/commands/instructions/validate/validate.d.ts.map +1 -1
  85. package/dist/commands/instructions/validate/validate.js +12 -1
  86. package/dist/commands/project/index.d.ts.map +1 -1
  87. package/dist/commands/project/index.js +2 -0
  88. package/dist/commands/project/log/append.d.ts +195 -0
  89. package/dist/commands/project/log/append.d.ts.map +1 -1
  90. package/dist/commands/project/log/append.js +1034 -11
  91. package/dist/commands/project/new/index.d.ts +3 -2
  92. package/dist/commands/project/new/index.d.ts.map +1 -1
  93. package/dist/commands/project/new/index.js +2 -1
  94. package/dist/commands/project/new/scaffold.d.ts +5 -3
  95. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  96. package/dist/commands/project/new/scaffold.js +38 -10
  97. package/dist/commands/project/promote/index.d.ts +2 -0
  98. package/dist/commands/project/promote/index.d.ts.map +1 -0
  99. package/dist/commands/project/promote/index.js +1 -0
  100. package/dist/commands/project/promote/promote.d.ts +47 -0
  101. package/dist/commands/project/promote/promote.d.ts.map +1 -0
  102. package/dist/commands/project/promote/promote.js +322 -0
  103. package/dist/commands/project/split/run.d.ts.map +1 -1
  104. package/dist/commands/project/split/run.js +8 -1
  105. package/dist/commands/project/validate-plan/index.d.ts.map +1 -1
  106. package/dist/commands/project/validate-plan/index.js +25 -1
  107. package/dist/commands/project/validate-plan/validate-plan.d.ts +8 -0
  108. package/dist/commands/project/validate-plan/validate-plan.d.ts.map +1 -1
  109. package/dist/commands/project/validate-plan/validate-plan.js +49 -0
  110. package/dist/commands/shared/frontmatter.d.ts +1 -1
  111. package/dist/commands/shared/frontmatter.d.ts.map +1 -1
  112. package/dist/commands/shared/frontmatter.js +1 -0
  113. package/dist/commands/state/generate.d.ts.map +1 -1
  114. package/dist/commands/state/generate.js +23 -0
  115. package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
  116. package/dist/commands/tools/shared/pack-manifest.js +2 -0
  117. package/dist/config/oat-config.d.ts +41 -0
  118. package/dist/config/oat-config.d.ts.map +1 -1
  119. package/dist/config/oat-config.js +111 -0
  120. package/package.json +2 -2
@@ -1,13 +1,14 @@
1
1
  import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
2
2
  import { type ProjectScope } from '../../shared/project-scope.js';
3
+ import { type WorkflowMode } from '@open-agent-toolkit/control-plane';
3
4
  import { Command } from 'commander';
4
- import { type ProjectScaffoldMode, type ScaffoldProjectResult } from './scaffold.js';
5
+ import { type ScaffoldProjectResult } from './scaffold.js';
5
6
  interface ProjectNewDependencies {
6
7
  buildCommandContext: (options: GlobalOptions) => CommandContext;
7
8
  scaffoldProject: (options: {
8
9
  repoRoot: string;
9
10
  projectName: string;
10
- mode: ProjectScaffoldMode;
11
+ mode: WorkflowMode;
11
12
  force: boolean;
12
13
  setActive: boolean;
13
14
  refreshDashboard: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAE5C,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AAmBpB,UAAU,sBAAsB;IAC9B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,eAAe,EAAE,CAAC,OAAO,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,mBAAmB,CAAC;QAC1B,KAAK,EAAE,OAAO,CAAC;QACf,SAAS,EAAE,OAAO,CAAC;QACnB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,KAAK,CAAC,EAAE,YAAY,CAAC;KACtB,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtC;AAmGD,wBAAgB,uBAAuB,CACrC,SAAS,GAAE,OAAO,CAAC,sBAAsB,CAAM,GAC9C,OAAO,CAgDT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EAEL,KAAK,YAAY,EAClB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAE5C,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,YAAY,CAAC;AAmBpB,UAAU,sBAAsB;IAC9B,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;IAChE,eAAe,EAAE,CAAC,OAAO,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,OAAO,CAAC;QACf,SAAS,EAAE,OAAO,CAAC;QACnB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,MAAM,EAAE,OAAO,CAAC;QAChB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,KAAK,CAAC,EAAE,YAAY,CAAC;KACtB,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtC;AAmGD,wBAAgB,uBAAuB,CACrC,SAAS,GAAE,OAAO,CAAC,sBAAsB,CAAM,GAC9C,OAAO,CAgDT"}
@@ -2,6 +2,7 @@ import { buildCommandContext, } from '../../../app/command-context.js';
2
2
  import { PROJECT_SCOPES, } from '../../shared/project-scope.js';
3
3
  import { readGlobalOptions } from '../../shared/shared.utils.js';
4
4
  import { CliError } from '../../../errors/cli-error.js';
5
+ import { WORKFLOW_MODES, } from '@open-agent-toolkit/control-plane';
5
6
  import { Command, Option } from 'commander';
6
7
  import { scaffoldProject as defaultScaffoldProject, } from './scaffold.js';
7
8
  const COMMIT_STATUS_MESSAGES = {
@@ -98,7 +99,7 @@ export function createProjectNewCommand(overrides = {}) {
98
99
  .description('Create or update an OAT project scaffold')
99
100
  .argument('<name>', 'Project name (letters, numbers, dash, underscore)')
100
101
  .addOption(new Option('--mode <mode>', 'Scaffold mode')
101
- .choices(['spec-driven', 'quick', 'import'])
102
+ .choices([...WORKFLOW_MODES])
102
103
  .default('spec-driven'))
103
104
  .addOption(new Option('--scope <scope>', 'Project scope').choices(PROJECT_SCOPES))
104
105
  .option('--force', 'Allow a duplicate slug in another scope; scaffold remains non-destructive')
@@ -4,12 +4,12 @@ import { writeSyncedRecord } from '../../project/sync/record.js';
4
4
  import { commitRecordChange, createSyncedProject, pushSynced, rollbackCreatedSyncedProject } from '../../project/sync/ref-sync.js';
5
5
  import { resolveDefaultScope, type ProjectScope } from '../../shared/project-scope.js';
6
6
  import { setActiveProject } from '../../../config/oat-config.js';
7
- export type ProjectScaffoldMode = 'spec-driven' | 'quick' | 'import';
7
+ import type { WorkflowMode } from '@open-agent-toolkit/control-plane';
8
8
  export interface ScaffoldProjectOptions {
9
9
  repoRoot: string;
10
10
  projectName: string;
11
11
  scope?: ProjectScope;
12
- mode?: ProjectScaffoldMode;
12
+ mode?: WorkflowMode;
13
13
  force?: boolean;
14
14
  setActive?: boolean;
15
15
  refreshDashboard?: boolean;
@@ -34,7 +34,7 @@ export interface ScaffoldProjectOptions {
34
34
  */
35
35
  export type CommitScaffoldStatus = 'committed' | 'skipped_disabled' | 'skipped_no_worktree' | 'skipped_nothing' | 'failed';
36
36
  export interface ScaffoldProjectResult {
37
- mode: ProjectScaffoldMode;
37
+ mode: WorkflowMode;
38
38
  scope: ProjectScope;
39
39
  projectsRoot: string;
40
40
  projectPath: string;
@@ -60,5 +60,7 @@ export interface ScaffoldProjectDependencies {
60
60
  ensureScopedRootGitignore: typeof ensureScopedRootGitignore;
61
61
  setActiveProject: typeof setActiveProject;
62
62
  }
63
+ export declare function applyTemplateReplacements(template: string, projectName: string, today: string, nowUtc: string, mode: WorkflowMode): string;
64
+ export declare function resolveTemplateSource(userOatRoot: string, repoRoot: string, templateFile: string): Promise<string>;
63
65
  export declare function scaffoldProject(options: ScaffoldProjectOptions, overrides?: Partial<ScaffoldProjectDependencies>): Promise<ScaffoldProjectResult>;
64
66
  //# sourceMappingURL=scaffold.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/scaffold.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAErE,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAEL,iBAAiB,EAClB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEL,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,4BAA4B,EAC7B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAGL,mBAAmB,EAGnB,KAAK,YAAY,EAClB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAOtD,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,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wBAAwB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAC5B,WAAW,GACX,kBAAkB,GAClB,qBAAqB,GACrB,iBAAiB,GACjB,QAAQ,CAAC;AAEb,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,mBAAmB,CAAC;IAC1B,KAAK,EAAE,YAAY,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,oBAAoB,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,2BAA2B;IAC1C,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,SAAS,EAAE,SAAS,CAAC;IACrB,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,4BAA4B,EAAE,OAAO,4BAA4B,CAAC;IAClE,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,yBAAyB,EAAE,OAAO,yBAAyB,CAAC;IAC5D,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;CAC3C;AA6bD,wBAAsB,eAAe,CACnC,OAAO,EAAE,sBAAsB,EAC/B,SAAS,GAAE,OAAO,CAAC,2BAA2B,CAAM,GACnD,OAAO,CAAC,qBAAqB,CAAC,CAsXhC"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/new/scaffold.ts"],"names":[],"mappings":"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"}
@@ -37,7 +37,15 @@ const TEMPLATES_BY_MODE = {
37
37
  ],
38
38
  quick: ['state.md', 'discovery.md', 'plan.md', 'implementation.md'],
39
39
  import: ['state.md', 'plan.md', 'implementation.md'],
40
+ lite: [
41
+ 'state.md',
42
+ { source: 'plan-lite.md', target: 'plan.md' },
43
+ 'implementation.md',
44
+ ],
40
45
  };
46
+ function normalizeTemplateEntry(entry) {
47
+ return typeof entry === 'string' ? { source: entry, target: entry } : entry;
48
+ }
41
49
  const OAT_PLACEHOLDER_PATTERN = /(?<!\{)\{\s*(OAT_[A-Z0-9_]+)\s*\}(?!\})/g;
42
50
  const STATE_TEMPLATE_BY_MODE = {
43
51
  'spec-driven': {
@@ -97,6 +105,22 @@ const STATE_TEMPLATE_BY_MODE = {
97
105
  ],
98
106
  nextMilestone: 'Run `oat-project-import-plan` to normalize the external plan',
99
107
  },
108
+ lite: {
109
+ hillCheckpoints: '[]',
110
+ phase: 'plan',
111
+ status: 'Plan',
112
+ currentPhase: 'Plan - Authoring a single-phase lite plan',
113
+ artifacts: [
114
+ '- **Plan:** `plan.md` (scaffolded lite template — not started)',
115
+ '- **Implementation:** `implementation.md` (scaffolded template — not started)',
116
+ ],
117
+ progress: [
118
+ '- ✓ Lite project scaffolded',
119
+ '- ✓ Plan and implementation artifacts scaffolded',
120
+ '- ⧗ Awaiting critical interview and plan authoring',
121
+ ],
122
+ nextMilestone: 'Run `oat-project-lite` to author and approve the plan',
123
+ },
100
124
  };
101
125
  function replaceOatPlaceholders(template, replacements) {
102
126
  return template.replace(OAT_PLACEHOLDER_PATTERN, (placeholder, token) => replacements[token] ?? placeholder);
@@ -120,7 +144,7 @@ function validateProjectName(name) {
120
144
  function shellQuote(value) {
121
145
  return `'${value.replaceAll("'", "'\\''")}'`;
122
146
  }
123
- function applyTemplateReplacements(template, projectName, today, nowUtc, mode) {
147
+ export function applyTemplateReplacements(template, projectName, today, nowUtc, mode) {
124
148
  const stateContent = STATE_TEMPLATE_BY_MODE[mode];
125
149
  const oatReplacements = {
126
150
  OAT_HILL_CHECKPOINTS: stateContent.hillCheckpoints,
@@ -185,21 +209,25 @@ async function commitScaffold(cwd, absoluteProjectPath, projectName, createdFile
185
209
  async function scaffoldModeTemplates(userOatRoot, repoRoot, absoluteProjectPath, projectName, mode, today, nowUtc) {
186
210
  const createdFiles = [];
187
211
  const skippedFiles = [];
188
- for (const templateFile of TEMPLATES_BY_MODE[mode]) {
189
- const src = await resolveTemplateSource(userOatRoot, repoRoot, templateFile);
190
- const dest = join(absoluteProjectPath, templateFile);
212
+ for (const entry of TEMPLATES_BY_MODE[mode]) {
213
+ const { source, target } = normalizeTemplateEntry(entry);
214
+ const src = await resolveTemplateSource(userOatRoot, repoRoot, source);
215
+ const dest = join(absoluteProjectPath, target);
191
216
  if (await fileExists(dest)) {
192
- skippedFiles.push(templateFile);
217
+ skippedFiles.push(target);
193
218
  continue;
194
219
  }
195
220
  const template = await readFile(src, 'utf8');
196
- const rendered = applyTemplateReplacements(template, projectName, today, nowUtc, mode);
197
- assertNoUnresolvedOatPlaceholders(rendered, templateFile);
198
- if (templateFile === 'state.md') {
221
+ let rendered = applyTemplateReplacements(template, projectName, today, nowUtc, mode);
222
+ if (mode === 'lite' && target === 'plan.md') {
223
+ rendered = rendered.replace(/^---\n/, '---\noat_template: true\n');
224
+ }
225
+ assertNoUnresolvedOatPlaceholders(rendered, source);
226
+ if (target === 'state.md') {
199
227
  assertValidProjectStateContent(rendered, { filePath: dest });
200
228
  }
201
229
  await writeFile(dest, rendered, 'utf8');
202
- createdFiles.push(templateFile);
230
+ createdFiles.push(target);
203
231
  }
204
232
  return { createdFiles, skippedFiles };
205
233
  }
@@ -214,7 +242,7 @@ async function scaffoldProjectLog(userOatRoot, repoRoot, absoluteProjectPath, pr
214
242
  await writeFile(dest, instantiateProjectLogTemplate(template, projectName, today), 'utf8');
215
243
  return 'created';
216
244
  }
217
- async function resolveTemplateSource(userOatRoot, repoRoot, templateFile) {
245
+ export async function resolveTemplateSource(userOatRoot, repoRoot, templateFile) {
218
246
  const userSource = join(userOatRoot, 'templates', templateFile);
219
247
  if (await fileExists(userSource)) {
220
248
  return userSource;
@@ -0,0 +1,2 @@
1
+ export { createProjectPromoteCommand } from './promote.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/promote/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1 @@
1
+ export { createProjectPromoteCommand } from './promote.js';
@@ -0,0 +1,47 @@
1
+ import { mkdir as defaultMkdir, readFile as defaultReadFile, rename as defaultRename, writeFile as defaultWriteFile } from 'node:fs/promises';
2
+ import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
3
+ import { resolveTemplateSource as defaultResolveTemplateSource } from '../../project/new/scaffold.js';
4
+ import { type GitRunner } from '../../project/sync/git.js';
5
+ import { type PushResult, type SyncTarget } from '../../project/sync/ref-sync.js';
6
+ import { fileExists as defaultFileExists } from '../../../fs/io.js';
7
+ import { Command } from 'commander';
8
+ export interface LitePlanSections {
9
+ summary: string;
10
+ decisions: string;
11
+ assumptions: string;
12
+ outOfScope: string;
13
+ validationCriteria: string;
14
+ productBehavior?: string;
15
+ technicalDesign?: string;
16
+ }
17
+ type PromotionReason = 'promoted' | 'unsupported-target' | 'not-lite' | 'lite-plan-reference-exists' | 'invalid-lite-plan' | 'scope-unresolved' | 'project-unreadable' | 'template-unreadable' | 'write-failed' | 'persistence-failed';
18
+ export type PromotionResult = {
19
+ status: 'promoted';
20
+ reason: 'promoted';
21
+ files: string[];
22
+ } | {
23
+ status: 'refused';
24
+ reason: Exclude<PromotionReason, 'promoted'>;
25
+ files: string[];
26
+ };
27
+ export interface ProjectPromoteDependencies {
28
+ buildCommandContext: (options: GlobalOptions) => CommandContext;
29
+ resolveProjectRoot: (cwd: string) => Promise<string>;
30
+ resolveProjectsRoot: (repoRoot: string, env: NodeJS.ProcessEnv) => Promise<string>;
31
+ resolveTemplateSource: typeof defaultResolveTemplateSource;
32
+ readFile: typeof defaultReadFile;
33
+ writeFile: typeof defaultWriteFile;
34
+ mkdir: typeof defaultMkdir;
35
+ rename: typeof defaultRename;
36
+ fileExists: typeof defaultFileExists;
37
+ gitRunner: GitRunner;
38
+ pushSynced: (target: SyncTarget, git: GitRunner, options: {
39
+ message?: string;
40
+ }) => Promise<PushResult>;
41
+ processEnv: NodeJS.ProcessEnv;
42
+ now: () => Date;
43
+ }
44
+ export declare function parseLitePlanSections(planContent: string, liteTemplateContent?: string): LitePlanSections | null;
45
+ export declare function createProjectPromoteCommand(overrides?: Partial<ProjectPromoteDependencies>): Command;
46
+ export {};
47
+ //# sourceMappingURL=promote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promote.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/promote/promote.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,IAAI,YAAY,EACrB,QAAQ,IAAI,eAAe,EAC3B,MAAM,IAAI,aAAa,EACvB,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,qBAAqB,IAAI,4BAA4B,EACtD,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAEzD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAkBpC,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,KAAK,eAAe,GAChB,UAAU,GACV,oBAAoB,GACpB,UAAU,GACV,4BAA4B,GAC5B,mBAAmB,GACnB,kBAAkB,GAClB,oBAAoB,GACpB,qBAAqB,GACrB,cAAc,GACd,oBAAoB,CAAC;AAEzB,MAAM,MAAM,eAAe,GACvB;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,GACD;IACE,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC7C,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAMN,MAAM,WAAW,0BAA0B;IACzC,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,qBAAqB,EAAE,OAAO,4BAA4B,CAAC;IAC3D,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,KAAK,EAAE,OAAO,YAAY,CAAC;IAC3B,MAAM,EAAE,OAAO,aAAa,CAAC;IAC7B,UAAU,EAAE,OAAO,iBAAiB,CAAC;IACrC,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,CACV,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,KAC1B,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AA4ED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,gBAAgB,GAAG,IAAI,CAYzB;AA0SD,wBAAgB,2BAA2B,CACzC,SAAS,GAAE,OAAO,CAAC,0BAA0B,CAAM,GAClD,OAAO,CA+BT"}
@@ -0,0 +1,322 @@
1
+ import { mkdir as defaultMkdir, readFile as defaultReadFile, rename as defaultRename, writeFile as defaultWriteFile, } from 'node:fs/promises';
2
+ import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
3
+ import { buildCommandContext, } from '../../../app/command-context.js';
4
+ import { applyTemplateReplacements, resolveTemplateSource as defaultResolveTemplateSource, } from '../../project/new/scaffold.js';
5
+ import { defaultGitRunner } from '../../project/sync/git.js';
6
+ import { buildSyncTarget, pushSynced as defaultPushSynced, } from '../../project/sync/ref-sync.js';
7
+ import { getFrontmatterBlock } from '../../shared/frontmatter.js';
8
+ import { replaceFrontmatter } from '../../shared/frontmatter-write.js';
9
+ import { resolveProjectsRoot as defaultResolveProjectsRoot } from '../../shared/oat-paths.js';
10
+ import { resolveProjectScope } from '../../shared/project-scope.js';
11
+ import { readGlobalOptions } from '../../shared/shared.utils.js';
12
+ import { fileExists as defaultFileExists } from '../../../fs/io.js';
13
+ import { resolveProjectRoot } from '../../../fs/paths.js';
14
+ import { Command } from 'commander';
15
+ import YAML from 'yaml';
16
+ const PROMOTED_FILES = [
17
+ 'discovery.md',
18
+ 'references/lite-plan.md',
19
+ 'plan.md',
20
+ 'state.md',
21
+ ];
22
+ const LITE_SECTION_HEADINGS = [
23
+ ['Summary', 'summary'],
24
+ ['Decisions', 'decisions'],
25
+ ['Assumptions', 'assumptions'],
26
+ ['Out of Scope', 'outOfScope'],
27
+ ['Validation Criteria', 'validationCriteria'],
28
+ ];
29
+ const DEFAULT_DEPENDENCIES = {
30
+ buildCommandContext,
31
+ resolveProjectRoot,
32
+ resolveProjectsRoot: defaultResolveProjectsRoot,
33
+ resolveTemplateSource: defaultResolveTemplateSource,
34
+ readFile: defaultReadFile,
35
+ writeFile: defaultWriteFile,
36
+ mkdir: defaultMkdir,
37
+ rename: defaultRename,
38
+ fileExists: defaultFileExists,
39
+ gitRunner: defaultGitRunner,
40
+ pushSynced: defaultPushSynced,
41
+ processEnv: process.env,
42
+ now: () => new Date(),
43
+ };
44
+ function extractSection(content, heading) {
45
+ const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
46
+ const headingMatch = new RegExp(`^## ${escapedHeading}[ \\t]*$`, 'm').exec(content);
47
+ if (!headingMatch)
48
+ return null;
49
+ const bodyStart = headingMatch.index + headingMatch[0].length;
50
+ const remaining = content.slice(bodyStart);
51
+ const nextHeading = /^##\s+/m.exec(remaining);
52
+ const bodyEnd = nextHeading ? bodyStart + nextHeading.index : content.length;
53
+ const body = content.slice(bodyStart, bodyEnd).trim();
54
+ return body.length > 0 ? body : null;
55
+ }
56
+ function extractLitePlanSections(planContent) {
57
+ const sections = {};
58
+ for (const [heading, key] of LITE_SECTION_HEADINGS) {
59
+ const body = extractSection(planContent, heading);
60
+ if (!body)
61
+ return null;
62
+ sections[key] = body;
63
+ }
64
+ const productBehavior = extractSection(planContent, 'Product Behavior');
65
+ const technicalDesign = extractSection(planContent, 'Technical Design');
66
+ if (productBehavior)
67
+ sections.productBehavior = productBehavior;
68
+ if (technicalDesign)
69
+ sections.technicalDesign = technicalDesign;
70
+ return sections;
71
+ }
72
+ function containsUnresolvedTemplateContent(sections, templateSections) {
73
+ const containsTemplateMarkers = (key) => {
74
+ const content = sections[key];
75
+ const templateContent = templateSections[key];
76
+ if (!content || !templateContent)
77
+ return false;
78
+ if (content === templateContent)
79
+ return true;
80
+ const markers = templateContent
81
+ .match(/\[[^\]\n]+\]|\{[^{}\n]+\}/g)
82
+ ?.filter((marker) => !/^\[\s*\]$/.test(marker)) ?? [];
83
+ const instructionalComments = templateContent.match(/<!--[\s\S]*?-->/g) ?? [];
84
+ return [...markers, ...instructionalComments].some((marker) => content.includes(marker));
85
+ };
86
+ return (LITE_SECTION_HEADINGS.some(([, key]) => containsTemplateMarkers(key)) ||
87
+ ['productBehavior', 'technicalDesign'].some((key) => containsTemplateMarkers(key)));
88
+ }
89
+ export function parseLitePlanSections(planContent, liteTemplateContent) {
90
+ const sections = extractLitePlanSections(planContent);
91
+ if (!sections || !liteTemplateContent)
92
+ return sections;
93
+ const templateSections = extractLitePlanSections(liteTemplateContent);
94
+ if (!templateSections ||
95
+ containsUnresolvedTemplateContent(sections, templateSections)) {
96
+ return null;
97
+ }
98
+ return sections;
99
+ }
100
+ function readObjectFrontmatter(content, filePath) {
101
+ const block = getFrontmatterBlock(content);
102
+ if (!block) {
103
+ throw new Error(`${filePath} is missing frontmatter`);
104
+ }
105
+ const parsed = YAML.parse(block);
106
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
107
+ throw new Error(`${filePath} frontmatter must be a YAML object`);
108
+ }
109
+ return parsed;
110
+ }
111
+ function replaceSection(content, heading, body) {
112
+ const escapedHeading = heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
113
+ const headingMatch = new RegExp(`^## ${escapedHeading}[ \\t]*$`, 'm').exec(content);
114
+ if (!headingMatch) {
115
+ throw new Error(`discovery template is missing ## ${heading}`);
116
+ }
117
+ const bodyStart = headingMatch.index + headingMatch[0].length;
118
+ const remaining = content.slice(bodyStart);
119
+ const nextHeading = /^##\s+/m.exec(remaining);
120
+ const bodyEnd = nextHeading ? bodyStart + nextHeading.index : content.length;
121
+ const suffix = content.slice(bodyEnd).replace(/^\s*/, '');
122
+ return `${content.slice(0, bodyStart)}\n\n${body.trim()}\n\n${suffix}`;
123
+ }
124
+ function renderDiscovery(template, projectName, today, nowUtc, sections) {
125
+ let rendered = applyTemplateReplacements(template, projectName, today, nowUtc, 'quick');
126
+ const frontmatter = readObjectFrontmatter(rendered, 'rendered discovery.md');
127
+ frontmatter['oat_ready_for'] = 'oat-project-quick-start';
128
+ rendered = replaceFrontmatter(rendered, YAML.stringify(frontmatter).trimEnd());
129
+ const successCriteria = sections.productBehavior
130
+ ? `### Product Behavior (from Lite plan)\n\n${sections.productBehavior}\n\n### Validation Criteria (from Lite plan)\n\n${sections.validationCriteria}`
131
+ : sections.validationCriteria;
132
+ for (const [heading, body] of [
133
+ ['Initial Request', sections.summary],
134
+ ['Key Decisions', sections.decisions],
135
+ ['Assumptions', sections.assumptions],
136
+ ['Out of Scope', sections.outOfScope],
137
+ ['Success Criteria', successCriteria],
138
+ ]) {
139
+ rendered = replaceSection(rendered, heading, body);
140
+ }
141
+ if (sections.technicalDesign) {
142
+ const nextSteps = /^## Next Steps[ \t]*$/m.exec(rendered);
143
+ if (!nextSteps) {
144
+ throw new Error('discovery template is missing ## Next Steps');
145
+ }
146
+ const carriedForward = [
147
+ '## Carried-Forward Technical Design',
148
+ '',
149
+ '> Prior implementation context carried forward from the Lite plan; this is not a new discovery deliverable.',
150
+ '',
151
+ sections.technicalDesign,
152
+ '',
153
+ ].join('\n');
154
+ rendered = `${rendered.slice(0, nextSteps.index)}${carriedForward}${rendered.slice(nextSteps.index)}`;
155
+ }
156
+ return rendered;
157
+ }
158
+ function renderQuickState(stateContent, statePath, nowUtc) {
159
+ const frontmatter = readObjectFrontmatter(stateContent, statePath);
160
+ frontmatter['oat_workflow_mode'] = 'quick';
161
+ frontmatter['oat_phase'] = 'discovery';
162
+ frontmatter['oat_phase_status'] = 'complete';
163
+ frontmatter['oat_ready_for'] = 'oat-project-quick-start';
164
+ frontmatter['oat_project_state_updated'] = nowUtc;
165
+ return replaceFrontmatter(stateContent, YAML.stringify(frontmatter).trimEnd());
166
+ }
167
+ function repoRelativePath(repoRoot, path) {
168
+ return relative(repoRoot, path).split(sep).join('/');
169
+ }
170
+ async function persistPromotion(repoRoot, projectsRoot, projectRoot, projectName, scope, dependencies) {
171
+ const message = `chore(oat): promote ${projectName} to quick`;
172
+ if (scope === 'synced') {
173
+ const target = buildSyncTarget(repoRoot, projectsRoot, projectName);
174
+ const result = await dependencies.pushSynced(target, dependencies.gitRunner, { message });
175
+ if (result.status !== 'pushed' && result.status !== 'up-to-date') {
176
+ throw new Error(`synced promotion push ${result.status}`);
177
+ }
178
+ return;
179
+ }
180
+ if (scope === 'local') {
181
+ return;
182
+ }
183
+ const pathspecs = PROMOTED_FILES.map((file) => repoRelativePath(repoRoot, join(projectRoot, file)));
184
+ await dependencies.gitRunner.run(['add', '--', ...pathspecs], {
185
+ cwd: repoRoot,
186
+ });
187
+ await dependencies.gitRunner.run(['commit', '-m', message, '--', ...pathspecs], { cwd: repoRoot });
188
+ }
189
+ async function promoteProject(projectPath, options, context, dependencies) {
190
+ if (options.to !== 'quick') {
191
+ return { status: 'refused', reason: 'unsupported-target', files: [] };
192
+ }
193
+ const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
194
+ const projectRoot = isAbsolute(projectPath)
195
+ ? resolve(projectPath)
196
+ : resolve(repoRoot, projectPath);
197
+ const projectName = basename(projectRoot);
198
+ const statePath = join(projectRoot, 'state.md');
199
+ const planPath = join(projectRoot, 'plan.md');
200
+ const referencePath = join(projectRoot, 'references', 'lite-plan.md');
201
+ let stateContent;
202
+ let planContent;
203
+ try {
204
+ [stateContent, planContent] = await Promise.all([
205
+ dependencies.readFile(statePath, 'utf8'),
206
+ dependencies.readFile(planPath, 'utf8'),
207
+ ]);
208
+ }
209
+ catch {
210
+ return { status: 'refused', reason: 'project-unreadable', files: [] };
211
+ }
212
+ let stateFrontmatter;
213
+ try {
214
+ stateFrontmatter = readObjectFrontmatter(stateContent, statePath);
215
+ }
216
+ catch {
217
+ return { status: 'refused', reason: 'project-unreadable', files: [] };
218
+ }
219
+ if (stateFrontmatter['oat_workflow_mode'] !== 'lite') {
220
+ return { status: 'refused', reason: 'not-lite', files: [] };
221
+ }
222
+ if (await dependencies.fileExists(referencePath)) {
223
+ return {
224
+ status: 'refused',
225
+ reason: 'lite-plan-reference-exists',
226
+ files: [],
227
+ };
228
+ }
229
+ const userOatRoot = join(context.home, '.oat');
230
+ let liteTemplateContent;
231
+ try {
232
+ const liteTemplatePath = await dependencies.resolveTemplateSource(userOatRoot, repoRoot, 'plan-lite.md');
233
+ liteTemplateContent = await dependencies.readFile(liteTemplatePath, 'utf8');
234
+ }
235
+ catch {
236
+ return { status: 'refused', reason: 'template-unreadable', files: [] };
237
+ }
238
+ const sections = parseLitePlanSections(planContent, liteTemplateContent);
239
+ if (!sections) {
240
+ return { status: 'refused', reason: 'invalid-lite-plan', files: [] };
241
+ }
242
+ const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
243
+ const scope = resolveProjectScope(projectRoot, projectsRoot, repoRoot);
244
+ if (!scope) {
245
+ return { status: 'refused', reason: 'scope-unresolved', files: [] };
246
+ }
247
+ const now = dependencies.now();
248
+ const nowUtc = now.toISOString();
249
+ const today = nowUtc.slice(0, 10);
250
+ let discoveryContent;
251
+ let quickPlanContent;
252
+ let quickStateContent;
253
+ try {
254
+ const [discoveryTemplatePath, quickPlanTemplatePath] = await Promise.all([
255
+ dependencies.resolveTemplateSource(userOatRoot, repoRoot, 'discovery.md'),
256
+ dependencies.resolveTemplateSource(userOatRoot, repoRoot, 'plan.md'),
257
+ ]);
258
+ const [discoveryTemplate, quickPlanTemplate] = await Promise.all([
259
+ dependencies.readFile(discoveryTemplatePath, 'utf8'),
260
+ dependencies.readFile(quickPlanTemplatePath, 'utf8'),
261
+ ]);
262
+ discoveryContent = renderDiscovery(discoveryTemplate, projectName, today, nowUtc, sections);
263
+ quickPlanContent = applyTemplateReplacements(quickPlanTemplate, projectName, today, nowUtc, 'quick');
264
+ quickStateContent = renderQuickState(stateContent, statePath, nowUtc);
265
+ }
266
+ catch {
267
+ return { status: 'refused', reason: 'template-unreadable', files: [] };
268
+ }
269
+ const writtenFiles = [];
270
+ try {
271
+ await dependencies.mkdir(join(projectRoot, 'references'), {
272
+ recursive: true,
273
+ });
274
+ await dependencies.writeFile(join(projectRoot, 'discovery.md'), discoveryContent, 'utf8');
275
+ writtenFiles.push('discovery.md');
276
+ await dependencies.rename(planPath, referencePath);
277
+ writtenFiles.push('references/lite-plan.md');
278
+ await dependencies.writeFile(planPath, quickPlanContent, 'utf8');
279
+ writtenFiles.push('plan.md');
280
+ await dependencies.writeFile(statePath, quickStateContent, 'utf8');
281
+ writtenFiles.push('state.md');
282
+ }
283
+ catch {
284
+ return { status: 'refused', reason: 'write-failed', files: writtenFiles };
285
+ }
286
+ try {
287
+ await persistPromotion(repoRoot, projectsRoot, projectRoot, projectName, scope, dependencies);
288
+ }
289
+ catch {
290
+ return {
291
+ status: 'refused',
292
+ reason: 'persistence-failed',
293
+ files: writtenFiles,
294
+ };
295
+ }
296
+ return {
297
+ status: 'promoted',
298
+ reason: 'promoted',
299
+ files: [...PROMOTED_FILES],
300
+ };
301
+ }
302
+ export function createProjectPromoteCommand(overrides = {}) {
303
+ const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
304
+ return new Command('promote')
305
+ .description('Promote an authored lite project to the quick workflow')
306
+ .argument('<project-path>', 'Lite project path to promote')
307
+ .requiredOption('--to <mode>', 'Target workflow mode')
308
+ .action(async (projectPath, options, command) => {
309
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
310
+ const result = await promoteProject(projectPath, options, context, dependencies);
311
+ if (context.json) {
312
+ context.logger.json(result);
313
+ }
314
+ else if (result.status === 'promoted') {
315
+ context.logger.info(`Promoted ${projectPath} to quick.`);
316
+ }
317
+ else {
318
+ context.logger.error(`Promotion refused: ${result.reason}.`);
319
+ }
320
+ process.exitCode = result.status === 'promoted' ? 0 : 1;
321
+ });
322
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/split/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAOlF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAMhE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,UAAU,oBAAoB;IAC5B,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,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,gBAAgB,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,OAAO,iBAAiB,CAAC;CACtC;AAiND,wBAAgB,4BAA4B,CAC1C,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,OAAO,CAkLT"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/split/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAGvE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAOlF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAMhE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAqBpC,UAAU,oBAAoB;IAC5B,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,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,OAAO,EAAE,OAAO,OAAO,CAAC;IACxB,IAAI,EAAE,OAAO,IAAI,CAAC;IAClB,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,gBAAgB,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,OAAO,iBAAiB,CAAC;CACtC;AAqND,wBAAgB,4BAA4B,CAC1C,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,OAAO,CA2LT"}
@@ -63,6 +63,9 @@ async function recordDetectedRecommendation(repoRoot, document, dependencies) {
63
63
  throw new Error('Cannot record detected split recommendation without an active project');
64
64
  }
65
65
  const discoveryPath = join(repoRoot, localConfig.activeProject, 'discovery.md');
66
+ if (!(await exists(discoveryPath, dependencies))) {
67
+ return false;
68
+ }
66
69
  await dependencies.appendFile(discoveryPath, [
67
70
  '',
68
71
  '## Detected Split Recommendation',
@@ -74,6 +77,7 @@ async function recordDetectedRecommendation(repoRoot, document, dependencies) {
74
77
  .join(', ')}`,
75
78
  '',
76
79
  ].join('\n'), 'utf8');
80
+ return true;
77
81
  }
78
82
  function isEffectivelyNonInteractive(options, context, processEnv) {
79
83
  return (options.nonInteractive === true ||
@@ -180,7 +184,10 @@ export function createProjectSplitRunCommand(overrides = {}) {
180
184
  if (isEffectivelyNonInteractive(options, context, dependencies.processEnv) &&
181
185
  (document.origin === 'detected-mid-stream' ||
182
186
  document.origin === 'detected-convergence')) {
183
- await recordDetectedRecommendation(repoRoot, document, dependencies);
187
+ const recorded = await recordDetectedRecommendation(repoRoot, document, dependencies);
188
+ if (!recorded) {
189
+ context.logger.info('skipped split recommendation: no discovery.md for this project');
190
+ }
184
191
  context.logger.error('Detected split requires interactive confirmation; recommendation recorded.');
185
192
  process.exitCode = 1;
186
193
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/validate-plan/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,UAAU,+BAA+B;IACvC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;CACjE;AAMD,wBAAgB,gCAAgC,CAC9C,SAAS,GAAE,OAAO,CAAC,+BAA+B,CAAM,GACvD,OAAO,CA0ET"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/validate-plan/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,UAAU,+BAA+B;IACvC,mBAAmB,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,cAAc,CAAC;CACjE;AAMD,wBAAgB,gCAAgC,CAC9C,SAAS,GAAE,OAAO,CAAC,+BAA+B,CAAM,GACvD,OAAO,CAsGT"}
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { buildCommandContext, } from '../../../app/command-context.js';
4
4
  import { readGlobalOptions } from '../../shared/shared.utils.js';
5
5
  import { Command } from 'commander';
6
- import { extractPhaseIdsFromPlan, parseFrontmatterFromContent, validateParallelGroups, } from './validate-plan.js';
6
+ import { extractPhaseIdsFromPlan, parseFrontmatterFromContent, validateLitePlan, validateParallelGroups, } from './validate-plan.js';
7
7
  const DEFAULT_DEPENDENCIES = {
8
8
  buildCommandContext,
9
9
  };
@@ -47,6 +47,30 @@ export function createProjectValidatePlanCommand(overrides = {}) {
47
47
  }
48
48
  const groups = frontmatterResult.data['oat_plan_parallel_groups'];
49
49
  const phaseIds = extractPhaseIdsFromPlan(content);
50
+ let workflowMode = null;
51
+ try {
52
+ const stateContent = readFileSync(join(options.projectPath, 'state.md'), 'utf-8');
53
+ const stateFrontmatter = parseFrontmatterFromContent(stateContent);
54
+ const mode = stateFrontmatter.kind === 'invalid'
55
+ ? null
56
+ : stateFrontmatter.data['oat_workflow_mode'];
57
+ workflowMode = typeof mode === 'string' ? mode : null;
58
+ }
59
+ catch {
60
+ workflowMode = null;
61
+ }
62
+ const liteResult = validateLitePlan(content, workflowMode);
63
+ if (!liteResult.ok) {
64
+ if (context.json) {
65
+ context.logger.json({ valid: false, errors: [liteResult.message] });
66
+ }
67
+ else {
68
+ context.logger.error('Plan validation failed:');
69
+ context.logger.error(` - ${liteResult.message}`);
70
+ }
71
+ process.exitCode = 1;
72
+ return;
73
+ }
50
74
  const result = validateParallelGroups(groups, phaseIds);
51
75
  if (result.valid) {
52
76
  if (context.json) {