@fission-ai/openspec 1.4.0 → 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 +94 -76
- 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 -9
- package/dist/commands/workspace/selection.js +0 -115
- 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 -6
- package/dist/commands/workspace.js +0 -587
- 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 -119
- 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
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type StoreBackendConfig, type StoreGitBackendConfig, type StorePathOptions, type StoreRegistryEntry, type StoreRegistryState } from './foundation.js';
|
|
2
|
+
export interface RegisterStoreInput extends StorePathOptions {
|
|
3
|
+
id: string;
|
|
4
|
+
localPath: string;
|
|
5
|
+
remote?: string;
|
|
6
|
+
branch?: string;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ResolveRegisteredStoreInput extends StorePathOptions {
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
12
|
+
export interface GetRegisteredStoreInput extends ResolveRegisteredStoreInput {
|
|
13
|
+
expectedBackend?: StoreGitBackendConfig;
|
|
14
|
+
}
|
|
15
|
+
export interface UnregisterStoreInput extends StorePathOptions {
|
|
16
|
+
id: string;
|
|
17
|
+
expectedBackend?: StoreGitBackendConfig;
|
|
18
|
+
beforeCommit?: (entry: RegisteredStoreEntry) => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export type ListRegisteredStoresOptions = StorePathOptions;
|
|
21
|
+
export interface RegisteredStoreEntry extends StoreRegistryEntry {
|
|
22
|
+
storeRoot: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ResolvedStore {
|
|
25
|
+
id: string;
|
|
26
|
+
storeRoot: string;
|
|
27
|
+
backend: StoreGitBackendConfig;
|
|
28
|
+
}
|
|
29
|
+
export interface StoreRegistrationCommit extends ResolvedStore {
|
|
30
|
+
metadataCreated: boolean;
|
|
31
|
+
registryUpdated: boolean;
|
|
32
|
+
alreadyRegistered: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface CommitStoreRegistrationInput extends StorePathOptions {
|
|
35
|
+
id: string;
|
|
36
|
+
backend: StoreGitBackendConfig;
|
|
37
|
+
writeMetadataIfMissing: boolean;
|
|
38
|
+
}
|
|
39
|
+
export declare function getStoreRootForBackend(backend: StoreBackendConfig): string;
|
|
40
|
+
export declare function assertNoRegisteredStoreConflict(registry: StoreRegistryState | null, id: string, backend: StoreGitBackendConfig): void;
|
|
41
|
+
export declare function commitStoreRegistration(input: CommitStoreRegistrationInput): Promise<StoreRegistrationCommit>;
|
|
42
|
+
export declare function registerStore(input: RegisterStoreInput): Promise<ResolvedStore>;
|
|
43
|
+
export interface RegistrySnapshot {
|
|
44
|
+
/** null = the registry is unreadable; [] = empty or absent. */
|
|
45
|
+
entries: StoreRegistryEntry[] | null;
|
|
46
|
+
unreadable: boolean;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One registry read serving every consumer in a command.
|
|
50
|
+
*/
|
|
51
|
+
export declare function readRegistrySnapshot(options?: {
|
|
52
|
+
globalDataDir?: string;
|
|
53
|
+
}): Promise<RegistrySnapshot>;
|
|
54
|
+
export declare function listRegisteredStores(options?: ListRegisteredStoresOptions): Promise<RegisteredStoreEntry[]>;
|
|
55
|
+
export declare function getRegisteredStore(input: GetRegisteredStoreInput): Promise<RegisteredStoreEntry>;
|
|
56
|
+
export declare function unregisterStoreRegistration(input: UnregisterStoreInput): Promise<RegisteredStoreEntry>;
|
|
57
|
+
export declare function resolveRegisteredStore(input: ResolveRegisteredStoreInput): Promise<ResolvedStore>;
|
|
58
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import { getStoreMetadataPath, getStoreMetadataDir, listStoreRegistryEntries, readStoreRegistryState, readOptionalStoreMetadataState, resolveGitStoreBackendConfig, updateStoreRegistryState, validateStoreId, writeStoreMetadataState, } from './foundation.js';
|
|
3
|
+
import { StoreError } from './errors.js';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import { FileSystemUtils } from '../../utils/file-system.js';
|
|
6
|
+
export function getStoreRootForBackend(backend) {
|
|
7
|
+
switch (backend.type) {
|
|
8
|
+
case 'git':
|
|
9
|
+
return backend.local_path;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function normalizePathForComparison(targetPath) {
|
|
13
|
+
try {
|
|
14
|
+
return FileSystemUtils.canonicalizeExistingPath(targetPath);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// Nonexistent (e.g. stale) paths still deserve a resolved compare;
|
|
18
|
+
// aligns with the operations.ts sibling fallback.
|
|
19
|
+
return path.resolve(targetPath);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function assertNoRegisteredStoreConflict(registry, id, backend) {
|
|
23
|
+
const nextPath = normalizePathForComparison(getStoreRootForBackend(backend));
|
|
24
|
+
for (const entry of listStoreRegistryEntries(registry ?? { version: 1, stores: {} })) {
|
|
25
|
+
const entryPath = normalizePathForComparison(getStoreRootForBackend(entry.backend));
|
|
26
|
+
if (entry.id === id && entryPath === nextPath) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (entry.id === id) {
|
|
30
|
+
throw new StoreError(`Store '${id}' is already registered at ${getStoreRootForBackend(entry.backend)}. One checkout per store id is supported on this machine.`, 'store_id_conflict', {
|
|
31
|
+
target: 'store.id',
|
|
32
|
+
fix: `Use the existing registration, or run openspec store unregister ${id} first to switch this id to a different checkout.`,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (entryPath === nextPath) {
|
|
36
|
+
throw new StoreError(`Store path is already registered as '${entry.id}'.`, 'store_path_conflict', {
|
|
37
|
+
target: 'store.root',
|
|
38
|
+
fix: `Use the existing '${entry.id}' registration or choose a different path.`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function withRegisteredStore(registry, id, backend) {
|
|
44
|
+
assertNoRegisteredStoreConflict(registry, id, backend);
|
|
45
|
+
const stores = {
|
|
46
|
+
...(registry?.stores ?? {}),
|
|
47
|
+
[id]: {
|
|
48
|
+
backend,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
version: 1,
|
|
53
|
+
stores: Object.fromEntries(Object.entries(stores).sort(([leftId], [rightId]) => leftId.localeCompare(rightId))),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function getRegisteredStoreOrThrow(registry, id) {
|
|
57
|
+
const entry = registry?.stores[id];
|
|
58
|
+
if (!entry) {
|
|
59
|
+
throw new StoreError(`Unknown store '${id}'`, 'store_not_found', {
|
|
60
|
+
target: 'store.id',
|
|
61
|
+
fix: 'Run openspec store list to see registered stores.',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
id,
|
|
66
|
+
backend: entry.backend,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** Same checkout: type, canonical path, and branch — remote excluded. */
|
|
70
|
+
function sameCheckout(actual, expected) {
|
|
71
|
+
return (actual.type === expected.type &&
|
|
72
|
+
normalizePathForComparison(actual.local_path) ===
|
|
73
|
+
normalizePathForComparison(expected.local_path) &&
|
|
74
|
+
actual.branch === expected.branch);
|
|
75
|
+
}
|
|
76
|
+
function storeBackendsMatch(actual, expected) {
|
|
77
|
+
return sameCheckout(actual, expected) && actual.remote === expected.remote;
|
|
78
|
+
}
|
|
79
|
+
function assertExpectedRegisteredBackend(id, actual, expected) {
|
|
80
|
+
if (!expected || storeBackendsMatch(actual, expected))
|
|
81
|
+
return;
|
|
82
|
+
throw new StoreError(`Store '${id}' changed before cleanup completed.`, 'store_registry_changed', {
|
|
83
|
+
target: 'store.registry',
|
|
84
|
+
fix: 'Retry the cleanup command after reviewing the current store registration.',
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function withoutRegisteredStore(registry, id, expectedBackend) {
|
|
88
|
+
const removed = getRegisteredStoreOrThrow(registry, id);
|
|
89
|
+
assertExpectedRegisteredBackend(id, removed.backend, expectedBackend);
|
|
90
|
+
const stores = { ...(registry?.stores ?? {}) };
|
|
91
|
+
delete stores[id];
|
|
92
|
+
return {
|
|
93
|
+
removed,
|
|
94
|
+
next: {
|
|
95
|
+
version: 1,
|
|
96
|
+
stores: Object.fromEntries(Object.entries(stores).sort(([leftId], [rightId]) => leftId.localeCompare(rightId))),
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
async function ensureStoreMetadata(storeRoot, id, options) {
|
|
101
|
+
const metadata = await readOptionalStoreMetadataState(storeRoot);
|
|
102
|
+
if (!metadata) {
|
|
103
|
+
if (!options.writeIfMissing) {
|
|
104
|
+
throw new StoreError(`Registered store '${id}' is missing metadata at ${getStoreMetadataPath(storeRoot)}`, 'store_metadata_missing', {
|
|
105
|
+
target: 'store.metadata',
|
|
106
|
+
fix: `Create ${getStoreMetadataPath(storeRoot)} or rerun "openspec store register <path>".`,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
await writeStoreMetadataState(storeRoot, {
|
|
110
|
+
version: 1,
|
|
111
|
+
id,
|
|
112
|
+
});
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
if (metadata.id !== id) {
|
|
116
|
+
throw new StoreError(`Store metadata id '${metadata.id}' does not match registered id '${id}'`, 'store_metadata_id_mismatch', {
|
|
117
|
+
target: 'store.metadata',
|
|
118
|
+
fix: 'Repair the local registry or store metadata so the ids match.',
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
export async function commitStoreRegistration(input) {
|
|
124
|
+
const id = validateStoreId(input.id);
|
|
125
|
+
const backend = input.backend;
|
|
126
|
+
const storeRoot = getStoreRootForBackend(backend);
|
|
127
|
+
let metadataCreated = false;
|
|
128
|
+
let isRerun = false;
|
|
129
|
+
let registryUpdated = false;
|
|
130
|
+
try {
|
|
131
|
+
metadataCreated = await ensureStoreMetadata(storeRoot, id, {
|
|
132
|
+
writeIfMissing: input.writeMetadataIfMissing,
|
|
133
|
+
});
|
|
134
|
+
const registry = await readStoreRegistryState({
|
|
135
|
+
globalDataDir: input.globalDataDir,
|
|
136
|
+
});
|
|
137
|
+
const existing = registry?.stores[id];
|
|
138
|
+
const existingBackend = existing?.backend;
|
|
139
|
+
// Same checkout = a rerun for an already-registered store (the 1.3
|
|
140
|
+
// reporting contract), whether or not the observed remote changed;
|
|
141
|
+
// only a remote change needs the registry write (the refresh).
|
|
142
|
+
isRerun = existingBackend !== undefined && sameCheckout(existingBackend, backend);
|
|
143
|
+
const upToDate = isRerun && existingBackend !== undefined && storeBackendsMatch(existingBackend, backend);
|
|
144
|
+
if (!upToDate) {
|
|
145
|
+
await updateStoreRegistryState((registry) => withRegisteredStore(registry, id, backend), { globalDataDir: input.globalDataDir });
|
|
146
|
+
registryUpdated = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (metadataCreated) {
|
|
151
|
+
// A concurrent registration may have read our metadata as
|
|
152
|
+
// pre-existing and committed against it - never delete metadata a
|
|
153
|
+
// committed registry entry depends on.
|
|
154
|
+
const current = await readStoreRegistryState({
|
|
155
|
+
globalDataDir: input.globalDataDir,
|
|
156
|
+
}).catch(() => null);
|
|
157
|
+
if (!current?.stores[id]) {
|
|
158
|
+
await fs.rm(getStoreMetadataPath(storeRoot), { force: true });
|
|
159
|
+
await fs.rmdir(getStoreMetadataDir(storeRoot)).catch(() => undefined);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
id,
|
|
166
|
+
storeRoot,
|
|
167
|
+
backend,
|
|
168
|
+
metadataCreated,
|
|
169
|
+
registryUpdated,
|
|
170
|
+
alreadyRegistered: isRerun,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
export async function registerStore(input) {
|
|
174
|
+
const id = validateStoreId(input.id);
|
|
175
|
+
const backend = await resolveGitStoreBackendConfig({
|
|
176
|
+
localPath: input.localPath,
|
|
177
|
+
...(input.remote !== undefined ? { remote: input.remote } : {}),
|
|
178
|
+
...(input.branch !== undefined ? { branch: input.branch } : {}),
|
|
179
|
+
}, input.cwd);
|
|
180
|
+
const storeRoot = getStoreRootForBackend(backend);
|
|
181
|
+
const committed = await commitStoreRegistration({
|
|
182
|
+
id,
|
|
183
|
+
backend,
|
|
184
|
+
writeMetadataIfMissing: true,
|
|
185
|
+
...(input.globalDataDir ? { globalDataDir: input.globalDataDir } : {}),
|
|
186
|
+
});
|
|
187
|
+
return {
|
|
188
|
+
id: committed.id,
|
|
189
|
+
storeRoot: committed.storeRoot,
|
|
190
|
+
backend: committed.backend,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* One registry read serving every consumer in a command.
|
|
195
|
+
*/
|
|
196
|
+
export async function readRegistrySnapshot(options = {}) {
|
|
197
|
+
try {
|
|
198
|
+
const registry = await readStoreRegistryState(options);
|
|
199
|
+
return {
|
|
200
|
+
entries: registry ? listStoreRegistryEntries(registry) : [],
|
|
201
|
+
unreadable: false,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return { entries: null, unreadable: true };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export async function listRegisteredStores(options = {}) {
|
|
209
|
+
const registry = await readStoreRegistryState(options);
|
|
210
|
+
if (!registry) {
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
return listStoreRegistryEntries(registry).map((entry) => ({
|
|
214
|
+
...entry,
|
|
215
|
+
storeRoot: getStoreRootForBackend(entry.backend),
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
export async function getRegisteredStore(input) {
|
|
219
|
+
const id = validateStoreId(input.id);
|
|
220
|
+
const registry = await readStoreRegistryState({
|
|
221
|
+
globalDataDir: input.globalDataDir,
|
|
222
|
+
});
|
|
223
|
+
const entry = getRegisteredStoreOrThrow(registry, id);
|
|
224
|
+
assertExpectedRegisteredBackend(id, entry.backend, input.expectedBackend);
|
|
225
|
+
return {
|
|
226
|
+
...entry,
|
|
227
|
+
storeRoot: getStoreRootForBackend(entry.backend),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
export async function unregisterStoreRegistration(input) {
|
|
231
|
+
const id = validateStoreId(input.id);
|
|
232
|
+
let removed;
|
|
233
|
+
await updateStoreRegistryState(async (registry) => {
|
|
234
|
+
const result = withoutRegisteredStore(registry, id, input.expectedBackend);
|
|
235
|
+
const removedEntry = {
|
|
236
|
+
...result.removed,
|
|
237
|
+
storeRoot: getStoreRootForBackend(result.removed.backend),
|
|
238
|
+
};
|
|
239
|
+
await input.beforeCommit?.(removedEntry);
|
|
240
|
+
removed = result.removed;
|
|
241
|
+
return result.next;
|
|
242
|
+
}, { globalDataDir: input.globalDataDir });
|
|
243
|
+
if (!removed) {
|
|
244
|
+
throw new StoreError(`Unknown store '${id}'`, 'store_not_found', {
|
|
245
|
+
target: 'store.id',
|
|
246
|
+
fix: 'Run openspec store list to see registered stores.',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
...removed,
|
|
251
|
+
storeRoot: getStoreRootForBackend(removed.backend),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
export async function resolveRegisteredStore(input) {
|
|
255
|
+
const id = validateStoreId(input.id);
|
|
256
|
+
const registry = await readStoreRegistryState({
|
|
257
|
+
globalDataDir: input.globalDataDir,
|
|
258
|
+
});
|
|
259
|
+
if (!registry) {
|
|
260
|
+
throw new StoreError('No store registry found', 'no_store_registry', {
|
|
261
|
+
target: 'store.id',
|
|
262
|
+
fix: 'Register a store with openspec store register <path>, then select it with --store <id>.',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
const entry = getRegisteredStoreOrThrow(registry, id);
|
|
266
|
+
const backend = entry.backend;
|
|
267
|
+
const storeRoot = getStoreRootForBackend(backend);
|
|
268
|
+
await ensureStoreMetadata(storeRoot, id, { writeIfMissing: false });
|
|
269
|
+
return {
|
|
270
|
+
id,
|
|
271
|
+
storeRoot,
|
|
272
|
+
backend,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getApplyChangeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-apply-change',
|
|
4
5
|
description: 'Implement tasks from an OpenSpec change. Use when the user wants to start implementing, continue implementation, or work through tasks.',
|
|
5
6
|
instructions: `Implement tasks from an OpenSpec change.
|
|
6
7
|
|
|
8
|
+
${STORE_SELECTION_GUIDANCE}
|
|
9
|
+
|
|
7
10
|
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
8
11
|
|
|
9
12
|
**Steps**
|
|
@@ -43,8 +46,6 @@ export function getApplyChangeSkillTemplate() {
|
|
|
43
46
|
- If \`state: "all_done"\`: congratulate, suggest archive
|
|
44
47
|
- Otherwise: proceed to implementation
|
|
45
48
|
|
|
46
|
-
**Workspace guard:** If status JSON reports \`actionContext.mode: "workspace-planning"\` and \`allowedEditRoots\` is empty, explain that full workspace apply is not supported in this slice. Treat linked repos and folders as read-only context, ask the user to select an affected area through an explicit implementation workflow, and STOP before editing files.
|
|
47
|
-
|
|
48
49
|
4. **Read context files**
|
|
49
50
|
|
|
50
51
|
Read every file path listed under \`contextFiles\` from the apply instructions output.
|
|
@@ -163,6 +164,8 @@ export function getOpsxApplyCommandTemplate() {
|
|
|
163
164
|
tags: ['workflow', 'artifacts', 'experimental'],
|
|
164
165
|
content: `Implement tasks from an OpenSpec change.
|
|
165
166
|
|
|
167
|
+
${STORE_SELECTION_GUIDANCE}
|
|
168
|
+
|
|
166
169
|
**Input**: Optionally specify a change name (e.g., \`/opsx:apply add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
167
170
|
|
|
168
171
|
**Steps**
|
|
@@ -202,8 +205,6 @@ export function getOpsxApplyCommandTemplate() {
|
|
|
202
205
|
- If \`state: "all_done"\`: congratulate, suggest archive
|
|
203
206
|
- Otherwise: proceed to implementation
|
|
204
207
|
|
|
205
|
-
**Workspace guard:** If status JSON reports \`actionContext.mode: "workspace-planning"\` and \`allowedEditRoots\` is empty, explain that full workspace apply is not supported in this slice. Treat linked repos and folders as read-only context, ask the user to select an affected area through an explicit implementation workflow, and STOP before editing files.
|
|
206
|
-
|
|
207
208
|
4. **Read context files**
|
|
208
209
|
|
|
209
210
|
Read every file path listed under \`contextFiles\` from the apply instructions output.
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getArchiveChangeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-archive-change',
|
|
4
5
|
description: 'Archive a completed change in the experimental workflow. Use when the user wants to finalize and archive a change after implementation is complete.',
|
|
5
6
|
instructions: `Archive a completed change in the experimental workflow.
|
|
6
7
|
|
|
8
|
+
${STORE_SELECTION_GUIDANCE}
|
|
9
|
+
|
|
7
10
|
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
8
11
|
|
|
9
12
|
**Steps**
|
|
@@ -26,8 +29,6 @@ export function getArchiveChangeSkillTemplate() {
|
|
|
26
29
|
- \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context
|
|
27
30
|
- \`artifacts\`: List of artifacts with their status (\`done\` or other)
|
|
28
31
|
|
|
29
|
-
If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace archive is not supported in this slice and STOP. Do not move workspace changes into repo-local archives or edit linked repos.
|
|
30
|
-
|
|
31
32
|
**If any artifacts are not \`done\`:**
|
|
32
33
|
- Display warning listing incomplete artifacts
|
|
33
34
|
- Use **AskUserQuestion tool** to confirm user wants to proceed
|
|
@@ -121,6 +122,8 @@ export function getOpsxArchiveCommandTemplate() {
|
|
|
121
122
|
tags: ['workflow', 'archive', 'experimental'],
|
|
122
123
|
content: `Archive a completed change in the experimental workflow.
|
|
123
124
|
|
|
125
|
+
${STORE_SELECTION_GUIDANCE}
|
|
126
|
+
|
|
124
127
|
**Input**: Optionally specify a change name after \`/opsx:archive\` (e.g., \`/opsx:archive add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
125
128
|
|
|
126
129
|
**Steps**
|
|
@@ -143,8 +146,6 @@ export function getOpsxArchiveCommandTemplate() {
|
|
|
143
146
|
- \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`: path and scope context
|
|
144
147
|
- \`artifacts\`: List of artifacts with their status (\`done\` or other)
|
|
145
148
|
|
|
146
|
-
If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace archive is not supported in this slice and STOP. Do not move workspace changes into repo-local archives or edit linked repos.
|
|
147
|
-
|
|
148
149
|
**If any artifacts are not \`done\`:**
|
|
149
150
|
- Display warning listing incomplete artifacts
|
|
150
151
|
- Prompt user for confirmation to continue
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getBulkArchiveChangeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-bulk-archive-change',
|
|
@@ -6,6 +7,8 @@ export function getBulkArchiveChangeSkillTemplate() {
|
|
|
6
7
|
|
|
7
8
|
This skill allows you to batch-archive changes, handling spec conflicts intelligently by checking the codebase to determine what's actually implemented.
|
|
8
9
|
|
|
10
|
+
${STORE_SELECTION_GUIDANCE}
|
|
11
|
+
|
|
9
12
|
**Input**: None required (prompts for selection)
|
|
10
13
|
|
|
11
14
|
**Steps**
|
|
@@ -33,8 +36,6 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
|
|
|
33
36
|
- Parse \`schemaName\`, \`artifacts\`, \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`
|
|
34
37
|
- Note which artifacts are \`done\` vs other states
|
|
35
38
|
|
|
36
|
-
If any selected change reports \`actionContext.mode: "workspace-planning"\`, explain that workspace bulk archive is not supported in this slice and STOP before syncing specs or moving changes. Do not fall back to repo-local paths or edit linked repos.
|
|
37
|
-
|
|
38
39
|
b. **Task completion** - Read \`artifactPaths.tasks.existingOutputPaths\` from status JSON
|
|
39
40
|
- Count \`- [ ]\` (incomplete) vs \`- [x]\` (complete)
|
|
40
41
|
- If no tasks file exists, note as "No tasks"
|
|
@@ -254,6 +255,8 @@ export function getOpsxBulkArchiveCommandTemplate() {
|
|
|
254
255
|
|
|
255
256
|
This skill allows you to batch-archive changes, handling spec conflicts intelligently by checking the codebase to determine what's actually implemented.
|
|
256
257
|
|
|
258
|
+
${STORE_SELECTION_GUIDANCE}
|
|
259
|
+
|
|
257
260
|
**Input**: None required (prompts for selection)
|
|
258
261
|
|
|
259
262
|
**Steps**
|
|
@@ -281,8 +284,6 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
|
|
|
281
284
|
- Parse \`schemaName\`, \`artifacts\`, \`planningHome\`, \`changeRoot\`, \`artifactPaths\`, and \`actionContext\`
|
|
282
285
|
- Note which artifacts are \`done\` vs other states
|
|
283
286
|
|
|
284
|
-
If any selected change reports \`actionContext.mode: "workspace-planning"\`, explain that workspace bulk archive is not supported in this slice and STOP before syncing specs or moving changes. Do not fall back to repo-local paths or edit linked repos.
|
|
285
|
-
|
|
286
287
|
b. **Task completion** - Read \`artifactPaths.tasks.existingOutputPaths\` from status JSON
|
|
287
288
|
- Count \`- [ ]\` (incomplete) vs \`- [x]\` (complete)
|
|
288
289
|
- If no tasks file exists, note as "No tasks"
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getContinueChangeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-continue-change',
|
|
4
5
|
description: 'Continue working on an OpenSpec change by creating the next artifact. Use when the user wants to progress their change, create the next artifact, or continue their workflow.',
|
|
5
6
|
instructions: `Continue working on a change by creating the next artifact.
|
|
6
7
|
|
|
8
|
+
${STORE_SELECTION_GUIDANCE}
|
|
9
|
+
|
|
7
10
|
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
8
11
|
|
|
9
12
|
**Steps**
|
|
@@ -61,7 +64,7 @@ export function getContinueChangeSkillTemplate() {
|
|
|
61
64
|
- Read any completed dependency files for context
|
|
62
65
|
- Use \`template\` as the structure - fill in its sections
|
|
63
66
|
- Apply \`context\` and \`rules\` as constraints when writing - but do NOT copy them into the file
|
|
64
|
-
- Write to the \`resolvedOutputPath\` specified in instructions. If it is a glob pattern, choose the concrete file path using the schema instruction and
|
|
67
|
+
- Write to the \`resolvedOutputPath\` specified in instructions. If it is a glob pattern, choose the concrete file path using the schema instruction and the change's context
|
|
65
68
|
- Show what was created and what's now unlocked
|
|
66
69
|
- STOP after creating ONE artifact
|
|
67
70
|
|
|
@@ -123,6 +126,8 @@ export function getOpsxContinueCommandTemplate() {
|
|
|
123
126
|
tags: ['workflow', 'artifacts', 'experimental'],
|
|
124
127
|
content: `Continue working on a change by creating the next artifact.
|
|
125
128
|
|
|
129
|
+
${STORE_SELECTION_GUIDANCE}
|
|
130
|
+
|
|
126
131
|
**Input**: Optionally specify a change name after \`/opsx:continue\` (e.g., \`/opsx:continue add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
127
132
|
|
|
128
133
|
**Steps**
|
|
@@ -180,7 +185,7 @@ export function getOpsxContinueCommandTemplate() {
|
|
|
180
185
|
- Read any completed dependency files for context
|
|
181
186
|
- Use \`template\` as the structure - fill in its sections
|
|
182
187
|
- Apply \`context\` and \`rules\` as constraints when writing - but do NOT copy them into the file
|
|
183
|
-
- Write to the \`resolvedOutputPath\` specified in instructions. If it is a glob pattern, choose the concrete file path using the schema instruction and
|
|
188
|
+
- Write to the \`resolvedOutputPath\` specified in instructions. If it is a glob pattern, choose the concrete file path using the schema instruction and the change's context
|
|
184
189
|
- Show what was created and what's now unlocked
|
|
185
190
|
- STOP after creating ONE artifact
|
|
186
191
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getExploreSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-explore',
|
|
@@ -8,6 +9,8 @@ export function getExploreSkillTemplate() {
|
|
|
8
9
|
|
|
9
10
|
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
|
10
11
|
|
|
12
|
+
${STORE_SELECTION_GUIDANCE}
|
|
13
|
+
|
|
11
14
|
---
|
|
12
15
|
|
|
13
16
|
## The Stance
|
|
@@ -295,6 +298,8 @@ export function getOpsxExploreCommandTemplate() {
|
|
|
295
298
|
|
|
296
299
|
**This is a stance, not a workflow.** There are no fixed steps, no required sequence, no mandatory outputs. You're a thinking partner helping the user explore.
|
|
297
300
|
|
|
301
|
+
${STORE_SELECTION_GUIDANCE}
|
|
302
|
+
|
|
298
303
|
**Input**: The argument after \`/opsx:explore\` is whatever the user wants to think about. Could be:
|
|
299
304
|
- A vague idea: "real-time collaboration"
|
|
300
305
|
- A specific problem: "the auth system is getting unwieldy"
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getFfChangeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-ff-change',
|
|
4
5
|
description: 'Fast-forward through OpenSpec artifact creation. Use when the user wants to quickly create all artifacts needed for implementation without stepping through each one individually.',
|
|
5
6
|
instructions: `Fast-forward through artifact creation - generate everything needed to start implementation in one go.
|
|
6
7
|
|
|
8
|
+
${STORE_SELECTION_GUIDANCE}
|
|
9
|
+
|
|
7
10
|
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
|
8
11
|
|
|
9
12
|
**Steps**
|
|
@@ -106,6 +109,8 @@ export function getOpsxFfCommandTemplate() {
|
|
|
106
109
|
tags: ['workflow', 'artifacts', 'experimental'],
|
|
107
110
|
content: `Fast-forward through artifact creation - generate everything needed to start implementation.
|
|
108
111
|
|
|
112
|
+
${STORE_SELECTION_GUIDANCE}
|
|
113
|
+
|
|
109
114
|
**Input**: The argument after \`/opsx:ff\` is the change name (kebab-case), OR a description of what the user wants to build.
|
|
110
115
|
|
|
111
116
|
**Steps**
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getNewChangeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-new-change',
|
|
4
5
|
description: 'Start a new OpenSpec change using the experimental artifact workflow. Use when the user wants to create a new feature, fix, or modification with a structured step-by-step approach.',
|
|
5
6
|
instructions: `Start a new change using the experimental artifact-driven approach.
|
|
6
7
|
|
|
8
|
+
${STORE_SELECTION_GUIDANCE}
|
|
9
|
+
|
|
7
10
|
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
|
8
11
|
|
|
9
12
|
**Steps**
|
|
@@ -78,6 +81,8 @@ export function getOpsxNewCommandTemplate() {
|
|
|
78
81
|
tags: ['workflow', 'artifacts', 'experimental'],
|
|
79
82
|
content: `Start a new change using the experimental artifact-driven approach.
|
|
80
83
|
|
|
84
|
+
${STORE_SELECTION_GUIDANCE}
|
|
85
|
+
|
|
81
86
|
**Input**: The argument after \`/opsx:new\` is the change name (kebab-case), OR a description of what the user wants to build.
|
|
82
87
|
|
|
83
88
|
**Steps**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getOnboardSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-onboard',
|
|
@@ -11,6 +12,8 @@ export function getOnboardSkillTemplate() {
|
|
|
11
12
|
function getOnboardInstructions() {
|
|
12
13
|
return `Guide the user through their first complete OpenSpec workflow cycle. This is a teaching experience—you'll do real work in their codebase while explaining each step.
|
|
13
14
|
|
|
15
|
+
${STORE_SELECTION_GUIDANCE}
|
|
16
|
+
|
|
14
17
|
---
|
|
15
18
|
|
|
16
19
|
## Preflight
|
|
@@ -269,7 +272,7 @@ For a small task like this, we might only need one spec file.
|
|
|
269
272
|
**DO:** Resolve where the spec file should be created:
|
|
270
273
|
\`\`\`bash
|
|
271
274
|
openspec instructions specs --change "<name>" --json
|
|
272
|
-
# Use resolvedOutputPath from the JSON. If it is a glob, choose the concrete file path using the schema instruction and
|
|
275
|
+
# Use resolvedOutputPath from the JSON. If it is a glob, choose the concrete file path using the schema instruction and the change's context.
|
|
273
276
|
\`\`\`
|
|
274
277
|
|
|
275
278
|
Draft the spec content:
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getOpsxProposeSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-propose',
|
|
@@ -13,6 +14,8 @@ When ready to implement, run /opsx:apply
|
|
|
13
14
|
|
|
14
15
|
---
|
|
15
16
|
|
|
17
|
+
${STORE_SELECTION_GUIDANCE}
|
|
18
|
+
|
|
16
19
|
**Input**: The user's request should include a change name (kebab-case) OR a description of what they want to build.
|
|
17
20
|
|
|
18
21
|
**Steps**
|
|
@@ -124,6 +127,8 @@ When ready to implement, run /opsx:apply
|
|
|
124
127
|
|
|
125
128
|
---
|
|
126
129
|
|
|
130
|
+
${STORE_SELECTION_GUIDANCE}
|
|
131
|
+
|
|
127
132
|
**Input**: The argument after \`/opsx:propose\` is the change name (kebab-case), OR a description of what the user wants to build.
|
|
128
133
|
|
|
129
134
|
**Steps**
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared store-selection guidance for skill template workflows.
|
|
3
|
+
*
|
|
4
|
+
* Interpolated into every workflow's instructions so generated skills
|
|
5
|
+
* consistently teach how to target a registered store with `--store <id>`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const STORE_SELECTION_GUIDANCE = "**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run `openspec store list --json` to discover registered store ids, then pass `--store <id>` on the commands that read or write specs and changes (`new change`, `status`, `instructions`, `list`, `show`, `validate`, `archive`, `doctor`, `context`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local `openspec/` root.";
|
|
8
|
+
//# sourceMappingURL=store-selection.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared store-selection guidance for skill template workflows.
|
|
3
|
+
*
|
|
4
|
+
* Interpolated into every workflow's instructions so generated skills
|
|
5
|
+
* consistently teach how to target a registered store with `--store <id>`.
|
|
6
|
+
*/
|
|
7
|
+
export const STORE_SELECTION_GUIDANCE = `**Store selection:** If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run \`openspec store list --json\` to discover registered store ids, then pass \`--store <id>\` on the commands that read or write specs and changes (\`new change\`, \`status\`, \`instructions\`, \`list\`, \`show\`, \`validate\`, \`archive\`, \`doctor\`, \`context\`). Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local \`openspec/\` root.`;
|
|
8
|
+
//# sourceMappingURL=store-selection.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { STORE_SELECTION_GUIDANCE } from './store-selection.js';
|
|
1
2
|
export function getSyncSpecsSkillTemplate() {
|
|
2
3
|
return {
|
|
3
4
|
name: 'openspec-sync-specs',
|
|
@@ -6,6 +7,8 @@ export function getSyncSpecsSkillTemplate() {
|
|
|
6
7
|
|
|
7
8
|
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
|
8
9
|
|
|
10
|
+
${STORE_SELECTION_GUIDANCE}
|
|
11
|
+
|
|
9
12
|
**Input**: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
10
13
|
|
|
11
14
|
**Steps**
|
|
@@ -25,8 +28,6 @@ This is an **agent-driven** operation - you will read delta specs and directly e
|
|
|
25
28
|
openspec status --change "<name>" --json
|
|
26
29
|
\`\`\`
|
|
27
30
|
|
|
28
|
-
If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace spec sync is not supported in this slice and STOP. Do not fall back to repo-local paths or edit linked repos.
|
|
29
|
-
|
|
30
31
|
3. **Find delta specs**
|
|
31
32
|
|
|
32
33
|
Use \`artifactPaths.specs.existingOutputPaths\` from the status JSON as the list of delta spec files.
|
|
@@ -153,6 +154,8 @@ export function getOpsxSyncCommandTemplate() {
|
|
|
153
154
|
|
|
154
155
|
This is an **agent-driven** operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
|
|
155
156
|
|
|
157
|
+
${STORE_SELECTION_GUIDANCE}
|
|
158
|
+
|
|
156
159
|
**Input**: Optionally specify a change name after \`/opsx:sync\` (e.g., \`/opsx:sync add-auth\`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
157
160
|
|
|
158
161
|
**Steps**
|
|
@@ -172,8 +175,6 @@ This is an **agent-driven** operation - you will read delta specs and directly e
|
|
|
172
175
|
openspec status --change "<name>" --json
|
|
173
176
|
\`\`\`
|
|
174
177
|
|
|
175
|
-
If status reports \`actionContext.mode: "workspace-planning"\`, explain that workspace spec sync is not supported in this slice and STOP. Do not fall back to repo-local paths or edit linked repos.
|
|
176
|
-
|
|
177
178
|
3. **Find delta specs**
|
|
178
179
|
|
|
179
180
|
Use \`artifactPaths.specs.existingOutputPaths\` from the status JSON as the list of delta spec files.
|