@open-agent-toolkit/cli 0.2.40 → 0.2.45

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 (213) hide show
  1. package/assets/agents/oat-codebase-mapper.md +15 -3
  2. package/assets/agents/oat-phase-implementer.md +34 -9
  3. package/assets/agents/oat-reviewer.md +2 -2
  4. package/assets/bundle-metadata.json +1 -1
  5. package/assets/docs/cli-utilities/configuration.md +11 -1
  6. package/assets/docs/cli-utilities/tool-packs.md +8 -0
  7. package/assets/docs/contributing/skills.md +67 -14
  8. package/assets/docs/quickstart.md +9 -0
  9. package/assets/docs/reference/cli-reference.md +56 -1
  10. package/assets/docs/reference/file-locations.md +18 -1
  11. package/assets/docs/reference/oat-directory-structure.md +20 -4
  12. package/assets/docs/workflows/projects/artifacts.md +27 -1
  13. package/assets/docs/workflows/projects/implementation-execution.md +22 -0
  14. package/assets/docs/workflows/projects/index.md +6 -0
  15. package/assets/docs/workflows/projects/lifecycle.md +19 -5
  16. package/assets/docs/workflows/projects/picking-up-projects.md +75 -0
  17. package/assets/docs/workflows/projects/pr-flow.md +22 -1
  18. package/assets/docs/workflows/projects/reviewing-oat-prs.md +60 -0
  19. package/assets/public-package-versions.json +4 -4
  20. package/assets/skills/analyze/SKILL.md +13 -5
  21. package/assets/skills/compare/SKILL.md +11 -2
  22. package/assets/skills/oat-brainstorm/SKILL.md +135 -32
  23. package/assets/skills/oat-brainstorm/references/destinations.md +9 -6
  24. package/assets/skills/oat-cursor-cloud-projects/SKILL.md +11 -1
  25. package/assets/skills/oat-dispatch-subagents/SKILL.md +19 -6
  26. package/assets/skills/oat-doctor/SKILL.md +8 -1
  27. package/assets/skills/oat-project-autonomous/SKILL.md +45 -1
  28. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +8 -8
  29. package/assets/skills/oat-project-capture/SKILL.md +29 -9
  30. package/assets/skills/oat-project-complete/SKILL.md +610 -38
  31. package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +265 -0
  32. package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +764 -0
  33. package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +134 -0
  34. package/assets/skills/oat-project-complete/scripts/validate-nonarchive-lifecycle-receipt.mjs +160 -0
  35. package/assets/skills/oat-project-design/SKILL.md +33 -9
  36. package/assets/skills/oat-project-discover/SKILL.md +16 -3
  37. package/assets/skills/oat-project-document/SKILL.md +10 -4
  38. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +8 -8
  39. package/assets/skills/oat-project-implement/SKILL.md +14 -3
  40. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +18 -6
  41. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +8 -8
  42. package/assets/skills/oat-project-implement/references/phase-execution.md +9 -3
  43. package/assets/skills/oat-project-implement/references/plan-and-resume.md +3 -0
  44. package/assets/skills/oat-project-import-plan/SKILL.md +15 -9
  45. package/assets/skills/oat-project-new/SKILL.md +7 -5
  46. package/assets/skills/oat-project-next/SKILL.md +32 -10
  47. package/assets/skills/oat-project-plan/SKILL.md +18 -3
  48. package/assets/skills/oat-project-pr-final/SKILL.md +23 -2
  49. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +8 -8
  50. package/assets/skills/oat-project-pr-progress/SKILL.md +23 -2
  51. package/assets/skills/oat-project-progress/SKILL.md +23 -9
  52. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +20 -2
  53. package/assets/skills/oat-project-quick-start/SKILL.md +48 -18
  54. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +8 -8
  55. package/assets/skills/oat-project-reconcile/SKILL.md +14 -10
  56. package/assets/skills/oat-project-retro/SKILL.md +3 -3
  57. package/assets/skills/oat-project-retro/references/apply-procedure.md +102 -6
  58. package/assets/skills/oat-project-retro-file/SKILL.md +57 -6
  59. package/assets/skills/oat-project-review-provide/SKILL.md +56 -6
  60. package/assets/skills/oat-project-review-receive/SKILL.md +16 -7
  61. package/assets/skills/oat-project-review-receive-remote/SKILL.md +9 -3
  62. package/assets/skills/oat-project-revise/SKILL.md +12 -3
  63. package/assets/skills/oat-project-spec/SKILL.md +16 -3
  64. package/assets/skills/oat-project-summary/SKILL.md +113 -13
  65. package/assets/skills/oat-repo-improve/SKILL.md +18 -7
  66. package/assets/skills/oat-review-provide-remote/SKILL.md +11 -2
  67. package/assets/skills/oat-wave-execute/SKILL.md +34 -2
  68. package/assets/skills/oat-worktree-bootstrap/SKILL.md +13 -1
  69. package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +13 -1
  70. package/dist/commands/config/index.d.ts +1 -0
  71. package/dist/commands/config/index.d.ts.map +1 -1
  72. package/dist/commands/config/index.js +30 -2
  73. package/dist/commands/doctor/index.d.ts +2 -0
  74. package/dist/commands/doctor/index.d.ts.map +1 -1
  75. package/dist/commands/doctor/index.js +27 -3
  76. package/dist/commands/doctor/synced-projects.d.ts +12 -0
  77. package/dist/commands/doctor/synced-projects.d.ts.map +1 -0
  78. package/dist/commands/doctor/synced-projects.js +208 -0
  79. package/dist/commands/init/gitattributes.d.ts +4 -0
  80. package/dist/commands/init/gitattributes.d.ts.map +1 -0
  81. package/dist/commands/init/gitattributes.js +12 -0
  82. package/dist/commands/init/gitignore.d.ts +8 -1
  83. package/dist/commands/init/gitignore.d.ts.map +1 -1
  84. package/dist/commands/init/gitignore.js +146 -41
  85. package/dist/commands/init/index.d.ts +2 -0
  86. package/dist/commands/init/index.d.ts.map +1 -1
  87. package/dist/commands/init/index.js +3 -0
  88. package/dist/commands/init/managed-block.d.ts +10 -0
  89. package/dist/commands/init/managed-block.d.ts.map +1 -0
  90. package/dist/commands/init/managed-block.js +40 -0
  91. package/dist/commands/init/tools/workflows/install-workflows.js +1 -1
  92. package/dist/commands/local/index.d.ts.map +1 -1
  93. package/dist/commands/local/index.js +5 -1
  94. package/dist/commands/local/sync.d.ts +4 -0
  95. package/dist/commands/local/sync.d.ts.map +1 -1
  96. package/dist/commands/local/sync.js +123 -2
  97. package/dist/commands/pjm/adoption.d.ts +2 -0
  98. package/dist/commands/pjm/adoption.d.ts.map +1 -1
  99. package/dist/commands/pjm/adoption.js +1 -1
  100. package/dist/commands/project/archive/archive-utils.d.ts +24 -0
  101. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  102. package/dist/commands/project/archive/archive-utils.js +349 -22
  103. package/dist/commands/project/archive/index.d.ts.map +1 -1
  104. package/dist/commands/project/archive/index.js +1 -0
  105. package/dist/commands/project/archive/push-runner.d.ts +5 -1
  106. package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
  107. package/dist/commands/project/archive/push-runner.js +33 -4
  108. package/dist/commands/project/complete-state/index.d.ts.map +1 -1
  109. package/dist/commands/project/complete-state/index.js +3 -1
  110. package/dist/commands/project/index.d.ts.map +1 -1
  111. package/dist/commands/project/index.js +12 -0
  112. package/dist/commands/project/links/compute.d.ts +9 -0
  113. package/dist/commands/project/links/compute.d.ts.map +1 -0
  114. package/dist/commands/project/links/compute.js +27 -0
  115. package/dist/commands/project/links/index.d.ts +17 -0
  116. package/dist/commands/project/links/index.d.ts.map +1 -0
  117. package/dist/commands/project/links/index.js +72 -0
  118. package/dist/commands/project/links/refresh.d.ts +26 -0
  119. package/dist/commands/project/links/refresh.d.ts.map +1 -0
  120. package/dist/commands/project/links/refresh.js +92 -0
  121. package/dist/commands/project/links/render.d.ts +24 -0
  122. package/dist/commands/project/links/render.d.ts.map +1 -0
  123. package/dist/commands/project/links/render.js +68 -0
  124. package/dist/commands/project/list.d.ts +5 -0
  125. package/dist/commands/project/list.d.ts.map +1 -1
  126. package/dist/commands/project/list.js +182 -9
  127. package/dist/commands/project/migrate/index.d.ts +22 -0
  128. package/dist/commands/project/migrate/index.d.ts.map +1 -0
  129. package/dist/commands/project/migrate/index.js +79 -0
  130. package/dist/commands/project/new/index.d.ts +2 -0
  131. package/dist/commands/project/new/index.d.ts.map +1 -1
  132. package/dist/commands/project/new/index.js +14 -2
  133. package/dist/commands/project/new/scaffold.d.ts +22 -1
  134. package/dist/commands/project/new/scaffold.d.ts.map +1 -1
  135. package/dist/commands/project/new/scaffold.js +287 -69
  136. package/dist/commands/project/open/index.d.ts +16 -0
  137. package/dist/commands/project/open/index.d.ts.map +1 -1
  138. package/dist/commands/project/open/index.js +151 -19
  139. package/dist/commands/project/pause/index.d.ts +8 -0
  140. package/dist/commands/project/pause/index.d.ts.map +1 -1
  141. package/dist/commands/project/pause/index.js +129 -19
  142. package/dist/commands/project/prune/index.d.ts +21 -0
  143. package/dist/commands/project/prune/index.d.ts.map +1 -0
  144. package/dist/commands/project/prune/index.js +95 -0
  145. package/dist/commands/project/pull/index.d.ts +28 -0
  146. package/dist/commands/project/pull/index.d.ts.map +1 -0
  147. package/dist/commands/project/pull/index.js +157 -0
  148. package/dist/commands/project/push/index.d.ts +22 -0
  149. package/dist/commands/project/push/index.d.ts.map +1 -0
  150. package/dist/commands/project/push/index.js +100 -0
  151. package/dist/commands/project/scope/index.d.ts +17 -0
  152. package/dist/commands/project/scope/index.d.ts.map +1 -0
  153. package/dist/commands/project/scope/index.js +107 -0
  154. package/dist/commands/project/split/run.d.ts +4 -0
  155. package/dist/commands/project/split/run.d.ts.map +1 -1
  156. package/dist/commands/project/split/run.js +48 -10
  157. package/dist/commands/project/sync/git.d.ts +18 -0
  158. package/dist/commands/project/sync/git.d.ts.map +1 -0
  159. package/dist/commands/project/sync/git.js +60 -0
  160. package/dist/commands/project/sync/record.d.ts +22 -0
  161. package/dist/commands/project/sync/record.d.ts.map +1 -0
  162. package/dist/commands/project/sync/record.js +133 -0
  163. package/dist/commands/project/sync/ref-sync.d.ts +120 -0
  164. package/dist/commands/project/sync/ref-sync.d.ts.map +1 -0
  165. package/dist/commands/project/sync/ref-sync.js +962 -0
  166. package/dist/commands/project/sync/resolve-target.d.ts +27 -0
  167. package/dist/commands/project/sync/resolve-target.d.ts.map +1 -0
  168. package/dist/commands/project/sync/resolve-target.js +159 -0
  169. package/dist/commands/shared/oat-paths.d.ts +4 -1
  170. package/dist/commands/shared/oat-paths.d.ts.map +1 -1
  171. package/dist/commands/shared/oat-paths.js +2 -2
  172. package/dist/commands/shared/project-scope.d.ts +23 -0
  173. package/dist/commands/shared/project-scope.d.ts.map +1 -0
  174. package/dist/commands/shared/project-scope.js +106 -0
  175. package/dist/commands/tools/migrate/index.js +1 -1
  176. package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
  177. package/dist/commands/tools/shared/pack-inventory.js +11 -0
  178. package/dist/commands/tools/shared/pack-lifecycle.js +1 -1
  179. package/dist/commands/tools/shared/pack-reconcile.d.ts.map +1 -1
  180. package/dist/commands/tools/shared/pack-reconcile.js +4 -1
  181. package/dist/commands/tools/update/index.d.ts.map +1 -1
  182. package/dist/commands/tools/update/index.js +20 -10
  183. package/dist/commands/tools/update/update-tools.d.ts +2 -0
  184. package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
  185. package/dist/config/oat-config.d.ts +6 -3
  186. package/dist/config/oat-config.d.ts.map +1 -1
  187. package/dist/config/oat-config.js +41 -7
  188. package/dist/config/resolve.d.ts.map +1 -1
  189. package/dist/config/resolve.js +2 -1
  190. package/dist/fs/io.d.ts +2 -1
  191. package/dist/fs/io.d.ts.map +1 -1
  192. package/dist/fs/io.js +5 -2
  193. package/dist/projects/split/finalize.d.ts.map +1 -1
  194. package/dist/projects/split/finalize.js +55 -2
  195. package/dist/projects/split/resume.d.ts +3 -0
  196. package/dist/projects/split/resume.d.ts.map +1 -1
  197. package/dist/projects/split/resume.js +34 -7
  198. package/dist/projects/split/seed-children.d.ts.map +1 -1
  199. package/dist/projects/split/seed-children.js +6 -0
  200. package/dist/projects/split/write-parent.d.ts +7 -0
  201. package/dist/projects/split/write-parent.d.ts.map +1 -1
  202. package/dist/projects/split/write-parent.js +7 -4
  203. package/dist/release/public-package-contract.d.ts.map +1 -1
  204. package/dist/release/public-package-contract.js +1 -0
  205. package/dist/validation/skills.d.ts.map +1 -1
  206. package/dist/validation/skills.js +434 -0
  207. package/package.json +4 -4
  208. package/dist/__tests__/skills/split-flow-fixtures.d.ts +0 -50
  209. package/dist/__tests__/skills/split-flow-fixtures.d.ts.map +0 -1
  210. package/dist/__tests__/skills/split-flow-fixtures.js +0 -161
  211. package/dist/commands/__tests__/helpers.d.ts +0 -12
  212. package/dist/commands/__tests__/helpers.d.ts.map +0 -1
  213. package/dist/commands/__tests__/helpers.js +0 -36
@@ -0,0 +1,157 @@
1
+ import { buildCommandContext, } from '../../../app/command-context.js';
2
+ import { defaultGitRunner } from '../../project/sync/git.js';
3
+ import { abortSynced as defaultAbortSynced, commitRecordChange as defaultCommitRecordChange, continueSynced as defaultContinueSynced, pullChildren as defaultPullChildren, pullSynced as defaultPullSynced, } from '../../project/sync/ref-sync.js';
4
+ import { resolveSyncedTarget, } from '../../project/sync/resolve-target.js';
5
+ import { readGlobalOptions } from '../../shared/shared.utils.js';
6
+ import { CliError } from '../../../errors/cli-error.js';
7
+ import { resolveProjectRoot } from '../../../fs/paths.js';
8
+ import { Command } from 'commander';
9
+ const DEFAULT_DEPENDENCIES = {
10
+ buildCommandContext,
11
+ resolveProjectRoot,
12
+ resolveSyncedTarget,
13
+ pullSynced: defaultPullSynced,
14
+ pullChildren: defaultPullChildren,
15
+ commitRecordChange: defaultCommitRecordChange,
16
+ continueSynced: defaultContinueSynced,
17
+ abortSynced: defaultAbortSynced,
18
+ gitRunner: defaultGitRunner,
19
+ processEnv: process.env,
20
+ };
21
+ function shellQuote(value) {
22
+ return `'${value.replaceAll("'", "'\\''")}'`;
23
+ }
24
+ function reportPullResult(context, result, target, targetArg, children = []) {
25
+ const payload = {
26
+ ...result,
27
+ adopted: result.adopted ?? false,
28
+ ref: target.ref,
29
+ children,
30
+ };
31
+ if (context.json) {
32
+ context.logger.json(payload);
33
+ }
34
+ else if (result.status === 'created' ||
35
+ result.status === 'updated' ||
36
+ result.status === 'up-to-date') {
37
+ context.logger.info(`Pull ${result.status}: ${target.slug} at ${result.sha}.`);
38
+ }
39
+ else if (result.status === 'conflict') {
40
+ context.logger.error(`Pull ${result.status}${result.conflicts?.length ? ` (${result.conflicts.join(', ')})` : ''}. Resolve the files, then run oat project pull ${shellQuote(targetArg)} --continue; or run oat project pull ${shellQuote(targetArg)} --abort.`);
41
+ }
42
+ else {
43
+ context.logger.error(`Pull dirty. Commit and publish with oat project push ${shellQuote(targetArg)}, or explicitly stash/clean the synced checkout before retrying oat project pull ${shellQuote(targetArg)}.`);
44
+ }
45
+ if (!context.json) {
46
+ for (const child of children) {
47
+ if (child.status !== 'missing' &&
48
+ child.status !== 'error' &&
49
+ child.status !== 'conflict' &&
50
+ child.status !== 'dirty') {
51
+ continue;
52
+ }
53
+ const childArg = shellQuote(child.slug);
54
+ const diagnostic = child.message ??
55
+ (child.conflicts?.length
56
+ ? `conflicts: ${child.conflicts.join(', ')}`
57
+ : 'no diagnostic provided');
58
+ const retry = child.status === 'conflict'
59
+ ? `Resolve its files, then run oat project pull ${childArg} --continue; or oat project pull ${childArg} --abort.`
60
+ : child.status === 'dirty'
61
+ ? `Run oat project push ${childArg}, or explicitly stash/clean it before retrying oat project pull ${childArg}.`
62
+ : child.status === 'error'
63
+ ? `Repair the reported Git/system failure, then retry oat project pull ${childArg}.`
64
+ : `Retry with oat project pull ${childArg}.`;
65
+ context.logger.error(`Child ${child.slug} ${child.status}: ${diagnostic}. ${retry}`);
66
+ }
67
+ }
68
+ const childExitCode = children.reduce((highest, child) => {
69
+ const current = child.exitCode ??
70
+ (child.status === 'error'
71
+ ? 2
72
+ : child.status === 'missing' ||
73
+ child.status === 'conflict' ||
74
+ child.status === 'dirty'
75
+ ? 1
76
+ : 0);
77
+ return current > highest ? current : highest;
78
+ }, 0);
79
+ process.exitCode =
80
+ result.status === 'created' ||
81
+ result.status === 'updated' ||
82
+ result.status === 'up-to-date'
83
+ ? childExitCode
84
+ : 1;
85
+ }
86
+ async function runPull(context, pathOrSlug, options, dependencies) {
87
+ try {
88
+ if (options.continue && options.abort) {
89
+ throw new CliError('`--continue` and `--abort` are mutually exclusive.', 1);
90
+ }
91
+ const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
92
+ const target = await dependencies.resolveSyncedTarget({ repoRoot, env: dependencies.processEnv }, pathOrSlug, {}, { allowMissingCheckout: !options.continue && !options.abort });
93
+ const targetArg = pathOrSlug ?? target.projectPath;
94
+ if (options.abort) {
95
+ await dependencies.abortSynced(target, dependencies.gitRunner);
96
+ if (context.json) {
97
+ context.logger.json({ status: 'aborted', ref: target.ref });
98
+ }
99
+ else {
100
+ context.logger.info(`Aborted pull recovery for ${target.slug}.`);
101
+ }
102
+ process.exitCode = 0;
103
+ return;
104
+ }
105
+ const result = options.continue
106
+ ? await dependencies.continueSynced(target, dependencies.gitRunner, {
107
+ adopt: target.adoptionRecord !== 'durable',
108
+ adoptionRecord: target.adoptionRecord,
109
+ })
110
+ : await dependencies.pullSynced(target, dependencies.gitRunner, {
111
+ adopt: target.adopt,
112
+ adoptionRecord: target.adoptionRecord,
113
+ });
114
+ const successful = result.status === 'created' ||
115
+ result.status === 'updated' ||
116
+ result.status === 'up-to-date';
117
+ const children = successful && options.children !== false
118
+ ? await dependencies.pullChildren(target, dependencies.gitRunner)
119
+ : [];
120
+ const pendingRecordPaths = [
121
+ ...(result.pendingRecordPaths ?? []),
122
+ ...children.flatMap((child) => child.pendingRecordPaths ?? []),
123
+ ];
124
+ if (options.commit !== false && pendingRecordPaths.length > 0) {
125
+ const slugs = [
126
+ ...(result.adopted ? [target.slug] : []),
127
+ ...children.filter((child) => child.adopted).map((child) => child.slug),
128
+ ];
129
+ await dependencies.commitRecordChange(repoRoot, pendingRecordPaths, `chore(oat): adopt synced project${slugs.length === 1 ? '' : 's'} ${slugs.join(', ')}`, dependencies.gitRunner, { projectRoots: target });
130
+ }
131
+ reportPullResult(context, result, target, targetArg, children);
132
+ }
133
+ catch (error) {
134
+ const message = error instanceof Error ? error.message : String(error);
135
+ if (context.json) {
136
+ context.logger.json({ status: 'error', message });
137
+ }
138
+ else {
139
+ context.logger.error(message);
140
+ }
141
+ process.exitCode = error instanceof CliError ? error.exitCode : 2;
142
+ }
143
+ }
144
+ export function createProjectPullCommand(overrides = {}) {
145
+ const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
146
+ return new Command('pull')
147
+ .description('Materialize or update a synced OAT project')
148
+ .argument('[project-path|slug]', 'Synced project path or slug')
149
+ .option('--continue', 'Continue a resolved sync rebase')
150
+ .option('--abort', 'Abort an in-progress sync rebase')
151
+ .option('--no-children', 'Do not pull coordination child projects')
152
+ .option('--no-commit', 'Leave adopted discovery records uncommitted')
153
+ .action(async (pathOrSlug, options, command) => {
154
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
155
+ await runPull(context, pathOrSlug, options, dependencies);
156
+ });
157
+ }
@@ -0,0 +1,22 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
3
+ import { refreshPrLinks } from '../../project/links/refresh.js';
4
+ import { type GitRunner } from '../../project/sync/git.js';
5
+ import { type PushResult, type SyncTarget } from '../../project/sync/ref-sync.js';
6
+ import { resolveSyncedTarget } from '../../project/sync/resolve-target.js';
7
+ import { Command } from 'commander';
8
+ interface ProjectPushDependencies {
9
+ buildCommandContext: (options: GlobalOptions) => CommandContext;
10
+ resolveProjectRoot: (cwd: string) => Promise<string>;
11
+ resolveSyncedTarget: typeof resolveSyncedTarget;
12
+ pushSynced: (target: SyncTarget, git: GitRunner, options: {
13
+ message?: string;
14
+ }) => Promise<PushResult>;
15
+ gitRunner: GitRunner;
16
+ processEnv: NodeJS.ProcessEnv;
17
+ refreshPrLinks: typeof refreshPrLinks;
18
+ readFile: typeof readFile;
19
+ }
20
+ export declare function createProjectPushCommand(overrides?: Partial<ProjectPushDependencies>): Command;
21
+ export {};
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/push/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAQ5E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,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,OAAO,mBAAmB,CAAC;IAChD,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,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,cAAc,EAAE,OAAO,cAAc,CAAC;IACtC,QAAQ,EAAE,OAAO,QAAQ,CAAC;CAC3B;AAqGD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAmBT"}
@@ -0,0 +1,100 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { buildCommandContext, } from '../../../app/command-context.js';
4
+ import { refreshPrLinks } from '../../project/links/refresh.js';
5
+ import { defaultGitRunner } from '../../project/sync/git.js';
6
+ import { pushSynced as defaultPushSynced, } from '../../project/sync/ref-sync.js';
7
+ import { resolveSyncedTarget } from '../../project/sync/resolve-target.js';
8
+ import { getFrontmatterBlock, parseFrontmatterScalarFields, } from '../../shared/frontmatter.js';
9
+ import { readGlobalOptions } from '../../shared/shared.utils.js';
10
+ import { CliError } from '../../../errors/cli-error.js';
11
+ import { resolveProjectRoot } from '../../../fs/paths.js';
12
+ import { Command } from 'commander';
13
+ const DEFAULT_DEPENDENCIES = {
14
+ buildCommandContext,
15
+ resolveProjectRoot,
16
+ resolveSyncedTarget,
17
+ pushSynced: defaultPushSynced,
18
+ gitRunner: defaultGitRunner,
19
+ processEnv: process.env,
20
+ refreshPrLinks,
21
+ readFile,
22
+ };
23
+ function shellQuote(value) {
24
+ return `'${value.replaceAll("'", "'\\''")}'`;
25
+ }
26
+ async function runPush(context, pathOrSlug, options, dependencies) {
27
+ try {
28
+ const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
29
+ const target = await dependencies.resolveSyncedTarget({ repoRoot, env: dependencies.processEnv }, pathOrSlug);
30
+ const result = await dependencies.pushSynced(target, dependencies.gitRunner, { message: options.message });
31
+ let prRefresh;
32
+ if (options.refreshPr &&
33
+ (result.status === 'pushed' || result.status === 'up-to-date')) {
34
+ const state = await dependencies
35
+ .readFile(join(target.projectPath, 'state.md'), 'utf8')
36
+ .catch(() => null);
37
+ const frontmatter = state ? getFrontmatterBlock(state) : null;
38
+ if (frontmatter) {
39
+ const parsed = parseFrontmatterScalarFields(frontmatter, [
40
+ 'oat_pr_status',
41
+ 'oat_pr_url',
42
+ ]);
43
+ if (parsed.valid &&
44
+ parsed.values.oat_pr_status === 'open' &&
45
+ parsed.values.oat_pr_url) {
46
+ try {
47
+ prRefresh = await dependencies.refreshPrLinks(target, parsed.values.oat_pr_url, { warn: (message) => context.logger.warn(message) });
48
+ }
49
+ catch (error) {
50
+ const message = error instanceof Error ? error.message : String(error);
51
+ context.logger.warn(`Could not refresh PR links after successfully pushing ${target.slug}: ${message}`);
52
+ prRefresh = 'failed';
53
+ }
54
+ }
55
+ }
56
+ }
57
+ const payload = { ...result, ref: target.ref, prRefresh };
58
+ if (context.json) {
59
+ context.logger.json(payload);
60
+ }
61
+ else if (result.status === 'pushed') {
62
+ context.logger.info(`Pushed ${target.slug} at ${result.sha}.`);
63
+ }
64
+ else if (result.status === 'up-to-date') {
65
+ context.logger.info(`${target.slug} is up to date at ${result.sha}.`);
66
+ }
67
+ else if (result.status === 'conflict') {
68
+ const targetArg = pathOrSlug ?? target.projectPath;
69
+ context.logger.error(`Push conflict${result.conflicts?.length ? ` (${result.conflicts.join(', ')})` : ''}. Resolve the files, then run oat project pull ${shellQuote(targetArg)} --continue; or run oat project pull ${shellQuote(targetArg)} --abort.`);
70
+ }
71
+ else {
72
+ const targetArg = pathOrSlug ?? target.projectPath;
73
+ context.logger.error(`Push rejected. Run oat project pull ${shellQuote(targetArg)}, then run oat project push ${shellQuote(targetArg)}.`);
74
+ }
75
+ process.exitCode =
76
+ result.status === 'pushed' || result.status === 'up-to-date' ? 0 : 1;
77
+ }
78
+ catch (error) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ if (context.json) {
81
+ context.logger.json({ status: 'error', message });
82
+ }
83
+ else {
84
+ context.logger.error(message);
85
+ }
86
+ process.exitCode = error instanceof CliError ? error.exitCode : 2;
87
+ }
88
+ }
89
+ export function createProjectPushCommand(overrides = {}) {
90
+ const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
91
+ return new Command('push')
92
+ .description('Commit and publish a synced OAT project')
93
+ .argument('[project-path|slug]', 'Synced project path or slug')
94
+ .option('--message <message>', 'Artifact commit message')
95
+ .option('--no-refresh-pr', 'Do not refresh PR artifact links')
96
+ .action(async (pathOrSlug, options, command) => {
97
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
98
+ await runPush(context, pathOrSlug, options, dependencies);
99
+ });
100
+ }
@@ -0,0 +1,17 @@
1
+ import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
2
+ import { readSyncedRecord } from '../../project/sync/record.js';
3
+ import { isSyncedCheckout } from '../../shared/project-scope.js';
4
+ import { type OatLocalConfig } from '../../../config/oat-config.js';
5
+ import { Command } from 'commander';
6
+ interface ProjectScopeDependencies {
7
+ buildCommandContext: (options: GlobalOptions) => CommandContext;
8
+ resolveProjectRoot: (cwd: string) => Promise<string>;
9
+ resolveProjectsRoot: (repoRoot: string, env: NodeJS.ProcessEnv) => Promise<string>;
10
+ readOatLocalConfig: (repoRoot: string) => Promise<OatLocalConfig>;
11
+ readSyncedRecord: typeof readSyncedRecord;
12
+ isSyncedCheckout: typeof isSyncedCheckout;
13
+ processEnv: NodeJS.ProcessEnv;
14
+ }
15
+ export declare function createProjectScopeCommand(overrides?: Partial<ProjectScopeDependencies>): Command;
16
+ export {};
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/scope/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,OAAO,EAEL,gBAAgB,EAKjB,MAAM,gCAAgC,CAAC;AAExC,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAG7E,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAM5C,UAAU,wBAAwB;IAChC,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,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAoHD,wBAAgB,yBAAyB,CACvC,SAAS,GAAE,OAAO,CAAC,wBAAwB,CAAM,GAChD,OAAO,CAuBT"}
@@ -0,0 +1,107 @@
1
+ import { basename, isAbsolute, join, relative, resolve, sep } from 'node:path';
2
+ import { buildCommandContext, } from '../../../app/command-context.js';
3
+ import { readSyncedRecord } from '../../project/sync/record.js';
4
+ import { resolveProjectsRoot } from '../../shared/oat-paths.js';
5
+ import { canonicalizePath, isSyncedCheckout, resolveProjectScope, resolveProjectsParent, resolveScopeRoot, syncedRecordPath, } from '../../shared/project-scope.js';
6
+ import { readGlobalOptions } from '../../shared/shared.utils.js';
7
+ import { readOatLocalConfig } from '../../../config/oat-config.js';
8
+ import { CliError } from '../../../errors/cli-error.js';
9
+ import { resolveProjectRoot } from '../../../fs/paths.js';
10
+ import { Command, Option } from 'commander';
11
+ const DEFAULT_DEPENDENCIES = {
12
+ buildCommandContext,
13
+ resolveProjectRoot,
14
+ resolveProjectsRoot,
15
+ readOatLocalConfig,
16
+ readSyncedRecord,
17
+ isSyncedCheckout,
18
+ processEnv: process.env,
19
+ };
20
+ function isWithin(root, candidate) {
21
+ const child = relative(root, candidate);
22
+ return (child === '' ||
23
+ (child !== '..' && !child.startsWith(`..${sep}`) && !isAbsolute(child)));
24
+ }
25
+ async function runProjectScope(context, pathArg, options, dependencies) {
26
+ try {
27
+ const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
28
+ const projectsRoot = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
29
+ const canonicalRepoRoot = canonicalizePath(repoRoot);
30
+ const configuredRoot = canonicalizePath(isAbsolute(projectsRoot)
31
+ ? resolve(projectsRoot)
32
+ : resolve(repoRoot, projectsRoot));
33
+ const configuredParent = resolveProjectsParent(repoRoot, projectsRoot);
34
+ const archivedRoot = join(configuredParent, 'archived');
35
+ let target = pathArg;
36
+ if (!target) {
37
+ target =
38
+ (await dependencies.readOatLocalConfig(repoRoot)).activeProject ??
39
+ undefined;
40
+ if (!target) {
41
+ throw new CliError('No active project is configured.', 1);
42
+ }
43
+ }
44
+ const absoluteProjectPath = canonicalizePath(isAbsolute(target) ? resolve(target) : resolve(repoRoot, target));
45
+ if (isWithin(archivedRoot, absoluteProjectPath)) {
46
+ throw new CliError('Archived projects have no active project scope. Restore or open a live project instead.', 1);
47
+ }
48
+ const scope = resolveProjectScope(absoluteProjectPath, configuredRoot, repoRoot);
49
+ if (!scope) {
50
+ throw new CliError(`Project path is outside the shared, local, and synced scope roots: ${target}`, 1);
51
+ }
52
+ const projectPath = relative(canonicalRepoRoot, absoluteProjectPath)
53
+ .split(sep)
54
+ .join('/');
55
+ const payload = {
56
+ status: 'ok',
57
+ projectPath,
58
+ scope,
59
+ checkout: 'n/a',
60
+ };
61
+ if (scope === 'synced') {
62
+ const slug = basename(absoluteProjectPath);
63
+ const scopeRoot = resolveScopeRoot(repoRoot, projectsRoot, 'synced');
64
+ const record = await dependencies.readSyncedRecord(syncedRecordPath(scopeRoot, slug));
65
+ payload['ref'] = record?.ref;
66
+ payload['record'] = record ?? undefined;
67
+ payload['checkout'] = (await dependencies.isSyncedCheckout(absoluteProjectPath))
68
+ ? 'present'
69
+ : 'absent';
70
+ }
71
+ if (options.format === 'value') {
72
+ context.logger.info(scope);
73
+ }
74
+ else if (context.json || options.format === 'json') {
75
+ context.logger.json(payload);
76
+ }
77
+ else {
78
+ context.logger.info(`Project: ${projectPath}`);
79
+ context.logger.info(`Scope: ${scope}`);
80
+ }
81
+ process.exitCode = 0;
82
+ }
83
+ catch (error) {
84
+ const message = error instanceof Error ? error.message : String(error);
85
+ if (context.json || options.format === 'json') {
86
+ context.logger.json({ status: 'error', message });
87
+ }
88
+ else {
89
+ context.logger.error(message);
90
+ }
91
+ process.exitCode = error instanceof CliError ? error.exitCode : 2;
92
+ }
93
+ }
94
+ export function createProjectScopeCommand(overrides = {}) {
95
+ const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
96
+ return new Command('scope')
97
+ .description('Report the scope of an OAT project')
98
+ .argument('[project-path]', 'Project path; defaults to activeProject')
99
+ .addOption(new Option('--format <format>', 'Output format').choices([
100
+ 'json',
101
+ 'value',
102
+ ]))
103
+ .action(async (projectPath, options, command) => {
104
+ const context = dependencies.buildCommandContext(readGlobalOptions(command));
105
+ await runProjectScope(context, projectPath, options, dependencies);
106
+ });
107
+ }
@@ -1,5 +1,7 @@
1
1
  import { appendFile, readFile, readdir, stat } from 'node:fs/promises';
2
2
  import { type CommandContext, type GlobalOptions } from '../../../app/command-context.js';
3
+ import { type GitRunner } from '../../project/sync/git.js';
4
+ import { pushSynced as defaultPushSynced } from '../../project/sync/ref-sync.js';
3
5
  import { confirmAction } from '../../shared/shared.prompts.js';
4
6
  import { Command } from 'commander';
5
7
  interface RunSplitDependencies {
@@ -15,6 +17,8 @@ interface RunSplitDependencies {
15
17
  }) => Promise<void>;
16
18
  confirmAction: typeof confirmAction;
17
19
  processEnv: NodeJS.ProcessEnv;
20
+ gitRunner: GitRunner;
21
+ pushSynced: typeof defaultPushSynced;
18
22
  }
19
23
  export declare function createProjectSplitRunCommand(overrides?: Partial<RunSplitDependencies>): Command;
20
24
  export {};
@@ -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;AAE9B,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAKhE,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;CAC/B;AAsLD,wBAAgB,4BAA4B,CAC1C,SAAS,GAAE,OAAO,CAAC,oBAAoB,CAAM,GAC5C,OAAO,CAgKT"}
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,11 +1,15 @@
1
1
  import { appendFile, readFile, readdir, stat } from 'node:fs/promises';
2
2
  import { isAbsolute, join, relative } from 'node:path';
3
3
  import { buildCommandContext, } from '../../../app/command-context.js';
4
+ import { defaultGitRunner } from '../../project/sync/git.js';
5
+ import { pushSynced as defaultPushSynced } from '../../project/sync/ref-sync.js';
4
6
  import { resolveProjectsRoot } from '../../shared/oat-paths.js';
7
+ import { resolveProjectScope, resolveScopeRoot, } from '../../shared/project-scope.js';
5
8
  import { confirmAction } from '../../shared/shared.prompts.js';
6
9
  import { readGlobalOptions } from '../../shared/shared.utils.js';
7
10
  import { generateStateDashboard } from '../../state/generate.js';
8
11
  import { readOatLocalConfig } from '../../../config/oat-config.js';
12
+ import { CliError } from '../../../errors/cli-error.js';
9
13
  import { resolveProjectRoot } from '../../../fs/paths.js';
10
14
  import { Command } from 'commander';
11
15
  import { validateSplitPlanDocumentShape } from '../../../projects/split/document-validation.js';
@@ -27,6 +31,8 @@ const DEFAULT_DEPENDENCIES = {
27
31
  },
28
32
  confirmAction,
29
33
  processEnv: process.env,
34
+ gitRunner: defaultGitRunner,
35
+ pushSynced: defaultPushSynced,
30
36
  };
31
37
  async function exists(path, dependencies) {
32
38
  try {
@@ -122,13 +128,33 @@ async function runFreshSplit(document, repoRoot, projectsRoot, dependencies, opt
122
128
  }
123
129
  const validation = validateChildPlan(document.plan, slugs);
124
130
  if (!validation.ok) {
125
- throw new Error(`Split plan validation failed: ${validation.errors
131
+ throw new CliError(`Split plan validation failed: ${validation.errors
126
132
  .map((error) => error.message)
127
- .join('; ')}`);
133
+ .join('; ')}`, 1);
128
134
  }
129
- await writeCoordinationParent(document, { repoRoot, projectsRoot });
130
- await seedChildren(document.plan, { repoRoot, projectsRoot });
131
- await finalizeSplit(document.plan, { repoRoot, projectsRoot });
135
+ const localConfig = await readOatLocalConfig(repoRoot);
136
+ const scope = localConfig.activeProject
137
+ ? resolveProjectScope(localConfig.activeProject, projectsRoot, repoRoot)
138
+ : null;
139
+ const inheritedScope = scope ?? 'shared';
140
+ const absoluteScopeRoot = inheritedScope === 'shared'
141
+ ? isAbsolute(projectsRoot)
142
+ ? projectsRoot
143
+ : join(repoRoot, projectsRoot)
144
+ : resolveScopeRoot(repoRoot, projectsRoot, inheritedScope);
145
+ const scopeRoot = toRepoRelativeProjectPath(repoRoot, absoluteScopeRoot);
146
+ const splitContext = {
147
+ repoRoot,
148
+ projectsRoot,
149
+ scope: inheritedScope,
150
+ scopeRoot,
151
+ env: dependencies.processEnv,
152
+ gitRunner: dependencies.gitRunner,
153
+ pushSynced: dependencies.pushSynced,
154
+ };
155
+ await writeCoordinationParent(document, splitContext);
156
+ await seedChildren(document.plan, splitContext);
157
+ await finalizeSplit(document.plan, splitContext);
132
158
  }
133
159
  export function createProjectSplitRunCommand(overrides = {}) {
134
160
  const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
@@ -146,9 +172,9 @@ export function createProjectSplitRunCommand(overrides = {}) {
146
172
  const parsed = JSON.parse(raw);
147
173
  const documentShape = validateSplitPlanDocumentShape(parsed);
148
174
  if (!documentShape.ok) {
149
- throw new Error(`Invalid SplitPlanDocument: ${documentShape.errors
175
+ throw new CliError(`Invalid SplitPlanDocument: ${documentShape.errors
150
176
  .map((error) => error.message)
151
- .join('; ')}`);
177
+ .join('; ')}`, 1);
152
178
  }
153
179
  const document = documentShape.document;
154
180
  if (isEffectivelyNonInteractive(options, context, dependencies.processEnv) &&
@@ -159,7 +185,14 @@ export function createProjectSplitRunCommand(overrides = {}) {
159
185
  process.exitCode = 1;
160
186
  return;
161
187
  }
162
- const parentPath = join(projectsRoot, document.plan.parentSlug)
188
+ const localConfig = await readOatLocalConfig(repoRoot);
189
+ const activeScope = localConfig.activeProject
190
+ ? resolveProjectScope(localConfig.activeProject, projectsRoot, repoRoot)
191
+ : null;
192
+ const parentRoot = activeScope && activeScope !== 'shared'
193
+ ? resolveScopeRoot(repoRoot, projectsRoot, activeScope)
194
+ : projectsRoot;
195
+ const parentPath = join(parentRoot, document.plan.parentSlug)
163
196
  .split('\\')
164
197
  .join('/');
165
198
  const absoluteParentPath = isAbsolute(parentPath)
@@ -216,7 +249,12 @@ export function createProjectSplitRunCommand(overrides = {}) {
216
249
  throw new SplitResumeError('Split resume cancelled.');
217
250
  }
218
251
  }
219
- await continueSplitResume(partial, { repoRoot, projectsRoot });
252
+ await continueSplitResume(partial, {
253
+ repoRoot,
254
+ projectsRoot,
255
+ gitRunner: dependencies.gitRunner,
256
+ pushSynced: dependencies.pushSynced,
257
+ });
220
258
  }
221
259
  else {
222
260
  await runFreshSplit(document, repoRoot, projectsRoot, dependencies);
@@ -239,7 +277,7 @@ export function createProjectSplitRunCommand(overrides = {}) {
239
277
  process.exitCode = 1;
240
278
  return;
241
279
  }
242
- process.exitCode = 1;
280
+ process.exitCode = error instanceof CliError ? error.exitCode : 2;
243
281
  }
244
282
  });
245
283
  }
@@ -0,0 +1,18 @@
1
+ import { type ExecFileException, type ExecFileOptionsWithStringEncoding } from 'node:child_process';
2
+ export interface GitResult {
3
+ stdout: string;
4
+ stderr: string;
5
+ code: number;
6
+ }
7
+ export interface GitRunOptions {
8
+ cwd: string;
9
+ env?: NodeJS.ProcessEnv;
10
+ allowFailure?: boolean;
11
+ }
12
+ export interface GitRunner {
13
+ run(args: string[], options: GitRunOptions): Promise<GitResult>;
14
+ }
15
+ export type ExecFileImplementation = (file: string, args: readonly string[], options: ExecFileOptionsWithStringEncoding, callback: (error: ExecFileException | null, stdout: string, stderr: string) => void) => unknown;
16
+ export declare function createGitRunner(execFileImpl?: ExecFileImplementation): GitRunner;
17
+ export declare const defaultGitRunner: GitRunner;
18
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/sync/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,iCAAiC,EACvC,MAAM,oBAAoB,CAAC;AAI5B,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;CACjE;AAED,MAAM,MAAM,sBAAsB,GAAG,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,EAAE,iCAAiC,EAC1C,QAAQ,EAAE,CACR,KAAK,EAAE,iBAAiB,GAAG,IAAI,EAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,KACX,IAAI,KACN,OAAO,CAAC;AAqCb,wBAAgB,eAAe,CAC7B,YAAY,GAAE,sBAAiC,GAC9C,SAAS,CAqCX;AAED,eAAO,MAAM,gBAAgB,WAAoB,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { execFile, } from 'node:child_process';
2
+ import { CliError } from '../../../errors/cli-error.js';
3
+ function numericExitCode(error) {
4
+ return typeof error?.code === 'number' ? error.code : 0;
5
+ }
6
+ function spawnFailure(error) {
7
+ const code = typeof error.code === 'string' ? ` (${error.code})` : '';
8
+ return new CliError(`Unable to run the Git executable${code}: ${error.message || 'unknown process spawn error'}. Install Git and ensure it is available on PATH.`, 2);
9
+ }
10
+ function gitEnvironment(overrides) {
11
+ const env = {
12
+ ...process.env,
13
+ ...overrides,
14
+ LANG: 'C',
15
+ LANGUAGE: 'C',
16
+ LC_ALL: 'C',
17
+ };
18
+ for (const variable of [
19
+ 'GIT_DIR',
20
+ 'GIT_WORK_TREE',
21
+ 'GIT_INDEX_FILE',
22
+ 'GIT_COMMON_DIR',
23
+ 'GIT_OBJECT_DIRECTORY',
24
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
25
+ 'GIT_NAMESPACE',
26
+ 'GIT_CEILING_DIRECTORIES',
27
+ ]) {
28
+ delete env[variable];
29
+ }
30
+ return env;
31
+ }
32
+ export function createGitRunner(execFileImpl = execFile) {
33
+ return {
34
+ async run(args, options) {
35
+ const result = await new Promise((resolve, reject) => {
36
+ execFileImpl('git', args, {
37
+ cwd: options.cwd,
38
+ env: gitEnvironment(options.env),
39
+ encoding: 'utf8',
40
+ }, (error, stdout, stderr) => {
41
+ if (error && typeof error.code !== 'number') {
42
+ reject(spawnFailure(error));
43
+ return;
44
+ }
45
+ resolve({
46
+ stdout: stdout.trim(),
47
+ stderr: stderr.trim(),
48
+ code: numericExitCode(error),
49
+ });
50
+ });
51
+ });
52
+ if (result.code !== 0 && !options.allowFailure) {
53
+ const detail = result.stderr || result.stdout || 'unknown git error';
54
+ throw new CliError(`git ${args.join(' ')} failed (exit ${result.code}): ${detail}`, 2);
55
+ }
56
+ return result;
57
+ },
58
+ };
59
+ }
60
+ export const defaultGitRunner = createGitRunner();