@open-agent-toolkit/cli 0.0.27 → 0.0.29
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/docs/cli-utilities/config-and-local-state.md +2 -1
- package/assets/docs/cli-utilities/configuration.md +111 -0
- package/assets/docs/cli-utilities/tool-packs.md +1 -1
- package/assets/docs/reference/cli-reference.md +25 -0
- package/assets/docs/reference/file-locations.md +2 -1
- package/assets/docs/reference/oat-directory-structure.md +1 -0
- package/assets/docs/workflows/projects/hill-checkpoints.md +17 -0
- package/assets/docs/workflows/projects/lifecycle.md +6 -0
- package/assets/docs/workflows/projects/reviews.md +24 -0
- package/assets/docs/workflows/skills/index.md +2 -0
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/oat-project-complete/SKILL.md +20 -1
- package/assets/skills/oat-project-implement/SKILL.md +85 -4
- package/assets/skills/oat-project-next/SKILL.md +2 -2
- package/assets/skills/oat-project-review-provide/SKILL.md +12 -2
- package/assets/skills/oat-project-review-receive/SKILL.md +23 -1
- package/assets/skills/oat-project-review-receive-remote/SKILL.md +17 -1
- package/assets/skills/oat-wrap-up/SKILL.md +417 -0
- package/assets/skills/oat-wrap-up/references/automation-recipes.md +100 -0
- package/assets/skills/oat-wrap-up/references/report-template.md +90 -0
- package/dist/commands/config/index.d.ts +5 -1
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +283 -140
- package/dist/commands/init/tools/shared/skill-manifest.d.ts +1 -1
- package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
- package/dist/commands/init/tools/shared/skill-manifest.js +1 -0
- package/dist/config/oat-config.d.ts +15 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +54 -0
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +21 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/config/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGhF,OAAO,EACL,KAAK,SAAS,EACd,KAAK,cAAc,EAGnB,KAAK,UAAU,EAOhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoDpC,UAAU,yBAAyB;IACjC,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,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IACxD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,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,cAAc,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,eAAe,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,mBAAmB,EAAE,CACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,CACtB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EACrB,GAAG,EAAE,MAAM,CAAC,UAAU,KACnB,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC;CAC/B;AAyiCD,wBAAgB,mBAAmB,CACjC,SAAS,GAAE,OAAO,CAAC,yBAAyB,CAAM,GACjD,OAAO,CA0GT"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
1
2
|
import { buildCommandContext } from '../../app/command-context.js';
|
|
2
3
|
import { resolveProjectsRoot } from '../shared/oat-paths.js';
|
|
3
4
|
import { readGlobalOptions } from '../shared/shared.utils.js';
|
|
4
|
-
import { readOatConfig, readOatLocalConfig, writeOatConfig, writeOatLocalConfig, } from '../../config/oat-config.js';
|
|
5
|
+
import { readOatConfig, readOatLocalConfig, readUserConfig, writeOatConfig, writeOatLocalConfig, writeUserConfig, } from '../../config/oat-config.js';
|
|
6
|
+
import { resolveEffectiveConfig, } from '../../config/resolve.js';
|
|
5
7
|
import { resolveProjectRoot } from '../../fs/paths.js';
|
|
6
8
|
import { Command } from 'commander';
|
|
7
9
|
import { createConfigDumpCommand } from './dump.js';
|
|
@@ -11,6 +13,7 @@ const KEY_ORDER = [
|
|
|
11
13
|
'archive.s3Uri',
|
|
12
14
|
'archive.s3SyncOnComplete',
|
|
13
15
|
'archive.summaryExportPath',
|
|
16
|
+
'archive.wrapUpExportPath',
|
|
14
17
|
'autoReviewAtCheckpoints',
|
|
15
18
|
'lastPausedProject',
|
|
16
19
|
'documentation.root',
|
|
@@ -26,6 +29,12 @@ const KEY_ORDER = [
|
|
|
26
29
|
'tools.research',
|
|
27
30
|
'tools.utility',
|
|
28
31
|
'tools.workflows',
|
|
32
|
+
'workflow.hillCheckpointDefault',
|
|
33
|
+
'workflow.archiveOnComplete',
|
|
34
|
+
'workflow.createPrOnComplete',
|
|
35
|
+
'workflow.postImplementSequence',
|
|
36
|
+
'workflow.reviewExecutionModel',
|
|
37
|
+
'workflow.autoNarrowReReviewScope',
|
|
29
38
|
'worktrees.root',
|
|
30
39
|
];
|
|
31
40
|
const CONFIG_CATALOG = [
|
|
@@ -150,6 +159,17 @@ const CONFIG_CATALOG = [
|
|
|
150
159
|
owningCommand: 'oat config set archive.summaryExportPath <value>',
|
|
151
160
|
description: 'Repository-relative directory where completion copies project summaries for durable tracked reference.',
|
|
152
161
|
},
|
|
162
|
+
{
|
|
163
|
+
key: 'archive.wrapUpExportPath',
|
|
164
|
+
group: 'Shared Repo (.oat/config.json)',
|
|
165
|
+
file: '.oat/config.json',
|
|
166
|
+
scope: 'shared repo',
|
|
167
|
+
type: 'string',
|
|
168
|
+
defaultValue: 'unset',
|
|
169
|
+
mutability: 'read/write',
|
|
170
|
+
owningCommand: 'oat config set archive.wrapUpExportPath <value>',
|
|
171
|
+
description: 'Repository-relative directory where the oat-wrap-up skill writes date-ranged shipping digests. When unset, the skill falls back to `.oat/repo/reference/wrap-ups`.',
|
|
172
|
+
},
|
|
153
173
|
{
|
|
154
174
|
key: 'tools.core',
|
|
155
175
|
group: 'Shared Repo (.oat/config.json)',
|
|
@@ -268,8 +288,74 @@ const CONFIG_CATALOG = [
|
|
|
268
288
|
type: 'string | null',
|
|
269
289
|
defaultValue: 'null',
|
|
270
290
|
mutability: 'read/write',
|
|
271
|
-
owningCommand: '
|
|
272
|
-
description: 'User-level active idea fallback when no repo-local active idea is set.',
|
|
291
|
+
owningCommand: 'oat config set activeIdea <value> --user',
|
|
292
|
+
description: 'User-level active idea fallback used when no repo-local active idea is set. Writable via `oat config set activeIdea <value> --user`.',
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
key: 'workflow.hillCheckpointDefault',
|
|
296
|
+
group: 'Workflow Preferences (3-layer: local > shared > user)',
|
|
297
|
+
file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
|
|
298
|
+
scope: 'workflow',
|
|
299
|
+
type: 'every | final',
|
|
300
|
+
defaultValue: 'unset',
|
|
301
|
+
mutability: 'read/write',
|
|
302
|
+
owningCommand: 'oat config set workflow.hillCheckpointDefault <value>',
|
|
303
|
+
description: 'Default HiLL checkpoint behavior in oat-project-implement: "every" pauses after every phase, "final" pauses only after the last phase. When unset, the skill prompts. Resolution: env > local > shared > user > default.',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
key: 'workflow.archiveOnComplete',
|
|
307
|
+
group: 'Workflow Preferences (3-layer: local > shared > user)',
|
|
308
|
+
file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
|
|
309
|
+
scope: 'workflow',
|
|
310
|
+
type: 'boolean',
|
|
311
|
+
defaultValue: 'unset',
|
|
312
|
+
mutability: 'read/write',
|
|
313
|
+
owningCommand: 'oat config set workflow.archiveOnComplete <true|false>',
|
|
314
|
+
description: 'Skip the "Archive after completion?" prompt in oat-project-complete. When unset, the skill prompts. Resolution: env > local > shared > user > default.',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
key: 'workflow.createPrOnComplete',
|
|
318
|
+
group: 'Workflow Preferences (3-layer: local > shared > user)',
|
|
319
|
+
file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
|
|
320
|
+
scope: 'workflow',
|
|
321
|
+
type: 'boolean',
|
|
322
|
+
defaultValue: 'unset',
|
|
323
|
+
mutability: 'read/write',
|
|
324
|
+
owningCommand: 'oat config set workflow.createPrOnComplete <true|false>',
|
|
325
|
+
description: 'Skip the "Open a PR?" prompt in oat-project-complete. When true, completion auto-triggers PR creation. Resolution: env > local > shared > user > default.',
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
key: 'workflow.postImplementSequence',
|
|
329
|
+
group: 'Workflow Preferences (3-layer: local > shared > user)',
|
|
330
|
+
file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
|
|
331
|
+
scope: 'workflow',
|
|
332
|
+
type: 'wait | summary | pr | docs-pr',
|
|
333
|
+
defaultValue: 'unset',
|
|
334
|
+
mutability: 'read/write',
|
|
335
|
+
owningCommand: 'oat config set workflow.postImplementSequence <value>',
|
|
336
|
+
description: 'Default post-implementation chaining: "wait" stops without auto-chaining, "summary" generates summary only, "pr" runs pr-final (which auto-generates summary), "docs-pr" runs docs sync then pr-final. When unset, the skill prompts. Resolution: env > local > shared > user > default.',
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
key: 'workflow.reviewExecutionModel',
|
|
340
|
+
group: 'Workflow Preferences (3-layer: local > shared > user)',
|
|
341
|
+
file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
|
|
342
|
+
scope: 'workflow',
|
|
343
|
+
type: 'subagent | inline | fresh-session',
|
|
344
|
+
defaultValue: 'unset',
|
|
345
|
+
mutability: 'read/write',
|
|
346
|
+
owningCommand: 'oat config set workflow.reviewExecutionModel <value>',
|
|
347
|
+
description: 'Default execution model for the final review step in oat-project-implement: "subagent" dispatches a review subagent, "inline" runs the review in-context, "fresh-session" prints guidance for running the review in a separate session (with an escape hatch to subagent/inline). When unset, the skill prompts. Resolution: env > local > shared > user > default.',
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
key: 'workflow.autoNarrowReReviewScope',
|
|
351
|
+
group: 'Workflow Preferences (3-layer: local > shared > user)',
|
|
352
|
+
file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
|
|
353
|
+
scope: 'workflow',
|
|
354
|
+
type: 'boolean',
|
|
355
|
+
defaultValue: 'unset',
|
|
356
|
+
mutability: 'read/write',
|
|
357
|
+
owningCommand: 'oat config set workflow.autoNarrowReReviewScope <true|false>',
|
|
358
|
+
description: 'Auto-narrow re-review scope to fix-task commits in oat-project-review-provide when re-reviewing completed fix tasks. Has no effect on initial reviews (there is nothing to narrow to). When unset, the skill prompts. Resolution: env > local > shared > user > default.',
|
|
273
359
|
},
|
|
274
360
|
{
|
|
275
361
|
key: 'sync.defaultStrategy',
|
|
@@ -312,7 +398,10 @@ const DEFAULT_DEPENDENCIES = {
|
|
|
312
398
|
writeOatConfig,
|
|
313
399
|
readOatLocalConfig,
|
|
314
400
|
writeOatLocalConfig,
|
|
401
|
+
readUserConfig,
|
|
402
|
+
writeUserConfig,
|
|
315
403
|
resolveProjectsRoot,
|
|
404
|
+
resolveEffectiveConfig,
|
|
316
405
|
processEnv: process.env,
|
|
317
406
|
};
|
|
318
407
|
function isConfigKey(value) {
|
|
@@ -325,150 +414,168 @@ function normalizeSharedRoot(value) {
|
|
|
325
414
|
}
|
|
326
415
|
return trimmed.replace(/\/+$/, '');
|
|
327
416
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
417
|
+
const WORKFLOW_ENUM_VALUES = {
|
|
418
|
+
'workflow.hillCheckpointDefault': ['every', 'final'],
|
|
419
|
+
'workflow.postImplementSequence': ['wait', 'summary', 'pr', 'docs-pr'],
|
|
420
|
+
'workflow.reviewExecutionModel': ['subagent', 'inline', 'fresh-session'],
|
|
421
|
+
};
|
|
422
|
+
const WORKFLOW_BOOLEAN_KEYS = new Set([
|
|
423
|
+
'workflow.archiveOnComplete',
|
|
424
|
+
'workflow.createPrOnComplete',
|
|
425
|
+
'workflow.autoNarrowReReviewScope',
|
|
426
|
+
]);
|
|
427
|
+
function isWorkflowKey(key) {
|
|
428
|
+
return key.startsWith('workflow.');
|
|
429
|
+
}
|
|
430
|
+
function isStateKey(key) {
|
|
431
|
+
return (key === 'activeIdea' ||
|
|
432
|
+
key === 'activeProject' ||
|
|
433
|
+
key === 'lastPausedProject');
|
|
434
|
+
}
|
|
435
|
+
function isStructuralKey(key) {
|
|
436
|
+
return (key === 'projects.root' ||
|
|
437
|
+
key === 'worktrees.root' ||
|
|
438
|
+
key === 'git.defaultBranch' ||
|
|
439
|
+
key.startsWith('documentation.') ||
|
|
440
|
+
key.startsWith('archive.') ||
|
|
441
|
+
key.startsWith('tools.'));
|
|
442
|
+
}
|
|
443
|
+
function validateSurfaceForKey(key, surface) {
|
|
444
|
+
if (surface === 'auto') {
|
|
445
|
+
return;
|
|
336
446
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
value: configRoot.replace(/\/+$/, ''),
|
|
343
|
-
source: 'config.json',
|
|
344
|
-
};
|
|
447
|
+
if (isStructuralKey(key)) {
|
|
448
|
+
if (surface !== 'shared') {
|
|
449
|
+
throw new Error(`Cannot set structural key '${key}' at '${surface}' scope. Structural keys (projects.root, worktrees.root, git.*, documentation.*, archive.*, tools.*) can only be set at shared scope (.oat/config.json).`);
|
|
450
|
+
}
|
|
451
|
+
return;
|
|
345
452
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
453
|
+
if (isStateKey(key)) {
|
|
454
|
+
// activeIdea has both a repo-local and a user-level surface in the
|
|
455
|
+
// catalog (the user-level entry is the global fallback). Both surfaces
|
|
456
|
+
// are writable; shared is not supported because an idea pointer is not
|
|
457
|
+
// a team decision.
|
|
458
|
+
if (key === 'activeIdea') {
|
|
459
|
+
if (surface !== 'local' && surface !== 'user') {
|
|
460
|
+
throw new Error(`Cannot set 'activeIdea' at '${surface}' scope. activeIdea can only be set at local scope (.oat/config.local.json) or user scope (~/.oat/config.json).`);
|
|
461
|
+
}
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
// activeProject and lastPausedProject are per-checkout state only.
|
|
465
|
+
if (surface !== 'local') {
|
|
466
|
+
throw new Error(`Cannot set state key '${key}' at '${surface}' scope. State keys (activeProject, lastPausedProject) can only be set at local scope (.oat/config.local.json).`);
|
|
467
|
+
}
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
// autoReviewAtCheckpoints is currently shared-only. Multi-surface support
|
|
471
|
+
// for behavioral keys is out of scope for p01-t04 (workflow.* keys only).
|
|
472
|
+
if (key === 'autoReviewAtCheckpoints' && surface !== 'shared') {
|
|
473
|
+
throw new Error(`Cannot set 'autoReviewAtCheckpoints' at '${surface}' scope. This key is currently shared-only.`);
|
|
474
|
+
}
|
|
475
|
+
// Workflow keys accept any non-auto surface.
|
|
352
476
|
}
|
|
353
|
-
|
|
354
|
-
if (key
|
|
355
|
-
return
|
|
477
|
+
function defaultSurfaceForKey(key) {
|
|
478
|
+
if (isWorkflowKey(key) || isStateKey(key)) {
|
|
479
|
+
return 'local';
|
|
356
480
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
else if (key === 'documentation.tooling') {
|
|
365
|
-
value = doc?.tooling ?? null;
|
|
366
|
-
}
|
|
367
|
-
else if (key === 'documentation.config') {
|
|
368
|
-
value = doc?.config ?? null;
|
|
369
|
-
}
|
|
370
|
-
else if (key === 'documentation.requireForProjectCompletion') {
|
|
371
|
-
value =
|
|
372
|
-
doc?.requireForProjectCompletion != null
|
|
373
|
-
? String(doc.requireForProjectCompletion)
|
|
374
|
-
: 'false';
|
|
481
|
+
return 'shared';
|
|
482
|
+
}
|
|
483
|
+
function parseWorkflowValue(key, rawValue) {
|
|
484
|
+
if (WORKFLOW_BOOLEAN_KEYS.has(key)) {
|
|
485
|
+
const normalized = rawValue.trim().toLowerCase();
|
|
486
|
+
if (normalized !== 'true' && normalized !== 'false') {
|
|
487
|
+
throw new Error(`Invalid value for ${key}: expected 'true' or 'false', got '${rawValue}'`);
|
|
375
488
|
}
|
|
376
|
-
return
|
|
377
|
-
key,
|
|
378
|
-
value,
|
|
379
|
-
source: doc ? 'config.json' : 'default',
|
|
380
|
-
};
|
|
489
|
+
return normalized === 'true';
|
|
381
490
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
value = archive?.s3Uri ?? null;
|
|
388
|
-
}
|
|
389
|
-
else if (key === 'archive.s3SyncOnComplete') {
|
|
390
|
-
value =
|
|
391
|
-
archive?.s3SyncOnComplete != null
|
|
392
|
-
? String(archive.s3SyncOnComplete)
|
|
393
|
-
: 'false';
|
|
394
|
-
}
|
|
395
|
-
else if (key === 'archive.summaryExportPath') {
|
|
396
|
-
value = archive?.summaryExportPath ?? null;
|
|
491
|
+
const allowed = WORKFLOW_ENUM_VALUES[key];
|
|
492
|
+
if (allowed) {
|
|
493
|
+
const normalized = rawValue.trim();
|
|
494
|
+
if (!allowed.includes(normalized)) {
|
|
495
|
+
throw new Error(`Invalid value for ${key}: expected one of ${allowed.join(' | ')}, got '${rawValue}'`);
|
|
397
496
|
}
|
|
398
|
-
return
|
|
399
|
-
key,
|
|
400
|
-
value,
|
|
401
|
-
source: archive ? 'config.json' : 'default',
|
|
402
|
-
};
|
|
497
|
+
return normalized;
|
|
403
498
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
499
|
+
throw new Error(`Unknown workflow key: ${key}`);
|
|
500
|
+
}
|
|
501
|
+
function applyWorkflowValue(workflow, key, value) {
|
|
502
|
+
const subKey = key.slice('workflow.'.length);
|
|
503
|
+
return {
|
|
504
|
+
...workflow,
|
|
505
|
+
[subKey]: value,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function formatResolvedValue(value) {
|
|
509
|
+
if (value === null || value === undefined) {
|
|
510
|
+
return null;
|
|
413
511
|
}
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
return {
|
|
417
|
-
key,
|
|
418
|
-
value: config.git?.defaultBranch ?? null,
|
|
419
|
-
source: config.git?.defaultBranch ? 'config.json' : 'default',
|
|
420
|
-
};
|
|
512
|
+
if (typeof value === 'boolean') {
|
|
513
|
+
return String(value);
|
|
421
514
|
}
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
if (envRoot) {
|
|
425
|
-
return {
|
|
426
|
-
key,
|
|
427
|
-
value: envRoot.replace(/\/+$/, ''),
|
|
428
|
-
source: 'env',
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
const config = await dependencies.readOatConfig(repoRoot);
|
|
432
|
-
const value = config.worktrees?.root?.trim();
|
|
433
|
-
if (value) {
|
|
434
|
-
return {
|
|
435
|
-
key,
|
|
436
|
-
value: value.replace(/\/+$/, ''),
|
|
437
|
-
source: 'config.json',
|
|
438
|
-
};
|
|
439
|
-
}
|
|
440
|
-
return {
|
|
441
|
-
key,
|
|
442
|
-
value: '.worktrees',
|
|
443
|
-
source: 'default',
|
|
444
|
-
};
|
|
515
|
+
if (typeof value === 'string') {
|
|
516
|
+
return value;
|
|
445
517
|
}
|
|
446
|
-
if (
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
518
|
+
if (Array.isArray(value)) {
|
|
519
|
+
return value.join(',');
|
|
520
|
+
}
|
|
521
|
+
return String(value);
|
|
522
|
+
}
|
|
523
|
+
async function getConfigValue(repoRoot, userConfigDir, key, dependencies) {
|
|
524
|
+
const resolved = await dependencies.resolveEffectiveConfig(repoRoot, userConfigDir, dependencies.processEnv);
|
|
525
|
+
const entry = resolved.resolved[key];
|
|
526
|
+
if (!entry) {
|
|
527
|
+
return { key, value: null, source: 'default' };
|
|
455
528
|
}
|
|
456
|
-
const localConfig = await dependencies.readOatLocalConfig(repoRoot);
|
|
457
|
-
const localKey = key;
|
|
458
|
-
const hasKey = Object.hasOwn(localConfig, localKey);
|
|
459
|
-
const value = localConfig[localKey] ?? null;
|
|
460
529
|
return {
|
|
461
530
|
key,
|
|
462
|
-
value,
|
|
463
|
-
source:
|
|
531
|
+
value: formatResolvedValue(entry.value),
|
|
532
|
+
source: entry.source,
|
|
464
533
|
};
|
|
465
534
|
}
|
|
466
|
-
async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
535
|
+
async function setConfigValue(repoRoot, userConfigDir, key, rawValue, surface, dependencies) {
|
|
536
|
+
validateSurfaceForKey(key, surface);
|
|
537
|
+
const effectiveSurface = surface === 'auto' ? defaultSurfaceForKey(key) : surface;
|
|
538
|
+
if (isWorkflowKey(key)) {
|
|
539
|
+
const parsedValue = parseWorkflowValue(key, rawValue);
|
|
540
|
+
const displayValue = typeof parsedValue === 'boolean' ? String(parsedValue) : parsedValue;
|
|
541
|
+
if (effectiveSurface === 'user') {
|
|
542
|
+
const userConfig = await dependencies.readUserConfig(userConfigDir);
|
|
543
|
+
await dependencies.writeUserConfig(userConfigDir, {
|
|
544
|
+
...userConfig,
|
|
545
|
+
workflow: applyWorkflowValue(userConfig.workflow ?? {}, key, parsedValue),
|
|
546
|
+
});
|
|
547
|
+
return { key, value: displayValue, source: 'user' };
|
|
548
|
+
}
|
|
549
|
+
if (effectiveSurface === 'local') {
|
|
550
|
+
const localConfig = await dependencies.readOatLocalConfig(repoRoot);
|
|
551
|
+
await dependencies.writeOatLocalConfig(repoRoot, {
|
|
552
|
+
...localConfig,
|
|
553
|
+
workflow: applyWorkflowValue(localConfig.workflow ?? {}, key, parsedValue),
|
|
554
|
+
});
|
|
555
|
+
return { key, value: displayValue, source: 'local' };
|
|
556
|
+
}
|
|
557
|
+
// shared
|
|
558
|
+
const sharedConfig = await dependencies.readOatConfig(repoRoot);
|
|
559
|
+
await dependencies.writeOatConfig(repoRoot, {
|
|
560
|
+
...sharedConfig,
|
|
561
|
+
workflow: applyWorkflowValue(sharedConfig.workflow ?? {}, key, parsedValue),
|
|
562
|
+
});
|
|
563
|
+
return { key, value: displayValue, source: 'shared' };
|
|
564
|
+
}
|
|
467
565
|
if (key === 'activeIdea' ||
|
|
468
566
|
key === 'activeProject' ||
|
|
469
567
|
key === 'lastPausedProject') {
|
|
470
|
-
const localConfig = await dependencies.readOatLocalConfig(repoRoot);
|
|
471
568
|
const nextValue = rawValue === '' ? null : rawValue;
|
|
569
|
+
// activeIdea --user writes to ~/.oat/config.json
|
|
570
|
+
if (key === 'activeIdea' && effectiveSurface === 'user') {
|
|
571
|
+
const userConfig = await dependencies.readUserConfig(userConfigDir);
|
|
572
|
+
await dependencies.writeUserConfig(userConfigDir, {
|
|
573
|
+
...userConfig,
|
|
574
|
+
activeIdea: nextValue,
|
|
575
|
+
});
|
|
576
|
+
return { key, value: nextValue, source: 'user' };
|
|
577
|
+
}
|
|
578
|
+
const localConfig = await dependencies.readOatLocalConfig(repoRoot);
|
|
472
579
|
await dependencies.writeOatLocalConfig(repoRoot, {
|
|
473
580
|
...localConfig,
|
|
474
581
|
[key]: nextValue,
|
|
@@ -476,7 +583,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
|
476
583
|
return {
|
|
477
584
|
key,
|
|
478
585
|
value: nextValue,
|
|
479
|
-
source: '
|
|
586
|
+
source: 'local',
|
|
480
587
|
};
|
|
481
588
|
}
|
|
482
589
|
const config = await dependencies.readOatConfig(repoRoot);
|
|
@@ -505,7 +612,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
|
505
612
|
return {
|
|
506
613
|
key,
|
|
507
614
|
value: resultValue,
|
|
508
|
-
source: '
|
|
615
|
+
source: 'shared',
|
|
509
616
|
};
|
|
510
617
|
}
|
|
511
618
|
if (key.startsWith('archive.')) {
|
|
@@ -519,6 +626,9 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
|
519
626
|
else if (key === 'archive.summaryExportPath') {
|
|
520
627
|
archive.summaryExportPath = normalizeSharedRoot(rawValue);
|
|
521
628
|
}
|
|
629
|
+
else if (key === 'archive.wrapUpExportPath') {
|
|
630
|
+
archive.wrapUpExportPath = normalizeSharedRoot(rawValue);
|
|
631
|
+
}
|
|
522
632
|
await dependencies.writeOatConfig(repoRoot, {
|
|
523
633
|
...config,
|
|
524
634
|
archive,
|
|
@@ -529,7 +639,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
|
529
639
|
return {
|
|
530
640
|
key,
|
|
531
641
|
value: resultValue,
|
|
532
|
-
source: '
|
|
642
|
+
source: 'shared',
|
|
533
643
|
};
|
|
534
644
|
}
|
|
535
645
|
if (key.startsWith('tools.')) {
|
|
@@ -543,7 +653,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
|
543
653
|
return {
|
|
544
654
|
key,
|
|
545
655
|
value: String(tools[packName] ?? false),
|
|
546
|
-
source: '
|
|
656
|
+
source: 'shared',
|
|
547
657
|
};
|
|
548
658
|
}
|
|
549
659
|
if (key === 'git.defaultBranch') {
|
|
@@ -561,7 +671,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
|
561
671
|
return {
|
|
562
672
|
key,
|
|
563
673
|
value: nextValue,
|
|
564
|
-
source: '
|
|
674
|
+
source: 'shared',
|
|
565
675
|
};
|
|
566
676
|
}
|
|
567
677
|
if (key === 'autoReviewAtCheckpoints') {
|
|
@@ -573,7 +683,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
|
573
683
|
return {
|
|
574
684
|
key,
|
|
575
685
|
value: String(nextValue),
|
|
576
|
-
source: '
|
|
686
|
+
source: 'shared',
|
|
577
687
|
};
|
|
578
688
|
}
|
|
579
689
|
const normalizedValue = normalizeSharedRoot(rawValue);
|
|
@@ -592,7 +702,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
|
|
|
592
702
|
return {
|
|
593
703
|
key,
|
|
594
704
|
value: normalizedValue,
|
|
595
|
-
source: '
|
|
705
|
+
source: 'shared',
|
|
596
706
|
};
|
|
597
707
|
}
|
|
598
708
|
function formatList(values) {
|
|
@@ -656,7 +766,8 @@ async function runGet(keyArg, context, dependencies) {
|
|
|
656
766
|
throw new Error(`Unknown config key: ${keyArg}`);
|
|
657
767
|
}
|
|
658
768
|
const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
659
|
-
const
|
|
769
|
+
const userConfigDir = join(context.home, '.oat');
|
|
770
|
+
const value = await getConfigValue(repoRoot, userConfigDir, keyArg, dependencies);
|
|
660
771
|
if (context.json) {
|
|
661
772
|
context.logger.json({
|
|
662
773
|
status: 'ok',
|
|
@@ -679,13 +790,14 @@ async function runGet(keyArg, context, dependencies) {
|
|
|
679
790
|
process.exitCode = 1;
|
|
680
791
|
}
|
|
681
792
|
}
|
|
682
|
-
async function runSet(keyArg, rawValue, context, dependencies) {
|
|
793
|
+
async function runSet(keyArg, rawValue, surface, context, dependencies) {
|
|
683
794
|
try {
|
|
684
795
|
if (!isConfigKey(keyArg)) {
|
|
685
796
|
throw new Error(`Unknown config key: ${keyArg}`);
|
|
686
797
|
}
|
|
687
798
|
const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
688
|
-
const
|
|
799
|
+
const userConfigDir = join(context.home, '.oat');
|
|
800
|
+
const result = await setConfigValue(repoRoot, userConfigDir, keyArg, rawValue, surface, dependencies);
|
|
689
801
|
if (context.json) {
|
|
690
802
|
context.logger.json({
|
|
691
803
|
status: 'ok',
|
|
@@ -711,9 +823,10 @@ async function runSet(keyArg, rawValue, context, dependencies) {
|
|
|
711
823
|
async function runList(context, dependencies) {
|
|
712
824
|
try {
|
|
713
825
|
const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
826
|
+
const userConfigDir = join(context.home, '.oat');
|
|
714
827
|
const values = [];
|
|
715
828
|
for (const key of KEY_ORDER) {
|
|
716
|
-
values.push(await getConfigValue(repoRoot, key, dependencies));
|
|
829
|
+
values.push(await getConfigValue(repoRoot, userConfigDir, key, dependencies));
|
|
717
830
|
}
|
|
718
831
|
if (context.json) {
|
|
719
832
|
context.logger.json({
|
|
@@ -789,9 +902,39 @@ export function createConfigCommand(overrides = {}) {
|
|
|
789
902
|
.description('Set an OAT config value')
|
|
790
903
|
.argument('<key>', 'Config key')
|
|
791
904
|
.argument('<value>', 'Config value')
|
|
792
|
-
.
|
|
905
|
+
.option('--shared', 'Write to the shared repo config (.oat/config.json)')
|
|
906
|
+
.option('--local', 'Write to the repo-local config (.oat/config.local.json)')
|
|
907
|
+
.option('--user', 'Write to the user-level config (~/.oat/config.json)')
|
|
908
|
+
.action(async (key, value, options, command) => {
|
|
793
909
|
const context = dependencies.buildCommandContext(readGlobalOptions(command));
|
|
794
|
-
|
|
910
|
+
try {
|
|
911
|
+
const flagsPresent = [
|
|
912
|
+
options.shared,
|
|
913
|
+
options.local,
|
|
914
|
+
options.user,
|
|
915
|
+
].filter(Boolean).length;
|
|
916
|
+
if (flagsPresent > 1) {
|
|
917
|
+
throw new Error('--shared, --local, and --user flags are mutually exclusive; pass at most one.');
|
|
918
|
+
}
|
|
919
|
+
let surface = 'auto';
|
|
920
|
+
if (options.shared)
|
|
921
|
+
surface = 'shared';
|
|
922
|
+
else if (options.local)
|
|
923
|
+
surface = 'local';
|
|
924
|
+
else if (options.user)
|
|
925
|
+
surface = 'user';
|
|
926
|
+
await runSet(key, value, surface, context, dependencies);
|
|
927
|
+
}
|
|
928
|
+
catch (error) {
|
|
929
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
930
|
+
if (context.json) {
|
|
931
|
+
context.logger.json({ status: 'error', message });
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
context.logger.error(message);
|
|
935
|
+
}
|
|
936
|
+
process.exitCode = 1;
|
|
937
|
+
}
|
|
795
938
|
}))
|
|
796
939
|
.addCommand(new Command('list')
|
|
797
940
|
.description('List resolved OAT config values with sources')
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `bundle-assets.sh` maintains its own bash array — `bundle-consistency.test.ts`
|
|
6
6
|
* validates that it stays in sync with these lists.
|
|
7
7
|
*/
|
|
8
|
-
export declare const WORKFLOW_SKILLS: readonly ["oat-project-capture", "oat-project-clear-active", "oat-project-complete", "oat-project-design", "oat-project-discover", "oat-project-document", "oat-project-implement", "oat-project-import-plan", "oat-project-new", "oat-project-next", "oat-project-open", "oat-project-plan", "oat-project-plan-writing", "oat-project-pr-final", "oat-project-pr-progress", "oat-project-progress", "oat-project-promote-spec-driven", "oat-project-quick-start", "oat-project-reconcile", "oat-project-revise", "oat-project-review-provide", "oat-project-review-receive", "oat-project-review-receive-remote", "oat-project-spec", "oat-project-subagent-implement", "oat-project-summary", "oat-repo-knowledge-index", "oat-worktree-bootstrap", "oat-worktree-bootstrap-auto"];
|
|
8
|
+
export declare const WORKFLOW_SKILLS: readonly ["oat-project-capture", "oat-project-clear-active", "oat-project-complete", "oat-project-design", "oat-project-discover", "oat-project-document", "oat-project-implement", "oat-project-import-plan", "oat-project-new", "oat-project-next", "oat-project-open", "oat-project-plan", "oat-project-plan-writing", "oat-project-pr-final", "oat-project-pr-progress", "oat-project-progress", "oat-project-promote-spec-driven", "oat-project-quick-start", "oat-project-reconcile", "oat-project-revise", "oat-project-review-provide", "oat-project-review-receive", "oat-project-review-receive-remote", "oat-project-spec", "oat-project-subagent-implement", "oat-project-summary", "oat-repo-knowledge-index", "oat-worktree-bootstrap", "oat-worktree-bootstrap-auto", "oat-wrap-up"];
|
|
9
9
|
export declare const WORKFLOW_AGENTS: readonly ["oat-codebase-mapper.md", "oat-reviewer.md"];
|
|
10
10
|
export declare const WORKFLOW_TEMPLATES: readonly ["state.md", "discovery.md", "spec.md", "design.md", "plan.md", "implementation.md", "summary.md"];
|
|
11
11
|
export declare const WORKFLOW_SCRIPTS: readonly ["generate-oat-state.sh", "generate-thin-index.sh", "resolve-tracking.sh"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill-manifest.d.ts","sourceRoot":"","sources":["../../../../../src/commands/init/tools/shared/skill-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"skill-manifest.d.ts","sourceRoot":"","sources":["../../../../../src/commands/init/tools/shared/skill-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,eAAO,MAAM,eAAe,qwBA+BlB,CAAC;AAEX,eAAO,MAAM,eAAe,wDAGlB,CAAC;AAEX,eAAO,MAAM,kBAAkB,6GAQrB,CAAC;AAEX,eAAO,MAAM,gBAAgB,qFAInB,CAAC;AAIX,eAAO,MAAM,WAAW,2FAKd,CAAC;AAIX,eAAO,MAAM,WAAW,qCAAsC,CAAC;AAI/D,eAAO,MAAM,WAAW,mHAKd,CAAC;AAEX,eAAO,MAAM,YAAY,kCAAmC,CAAC;AAI7D,eAAO,MAAM,cAAc,gJAMjB,CAAC;AAIX,eAAO,MAAM,yBAAyB,kGAI5B,CAAC;AAEX,eAAO,MAAM,4BAA4B,4CAG/B,CAAC;AAEX,eAAO,MAAM,0BAA0B,aAAc,CAAC;AAItD,eAAO,MAAM,eAAe,2EAMlB,CAAC;AAEX,eAAO,MAAM,eAAe,qCAAsC,CAAC"}
|
|
@@ -12,6 +12,18 @@ export interface OatArchiveConfig {
|
|
|
12
12
|
s3Uri?: string;
|
|
13
13
|
s3SyncOnComplete?: boolean;
|
|
14
14
|
summaryExportPath?: string;
|
|
15
|
+
wrapUpExportPath?: string;
|
|
16
|
+
}
|
|
17
|
+
export type WorkflowHillCheckpointDefault = 'every' | 'final';
|
|
18
|
+
export type WorkflowPostImplementSequence = 'wait' | 'summary' | 'pr' | 'docs-pr';
|
|
19
|
+
export type WorkflowReviewExecutionModel = 'subagent' | 'inline' | 'fresh-session';
|
|
20
|
+
export interface OatWorkflowConfig {
|
|
21
|
+
hillCheckpointDefault?: WorkflowHillCheckpointDefault;
|
|
22
|
+
archiveOnComplete?: boolean;
|
|
23
|
+
createPrOnComplete?: boolean;
|
|
24
|
+
postImplementSequence?: WorkflowPostImplementSequence;
|
|
25
|
+
reviewExecutionModel?: WorkflowReviewExecutionModel;
|
|
26
|
+
autoNarrowReReviewScope?: boolean;
|
|
15
27
|
}
|
|
16
28
|
export type OatToolsConfig = Partial<Record<'core' | 'ideas' | 'docs' | 'workflows' | 'utility' | 'project-management' | 'research', boolean>>;
|
|
17
29
|
export interface OatConfig {
|
|
@@ -28,16 +40,19 @@ export interface OatConfig {
|
|
|
28
40
|
documentation?: OatDocumentationConfig;
|
|
29
41
|
localPaths?: string[];
|
|
30
42
|
autoReviewAtCheckpoints?: boolean;
|
|
43
|
+
workflow?: OatWorkflowConfig;
|
|
31
44
|
}
|
|
32
45
|
export interface OatLocalConfig {
|
|
33
46
|
version: number;
|
|
34
47
|
activeProject?: string | null;
|
|
35
48
|
lastPausedProject?: string | null;
|
|
36
49
|
activeIdea?: string | null;
|
|
50
|
+
workflow?: OatWorkflowConfig;
|
|
37
51
|
}
|
|
38
52
|
export interface UserConfig {
|
|
39
53
|
version: number;
|
|
40
54
|
activeIdea?: string | null;
|
|
55
|
+
workflow?: OatWorkflowConfig;
|
|
41
56
|
}
|
|
42
57
|
export interface ActiveProjectResolution {
|
|
43
58
|
name: string | null;
|