@open-agent-toolkit/cli 0.2.36 → 0.2.38
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/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/backlog-lifecycle.md +18 -0
- package/assets/docs/cli-utilities/bootstrap.md +6 -3
- package/assets/docs/cli-utilities/config-and-local-state.md +26 -12
- package/assets/docs/cli-utilities/configuration.md +36 -13
- package/assets/docs/cli-utilities/tool-packs.md +423 -51
- package/assets/docs/provider-sync/scope-and-surface.md +4 -1
- package/assets/docs/reference/cli-reference.md +5 -3
- package/assets/docs/reference/file-locations.md +28 -4
- package/assets/docs/reference/troubleshooting.md +132 -2
- package/assets/docs/workflows/projects/lifecycle.md +21 -2
- package/assets/public-package-versions.json +4 -4
- package/assets/scripts/generate-oat-state.sh +441 -0
- package/assets/scripts/generate-thin-index.sh +200 -0
- package/assets/skills/oat-agent-instructions-analyze/SKILL.md +10 -5
- package/assets/skills/oat-agent-instructions-apply/SKILL.md +30 -5
- package/assets/skills/oat-brainstorm/SKILL.md +34 -8
- package/assets/skills/oat-brainstorm/references/destinations.md +6 -1
- package/assets/skills/oat-docs-analyze/SKILL.md +9 -4
- package/assets/skills/oat-docs-apply/SKILL.md +6 -3
- package/assets/skills/oat-pjm-add-backlog-item/SKILL.md +14 -1
- package/assets/skills/oat-pjm-decision/SKILL.md +22 -10
- package/assets/skills/oat-pjm-review-backlog/SKILL.md +20 -3
- package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +20 -10
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
- package/assets/skills/oat-project-document/SKILL.md +21 -3
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-summary/SKILL.md +23 -11
- package/assets/skills/oat-repo-knowledge-index/SKILL.md +6 -3
- package/dist/commands/backlog/index.d.ts +2 -0
- package/dist/commands/backlog/index.d.ts.map +1 -1
- package/dist/commands/backlog/index.js +67 -23
- package/dist/commands/backlog/new.d.ts +3 -0
- package/dist/commands/backlog/new.d.ts.map +1 -1
- package/dist/commands/backlog/new.js +8 -21
- package/dist/commands/decision/agents-guidance.d.ts.map +1 -1
- package/dist/commands/decision/agents-guidance.js +2 -1
- package/dist/commands/decision/index.d.ts +2 -0
- package/dist/commands/decision/index.d.ts.map +1 -1
- package/dist/commands/decision/index.js +19 -3
- package/dist/commands/decision/new.d.ts +4 -0
- package/dist/commands/decision/new.d.ts.map +1 -1
- package/dist/commands/decision/new.js +11 -30
- package/dist/commands/doctor/index.d.ts +4 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +254 -9
- package/dist/commands/init/tools/brainstorm/index.d.ts.map +1 -1
- package/dist/commands/init/tools/brainstorm/index.js +20 -6
- package/dist/commands/init/tools/docs/index.d.ts.map +1 -1
- package/dist/commands/init/tools/docs/index.js +4 -1
- package/dist/commands/init/tools/ideas/index.d.ts.map +1 -1
- package/dist/commands/init/tools/ideas/index.js +4 -1
- package/dist/commands/init/tools/index.d.ts +11 -2
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +459 -351
- package/dist/commands/init/tools/install-state.d.ts +2 -0
- package/dist/commands/init/tools/install-state.d.ts.map +1 -1
- package/dist/commands/init/tools/install-state.js +11 -0
- package/dist/commands/init/tools/project-management/agents-guidance.d.ts.map +1 -1
- package/dist/commands/init/tools/project-management/agents-guidance.js +3 -1
- package/dist/commands/init/tools/project-management/index.d.ts +1 -0
- package/dist/commands/init/tools/project-management/index.d.ts.map +1 -1
- package/dist/commands/init/tools/project-management/index.js +24 -55
- package/dist/commands/init/tools/project-management/install-project-management.d.ts +4 -0
- package/dist/commands/init/tools/project-management/install-project-management.d.ts.map +1 -1
- package/dist/commands/init/tools/project-management/install-project-management.js +4 -1
- package/dist/commands/init/tools/research/index.d.ts.map +1 -1
- package/dist/commands/init/tools/research/index.js +4 -1
- package/dist/commands/init/tools/shared/copy-helpers.d.ts.map +1 -1
- package/dist/commands/init/tools/shared/copy-helpers.js +10 -0
- package/dist/commands/init/tools/shared/skill-manifest.d.ts +15 -15
- package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
- package/dist/commands/init/tools/shared/skill-manifest.js +17 -122
- package/dist/commands/init/tools/utility/index.d.ts.map +1 -1
- package/dist/commands/init/tools/utility/index.js +4 -1
- package/dist/commands/init/tools/workflows/index.d.ts.map +1 -1
- package/dist/commands/init/tools/workflows/index.js +4 -1
- package/dist/commands/init/tools/workflows/install-workflows.d.ts.map +1 -1
- package/dist/commands/init/tools/workflows/install-workflows.js +2 -4
- package/dist/commands/pjm/adoption.d.ts +12 -0
- package/dist/commands/pjm/adoption.d.ts.map +1 -0
- package/dist/commands/pjm/adoption.js +47 -0
- package/dist/commands/pjm/doctor.d.ts +3 -2
- package/dist/commands/pjm/doctor.d.ts.map +1 -1
- package/dist/commands/pjm/doctor.js +28 -13
- package/dist/commands/pjm/index.d.ts +2 -0
- package/dist/commands/pjm/index.d.ts.map +1 -1
- package/dist/commands/pjm/index.js +11 -3
- package/dist/commands/pjm/init.d.ts +3 -0
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +37 -32
- package/dist/commands/pjm/migrate.d.ts +1 -0
- package/dist/commands/pjm/migrate.d.ts.map +1 -1
- package/dist/commands/pjm/migrate.js +1 -0
- package/dist/commands/pjm/template-source.d.ts +14 -0
- package/dist/commands/pjm/template-source.d.ts.map +1 -0
- package/dist/commands/pjm/template-source.js +56 -0
- package/dist/commands/remove/skills/remove-skills.d.ts.map +1 -1
- package/dist/commands/remove/skills/remove-skills.js +10 -19
- package/dist/commands/status/index.d.ts +5 -0
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +203 -2
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +40 -7
- package/dist/commands/sync/sync.types.d.ts +4 -0
- package/dist/commands/sync/sync.types.d.ts.map +1 -1
- package/dist/commands/tools/has/has-pack.d.ts +11 -2
- package/dist/commands/tools/has/has-pack.d.ts.map +1 -1
- package/dist/commands/tools/has/has-pack.js +33 -20
- package/dist/commands/tools/index.d.ts.map +1 -1
- package/dist/commands/tools/index.js +3 -1
- package/dist/commands/tools/info/info-tool.d.ts +3 -0
- package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
- package/dist/commands/tools/info/info-tool.js +29 -4
- package/dist/commands/tools/list/list-tools.d.ts +3 -0
- package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
- package/dist/commands/tools/list/list-tools.js +31 -4
- package/dist/commands/tools/migrate/index.d.ts +26 -0
- package/dist/commands/tools/migrate/index.d.ts.map +1 -0
- package/dist/commands/tools/migrate/index.js +312 -0
- package/dist/commands/tools/migrate/migrate-pack.d.ts +81 -0
- package/dist/commands/tools/migrate/migrate-pack.d.ts.map +1 -0
- package/dist/commands/tools/migrate/migrate-pack.js +324 -0
- package/dist/commands/tools/outdated/outdated-tools.d.ts +11 -0
- package/dist/commands/tools/outdated/outdated-tools.d.ts.map +1 -1
- package/dist/commands/tools/outdated/outdated-tools.js +33 -5
- package/dist/commands/tools/remove/index.d.ts.map +1 -1
- package/dist/commands/tools/remove/index.js +73 -20
- package/dist/commands/tools/remove/remove-tools.d.ts +28 -0
- package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
- package/dist/commands/tools/remove/remove-tools.js +156 -26
- package/dist/commands/tools/shared/apply-pack-reconcile.d.ts +41 -0
- package/dist/commands/tools/shared/apply-pack-reconcile.d.ts.map +1 -0
- package/dist/commands/tools/shared/apply-pack-reconcile.js +126 -0
- package/dist/commands/tools/shared/auto-sync.d.ts +5 -2
- package/dist/commands/tools/shared/auto-sync.d.ts.map +1 -1
- package/dist/commands/tools/shared/auto-sync.js +1 -0
- package/dist/commands/tools/shared/content-digest.d.ts +3 -0
- package/dist/commands/tools/shared/content-digest.d.ts.map +1 -0
- package/dist/commands/tools/shared/content-digest.js +57 -0
- package/dist/commands/tools/shared/format-pack-inventory.d.ts +3 -0
- package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -0
- package/dist/commands/tools/shared/format-pack-inventory.js +21 -0
- package/dist/commands/tools/shared/install-sync-context.d.ts.map +1 -1
- package/dist/commands/tools/shared/install-sync-context.js +2 -25
- package/dist/commands/tools/shared/pack-inventory.d.ts +46 -0
- package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-inventory.js +207 -0
- package/dist/commands/tools/shared/pack-lifecycle.d.ts +34 -0
- package/dist/commands/tools/shared/pack-lifecycle.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-lifecycle.js +67 -0
- package/dist/commands/tools/shared/pack-manifest.d.ts +11 -0
- package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-manifest.js +384 -0
- package/dist/commands/tools/shared/pack-paths.d.ts +21 -0
- package/dist/commands/tools/shared/pack-paths.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-paths.js +35 -0
- package/dist/commands/tools/shared/pack-reconcile.d.ts +64 -0
- package/dist/commands/tools/shared/pack-reconcile.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-reconcile.js +158 -0
- package/dist/commands/tools/shared/project-tools-config.d.ts.map +1 -1
- package/dist/commands/tools/shared/project-tools-config.js +12 -38
- package/dist/commands/tools/shared/scan-tools.js +2 -2
- package/dist/commands/tools/shared/scoped-pack-intent.d.ts +34 -0
- package/dist/commands/tools/shared/scoped-pack-intent.d.ts.map +1 -0
- package/dist/commands/tools/shared/scoped-pack-intent.js +116 -0
- package/dist/commands/tools/shared/types.d.ts +22 -0
- package/dist/commands/tools/shared/types.d.ts.map +1 -1
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +30 -5
- package/dist/commands/tools/update/update-tools.d.ts +9 -1
- package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
- package/dist/commands/tools/update/update-tools.js +176 -64
- package/dist/config/oat-config.d.ts +6 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +49 -20
- package/dist/fs/paths.d.ts +12 -0
- package/dist/fs/paths.d.ts.map +1 -1
- package/dist/fs/paths.js +87 -1
- package/package.json +2 -2
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { rm, unlink } from 'node:fs/promises';
|
|
2
1
|
import { join } from 'node:path';
|
|
3
2
|
import { buildCommandContext, } from '../../../app/command-context.js';
|
|
4
|
-
import { buildDecisionAgentsSectionBody, DECISION_AGENTS_SECTION_KEY, } from '../../decision/agents-guidance.js';
|
|
5
3
|
import { copyDirWithStatus } from '../../init/tools/shared/copy-helpers.js';
|
|
6
4
|
import { applyGitignore } from '../../local/apply.js';
|
|
7
5
|
import { addLocalPaths } from '../../local/manage.js';
|
|
8
6
|
import { removeAgentsMdSection, upsertAgentsMdSection, } from '../../shared/agents-md.js';
|
|
7
|
+
import { withScopeOption } from '../../shared/scope-option.js';
|
|
9
8
|
import { selectManyWithAbort, selectWithAbort, } from '../../shared/shared.prompts.js';
|
|
10
9
|
import { readGlobalOptions } from '../../shared/shared.utils.js';
|
|
11
10
|
import { canonicalPathsForPacks, getInstalledCanonicalPaths, setInstalledCanonicalPaths, } from '../../tools/shared/install-sync-context.js';
|
|
11
|
+
import { inventoryPack, } from '../../tools/shared/pack-inventory.js';
|
|
12
|
+
import { reconcilePackLifecycles, } from '../../tools/shared/pack-lifecycle.js';
|
|
13
|
+
import { getPackDefinition } from '../../tools/shared/pack-manifest.js';
|
|
12
14
|
import { reconcileProjectToolsConfig } from '../../tools/shared/project-tools-config.js';
|
|
13
15
|
import { scanTools } from '../../tools/shared/scan-tools.js';
|
|
14
16
|
import { readOatConfig, resolveLocalPaths, writeOatConfig, } from '../../../config/oat-config.js';
|
|
@@ -23,13 +25,12 @@ import { createInitToolsDocsCommand } from './docs/index.js';
|
|
|
23
25
|
import { installDocs as defaultInstallDocs, } from './docs/install-docs.js';
|
|
24
26
|
import { createInitToolsIdeasCommand } from './ideas/index.js';
|
|
25
27
|
import { installIdeas as defaultInstallIdeas, } from './ideas/install-ideas.js';
|
|
26
|
-
import { buildPackInstallStateMap, } from './install-state.js';
|
|
28
|
+
import { buildPackInstallStateMap, buildPackInstallStateMapFromInventory, } from './install-state.js';
|
|
27
29
|
import { createInitToolsProjectManagementCommand } from './project-management/index.js';
|
|
28
|
-
import { buildProjectManagementAgentsSectionBody, PROJECT_MANAGEMENT_AGENTS_SECTION_KEY, } from './project-management/agents-guidance.js';
|
|
29
30
|
import { installProjectManagement as defaultInstallProjectManagement, } from './project-management/install-project-management.js';
|
|
30
31
|
import { createInitToolsResearchCommand } from './research/index.js';
|
|
31
32
|
import { installResearch as defaultInstallResearch, } from './research/install-research.js';
|
|
32
|
-
import {
|
|
33
|
+
import { DOCS_SKILLS, RESEARCH_SKILLS, UTILITY_SKILLS, resolvePackDefaultScope, } from './shared/skill-manifest.js';
|
|
33
34
|
import { createInitToolsUtilityCommand } from './utility/index.js';
|
|
34
35
|
import { installUtility as defaultInstallUtility, } from './utility/install-utility.js';
|
|
35
36
|
import { createInitToolsWorkflowsCommand } from './workflows/index.js';
|
|
@@ -47,33 +48,8 @@ const ALL_TOOL_PACKS = [
|
|
|
47
48
|
'research',
|
|
48
49
|
'brainstorm',
|
|
49
50
|
];
|
|
50
|
-
const USER_ELIGIBLE_PACK_MEMBERS = {
|
|
51
|
-
ideas: {
|
|
52
|
-
skills: IDEA_SKILLS,
|
|
53
|
-
agents: [],
|
|
54
|
-
},
|
|
55
|
-
docs: {
|
|
56
|
-
skills: DOCS_SKILLS,
|
|
57
|
-
agents: [],
|
|
58
|
-
},
|
|
59
|
-
workflows: {
|
|
60
|
-
skills: WORKFLOW_SKILLS,
|
|
61
|
-
agents: WORKFLOW_AGENTS,
|
|
62
|
-
},
|
|
63
|
-
utility: {
|
|
64
|
-
skills: UTILITY_SKILLS,
|
|
65
|
-
agents: [],
|
|
66
|
-
},
|
|
67
|
-
research: {
|
|
68
|
-
skills: RESEARCH_SKILLS,
|
|
69
|
-
agents: RESEARCH_AGENTS,
|
|
70
|
-
},
|
|
71
|
-
brainstorm: {
|
|
72
|
-
skills: BRAINSTORM_SKILLS,
|
|
73
|
-
agents: [],
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
51
|
let lastRunInitToolsMetadata = null;
|
|
52
|
+
const appliedScopesByCommand = new WeakMap();
|
|
77
53
|
const DEFAULT_DEPENDENCIES = {
|
|
78
54
|
buildCommandContext,
|
|
79
55
|
resolveProjectRoot,
|
|
@@ -91,21 +67,6 @@ const DEFAULT_DEPENDENCIES = {
|
|
|
91
67
|
installResearch: defaultInstallResearch,
|
|
92
68
|
installBrainstorm: defaultInstallBrainstorm,
|
|
93
69
|
copyDirWithStatus,
|
|
94
|
-
removeDirectory: async (target) => {
|
|
95
|
-
await rm(target, { recursive: true, force: true });
|
|
96
|
-
},
|
|
97
|
-
removeFile: async (target) => {
|
|
98
|
-
try {
|
|
99
|
-
await unlink(target);
|
|
100
|
-
}
|
|
101
|
-
catch (error) {
|
|
102
|
-
if (!(error instanceof Error) ||
|
|
103
|
-
!('code' in error) ||
|
|
104
|
-
error.code !== 'ENOENT') {
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
70
|
addLocalPaths,
|
|
110
71
|
applyGitignore,
|
|
111
72
|
readOatConfig,
|
|
@@ -113,12 +74,15 @@ const DEFAULT_DEPENDENCIES = {
|
|
|
113
74
|
resolveLocalPaths,
|
|
114
75
|
upsertAgentsMdSection,
|
|
115
76
|
removeAgentsMdSection,
|
|
77
|
+
reconcilePacks: reconcilePackLifecycles,
|
|
78
|
+
inventoryPack,
|
|
116
79
|
};
|
|
117
80
|
const USER_ELIGIBLE_PACKS = new Set([
|
|
118
81
|
'ideas',
|
|
119
82
|
'docs',
|
|
120
83
|
'workflows',
|
|
121
84
|
'utility',
|
|
85
|
+
'project-management',
|
|
122
86
|
'research',
|
|
123
87
|
'brainstorm',
|
|
124
88
|
]);
|
|
@@ -163,8 +127,7 @@ function reconcilePackScope(current, desired) {
|
|
|
163
127
|
const currentScopes = new Set(scopesForLocation(current));
|
|
164
128
|
const desiredScopes = new Set(scopesForEndState(desired));
|
|
165
129
|
const adds = [...desiredScopes].filter((scope) => !currentScopes.has(scope));
|
|
166
|
-
|
|
167
|
-
return { adds, removes };
|
|
130
|
+
return { adds };
|
|
168
131
|
}
|
|
169
132
|
/**
|
|
170
133
|
* Render a batch change summary (`+ pack@scope` adds / `- pack@scope` removes)
|
|
@@ -197,12 +160,23 @@ function unionScopeWithCurrent(current, requested) {
|
|
|
197
160
|
return scopes.has('user') ? 'user' : 'project';
|
|
198
161
|
}
|
|
199
162
|
async function loadInstalledPackStates(projectRoot, userRoot, assetsRoot, dependencies) {
|
|
200
|
-
|
|
201
|
-
dependencies.
|
|
202
|
-
|
|
203
|
-
scopeRoot: projectRoot,
|
|
163
|
+
if (dependencies.reconcilePacks && dependencies.inventoryPack) {
|
|
164
|
+
const inventories = await Promise.all(ALL_TOOL_PACKS.map((pack) => dependencies.inventoryPack({
|
|
165
|
+
pack,
|
|
204
166
|
assetsRoot,
|
|
205
|
-
|
|
167
|
+
...(projectRoot ? { projectRoot } : {}),
|
|
168
|
+
userRoot,
|
|
169
|
+
})));
|
|
170
|
+
return buildPackInstallStateMapFromInventory(ALL_TOOL_PACKS, inventories);
|
|
171
|
+
}
|
|
172
|
+
const [projectTools, userTools] = await Promise.all([
|
|
173
|
+
projectRoot
|
|
174
|
+
? dependencies.scanTools({
|
|
175
|
+
scope: 'project',
|
|
176
|
+
scopeRoot: projectRoot,
|
|
177
|
+
assetsRoot,
|
|
178
|
+
})
|
|
179
|
+
: Promise.resolve([]),
|
|
206
180
|
dependencies.scanTools({
|
|
207
181
|
scope: 'user',
|
|
208
182
|
scopeRoot: userRoot,
|
|
@@ -244,7 +218,7 @@ function buildPackChoices(installedPackStates) {
|
|
|
244
218
|
checked: true,
|
|
245
219
|
},
|
|
246
220
|
{
|
|
247
|
-
label: `Project Management [project]${installedPackStates['project-management'].location === 'not-installed' ? '' : ` (installed: ${formatInstalledLocation(installedPackStates['project-management'].location)})`}`,
|
|
221
|
+
label: `Project Management [project|user]${installedPackStates['project-management'].location === 'not-installed' ? '' : ` (installed: ${formatInstalledLocation(installedPackStates['project-management'].location)})`}`,
|
|
248
222
|
value: 'project-management',
|
|
249
223
|
checked: false,
|
|
250
224
|
},
|
|
@@ -275,23 +249,6 @@ export function consumeInitToolsRunMetadata() {
|
|
|
275
249
|
lastRunInitToolsMetadata = null;
|
|
276
250
|
return metadata;
|
|
277
251
|
}
|
|
278
|
-
async function removePackFromScope(pack, root, dependencies) {
|
|
279
|
-
const members = USER_ELIGIBLE_PACK_MEMBERS[pack];
|
|
280
|
-
for (const skill of members.skills) {
|
|
281
|
-
await dependencies.removeDirectory(join(root, '.agents', 'skills', skill));
|
|
282
|
-
}
|
|
283
|
-
for (const agent of members.agents) {
|
|
284
|
-
await dependencies.removeFile(join(root, '.agents', 'agents', agent));
|
|
285
|
-
}
|
|
286
|
-
if (pack === 'workflows') {
|
|
287
|
-
for (const template of WORKFLOW_TEMPLATES) {
|
|
288
|
-
await dependencies.removeFile(join(root, '.oat', 'templates', template));
|
|
289
|
-
}
|
|
290
|
-
for (const script of WORKFLOW_SCRIPTS) {
|
|
291
|
-
await dependencies.removeFile(join(root, '.oat', 'scripts', script));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
252
|
/**
|
|
296
253
|
* Resolve a possibly-deferred scope-selection signal into a concrete mode.
|
|
297
254
|
*
|
|
@@ -327,9 +284,9 @@ async function resolveDeferredGate(scopeSelection, interactive, dependencies) {
|
|
|
327
284
|
}
|
|
328
285
|
async function resolvePackScopes(context, selections, installedPackStates, dependencies) {
|
|
329
286
|
const scopes = {};
|
|
330
|
-
//
|
|
287
|
+
// Scope eligibility is release-owned by the canonical manifest.
|
|
331
288
|
for (const pack of selections) {
|
|
332
|
-
if (!
|
|
289
|
+
if (!getPackDefinition(pack).allowedScopes.includes('user')) {
|
|
333
290
|
scopes[pack] = 'project';
|
|
334
291
|
}
|
|
335
292
|
}
|
|
@@ -389,14 +346,23 @@ async function resolvePackScopes(context, selections, installedPackStates, depen
|
|
|
389
346
|
const currentLocation = installedPackStates[pack].location;
|
|
390
347
|
const defaultEndState = resolvePackDefaultEndState(pack, currentLocation);
|
|
391
348
|
const selection = await dependencies.selectWithAbort(`Where should ${pack} install?`, buildPackEndStateChoices(pack, currentLocation, defaultEndState), { interactive: context.interactive });
|
|
392
|
-
|
|
349
|
+
const requested = selection ?? defaultEndState;
|
|
350
|
+
scopes[pack] =
|
|
351
|
+
requested === 'both'
|
|
352
|
+
? 'both'
|
|
353
|
+
: unionScopeWithCurrent(currentLocation, requested);
|
|
393
354
|
}
|
|
394
355
|
return scopes;
|
|
395
356
|
}
|
|
396
357
|
/**
|
|
397
|
-
*
|
|
398
|
-
*
|
|
399
|
-
*
|
|
358
|
+
* Existing-placement precedence: a pack that is already installed keeps the
|
|
359
|
+
* placement it has, and only a pack that is not present anywhere falls back to
|
|
360
|
+
* its configured default scope.
|
|
361
|
+
*
|
|
362
|
+
* This is the single definition of that rule. It backs the interactive
|
|
363
|
+
* selector's offered default, non-interactive aggregate resolution, and the
|
|
364
|
+
* per-pack `oat tools install <pack>` subcommands, so a bare re-install can
|
|
365
|
+
* never silently migrate or duplicate an existing install across scopes.
|
|
400
366
|
*/
|
|
401
367
|
function resolvePackDefaultEndState(pack, currentLocation) {
|
|
402
368
|
switch (currentLocation) {
|
|
@@ -523,9 +489,16 @@ export function buildToolPacksSectionBody(packs) {
|
|
|
523
489
|
export async function runInitTools(context, dependencies) {
|
|
524
490
|
lastRunInitToolsMetadata = null;
|
|
525
491
|
try {
|
|
526
|
-
const projectRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
527
492
|
const userRoot = dependencies.resolveScopeRoot('user', context.cwd, context.home);
|
|
528
493
|
const assetsRoot = await dependencies.resolveAssetsRoot();
|
|
494
|
+
let projectRoot = null;
|
|
495
|
+
try {
|
|
496
|
+
projectRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
if (context.scope === 'project')
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
529
502
|
const initialPackStates = await loadInstalledPackStates(projectRoot, userRoot, assetsRoot, dependencies);
|
|
530
503
|
const selectedPacks = context.interactive
|
|
531
504
|
? ((await dependencies.selectManyWithAbort('Select tool packs to install', buildPackChoices(initialPackStates), { interactive: context.interactive })) ?? [])
|
|
@@ -542,7 +515,7 @@ export async function runInitTools(context, dependencies) {
|
|
|
542
515
|
selectedPacks.push('project-management');
|
|
543
516
|
}
|
|
544
517
|
if (selectedPacks.length === 0) {
|
|
545
|
-
lastRunInitToolsMetadata = { affectedScopes: [] };
|
|
518
|
+
lastRunInitToolsMetadata = { affectedScopes: [], appliedScopes: [] };
|
|
546
519
|
if (!context.json) {
|
|
547
520
|
context.logger.info('No tool packs selected.');
|
|
548
521
|
}
|
|
@@ -551,285 +524,278 @@ export async function runInitTools(context, dependencies) {
|
|
|
551
524
|
}
|
|
552
525
|
const packScopes = await resolvePackScopes(context, selectedPacks, initialPackStates, dependencies);
|
|
553
526
|
function scopeRoot(scope) {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
// end-state, but only the scopes that actually changed (an add or a
|
|
559
|
-
// confirmed remove) are recorded in `affectedScopes`, so auto-sync never
|
|
560
|
-
// prunes a preserved scope.
|
|
561
|
-
const reconciliationByPack = new Map();
|
|
562
|
-
for (const pack of selectedPacks) {
|
|
563
|
-
if (!isUserEligiblePack(pack)) {
|
|
564
|
-
continue;
|
|
527
|
+
if (scope === 'user')
|
|
528
|
+
return userRoot;
|
|
529
|
+
if (!projectRoot) {
|
|
530
|
+
throw new Error('Project scope is unavailable outside a repository');
|
|
565
531
|
}
|
|
566
|
-
|
|
567
|
-
}
|
|
568
|
-
// Scopes a pack should install into (its full desired end-state for
|
|
569
|
-
// user-eligible packs; the resolved scope otherwise). Re-installing an
|
|
570
|
-
// already-present scope is an idempotent copy.
|
|
571
|
-
function packTargets(pack) {
|
|
572
|
-
return packScopes[pack] === 'both'
|
|
573
|
-
? [projectRoot, userRoot]
|
|
574
|
-
: [packScopes[pack] === 'user' ? userRoot : projectRoot];
|
|
575
|
-
}
|
|
576
|
-
// Only scopes that received a new add for this pack should be auto-synced.
|
|
577
|
-
function addedScopes(pack) {
|
|
578
|
-
return new Set(reconciliationByPack.get(pack)?.adds);
|
|
532
|
+
return projectRoot;
|
|
579
533
|
}
|
|
580
534
|
const outdatedSkills = [];
|
|
581
535
|
const affectedScopes = new Set();
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
// Interactive removal gate: if any pack would lose a scope, surface one
|
|
600
|
-
// batch change summary and require a single confirmation before mutating
|
|
601
|
-
// anything. Declining aborts with zero changes (no installs, no removals).
|
|
602
|
-
if (stagedRemovals.length > 0 && context.interactive) {
|
|
603
|
-
const stagedAdds = [];
|
|
604
|
-
for (const [pack, reconciliation] of reconciliationByPack) {
|
|
605
|
-
for (const scope of reconciliation.adds) {
|
|
606
|
-
stagedAdds.push({ pack, scope });
|
|
536
|
+
if (dependencies.reconcilePacks) {
|
|
537
|
+
const requests = [];
|
|
538
|
+
for (const pack of selectedPacks) {
|
|
539
|
+
const targets = pack === 'core'
|
|
540
|
+
? [{ scope: 'user', root: userRoot }]
|
|
541
|
+
: scopesForEndState(packScopes[pack]).map((scope) => ({
|
|
542
|
+
scope,
|
|
543
|
+
root: scopeRoot(scope),
|
|
544
|
+
}));
|
|
545
|
+
for (const target of targets) {
|
|
546
|
+
requests.push({
|
|
547
|
+
pack,
|
|
548
|
+
scope: target.scope,
|
|
549
|
+
scopeRoot: target.root,
|
|
550
|
+
assetsRoot,
|
|
551
|
+
action: 'install',
|
|
552
|
+
});
|
|
607
553
|
}
|
|
608
554
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
value: 'no',
|
|
614
|
-
description: 'Make no changes',
|
|
615
|
-
},
|
|
616
|
-
{
|
|
617
|
-
label: 'Yes, apply adds and removals',
|
|
618
|
-
value: 'yes',
|
|
619
|
-
description: 'Install adds and delete the listed removals',
|
|
620
|
-
},
|
|
621
|
-
], { interactive: context.interactive });
|
|
622
|
-
if (confirmation !== 'yes') {
|
|
623
|
-
lastRunInitToolsMetadata = { affectedScopes: [] };
|
|
624
|
-
if (!context.json) {
|
|
625
|
-
context.logger.info('No changes applied.');
|
|
626
|
-
}
|
|
627
|
-
process.exitCode = 0;
|
|
628
|
-
return [];
|
|
555
|
+
const lifecycle = await dependencies.reconcilePacks(requests);
|
|
556
|
+
for (const { plan } of lifecycle) {
|
|
557
|
+
if (plan.operations.length > 0)
|
|
558
|
+
affectedScopes.add(plan.scope);
|
|
629
559
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
...skill,
|
|
646
|
-
targetRoot: userRoot,
|
|
647
|
-
selectionKey: `${skill.name}:${userRoot}`,
|
|
648
|
-
});
|
|
560
|
+
if (requests.some(({ pack, scope }) => pack === 'workflows' && scope === 'project')) {
|
|
561
|
+
const repoRoot = scopeRoot('project');
|
|
562
|
+
const existingConfig = await dependencies.readOatConfig(repoRoot);
|
|
563
|
+
const paths = [
|
|
564
|
+
'.oat/projects/**/pr',
|
|
565
|
+
'.oat/projects/**/reviews/archived',
|
|
566
|
+
];
|
|
567
|
+
const existing = new Set(dependencies.resolveLocalPaths(existingConfig));
|
|
568
|
+
if (!paths.every((path) => existing.has(path))) {
|
|
569
|
+
const addResult = await dependencies.addLocalPaths(repoRoot, paths);
|
|
570
|
+
if (addResult.added.length > 0) {
|
|
571
|
+
const config = await dependencies.readOatConfig(repoRoot);
|
|
572
|
+
await dependencies.applyGitignore(repoRoot, dependencies.resolveLocalPaths(config));
|
|
573
|
+
}
|
|
574
|
+
}
|
|
649
575
|
}
|
|
650
576
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
577
|
+
else {
|
|
578
|
+
// Reconcile current placement vs the desired end-state per user-eligible
|
|
579
|
+
// pack into adds/removes. Installs copy idempotently into the full desired
|
|
580
|
+
// end-state, but only the scopes that actually changed (an add or a
|
|
581
|
+
// confirmed remove) are recorded in `affectedScopes`, so auto-sync never
|
|
582
|
+
// prunes a preserved scope.
|
|
583
|
+
const reconciliationByPack = new Map();
|
|
584
|
+
for (const pack of selectedPacks) {
|
|
585
|
+
if (!isUserEligiblePack(pack)) {
|
|
586
|
+
continue;
|
|
657
587
|
}
|
|
658
|
-
|
|
588
|
+
reconciliationByPack.set(pack, reconcilePackScope(initialPackStates[pack].location, packScopes[pack]));
|
|
589
|
+
}
|
|
590
|
+
// Scopes a pack should install into (its full desired end-state for
|
|
591
|
+
// user-eligible packs; the resolved scope otherwise). Re-installing an
|
|
592
|
+
// already-present scope is an idempotent copy.
|
|
593
|
+
function packTargets(pack) {
|
|
594
|
+
return packScopes[pack] === 'both'
|
|
595
|
+
? [scopeRoot('project'), userRoot]
|
|
596
|
+
: [scopeRoot(packScopes[pack] === 'user' ? 'user' : 'project')];
|
|
597
|
+
}
|
|
598
|
+
// Only scopes that received a new add for this pack should be auto-synced.
|
|
599
|
+
function addedScopes(pack) {
|
|
600
|
+
return new Set(reconciliationByPack.get(pack)?.adds);
|
|
601
|
+
}
|
|
602
|
+
if (selectedPacks.includes('core')) {
|
|
603
|
+
// Core pack always installs at user scope, regardless of userEligibleScope
|
|
604
|
+
affectedScopes.add('user');
|
|
605
|
+
const coreResult = await dependencies.installCore({
|
|
659
606
|
assetsRoot,
|
|
660
|
-
targetRoot,
|
|
607
|
+
targetRoot: userRoot,
|
|
661
608
|
});
|
|
662
|
-
for (const skill of
|
|
609
|
+
for (const skill of coreResult.outdatedSkills) {
|
|
663
610
|
outdatedSkills.push({
|
|
664
611
|
...skill,
|
|
665
|
-
targetRoot,
|
|
666
|
-
selectionKey: `${skill.name}:${
|
|
612
|
+
targetRoot: userRoot,
|
|
613
|
+
selectionKey: `${skill.name}:${userRoot}`,
|
|
667
614
|
});
|
|
668
615
|
}
|
|
669
616
|
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
assetsRoot,
|
|
680
|
-
targetRoot,
|
|
681
|
-
skills: [...DOCS_SKILLS],
|
|
682
|
-
});
|
|
683
|
-
for (const skill of docsResult.outdatedSkills) {
|
|
684
|
-
outdatedSkills.push({
|
|
685
|
-
...skill,
|
|
617
|
+
if (selectedPacks.includes('ideas')) {
|
|
618
|
+
const ideasAdded = addedScopes('ideas');
|
|
619
|
+
for (const targetRoot of packTargets('ideas')) {
|
|
620
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
621
|
+
if (ideasAdded.has(targetScope)) {
|
|
622
|
+
affectedScopes.add(targetScope);
|
|
623
|
+
}
|
|
624
|
+
const ideasResult = await dependencies.installIdeas({
|
|
625
|
+
assetsRoot,
|
|
686
626
|
targetRoot,
|
|
687
|
-
selectionKey: `${skill.name}:${targetRoot}`,
|
|
688
627
|
});
|
|
628
|
+
for (const skill of ideasResult.outdatedSkills) {
|
|
629
|
+
outdatedSkills.push({
|
|
630
|
+
...skill,
|
|
631
|
+
targetRoot,
|
|
632
|
+
selectionKey: `${skill.name}:${targetRoot}`,
|
|
633
|
+
});
|
|
634
|
+
}
|
|
689
635
|
}
|
|
690
636
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
assetsRoot,
|
|
701
|
-
targetRoot,
|
|
702
|
-
scope: targetScope,
|
|
703
|
-
});
|
|
704
|
-
for (const skill of workflowsResult.outdatedSkills) {
|
|
705
|
-
outdatedSkills.push({
|
|
706
|
-
...skill,
|
|
637
|
+
if (selectedPacks.includes('docs')) {
|
|
638
|
+
const docsAdded = addedScopes('docs');
|
|
639
|
+
for (const targetRoot of packTargets('docs')) {
|
|
640
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
641
|
+
if (docsAdded.has(targetScope)) {
|
|
642
|
+
affectedScopes.add(targetScope);
|
|
643
|
+
}
|
|
644
|
+
const docsResult = await dependencies.installDocs({
|
|
645
|
+
assetsRoot,
|
|
707
646
|
targetRoot,
|
|
708
|
-
|
|
647
|
+
skills: [...DOCS_SKILLS],
|
|
709
648
|
});
|
|
649
|
+
for (const skill of docsResult.outdatedSkills) {
|
|
650
|
+
outdatedSkills.push({
|
|
651
|
+
...skill,
|
|
652
|
+
targetRoot,
|
|
653
|
+
selectionKey: `${skill.name}:${targetRoot}`,
|
|
654
|
+
});
|
|
655
|
+
}
|
|
710
656
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
const
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
657
|
+
}
|
|
658
|
+
if (selectedPacks.includes('workflows')) {
|
|
659
|
+
const workflowsAdded = addedScopes('workflows');
|
|
660
|
+
for (const targetRoot of packTargets('workflows')) {
|
|
661
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
662
|
+
if (workflowsAdded.has(targetScope)) {
|
|
663
|
+
affectedScopes.add(targetScope);
|
|
664
|
+
}
|
|
665
|
+
const workflowsResult = await dependencies.installWorkflows({
|
|
666
|
+
assetsRoot,
|
|
667
|
+
targetRoot,
|
|
668
|
+
scope: targetScope,
|
|
669
|
+
});
|
|
670
|
+
for (const skill of workflowsResult.outdatedSkills) {
|
|
671
|
+
outdatedSkills.push({
|
|
672
|
+
...skill,
|
|
673
|
+
targetRoot,
|
|
674
|
+
selectionKey: `${skill.name}:${targetRoot}`,
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
if (targetScope === 'project') {
|
|
678
|
+
const resolvedRoot = workflowsResult.resolvedProjectsRoot || '.oat/projects/shared';
|
|
679
|
+
const projectsBase = resolvedRoot.replace(/\/[^/]+$/, '');
|
|
680
|
+
const PR_ARCHIVE_LOCAL_PATHS = [
|
|
681
|
+
`${projectsBase}/**/pr`,
|
|
682
|
+
`${projectsBase}/**/reviews/archived`,
|
|
683
|
+
];
|
|
684
|
+
const existingConfig = await dependencies.readOatConfig(scopeRoot('project'));
|
|
685
|
+
const existingLocalPaths = new Set(dependencies.resolveLocalPaths(existingConfig));
|
|
686
|
+
const alreadyConfigured = PR_ARCHIVE_LOCAL_PATHS.every((p) => existingLocalPaths.has(p));
|
|
687
|
+
if (!alreadyConfigured) {
|
|
688
|
+
let makeLocal = true;
|
|
689
|
+
if (context.interactive) {
|
|
690
|
+
const selected = await dependencies.selectWithAbort('Should shared-project PR directories and archived review history be local-only (gitignored) or version-controlled?', [
|
|
691
|
+
{
|
|
692
|
+
label: 'Local only (recommended)',
|
|
693
|
+
value: 'local',
|
|
694
|
+
description: 'PR artifacts and archived reviews stay local; active reviews remain tracked until received',
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
label: 'Version controlled',
|
|
698
|
+
value: 'tracked',
|
|
699
|
+
description: 'PR artifacts and archived reviews are committed to the repo too',
|
|
700
|
+
},
|
|
701
|
+
], { interactive: context.interactive });
|
|
702
|
+
makeLocal = selected !== 'tracked';
|
|
703
|
+
}
|
|
704
|
+
if (makeLocal) {
|
|
705
|
+
const addResult = await dependencies.addLocalPaths(scopeRoot('project'), PR_ARCHIVE_LOCAL_PATHS);
|
|
706
|
+
if (addResult.added.length > 0) {
|
|
707
|
+
const config = await dependencies.readOatConfig(scopeRoot('project'));
|
|
708
|
+
const allPaths = dependencies.resolveLocalPaths(config);
|
|
709
|
+
await dependencies.applyGitignore(scopeRoot('project'), allPaths);
|
|
710
|
+
}
|
|
744
711
|
}
|
|
745
712
|
}
|
|
746
713
|
}
|
|
747
714
|
}
|
|
748
715
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
assetsRoot,
|
|
759
|
-
targetRoot,
|
|
760
|
-
skills: [...UTILITY_SKILLS],
|
|
761
|
-
});
|
|
762
|
-
for (const skill of utilityResult.outdatedSkills) {
|
|
763
|
-
outdatedSkills.push({
|
|
764
|
-
...skill,
|
|
716
|
+
if (selectedPacks.includes('utility')) {
|
|
717
|
+
const utilityAdded = addedScopes('utility');
|
|
718
|
+
for (const targetRoot of packTargets('utility')) {
|
|
719
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
720
|
+
if (utilityAdded.has(targetScope)) {
|
|
721
|
+
affectedScopes.add(targetScope);
|
|
722
|
+
}
|
|
723
|
+
const utilityResult = await dependencies.installUtility({
|
|
724
|
+
assetsRoot,
|
|
765
725
|
targetRoot,
|
|
766
|
-
|
|
726
|
+
skills: [...UTILITY_SKILLS],
|
|
767
727
|
});
|
|
728
|
+
for (const skill of utilityResult.outdatedSkills) {
|
|
729
|
+
outdatedSkills.push({
|
|
730
|
+
...skill,
|
|
731
|
+
targetRoot,
|
|
732
|
+
selectionKey: `${skill.name}:${targetRoot}`,
|
|
733
|
+
});
|
|
734
|
+
}
|
|
768
735
|
}
|
|
769
736
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
outdatedSkills.push({
|
|
780
|
-
...skill,
|
|
781
|
-
targetRoot,
|
|
782
|
-
selectionKey: `${skill.name}:${targetRoot}`,
|
|
783
|
-
});
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
if (selectedPacks.includes('research')) {
|
|
787
|
-
const researchAdded = addedScopes('research');
|
|
788
|
-
for (const targetRoot of packTargets('research')) {
|
|
789
|
-
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
790
|
-
if (researchAdded.has(targetScope)) {
|
|
791
|
-
affectedScopes.add(targetScope);
|
|
792
|
-
}
|
|
793
|
-
const researchResult = await dependencies.installResearch({
|
|
794
|
-
assetsRoot,
|
|
795
|
-
targetRoot,
|
|
796
|
-
skills: [...RESEARCH_SKILLS],
|
|
797
|
-
});
|
|
798
|
-
for (const skill of researchResult.outdatedSkills) {
|
|
799
|
-
outdatedSkills.push({
|
|
800
|
-
...skill,
|
|
737
|
+
if (selectedPacks.includes('project-management')) {
|
|
738
|
+
const projectManagementAdded = addedScopes('project-management');
|
|
739
|
+
for (const targetRoot of packTargets('project-management')) {
|
|
740
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
741
|
+
if (projectManagementAdded.has(targetScope)) {
|
|
742
|
+
affectedScopes.add(targetScope);
|
|
743
|
+
}
|
|
744
|
+
const projectManagementResult = await dependencies.installProjectManagement({
|
|
745
|
+
assetsRoot,
|
|
801
746
|
targetRoot,
|
|
802
|
-
selectionKey: `${skill.name}:${targetRoot}`,
|
|
803
747
|
});
|
|
748
|
+
for (const skill of projectManagementResult.outdatedSkills) {
|
|
749
|
+
outdatedSkills.push({
|
|
750
|
+
...skill,
|
|
751
|
+
targetRoot,
|
|
752
|
+
selectionKey: `${skill.name}:${targetRoot}`,
|
|
753
|
+
});
|
|
754
|
+
}
|
|
804
755
|
}
|
|
805
756
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
757
|
+
if (selectedPacks.includes('research')) {
|
|
758
|
+
const researchAdded = addedScopes('research');
|
|
759
|
+
for (const targetRoot of packTargets('research')) {
|
|
760
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
761
|
+
if (researchAdded.has(targetScope)) {
|
|
762
|
+
affectedScopes.add(targetScope);
|
|
763
|
+
}
|
|
764
|
+
const researchResult = await dependencies.installResearch({
|
|
765
|
+
assetsRoot,
|
|
766
|
+
targetRoot,
|
|
767
|
+
skills: [...RESEARCH_SKILLS],
|
|
768
|
+
});
|
|
769
|
+
for (const skill of researchResult.outdatedSkills) {
|
|
770
|
+
outdatedSkills.push({
|
|
771
|
+
...skill,
|
|
772
|
+
targetRoot,
|
|
773
|
+
selectionKey: `${skill.name}:${targetRoot}`,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
813
776
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
777
|
+
}
|
|
778
|
+
if (selectedPacks.includes('brainstorm')) {
|
|
779
|
+
const brainstormAdded = addedScopes('brainstorm');
|
|
780
|
+
for (const targetRoot of packTargets('brainstorm')) {
|
|
781
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
782
|
+
if (brainstormAdded.has(targetScope)) {
|
|
783
|
+
affectedScopes.add(targetScope);
|
|
784
|
+
}
|
|
785
|
+
const brainstormResult = await dependencies.installBrainstorm({
|
|
786
|
+
assetsRoot,
|
|
821
787
|
targetRoot,
|
|
822
|
-
selectionKey: `${skill.name}:${targetRoot}`,
|
|
823
788
|
});
|
|
789
|
+
for (const skill of brainstormResult.outdatedSkills) {
|
|
790
|
+
outdatedSkills.push({
|
|
791
|
+
...skill,
|
|
792
|
+
targetRoot,
|
|
793
|
+
selectionKey: `${skill.name}:${targetRoot}`,
|
|
794
|
+
});
|
|
795
|
+
}
|
|
824
796
|
}
|
|
825
797
|
}
|
|
826
798
|
}
|
|
827
|
-
// Apply confirmed removals only after every add has succeeded, so a failed
|
|
828
|
-
// replacement install can never leave a pack uninstalled in both scopes.
|
|
829
|
-
for (const { pack, scope } of stagedRemovals) {
|
|
830
|
-
await removePackFromScope(pack, scopeRoot(scope), dependencies);
|
|
831
|
-
affectedScopes.add(scope);
|
|
832
|
-
}
|
|
833
799
|
if (outdatedSkills.length > 0) {
|
|
834
800
|
reportOutdatedSkills(context, outdatedSkills);
|
|
835
801
|
if (context.interactive) {
|
|
@@ -854,32 +820,27 @@ export async function runInitTools(context, dependencies) {
|
|
|
854
820
|
pack,
|
|
855
821
|
scope: packScopes[pack],
|
|
856
822
|
}));
|
|
823
|
+
const adoptsProject = packScopeInfo.some(({ scope }) => scope !== 'user');
|
|
857
824
|
const sectionBody = buildToolPacksSectionBody(packScopeInfo);
|
|
858
|
-
const sectionResult =
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
825
|
+
const sectionResult = adoptsProject
|
|
826
|
+
? await dependencies.upsertAgentsMdSection(scopeRoot('project'), 'tools', sectionBody)
|
|
827
|
+
: { action: 'no-change' };
|
|
828
|
+
// Pack placement never writes the project-management or decisions AGENTS
|
|
829
|
+
// sections. Those belong to explicit repository adoption (`oat pjm init`,
|
|
830
|
+
// via `initializeRepoReference`), which owns the `pjm.initialized` marker.
|
|
831
|
+
if (adoptsProject) {
|
|
832
|
+
await dependencies.removeAgentsMdSection(scopeRoot('project'), 'workflows');
|
|
833
|
+
}
|
|
867
834
|
if (!context.json && sectionResult.action !== 'no-change') {
|
|
868
835
|
context.logger.info(`AGENTS.md tool packs section ${sectionResult.action}.`);
|
|
869
836
|
}
|
|
870
|
-
if (!context.json &&
|
|
871
|
-
projectManagementSectionResult !== null &&
|
|
872
|
-
projectManagementSectionResult.action !== 'no-change') {
|
|
873
|
-
context.logger.info(`AGENTS.md project-management section ${projectManagementSectionResult.action}.`);
|
|
874
|
-
}
|
|
875
|
-
if (!context.json &&
|
|
876
|
-
decisionSectionResult !== null &&
|
|
877
|
-
decisionSectionResult.action !== 'no-change') {
|
|
878
|
-
context.logger.info(`AGENTS.md decisions section ${decisionSectionResult.action}.`);
|
|
879
|
-
}
|
|
880
837
|
const affectedScopesList = [...affectedScopes];
|
|
838
|
+
const appliedScopes = [
|
|
839
|
+
...new Set(packScopeInfo.flatMap(({ scope }) => scopesForEndState(scope))),
|
|
840
|
+
];
|
|
881
841
|
lastRunInitToolsMetadata = {
|
|
882
842
|
affectedScopes: affectedScopesList,
|
|
843
|
+
appliedScopes,
|
|
883
844
|
};
|
|
884
845
|
reportSuccess(context, packScopeInfo, affectedScopesList);
|
|
885
846
|
process.exitCode = 0;
|
|
@@ -901,11 +862,138 @@ export async function runInitTools(context, dependencies) {
|
|
|
901
862
|
export async function runInitToolsWithDefaults(context) {
|
|
902
863
|
return runInitTools(context, { ...DEFAULT_DEPENDENCIES });
|
|
903
864
|
}
|
|
865
|
+
/**
|
|
866
|
+
* Scopes a bare `oat tools install <pack>` (no explicit `--scope`) should
|
|
867
|
+
* target.
|
|
868
|
+
*
|
|
869
|
+
* Delegates to `resolvePackDefaultEndState`, the same existing-placement rule
|
|
870
|
+
* the aggregate installer uses, so a pack already installed at project scope
|
|
871
|
+
* stays at project scope instead of gaining a second copy at the pack's
|
|
872
|
+
* `defaultScope`. Falls back to the configured default only when placement
|
|
873
|
+
* cannot be observed (no inventory dependency, or an inventory failure).
|
|
874
|
+
*/
|
|
875
|
+
async function resolvePackCommandScopes(pack, context, assetsRoot, dependencies) {
|
|
876
|
+
const definition = getPackDefinition(pack);
|
|
877
|
+
const inventory = dependencies.inventoryPack;
|
|
878
|
+
if (!inventory)
|
|
879
|
+
return [definition.defaultScope];
|
|
880
|
+
try {
|
|
881
|
+
const userRoot = dependencies.resolveScopeRoot('user', context.cwd, context.home);
|
|
882
|
+
const projectRoot = definition.allowedScopes.includes('project')
|
|
883
|
+
? await dependencies
|
|
884
|
+
.resolveProjectRoot(context.cwd)
|
|
885
|
+
.catch(() => undefined)
|
|
886
|
+
: undefined;
|
|
887
|
+
const packInventory = await inventory({
|
|
888
|
+
pack,
|
|
889
|
+
assetsRoot,
|
|
890
|
+
...(projectRoot ? { projectRoot } : {}),
|
|
891
|
+
userRoot,
|
|
892
|
+
});
|
|
893
|
+
const states = buildPackInstallStateMapFromInventory([pack], [packInventory]);
|
|
894
|
+
return scopesForEndState(resolvePackDefaultEndState(pack, states[pack].location)).filter((scope) => definition.allowedScopes.includes(scope));
|
|
895
|
+
}
|
|
896
|
+
catch {
|
|
897
|
+
return [definition.defaultScope];
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
function createReconciledPackCommand(pack, dependencies) {
|
|
901
|
+
const definition = getPackDefinition(pack);
|
|
902
|
+
const descriptions = {
|
|
903
|
+
core: 'Install OAT core skills (diagnostics, docs)',
|
|
904
|
+
ideas: 'Install OAT ideas skills, templates, and idea workflow files',
|
|
905
|
+
docs: 'Install OAT docs workflow skills',
|
|
906
|
+
'project-management': 'Install OAT project-management skills and templates',
|
|
907
|
+
workflows: 'Install OAT workflows skills, agents, templates, and scripts',
|
|
908
|
+
utility: 'Install OAT utility skills',
|
|
909
|
+
research: 'Install OAT research skills',
|
|
910
|
+
brainstorm: 'Install OAT brainstorm skill (always-on entry point with visual companion)',
|
|
911
|
+
};
|
|
912
|
+
const base = new Command(pack).description(descriptions[pack]);
|
|
913
|
+
const packCommand = pack === 'core' ? base : withScopeOption(base);
|
|
914
|
+
return packCommand
|
|
915
|
+
.option('--force', 'Reconcile managed assets to bundled content')
|
|
916
|
+
.action(async (_options, command) => {
|
|
917
|
+
const context = dependencies.buildCommandContext(readGlobalOptions(command));
|
|
918
|
+
try {
|
|
919
|
+
const assetsRoot = await dependencies.resolveAssetsRoot();
|
|
920
|
+
const explicitScope = command.getOptionValueSourceWithGlobals('scope') === 'cli';
|
|
921
|
+
const requestedScope = context.scope === 'project' || context.scope === 'user'
|
|
922
|
+
? context.scope
|
|
923
|
+
: null;
|
|
924
|
+
const scopes = explicitScope && context.scope === 'all'
|
|
925
|
+
? [...definition.allowedScopes]
|
|
926
|
+
: requestedScope
|
|
927
|
+
? [requestedScope]
|
|
928
|
+
: await resolvePackCommandScopes(pack, context, assetsRoot, dependencies);
|
|
929
|
+
for (const scope of scopes) {
|
|
930
|
+
if (!definition.allowedScopes.includes(scope)) {
|
|
931
|
+
throw new Error(`Pack ${pack} does not allow ${scope} scope`);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
const requests = await Promise.all(scopes.map(async (scope) => ({
|
|
935
|
+
pack,
|
|
936
|
+
scope,
|
|
937
|
+
scopeRoot: scope === 'project'
|
|
938
|
+
? await dependencies.resolveProjectRoot(context.cwd)
|
|
939
|
+
: dependencies.resolveScopeRoot('user', context.cwd, context.home),
|
|
940
|
+
assetsRoot,
|
|
941
|
+
action: 'install',
|
|
942
|
+
})));
|
|
943
|
+
const results = await dependencies.reconcilePacks(requests);
|
|
944
|
+
appliedScopesByCommand.set(command, results.map(({ request }) => request.scope));
|
|
945
|
+
setInstalledCanonicalPaths(command, canonicalPathsForPacks([pack]));
|
|
946
|
+
// Project-scope placement installs capability only. The
|
|
947
|
+
// project-management and decisions AGENTS sections are written by the
|
|
948
|
+
// explicit adoption action (`oat pjm init`), never by pack placement.
|
|
949
|
+
if (context.json) {
|
|
950
|
+
context.logger.json({
|
|
951
|
+
status: 'ok',
|
|
952
|
+
pack,
|
|
953
|
+
scopes,
|
|
954
|
+
results,
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
context.logger.info(`Installed ${pack} tool pack.`);
|
|
959
|
+
for (const result of results) {
|
|
960
|
+
context.logger.info(`Scope: ${result.request.scope}`);
|
|
961
|
+
context.logger.info(`Target root: ${result.request.scopeRoot}`);
|
|
962
|
+
context.logger.info(`Reconciled operations: ${result.apply.applied.length}`);
|
|
963
|
+
context.logger.info(`Run: oat sync --scope ${result.request.scope}`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
process.exitCode = 0;
|
|
967
|
+
}
|
|
968
|
+
catch (error) {
|
|
969
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
970
|
+
if (context.json)
|
|
971
|
+
context.logger.json({ status: 'error', message });
|
|
972
|
+
else
|
|
973
|
+
context.logger.error(message);
|
|
974
|
+
process.exitCode = 1;
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
}
|
|
904
978
|
export function createInitToolsCommand(overrides = {}) {
|
|
905
979
|
const dependencies = {
|
|
906
980
|
...DEFAULT_DEPENDENCIES,
|
|
907
981
|
...overrides,
|
|
908
982
|
};
|
|
983
|
+
const hasLegacyInstallerOverrides = [
|
|
984
|
+
'installCore',
|
|
985
|
+
'installIdeas',
|
|
986
|
+
'installDocs',
|
|
987
|
+
'installWorkflows',
|
|
988
|
+
'installUtility',
|
|
989
|
+
'installProjectManagement',
|
|
990
|
+
'installResearch',
|
|
991
|
+
'installBrainstorm',
|
|
992
|
+
].some((key) => Object.prototype.hasOwnProperty.call(overrides, key));
|
|
993
|
+
if (hasLegacyInstallerOverrides &&
|
|
994
|
+
!Object.prototype.hasOwnProperty.call(overrides, 'reconcilePacks')) {
|
|
995
|
+
dependencies.reconcilePacks = undefined;
|
|
996
|
+
}
|
|
909
997
|
async function reconcileAfterInstall(actionCommand) {
|
|
910
998
|
if (process.exitCode !== undefined && process.exitCode !== 0) {
|
|
911
999
|
return;
|
|
@@ -914,6 +1002,14 @@ export function createInitToolsCommand(overrides = {}) {
|
|
|
914
1002
|
return;
|
|
915
1003
|
}
|
|
916
1004
|
const context = dependencies.buildCommandContext(readGlobalOptions(actionCommand));
|
|
1005
|
+
const appliedScopes = appliedScopesByCommand.get(actionCommand) ??
|
|
1006
|
+
(dependencies.reconcilePacks
|
|
1007
|
+
? []
|
|
1008
|
+
: context.scope === 'user'
|
|
1009
|
+
? ['user']
|
|
1010
|
+
: ['project']);
|
|
1011
|
+
if (!appliedScopes.includes('project'))
|
|
1012
|
+
return;
|
|
917
1013
|
const repoRoot = await dependencies.resolveProjectRoot(context.cwd);
|
|
918
1014
|
await reconcileProjectToolsConfig({
|
|
919
1015
|
repoRoot,
|
|
@@ -921,23 +1017,34 @@ export function createInitToolsCommand(overrides = {}) {
|
|
|
921
1017
|
home: context.home,
|
|
922
1018
|
}, dependencies);
|
|
923
1019
|
}
|
|
924
|
-
const packCommands =
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1020
|
+
const packCommands = dependencies.reconcilePacks
|
|
1021
|
+
? [
|
|
1022
|
+
'core',
|
|
1023
|
+
'ideas',
|
|
1024
|
+
'docs',
|
|
1025
|
+
'project-management',
|
|
1026
|
+
'workflows',
|
|
1027
|
+
'utility',
|
|
1028
|
+
'research',
|
|
1029
|
+
'brainstorm',
|
|
1030
|
+
].map((pack) => createReconciledPackCommand(pack, dependencies))
|
|
1031
|
+
: [
|
|
1032
|
+
createInitToolsCoreCommand(),
|
|
1033
|
+
createInitToolsIdeasCommand(),
|
|
1034
|
+
createInitToolsDocsCommand(),
|
|
1035
|
+
createInitToolsProjectManagementCommand(),
|
|
1036
|
+
createInitToolsWorkflowsCommand(),
|
|
1037
|
+
createInitToolsUtilityCommand(),
|
|
1038
|
+
createInitToolsResearchCommand(),
|
|
1039
|
+
createInitToolsBrainstormCommand({
|
|
1040
|
+
buildCommandContext: dependencies.buildCommandContext,
|
|
1041
|
+
resolveProjectRoot: dependencies.resolveProjectRoot,
|
|
1042
|
+
resolveScopeRoot: dependencies.resolveScopeRoot,
|
|
1043
|
+
resolveAssetsRoot: dependencies.resolveAssetsRoot,
|
|
1044
|
+
installBrainstorm: dependencies.installBrainstorm,
|
|
1045
|
+
scanTools: dependencies.scanTools,
|
|
1046
|
+
}),
|
|
1047
|
+
];
|
|
941
1048
|
for (const packCommand of packCommands) {
|
|
942
1049
|
packCommand.hook('postAction', async (_thisCommand, actionCommand) => {
|
|
943
1050
|
await reconcileAfterInstall(actionCommand);
|
|
@@ -950,6 +1057,7 @@ export function createInitToolsCommand(overrides = {}) {
|
|
|
950
1057
|
command.action(async (_options, actionCommand) => {
|
|
951
1058
|
const context = dependencies.buildCommandContext(readGlobalOptions(actionCommand));
|
|
952
1059
|
const selectedPacks = await runInitTools(context, dependencies);
|
|
1060
|
+
appliedScopesByCommand.set(actionCommand, lastRunInitToolsMetadata?.appliedScopes ?? []);
|
|
953
1061
|
setInstalledCanonicalPaths(actionCommand, canonicalPathsForPacks(selectedPacks));
|
|
954
1062
|
await reconcileAfterInstall(actionCommand);
|
|
955
1063
|
});
|