@open-agent-toolkit/cli 0.0.26 → 0.0.28

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.
@@ -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';
@@ -26,6 +28,12 @@ const KEY_ORDER = [
26
28
  'tools.research',
27
29
  'tools.utility',
28
30
  'tools.workflows',
31
+ 'workflow.hillCheckpointDefault',
32
+ 'workflow.archiveOnComplete',
33
+ 'workflow.createPrOnComplete',
34
+ 'workflow.postImplementSequence',
35
+ 'workflow.reviewExecutionModel',
36
+ 'workflow.autoNarrowReReviewScope',
29
37
  'worktrees.root',
30
38
  ];
31
39
  const CONFIG_CATALOG = [
@@ -268,8 +276,74 @@ const CONFIG_CATALOG = [
268
276
  type: 'string | null',
269
277
  defaultValue: 'null',
270
278
  mutability: 'read/write',
271
- owningCommand: 'user config APIs (not surfaced via oat config set)',
272
- description: 'User-level active idea fallback when no repo-local active idea is set.',
279
+ owningCommand: 'oat config set activeIdea <value> --user',
280
+ description: 'User-level active idea fallback used when no repo-local active idea is set. Writable via `oat config set activeIdea <value> --user`.',
281
+ },
282
+ {
283
+ key: 'workflow.hillCheckpointDefault',
284
+ group: 'Workflow Preferences (3-layer: local > shared > user)',
285
+ file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
286
+ scope: 'workflow',
287
+ type: 'every | final',
288
+ defaultValue: 'unset',
289
+ mutability: 'read/write',
290
+ owningCommand: 'oat config set workflow.hillCheckpointDefault <value>',
291
+ 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.',
292
+ },
293
+ {
294
+ key: 'workflow.archiveOnComplete',
295
+ group: 'Workflow Preferences (3-layer: local > shared > user)',
296
+ file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
297
+ scope: 'workflow',
298
+ type: 'boolean',
299
+ defaultValue: 'unset',
300
+ mutability: 'read/write',
301
+ owningCommand: 'oat config set workflow.archiveOnComplete <true|false>',
302
+ description: 'Skip the "Archive after completion?" prompt in oat-project-complete. When unset, the skill prompts. Resolution: env > local > shared > user > default.',
303
+ },
304
+ {
305
+ key: 'workflow.createPrOnComplete',
306
+ group: 'Workflow Preferences (3-layer: local > shared > user)',
307
+ file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
308
+ scope: 'workflow',
309
+ type: 'boolean',
310
+ defaultValue: 'unset',
311
+ mutability: 'read/write',
312
+ owningCommand: 'oat config set workflow.createPrOnComplete <true|false>',
313
+ description: 'Skip the "Open a PR?" prompt in oat-project-complete. When true, completion auto-triggers PR creation. Resolution: env > local > shared > user > default.',
314
+ },
315
+ {
316
+ key: 'workflow.postImplementSequence',
317
+ group: 'Workflow Preferences (3-layer: local > shared > user)',
318
+ file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
319
+ scope: 'workflow',
320
+ type: 'wait | summary | pr | docs-pr',
321
+ defaultValue: 'unset',
322
+ mutability: 'read/write',
323
+ owningCommand: 'oat config set workflow.postImplementSequence <value>',
324
+ 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.',
325
+ },
326
+ {
327
+ key: 'workflow.reviewExecutionModel',
328
+ group: 'Workflow Preferences (3-layer: local > shared > user)',
329
+ file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
330
+ scope: 'workflow',
331
+ type: 'subagent | inline | fresh-session',
332
+ defaultValue: 'unset',
333
+ mutability: 'read/write',
334
+ owningCommand: 'oat config set workflow.reviewExecutionModel <value>',
335
+ 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.',
336
+ },
337
+ {
338
+ key: 'workflow.autoNarrowReReviewScope',
339
+ group: 'Workflow Preferences (3-layer: local > shared > user)',
340
+ file: '.oat/config.local.json | .oat/config.json | ~/.oat/config.json',
341
+ scope: 'workflow',
342
+ type: 'boolean',
343
+ defaultValue: 'unset',
344
+ mutability: 'read/write',
345
+ owningCommand: 'oat config set workflow.autoNarrowReReviewScope <true|false>',
346
+ 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
347
  },
274
348
  {
275
349
  key: 'sync.defaultStrategy',
@@ -312,7 +386,10 @@ const DEFAULT_DEPENDENCIES = {
312
386
  writeOatConfig,
313
387
  readOatLocalConfig,
314
388
  writeOatLocalConfig,
389
+ readUserConfig,
390
+ writeUserConfig,
315
391
  resolveProjectsRoot,
392
+ resolveEffectiveConfig,
316
393
  processEnv: process.env,
317
394
  };
318
395
  function isConfigKey(value) {
@@ -325,150 +402,168 @@ function normalizeSharedRoot(value) {
325
402
  }
326
403
  return trimmed.replace(/\/+$/, '');
327
404
  }
328
- async function resolveProjectsRootWithSource(repoRoot, dependencies) {
329
- const envRoot = dependencies.processEnv.OAT_PROJECTS_ROOT?.trim();
330
- if (envRoot) {
331
- return {
332
- key: 'projects.root',
333
- value: envRoot.replace(/\/+$/, ''),
334
- source: 'env',
335
- };
405
+ const WORKFLOW_ENUM_VALUES = {
406
+ 'workflow.hillCheckpointDefault': ['every', 'final'],
407
+ 'workflow.postImplementSequence': ['wait', 'summary', 'pr', 'docs-pr'],
408
+ 'workflow.reviewExecutionModel': ['subagent', 'inline', 'fresh-session'],
409
+ };
410
+ const WORKFLOW_BOOLEAN_KEYS = new Set([
411
+ 'workflow.archiveOnComplete',
412
+ 'workflow.createPrOnComplete',
413
+ 'workflow.autoNarrowReReviewScope',
414
+ ]);
415
+ function isWorkflowKey(key) {
416
+ return key.startsWith('workflow.');
417
+ }
418
+ function isStateKey(key) {
419
+ return (key === 'activeIdea' ||
420
+ key === 'activeProject' ||
421
+ key === 'lastPausedProject');
422
+ }
423
+ function isStructuralKey(key) {
424
+ return (key === 'projects.root' ||
425
+ key === 'worktrees.root' ||
426
+ key === 'git.defaultBranch' ||
427
+ key.startsWith('documentation.') ||
428
+ key.startsWith('archive.') ||
429
+ key.startsWith('tools.'));
430
+ }
431
+ function validateSurfaceForKey(key, surface) {
432
+ if (surface === 'auto') {
433
+ return;
336
434
  }
337
- const config = await dependencies.readOatConfig(repoRoot);
338
- const configRoot = config.projects?.root?.trim();
339
- if (configRoot) {
340
- return {
341
- key: 'projects.root',
342
- value: configRoot.replace(/\/+$/, ''),
343
- source: 'config.json',
344
- };
435
+ if (isStructuralKey(key)) {
436
+ if (surface !== 'shared') {
437
+ 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).`);
438
+ }
439
+ return;
345
440
  }
346
- const value = await dependencies.resolveProjectsRoot(repoRoot, dependencies.processEnv);
347
- return {
348
- key: 'projects.root',
349
- value,
350
- source: 'default',
351
- };
441
+ if (isStateKey(key)) {
442
+ // activeIdea has both a repo-local and a user-level surface in the
443
+ // catalog (the user-level entry is the global fallback). Both surfaces
444
+ // are writable; shared is not supported because an idea pointer is not
445
+ // a team decision.
446
+ if (key === 'activeIdea') {
447
+ if (surface !== 'local' && surface !== 'user') {
448
+ 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).`);
449
+ }
450
+ return;
451
+ }
452
+ // activeProject and lastPausedProject are per-checkout state only.
453
+ if (surface !== 'local') {
454
+ 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).`);
455
+ }
456
+ return;
457
+ }
458
+ // autoReviewAtCheckpoints is currently shared-only. Multi-surface support
459
+ // for behavioral keys is out of scope for p01-t04 (workflow.* keys only).
460
+ if (key === 'autoReviewAtCheckpoints' && surface !== 'shared') {
461
+ throw new Error(`Cannot set 'autoReviewAtCheckpoints' at '${surface}' scope. This key is currently shared-only.`);
462
+ }
463
+ // Workflow keys accept any non-auto surface.
352
464
  }
353
- async function getConfigValue(repoRoot, key, dependencies) {
354
- if (key === 'projects.root') {
355
- return resolveProjectsRootWithSource(repoRoot, dependencies);
465
+ function defaultSurfaceForKey(key) {
466
+ if (isWorkflowKey(key) || isStateKey(key)) {
467
+ return 'local';
356
468
  }
357
- if (key.startsWith('documentation.')) {
358
- const config = await dependencies.readOatConfig(repoRoot);
359
- const doc = config.documentation;
360
- let value = null;
361
- if (key === 'documentation.root') {
362
- value = doc?.root ?? null;
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';
469
+ return 'shared';
470
+ }
471
+ function parseWorkflowValue(key, rawValue) {
472
+ if (WORKFLOW_BOOLEAN_KEYS.has(key)) {
473
+ const normalized = rawValue.trim().toLowerCase();
474
+ if (normalized !== 'true' && normalized !== 'false') {
475
+ throw new Error(`Invalid value for ${key}: expected 'true' or 'false', got '${rawValue}'`);
375
476
  }
376
- return {
377
- key,
378
- value,
379
- source: doc ? 'config.json' : 'default',
380
- };
477
+ return normalized === 'true';
381
478
  }
382
- if (key.startsWith('archive.')) {
383
- const config = await dependencies.readOatConfig(repoRoot);
384
- const archive = config.archive;
385
- let value = null;
386
- if (key === 'archive.s3Uri') {
387
- value = archive?.s3Uri ?? null;
479
+ const allowed = WORKFLOW_ENUM_VALUES[key];
480
+ if (allowed) {
481
+ const normalized = rawValue.trim();
482
+ if (!allowed.includes(normalized)) {
483
+ throw new Error(`Invalid value for ${key}: expected one of ${allowed.join(' | ')}, got '${rawValue}'`);
388
484
  }
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;
397
- }
398
- return {
399
- key,
400
- value,
401
- source: archive ? 'config.json' : 'default',
402
- };
485
+ return normalized;
403
486
  }
404
- if (key.startsWith('tools.')) {
405
- const config = await dependencies.readOatConfig(repoRoot);
406
- const packName = key.slice('tools.'.length);
407
- const tools = config.tools ?? {};
408
- return {
409
- key,
410
- value: String(tools[packName] ?? false),
411
- source: config.tools ? 'config.json' : 'default',
412
- };
487
+ throw new Error(`Unknown workflow key: ${key}`);
488
+ }
489
+ function applyWorkflowValue(workflow, key, value) {
490
+ const subKey = key.slice('workflow.'.length);
491
+ return {
492
+ ...workflow,
493
+ [subKey]: value,
494
+ };
495
+ }
496
+ function formatResolvedValue(value) {
497
+ if (value === null || value === undefined) {
498
+ return null;
413
499
  }
414
- if (key === 'git.defaultBranch') {
415
- const config = await dependencies.readOatConfig(repoRoot);
416
- return {
417
- key,
418
- value: config.git?.defaultBranch ?? null,
419
- source: config.git?.defaultBranch ? 'config.json' : 'default',
420
- };
500
+ if (typeof value === 'boolean') {
501
+ return String(value);
421
502
  }
422
- if (key === 'worktrees.root') {
423
- const envRoot = dependencies.processEnv.OAT_WORKTREES_ROOT?.trim();
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
- };
503
+ if (typeof value === 'string') {
504
+ return value;
445
505
  }
446
- if (key === 'autoReviewAtCheckpoints') {
447
- const config = await dependencies.readOatConfig(repoRoot);
448
- return {
449
- key,
450
- value: config.autoReviewAtCheckpoints != null
451
- ? String(config.autoReviewAtCheckpoints)
452
- : 'false',
453
- source: config.autoReviewAtCheckpoints != null ? 'config.json' : 'default',
454
- };
506
+ if (Array.isArray(value)) {
507
+ return value.join(',');
508
+ }
509
+ return String(value);
510
+ }
511
+ async function getConfigValue(repoRoot, userConfigDir, key, dependencies) {
512
+ const resolved = await dependencies.resolveEffectiveConfig(repoRoot, userConfigDir, dependencies.processEnv);
513
+ const entry = resolved.resolved[key];
514
+ if (!entry) {
515
+ return { key, value: null, source: 'default' };
455
516
  }
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
517
  return {
461
518
  key,
462
- value,
463
- source: hasKey ? 'config.local.json' : 'default',
519
+ value: formatResolvedValue(entry.value),
520
+ source: entry.source,
464
521
  };
465
522
  }
466
- async function setConfigValue(repoRoot, key, rawValue, dependencies) {
523
+ async function setConfigValue(repoRoot, userConfigDir, key, rawValue, surface, dependencies) {
524
+ validateSurfaceForKey(key, surface);
525
+ const effectiveSurface = surface === 'auto' ? defaultSurfaceForKey(key) : surface;
526
+ if (isWorkflowKey(key)) {
527
+ const parsedValue = parseWorkflowValue(key, rawValue);
528
+ const displayValue = typeof parsedValue === 'boolean' ? String(parsedValue) : parsedValue;
529
+ if (effectiveSurface === 'user') {
530
+ const userConfig = await dependencies.readUserConfig(userConfigDir);
531
+ await dependencies.writeUserConfig(userConfigDir, {
532
+ ...userConfig,
533
+ workflow: applyWorkflowValue(userConfig.workflow ?? {}, key, parsedValue),
534
+ });
535
+ return { key, value: displayValue, source: 'user' };
536
+ }
537
+ if (effectiveSurface === 'local') {
538
+ const localConfig = await dependencies.readOatLocalConfig(repoRoot);
539
+ await dependencies.writeOatLocalConfig(repoRoot, {
540
+ ...localConfig,
541
+ workflow: applyWorkflowValue(localConfig.workflow ?? {}, key, parsedValue),
542
+ });
543
+ return { key, value: displayValue, source: 'local' };
544
+ }
545
+ // shared
546
+ const sharedConfig = await dependencies.readOatConfig(repoRoot);
547
+ await dependencies.writeOatConfig(repoRoot, {
548
+ ...sharedConfig,
549
+ workflow: applyWorkflowValue(sharedConfig.workflow ?? {}, key, parsedValue),
550
+ });
551
+ return { key, value: displayValue, source: 'shared' };
552
+ }
467
553
  if (key === 'activeIdea' ||
468
554
  key === 'activeProject' ||
469
555
  key === 'lastPausedProject') {
470
- const localConfig = await dependencies.readOatLocalConfig(repoRoot);
471
556
  const nextValue = rawValue === '' ? null : rawValue;
557
+ // activeIdea --user writes to ~/.oat/config.json
558
+ if (key === 'activeIdea' && effectiveSurface === 'user') {
559
+ const userConfig = await dependencies.readUserConfig(userConfigDir);
560
+ await dependencies.writeUserConfig(userConfigDir, {
561
+ ...userConfig,
562
+ activeIdea: nextValue,
563
+ });
564
+ return { key, value: nextValue, source: 'user' };
565
+ }
566
+ const localConfig = await dependencies.readOatLocalConfig(repoRoot);
472
567
  await dependencies.writeOatLocalConfig(repoRoot, {
473
568
  ...localConfig,
474
569
  [key]: nextValue,
@@ -476,7 +571,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
476
571
  return {
477
572
  key,
478
573
  value: nextValue,
479
- source: 'config.local.json',
574
+ source: 'local',
480
575
  };
481
576
  }
482
577
  const config = await dependencies.readOatConfig(repoRoot);
@@ -505,7 +600,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
505
600
  return {
506
601
  key,
507
602
  value: resultValue,
508
- source: 'config.json',
603
+ source: 'shared',
509
604
  };
510
605
  }
511
606
  if (key.startsWith('archive.')) {
@@ -529,7 +624,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
529
624
  return {
530
625
  key,
531
626
  value: resultValue,
532
- source: 'config.json',
627
+ source: 'shared',
533
628
  };
534
629
  }
535
630
  if (key.startsWith('tools.')) {
@@ -543,7 +638,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
543
638
  return {
544
639
  key,
545
640
  value: String(tools[packName] ?? false),
546
- source: 'config.json',
641
+ source: 'shared',
547
642
  };
548
643
  }
549
644
  if (key === 'git.defaultBranch') {
@@ -561,7 +656,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
561
656
  return {
562
657
  key,
563
658
  value: nextValue,
564
- source: 'config.json',
659
+ source: 'shared',
565
660
  };
566
661
  }
567
662
  if (key === 'autoReviewAtCheckpoints') {
@@ -573,7 +668,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
573
668
  return {
574
669
  key,
575
670
  value: String(nextValue),
576
- source: 'config.json',
671
+ source: 'shared',
577
672
  };
578
673
  }
579
674
  const normalizedValue = normalizeSharedRoot(rawValue);
@@ -592,7 +687,7 @@ async function setConfigValue(repoRoot, key, rawValue, dependencies) {
592
687
  return {
593
688
  key,
594
689
  value: normalizedValue,
595
- source: 'config.json',
690
+ source: 'shared',
596
691
  };
597
692
  }
598
693
  function formatList(values) {
@@ -656,7 +751,8 @@ async function runGet(keyArg, context, dependencies) {
656
751
  throw new Error(`Unknown config key: ${keyArg}`);
657
752
  }
658
753
  const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
659
- const value = await getConfigValue(repoRoot, keyArg, dependencies);
754
+ const userConfigDir = join(context.home, '.oat');
755
+ const value = await getConfigValue(repoRoot, userConfigDir, keyArg, dependencies);
660
756
  if (context.json) {
661
757
  context.logger.json({
662
758
  status: 'ok',
@@ -679,13 +775,14 @@ async function runGet(keyArg, context, dependencies) {
679
775
  process.exitCode = 1;
680
776
  }
681
777
  }
682
- async function runSet(keyArg, rawValue, context, dependencies) {
778
+ async function runSet(keyArg, rawValue, surface, context, dependencies) {
683
779
  try {
684
780
  if (!isConfigKey(keyArg)) {
685
781
  throw new Error(`Unknown config key: ${keyArg}`);
686
782
  }
687
783
  const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
688
- const result = await setConfigValue(repoRoot, keyArg, rawValue, dependencies);
784
+ const userConfigDir = join(context.home, '.oat');
785
+ const result = await setConfigValue(repoRoot, userConfigDir, keyArg, rawValue, surface, dependencies);
689
786
  if (context.json) {
690
787
  context.logger.json({
691
788
  status: 'ok',
@@ -711,9 +808,10 @@ async function runSet(keyArg, rawValue, context, dependencies) {
711
808
  async function runList(context, dependencies) {
712
809
  try {
713
810
  const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
811
+ const userConfigDir = join(context.home, '.oat');
714
812
  const values = [];
715
813
  for (const key of KEY_ORDER) {
716
- values.push(await getConfigValue(repoRoot, key, dependencies));
814
+ values.push(await getConfigValue(repoRoot, userConfigDir, key, dependencies));
717
815
  }
718
816
  if (context.json) {
719
817
  context.logger.json({
@@ -789,9 +887,39 @@ export function createConfigCommand(overrides = {}) {
789
887
  .description('Set an OAT config value')
790
888
  .argument('<key>', 'Config key')
791
889
  .argument('<value>', 'Config value')
792
- .action(async (key, value, _options, command) => {
890
+ .option('--shared', 'Write to the shared repo config (.oat/config.json)')
891
+ .option('--local', 'Write to the repo-local config (.oat/config.local.json)')
892
+ .option('--user', 'Write to the user-level config (~/.oat/config.json)')
893
+ .action(async (key, value, options, command) => {
793
894
  const context = dependencies.buildCommandContext(readGlobalOptions(command));
794
- await runSet(key, value, context, dependencies);
895
+ try {
896
+ const flagsPresent = [
897
+ options.shared,
898
+ options.local,
899
+ options.user,
900
+ ].filter(Boolean).length;
901
+ if (flagsPresent > 1) {
902
+ throw new Error('--shared, --local, and --user flags are mutually exclusive; pass at most one.');
903
+ }
904
+ let surface = 'auto';
905
+ if (options.shared)
906
+ surface = 'shared';
907
+ else if (options.local)
908
+ surface = 'local';
909
+ else if (options.user)
910
+ surface = 'user';
911
+ await runSet(key, value, surface, context, dependencies);
912
+ }
913
+ catch (error) {
914
+ const message = error instanceof Error ? error.message : String(error);
915
+ if (context.json) {
916
+ context.logger.json({ status: 'error', message });
917
+ }
918
+ else {
919
+ context.logger.error(message);
920
+ }
921
+ process.exitCode = 1;
922
+ }
795
923
  }))
796
924
  .addCommand(new Command('list')
797
925
  .description('List resolved OAT config values with sources')
@@ -13,6 +13,17 @@ export interface OatArchiveConfig {
13
13
  s3SyncOnComplete?: boolean;
14
14
  summaryExportPath?: string;
15
15
  }
16
+ export type WorkflowHillCheckpointDefault = 'every' | 'final';
17
+ export type WorkflowPostImplementSequence = 'wait' | 'summary' | 'pr' | 'docs-pr';
18
+ export type WorkflowReviewExecutionModel = 'subagent' | 'inline' | 'fresh-session';
19
+ export interface OatWorkflowConfig {
20
+ hillCheckpointDefault?: WorkflowHillCheckpointDefault;
21
+ archiveOnComplete?: boolean;
22
+ createPrOnComplete?: boolean;
23
+ postImplementSequence?: WorkflowPostImplementSequence;
24
+ reviewExecutionModel?: WorkflowReviewExecutionModel;
25
+ autoNarrowReReviewScope?: boolean;
26
+ }
16
27
  export type OatToolsConfig = Partial<Record<'core' | 'ideas' | 'docs' | 'workflows' | 'utility' | 'project-management' | 'research', boolean>>;
17
28
  export interface OatConfig {
18
29
  version: number;
@@ -28,16 +39,19 @@ export interface OatConfig {
28
39
  documentation?: OatDocumentationConfig;
29
40
  localPaths?: string[];
30
41
  autoReviewAtCheckpoints?: boolean;
42
+ workflow?: OatWorkflowConfig;
31
43
  }
32
44
  export interface OatLocalConfig {
33
45
  version: number;
34
46
  activeProject?: string | null;
35
47
  lastPausedProject?: string | null;
36
48
  activeIdea?: string | null;
49
+ workflow?: OatWorkflowConfig;
37
50
  }
38
51
  export interface UserConfig {
39
52
  version: number;
40
53
  activeIdea?: string | null;
54
+ workflow?: OatWorkflowConfig;
41
55
  }
42
56
  export interface ActiveProjectResolution {
43
57
  name: string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"oat-config.d.ts","sourceRoot":"","sources":["../../src/config/oat-config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"oat-config.d.ts","sourceRoot":"","sources":["../../src/config/oat-config.ts"],"names":[],"mappings":"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"}