@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.
- package/assets/agents/oat-codebase-mapper.md +15 -3
- package/assets/agents/oat-phase-implementer.md +34 -9
- package/assets/agents/oat-reviewer.md +2 -2
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +11 -1
- package/assets/docs/cli-utilities/tool-packs.md +8 -0
- package/assets/docs/contributing/skills.md +67 -14
- package/assets/docs/quickstart.md +9 -0
- package/assets/docs/reference/cli-reference.md +56 -1
- package/assets/docs/reference/file-locations.md +18 -1
- package/assets/docs/reference/oat-directory-structure.md +20 -4
- package/assets/docs/workflows/projects/artifacts.md +27 -1
- package/assets/docs/workflows/projects/implementation-execution.md +22 -0
- package/assets/docs/workflows/projects/index.md +6 -0
- package/assets/docs/workflows/projects/lifecycle.md +19 -5
- package/assets/docs/workflows/projects/picking-up-projects.md +75 -0
- package/assets/docs/workflows/projects/pr-flow.md +22 -1
- package/assets/docs/workflows/projects/reviewing-oat-prs.md +60 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/analyze/SKILL.md +13 -5
- package/assets/skills/compare/SKILL.md +11 -2
- package/assets/skills/oat-brainstorm/SKILL.md +135 -32
- package/assets/skills/oat-brainstorm/references/destinations.md +9 -6
- package/assets/skills/oat-cursor-cloud-projects/SKILL.md +11 -1
- package/assets/skills/oat-dispatch-subagents/SKILL.md +19 -6
- package/assets/skills/oat-doctor/SKILL.md +8 -1
- package/assets/skills/oat-project-autonomous/SKILL.md +45 -1
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +8 -8
- package/assets/skills/oat-project-capture/SKILL.md +29 -9
- package/assets/skills/oat-project-complete/SKILL.md +610 -38
- package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +265 -0
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +764 -0
- package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +134 -0
- package/assets/skills/oat-project-complete/scripts/validate-nonarchive-lifecycle-receipt.mjs +160 -0
- package/assets/skills/oat-project-design/SKILL.md +33 -9
- package/assets/skills/oat-project-discover/SKILL.md +16 -3
- package/assets/skills/oat-project-document/SKILL.md +10 -4
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +8 -8
- package/assets/skills/oat-project-implement/SKILL.md +14 -3
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +18 -6
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +8 -8
- package/assets/skills/oat-project-implement/references/phase-execution.md +9 -3
- package/assets/skills/oat-project-implement/references/plan-and-resume.md +3 -0
- package/assets/skills/oat-project-import-plan/SKILL.md +15 -9
- package/assets/skills/oat-project-new/SKILL.md +7 -5
- package/assets/skills/oat-project-next/SKILL.md +32 -10
- package/assets/skills/oat-project-plan/SKILL.md +18 -3
- package/assets/skills/oat-project-pr-final/SKILL.md +23 -2
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +8 -8
- package/assets/skills/oat-project-pr-progress/SKILL.md +23 -2
- package/assets/skills/oat-project-progress/SKILL.md +23 -9
- package/assets/skills/oat-project-promote-spec-driven/SKILL.md +20 -2
- package/assets/skills/oat-project-quick-start/SKILL.md +48 -18
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +8 -8
- package/assets/skills/oat-project-reconcile/SKILL.md +14 -10
- package/assets/skills/oat-project-retro/SKILL.md +3 -3
- package/assets/skills/oat-project-retro/references/apply-procedure.md +102 -6
- package/assets/skills/oat-project-retro-file/SKILL.md +57 -6
- package/assets/skills/oat-project-review-provide/SKILL.md +56 -6
- package/assets/skills/oat-project-review-receive/SKILL.md +16 -7
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +9 -3
- package/assets/skills/oat-project-revise/SKILL.md +12 -3
- package/assets/skills/oat-project-spec/SKILL.md +16 -3
- package/assets/skills/oat-project-summary/SKILL.md +113 -13
- package/assets/skills/oat-repo-improve/SKILL.md +18 -7
- package/assets/skills/oat-review-provide-remote/SKILL.md +11 -2
- package/assets/skills/oat-wave-execute/SKILL.md +34 -2
- package/assets/skills/oat-worktree-bootstrap/SKILL.md +13 -1
- package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +13 -1
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +30 -2
- package/dist/commands/doctor/index.d.ts +2 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +27 -3
- package/dist/commands/doctor/synced-projects.d.ts +12 -0
- package/dist/commands/doctor/synced-projects.d.ts.map +1 -0
- package/dist/commands/doctor/synced-projects.js +208 -0
- package/dist/commands/init/gitattributes.d.ts +4 -0
- package/dist/commands/init/gitattributes.d.ts.map +1 -0
- package/dist/commands/init/gitattributes.js +12 -0
- package/dist/commands/init/gitignore.d.ts +8 -1
- package/dist/commands/init/gitignore.d.ts.map +1 -1
- package/dist/commands/init/gitignore.js +146 -41
- package/dist/commands/init/index.d.ts +2 -0
- package/dist/commands/init/index.d.ts.map +1 -1
- package/dist/commands/init/index.js +3 -0
- package/dist/commands/init/managed-block.d.ts +10 -0
- package/dist/commands/init/managed-block.d.ts.map +1 -0
- package/dist/commands/init/managed-block.js +40 -0
- package/dist/commands/init/tools/workflows/install-workflows.js +1 -1
- package/dist/commands/local/index.d.ts.map +1 -1
- package/dist/commands/local/index.js +5 -1
- package/dist/commands/local/sync.d.ts +4 -0
- package/dist/commands/local/sync.d.ts.map +1 -1
- package/dist/commands/local/sync.js +123 -2
- package/dist/commands/pjm/adoption.d.ts +2 -0
- package/dist/commands/pjm/adoption.d.ts.map +1 -1
- package/dist/commands/pjm/adoption.js +1 -1
- package/dist/commands/project/archive/archive-utils.d.ts +24 -0
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +349 -22
- package/dist/commands/project/archive/index.d.ts.map +1 -1
- package/dist/commands/project/archive/index.js +1 -0
- package/dist/commands/project/archive/push-runner.d.ts +5 -1
- package/dist/commands/project/archive/push-runner.d.ts.map +1 -1
- package/dist/commands/project/archive/push-runner.js +33 -4
- package/dist/commands/project/complete-state/index.d.ts.map +1 -1
- package/dist/commands/project/complete-state/index.js +3 -1
- package/dist/commands/project/index.d.ts.map +1 -1
- package/dist/commands/project/index.js +12 -0
- package/dist/commands/project/links/compute.d.ts +9 -0
- package/dist/commands/project/links/compute.d.ts.map +1 -0
- package/dist/commands/project/links/compute.js +27 -0
- package/dist/commands/project/links/index.d.ts +17 -0
- package/dist/commands/project/links/index.d.ts.map +1 -0
- package/dist/commands/project/links/index.js +72 -0
- package/dist/commands/project/links/refresh.d.ts +26 -0
- package/dist/commands/project/links/refresh.d.ts.map +1 -0
- package/dist/commands/project/links/refresh.js +92 -0
- package/dist/commands/project/links/render.d.ts +24 -0
- package/dist/commands/project/links/render.d.ts.map +1 -0
- package/dist/commands/project/links/render.js +68 -0
- package/dist/commands/project/list.d.ts +5 -0
- package/dist/commands/project/list.d.ts.map +1 -1
- package/dist/commands/project/list.js +182 -9
- package/dist/commands/project/migrate/index.d.ts +22 -0
- package/dist/commands/project/migrate/index.d.ts.map +1 -0
- package/dist/commands/project/migrate/index.js +79 -0
- package/dist/commands/project/new/index.d.ts +2 -0
- package/dist/commands/project/new/index.d.ts.map +1 -1
- package/dist/commands/project/new/index.js +14 -2
- package/dist/commands/project/new/scaffold.d.ts +22 -1
- package/dist/commands/project/new/scaffold.d.ts.map +1 -1
- package/dist/commands/project/new/scaffold.js +287 -69
- package/dist/commands/project/open/index.d.ts +16 -0
- package/dist/commands/project/open/index.d.ts.map +1 -1
- package/dist/commands/project/open/index.js +151 -19
- package/dist/commands/project/pause/index.d.ts +8 -0
- package/dist/commands/project/pause/index.d.ts.map +1 -1
- package/dist/commands/project/pause/index.js +129 -19
- package/dist/commands/project/prune/index.d.ts +21 -0
- package/dist/commands/project/prune/index.d.ts.map +1 -0
- package/dist/commands/project/prune/index.js +95 -0
- package/dist/commands/project/pull/index.d.ts +28 -0
- package/dist/commands/project/pull/index.d.ts.map +1 -0
- package/dist/commands/project/pull/index.js +157 -0
- package/dist/commands/project/push/index.d.ts +22 -0
- package/dist/commands/project/push/index.d.ts.map +1 -0
- package/dist/commands/project/push/index.js +100 -0
- package/dist/commands/project/scope/index.d.ts +17 -0
- package/dist/commands/project/scope/index.d.ts.map +1 -0
- package/dist/commands/project/scope/index.js +107 -0
- package/dist/commands/project/split/run.d.ts +4 -0
- package/dist/commands/project/split/run.d.ts.map +1 -1
- package/dist/commands/project/split/run.js +48 -10
- package/dist/commands/project/sync/git.d.ts +18 -0
- package/dist/commands/project/sync/git.d.ts.map +1 -0
- package/dist/commands/project/sync/git.js +60 -0
- package/dist/commands/project/sync/record.d.ts +22 -0
- package/dist/commands/project/sync/record.d.ts.map +1 -0
- package/dist/commands/project/sync/record.js +133 -0
- package/dist/commands/project/sync/ref-sync.d.ts +120 -0
- package/dist/commands/project/sync/ref-sync.d.ts.map +1 -0
- package/dist/commands/project/sync/ref-sync.js +962 -0
- package/dist/commands/project/sync/resolve-target.d.ts +27 -0
- package/dist/commands/project/sync/resolve-target.d.ts.map +1 -0
- package/dist/commands/project/sync/resolve-target.js +159 -0
- package/dist/commands/shared/oat-paths.d.ts +4 -1
- package/dist/commands/shared/oat-paths.d.ts.map +1 -1
- package/dist/commands/shared/oat-paths.js +2 -2
- package/dist/commands/shared/project-scope.d.ts +23 -0
- package/dist/commands/shared/project-scope.d.ts.map +1 -0
- package/dist/commands/shared/project-scope.js +106 -0
- package/dist/commands/tools/migrate/index.js +1 -1
- package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-inventory.js +11 -0
- package/dist/commands/tools/shared/pack-lifecycle.js +1 -1
- package/dist/commands/tools/shared/pack-reconcile.d.ts.map +1 -1
- package/dist/commands/tools/shared/pack-reconcile.js +4 -1
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +20 -10
- package/dist/commands/tools/update/update-tools.d.ts +2 -0
- package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
- package/dist/config/oat-config.d.ts +6 -3
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +41 -7
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +2 -1
- package/dist/fs/io.d.ts +2 -1
- package/dist/fs/io.d.ts.map +1 -1
- package/dist/fs/io.js +5 -2
- package/dist/projects/split/finalize.d.ts.map +1 -1
- package/dist/projects/split/finalize.js +55 -2
- package/dist/projects/split/resume.d.ts +3 -0
- package/dist/projects/split/resume.d.ts.map +1 -1
- package/dist/projects/split/resume.js +34 -7
- package/dist/projects/split/seed-children.d.ts.map +1 -1
- package/dist/projects/split/seed-children.js +6 -0
- package/dist/projects/split/write-parent.d.ts +7 -0
- package/dist/projects/split/write-parent.d.ts.map +1 -1
- package/dist/projects/split/write-parent.js +7 -4
- package/dist/release/public-package-contract.d.ts.map +1 -1
- package/dist/release/public-package-contract.js +1 -0
- package/dist/validation/skills.d.ts.map +1 -1
- package/dist/validation/skills.js +434 -0
- package/package.json +4 -4
- package/dist/__tests__/skills/split-flow-fixtures.d.ts +0 -50
- package/dist/__tests__/skills/split-flow-fixtures.d.ts.map +0 -1
- package/dist/__tests__/skills/split-flow-fixtures.js +0 -161
- package/dist/commands/__tests__/helpers.d.ts +0 -12
- package/dist/commands/__tests__/helpers.d.ts.map +0 -1
- package/dist/commands/__tests__/helpers.js +0 -36
|
@@ -1,15 +1,31 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
|
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,
|
|
139
|
-
const
|
|
140
|
-
|
|
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(
|
|
167
|
+
const pathspecs = createdFiles.map((file) => join(absoluteProjectPath, file));
|
|
158
168
|
try {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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(
|
|
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,
|
|
206
|
+
async function scaffoldProjectLog(userOatRoot, repoRoot, absoluteProjectPath, projectName, today) {
|
|
205
207
|
const templateFile = 'project-log.md';
|
|
206
|
-
const dest = join(
|
|
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(
|
|
228
|
-
|
|
229
|
-
await mkdir(
|
|
230
|
-
await mkdir(join(
|
|
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(
|
|
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
|
-
|
|
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
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
-
|
|
271
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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;
|
|
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"}
|