@fission-ai/openspec 1.4.1 → 1.5.0
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 +24 -3
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +95 -95
- package/dist/commands/change.d.ts +5 -1
- package/dist/commands/change.js +17 -15
- package/dist/commands/config.js +6 -65
- package/dist/commands/context.d.ts +3 -0
- package/dist/commands/context.js +155 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.js +163 -0
- package/dist/commands/shared-gather.d.ts +14 -0
- package/dist/commands/shared-gather.js +31 -0
- package/dist/commands/shared-output.d.ts +18 -0
- package/dist/commands/shared-output.js +61 -0
- package/dist/commands/show.d.ts +11 -6
- package/dist/commands/show.js +70 -25
- package/dist/commands/spec.d.ts +5 -1
- package/dist/commands/spec.js +15 -4
- package/dist/commands/store.d.ts +3 -0
- package/dist/commands/store.js +547 -0
- package/dist/commands/validate.d.ts +2 -0
- package/dist/commands/validate.js +75 -39
- package/dist/commands/workflow/index.d.ts +0 -2
- package/dist/commands/workflow/index.js +0 -1
- package/dist/commands/workflow/instructions.d.ts +11 -1
- package/dist/commands/workflow/instructions.js +71 -22
- package/dist/commands/workflow/new-change.d.ts +6 -3
- package/dist/commands/workflow/new-change.js +42 -77
- package/dist/commands/workflow/shared.d.ts +15 -3
- package/dist/commands/workflow/shared.js +20 -3
- package/dist/commands/workflow/status.d.ts +2 -0
- package/dist/commands/workflow/status.js +19 -17
- package/dist/commands/workset-input.d.ts +19 -0
- package/dist/commands/workset-input.js +112 -0
- package/dist/commands/workset-prompts.d.ts +12 -0
- package/dist/commands/workset-prompts.js +143 -0
- package/dist/commands/workset.d.ts +25 -0
- package/dist/commands/workset.js +446 -0
- package/dist/core/archive.d.ts +17 -6
- package/dist/core/archive.js +209 -56
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +18 -13
- package/dist/core/artifact-graph/instruction-loader.js +8 -31
- package/dist/core/change-metadata/schema.d.ts +1 -0
- package/dist/core/change-metadata/schema.js +4 -2
- package/dist/core/change-status-policy.d.ts +5 -18
- package/dist/core/change-status-policy.js +2 -37
- package/dist/core/command-generation/adapters/bob.js +1 -14
- package/dist/core/command-generation/adapters/claude.js +1 -14
- package/dist/core/command-generation/adapters/cursor.js +1 -14
- package/dist/core/command-generation/adapters/pi.js +1 -14
- package/dist/core/command-generation/adapters/windsurf.js +1 -14
- package/dist/core/command-generation/yaml.d.ts +22 -0
- package/dist/core/command-generation/yaml.js +38 -0
- package/dist/core/completions/command-registry.js +87 -289
- package/dist/core/completions/generators/zsh-generator.js +3 -1
- package/dist/core/completions/installers/bash-installer.js +3 -0
- package/dist/core/completions/installers/fish-installer.js +8 -0
- package/dist/core/completions/installers/powershell-installer.js +30 -2
- package/dist/core/completions/installers/zsh-installer.js +3 -0
- package/dist/core/completions/shared-flags.d.ts +1 -0
- package/dist/core/completions/shared-flags.js +5 -0
- package/dist/core/config-schema.d.ts +2 -1
- package/dist/core/config-schema.js +26 -0
- package/dist/core/file-state.d.ts +36 -0
- package/dist/core/file-state.js +112 -0
- package/dist/core/global-config.d.ts +2 -0
- package/dist/core/id.d.ts +17 -0
- package/dist/core/id.js +30 -0
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +3 -3
- package/dist/core/init.js +24 -4
- package/dist/core/list.d.ts +2 -0
- package/dist/core/list.js +19 -5
- package/dist/core/openers.d.ts +77 -0
- package/dist/core/openers.js +251 -0
- package/dist/core/openspec-root.d.ts +45 -0
- package/dist/core/openspec-root.js +192 -0
- package/dist/core/planning-home.d.ts +1 -6
- package/dist/core/planning-home.js +2 -43
- package/dist/core/project-config.d.ts +44 -1
- package/dist/core/project-config.js +160 -10
- package/dist/core/references.d.ts +63 -0
- package/dist/core/references.js +310 -0
- package/dist/core/relationship-health.d.ts +65 -0
- package/dist/core/relationship-health.js +64 -0
- package/dist/core/root-selection.d.ts +122 -0
- package/dist/core/root-selection.js +337 -0
- package/dist/core/specs-apply.d.ts +6 -1
- package/dist/core/specs-apply.js +6 -4
- package/dist/core/store/errors.d.ts +20 -0
- package/dist/core/{context-store → store}/errors.js +3 -3
- package/dist/core/store/foundation.d.ts +56 -0
- package/dist/core/store/foundation.js +251 -0
- package/dist/core/store/git.d.ts +23 -0
- package/dist/core/store/git.js +137 -0
- package/dist/core/{context-store → store}/index.d.ts +0 -1
- package/dist/core/{context-store → store}/index.js +0 -1
- package/dist/core/store/operations.d.ts +114 -0
- package/dist/core/store/operations.js +783 -0
- package/dist/core/store/registry.d.ts +58 -0
- package/dist/core/store/registry.js +275 -0
- package/dist/core/templates/workflows/apply-change.js +5 -4
- package/dist/core/templates/workflows/archive-change.js +5 -4
- package/dist/core/templates/workflows/bulk-archive-change.js +5 -4
- package/dist/core/templates/workflows/continue-change.js +7 -2
- package/dist/core/templates/workflows/explore.js +5 -0
- package/dist/core/templates/workflows/ff-change.js +5 -0
- package/dist/core/templates/workflows/new-change.js +5 -0
- package/dist/core/templates/workflows/onboard.js +4 -1
- package/dist/core/templates/workflows/propose.js +5 -0
- package/dist/core/templates/workflows/store-selection.d.ts +8 -0
- package/dist/core/templates/workflows/store-selection.js +8 -0
- package/dist/core/templates/workflows/sync-specs.js +5 -4
- package/dist/core/templates/workflows/verify-change.js +5 -4
- package/dist/core/working-set.d.ts +47 -0
- package/dist/core/working-set.js +43 -0
- package/dist/core/worksets.d.ts +75 -0
- package/dist/core/worksets.js +245 -0
- package/dist/core/zod-issues.d.ts +4 -0
- package/dist/core/zod-issues.js +10 -0
- package/dist/utils/change-metadata.d.ts +1 -0
- package/dist/utils/change-metadata.js +1 -1
- package/dist/utils/change-utils.js +15 -0
- package/dist/utils/file-system.js +36 -17
- package/package.json +6 -6
- package/dist/commands/context-store.d.ts +0 -3
- package/dist/commands/context-store.js +0 -475
- package/dist/commands/initiative.d.ts +0 -13
- package/dist/commands/initiative.js +0 -318
- package/dist/commands/workflow/initiative-link.d.ts +0 -24
- package/dist/commands/workflow/initiative-link.js +0 -47
- package/dist/commands/workflow/set-change.d.ts +0 -13
- package/dist/commands/workflow/set-change.js +0 -87
- package/dist/commands/workspace/context-status.d.ts +0 -4
- package/dist/commands/workspace/context-status.js +0 -59
- package/dist/commands/workspace/open-target-selection.d.ts +0 -13
- package/dist/commands/workspace/open-target-selection.js +0 -146
- package/dist/commands/workspace/open-view.d.ts +0 -62
- package/dist/commands/workspace/open-view.js +0 -249
- package/dist/commands/workspace/open.d.ts +0 -37
- package/dist/commands/workspace/open.js +0 -110
- package/dist/commands/workspace/opener-selection.d.ts +0 -11
- package/dist/commands/workspace/opener-selection.js +0 -98
- package/dist/commands/workspace/operations.d.ts +0 -29
- package/dist/commands/workspace/operations.js +0 -543
- package/dist/commands/workspace/prompt-theme.d.ts +0 -29
- package/dist/commands/workspace/prompt-theme.js +0 -24
- package/dist/commands/workspace/registration.d.ts +0 -13
- package/dist/commands/workspace/registration.js +0 -84
- package/dist/commands/workspace/selection.d.ts +0 -8
- package/dist/commands/workspace/selection.js +0 -104
- package/dist/commands/workspace/setup-prompts.d.ts +0 -13
- package/dist/commands/workspace/setup-prompts.js +0 -121
- package/dist/commands/workspace/types.d.ts +0 -103
- package/dist/commands/workspace/types.js +0 -36
- package/dist/commands/workspace.d.ts +0 -5
- package/dist/commands/workspace.js +0 -574
- package/dist/core/collections/index.d.ts +0 -3
- package/dist/core/collections/index.js +0 -3
- package/dist/core/collections/initiatives/collection.d.ts +0 -4
- package/dist/core/collections/initiatives/collection.js +0 -17
- package/dist/core/collections/initiatives/index.d.ts +0 -6
- package/dist/core/collections/initiatives/index.js +0 -6
- package/dist/core/collections/initiatives/operations.d.ts +0 -49
- package/dist/core/collections/initiatives/operations.js +0 -175
- package/dist/core/collections/initiatives/resolution.d.ts +0 -87
- package/dist/core/collections/initiatives/resolution.js +0 -374
- package/dist/core/collections/initiatives/schema.d.ts +0 -41
- package/dist/core/collections/initiatives/schema.js +0 -134
- package/dist/core/collections/initiatives/templates.d.ts +0 -12
- package/dist/core/collections/initiatives/templates.js +0 -90
- package/dist/core/collections/runtime.d.ts +0 -46
- package/dist/core/collections/runtime.js +0 -194
- package/dist/core/context-store/binding.d.ts +0 -53
- package/dist/core/context-store/binding.js +0 -197
- package/dist/core/context-store/errors.d.ts +0 -20
- package/dist/core/context-store/foundation.d.ts +0 -55
- package/dist/core/context-store/foundation.js +0 -321
- package/dist/core/context-store/operations.d.ts +0 -85
- package/dist/core/context-store/operations.js +0 -528
- package/dist/core/context-store/registry.d.ts +0 -45
- package/dist/core/context-store/registry.js +0 -229
- package/dist/core/workspace/foundation.d.ts +0 -67
- package/dist/core/workspace/foundation.js +0 -295
- package/dist/core/workspace/index.d.ts +0 -8
- package/dist/core/workspace/index.js +0 -8
- package/dist/core/workspace/legacy-state.d.ts +0 -28
- package/dist/core/workspace/legacy-state.js +0 -200
- package/dist/core/workspace/link-input.d.ts +0 -9
- package/dist/core/workspace/link-input.js +0 -32
- package/dist/core/workspace/open-surface.d.ts +0 -45
- package/dist/core/workspace/open-surface.js +0 -215
- package/dist/core/workspace/openers.d.ts +0 -21
- package/dist/core/workspace/openers.js +0 -124
- package/dist/core/workspace/registry.d.ts +0 -24
- package/dist/core/workspace/registry.js +0 -146
- package/dist/core/workspace/skills.d.ts +0 -57
- package/dist/core/workspace/skills.js +0 -334
- package/dist/core/workspace/state-io.d.ts +0 -10
- package/dist/core/workspace/state-io.js +0 -121
- package/schemas/workspace-planning/schema.yaml +0 -72
- package/schemas/workspace-planning/templates/design.md +0 -33
- package/schemas/workspace-planning/templates/proposal.md +0 -28
- package/schemas/workspace-planning/templates/spec.md +0 -9
- package/schemas/workspace-planning/templates/tasks.md +0 -15
package/README.md
CHANGED
|
@@ -47,6 +47,14 @@ Our philosophy:
|
|
|
47
47
|
## See it in action
|
|
48
48
|
|
|
49
49
|
```text
|
|
50
|
+
You: /opsx:explore
|
|
51
|
+
AI: What would you like to explore?
|
|
52
|
+
You: I want dark mode but I'm not sure how to do it cleanly.
|
|
53
|
+
AI: Let me look at your styling setup...
|
|
54
|
+
Cleanest path here: CSS variables + a small theme context,
|
|
55
|
+
with system-preference detection. No new dependencies. Scope it?
|
|
56
|
+
You: Yes, let's do it.
|
|
57
|
+
|
|
50
58
|
You: /opsx:propose add-dark-mode
|
|
51
59
|
AI: Created openspec/changes/add-dark-mode/
|
|
52
60
|
✓ proposal.md — why we're doing this, what's changing
|
|
@@ -94,9 +102,12 @@ cd your-project
|
|
|
94
102
|
openspec init
|
|
95
103
|
```
|
|
96
104
|
|
|
97
|
-
Now
|
|
105
|
+
Now talk to your AI:
|
|
106
|
+
|
|
107
|
+
- **Not sure what to build yet?** Start with `/opsx:explore`, a no-stakes thinking partner that reads your code, weighs options, and shapes a plan before anything is written. ([Explore guide](docs/explore.md))
|
|
108
|
+
- **Already know what you want?** Go straight to `/opsx:propose <what-you-want-to-build>`.
|
|
98
109
|
|
|
99
|
-
If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`.
|
|
110
|
+
Both are in the default profile. If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/opsx:verify`, `/opsx:bulk-archive`, `/opsx:onboard`), select it with `openspec config profile` and apply with `openspec update`.
|
|
100
111
|
|
|
101
112
|
> [!NOTE]
|
|
102
113
|
> Not sure if your tool is supported? [View the full list](docs/supported-tools.md) – we support 25+ tools and growing.
|
|
@@ -105,14 +116,24 @@ If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/
|
|
|
105
116
|
|
|
106
117
|
## Docs
|
|
107
118
|
|
|
119
|
+
**Start here:** the **[Documentation Home](docs/README.md)** maps everything. New to OpenSpec? Read [Getting Started](docs/getting-started.md), then [How Commands Work](docs/how-commands-work.md) (where you actually type `/opsx:propose`).
|
|
120
|
+
|
|
108
121
|
→ **[Getting Started](docs/getting-started.md)**: first steps<br>
|
|
122
|
+
→ **[Explore First](docs/explore.md)**: think it through with `/opsx:explore` before you commit<br>
|
|
123
|
+
→ **[How Commands Work](docs/how-commands-work.md)**: where slash commands run vs the CLI<br>
|
|
124
|
+
→ **[Core Concepts at a Glance](docs/overview.md)**: the whole mental model, one page<br>
|
|
125
|
+
→ **[Examples & Recipes](docs/examples.md)**: real changes, start to finish<br>
|
|
109
126
|
→ **[Workflows](docs/workflows.md)**: combos and patterns<br>
|
|
127
|
+
→ **[Existing Projects](docs/existing-projects.md)**: adopt OpenSpec on a brownfield codebase<br>
|
|
128
|
+
→ **[Editing a Change](docs/editing-changes.md)**: update artifacts, go back, reconcile manual edits<br>
|
|
110
129
|
→ **[Commands](docs/commands.md)**: slash commands & skills<br>
|
|
111
130
|
→ **[CLI](docs/cli.md)**: terminal reference<br>
|
|
131
|
+
→ **[Stores](docs/stores-beta/user-guide.md)**: plan in a separate repo, shared across your team (beta)<br>
|
|
112
132
|
→ **[Supported Tools](docs/supported-tools.md)**: tool integrations & install paths<br>
|
|
113
133
|
→ **[Concepts](docs/concepts.md)**: how it all fits<br>
|
|
114
134
|
→ **[Multi-Language](docs/multi-language.md)**: multi-language support<br>
|
|
115
|
-
→ **[Customization](docs/customization.md)**: make it yours
|
|
135
|
+
→ **[Customization](docs/customization.md)**: make it yours<br>
|
|
136
|
+
→ **[FAQ](docs/faq.md)** · **[Troubleshooting](docs/troubleshooting.md)** · **[Glossary](docs/glossary.md)**: quick help
|
|
116
137
|
|
|
117
138
|
|
|
118
139
|
## Community schemas
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
declare const program: Command;
|
|
3
|
+
/**
|
|
4
|
+
* Get the full command path for nested commands.
|
|
5
|
+
* For example: 'change show' -> 'change:show'
|
|
6
|
+
*/
|
|
7
|
+
export declare function getCommandPath(command: Command): string;
|
|
3
8
|
export { program };
|
|
4
9
|
export declare function runCli(argv?: string[]): void;
|
|
5
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cli/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { asStatus } from '../commands/shared-output.js';
|
|
2
|
+
import { Command, Option } from 'commander';
|
|
2
3
|
import { createRequire } from 'module';
|
|
3
4
|
import ora from 'ora';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
import { fileURLToPath } from 'url';
|
|
6
7
|
import { promises as fs } from 'fs';
|
|
7
|
-
import { AI_TOOLS
|
|
8
|
+
import { AI_TOOLS } from '../core/config.js';
|
|
8
9
|
import { UpdateCommand } from '../core/update.js';
|
|
9
10
|
import { ListCommand } from '../core/list.js';
|
|
10
11
|
import { ArchiveCommand } from '../core/archive.js';
|
|
11
12
|
import { ViewCommand } from '../core/view.js';
|
|
13
|
+
import { resolveRootForCommand, toRootOutput } from '../core/root-selection.js';
|
|
12
14
|
import { registerSpecCommand } from '../commands/spec.js';
|
|
13
15
|
import { ChangeCommand } from '../commands/change.js';
|
|
14
16
|
import { ValidateCommand } from '../commands/validate.js';
|
|
@@ -17,12 +19,37 @@ import { CompletionCommand } from '../commands/completion.js';
|
|
|
17
19
|
import { FeedbackCommand } from '../commands/feedback.js';
|
|
18
20
|
import { registerConfigCommand } from '../commands/config.js';
|
|
19
21
|
import { registerSchemaCommand } from '../commands/schema.js';
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import { statusCommand, instructionsCommand, applyInstructionsCommand, templatesCommand, schemasCommand, newChangeCommand,
|
|
22
|
+
import { registerStoreCommand } from '../commands/store.js';
|
|
23
|
+
import { registerDoctorCommand } from '../commands/doctor.js';
|
|
24
|
+
import { registerContextCommand } from '../commands/context.js';
|
|
25
|
+
import { registerWorksetCommand } from '../commands/workset.js';
|
|
26
|
+
import { statusCommand, instructionsCommand, applyInstructionsCommand, templatesCommand, schemasCommand, newChangeCommand, DEFAULT_SCHEMA, } from '../commands/workflow/index.js';
|
|
25
27
|
import { maybeShowTelemetryNotice, trackCommand, shutdown } from '../telemetry/index.js';
|
|
28
|
+
import { COMMON_FLAGS } from '../core/completions/shared-flags.js';
|
|
29
|
+
const STORE_OPTION_DESCRIPTION = COMMON_FLAGS.store.description;
|
|
30
|
+
// Deliberate rejection path: --store-path stays registered (hidden) so the
|
|
31
|
+
// resolver can explain that registering the path is the supported route,
|
|
32
|
+
// instead of Commander emitting a generic unknown-option error (or, for
|
|
33
|
+
// `show`, silently ignoring it via allowUnknownOption).
|
|
34
|
+
function hiddenStorePathOption() {
|
|
35
|
+
return new Option('--store-path <path>', 'Not supported; register the path with "openspec store register <path>" and use --store <id>').hideHelp();
|
|
36
|
+
}
|
|
37
|
+
function failWithError(error, json) {
|
|
38
|
+
// The agent contract: every --json failure leaves exactly one JSON
|
|
39
|
+
// document on stdout (the command's null-shape plus a status array).
|
|
40
|
+
if (json?.enabled) {
|
|
41
|
+
console.log(JSON.stringify({ ...(json.payload ?? {}), status: [asStatus(error, json.fallbackCode ?? 'command_error')] }, null, 2));
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
ora().fail(`Error: ${error.message}`);
|
|
46
|
+
// Resolution and store errors carry a pasteable fix - never drop it.
|
|
47
|
+
const fix = error.diagnostic?.fix;
|
|
48
|
+
if (fix) {
|
|
49
|
+
console.error(`Fix: ${fix}`);
|
|
50
|
+
}
|
|
51
|
+
process.exitCode = process.exitCode ?? 1;
|
|
52
|
+
}
|
|
26
53
|
const program = new Command();
|
|
27
54
|
const require = createRequire(import.meta.url);
|
|
28
55
|
const { version } = require('../../package.json');
|
|
@@ -30,7 +57,7 @@ const { version } = require('../../package.json');
|
|
|
30
57
|
* Get the full command path for nested commands.
|
|
31
58
|
* For example: 'change show' -> 'change:show'
|
|
32
59
|
*/
|
|
33
|
-
function getCommandPath(command) {
|
|
60
|
+
export function getCommandPath(command) {
|
|
34
61
|
const names = [];
|
|
35
62
|
let current = command;
|
|
36
63
|
while (current) {
|
|
@@ -70,21 +97,6 @@ program.hook('postAction', async () => {
|
|
|
70
97
|
});
|
|
71
98
|
const availableToolIds = AI_TOOLS.filter((tool) => tool.skillsDir).map((tool) => tool.value);
|
|
72
99
|
const toolsOptionDescription = `Configure AI tools non-interactively. Use "all", "none", or a comma-separated list of: ${availableToolIds.join(', ')}`;
|
|
73
|
-
async function hasRepoLocalOpenSpecProject(projectPath) {
|
|
74
|
-
try {
|
|
75
|
-
const stats = await fs.stat(path.join(projectPath, OPENSPEC_DIR_NAME));
|
|
76
|
-
return stats.isDirectory();
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
const code = typeof error === 'object' && error !== null && 'code' in error
|
|
80
|
-
? error.code
|
|
81
|
-
: undefined;
|
|
82
|
-
if (code !== 'ENOENT' && code !== 'ENOTDIR') {
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
100
|
program
|
|
89
101
|
.command('init [path]')
|
|
90
102
|
.description('Initialize OpenSpec in your project')
|
|
@@ -122,8 +134,7 @@ program
|
|
|
122
134
|
await initCommand.execute(targetPath);
|
|
123
135
|
}
|
|
124
136
|
catch (error) {
|
|
125
|
-
|
|
126
|
-
ora().fail(`Error: ${error.message}`);
|
|
137
|
+
failWithError(error);
|
|
127
138
|
process.exit(1);
|
|
128
139
|
}
|
|
129
140
|
});
|
|
@@ -144,8 +155,7 @@ program
|
|
|
144
155
|
await initCommand.execute('.');
|
|
145
156
|
}
|
|
146
157
|
catch (error) {
|
|
147
|
-
|
|
148
|
-
ora().fail(`Error: ${error.message}`);
|
|
158
|
+
failWithError(error);
|
|
149
159
|
process.exit(1);
|
|
150
160
|
}
|
|
151
161
|
});
|
|
@@ -155,21 +165,11 @@ program
|
|
|
155
165
|
.option('--force', 'Force update even when tools are up to date')
|
|
156
166
|
.action(async (targetPath = '.', options) => {
|
|
157
167
|
try {
|
|
158
|
-
const resolvedPath = path.resolve(targetPath);
|
|
159
168
|
const updateCommand = new UpdateCommand({ force: options?.force });
|
|
160
|
-
|
|
161
|
-
await updateCommand.execute(resolvedPath);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
const workspaceRoot = await findWorkspaceRoot(resolvedPath);
|
|
165
|
-
if (workspaceRoot) {
|
|
166
|
-
throw new Error('OpenSpec workspace detected. Run `openspec workspace update` to refresh workspace-local guidance and skills.');
|
|
167
|
-
}
|
|
168
|
-
await updateCommand.execute(resolvedPath);
|
|
169
|
+
await updateCommand.execute(targetPath);
|
|
169
170
|
}
|
|
170
171
|
catch (error) {
|
|
171
|
-
|
|
172
|
-
ora().fail(`Error: ${error.message}`);
|
|
172
|
+
failWithError(error);
|
|
173
173
|
process.exit(1);
|
|
174
174
|
}
|
|
175
175
|
});
|
|
@@ -180,16 +180,32 @@ program
|
|
|
180
180
|
.option('--changes', 'List changes explicitly (default)')
|
|
181
181
|
.option('--sort <order>', 'Sort order: "recent" (default) or "name"', 'recent')
|
|
182
182
|
.option('--json', 'Output as JSON (for programmatic use)')
|
|
183
|
+
.option('--store <id>', STORE_OPTION_DESCRIPTION)
|
|
184
|
+
.addOption(hiddenStorePathOption())
|
|
183
185
|
.action(async (options) => {
|
|
184
186
|
try {
|
|
187
|
+
const root = await resolveRootForCommand(options ?? {}, {
|
|
188
|
+
json: options?.json,
|
|
189
|
+
failurePayload: options?.specs ? { specs: [], root: null } : { changes: [], root: null },
|
|
190
|
+
});
|
|
191
|
+
if (!root) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
185
194
|
const listCommand = new ListCommand();
|
|
186
195
|
const mode = options?.specs ? 'specs' : 'changes';
|
|
187
196
|
const sort = options?.sort === 'name' ? 'name' : 'recent';
|
|
188
|
-
await listCommand.execute(
|
|
197
|
+
await listCommand.execute(root.path, mode, {
|
|
198
|
+
sort,
|
|
199
|
+
json: options?.json,
|
|
200
|
+
...(options?.json ? { root: toRootOutput(root) } : {}),
|
|
201
|
+
});
|
|
189
202
|
}
|
|
190
203
|
catch (error) {
|
|
191
|
-
|
|
192
|
-
|
|
204
|
+
failWithError(error, {
|
|
205
|
+
enabled: options?.json,
|
|
206
|
+
payload: options?.specs ? { specs: [], root: null } : { changes: [], root: null },
|
|
207
|
+
fallbackCode: 'list_error',
|
|
208
|
+
});
|
|
193
209
|
process.exit(1);
|
|
194
210
|
}
|
|
195
211
|
});
|
|
@@ -202,8 +218,7 @@ program
|
|
|
202
218
|
await viewCommand.execute('.');
|
|
203
219
|
}
|
|
204
220
|
catch (error) {
|
|
205
|
-
|
|
206
|
-
ora().fail(`Error: ${error.message}`);
|
|
221
|
+
failWithError(error);
|
|
207
222
|
process.exit(1);
|
|
208
223
|
}
|
|
209
224
|
});
|
|
@@ -273,23 +288,26 @@ program
|
|
|
273
288
|
.option('-y, --yes', 'Skip confirmation prompts')
|
|
274
289
|
.option('--skip-specs', 'Skip spec update operations (useful for infrastructure, tooling, or doc-only changes)')
|
|
275
290
|
.option('--no-validate', 'Skip validation (not recommended, requires confirmation)')
|
|
291
|
+
.option('--json', 'Output as JSON (non-interactive)')
|
|
292
|
+
.option('--store <id>', STORE_OPTION_DESCRIPTION)
|
|
293
|
+
.addOption(hiddenStorePathOption())
|
|
276
294
|
.action(async (changeName, options) => {
|
|
277
295
|
try {
|
|
278
296
|
const archiveCommand = new ArchiveCommand();
|
|
279
297
|
await archiveCommand.execute(changeName, options);
|
|
280
298
|
}
|
|
281
299
|
catch (error) {
|
|
282
|
-
|
|
283
|
-
ora().fail(`Error: ${error.message}`);
|
|
300
|
+
failWithError(error);
|
|
284
301
|
process.exit(1);
|
|
285
302
|
}
|
|
286
303
|
});
|
|
287
304
|
registerSpecCommand(program);
|
|
288
305
|
registerConfigCommand(program);
|
|
289
306
|
registerSchemaCommand(program);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
307
|
+
registerStoreCommand(program);
|
|
308
|
+
registerDoctorCommand(program);
|
|
309
|
+
registerContextCommand(program);
|
|
310
|
+
registerWorksetCommand(program);
|
|
293
311
|
// Top-level validate command
|
|
294
312
|
program
|
|
295
313
|
.command('validate [item-name]')
|
|
@@ -302,14 +320,15 @@ program
|
|
|
302
320
|
.option('--json', 'Output validation results as JSON')
|
|
303
321
|
.option('--concurrency <n>', 'Max concurrent validations (defaults to env OPENSPEC_CONCURRENCY or 6)')
|
|
304
322
|
.option('--no-interactive', 'Disable interactive prompts')
|
|
323
|
+
.option('--store <id>', STORE_OPTION_DESCRIPTION)
|
|
324
|
+
.addOption(hiddenStorePathOption())
|
|
305
325
|
.action(async (itemName, options) => {
|
|
306
326
|
try {
|
|
307
327
|
const validateCommand = new ValidateCommand();
|
|
308
328
|
await validateCommand.execute(itemName, options);
|
|
309
329
|
}
|
|
310
330
|
catch (error) {
|
|
311
|
-
|
|
312
|
-
ora().fail(`Error: ${error.message}`);
|
|
331
|
+
failWithError(error, { enabled: options?.json, fallbackCode: 'validate_error' });
|
|
313
332
|
process.exit(1);
|
|
314
333
|
}
|
|
315
334
|
});
|
|
@@ -327,6 +346,10 @@ program
|
|
|
327
346
|
.option('--requirements', 'JSON only: Show only requirements (exclude scenarios)')
|
|
328
347
|
.option('--no-scenarios', 'JSON only: Exclude scenario content')
|
|
329
348
|
.option('-r, --requirement <id>', 'JSON only: Show specific requirement by ID (1-based)')
|
|
349
|
+
.option('--store <id>', STORE_OPTION_DESCRIPTION)
|
|
350
|
+
// Explicit registration required: allowUnknownOption would otherwise
|
|
351
|
+
// silently swallow --store-path instead of rejecting it deliberately.
|
|
352
|
+
.addOption(hiddenStorePathOption())
|
|
330
353
|
// allow unknown options to pass-through to underlying command implementation
|
|
331
354
|
.allowUnknownOption(true)
|
|
332
355
|
.action(async (itemName, options) => {
|
|
@@ -335,8 +358,7 @@ program
|
|
|
335
358
|
await showCommand.execute(itemName, options ?? {});
|
|
336
359
|
}
|
|
337
360
|
catch (error) {
|
|
338
|
-
|
|
339
|
-
ora().fail(`Error: ${error.message}`);
|
|
361
|
+
failWithError(error, { enabled: options?.json, fallbackCode: 'show_error' });
|
|
340
362
|
process.exit(1);
|
|
341
363
|
}
|
|
342
364
|
});
|
|
@@ -351,8 +373,7 @@ program
|
|
|
351
373
|
await feedbackCommand.execute(message, options);
|
|
352
374
|
}
|
|
353
375
|
catch (error) {
|
|
354
|
-
|
|
355
|
-
ora().fail(`Error: ${error.message}`);
|
|
376
|
+
failWithError(error);
|
|
356
377
|
process.exit(1);
|
|
357
378
|
}
|
|
358
379
|
});
|
|
@@ -369,8 +390,7 @@ completionCmd
|
|
|
369
390
|
await completionCommand.generate({ shell });
|
|
370
391
|
}
|
|
371
392
|
catch (error) {
|
|
372
|
-
|
|
373
|
-
ora().fail(`Error: ${error.message}`);
|
|
393
|
+
failWithError(error);
|
|
374
394
|
process.exit(1);
|
|
375
395
|
}
|
|
376
396
|
});
|
|
@@ -384,8 +404,7 @@ completionCmd
|
|
|
384
404
|
await completionCommand.install({ shell, verbose: options?.verbose });
|
|
385
405
|
}
|
|
386
406
|
catch (error) {
|
|
387
|
-
|
|
388
|
-
ora().fail(`Error: ${error.message}`);
|
|
407
|
+
failWithError(error);
|
|
389
408
|
process.exit(1);
|
|
390
409
|
}
|
|
391
410
|
});
|
|
@@ -399,8 +418,7 @@ completionCmd
|
|
|
399
418
|
await completionCommand.uninstall({ shell, yes: options?.yes });
|
|
400
419
|
}
|
|
401
420
|
catch (error) {
|
|
402
|
-
|
|
403
|
-
ora().fail(`Error: ${error.message}`);
|
|
421
|
+
failWithError(error);
|
|
404
422
|
process.exit(1);
|
|
405
423
|
}
|
|
406
424
|
});
|
|
@@ -428,13 +446,14 @@ program
|
|
|
428
446
|
.option('--change <id>', 'Change name to show status for')
|
|
429
447
|
.option('--schema <name>', 'Schema override (auto-detected from config.yaml)')
|
|
430
448
|
.option('--json', 'Output as JSON')
|
|
449
|
+
.option('--store <id>', STORE_OPTION_DESCRIPTION)
|
|
450
|
+
.addOption(hiddenStorePathOption())
|
|
431
451
|
.action(async (options) => {
|
|
432
452
|
try {
|
|
433
453
|
await statusCommand(options);
|
|
434
454
|
}
|
|
435
455
|
catch (error) {
|
|
436
|
-
|
|
437
|
-
ora().fail(`Error: ${error.message}`);
|
|
456
|
+
failWithError(error, { enabled: options.json, fallbackCode: 'change_error' });
|
|
438
457
|
process.exit(1);
|
|
439
458
|
}
|
|
440
459
|
});
|
|
@@ -445,6 +464,8 @@ program
|
|
|
445
464
|
.option('--change <id>', 'Change name')
|
|
446
465
|
.option('--schema <name>', 'Schema override (auto-detected from config.yaml)')
|
|
447
466
|
.option('--json', 'Output as JSON')
|
|
467
|
+
.option('--store <id>', STORE_OPTION_DESCRIPTION)
|
|
468
|
+
.addOption(hiddenStorePathOption())
|
|
448
469
|
.action(async (artifactId, options) => {
|
|
449
470
|
try {
|
|
450
471
|
// Special case: "apply" is not an artifact, but a command to get apply instructions
|
|
@@ -456,8 +477,7 @@ program
|
|
|
456
477
|
}
|
|
457
478
|
}
|
|
458
479
|
catch (error) {
|
|
459
|
-
|
|
460
|
-
ora().fail(`Error: ${error.message}`);
|
|
480
|
+
failWithError(error, { enabled: options.json, fallbackCode: 'change_error' });
|
|
461
481
|
process.exit(1);
|
|
462
482
|
}
|
|
463
483
|
});
|
|
@@ -472,8 +492,7 @@ program
|
|
|
472
492
|
await templatesCommand(options);
|
|
473
493
|
}
|
|
474
494
|
catch (error) {
|
|
475
|
-
|
|
476
|
-
ora().fail(`Error: ${error.message}`);
|
|
495
|
+
failWithError(error);
|
|
477
496
|
process.exit(1);
|
|
478
497
|
}
|
|
479
498
|
});
|
|
@@ -487,8 +506,7 @@ program
|
|
|
487
506
|
await schemasCommand(options);
|
|
488
507
|
}
|
|
489
508
|
catch (error) {
|
|
490
|
-
|
|
491
|
-
ora().fail(`Error: ${error.message}`);
|
|
509
|
+
failWithError(error);
|
|
492
510
|
process.exit(1);
|
|
493
511
|
}
|
|
494
512
|
});
|
|
@@ -498,39 +516,21 @@ newCmd
|
|
|
498
516
|
.command('change <name>')
|
|
499
517
|
.description('Create a new change directory')
|
|
500
518
|
.option('--description <text>', 'Description to add to README.md')
|
|
501
|
-
.option('--goal <text>', '
|
|
502
|
-
.option('--areas <names>', 'Comma-separated affected workspace link names')
|
|
503
|
-
.option('--initiative <id>', 'Link the repo-local change to an initiative')
|
|
504
|
-
.option('--store <id>', 'Context store id for --initiative')
|
|
505
|
-
.option('--store-path <path>', 'Existing local context store root for --initiative')
|
|
519
|
+
.option('--goal <text>', 'Optional goal metadata to store with the change')
|
|
506
520
|
.option('--schema <name>', `Workflow schema to use (default: ${DEFAULT_SCHEMA})`)
|
|
507
521
|
.option('--json', 'Output as JSON')
|
|
522
|
+
.option('--store <id>', STORE_OPTION_DESCRIPTION)
|
|
523
|
+
.addOption(hiddenStorePathOption())
|
|
524
|
+
// Removed options kept registered (hidden) so users get a deliberate
|
|
525
|
+
// explanation instead of a generic unknown-option error.
|
|
526
|
+
.addOption(new Option('--initiative <id>', 'No longer supported').hideHelp())
|
|
527
|
+
.addOption(new Option('--areas <names>', 'No longer supported').hideHelp())
|
|
508
528
|
.action(async (name, options) => {
|
|
509
529
|
try {
|
|
510
530
|
await newChangeCommand(name, options);
|
|
511
531
|
}
|
|
512
532
|
catch (error) {
|
|
513
|
-
|
|
514
|
-
ora().fail(`Error: ${error.message}`);
|
|
515
|
-
process.exit(1);
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
// Set command group
|
|
519
|
-
const setCmd = program.command('set').description('Set checked-in OpenSpec metadata');
|
|
520
|
-
setCmd
|
|
521
|
-
.command('change <name>')
|
|
522
|
-
.description('Set repo-local change metadata')
|
|
523
|
-
.option('--initiative <id>', 'Link the repo-local change to an initiative')
|
|
524
|
-
.option('--store <id>', 'Context store id for --initiative')
|
|
525
|
-
.option('--store-path <path>', 'Existing local context store root for --initiative')
|
|
526
|
-
.option('--json', 'Output as JSON')
|
|
527
|
-
.action(async (name, options) => {
|
|
528
|
-
try {
|
|
529
|
-
await setChangeCommand(name, options);
|
|
530
|
-
}
|
|
531
|
-
catch (error) {
|
|
532
|
-
console.log();
|
|
533
|
-
ora().fail(`Error: ${error.message}`);
|
|
533
|
+
failWithError(error);
|
|
534
534
|
process.exit(1);
|
|
535
535
|
}
|
|
536
536
|
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import type { RootOutput } from '../core/root-selection.js';
|
|
1
2
|
export declare class ChangeCommand {
|
|
2
3
|
private converter;
|
|
3
|
-
|
|
4
|
+
private rootPath?;
|
|
5
|
+
constructor(rootPath?: string);
|
|
6
|
+
private getChangesPath;
|
|
4
7
|
/**
|
|
5
8
|
* Show a change proposal.
|
|
6
9
|
* - Text mode: raw markdown passthrough (no filters)
|
|
@@ -12,6 +15,7 @@ export declare class ChangeCommand {
|
|
|
12
15
|
requirementsOnly?: boolean;
|
|
13
16
|
deltasOnly?: boolean;
|
|
14
17
|
noInteractive?: boolean;
|
|
18
|
+
rootOutput?: RootOutput;
|
|
15
19
|
}): Promise<void>;
|
|
16
20
|
/**
|
|
17
21
|
* List active changes.
|
package/dist/commands/change.js
CHANGED
|
@@ -11,8 +11,15 @@ const TASK_PATTERN = /^[-*]\s+\[[\sx]\]/i;
|
|
|
11
11
|
const COMPLETED_TASK_PATTERN = /^[-*]\s+\[x\]/i;
|
|
12
12
|
export class ChangeCommand {
|
|
13
13
|
converter;
|
|
14
|
-
|
|
14
|
+
rootPath;
|
|
15
|
+
// rootPath is set only by root-aware callers (top-level `show`); the
|
|
16
|
+
// deprecated noun-form commands stay cwd-based.
|
|
17
|
+
constructor(rootPath) {
|
|
15
18
|
this.converter = new JsonConverter();
|
|
19
|
+
this.rootPath = rootPath;
|
|
20
|
+
}
|
|
21
|
+
getChangesPath() {
|
|
22
|
+
return path.join(this.rootPath ?? process.cwd(), 'openspec', 'changes');
|
|
16
23
|
}
|
|
17
24
|
/**
|
|
18
25
|
* Show a change proposal.
|
|
@@ -21,7 +28,7 @@ export class ChangeCommand {
|
|
|
21
28
|
* Note: --requirements-only is deprecated alias for --deltas-only
|
|
22
29
|
*/
|
|
23
30
|
async show(changeName, options) {
|
|
24
|
-
const changesPath =
|
|
31
|
+
const changesPath = this.getChangesPath();
|
|
25
32
|
if (!changeName) {
|
|
26
33
|
const canPrompt = isInteractive(options);
|
|
27
34
|
const changes = await this.getActiveChanges(changesPath);
|
|
@@ -62,19 +69,14 @@ export class ChangeCommand {
|
|
|
62
69
|
const title = this.extractTitle(contentForTitle, changeName);
|
|
63
70
|
const id = parsed.name;
|
|
64
71
|
const deltas = parsed.deltas || [];
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
deltaCount: deltas.length,
|
|
74
|
-
deltas,
|
|
75
|
-
};
|
|
76
|
-
console.log(JSON.stringify(output, null, 2));
|
|
77
|
-
}
|
|
72
|
+
const output = {
|
|
73
|
+
id,
|
|
74
|
+
title,
|
|
75
|
+
deltaCount: deltas.length,
|
|
76
|
+
deltas,
|
|
77
|
+
...(options.rootOutput ? { root: options.rootOutput } : {}),
|
|
78
|
+
};
|
|
79
|
+
console.log(JSON.stringify(output, null, 2));
|
|
78
80
|
}
|
|
79
81
|
else {
|
|
80
82
|
const content = await fs.readFile(proposalPath, 'utf-8');
|
package/dist/commands/config.js
CHANGED
|
@@ -6,7 +6,7 @@ import { getNestedValue, setNestedValue, deleteNestedValue, coerceValue, formatV
|
|
|
6
6
|
import { CORE_WORKFLOWS, ALL_WORKFLOWS, getProfileWorkflows } from '../core/profiles.js';
|
|
7
7
|
import { OPENSPEC_DIR_NAME } from '../core/config.js';
|
|
8
8
|
import { hasProjectConfigDrift } from '../core/profile-sync-drift.js';
|
|
9
|
-
import {
|
|
9
|
+
import { isPromptCancellationError } from './shared-output.js';
|
|
10
10
|
const WORKFLOW_PROMPT_META = {
|
|
11
11
|
propose: {
|
|
12
12
|
name: 'Propose change',
|
|
@@ -53,10 +53,6 @@ const WORKFLOW_PROMPT_META = {
|
|
|
53
53
|
description: 'Guided onboarding flow for OpenSpec',
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
|
-
function isPromptCancellationError(error) {
|
|
57
|
-
return (error instanceof Error &&
|
|
58
|
-
(error.name === 'ExitPromptError' || error.message.includes('force closed the prompt with SIGINT')));
|
|
59
|
-
}
|
|
60
56
|
/**
|
|
61
57
|
* Resolve the effective current profile state from global config defaults.
|
|
62
58
|
*/
|
|
@@ -133,16 +129,6 @@ export function diffProfileState(before, after) {
|
|
|
133
129
|
lines,
|
|
134
130
|
};
|
|
135
131
|
}
|
|
136
|
-
async function resolveWorkspaceConfigProfileContext(cwd = process.cwd()) {
|
|
137
|
-
const workspaceRoot = await findWorkspaceRoot(cwd);
|
|
138
|
-
if (!workspaceRoot) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
root: workspaceRoot,
|
|
143
|
-
commandCwd: cwd,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
132
|
function maybeWarnProjectConfigDrift(projectDir, state, colorize) {
|
|
147
133
|
const openspecDir = path.join(projectDir, OPENSPEC_DIR_NAME);
|
|
148
134
|
if (!fs.existsSync(openspecDir)) {
|
|
@@ -153,28 +139,7 @@ function maybeWarnProjectConfigDrift(projectDir, state, colorize) {
|
|
|
153
139
|
}
|
|
154
140
|
console.log(colorize('Warning: Global config is not applied to this project. Run `openspec update` to sync.'));
|
|
155
141
|
}
|
|
156
|
-
|
|
157
|
-
const workspaceContext = await resolveWorkspaceConfigProfileContext();
|
|
158
|
-
if (workspaceContext) {
|
|
159
|
-
let viewState = null;
|
|
160
|
-
try {
|
|
161
|
-
viewState = await readOptionalWorkspaceViewState(workspaceContext.root);
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
if (hasWorkspaceSkillProfileDrift(viewState)) {
|
|
167
|
-
console.log(colorize('Warning: Workspace-local agent skills are out of sync with the active global profile. Run `openspec workspace update` to sync.'));
|
|
168
|
-
}
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
maybeWarnProjectConfigDrift(process.cwd(), state, colorize);
|
|
172
|
-
}
|
|
173
|
-
function printConfigProfileApplyGuidance(workspaceContext) {
|
|
174
|
-
if (workspaceContext) {
|
|
175
|
-
console.log('Config updated. Run `openspec workspace update` to apply it to workspace-local skills.');
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
142
|
+
function printConfigProfileApplyGuidance() {
|
|
178
143
|
console.log('Config updated. Run `openspec update` in your projects to apply.');
|
|
179
144
|
}
|
|
180
145
|
/**
|
|
@@ -418,8 +383,7 @@ export function registerConfigCommand(program) {
|
|
|
418
383
|
config.workflows = [...CORE_WORKFLOWS];
|
|
419
384
|
// Preserve delivery setting
|
|
420
385
|
saveGlobalConfig(config);
|
|
421
|
-
|
|
422
|
-
printConfigProfileApplyGuidance(workspaceContext);
|
|
386
|
+
printConfigProfileApplyGuidance();
|
|
423
387
|
return;
|
|
424
388
|
}
|
|
425
389
|
if (preset) {
|
|
@@ -472,7 +436,7 @@ export function registerConfigCommand(program) {
|
|
|
472
436
|
});
|
|
473
437
|
if (action === 'keep') {
|
|
474
438
|
console.log('No config changes.');
|
|
475
|
-
|
|
439
|
+
maybeWarnProjectConfigDrift(process.cwd(), currentState, chalk.yellow);
|
|
476
440
|
return;
|
|
477
441
|
}
|
|
478
442
|
const nextState = {
|
|
@@ -541,7 +505,7 @@ export function registerConfigCommand(program) {
|
|
|
541
505
|
const diff = diffProfileState(currentState, nextState);
|
|
542
506
|
if (!diff.hasChanges) {
|
|
543
507
|
console.log('No config changes.');
|
|
544
|
-
|
|
508
|
+
maybeWarnProjectConfigDrift(process.cwd(), nextState, chalk.yellow);
|
|
545
509
|
return;
|
|
546
510
|
}
|
|
547
511
|
console.log(chalk.bold('\nConfig changes:'));
|
|
@@ -553,29 +517,6 @@ export function registerConfigCommand(program) {
|
|
|
553
517
|
config.delivery = nextState.delivery;
|
|
554
518
|
config.workflows = nextState.workflows;
|
|
555
519
|
saveGlobalConfig(config);
|
|
556
|
-
const workspaceContext = await resolveWorkspaceConfigProfileContext();
|
|
557
|
-
if (workspaceContext) {
|
|
558
|
-
const applyNow = await confirm({
|
|
559
|
-
message: 'Apply changes to this workspace now?',
|
|
560
|
-
default: true,
|
|
561
|
-
});
|
|
562
|
-
if (applyNow) {
|
|
563
|
-
try {
|
|
564
|
-
execSync('npx openspec workspace update', {
|
|
565
|
-
stdio: 'inherit',
|
|
566
|
-
cwd: workspaceContext.commandCwd,
|
|
567
|
-
});
|
|
568
|
-
console.log('Run `openspec workspace update` in your other workspaces to apply.');
|
|
569
|
-
}
|
|
570
|
-
catch {
|
|
571
|
-
console.error('`openspec workspace update` failed. Please run it manually to apply the profile changes.');
|
|
572
|
-
process.exitCode = 1;
|
|
573
|
-
}
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
printConfigProfileApplyGuidance(workspaceContext);
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
520
|
// Check if inside an OpenSpec project
|
|
580
521
|
const projectDir = process.cwd();
|
|
581
522
|
const openspecDir = path.join(projectDir, OPENSPEC_DIR_NAME);
|
|
@@ -596,7 +537,7 @@ export function registerConfigCommand(program) {
|
|
|
596
537
|
return;
|
|
597
538
|
}
|
|
598
539
|
}
|
|
599
|
-
printConfigProfileApplyGuidance(
|
|
540
|
+
printConfigProfileApplyGuidance();
|
|
600
541
|
}
|
|
601
542
|
catch (error) {
|
|
602
543
|
if (isPromptCancellationError(error)) {
|