@open-agent-toolkit/cli 0.2.51 → 0.2.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/bootstrap.md +6 -2
  3. package/assets/docs/cli-utilities/tool-packs.md +87 -33
  4. package/assets/docs/provider-sync/commands.md +30 -2
  5. package/assets/docs/provider-sync/config.md +19 -0
  6. package/assets/docs/provider-sync/manifest-and-drift.md +37 -0
  7. package/assets/docs/provider-sync/providers.md +69 -2
  8. package/assets/docs/provider-sync/scope-and-surface.md +44 -0
  9. package/assets/docs/reference/cli-reference.md +1 -0
  10. package/assets/docs/reference/oat-directory-structure.md +11 -4
  11. package/assets/docs/workflows/projects/evidence-layers.md +110 -1
  12. package/assets/docs/workflows/projects/implementation-execution.md +13 -0
  13. package/assets/docs/workflows/projects/orchestration-model.md +18 -0
  14. package/assets/public-package-versions.json +4 -4
  15. package/assets/skills/oat-dispatch-subagents/SKILL.md +57 -1
  16. package/assets/skills/oat-dispatch-subagents/references/record-schema.md +77 -1
  17. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
  18. package/assets/skills/oat-project-dispatch-subagents/SKILL.md +24 -1
  19. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
  20. package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +11 -0
  21. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
  22. package/assets/skills/oat-project-plan-writing/SKILL.md +12 -1
  23. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
  24. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
  25. package/assets/skills/oat-project-review-provide/SKILL.md +12 -1
  26. package/assets/skills/oat-project-review-provide-remote/SKILL.md +12 -1
  27. package/dist/agents/canonical/index.d.ts +2 -0
  28. package/dist/agents/canonical/index.d.ts.map +1 -1
  29. package/dist/agents/canonical/index.js +1 -0
  30. package/dist/agents/canonical/resolve.d.ts +45 -0
  31. package/dist/agents/canonical/resolve.d.ts.map +1 -0
  32. package/dist/agents/canonical/resolve.js +175 -0
  33. package/dist/commands/decision/agents-guidance.d.ts +2 -2
  34. package/dist/commands/decision/agents-guidance.d.ts.map +1 -1
  35. package/dist/commands/decision/agents-guidance.js +1 -4
  36. package/dist/commands/decision/index.d.ts.map +1 -1
  37. package/dist/commands/decision/index.js +25 -6
  38. package/dist/commands/docs/init/index.d.ts +7 -1
  39. package/dist/commands/docs/init/index.d.ts.map +1 -1
  40. package/dist/commands/docs/init/index.js +53 -20
  41. package/dist/commands/doctor/index.d.ts +8 -2
  42. package/dist/commands/doctor/index.d.ts.map +1 -1
  43. package/dist/commands/doctor/index.js +157 -46
  44. package/dist/commands/init/index.d.ts +8 -3
  45. package/dist/commands/init/index.d.ts.map +1 -1
  46. package/dist/commands/init/index.js +66 -28
  47. package/dist/commands/init/tools/index.d.ts +12 -12
  48. package/dist/commands/init/tools/index.d.ts.map +1 -1
  49. package/dist/commands/init/tools/index.js +430 -99
  50. package/dist/commands/init/tools/install-state.d.ts +2 -0
  51. package/dist/commands/init/tools/install-state.d.ts.map +1 -1
  52. package/dist/commands/init/tools/install-state.js +11 -0
  53. package/dist/commands/init/tools/project-guidance.d.ts +49 -0
  54. package/dist/commands/init/tools/project-guidance.d.ts.map +1 -0
  55. package/dist/commands/init/tools/project-guidance.js +144 -0
  56. package/dist/commands/init/tools/workflows/index.d.ts +7 -0
  57. package/dist/commands/init/tools/workflows/index.d.ts.map +1 -1
  58. package/dist/commands/init/tools/workflows/index.js +160 -7
  59. package/dist/commands/pjm/index.d.ts.map +1 -1
  60. package/dist/commands/pjm/index.js +28 -3
  61. package/dist/commands/pjm/init.d.ts +5 -0
  62. package/dist/commands/pjm/init.d.ts.map +1 -1
  63. package/dist/commands/pjm/init.js +19 -3
  64. package/dist/commands/project/dispatch/index.d.ts +10 -0
  65. package/dist/commands/project/dispatch/index.d.ts.map +1 -0
  66. package/dist/commands/project/dispatch/index.js +137 -0
  67. package/dist/commands/project/dispatch/record.d.ts +91 -0
  68. package/dist/commands/project/dispatch/record.d.ts.map +1 -0
  69. package/dist/commands/project/dispatch/record.js +402 -0
  70. package/dist/commands/project/index.d.ts.map +1 -1
  71. package/dist/commands/project/index.js +2 -0
  72. package/dist/commands/providers/inspect/inspect.d.ts +8 -1
  73. package/dist/commands/providers/inspect/inspect.d.ts.map +1 -1
  74. package/dist/commands/providers/inspect/inspect.js +146 -26
  75. package/dist/commands/providers/list/list.d.ts +8 -1
  76. package/dist/commands/providers/list/list.d.ts.map +1 -1
  77. package/dist/commands/providers/list/list.js +139 -23
  78. package/dist/commands/providers/providers.types.d.ts +25 -1
  79. package/dist/commands/providers/providers.types.d.ts.map +1 -1
  80. package/dist/commands/providers/set/index.d.ts +9 -1
  81. package/dist/commands/providers/set/index.d.ts.map +1 -1
  82. package/dist/commands/providers/set/index.js +43 -33
  83. package/dist/commands/remove/skill/remove-skill.d.ts.map +1 -1
  84. package/dist/commands/remove/skill/remove-skill.js +11 -0
  85. package/dist/commands/shared/agents-md.d.ts +42 -14
  86. package/dist/commands/shared/agents-md.d.ts.map +1 -1
  87. package/dist/commands/shared/agents-md.js +364 -70
  88. package/dist/commands/status/index.d.ts +8 -3
  89. package/dist/commands/status/index.d.ts.map +1 -1
  90. package/dist/commands/status/index.js +149 -37
  91. package/dist/commands/sync/apply.d.ts.map +1 -1
  92. package/dist/commands/sync/apply.js +220 -29
  93. package/dist/commands/sync/dry-run.d.ts.map +1 -1
  94. package/dist/commands/sync/dry-run.js +30 -6
  95. package/dist/commands/sync/index.d.ts +17 -2
  96. package/dist/commands/sync/index.d.ts.map +1 -1
  97. package/dist/commands/sync/index.js +108 -43
  98. package/dist/commands/sync/sync.types.d.ts +39 -2
  99. package/dist/commands/sync/sync.types.d.ts.map +1 -1
  100. package/dist/commands/sync/sync.utils.d.ts +5 -1
  101. package/dist/commands/sync/sync.utils.d.ts.map +1 -1
  102. package/dist/commands/sync/sync.utils.js +73 -0
  103. package/dist/commands/tools/info/info-tool.d.ts +2 -0
  104. package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
  105. package/dist/commands/tools/info/info-tool.js +34 -12
  106. package/dist/commands/tools/install/index.d.ts.map +1 -1
  107. package/dist/commands/tools/install/index.js +19 -3
  108. package/dist/commands/tools/list/list-tools.d.ts +2 -0
  109. package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
  110. package/dist/commands/tools/list/list-tools.js +36 -7
  111. package/dist/commands/tools/remove/index.d.ts.map +1 -1
  112. package/dist/commands/tools/remove/index.js +150 -21
  113. package/dist/commands/tools/remove/remove-tools.d.ts +20 -0
  114. package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
  115. package/dist/commands/tools/remove/remove-tools.js +122 -1
  116. package/dist/commands/tools/shared/format-pack-inventory.d.ts +19 -0
  117. package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
  118. package/dist/commands/tools/shared/format-pack-inventory.js +109 -0
  119. package/dist/commands/tools/shared/pack-evidence.d.ts +66 -0
  120. package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -0
  121. package/dist/commands/tools/shared/pack-evidence.js +132 -0
  122. package/dist/commands/tools/shared/pack-inventory.d.ts +1 -0
  123. package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
  124. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +44 -0
  125. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -0
  126. package/dist/commands/tools/shared/pack-lifecycle-outcome.js +91 -0
  127. package/dist/commands/tools/update/index.d.ts.map +1 -1
  128. package/dist/commands/tools/update/index.js +74 -4
  129. package/dist/commands/tools/update/update-tools.d.ts +5 -0
  130. package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
  131. package/dist/commands/tools/update/update-tools.js +64 -2
  132. package/dist/config/oat-config.d.ts +5 -0
  133. package/dist/config/oat-config.d.ts.map +1 -1
  134. package/dist/config/oat-config.js +17 -5
  135. package/dist/config/user-sync-config.d.ts +1 -0
  136. package/dist/config/user-sync-config.d.ts.map +1 -1
  137. package/dist/config/user-sync-config.js +8 -0
  138. package/dist/drift/detector.d.ts +2 -2
  139. package/dist/drift/detector.d.ts.map +1 -1
  140. package/dist/drift/detector.js +18 -0
  141. package/dist/drift/strays.d.ts.map +1 -1
  142. package/dist/drift/strays.js +5 -0
  143. package/dist/engine/collection-sync.d.ts +14 -0
  144. package/dist/engine/collection-sync.d.ts.map +1 -0
  145. package/dist/engine/collection-sync.js +282 -0
  146. package/dist/engine/compute-plan.d.ts +8 -1
  147. package/dist/engine/compute-plan.d.ts.map +1 -1
  148. package/dist/engine/compute-plan.js +303 -12
  149. package/dist/engine/engine.types.d.ts +61 -0
  150. package/dist/engine/engine.types.d.ts.map +1 -1
  151. package/dist/engine/execute-plan.d.ts +22 -2
  152. package/dist/engine/execute-plan.d.ts.map +1 -1
  153. package/dist/engine/execute-plan.js +459 -27
  154. package/dist/engine/index.d.ts +1 -1
  155. package/dist/engine/index.d.ts.map +1 -1
  156. package/dist/engine/index.js +1 -1
  157. package/dist/engine/provider-path-safety.d.ts +1 -0
  158. package/dist/engine/provider-path-safety.d.ts.map +1 -1
  159. package/dist/engine/provider-path-safety.js +15 -0
  160. package/dist/engine/scanner.d.ts +9 -2
  161. package/dist/engine/scanner.d.ts.map +1 -1
  162. package/dist/engine/scanner.js +58 -4
  163. package/dist/fs/io.d.ts +67 -0
  164. package/dist/fs/io.d.ts.map +1 -1
  165. package/dist/fs/io.js +536 -3
  166. package/dist/manifest/index.d.ts +2 -2
  167. package/dist/manifest/index.d.ts.map +1 -1
  168. package/dist/manifest/index.js +1 -1
  169. package/dist/manifest/manager.d.ts +2 -2
  170. package/dist/manifest/manager.d.ts.map +1 -1
  171. package/dist/manifest/manager.js +20 -3
  172. package/dist/manifest/manifest.types.d.ts +704 -13
  173. package/dist/manifest/manifest.types.d.ts.map +1 -1
  174. package/dist/manifest/manifest.types.js +131 -15
  175. package/dist/providers/codex/codec/sync-extension.d.ts +1 -1
  176. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  177. package/dist/providers/codex/codec/sync-extension.js +48 -17
  178. package/dist/providers/cursor/codec/sync-extension.d.ts +1 -1
  179. package/dist/providers/cursor/codec/sync-extension.d.ts.map +1 -1
  180. package/dist/providers/cursor/codec/sync-extension.js +52 -23
  181. package/dist/providers/identity/absolute-paths.d.ts +68 -0
  182. package/dist/providers/identity/absolute-paths.d.ts.map +1 -0
  183. package/dist/providers/identity/absolute-paths.js +144 -0
  184. package/dist/providers/identity/claude-runtime-observation.d.ts +105 -0
  185. package/dist/providers/identity/claude-runtime-observation.d.ts.map +1 -0
  186. package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts +40 -0
  187. package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts.map +1 -0
  188. package/dist/providers/identity/claude-runtime-observation.fixtures.js +146 -0
  189. package/dist/providers/identity/claude-runtime-observation.js +229 -0
  190. package/dist/providers/identity/codex-runtime-observation.d.ts +75 -0
  191. package/dist/providers/identity/codex-runtime-observation.d.ts.map +1 -0
  192. package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts +37 -0
  193. package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts.map +1 -0
  194. package/dist/providers/identity/codex-runtime-observation.fixtures.js +539 -0
  195. package/dist/providers/identity/codex-runtime-observation.js +255 -0
  196. package/dist/providers/identity/generic-dispatch-record.d.ts +313 -0
  197. package/dist/providers/identity/generic-dispatch-record.d.ts.map +1 -0
  198. package/dist/providers/identity/generic-dispatch-record.js +539 -0
  199. package/dist/providers/identity/oat-dispatch-record.d.ts +1964 -0
  200. package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -0
  201. package/dist/providers/identity/oat-dispatch-record.js +691 -0
  202. package/dist/providers/identity/runtime-observation.d.ts +100 -0
  203. package/dist/providers/identity/runtime-observation.d.ts.map +1 -0
  204. package/dist/providers/identity/runtime-observation.js +169 -0
  205. package/dist/providers/shared/index.d.ts +2 -0
  206. package/dist/providers/shared/index.d.ts.map +1 -1
  207. package/dist/providers/shared/index.js +1 -0
  208. package/dist/providers/shared/materialization-extension.d.ts +12 -0
  209. package/dist/providers/shared/materialization-extension.d.ts.map +1 -1
  210. package/dist/providers/shared/registry.d.ts +69 -0
  211. package/dist/providers/shared/registry.d.ts.map +1 -0
  212. package/dist/providers/shared/registry.js +252 -0
  213. package/dist/providers/shared/restart-adviser.d.ts +26 -0
  214. package/dist/providers/shared/restart-adviser.d.ts.map +1 -0
  215. package/dist/providers/shared/restart-adviser.js +96 -0
  216. package/dist/shared/types.d.ts +4 -0
  217. package/dist/shared/types.d.ts.map +1 -1
  218. package/package.json +2 -2
@@ -3,19 +3,22 @@ import { buildCommandContext, } from '../../../app/command-context.js';
3
3
  import { copyDirWithStatus } from '../../init/tools/shared/copy-helpers.js';
4
4
  import { applyGitignore } from '../../local/apply.js';
5
5
  import { addLocalPaths } from '../../local/manage.js';
6
- import { removeAgentsMdSection, upsertAgentsMdSection, } from '../../shared/agents-md.js';
6
+ import { formatAgentsMdGuidanceResult, formatAgentsMdMutationFailure, upsertAgentsMdSection, } from '../../shared/agents-md.js';
7
7
  import { withScopeOption } from '../../shared/scope-option.js';
8
- import { selectManyWithAbort, selectWithAbort, } from '../../shared/shared.prompts.js';
8
+ import { confirmAction, selectManyWithAbort, selectWithAbort, } from '../../shared/shared.prompts.js';
9
9
  import { readGlobalOptions } from '../../shared/shared.utils.js';
10
10
  import { canonicalPathsForPacks, getInstalledCanonicalPaths, setInstalledCanonicalPaths, } from '../../tools/shared/install-sync-context.js';
11
+ import { packScopeFactsFromInventory, projectPackEvidence, } from '../../tools/shared/pack-evidence.js';
11
12
  import { inventoryPack, } from '../../tools/shared/pack-inventory.js';
12
13
  import { reconcilePackLifecycles, } from '../../tools/shared/pack-lifecycle.js';
14
+ import { evaluatePackLifecycleOutcome, resolveAdditivePackScopeSelection, } from '../../tools/shared/pack-lifecycle-outcome.js';
13
15
  import { getPackDefinition } from '../../tools/shared/pack-manifest.js';
14
16
  import { reconcileProjectToolsConfig } from '../../tools/shared/project-tools-config.js';
15
17
  import { scanTools } from '../../tools/shared/scan-tools.js';
16
18
  import { readOatConfig, resolveLocalPaths, writeOatConfig, } from '../../../config/oat-config.js';
17
19
  import { resolveAssetsRoot } from '../../../fs/assets.js';
18
20
  import { resolveProjectRoot, resolveScopeRoot } from '../../../fs/paths.js';
21
+ import { adviseProviderRefresh, } from '../../../providers/shared/restart-adviser.js';
19
22
  import { Command } from 'commander';
20
23
  import { createInitToolsBrainstormCommand } from './brainstorm/index.js';
21
24
  import { installBrainstorm as defaultInstallBrainstorm, } from './brainstorm/install-brainstorm.js';
@@ -25,7 +28,8 @@ import { createInitToolsDocsCommand } from './docs/index.js';
25
28
  import { installDocs as defaultInstallDocs, } from './docs/install-docs.js';
26
29
  import { createInitToolsIdeasCommand } from './ideas/index.js';
27
30
  import { installIdeas as defaultInstallIdeas, } from './ideas/install-ideas.js';
28
- import { buildPackInstallStateMap, buildPackInstallStateMapFromInventory, } from './install-state.js';
31
+ import { buildPackInstallStateMap, buildPackInstallStateMapFromEvidence, } from './install-state.js';
32
+ import { commandProjectGuidanceChoice, planProjectGuidance, reportableProjectGuidance, withProjectGuidanceOptions, } from './project-guidance.js';
29
33
  import { createInitToolsProjectManagementCommand } from './project-management/index.js';
30
34
  import { installProjectManagement as defaultInstallProjectManagement, } from './project-management/install-project-management.js';
31
35
  import { createInitToolsResearchCommand } from './research/index.js';
@@ -50,6 +54,7 @@ const ALL_TOOL_PACKS = [
50
54
  ];
51
55
  let lastRunInitToolsMetadata = null;
52
56
  const appliedScopesByCommand = new WeakMap();
57
+ const syncHandledByCommand = new WeakSet();
53
58
  const DEFAULT_DEPENDENCIES = {
54
59
  buildCommandContext,
55
60
  resolveProjectRoot,
@@ -58,6 +63,8 @@ const DEFAULT_DEPENDENCIES = {
58
63
  scanTools,
59
64
  selectManyWithAbort,
60
65
  selectWithAbort,
66
+ confirmAction,
67
+ planProjectGuidance,
61
68
  installCore: defaultInstallCore,
62
69
  installDocs: defaultInstallDocs,
63
70
  installIdeas: defaultInstallIdeas,
@@ -73,7 +80,6 @@ const DEFAULT_DEPENDENCIES = {
73
80
  writeOatConfig,
74
81
  resolveLocalPaths,
75
82
  upsertAgentsMdSection,
76
- removeAgentsMdSection,
77
83
  reconcilePacks: reconcilePackLifecycles,
78
84
  inventoryPack,
79
85
  };
@@ -89,6 +95,24 @@ const USER_ELIGIBLE_PACKS = new Set([
89
95
  function isUserEligiblePack(pack) {
90
96
  return USER_ELIGIBLE_PACKS.has(pack);
91
97
  }
98
+ async function loadPackEvidence(pack, projectRoot, userRoot, assetsRoot, inventory) {
99
+ const canonical = await inventory({
100
+ pack,
101
+ assetsRoot,
102
+ ...(projectRoot ? { projectRoot } : {}),
103
+ userRoot,
104
+ });
105
+ return projectPackEvidence({
106
+ canonical,
107
+ scopes: canonical.scopes.map(packScopeFactsFromInventory),
108
+ });
109
+ }
110
+ function assertVerifiedPackScopes(evidence, expected) {
111
+ if (evidence.unknownScopes.length > 0 ||
112
+ expected.some((scope) => !evidence.knownRealizedScopes.includes(scope))) {
113
+ throw new Error(`Pack ${evidence.pack} placement verification failed for ${expected.join(', ')} scope`);
114
+ }
115
+ }
92
116
  /**
93
117
  * Concrete scopes implied by a desired end-state placement.
94
118
  */
@@ -167,7 +191,10 @@ async function loadInstalledPackStates(projectRoot, userRoot, assetsRoot, depend
167
191
  ...(projectRoot ? { projectRoot } : {}),
168
192
  userRoot,
169
193
  })));
170
- return buildPackInstallStateMapFromInventory(ALL_TOOL_PACKS, inventories);
194
+ return buildPackInstallStateMapFromEvidence(ALL_TOOL_PACKS, inventories.map((canonical) => projectPackEvidence({
195
+ canonical,
196
+ scopes: canonical.scopes.map(packScopeFactsFromInventory),
197
+ })));
171
198
  }
172
199
  const [projectTools, userTools] = await Promise.all([
173
200
  projectRoot
@@ -249,6 +276,9 @@ export function consumeInitToolsRunMetadata() {
249
276
  lastRunInitToolsMetadata = null;
250
277
  return metadata;
251
278
  }
279
+ export function wasInstallSyncHandled(command) {
280
+ return syncHandledByCommand.has(command);
281
+ }
252
282
  /**
253
283
  * Resolve a possibly-deferred scope-selection signal into a concrete mode.
254
284
  *
@@ -389,12 +419,16 @@ function buildPackEndStateChoices(pack, currentLocation, defaultEndState) {
389
419
  {
390
420
  label: `Project scope (${pack})`,
391
421
  value: 'project',
392
- description: 'Install at project scope only',
422
+ description: currentLocation === 'user' || currentLocation === 'both'
423
+ ? 'Add or refresh project scope; keep user scope'
424
+ : 'Install at project scope',
393
425
  },
394
426
  {
395
427
  label: `User scope (${pack})`,
396
428
  value: 'user',
397
- description: 'Install at user scope only',
429
+ description: currentLocation === 'project' || currentLocation === 'both'
430
+ ? 'Add or refresh user scope; keep project scope'
431
+ : 'Install at user scope',
398
432
  },
399
433
  {
400
434
  label: `Project + user (${pack})`,
@@ -411,13 +445,31 @@ function buildPackEndStateChoices(pack, currentLocation, defaultEndState) {
411
445
  const rest = annotated.filter((choice) => choice.value !== defaultEndState);
412
446
  return defaultChoice ? [defaultChoice, ...rest] : annotated;
413
447
  }
414
- function reportSuccess(context, packs, syncScopes, adoptedPacks) {
448
+ function reportSuccess(context, packs, syncScopes, adoptedPacks, lifecycle, projectGuidance) {
449
+ const providerVisibility = initProviderVisibility(syncScopes);
415
450
  if (context.json) {
451
+ if (projectGuidance.action === 'blocked' ||
452
+ projectGuidance.action === 'manual-required') {
453
+ context.logger.json({
454
+ status: 'partial',
455
+ installedPacks: packs,
456
+ syncScopes,
457
+ lifecycle: lifecycle.map(({ selection, status }) => ({
458
+ pack: selection.pack,
459
+ status,
460
+ })),
461
+ projectGuidance: reportableProjectGuidance(projectGuidance),
462
+ });
463
+ return;
464
+ }
416
465
  context.logger.json({
417
466
  status: 'ok',
418
467
  installedPacks: packs,
419
468
  syncScopes,
420
469
  ...(adoptedPacks.length > 0 ? { adoptedPacks } : {}),
470
+ lifecycle,
471
+ projectGuidance: reportableProjectGuidance(projectGuidance),
472
+ ...(providerVisibility ? { providerVisibility } : {}),
421
473
  });
422
474
  return;
423
475
  }
@@ -431,6 +483,117 @@ function reportSuccess(context, packs, syncScopes, adoptedPacks) {
431
483
  if (syncScopes.length === 0) {
432
484
  context.logger.info('No sync needed.');
433
485
  }
486
+ else if (providerVisibility) {
487
+ context.logger.info(`Provider catalog visibility: ${providerVisibility.state} — ${providerVisibility.reason}`);
488
+ }
489
+ for (const outcome of lifecycle) {
490
+ context.logger.info(`Lifecycle ${outcome.selection.pack}: ${outcome.status} (${outcome.canonical.status})`);
491
+ }
492
+ const guidanceMessage = `Project guidance: ${projectGuidance.action} — ${projectGuidance.reason}`;
493
+ if (projectGuidance.action === 'blocked' ||
494
+ projectGuidance.action === 'manual-required') {
495
+ context.logger.warn(guidanceMessage);
496
+ if (projectGuidance.manualPatch) {
497
+ for (const line of formatAgentsMdGuidanceResult({
498
+ action: 'manual-required',
499
+ manualPatch: projectGuidance.manualPatch,
500
+ })) {
501
+ context.logger.info(line);
502
+ }
503
+ }
504
+ }
505
+ else {
506
+ context.logger.info(guidanceMessage);
507
+ }
508
+ }
509
+ async function applyProjectGuidance(plan, dependencies) {
510
+ if (plan.action !== 'create' || !plan.repoRoot)
511
+ return plan;
512
+ try {
513
+ const sectionResult = await dependencies.upsertAgentsMdSection(plan.repoRoot, plan.sectionKey, plan.body, plan.legacySectionAction === 'remove'
514
+ ? { removeSectionKeys: ['workflows'] }
515
+ : undefined);
516
+ if (sectionResult.action === 'manual-required') {
517
+ return {
518
+ ...plan,
519
+ action: 'manual-required',
520
+ reason: 'Accepted project guidance requires the reported manual AGENTS.md patch. Capability placement and PJM adoption were unchanged.',
521
+ manualPatch: sectionResult.manualPatch,
522
+ };
523
+ }
524
+ if (sectionResult.action === 'blocked') {
525
+ return {
526
+ ...plan,
527
+ action: 'blocked',
528
+ reason: `Accepted project guidance was blocked: ${sectionResult.blocked?.reason ?? 'AGENTS.md could not be planned safely.'} ${sectionResult.blocked?.action ?? ''}`.trim(),
529
+ };
530
+ }
531
+ return {
532
+ ...plan,
533
+ action: sectionResult.action === 'created' ? 'create' : 'no-change',
534
+ reason: `Accepted project guidance ${sectionResult.action}. Capability placement and PJM adoption were unchanged.`,
535
+ };
536
+ }
537
+ catch (error) {
538
+ return {
539
+ ...plan,
540
+ action: 'blocked',
541
+ reason: `Accepted project guidance was blocked: ${formatAgentsMdMutationFailure(error)}`,
542
+ };
543
+ }
544
+ }
545
+ async function planAndApplyProjectGuidanceAfterInstall(context, assetsRoot, installedProjectRoot, explicitChoice, dependencies) {
546
+ const initialPlan = await dependencies.planProjectGuidance({
547
+ repoRoot: installedProjectRoot,
548
+ packs: [],
549
+ explicitChoice,
550
+ interactive: context.interactive,
551
+ confirmAction: dependencies.confirmAction,
552
+ });
553
+ if (initialPlan.choice.choice !== 'accepted')
554
+ return initialPlan;
555
+ try {
556
+ const repoRoot = installedProjectRoot ??
557
+ (await dependencies.resolveProjectRoot(context.cwd));
558
+ const userRoot = dependencies.resolveScopeRoot('user', context.cwd, context.home);
559
+ const finalPackStates = await loadInstalledPackStates(repoRoot, userRoot, assetsRoot, dependencies);
560
+ const realizedPacks = ALL_TOOL_PACKS.flatMap((pack) => {
561
+ const scope = finalPackStates[pack].location;
562
+ return scope === 'not-installed'
563
+ ? []
564
+ : [{ pack, scope }];
565
+ });
566
+ const completePlan = await dependencies.planProjectGuidance({
567
+ repoRoot,
568
+ packs: realizedPacks,
569
+ explicitChoice: true,
570
+ interactive: false,
571
+ confirmAction: dependencies.confirmAction,
572
+ });
573
+ return applyProjectGuidance(completePlan, dependencies);
574
+ }
575
+ catch (error) {
576
+ return {
577
+ ...initialPlan,
578
+ action: 'blocked',
579
+ reason: `Accepted project guidance was blocked: ${formatAgentsMdMutationFailure(error)}`,
580
+ };
581
+ }
582
+ }
583
+ function initProviderVisibility(syncScopes) {
584
+ if (syncScopes.length === 0)
585
+ return null;
586
+ return adviseProviderRefresh({
587
+ policy: {
588
+ state: 'unknown',
589
+ reason: 'Init does not select one provider/content refresh contract for the aggregate sync',
590
+ },
591
+ materialization: 'unknown',
592
+ observation: {
593
+ state: 'not-reported',
594
+ reference: 'oat init records provider sync completion but does not query any active provider catalog',
595
+ },
596
+ });
434
597
  }
435
598
  function reportOutdatedSkills(context, outdatedSkills) {
436
599
  if (outdatedSkills.length === 0) {
@@ -451,47 +614,12 @@ async function updateOutdatedSkills(outdatedSkills, assetsRoot, dependencies) {
451
614
  }
452
615
  return updatedNames;
453
616
  }
454
- const PACK_DESCRIPTIONS = {
455
- core: 'Diagnostics and documentation (oat-doctor, oat-docs)',
456
- docs: 'Documentation and instruction governance workflows',
457
- workflows: 'Project lifecycle (create, discover, plan, implement, review, complete)',
458
- ideas: 'Idea capture and refinement',
459
- 'project-management': 'Local backlog, roadmap, and reference doc management (oat-pjm-* skills)',
460
- utility: 'Standalone utilities (skill authoring, maintainability review, code reviews)',
461
- research: 'Research, analysis, verification, and synthesis',
462
- brainstorm: 'Always-on brainstorming entry point with visual companion',
463
- };
464
- export function buildToolPacksSectionBody(packs) {
465
- const userPacks = packs.filter((p) => p.scope === 'user' || p.scope === 'both');
466
- const hasWorkflows = packs.some((p) => p.pack === 'workflows');
467
- const lines = [
468
- '## Tool Packs',
469
- '',
470
- '- **Skills directory:** `.agents/skills/`',
471
- '- **Discover available skills:** scan `.agents/skills/*/SKILL.md`',
472
- '- **Refresh provider views:** `oat sync --scope all`',
473
- '- **Update skills to latest versions:** `oat tools update`',
474
- ];
475
- if (userPacks.length > 0) {
476
- const userPackNames = userPacks.map((p) => p.pack).join(', ');
477
- lines.push(`- **User-scoped skills:** \`~/.agents/skills/\` (${userPackNames} packs installed at user scope)`);
478
- }
479
- lines.push('', '### Installed Packs', '');
480
- for (const { pack, scope } of packs) {
481
- const suffix = scope === 'user'
482
- ? ' _(user scope)_'
483
- : scope === 'both'
484
- ? ' _(project + user scope)_'
485
- : '';
486
- lines.push(`- **${pack}** — ${PACK_DESCRIPTIONS[pack]}${suffix}`);
487
- }
488
- if (hasWorkflows) {
489
- lines.push('', '### Workflow Execution Continuation', '', '- This guidance applies only to OAT project lifecycle execution, such as `oat-project-implement`, and OAT project review/receive flows. It does not apply to non-OAT tasks or ad-hoc work outside the OAT project workflow.', '- When executing an OAT project implementation or OAT project review workflow, do not stop at task boundaries, phase boundaries, or other clean checkpoints unless the configured HiLL checkpoint has been reached, a real blocker exists, or explicit user input is required.', '- Status summaries, completed bookkeeping, and "clean boundary" pauses are not valid stop reasons. After updating tracking artifacts, continue execution until an allowed stop condition applies.');
490
- }
491
- return lines.join('\n');
492
- }
493
- export async function runInitTools(context, dependencies) {
617
+ export { buildToolPacksSectionBody } from './project-guidance.js';
618
+ export async function runInitTools(context, dependencies, explicitProjectGuidance) {
494
619
  lastRunInitToolsMetadata = null;
620
+ let attemptedPacks = [];
621
+ let attemptedSelections = [];
622
+ let lifecycleOutcomes = [];
495
623
  try {
496
624
  const userRoot = dependencies.resolveScopeRoot('user', context.cwd, context.home);
497
625
  const assetsRoot = await dependencies.resolveAssetsRoot();
@@ -518,11 +646,13 @@ export async function runInitTools(context, dependencies) {
518
646
  if (!context.interactive) {
519
647
  selectedPacks.push('project-management');
520
648
  }
649
+ attemptedPacks = selectedPacks;
521
650
  if (selectedPacks.length === 0) {
522
651
  lastRunInitToolsMetadata = {
523
652
  affectedScopes: [],
524
653
  appliedScopes: [],
525
654
  adoptedPacks: [],
655
+ syncHandled: dependencies.syncAfterInstall !== undefined,
526
656
  };
527
657
  if (!context.json) {
528
658
  context.logger.info('No tool packs selected.');
@@ -531,6 +661,12 @@ export async function runInitTools(context, dependencies) {
531
661
  return [];
532
662
  }
533
663
  const packScopes = await resolvePackScopes(context, selectedPacks, initialPackStates, dependencies);
664
+ attemptedSelections = selectedPacks.map((pack) => resolveAdditivePackScopeSelection({
665
+ pack,
666
+ requested: pack === 'core' ? 'user' : packScopes[pack],
667
+ knownRealizedScopes: scopesForLocation(initialPackStates[pack].location),
668
+ unknownScopes: [],
669
+ }));
534
670
  function scopeRoot(scope) {
535
671
  if (scope === 'user')
536
672
  return userRoot;
@@ -565,6 +701,25 @@ export async function runInitTools(context, dependencies) {
565
701
  if (plan.operations.length > 0)
566
702
  affectedScopes.add(plan.scope);
567
703
  }
704
+ if (dependencies.inventoryPack) {
705
+ const finalEvidence = await Promise.all(selectedPacks.map((pack) => loadPackEvidence(pack, projectRoot, userRoot, assetsRoot, dependencies.inventoryPack)));
706
+ lifecycleOutcomes = finalEvidence.map((evidence) => {
707
+ const packResults = lifecycle.filter(({ request }) => request.pack === evidence.pack);
708
+ const selection = attemptedSelections.find(({ pack }) => pack === evidence.pack);
709
+ return evaluatePackLifecycleOutcome({
710
+ selection,
711
+ lifecycle: packResults,
712
+ sync: { scopes: [], status: 'not-run', providers: [] },
713
+ finalEvidence: evidence,
714
+ });
715
+ });
716
+ for (const evidence of finalEvidence) {
717
+ const target = evidence.pack === 'core'
718
+ ? ['user']
719
+ : scopesForEndState(packScopes[evidence.pack]);
720
+ assertVerifiedPackScopes(evidence, target);
721
+ }
722
+ }
568
723
  if (requests.some(({ pack, scope }) => pack === 'workflows' && scope === 'project')) {
569
724
  const repoRoot = scopeRoot('project');
570
725
  const existingConfig = await dependencies.readOatConfig(repoRoot);
@@ -828,20 +983,6 @@ export async function runInitTools(context, dependencies) {
828
983
  pack,
829
984
  scope: packScopes[pack],
830
985
  }));
831
- const adoptsProject = packScopeInfo.some(({ scope }) => scope !== 'user');
832
- const sectionBody = buildToolPacksSectionBody(packScopeInfo);
833
- const sectionResult = adoptsProject
834
- ? await dependencies.upsertAgentsMdSection(scopeRoot('project'), 'tools', sectionBody)
835
- : { action: 'no-change' };
836
- // Pack placement never writes the project-management or decisions AGENTS
837
- // sections. Those belong to explicit repository adoption (`oat pjm init`,
838
- // via `initializeRepoReference`), which owns the `pjm.initialized` marker.
839
- if (adoptsProject) {
840
- await dependencies.removeAgentsMdSection(scopeRoot('project'), 'workflows');
841
- }
842
- if (!context.json && sectionResult.action !== 'no-change') {
843
- context.logger.info(`AGENTS.md tool packs section ${sectionResult.action}.`);
844
- }
845
986
  const affectedScopesList = [...affectedScopes];
846
987
  const appliedScopes = [
847
988
  ...new Set(packScopeInfo.flatMap(({ scope }) => scopesForEndState(scope))),
@@ -853,20 +994,86 @@ export async function runInitTools(context, dependencies) {
853
994
  home: context.home,
854
995
  }, dependencies)).adoptedPacks
855
996
  : [];
997
+ const finalPackStates = await loadInstalledPackStates(projectRoot, userRoot, assetsRoot, dependencies);
998
+ const realizedPacks = ALL_TOOL_PACKS.flatMap((pack) => {
999
+ const scope = finalPackStates[pack].location;
1000
+ return scope === 'not-installed'
1001
+ ? []
1002
+ : [{ pack, scope }];
1003
+ });
1004
+ const plannedGuidance = await dependencies.planProjectGuidance({
1005
+ repoRoot: projectRoot,
1006
+ packs: realizedPacks,
1007
+ explicitChoice: explicitProjectGuidance,
1008
+ interactive: context.interactive,
1009
+ confirmAction: dependencies.confirmAction,
1010
+ });
1011
+ const guidancePlan = await applyProjectGuidance(plannedGuidance, dependencies);
1012
+ if (dependencies.syncAfterInstall) {
1013
+ const sync = await dependencies.syncAfterInstall(affectedScopesList, context, canonicalPathsForPacks(selectedPacks));
1014
+ lifecycleOutcomes = lifecycleOutcomes.map((outcome) => evaluatePackLifecycleOutcome({
1015
+ selection: outcome.selection,
1016
+ lifecycle: outcome.canonical.results,
1017
+ sync: {
1018
+ scopes: sync.scopes.filter((scope) => outcome.selection.targetScopes.includes(scope)),
1019
+ status: sync.scopes.length === 0
1020
+ ? 'not-run'
1021
+ : sync.synced
1022
+ ? 'complete'
1023
+ : 'failed',
1024
+ providers: [],
1025
+ ...(sync.error ? { error: sync.error } : {}),
1026
+ },
1027
+ finalEvidence: outcome.finalEvidence,
1028
+ }));
1029
+ }
856
1030
  lastRunInitToolsMetadata = {
857
1031
  affectedScopes: affectedScopesList,
858
1032
  appliedScopes,
859
1033
  adoptedPacks,
1034
+ syncHandled: dependencies.syncAfterInstall !== undefined,
860
1035
  };
861
- reportSuccess(context, packScopeInfo, affectedScopesList, adoptedPacks);
862
- process.exitCode = 0;
1036
+ reportSuccess(context, packScopeInfo, affectedScopesList, adoptedPacks, lifecycleOutcomes, guidancePlan);
1037
+ process.exitCode =
1038
+ lifecycleOutcomes.some(({ status }) => status !== 'complete') ||
1039
+ guidancePlan.action === 'blocked' ||
1040
+ guidancePlan.action === 'manual-required'
1041
+ ? 1
1042
+ : 0;
863
1043
  return selectedPacks;
864
1044
  }
865
1045
  catch (error) {
866
1046
  lastRunInitToolsMetadata = null;
867
1047
  const message = error instanceof Error ? error.message : String(error);
868
1048
  if (context.json) {
869
- context.logger.json({ status: 'error', message });
1049
+ context.logger.json({
1050
+ status: 'error',
1051
+ message,
1052
+ ...(attemptedPacks.length > 0
1053
+ ? {
1054
+ lifecycle: lifecycleOutcomes.length > 0
1055
+ ? lifecycleOutcomes
1056
+ : attemptedPacks.map((pack) => ({
1057
+ schemaVersion: 1,
1058
+ selection: attemptedSelections.find((selection) => selection.pack === pack) ?? {
1059
+ pack,
1060
+ requested: 'user',
1061
+ retainedRealizedScopes: [],
1062
+ targetScopes: [],
1063
+ },
1064
+ canonical: { status: 'failed', results: [] },
1065
+ sync: {
1066
+ scopes: [],
1067
+ status: 'not-run',
1068
+ providers: [],
1069
+ },
1070
+ finalEvidence: null,
1071
+ status: 'failed',
1072
+ recovery: [{ code: 'canonical-apply-failed', message }],
1073
+ })),
1074
+ }
1075
+ : {}),
1076
+ });
870
1077
  }
871
1078
  else {
872
1079
  context.logger.error(message);
@@ -875,8 +1082,8 @@ export async function runInitTools(context, dependencies) {
875
1082
  return [];
876
1083
  }
877
1084
  }
878
- export async function runInitToolsWithDefaults(context) {
879
- return runInitTools(context, { ...DEFAULT_DEPENDENCIES });
1085
+ export async function runInitToolsWithDefaults(context, explicitProjectGuidance) {
1086
+ return runInitTools(context, { ...DEFAULT_DEPENDENCIES }, explicitProjectGuidance);
880
1087
  }
881
1088
  /**
882
1089
  * Scopes a bare `oat tools install <pack>` (no explicit `--scope`) should
@@ -888,30 +1095,46 @@ export async function runInitToolsWithDefaults(context) {
888
1095
  * `defaultScope`. Falls back to the configured default only when placement
889
1096
  * cannot be observed (no inventory dependency, or an inventory failure).
890
1097
  */
891
- async function resolvePackCommandScopes(pack, context, assetsRoot, dependencies) {
1098
+ async function resolvePackCommandSelection(pack, context, assetsRoot, dependencies, requested) {
892
1099
  const definition = getPackDefinition(pack);
893
1100
  const inventory = dependencies.inventoryPack;
894
- if (!inventory)
895
- return [definition.defaultScope];
896
- try {
897
- const userRoot = dependencies.resolveScopeRoot('user', context.cwd, context.home);
898
- const projectRoot = definition.allowedScopes.includes('project')
899
- ? await dependencies
900
- .resolveProjectRoot(context.cwd)
901
- .catch(() => undefined)
902
- : undefined;
903
- const packInventory = await inventory({
1101
+ if (!inventory) {
1102
+ const fallback = requested ?? definition.defaultScope;
1103
+ const fallbackScopes = fallback === 'both' ? ['project', 'user'] : [fallback];
1104
+ return {
904
1105
  pack,
905
- assetsRoot,
906
- ...(projectRoot ? { projectRoot } : {}),
907
- userRoot,
908
- });
909
- const states = buildPackInstallStateMapFromInventory([pack], [packInventory]);
910
- return scopesForEndState(resolvePackDefaultEndState(pack, states[pack].location)).filter((scope) => definition.allowedScopes.includes(scope));
911
- }
912
- catch {
913
- return [definition.defaultScope];
1106
+ requested: fallback,
1107
+ retainedRealizedScopes: [],
1108
+ targetScopes: fallbackScopes.filter((scope) => definition.allowedScopes.includes(scope)),
1109
+ };
914
1110
  }
1111
+ const userRoot = dependencies.resolveScopeRoot('user', context.cwd, context.home);
1112
+ const projectRoot = definition.allowedScopes.includes('project')
1113
+ ? await dependencies.resolveProjectRoot(context.cwd).catch(() => undefined)
1114
+ : undefined;
1115
+ const canonical = await inventory({
1116
+ pack,
1117
+ assetsRoot,
1118
+ ...(projectRoot ? { projectRoot } : {}),
1119
+ userRoot,
1120
+ });
1121
+ const evidence = projectPackEvidence({
1122
+ canonical,
1123
+ scopes: canonical.scopes.map(packScopeFactsFromInventory),
1124
+ });
1125
+ const defaultRequest = evidence.knownRealizedScopes.length === 2
1126
+ ? 'both'
1127
+ : (evidence.knownRealizedScopes[0] ?? definition.defaultScope);
1128
+ const selection = resolveAdditivePackScopeSelection({
1129
+ pack,
1130
+ requested: requested ?? defaultRequest,
1131
+ knownRealizedScopes: evidence.knownRealizedScopes,
1132
+ unknownScopes: evidence.unknownScopes,
1133
+ });
1134
+ return {
1135
+ ...selection,
1136
+ targetScopes: selection.targetScopes.filter((scope) => definition.allowedScopes.includes(scope)),
1137
+ };
915
1138
  }
916
1139
  function createReconciledPackCommand(pack, dependencies) {
917
1140
  const definition = getPackDefinition(pack);
@@ -926,22 +1149,31 @@ function createReconciledPackCommand(pack, dependencies) {
926
1149
  brainstorm: 'Install OAT brainstorm skill (always-on entry point with visual companion)',
927
1150
  };
928
1151
  const base = new Command(pack).description(descriptions[pack]);
929
- const packCommand = pack === 'core' ? base : withScopeOption(base);
1152
+ const scopedCommand = pack === 'core' ? base : withScopeOption(base);
1153
+ const packCommand = pack === 'workflows'
1154
+ ? withProjectGuidanceOptions(scopedCommand)
1155
+ : scopedCommand;
930
1156
  return packCommand
931
1157
  .allowUnknownOption(false)
932
1158
  .action(async (_options, command) => {
933
1159
  const context = dependencies.buildCommandContext(readGlobalOptions(command));
1160
+ let lifecycleOutcome = null;
1161
+ let selection = null;
934
1162
  try {
1163
+ const explicitProjectGuidance = pack === 'workflows'
1164
+ ? commandProjectGuidanceChoice(command)
1165
+ : undefined;
935
1166
  const assetsRoot = await dependencies.resolveAssetsRoot();
936
1167
  const explicitScope = command.getOptionValueSourceWithGlobals('scope') === 'cli';
937
1168
  const requestedScope = context.scope === 'project' || context.scope === 'user'
938
1169
  ? context.scope
939
1170
  : null;
940
- const scopes = explicitScope && context.scope === 'all'
941
- ? [...definition.allowedScopes]
942
- : requestedScope
943
- ? [requestedScope]
944
- : await resolvePackCommandScopes(pack, context, assetsRoot, dependencies);
1171
+ selection = await resolvePackCommandSelection(pack, context, assetsRoot, dependencies, explicitScope && context.scope === 'all'
1172
+ ? 'both'
1173
+ : explicitScope && requestedScope
1174
+ ? requestedScope
1175
+ : undefined);
1176
+ const scopes = [...selection.targetScopes];
945
1177
  for (const scope of scopes) {
946
1178
  if (!definition.allowedScopes.includes(scope)) {
947
1179
  throw new Error(`Pack ${pack} does not allow ${scope} scope`);
@@ -957,8 +1189,46 @@ function createReconciledPackCommand(pack, dependencies) {
957
1189
  action: 'install',
958
1190
  })));
959
1191
  const results = await dependencies.reconcilePacks(requests);
960
- appliedScopesByCommand.set(command, results.map(({ request }) => request.scope));
1192
+ let finalEvidence = null;
1193
+ if (dependencies.inventoryPack) {
1194
+ finalEvidence = await loadPackEvidence(pack, requests.some(({ scope }) => scope === 'project')
1195
+ ? await dependencies.resolveProjectRoot(context.cwd)
1196
+ : null, dependencies.resolveScopeRoot('user', context.cwd, context.home), assetsRoot, dependencies.inventoryPack);
1197
+ }
1198
+ lifecycleOutcome = evaluatePackLifecycleOutcome({
1199
+ selection,
1200
+ lifecycle: results,
1201
+ sync: { scopes: [], status: 'not-run', providers: [] },
1202
+ finalEvidence,
1203
+ });
1204
+ if (finalEvidence)
1205
+ assertVerifiedPackScopes(finalEvidence, scopes);
1206
+ appliedScopesByCommand.set(command, results
1207
+ .filter(({ plan }) => plan.operations.length > 0)
1208
+ .map(({ request }) => request.scope));
961
1209
  setInstalledCanonicalPaths(command, canonicalPathsForPacks([pack]));
1210
+ if (dependencies.syncAfterInstall) {
1211
+ syncHandledByCommand.add(command);
1212
+ const changedScopes = results
1213
+ .filter(({ plan }) => plan.operations.length > 0)
1214
+ .map(({ request }) => request.scope);
1215
+ const sync = await dependencies.syncAfterInstall(changedScopes, context, canonicalPathsForPacks([pack]));
1216
+ lifecycleOutcome = evaluatePackLifecycleOutcome({
1217
+ selection,
1218
+ lifecycle: results,
1219
+ sync: {
1220
+ scopes: sync.scopes,
1221
+ status: sync.scopes.length === 0
1222
+ ? 'not-run'
1223
+ : sync.synced
1224
+ ? 'complete'
1225
+ : 'failed',
1226
+ providers: [],
1227
+ ...(sync.error ? { error: sync.error } : {}),
1228
+ },
1229
+ finalEvidence,
1230
+ });
1231
+ }
962
1232
  // Project-scope placement installs capability only. The
963
1233
  // project-management and decisions AGENTS sections are written by the
964
1234
  // explicit adoption action (`oat pjm init`), never by pack placement.
@@ -969,12 +1239,28 @@ function createReconciledPackCommand(pack, dependencies) {
969
1239
  home: context.home,
970
1240
  }, dependencies)).adoptedPacks
971
1241
  : [];
1242
+ const providerVisibility = initProviderVisibility(results
1243
+ .filter(({ plan }) => plan.operations.length > 0)
1244
+ .map(({ request }) => request.scope));
1245
+ const projectGuidance = pack === 'workflows'
1246
+ ? await planAndApplyProjectGuidanceAfterInstall(context, assetsRoot, requests.find(({ scope }) => scope === 'project')?.scopeRoot ??
1247
+ null, explicitProjectGuidance, dependencies)
1248
+ : null;
972
1249
  if (context.json) {
1250
+ const guidanceIncomplete = projectGuidance?.action === 'blocked' ||
1251
+ projectGuidance?.action === 'manual-required';
973
1252
  context.logger.json({
974
- status: 'ok',
1253
+ status: guidanceIncomplete ? 'partial' : 'ok',
975
1254
  pack,
976
1255
  scopes,
977
1256
  results,
1257
+ lifecycle: lifecycleOutcome,
1258
+ ...(projectGuidance
1259
+ ? {
1260
+ projectGuidance: reportableProjectGuidance(projectGuidance),
1261
+ }
1262
+ : {}),
1263
+ ...(providerVisibility ? { providerVisibility } : {}),
978
1264
  ...(adoptedPacks.length > 0 ? { adoptedPacks } : {}),
979
1265
  });
980
1266
  }
@@ -983,6 +1269,28 @@ function createReconciledPackCommand(pack, dependencies) {
983
1269
  context.logger.info(`Adopted project tool pack: ${adoptedPack}`);
984
1270
  }
985
1271
  context.logger.info(`Installed ${pack} tool pack.`);
1272
+ context.logger.info(`Lifecycle: ${lifecycleOutcome.status} (${lifecycleOutcome.canonical.status})`);
1273
+ if (providerVisibility) {
1274
+ context.logger.info(`Provider catalog visibility: ${providerVisibility.state} — ${providerVisibility.reason}`);
1275
+ }
1276
+ if (projectGuidance) {
1277
+ const guidanceMessage = `Project guidance: ${projectGuidance.action} — ${projectGuidance.reason}`;
1278
+ if (projectGuidance.action === 'blocked' ||
1279
+ projectGuidance.action === 'manual-required') {
1280
+ context.logger.warn(guidanceMessage);
1281
+ if (projectGuidance.manualPatch) {
1282
+ for (const line of formatAgentsMdGuidanceResult({
1283
+ action: 'manual-required',
1284
+ manualPatch: projectGuidance.manualPatch,
1285
+ })) {
1286
+ context.logger.info(line);
1287
+ }
1288
+ }
1289
+ }
1290
+ else {
1291
+ context.logger.info(guidanceMessage);
1292
+ }
1293
+ }
986
1294
  for (const result of results) {
987
1295
  context.logger.info(`Scope: ${result.request.scope}`);
988
1296
  context.logger.info(`Target root: ${result.request.scopeRoot}`);
@@ -990,12 +1298,35 @@ function createReconciledPackCommand(pack, dependencies) {
990
1298
  context.logger.info(`Run: oat sync --scope ${result.request.scope}`);
991
1299
  }
992
1300
  }
993
- process.exitCode = 0;
1301
+ process.exitCode =
1302
+ lifecycleOutcome.status === 'complete' &&
1303
+ projectGuidance?.action !== 'blocked' &&
1304
+ projectGuidance?.action !== 'manual-required'
1305
+ ? 0
1306
+ : 1;
994
1307
  }
995
1308
  catch (error) {
996
1309
  const message = error instanceof Error ? error.message : String(error);
1310
+ lifecycleOutcome ??= {
1311
+ schemaVersion: 1,
1312
+ selection: selection ?? {
1313
+ pack,
1314
+ requested: 'user',
1315
+ retainedRealizedScopes: [],
1316
+ targetScopes: [],
1317
+ },
1318
+ canonical: { status: 'failed', results: [] },
1319
+ sync: { scopes: [], status: 'not-run', providers: [] },
1320
+ finalEvidence: null,
1321
+ status: 'failed',
1322
+ recovery: [{ code: 'canonical-apply-failed', message }],
1323
+ };
997
1324
  if (context.json)
998
- context.logger.json({ status: 'error', message });
1325
+ context.logger.json({
1326
+ status: 'error',
1327
+ message,
1328
+ lifecycle: lifecycleOutcome,
1329
+ });
999
1330
  else
1000
1331
  context.logger.error(message);
1001
1332
  process.exitCode = 1;
@@ -1079,13 +1410,13 @@ export function createInitToolsCommand(overrides = {}) {
1079
1410
  });
1080
1411
  }
1081
1412
  }
1082
- const command = new Command('tools').description('Install OAT tool packs (core, ideas, docs, workflows, utility, project-management, research, brainstorm)');
1413
+ const command = withProjectGuidanceOptions(new Command('tools').description('Install OAT tool packs (core, ideas, docs, workflows, utility, project-management, research, brainstorm)'));
1083
1414
  for (const packCommand of packCommands) {
1084
1415
  command.addCommand(packCommand);
1085
1416
  }
1086
1417
  command.action(async (_options, actionCommand) => {
1087
1418
  const context = dependencies.buildCommandContext(readGlobalOptions(actionCommand));
1088
- const selectedPacks = await runInitTools(context, dependencies);
1419
+ const selectedPacks = await runInitTools(context, dependencies, commandProjectGuidanceChoice(actionCommand));
1089
1420
  appliedScopesByCommand.set(actionCommand, lastRunInitToolsMetadata?.appliedScopes ?? []);
1090
1421
  setInstalledCanonicalPaths(actionCommand, canonicalPathsForPacks(selectedPacks));
1091
1422
  });