@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
@@ -1,15 +1,31 @@
1
- import { execFileSync } from 'node:child_process';
2
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
1
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
3
2
  import { homedir } from 'node:os';
4
- import { join } from 'node:path';
3
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
4
+ import { ensureScopedRootGitignore } from '../../init/gitignore.js';
5
5
  import { instantiateProjectLogTemplate } from '../../project/log/append.js';
6
+ import { defaultGitRunner } from '../../project/sync/git.js';
7
+ import { buildSyncedRecord, writeSyncedRecord, } from '../../project/sync/record.js';
8
+ import { buildSyncTarget, commitRecordChange, createSyncedProject, pushSynced, rollbackCreatedSyncedProject, } from '../../project/sync/ref-sync.js';
6
9
  import { resolveProjectsRoot } from '../../shared/oat-paths.js';
10
+ import { canonicalizePath, PROJECT_SCOPES, resolveDefaultScope, resolveScopeRoot, syncedRecordPath, } from '../../shared/project-scope.js';
7
11
  import { generateStateDashboard } from '../../state/generate.js';
8
12
  import { setActiveProject } from '../../../config/oat-config.js';
9
13
  import { resolveEffectiveConfig } from '../../../config/resolve.js';
14
+ import { CliError } from '../../../errors/cli-error.js';
10
15
  import { resolveAssetsRoot } from '../../../fs/assets.js';
11
- import { fileExists } from '../../../fs/io.js';
16
+ import { dirExists, fileExists } from '../../../fs/io.js';
12
17
  import { assertValidProjectStateContent } from '../../../validation/project-state.js';
18
+ const DEFAULT_DEPENDENCIES = {
19
+ resolveDefaultScope,
20
+ gitRunner: defaultGitRunner,
21
+ createSyncedProject,
22
+ pushSynced,
23
+ commitRecordChange,
24
+ rollbackCreatedSyncedProject,
25
+ writeSyncedRecord,
26
+ ensureScopedRootGitignore,
27
+ setActiveProject,
28
+ };
13
29
  const TEMPLATES_BY_MODE = {
14
30
  'spec-driven': [
15
31
  'state.md',
@@ -95,12 +111,15 @@ function assertNoUnresolvedOatPlaceholders(rendered, templateFile) {
95
111
  }
96
112
  function validateProjectName(name) {
97
113
  if (name.startsWith('-')) {
98
- throw new Error(`Invalid project name "${name}". Project names must not start with a dash.`);
114
+ throw new CliError(`Invalid project name "${name}". Project names must not start with a dash.`, 1);
99
115
  }
100
116
  if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
101
- throw new Error(`Invalid project name "${name}". Use only letters, numbers, dash, and underscore.`);
117
+ throw new CliError(`Invalid project name "${name}". Use only letters, numbers, dash, and underscore.`, 1);
102
118
  }
103
119
  }
120
+ function shellQuote(value) {
121
+ return `'${value.replaceAll("'", "'\\''")}'`;
122
+ }
104
123
  function applyTemplateReplacements(template, projectName, today, nowUtc, mode) {
105
124
  const stateContent = STATE_TEMPLATE_BY_MODE[mode];
106
125
  const oatReplacements = {
@@ -135,18 +154,9 @@ async function defaultRefreshDashboard(repoRoot) {
135
154
  * git failure; on failure the captured git stderr is surfaced via `error`. This
136
155
  * never throws: any git error is classified as `failed`, not propagated.
137
156
  */
138
- function commitScaffold(cwd, projectPath, projectName, createdFiles) {
139
- const run = (args) => execFileSync('git', args, {
140
- cwd,
141
- encoding: 'utf8',
142
- // Capture stderr instead of inheriting it so deliberate skip/failure
143
- // probes (e.g. tests) do not leak raw `git fatal:` lines to the terminal.
144
- stdio: ['ignore', 'pipe', 'pipe'],
145
- }).trim();
146
- try {
147
- run(['rev-parse', '--is-inside-work-tree']);
148
- }
149
- catch {
157
+ async function commitScaffold(cwd, absoluteProjectPath, projectName, createdFiles, dependencies) {
158
+ const inWorktree = await dependencies.gitRunner.run(['rev-parse', '--is-inside-work-tree'], { cwd, allowFailure: true });
159
+ if (inWorktree.code !== 0) {
150
160
  return { status: 'skipped_no_worktree', committed: false };
151
161
  }
152
162
  // Only commit files this run created. Nothing created => nothing to commit,
@@ -154,38 +164,30 @@ function commitScaffold(cwd, projectPath, projectName, createdFiles) {
154
164
  if (createdFiles.length === 0) {
155
165
  return { status: 'skipped_nothing', committed: false };
156
166
  }
157
- const pathspecs = createdFiles.map((file) => join(projectPath, file));
167
+ const pathspecs = createdFiles.map((file) => join(absoluteProjectPath, file));
158
168
  try {
159
- run(['add', '--', ...pathspecs]);
160
- const staged = run(['diff', '--cached', '--name-only', '--', ...pathspecs]);
161
- if (staged.length === 0) {
169
+ const commit = await dependencies.commitRecordChange(cwd, pathspecs, `chore(oat): scaffold ${projectName}`, dependencies.gitRunner, {
170
+ projectRoots: {
171
+ sharedRoot: dirname(absoluteProjectPath),
172
+ syncedRoot: join(dirname(dirname(absoluteProjectPath)), 'synced'),
173
+ },
174
+ });
175
+ if (!commit) {
162
176
  return { status: 'skipped_nothing', committed: false };
163
177
  }
164
- run([
165
- 'commit',
166
- '-m',
167
- `chore(oat): scaffold ${projectName}`,
168
- '--',
169
- ...pathspecs,
170
- ]);
171
- const commitSha = run(['rev-parse', 'HEAD']);
172
- return { status: 'committed', committed: true, commitSha };
178
+ return { status: 'committed', committed: true, commitSha: commit.sha };
173
179
  }
174
180
  catch (error) {
175
- const stderr = error && typeof error === 'object' && 'stderr' in error
176
- ? error.stderr
177
- : undefined;
178
- const message = (stderr != null ? stderr.toString().trim() : '') ||
179
- (error instanceof Error ? error.message : String(error));
181
+ const message = error instanceof Error ? error.message : String(error);
180
182
  return { status: 'failed', committed: false, error: message };
181
183
  }
182
184
  }
183
- async function scaffoldModeTemplates(userOatRoot, repoRoot, projectPath, projectName, mode, today, nowUtc) {
185
+ async function scaffoldModeTemplates(userOatRoot, repoRoot, absoluteProjectPath, projectName, mode, today, nowUtc) {
184
186
  const createdFiles = [];
185
187
  const skippedFiles = [];
186
188
  for (const templateFile of TEMPLATES_BY_MODE[mode]) {
187
189
  const src = await resolveTemplateSource(userOatRoot, repoRoot, templateFile);
188
- const dest = join(repoRoot, projectPath, templateFile);
190
+ const dest = join(absoluteProjectPath, templateFile);
189
191
  if (await fileExists(dest)) {
190
192
  skippedFiles.push(templateFile);
191
193
  continue;
@@ -201,9 +203,9 @@ async function scaffoldModeTemplates(userOatRoot, repoRoot, projectPath, project
201
203
  }
202
204
  return { createdFiles, skippedFiles };
203
205
  }
204
- async function scaffoldProjectLog(userOatRoot, repoRoot, projectPath, projectName, today) {
206
+ async function scaffoldProjectLog(userOatRoot, repoRoot, absoluteProjectPath, projectName, today) {
205
207
  const templateFile = 'project-log.md';
206
- const dest = join(repoRoot, projectPath, templateFile);
208
+ const dest = join(absoluteProjectPath, templateFile);
207
209
  if (await fileExists(dest)) {
208
210
  return 'skipped';
209
211
  }
@@ -224,13 +226,12 @@ async function resolveTemplateSource(userOatRoot, repoRoot, templateFile) {
224
226
  const assetsRoot = await resolveAssetsRoot();
225
227
  return join(assetsRoot, 'templates', templateFile);
226
228
  }
227
- async function ensureStructure(repoRoot, projectPath, mode) {
228
- const projectRoot = join(repoRoot, projectPath);
229
- await mkdir(projectRoot, { recursive: true });
230
- await mkdir(join(projectRoot, 'reviews'), { recursive: true });
231
- await mkdir(join(projectRoot, 'pr'), { recursive: true });
229
+ async function ensureStructure(absoluteProjectPath, mode) {
230
+ await mkdir(absoluteProjectPath, { recursive: true });
231
+ await mkdir(join(absoluteProjectPath, 'reviews'), { recursive: true });
232
+ await mkdir(join(absoluteProjectPath, 'pr'), { recursive: true });
232
233
  if (mode === 'import') {
233
- const referencesDir = join(projectRoot, 'references');
234
+ const referencesDir = join(absoluteProjectPath, 'references');
234
235
  await mkdir(referencesDir, { recursive: true });
235
236
  const gitkeepPath = join(referencesDir, '.gitkeep');
236
237
  if (!(await fileExists(gitkeepPath))) {
@@ -238,7 +239,53 @@ async function ensureStructure(repoRoot, projectPath, mode) {
238
239
  }
239
240
  }
240
241
  }
241
- export async function scaffoldProject(options) {
242
+ async function ensureScopedProjectIgnored(repoRoot, absoluteScopeRoot, scope, dependencies) {
243
+ return dependencies.ensureScopedRootGitignore(repoRoot, absoluteScopeRoot, scope, dependencies.gitRunner, 'Cannot add the configured project-root rule because .gitignore has staged or unstaged changes; commit or stash those changes, then retry.');
244
+ }
245
+ async function assertCrossScopeSlugAvailable(repoRoot, projectsRoot, projectName, targetScope, dependencies) {
246
+ const collisions = [];
247
+ for (const scope of PROJECT_SCOPES) {
248
+ if (scope === targetScope)
249
+ continue;
250
+ const scopeRoot = resolveScopeRoot(repoRoot, projectsRoot, scope);
251
+ if (await dirExists(join(scopeRoot, projectName))) {
252
+ collisions.push(scope);
253
+ continue;
254
+ }
255
+ if (scope === 'synced' &&
256
+ (await fileExists(syncedRecordPath(scopeRoot, projectName)))) {
257
+ collisions.push(scope);
258
+ }
259
+ }
260
+ if (targetScope !== 'synced' && !collisions.includes('synced')) {
261
+ const workTree = await dependencies.gitRunner.run(['rev-parse', '--is-inside-work-tree'], { cwd: repoRoot, allowFailure: true });
262
+ if (workTree.code === 0) {
263
+ const syncedTarget = buildSyncTarget(repoRoot, projectsRoot, projectName);
264
+ const localRef = await dependencies.gitRunner.run(['show-ref', '--verify', '--quiet', syncedTarget.ref], { cwd: repoRoot, allowFailure: true });
265
+ if (localRef.code !== 0 && localRef.code !== 1) {
266
+ throw new CliError(`Unable to check synced project collision for ${projectName}: ${localRef.stderr || localRef.stdout || 'git show-ref failed'}`, 2);
267
+ }
268
+ if (localRef.code === 0) {
269
+ collisions.push('synced');
270
+ }
271
+ const remote = await dependencies.gitRunner.run(['remote', 'get-url', 'origin'], { cwd: repoRoot, allowFailure: true });
272
+ if (remote.code === 0 && localRef.code === 1) {
273
+ const remoteRef = await dependencies.gitRunner.run(['ls-remote', '--exit-code', syncedTarget.remote, syncedTarget.ref], { cwd: repoRoot, allowFailure: true });
274
+ if (remoteRef.code === 0) {
275
+ collisions.push('synced');
276
+ }
277
+ else if (remoteRef.code !== 2) {
278
+ console.error(`Warning: unable to verify remote synced project collision for ${projectName}; continuing ${targetScope} project creation: ${remoteRef.stderr || remoteRef.stdout || 'git ls-remote failed'}`);
279
+ }
280
+ }
281
+ }
282
+ }
283
+ if (collisions.length > 0) {
284
+ throw new CliError(`Project name "${projectName}" already exists in ${collisions.join(', ')} scope. Choose a unique name, or pass --force and use an explicit project path when opening the duplicate.`, 1);
285
+ }
286
+ }
287
+ export async function scaffoldProject(options, overrides = {}) {
288
+ const dependencies = { ...DEFAULT_DEPENDENCIES, ...overrides };
242
289
  const mode = options.mode ?? 'spec-driven';
243
290
  const setActive = options.setActive ?? true;
244
291
  const refreshDashboard = options.refreshDashboard ?? true;
@@ -254,25 +301,197 @@ export async function scaffoldProject(options) {
254
301
  const nowUtc = options.nowUtc ?? now.toISOString();
255
302
  validateProjectName(options.projectName);
256
303
  const projectsRoot = await resolveProjectsRoot(options.repoRoot, env);
257
- const projectPath = join(projectsRoot, options.projectName);
258
- // `--force` is currently accepted for compatibility with the legacy script.
259
- // Scaffold behavior is always non-destructive (create missing files only).
260
- void options.force;
261
- await ensureStructure(options.repoRoot, projectPath, mode);
262
- const { createdFiles, skippedFiles } = await scaffoldModeTemplates(userOatRoot, options.repoRoot, projectPath, options.projectName, mode, today, nowUtc);
263
- const effectiveProjectLog = options.projectLog ??
264
- (await resolveEffectiveConfig(options.repoRoot, userOatRoot, env)).resolved['workflow.projectLog']?.value === true;
265
- if (effectiveProjectLog) {
266
- const projectLogResult = await scaffoldProjectLog(userOatRoot, options.repoRoot, projectPath, options.projectName, today);
267
- if (projectLogResult === 'created') {
268
- createdFiles.push('project-log.md');
304
+ const scope = options.scope ??
305
+ (await dependencies.resolveDefaultScope(options.repoRoot, env));
306
+ const absoluteScopeRoot = canonicalizePath(scope === 'shared'
307
+ ? resolve(options.repoRoot, projectsRoot)
308
+ : resolveScopeRoot(options.repoRoot, projectsRoot, scope));
309
+ const absoluteProjectPath = canonicalizePath(join(absoluteScopeRoot, options.projectName));
310
+ const relativeProjectPath = relative(canonicalizePath(options.repoRoot), absoluteProjectPath);
311
+ const projectPath = relativeProjectPath === '..' ||
312
+ relativeProjectPath.startsWith('../') ||
313
+ relativeProjectPath.startsWith('..\\') ||
314
+ isAbsolute(relativeProjectPath)
315
+ ? absoluteProjectPath
316
+ : relativeProjectPath.split('\\').join('/');
317
+ if (scope === 'synced') {
318
+ const canonicalRepoRoot = canonicalizePath(options.repoRoot);
319
+ const relativeSyncedRoot = relative(canonicalRepoRoot, absoluteScopeRoot);
320
+ if (relativeSyncedRoot === '' ||
321
+ relativeSyncedRoot === '..' ||
322
+ relativeSyncedRoot.startsWith('../') ||
323
+ relativeSyncedRoot.startsWith('..\\') ||
324
+ isAbsolute(relativeSyncedRoot)) {
325
+ throw new CliError(`Cannot create synced project ${options.projectName}: the configured synced project root \`${absoluteScopeRoot}\` is outside repository \`${canonicalRepoRoot}\`. Use an in-repository projects.root for synced scope, or choose shared/local scope for external storage.`, 1);
326
+ }
327
+ }
328
+ if (!options.force) {
329
+ await assertCrossScopeSlugAvailable(options.repoRoot, projectsRoot, options.projectName, scope, dependencies);
330
+ }
331
+ let gitignoreChanged = false;
332
+ let gitignoreBefore;
333
+ let syncTarget;
334
+ let syncedCreatedByInvocation = false;
335
+ let published = false;
336
+ let recordWritten = false;
337
+ let ref;
338
+ let sha;
339
+ let recordPath;
340
+ let parentHeadBeforeRecordCommit;
341
+ let committed = false;
342
+ let commitSha;
343
+ let commitStatus = 'skipped_disabled';
344
+ let commitError;
345
+ let createdFiles = [];
346
+ let skippedFiles = [];
347
+ try {
348
+ if (scope === 'local') {
349
+ const repair = await ensureScopedProjectIgnored(options.repoRoot, absoluteScopeRoot, scope, dependencies);
350
+ if (repair.changed) {
351
+ console.error('Warning: applied the OAT local-project rule to .gitignore before scaffolding.');
352
+ }
353
+ }
354
+ if (scope === 'synced') {
355
+ const remote = await dependencies.gitRunner.run(['remote', 'get-url', 'origin'], { cwd: options.repoRoot, allowFailure: true });
356
+ if (remote.code !== 0) {
357
+ throw new CliError('Synced project creation requires a configured origin remote. Configure origin or use --scope local.', 1);
358
+ }
359
+ const repair = await ensureScopedProjectIgnored(options.repoRoot, absoluteScopeRoot, scope, dependencies);
360
+ gitignoreBefore = repair.before;
361
+ gitignoreChanged = repair.changed;
362
+ if (gitignoreChanged) {
363
+ console.error('Warning: applied the OAT synced-project rule to .gitignore before scaffolding.');
364
+ }
365
+ syncTarget = buildSyncTarget(options.repoRoot, projectsRoot, options.projectName);
366
+ await dependencies.createSyncedProject(syncTarget, dependencies.gitRunner);
367
+ syncedCreatedByInvocation = true;
368
+ }
369
+ await ensureStructure(absoluteProjectPath, mode);
370
+ ({ createdFiles, skippedFiles } = await scaffoldModeTemplates(userOatRoot, options.repoRoot, absoluteProjectPath, options.projectName, mode, today, nowUtc));
371
+ const effectiveProjectLog = options.projectLog ??
372
+ (await resolveEffectiveConfig(options.repoRoot, userOatRoot, env))
373
+ .resolved['workflow.projectLog']?.value === true;
374
+ if (effectiveProjectLog) {
375
+ const projectLogResult = await scaffoldProjectLog(userOatRoot, options.repoRoot, absoluteProjectPath, options.projectName, today);
376
+ if (projectLogResult === 'created') {
377
+ createdFiles.push('project-log.md');
378
+ }
379
+ else {
380
+ skippedFiles.push('project-log.md');
381
+ }
382
+ }
383
+ if (scope === 'synced' && syncTarget) {
384
+ const pushed = await dependencies.pushSynced(syncTarget, dependencies.gitRunner, { message: `chore(oat): scaffold ${options.projectName}` });
385
+ if (pushed.status !== 'pushed' && pushed.status !== 'up-to-date') {
386
+ throw new CliError(`Unable to publish synced project ${options.projectName}: ${pushed.status}.`, 1);
387
+ }
388
+ published = true;
389
+ ref = syncTarget.ref;
390
+ sha = pushed.sha;
391
+ recordPath = syncedRecordPath(absoluteScopeRoot, options.projectName);
392
+ await dependencies.writeSyncedRecord(recordPath, buildSyncedRecord(options.projectName, new Date(nowUtc)));
393
+ recordWritten = true;
394
+ if (options.commit) {
395
+ parentHeadBeforeRecordCommit = (await dependencies.gitRunner.run(['rev-parse', 'HEAD'], {
396
+ cwd: options.repoRoot,
397
+ })).stdout;
398
+ const recordCommit = await dependencies.commitRecordChange(options.repoRoot, [recordPath, ...(gitignoreChanged ? ['.gitignore'] : [])], `chore(oat): scaffold ${options.projectName}`, dependencies.gitRunner, { projectRoots: syncTarget });
399
+ committed = recordCommit !== null;
400
+ commitSha = recordCommit?.sha;
401
+ commitStatus = recordCommit ? 'committed' : 'skipped_nothing';
402
+ }
403
+ }
404
+ }
405
+ catch (error) {
406
+ if (scope === 'synced' && syncTarget && !published) {
407
+ if (syncedCreatedByInvocation) {
408
+ await dependencies.rollbackCreatedSyncedProject(syncTarget, dependencies.gitRunner);
409
+ }
410
+ if (gitignoreChanged && gitignoreBefore !== undefined) {
411
+ const gitignorePath = join(options.repoRoot, '.gitignore');
412
+ if (gitignoreBefore === null) {
413
+ await rm(gitignorePath, { force: true });
414
+ }
415
+ else {
416
+ await writeFile(gitignorePath, gitignoreBefore, 'utf8');
417
+ }
418
+ }
269
419
  }
270
- else {
271
- skippedFiles.push('project-log.md');
420
+ if (scope === 'synced' && published && syncTarget && recordPath) {
421
+ const detail = error instanceof Error ? error.message : String(error);
422
+ const currentParentHead = parentHeadBeforeRecordCommit
423
+ ? (await dependencies.gitRunner.run(['rev-parse', 'HEAD'], {
424
+ cwd: options.repoRoot,
425
+ })).stdout
426
+ : undefined;
427
+ const canRollbackPublishedScaffold = syncedCreatedByInvocation &&
428
+ sha !== undefined &&
429
+ (parentHeadBeforeRecordCommit === undefined ||
430
+ currentParentHead === parentHeadBeforeRecordCommit);
431
+ if (canRollbackPublishedScaffold) {
432
+ try {
433
+ await dependencies.gitRunner.run([
434
+ 'push',
435
+ `--force-with-lease=${syncTarget.ref}:${sha}`,
436
+ syncTarget.remote,
437
+ `:${syncTarget.ref}`,
438
+ ], { cwd: options.repoRoot });
439
+ await dependencies.gitRunner.run([
440
+ 'reset',
441
+ '-q',
442
+ '--',
443
+ relative(canonicalizePath(options.repoRoot), recordPath)
444
+ .split('\\')
445
+ .join('/'),
446
+ ...(gitignoreChanged ? ['.gitignore'] : []),
447
+ ], { cwd: options.repoRoot });
448
+ await rm(recordPath, { force: true });
449
+ await dependencies.rollbackCreatedSyncedProject(syncTarget, dependencies.gitRunner);
450
+ if (gitignoreChanged && gitignoreBefore !== undefined) {
451
+ const gitignorePath = join(options.repoRoot, '.gitignore');
452
+ if (gitignoreBefore === null) {
453
+ await rm(gitignorePath, { force: true });
454
+ }
455
+ else {
456
+ await writeFile(gitignorePath, gitignoreBefore, 'utf8');
457
+ }
458
+ }
459
+ throw new CliError(`Synced project ${options.projectName} scaffold failed and its attempt-owned ref, checkout, and record were rolled back: ${detail}. Retry project creation after correcting the failure.`, error instanceof CliError ? error.exitCode : 2);
460
+ }
461
+ catch (rollbackError) {
462
+ if (rollbackError instanceof CliError &&
463
+ rollbackError.message.includes('were rolled back')) {
464
+ throw rollbackError;
465
+ }
466
+ const rollbackDetail = rollbackError instanceof Error
467
+ ? rollbackError.message
468
+ : String(rollbackError);
469
+ throw new CliError(`Synced project ${options.projectName} scaffold failed: ${detail}. Automatic rollback also failed: ${rollbackDetail}. Preserve the checkout and run oat project prune ${options.projectName} --force after verifying the remote ref.`, 2);
470
+ }
471
+ }
472
+ if (!recordWritten) {
473
+ throw new CliError(`Synced project ${options.projectName} was published, but its discovery record was not written: ${detail}. Run oat project pull '${options.projectName}' to resume record adoption; do not rerun project creation.`, 2);
474
+ }
475
+ const relativeRecordPath = relative(canonicalizePath(options.repoRoot), recordPath)
476
+ .split('\\')
477
+ .join('/');
478
+ throw new CliError(`Synced project ${options.projectName} and its discovery record were written, but the parent commit failed: ${detail}. Repair Git, then run git add -- '${relativeRecordPath}'${gitignoreChanged ? " '.gitignore'" : ''} && git commit -m 'chore(oat): scaffold ${options.projectName}' -- '${relativeRecordPath}'${gitignoreChanged ? " '.gitignore'" : ''}; do not rerun project creation.`, 2);
272
479
  }
480
+ throw error;
273
481
  }
482
+ let activePointerUpdated = false;
274
483
  if (setActive) {
275
- await setActiveProject(options.repoRoot, projectPath);
484
+ try {
485
+ await dependencies.setActiveProject(options.repoRoot, projectPath);
486
+ activePointerUpdated = true;
487
+ }
488
+ catch (error) {
489
+ if (scope === 'synced' && published) {
490
+ const detail = error instanceof Error ? error.message : String(error);
491
+ throw new CliError(`Synced project ${options.projectName} was published and recorded, but its active pointer was not updated: ${detail}. Run oat project open ${shellQuote(projectPath)} to complete recovery; do not rerun project creation.`, 2);
492
+ }
493
+ throw error;
494
+ }
276
495
  }
277
496
  let dashboardRefreshed = false;
278
497
  if (refreshDashboard) {
@@ -284,14 +503,10 @@ export async function scaffoldProject(options) {
284
503
  console.error(`Warning: dashboard refresh failed: ${error instanceof Error ? error.message : String(error)}`);
285
504
  }
286
505
  }
287
- let committed = false;
288
- let commitSha;
289
- let commitStatus = 'skipped_disabled';
290
- let commitError;
291
- if (options.commit) {
506
+ if (options.commit && scope === 'shared') {
292
507
  // `projectPath` is relative to `repoRoot`, so git must run there for the
293
508
  // pathspecs to resolve to the scaffolded files.
294
- const commitResult = commitScaffold(options.repoRoot, projectPath, options.projectName, createdFiles);
509
+ const commitResult = await commitScaffold(options.repoRoot, absoluteProjectPath, options.projectName, createdFiles, dependencies);
295
510
  committed = commitResult.committed;
296
511
  commitSha = commitResult.commitSha;
297
512
  commitStatus = commitResult.status;
@@ -299,15 +514,18 @@ export async function scaffoldProject(options) {
299
514
  }
300
515
  return {
301
516
  mode,
517
+ scope,
302
518
  projectsRoot,
303
519
  projectPath,
304
520
  createdFiles,
305
521
  skippedFiles,
306
- activePointerUpdated: setActive,
522
+ activePointerUpdated,
307
523
  dashboardRefreshed,
308
524
  committed,
309
525
  commitSha,
310
526
  commitStatus,
311
527
  commitError,
528
+ ref,
529
+ sha,
312
530
  };
313
531
  }
@@ -1,5 +1,9 @@
1
1
  import { readFile as defaultReadFile, writeFile as defaultWriteFile } from 'node:fs/promises';
2
2
  import { buildCommandContext, type CommandContext } from '../../../app/command-context.js';
3
+ import { type GitRunner } from '../../project/sync/git.js';
4
+ import { readSyncedRecord } from '../../project/sync/record.js';
5
+ import { commitRecordChange as defaultCommitRecordChange, type PullResult, type PushResult, type AdoptionRecordState, type SyncTarget } from '../../project/sync/ref-sync.js';
6
+ import { resolveSyncedTarget } from '../../project/sync/resolve-target.js';
3
7
  import { type GenerateStateResult } from '../../state/generate.js';
4
8
  import { type OatLocalConfig } from '../../../config/oat-config.js';
5
9
  import { dirExists, fileExists } from '../../../fs/io.js';
@@ -18,6 +22,18 @@ interface ProjectOpenDependencies {
18
22
  writeFile: typeof defaultWriteFile;
19
23
  dirExists: typeof dirExists;
20
24
  fileExists: typeof fileExists;
25
+ readSyncedRecord: typeof readSyncedRecord;
26
+ resolveSyncedTarget: typeof resolveSyncedTarget;
27
+ pullSynced: (target: SyncTarget, git: GitRunner, options?: {
28
+ adopt?: boolean;
29
+ adoptionRecord?: AdoptionRecordState;
30
+ now?: Date;
31
+ }) => Promise<PullResult>;
32
+ pushSynced: (target: SyncTarget, git: GitRunner, options: {
33
+ message?: string;
34
+ }) => Promise<PushResult>;
35
+ commitRecordChange: typeof defaultCommitRecordChange;
36
+ gitRunner: GitRunner;
21
37
  processEnv: NodeJS.ProcessEnv;
22
38
  now: () => Date;
23
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/open/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAYhF,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,sBAAsB,EAAE,CAAC,OAAO,EAAE;QAChC,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAkKD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAkBT"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/project/open/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,QAAQ,IAAI,eAAe,EAC3B,SAAS,IAAI,gBAAgB,EAC9B,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EACL,kBAAkB,IAAI,yBAAyB,EAG/C,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAChB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,mBAAmB,EAEpB,MAAM,uCAAuC,CAAC;AAmB/C,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,UAAU,uBAAuB;IAC/B,mBAAmB,EAAE,CACnB,OAAO,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,KAC/C,cAAc,CAAC;IACpB,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACrD,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAClE,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,sBAAsB,EAAE,CAAC,OAAO,EAAE;QAChC,QAAQ,EAAE,MAAM,CAAC;KAClB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnC,QAAQ,EAAE,OAAO,eAAe,CAAC;IACjC,SAAS,EAAE,OAAO,gBAAgB,CAAC;IACnC,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,mBAAmB,EAAE,OAAO,mBAAmB,CAAC;IAChD,UAAU,EAAE,CACV,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,cAAc,CAAC,EAAE,mBAAmB,CAAC;QACrC,GAAG,CAAC,EAAE,IAAI,CAAC;KACZ,KACE,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,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,kBAAkB,EAAE,OAAO,yBAAyB,CAAC;IACrD,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;IAC9B,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAgXD,wBAAgB,wBAAwB,CACtC,SAAS,GAAE,OAAO,CAAC,uBAAuB,CAAM,GAC/C,OAAO,CAkBT"}