gmc-openspec 1.1.0 → 1.4.1
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/README.md +2 -2
- package/bin/openspec.js +3 -1
- package/dist/cli/index.d.ts +4 -1
- package/dist/cli/index.js +36 -2
- package/dist/commands/config.js +4 -4
- package/dist/commands/context-store.d.ts +3 -0
- package/dist/commands/context-store.js +475 -0
- package/dist/commands/initiative.d.ts +13 -0
- package/dist/commands/initiative.js +318 -0
- package/dist/commands/workflow/index.d.ts +2 -0
- package/dist/commands/workflow/index.js +1 -0
- package/dist/commands/workflow/initiative-link.d.ts +24 -0
- package/dist/commands/workflow/initiative-link.js +47 -0
- package/dist/commands/workflow/instructions.js +10 -2
- package/dist/commands/workflow/new-change.d.ts +4 -0
- package/dist/commands/workflow/new-change.js +72 -23
- package/dist/commands/workflow/set-change.d.ts +13 -0
- package/dist/commands/workflow/set-change.js +87 -0
- package/dist/commands/workflow/shared.d.ts +2 -0
- package/dist/commands/workflow/status.js +3 -0
- package/dist/commands/workspace/context-status.d.ts +4 -0
- package/dist/commands/workspace/context-status.js +59 -0
- package/dist/commands/workspace/open-target-selection.d.ts +13 -0
- package/dist/commands/workspace/open-target-selection.js +146 -0
- package/dist/commands/workspace/open-view.d.ts +62 -0
- package/dist/commands/workspace/open-view.js +249 -0
- package/dist/commands/workspace/open.d.ts +16 -8
- package/dist/commands/workspace/open.js +40 -14
- package/dist/commands/workspace/opener-selection.d.ts +11 -0
- package/dist/commands/workspace/opener-selection.js +98 -0
- package/dist/commands/workspace/operations.d.ts +14 -8
- package/dist/commands/workspace/operations.js +228 -160
- package/dist/commands/workspace/prompt-theme.d.ts +29 -0
- package/dist/commands/workspace/prompt-theme.js +24 -0
- package/dist/commands/workspace/registration.d.ts +13 -0
- package/dist/commands/workspace/registration.js +84 -0
- package/dist/commands/workspace/selection.d.ts +3 -0
- package/dist/commands/workspace/selection.js +42 -40
- package/dist/commands/workspace/setup-prompts.d.ts +13 -0
- package/dist/commands/workspace/setup-prompts.js +121 -0
- package/dist/commands/workspace/types.d.ts +15 -0
- package/dist/commands/workspace.js +59 -340
- package/dist/core/artifact-graph/index.d.ts +2 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +10 -23
- package/dist/core/artifact-graph/instruction-loader.js +28 -89
- package/dist/core/artifact-graph/types.d.ts +0 -7
- package/dist/core/artifact-graph/types.js +0 -19
- package/dist/core/change-metadata/index.d.ts +2 -0
- package/dist/core/change-metadata/index.js +2 -0
- package/dist/core/change-metadata/schema.d.ts +18 -0
- package/dist/core/change-metadata/schema.js +28 -0
- package/dist/core/change-status-policy.d.ts +50 -0
- package/dist/core/change-status-policy.js +70 -0
- package/dist/core/collections/index.d.ts +3 -0
- package/dist/core/collections/index.js +3 -0
- package/dist/core/collections/initiatives/collection.d.ts +4 -0
- package/dist/core/collections/initiatives/collection.js +17 -0
- package/dist/core/collections/initiatives/index.d.ts +6 -0
- package/dist/core/collections/initiatives/index.js +6 -0
- package/dist/core/collections/initiatives/operations.d.ts +49 -0
- package/dist/core/collections/initiatives/operations.js +175 -0
- package/dist/core/collections/initiatives/resolution.d.ts +87 -0
- package/dist/core/collections/initiatives/resolution.js +374 -0
- package/dist/core/collections/initiatives/schema.d.ts +41 -0
- package/dist/core/collections/initiatives/schema.js +134 -0
- package/dist/core/collections/initiatives/templates.d.ts +12 -0
- package/dist/core/collections/initiatives/templates.js +90 -0
- package/dist/core/collections/runtime.d.ts +46 -0
- package/dist/core/collections/runtime.js +194 -0
- package/dist/core/completions/command-registry.d.ts +1 -5
- package/dist/core/completions/command-registry.js +475 -70
- package/dist/core/completions/shared-flags.d.ts +12 -0
- package/dist/core/completions/shared-flags.js +28 -0
- package/dist/core/config.js +2 -1
- package/dist/core/context-store/binding.d.ts +53 -0
- package/dist/core/context-store/binding.js +197 -0
- package/dist/core/context-store/errors.d.ts +20 -0
- package/dist/core/context-store/errors.js +22 -0
- package/dist/core/context-store/foundation.d.ts +55 -0
- package/dist/core/context-store/foundation.js +321 -0
- package/dist/core/context-store/index.d.ts +6 -0
- package/dist/core/context-store/index.js +6 -0
- package/dist/core/context-store/operations.d.ts +85 -0
- package/dist/core/context-store/operations.js +528 -0
- package/dist/core/context-store/registry.d.ts +45 -0
- package/dist/core/context-store/registry.js +229 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/core/planning-home.js +5 -21
- package/dist/core/validation/validator.d.ts +11 -0
- package/dist/core/validation/validator.js +19 -2
- package/dist/core/workspace/foundation.d.ts +28 -48
- package/dist/core/workspace/foundation.js +130 -214
- package/dist/core/workspace/index.d.ts +2 -0
- package/dist/core/workspace/index.js +2 -0
- package/dist/core/workspace/legacy-state.d.ts +28 -0
- package/dist/core/workspace/legacy-state.js +200 -0
- package/dist/core/workspace/open-surface.d.ts +29 -8
- package/dist/core/workspace/open-surface.js +122 -44
- package/dist/core/workspace/openers.js +11 -6
- package/dist/core/workspace/registry.d.ts +24 -0
- package/dist/core/workspace/registry.js +146 -0
- package/dist/core/workspace/skills.d.ts +4 -2
- package/dist/core/workspace/skills.js +2 -2
- package/dist/core/workspace/state-io.d.ts +10 -0
- package/dist/core/workspace/state-io.js +119 -0
- package/dist/utils/change-metadata.d.ts +5 -2
- package/dist/utils/change-metadata.js +6 -12
- package/dist/utils/change-utils.d.ts +2 -2
- package/package.json +1 -1
|
@@ -1,37 +1,18 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import
|
|
3
|
-
import * as path from 'node:path';
|
|
4
|
-
import { createWorkspaceSkillSkippedReport, generateWorkspaceAgentSkills, getDefaultWorkspaceOpenerChoiceValue, getWorkspaceSkillCapableTools, getWorkspaceSkillToolIds, getWorkspaceOpenerLabel, isWorkspaceAgentOpenerId, listWorkspaceOpenerChoices, parseWorkspacePreferredOpenerValue, parseWorkspaceSkillToolsValue, updateWorkspaceAgentSkills, listWorkspaceRegistryEntries, readOptionalWorkspaceLocalState, writeWorkspaceLocalState, } from '../core/workspace/index.js';
|
|
2
|
+
import { createWorkspaceSkillSkippedReport, generateWorkspaceAgentSkills, getWorkspaceSkillCapableTools, getWorkspaceSkillToolIds, getWorkspaceOpenerLabel, parseWorkspaceSkillToolsValue, updateWorkspaceAgentSkills, listKnownWorkspaceEntries, readWorkspaceViewState, syncWorkspaceOpenSurface, writeWorkspaceViewState, } from '../core/workspace/index.js';
|
|
5
3
|
import { isInteractive, resolveNoInteractive } from '../utils/interactive.js';
|
|
6
|
-
import { addWorkspaceLink, createManagedWorkspace,
|
|
4
|
+
import { addWorkspaceLink, createManagedWorkspace, loadWorkspaceForDoctor, loadWorkspaceForList, parseSetupLinks, readWorkspaceForMutation, updateWorkspaceLink, validateWorkspaceNameForSetup, } from './workspace/operations.js';
|
|
7
5
|
import { selectWorkspaceForCommand, selectWorkspaceRootForCommand, } from './workspace/selection.js';
|
|
8
|
-
import {
|
|
6
|
+
import { launchWorkspaceOpenCommand, } from './workspace/open.js';
|
|
7
|
+
import { buildWorkspaceOpenJsonPayload, prepareWorkspaceOpen, } from './workspace/open-view.js';
|
|
8
|
+
import { getPreferredWorkspaceSkillAgentId, parseSetupOpenerOption, promptPreferredOpener, } from './workspace/opener-selection.js';
|
|
9
|
+
import { workspacePromptTheme } from './workspace/prompt-theme.js';
|
|
10
|
+
import { registerWorkspaceCommandWith } from './workspace/registration.js';
|
|
11
|
+
import { promptSetupLinks } from './workspace/setup-prompts.js';
|
|
9
12
|
import { WorkspaceCliError, appendStatus, asErrorMessage, asStatus, } from './workspace/types.js';
|
|
10
13
|
function printJson(payload) {
|
|
11
14
|
console.log(JSON.stringify(payload, null, 2));
|
|
12
15
|
}
|
|
13
|
-
const workspacePromptTheme = {
|
|
14
|
-
prefix: '',
|
|
15
|
-
style: {
|
|
16
|
-
answer: (text) => chalk.cyan(text),
|
|
17
|
-
defaultAnswer: (text) => chalk.dim(text),
|
|
18
|
-
error: (text) => chalk.red(text),
|
|
19
|
-
help: (text) => chalk.dim(text),
|
|
20
|
-
highlight: (text) => chalk.cyan(text),
|
|
21
|
-
key: (text) => chalk.cyan(text),
|
|
22
|
-
message: (text) => chalk.bold(text),
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
const workspaceSelectTheme = {
|
|
26
|
-
...workspacePromptTheme,
|
|
27
|
-
icon: {
|
|
28
|
-
cursor: chalk.cyan('>'),
|
|
29
|
-
},
|
|
30
|
-
style: {
|
|
31
|
-
...workspacePromptTheme.style,
|
|
32
|
-
keysHelpTip: (keys) => chalk.dim(keys.map(([key, action]) => `${key}: ${action}`).join(' | ')),
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
16
|
function printWorkspaceSetupIntro() {
|
|
36
17
|
console.log(chalk.bold('Workspace setup'));
|
|
37
18
|
console.log('');
|
|
@@ -63,125 +44,6 @@ async function promptWorkspaceName(initialName) {
|
|
|
63
44
|
},
|
|
64
45
|
});
|
|
65
46
|
}
|
|
66
|
-
async function promptExistingPath(message, defaultPath) {
|
|
67
|
-
const { input } = await import('@inquirer/prompts');
|
|
68
|
-
const pathInput = await input({
|
|
69
|
-
message,
|
|
70
|
-
default: defaultPath,
|
|
71
|
-
prefill: defaultPath ? 'editable' : undefined,
|
|
72
|
-
required: true,
|
|
73
|
-
theme: workspacePromptTheme,
|
|
74
|
-
validate(value) {
|
|
75
|
-
const resolvedPath = path.isAbsolute(value)
|
|
76
|
-
? path.resolve(value)
|
|
77
|
-
: path.resolve(process.cwd(), value);
|
|
78
|
-
return nodeFs.existsSync(resolvedPath) && nodeFs.statSync(resolvedPath).isDirectory()
|
|
79
|
-
? true
|
|
80
|
-
: 'Enter an existing repo or folder path.';
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
return resolveExistingDirectory(pathInput);
|
|
84
|
-
}
|
|
85
|
-
async function promptLinkName(existingLinks) {
|
|
86
|
-
const { input } = await import('@inquirer/prompts');
|
|
87
|
-
return input({
|
|
88
|
-
message: 'Link name:',
|
|
89
|
-
required: true,
|
|
90
|
-
theme: workspacePromptTheme,
|
|
91
|
-
validate(value) {
|
|
92
|
-
try {
|
|
93
|
-
validateLinkNameForCommand(value);
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
return asErrorMessage(error);
|
|
97
|
-
}
|
|
98
|
-
if (existingLinks[value]) {
|
|
99
|
-
return `Link name '${value}' is already linked to ${existingLinks[value]}.`;
|
|
100
|
-
}
|
|
101
|
-
return true;
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
async function promptSetupLinks() {
|
|
106
|
-
const { select } = await import('@inquirer/prompts');
|
|
107
|
-
const links = {};
|
|
108
|
-
console.log('');
|
|
109
|
-
console.log(chalk.bold('[2/5] Link repos or folders'));
|
|
110
|
-
console.log(chalk.dim('Start with the current directory, or enter another repo path.'));
|
|
111
|
-
console.log('');
|
|
112
|
-
while (true) {
|
|
113
|
-
const linkCount = Object.keys(links).length;
|
|
114
|
-
const resolvedPath = await promptExistingPath(linkCount === 0 ? 'Repo or folder path:' : 'Another repo or folder path:', linkCount === 0 ? '.' : undefined);
|
|
115
|
-
let linkName = inferLinkName(resolvedPath);
|
|
116
|
-
try {
|
|
117
|
-
validateLinkNameForCommand(linkName);
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
linkName = await promptLinkName(links);
|
|
121
|
-
}
|
|
122
|
-
if (links[linkName]) {
|
|
123
|
-
console.log(`Link name '${linkName}' is already linked to ${links[linkName]}.`);
|
|
124
|
-
linkName = await promptLinkName(links);
|
|
125
|
-
}
|
|
126
|
-
links[linkName] = resolvedPath;
|
|
127
|
-
console.log(chalk.green(`Added link '${linkName}'`));
|
|
128
|
-
console.log(chalk.dim(` ${resolvedPath}`));
|
|
129
|
-
const nextAction = await select({
|
|
130
|
-
message: 'Continue',
|
|
131
|
-
default: 'finish',
|
|
132
|
-
choices: [
|
|
133
|
-
{
|
|
134
|
-
name: 'Create workspace files',
|
|
135
|
-
short: 'Create workspace files',
|
|
136
|
-
value: 'finish',
|
|
137
|
-
description: 'Run a workspace check after setup',
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
name: 'Add another repo or folder',
|
|
141
|
-
short: 'Add another',
|
|
142
|
-
value: 'add',
|
|
143
|
-
description: 'Include another local directory in this workspace',
|
|
144
|
-
},
|
|
145
|
-
],
|
|
146
|
-
theme: workspaceSelectTheme,
|
|
147
|
-
});
|
|
148
|
-
if (nextAction === 'finish') {
|
|
149
|
-
return links;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function formatOpenerChoiceName(choice) {
|
|
154
|
-
return choice.unavailableNote ? `${choice.label} (${choice.unavailableNote})` : choice.label;
|
|
155
|
-
}
|
|
156
|
-
async function promptPreferredOpener(message, openerChoices = listWorkspaceOpenerChoices()) {
|
|
157
|
-
const { select } = await import('@inquirer/prompts');
|
|
158
|
-
const selectedValue = await select({
|
|
159
|
-
message,
|
|
160
|
-
default: getDefaultWorkspaceOpenerChoiceValue(openerChoices),
|
|
161
|
-
choices: openerChoices.map((choice) => ({
|
|
162
|
-
name: formatOpenerChoiceName(choice),
|
|
163
|
-
short: choice.label,
|
|
164
|
-
value: choice.value,
|
|
165
|
-
description: choice.unavailableNote ?? `Use ${choice.label}`,
|
|
166
|
-
})),
|
|
167
|
-
theme: workspaceSelectTheme,
|
|
168
|
-
});
|
|
169
|
-
return parseWorkspacePreferredOpenerValue(selectedValue);
|
|
170
|
-
}
|
|
171
|
-
function parseSetupOpenerOption(opener) {
|
|
172
|
-
if (!opener) {
|
|
173
|
-
return undefined;
|
|
174
|
-
}
|
|
175
|
-
try {
|
|
176
|
-
return parseWorkspacePreferredOpenerValue(opener);
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
|
-
throw new WorkspaceCliError(asErrorMessage(error), 'unsupported_workspace_opener', {
|
|
180
|
-
target: 'workspace.opener',
|
|
181
|
-
fix: 'Use --opener codex, --opener claude, --opener github-copilot, or --opener editor.',
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
47
|
function parseSetupToolsOption(tools) {
|
|
186
48
|
try {
|
|
187
49
|
return parseWorkspaceSkillToolsValue(tools);
|
|
@@ -204,12 +66,6 @@ function parseUpdateToolsOption(tools) {
|
|
|
204
66
|
});
|
|
205
67
|
}
|
|
206
68
|
}
|
|
207
|
-
function getPreferredWorkspaceSkillAgentId(preferredOpener) {
|
|
208
|
-
if (!preferredOpener || preferredOpener.kind !== 'agent') {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
return getWorkspaceSkillToolIds().includes(preferredOpener.id) ? preferredOpener.id : null;
|
|
212
|
-
}
|
|
213
69
|
async function promptWorkspaceSkillAgents(preferredOpener) {
|
|
214
70
|
const { searchableMultiSelect } = await import('../prompts/searchable-multi-select.js');
|
|
215
71
|
const preferredAgentId = getPreferredWorkspaceSkillAgentId(preferredOpener);
|
|
@@ -238,18 +94,6 @@ async function promptWorkspaceSkillAgents(preferredOpener) {
|
|
|
238
94
|
choices: sortedChoices,
|
|
239
95
|
});
|
|
240
96
|
}
|
|
241
|
-
function parseAgentOverride(agent) {
|
|
242
|
-
if (!isWorkspaceAgentOpenerId(agent)) {
|
|
243
|
-
throw new WorkspaceCliError(`Unsupported workspace agent '${agent}'. Supported agents: codex, claude, github-copilot.`, 'unsupported_workspace_agent', {
|
|
244
|
-
target: 'workspace.opener',
|
|
245
|
-
fix: 'Use --agent codex, --agent claude, or --agent github-copilot.',
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
return {
|
|
249
|
-
kind: 'agent',
|
|
250
|
-
id: agent,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
97
|
function printStatusLines(statuses) {
|
|
254
98
|
for (const status of statuses) {
|
|
255
99
|
const label = status.severity === 'warning' ? 'Warning' : 'Issue';
|
|
@@ -281,7 +125,14 @@ function collectWorkspaceIssues(workspace) {
|
|
|
281
125
|
function printDoctorHuman(result) {
|
|
282
126
|
console.log(`Workspace: ${result.workspace.name}`);
|
|
283
127
|
console.log(`Location: ${result.workspace.root}`);
|
|
284
|
-
|
|
128
|
+
if (result.workspace.context) {
|
|
129
|
+
const selector = result.workspace.context.store_selector;
|
|
130
|
+
const suffix = selector.kind === 'path' ? ` via ${selector.path}` : '';
|
|
131
|
+
console.log(`Context: ${result.workspace.context.store}/${result.workspace.context.initiative}${suffix}`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
console.log('Context: (none)');
|
|
135
|
+
}
|
|
285
136
|
console.log('');
|
|
286
137
|
printStatusLines(result.status);
|
|
287
138
|
if (result.status.length > 0) {
|
|
@@ -290,6 +141,15 @@ function printDoctorHuman(result) {
|
|
|
290
141
|
console.log('Linked repos or folders:');
|
|
291
142
|
printLinksHuman(result.workspace.links);
|
|
292
143
|
const issues = collectWorkspaceIssues(result.workspace);
|
|
144
|
+
console.log('');
|
|
145
|
+
console.log('Advisory edit boundaries:');
|
|
146
|
+
if (result.workspace.context) {
|
|
147
|
+
console.log(' Initiative/context-store files are shared coordination context.');
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.log(' No initiative coordination context is attached.');
|
|
151
|
+
}
|
|
152
|
+
console.log(' Linked repos and folders are local implementation context when selected.');
|
|
293
153
|
if (issues.length === 0) {
|
|
294
154
|
console.log('');
|
|
295
155
|
console.log('No workspace issues found.');
|
|
@@ -409,12 +269,9 @@ function setWorkspaceSkillFailureExitCode(report) {
|
|
|
409
269
|
}
|
|
410
270
|
}
|
|
411
271
|
async function writeWorkspaceSkillState(workspaceRoot, selectedAgentIds, report) {
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
};
|
|
416
|
-
await writeWorkspaceLocalState(workspaceRoot, {
|
|
417
|
-
...localState,
|
|
272
|
+
const viewState = await readWorkspaceViewState(workspaceRoot);
|
|
273
|
+
await writeWorkspaceViewState(workspaceRoot, {
|
|
274
|
+
...viewState,
|
|
418
275
|
workspace_skills: {
|
|
419
276
|
selected_agents: selectedAgentIds,
|
|
420
277
|
last_applied_profile: report.profile,
|
|
@@ -424,66 +281,6 @@ async function writeWorkspaceSkillState(workspaceRoot, selectedAgentIds, report)
|
|
|
424
281
|
},
|
|
425
282
|
});
|
|
426
283
|
}
|
|
427
|
-
async function resolveWorkspaceOpenOpener(localState, options) {
|
|
428
|
-
if (options.agent && options.editor) {
|
|
429
|
-
throw new WorkspaceCliError('workspace open accepts either --agent <tool> or --editor, not both.', 'workspace_opener_conflict', {
|
|
430
|
-
target: 'workspace.opener',
|
|
431
|
-
fix: 'Choose one opener override.',
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
if (options.agent) {
|
|
435
|
-
return parseAgentOverride(options.agent);
|
|
436
|
-
}
|
|
437
|
-
if (options.editor) {
|
|
438
|
-
return parseWorkspacePreferredOpenerValue('editor');
|
|
439
|
-
}
|
|
440
|
-
if (localState.preferred_opener) {
|
|
441
|
-
return localState.preferred_opener;
|
|
442
|
-
}
|
|
443
|
-
if (!resolveNoInteractive(options) && isInteractive(options)) {
|
|
444
|
-
const openerChoices = listWorkspaceOpenerChoices().filter((choice) => choice.available);
|
|
445
|
-
if (openerChoices.length === 0) {
|
|
446
|
-
throw new WorkspaceCliError('No supported workspace opener is available on PATH.', 'workspace_no_available_openers', {
|
|
447
|
-
target: 'workspace.opener',
|
|
448
|
-
fix: "Install VS Code ('code'), Codex ('codex'), or Claude ('claude'), then retry.",
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
return promptPreferredOpener('Open with:', openerChoices);
|
|
452
|
-
}
|
|
453
|
-
throw new WorkspaceCliError('This workspace does not have a preferred opener yet.', 'workspace_opener_unset', {
|
|
454
|
-
target: 'workspace.opener',
|
|
455
|
-
fix: 'Pass --agent <tool> or --editor, or run workspace setup interactively to choose a default opener.',
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
function assertWorkspaceOpenSupportedOptions(options) {
|
|
459
|
-
if (options.prepareOnly) {
|
|
460
|
-
throw new WorkspaceCliError('workspace open supports launching through a selected opener; preview output is reserved for a future context/query surface.', 'workspace_open_prepare_only_unsupported', {
|
|
461
|
-
target: 'workspace.open',
|
|
462
|
-
fix: 'Run openspec workspace open with --agent <tool> or --editor.',
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
if (options.json) {
|
|
466
|
-
throw new WorkspaceCliError('workspace open supports launching through a selected opener; machine-readable context is reserved for a future context/query surface.', 'workspace_open_json_unsupported', {
|
|
467
|
-
target: 'workspace.open',
|
|
468
|
-
fix: 'Use openspec workspace doctor --json for current workspace status.',
|
|
469
|
-
});
|
|
470
|
-
}
|
|
471
|
-
if (options.change) {
|
|
472
|
-
throw new WorkspaceCliError('workspace open currently supports root workspace open only; change-scoped open belongs to future workspace change planning.', 'workspace_open_change_unsupported', {
|
|
473
|
-
target: 'workspace.change',
|
|
474
|
-
fix: 'Open the root workspace, then start implementation from an explicit change workflow.',
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
function resolveOpenWorkspaceName(positionalName, options) {
|
|
479
|
-
if (positionalName && options.workspace && positionalName !== options.workspace) {
|
|
480
|
-
throw new WorkspaceCliError(`Conflicting workspace selectors: positional '${positionalName}' and --workspace '${options.workspace}'.`, 'workspace_selection_conflict', {
|
|
481
|
-
target: 'workspace.name',
|
|
482
|
-
fix: 'Use either the positional workspace name or --workspace with the same value.',
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
return positionalName ?? options.workspace;
|
|
486
|
-
}
|
|
487
284
|
function resolveUpdateWorkspaceName(positionalName, options) {
|
|
488
285
|
if (positionalName && options.workspace && positionalName !== options.workspace) {
|
|
489
286
|
throw new WorkspaceCliError(`Conflicting workspace selectors: positional '${positionalName}' and --workspace '${options.workspace}'.`, 'workspace_selection_conflict', {
|
|
@@ -493,16 +290,20 @@ function resolveUpdateWorkspaceName(positionalName, options) {
|
|
|
493
290
|
}
|
|
494
291
|
return positionalName ?? options.workspace;
|
|
495
292
|
}
|
|
496
|
-
function printWorkspaceOpenHuman(
|
|
497
|
-
console.log(`Opening workspace: ${
|
|
498
|
-
console.log(`Location: ${
|
|
499
|
-
|
|
500
|
-
|
|
293
|
+
function printWorkspaceOpenHuman(prepared) {
|
|
294
|
+
console.log(`Opening workspace: ${prepared.selected.name}`);
|
|
295
|
+
console.log(`Location: ${prepared.selected.root}`);
|
|
296
|
+
if (prepared.initiative) {
|
|
297
|
+
console.log(`Initiative: ${prepared.initiative.store}/${prepared.initiative.id}`);
|
|
298
|
+
console.log(`Initiative path: ${prepared.initiative.root}`);
|
|
299
|
+
}
|
|
300
|
+
console.log(`Opener: ${getWorkspaceOpenerLabel(prepared.opener)}`);
|
|
301
|
+
if (prepared.skipped.length === 0) {
|
|
501
302
|
return;
|
|
502
303
|
}
|
|
503
304
|
console.log('');
|
|
504
305
|
console.log('Skipped linked repos or folders:');
|
|
505
|
-
for (const link of skipped) {
|
|
306
|
+
for (const link of prepared.skipped) {
|
|
506
307
|
const location = link.path ?? '(no local path recorded)';
|
|
507
308
|
console.log(` ${link.name} -> ${location}`);
|
|
508
309
|
}
|
|
@@ -584,8 +385,6 @@ class WorkspaceCommand {
|
|
|
584
385
|
console.log('');
|
|
585
386
|
printWorkspaceListHuman([doctorResult.workspace]);
|
|
586
387
|
console.log('');
|
|
587
|
-
console.log(`Planning path: ${doctorResult.workspace.planning_path}`);
|
|
588
|
-
console.log('');
|
|
589
388
|
console.log('Workspace check:');
|
|
590
389
|
printWorkspaceCheckSummaryHuman(doctorResult);
|
|
591
390
|
console.log('');
|
|
@@ -603,8 +402,7 @@ class WorkspaceCommand {
|
|
|
603
402
|
}
|
|
604
403
|
async list(options = {}) {
|
|
605
404
|
try {
|
|
606
|
-
const
|
|
607
|
-
const entries = listWorkspaceRegistryEntries(registry);
|
|
405
|
+
const entries = await listKnownWorkspaceEntries();
|
|
608
406
|
const workspaces = await Promise.all(entries.map((entry) => loadWorkspaceForList(entry)));
|
|
609
407
|
const payload = { workspaces, status: [] };
|
|
610
408
|
if (options.json) {
|
|
@@ -696,19 +494,19 @@ class WorkspaceCommand {
|
|
|
696
494
|
}
|
|
697
495
|
}
|
|
698
496
|
async updateSelected(selected, options) {
|
|
699
|
-
const
|
|
497
|
+
const viewState = await readWorkspaceForMutation(selected);
|
|
498
|
+
await syncWorkspaceOpenSurface(selected.root, viewState);
|
|
700
499
|
const hasExplicitToolSelection = options.tools !== undefined;
|
|
701
500
|
const selectedAgentIds = hasExplicitToolSelection
|
|
702
501
|
? parseUpdateToolsOption(options.tools ?? '')
|
|
703
|
-
:
|
|
502
|
+
: viewState.workspace_skills?.selected_agents ?? [];
|
|
704
503
|
const previousSkillState = hasExplicitToolSelection
|
|
705
|
-
?
|
|
706
|
-
:
|
|
504
|
+
? viewState.workspace_skills ?? { selected_agents: [] }
|
|
505
|
+
: viewState.workspace_skills;
|
|
707
506
|
const skillReport = await updateWorkspaceAgentSkills(selected.root, selectedAgentIds, previousSkillState);
|
|
708
|
-
const shouldStoreSelection = hasExplicitToolSelection || Boolean(
|
|
507
|
+
const shouldStoreSelection = hasExplicitToolSelection || Boolean(viewState.workspace_skills);
|
|
709
508
|
if (shouldStoreSelection && !hasWorkspaceSkillFailures(skillReport)) {
|
|
710
509
|
await writeWorkspaceSkillState(selected.root, selectedAgentIds, skillReport);
|
|
711
|
-
await recordSelectedWorkspaceAfterMutation(selected);
|
|
712
510
|
}
|
|
713
511
|
const doctorResult = await loadWorkspaceForDoctor(selected);
|
|
714
512
|
if (options.json) {
|
|
@@ -737,22 +535,20 @@ class WorkspaceCommand {
|
|
|
737
535
|
}
|
|
738
536
|
async open(positionalName, options = {}) {
|
|
739
537
|
try {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
if (
|
|
752
|
-
|
|
538
|
+
const prepared = await prepareWorkspaceOpen(positionalName, options);
|
|
539
|
+
if (!options.json) {
|
|
540
|
+
printStatusLines(prepared.selected.status);
|
|
541
|
+
if (prepared.selected.status.length > 0) {
|
|
542
|
+
console.log('');
|
|
543
|
+
}
|
|
544
|
+
printWorkspaceOpenHuman(prepared);
|
|
545
|
+
}
|
|
546
|
+
await launchWorkspaceOpenCommand(prepared.command, {
|
|
547
|
+
stdio: options.json ? 'ignore' : 'inherit',
|
|
548
|
+
});
|
|
549
|
+
if (options.json) {
|
|
550
|
+
printJson(buildWorkspaceOpenJsonPayload(prepared));
|
|
753
551
|
}
|
|
754
|
-
printWorkspaceOpenHuman(selected.name, selected.root, opener, skipped);
|
|
755
|
-
await launchWorkspaceOpenCommand(command);
|
|
756
552
|
}
|
|
757
553
|
catch (error) {
|
|
758
554
|
this.handleFailure(options.json, { workspace: null, status: [] }, error);
|
|
@@ -785,84 +581,7 @@ export async function runWorkspaceUpdateForRoot(workspaceRoot, options = {}) {
|
|
|
785
581
|
const workspaceCommand = new WorkspaceCommand();
|
|
786
582
|
await workspaceCommand.updateRoot(workspaceRoot, options);
|
|
787
583
|
}
|
|
788
|
-
function collectOption(value, previous) {
|
|
789
|
-
return [...previous, value];
|
|
790
|
-
}
|
|
791
|
-
function addWorkspaceSelectionOptions(command) {
|
|
792
|
-
return command
|
|
793
|
-
.option('--workspace <name>', 'Workspace name from the local workspace registry')
|
|
794
|
-
.option('--json', 'Output as JSON')
|
|
795
|
-
.option('--no-interactive', 'Disable prompts');
|
|
796
|
-
}
|
|
797
584
|
export function registerWorkspaceCommand(program) {
|
|
798
|
-
|
|
799
|
-
const workspace = program
|
|
800
|
-
.command('workspace')
|
|
801
|
-
.description('Set up and inspect coordination workspaces');
|
|
802
|
-
workspace
|
|
803
|
-
.command('setup')
|
|
804
|
-
.description('Set up a workspace and link existing repos or folders')
|
|
805
|
-
.option('--name <name>', 'Workspace name')
|
|
806
|
-
.option('--link <link>', 'Repo or folder link. Use <path> or <name>=<path>.', collectOption, [])
|
|
807
|
-
.option('--opener <id>', 'Preferred opener: codex, claude, github-copilot, or editor')
|
|
808
|
-
.option('--tools <tools>', `Install OpenSpec skills for agents. Use "all", "none", or a comma-separated list of: ${getWorkspaceSkillToolIds().join(', ')}`)
|
|
809
|
-
.option('--json', 'Output as JSON')
|
|
810
|
-
.option('--no-interactive', 'Disable prompts')
|
|
811
|
-
.action(async (options) => {
|
|
812
|
-
await workspaceCommand.setup(options);
|
|
813
|
-
});
|
|
814
|
-
workspace
|
|
815
|
-
.command('list')
|
|
816
|
-
.description('List known OpenSpec workspaces')
|
|
817
|
-
.option('--json', 'Output as JSON')
|
|
818
|
-
.action(async (options) => {
|
|
819
|
-
await workspaceCommand.list(options);
|
|
820
|
-
});
|
|
821
|
-
workspace
|
|
822
|
-
.command('ls')
|
|
823
|
-
.description('List known OpenSpec workspaces')
|
|
824
|
-
.option('--json', 'Output as JSON')
|
|
825
|
-
.action(async (options) => {
|
|
826
|
-
await workspaceCommand.list(options);
|
|
827
|
-
});
|
|
828
|
-
addWorkspaceSelectionOptions(workspace
|
|
829
|
-
.command('link [nameOrPath] [path]')
|
|
830
|
-
.description('Link an existing repo or folder to a workspace')).action(async (nameOrPath, linkPath, options) => {
|
|
831
|
-
await workspaceCommand.link(nameOrPath, linkPath, options);
|
|
832
|
-
});
|
|
833
|
-
addWorkspaceSelectionOptions(workspace
|
|
834
|
-
.command('relink <name> <path>')
|
|
835
|
-
.description('Update the local path for an existing workspace link')).action(async (linkName, linkPath, options) => {
|
|
836
|
-
await workspaceCommand.relink(linkName, linkPath, options);
|
|
837
|
-
});
|
|
838
|
-
addWorkspaceSelectionOptions(workspace
|
|
839
|
-
.command('doctor')
|
|
840
|
-
.description('Check what a workspace can resolve on this machine')).action(async (options) => {
|
|
841
|
-
await workspaceCommand.doctor(options);
|
|
842
|
-
});
|
|
843
|
-
workspace
|
|
844
|
-
.command('update [name]')
|
|
845
|
-
.description('Refresh workspace-local OpenSpec agent skills from the active global profile')
|
|
846
|
-
.option('--workspace <name>', 'Workspace name from the local workspace registry')
|
|
847
|
-
.option('--tools <tools>', `Select agents for workspace skills. Use "all", "none", or a comma-separated list of: ${getWorkspaceSkillToolIds().join(', ')}. Global profile selects workflows; --tools selects agents.`)
|
|
848
|
-
.option('--json', 'Output as JSON')
|
|
849
|
-
.option('--no-interactive', 'Disable prompts')
|
|
850
|
-
.action(async (name, options) => {
|
|
851
|
-
await workspaceCommand.update(name, options);
|
|
852
|
-
});
|
|
853
|
-
workspace
|
|
854
|
-
.command('open [name]')
|
|
855
|
-
.description('Open a workspace in an agent or VS Code editor')
|
|
856
|
-
.option('--workspace <name>', 'Workspace name from the local workspace registry')
|
|
857
|
-
.option('--agent <tool>', 'Use an agent for this session: codex, claude, or github-copilot')
|
|
858
|
-
.option('--editor', 'Open the workspace in VS Code editor mode')
|
|
859
|
-
.option('--prepare-only', 'Unsupported: preview surfaces belong to a future context/query command')
|
|
860
|
-
.option('--json', 'Unsupported: machine-readable context belongs to a future context/query command')
|
|
861
|
-
.option('--change <id>', 'Unsupported: change-scoped open belongs to future workspace change planning')
|
|
862
|
-
.option('--no-interactive', 'Disable prompts')
|
|
863
|
-
.action(async (name, options) => {
|
|
864
|
-
await workspaceCommand.open(name, options);
|
|
865
|
-
});
|
|
866
|
-
// Intentionally no public `workspace create` command in this slice.
|
|
585
|
+
registerWorkspaceCommandWith(program, new WorkspaceCommand());
|
|
867
586
|
}
|
|
868
587
|
//# sourceMappingURL=workspace.js.map
|
|
@@ -4,5 +4,6 @@ export { ArtifactGraph } from './graph.js';
|
|
|
4
4
|
export { detectCompleted } from './state.js';
|
|
5
5
|
export { artifactOutputExists, isGlobPattern, resolveArtifactOutputs } from './outputs.js';
|
|
6
6
|
export { resolveSchema, listSchemas, listSchemasWithInfo, getSchemaDir, getPackageSchemasDir, getUserSchemasDir, SchemaLoadError, type SchemaInfo, } from './resolver.js';
|
|
7
|
-
export { loadTemplate, loadChangeContext, generateInstructions, formatChangeStatus, TemplateLoadError, type ChangeContext, type LoadChangeContextOptions, type ArtifactInstructions, type DependencyInfo, type ArtifactStatus, type ChangeStatus, type ArtifactPathSummary,
|
|
7
|
+
export { loadTemplate, loadChangeContext, generateInstructions, formatChangeStatus, TemplateLoadError, type ChangeContext, type LoadChangeContextOptions, type ArtifactInstructions, type DependencyInfo, type ArtifactStatus, type ChangeStatus, type ArtifactPathSummary, } from './instruction-loader.js';
|
|
8
|
+
export type { PlanningHomeSummary, AffectedAreasSummary, ActionContext, } from '../change-status-policy.js';
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ArtifactGraph } from './graph.js';
|
|
2
|
+
import { type ActionContext, type AffectedAreasSummary, type PlanningHomeSummary } from '../change-status-policy.js';
|
|
2
3
|
import type { PlanningHome } from '../planning-home.js';
|
|
4
|
+
import type { ChangeMetadata, InitiativeLink } from '../change-metadata/index.js';
|
|
3
5
|
import type { CompletedSet } from './types.js';
|
|
4
6
|
/**
|
|
5
7
|
* Error thrown when loading a template fails.
|
|
@@ -26,6 +28,10 @@ export interface ChangeContext {
|
|
|
26
28
|
projectRoot: string;
|
|
27
29
|
/** Resolved planning home for this change */
|
|
28
30
|
planningHome?: PlanningHome;
|
|
31
|
+
/** Parsed change metadata, when present */
|
|
32
|
+
metadata?: ChangeMetadata;
|
|
33
|
+
/** Stored initiative link, when this change is linked to shared context */
|
|
34
|
+
initiative?: InitiativeLink;
|
|
29
35
|
}
|
|
30
36
|
export interface LoadChangeContextOptions {
|
|
31
37
|
changeDir?: string;
|
|
@@ -45,6 +51,8 @@ export interface ArtifactInstructions {
|
|
|
45
51
|
changeDir: string;
|
|
46
52
|
/** Resolved planning home for this change */
|
|
47
53
|
planningHome?: PlanningHomeSummary;
|
|
54
|
+
/** Stored initiative link, when this change is linked to shared context */
|
|
55
|
+
initiative?: InitiativeLink;
|
|
48
56
|
/** Output path pattern (e.g., "proposal.md") */
|
|
49
57
|
outputPath: string;
|
|
50
58
|
/** Absolute output path or glob pattern resolved under the change directory */
|
|
@@ -102,6 +110,8 @@ export interface ChangeStatus {
|
|
|
102
110
|
schemaName: string;
|
|
103
111
|
/** Resolved planning home for this change */
|
|
104
112
|
planningHome?: PlanningHomeSummary;
|
|
113
|
+
/** Stored initiative link, when this change is linked to shared context */
|
|
114
|
+
initiative?: InitiativeLink;
|
|
105
115
|
/** Full path to the change root */
|
|
106
116
|
changeRoot: string;
|
|
107
117
|
/** Absolute artifact path details keyed by artifact ID */
|
|
@@ -124,29 +134,6 @@ export interface ArtifactPathSummary {
|
|
|
124
134
|
resolvedOutputPath: string;
|
|
125
135
|
existingOutputPaths: string[];
|
|
126
136
|
}
|
|
127
|
-
export interface PlanningHomeSummary {
|
|
128
|
-
kind: 'repo' | 'workspace';
|
|
129
|
-
root: string;
|
|
130
|
-
changesDir: string;
|
|
131
|
-
defaultSchema: string;
|
|
132
|
-
workspaceName?: string;
|
|
133
|
-
}
|
|
134
|
-
export interface AffectedAreasSummary {
|
|
135
|
-
known: string[];
|
|
136
|
-
unresolved: boolean;
|
|
137
|
-
invalid: string[];
|
|
138
|
-
}
|
|
139
|
-
export interface ActionContext {
|
|
140
|
-
mode: 'repo-local' | 'workspace-planning';
|
|
141
|
-
sourceOfTruth: 'repo' | 'workspace';
|
|
142
|
-
planningArtifacts: string[];
|
|
143
|
-
linkedContext: Array<{
|
|
144
|
-
name: string;
|
|
145
|
-
}>;
|
|
146
|
-
allowedEditRoots: string[];
|
|
147
|
-
requiresAffectedAreaSelection: boolean;
|
|
148
|
-
constraints: string[];
|
|
149
|
-
}
|
|
150
137
|
/**
|
|
151
138
|
* Loads a template from a schema's templates directory.
|
|
152
139
|
*
|