@fission-ai/openspec 1.3.0 → 1.4.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.
Files changed (179) hide show
  1. package/README.md +9 -6
  2. package/bin/openspec.js +3 -1
  3. package/dist/cli/index.d.ts +4 -1
  4. package/dist/cli/index.js +46 -2
  5. package/dist/commands/completion.js +7 -0
  6. package/dist/commands/config.js +64 -5
  7. package/dist/commands/context-store.d.ts +3 -0
  8. package/dist/commands/context-store.js +475 -0
  9. package/dist/commands/initiative.d.ts +13 -0
  10. package/dist/commands/initiative.js +318 -0
  11. package/dist/commands/workflow/index.d.ts +2 -0
  12. package/dist/commands/workflow/index.js +1 -0
  13. package/dist/commands/workflow/initiative-link.d.ts +24 -0
  14. package/dist/commands/workflow/initiative-link.js +47 -0
  15. package/dist/commands/workflow/instructions.d.ts +1 -1
  16. package/dist/commands/workflow/instructions.js +46 -83
  17. package/dist/commands/workflow/new-change.d.ts +6 -0
  18. package/dist/commands/workflow/new-change.js +114 -17
  19. package/dist/commands/workflow/set-change.d.ts +13 -0
  20. package/dist/commands/workflow/set-change.js +87 -0
  21. package/dist/commands/workflow/shared.d.ts +5 -3
  22. package/dist/commands/workflow/shared.js +6 -6
  23. package/dist/commands/workflow/status.js +24 -9
  24. package/dist/commands/workflow/templates.js +5 -4
  25. package/dist/commands/workspace/context-status.d.ts +4 -0
  26. package/dist/commands/workspace/context-status.js +59 -0
  27. package/dist/commands/workspace/open-target-selection.d.ts +13 -0
  28. package/dist/commands/workspace/open-target-selection.js +146 -0
  29. package/dist/commands/workspace/open-view.d.ts +62 -0
  30. package/dist/commands/workspace/open-view.js +249 -0
  31. package/dist/commands/workspace/open.d.ts +37 -0
  32. package/dist/commands/workspace/open.js +110 -0
  33. package/dist/commands/workspace/opener-selection.d.ts +11 -0
  34. package/dist/commands/workspace/opener-selection.js +98 -0
  35. package/dist/commands/workspace/operations.d.ts +29 -0
  36. package/dist/commands/workspace/operations.js +543 -0
  37. package/dist/commands/workspace/prompt-theme.d.ts +29 -0
  38. package/dist/commands/workspace/prompt-theme.js +24 -0
  39. package/dist/commands/workspace/registration.d.ts +13 -0
  40. package/dist/commands/workspace/registration.js +84 -0
  41. package/dist/commands/workspace/selection.d.ts +9 -0
  42. package/dist/commands/workspace/selection.js +115 -0
  43. package/dist/commands/workspace/setup-prompts.d.ts +13 -0
  44. package/dist/commands/workspace/setup-prompts.js +121 -0
  45. package/dist/commands/workspace/types.d.ts +103 -0
  46. package/dist/commands/workspace/types.js +36 -0
  47. package/dist/commands/workspace.d.ts +6 -0
  48. package/dist/commands/workspace.js +587 -0
  49. package/dist/core/artifact-graph/index.d.ts +3 -1
  50. package/dist/core/artifact-graph/index.js +1 -0
  51. package/dist/core/artifact-graph/instruction-loader.d.ts +41 -1
  52. package/dist/core/artifact-graph/instruction-loader.js +51 -9
  53. package/dist/core/artifact-graph/outputs.d.ts +14 -0
  54. package/dist/core/artifact-graph/outputs.js +39 -0
  55. package/dist/core/artifact-graph/state.js +2 -25
  56. package/dist/core/artifact-graph/types.d.ts +0 -5
  57. package/dist/core/artifact-graph/types.js +0 -14
  58. package/dist/core/change-metadata/index.d.ts +2 -0
  59. package/dist/core/change-metadata/index.js +2 -0
  60. package/dist/core/change-metadata/schema.d.ts +18 -0
  61. package/dist/core/change-metadata/schema.js +28 -0
  62. package/dist/core/change-status-policy.d.ts +50 -0
  63. package/dist/core/change-status-policy.js +70 -0
  64. package/dist/core/collections/index.d.ts +3 -0
  65. package/dist/core/collections/index.js +3 -0
  66. package/dist/core/collections/initiatives/collection.d.ts +4 -0
  67. package/dist/core/collections/initiatives/collection.js +17 -0
  68. package/dist/core/collections/initiatives/index.d.ts +6 -0
  69. package/dist/core/collections/initiatives/index.js +6 -0
  70. package/dist/core/collections/initiatives/operations.d.ts +49 -0
  71. package/dist/core/collections/initiatives/operations.js +175 -0
  72. package/dist/core/collections/initiatives/resolution.d.ts +87 -0
  73. package/dist/core/collections/initiatives/resolution.js +374 -0
  74. package/dist/core/collections/initiatives/schema.d.ts +41 -0
  75. package/dist/core/collections/initiatives/schema.js +134 -0
  76. package/dist/core/collections/initiatives/templates.d.ts +12 -0
  77. package/dist/core/collections/initiatives/templates.js +90 -0
  78. package/dist/core/collections/runtime.d.ts +46 -0
  79. package/dist/core/collections/runtime.js +194 -0
  80. package/dist/core/completions/command-registry.d.ts +1 -5
  81. package/dist/core/completions/command-registry.js +532 -32
  82. package/dist/core/completions/completion-provider.d.ts +11 -0
  83. package/dist/core/completions/completion-provider.js +27 -0
  84. package/dist/core/completions/generators/bash-generator.d.ts +3 -0
  85. package/dist/core/completions/generators/bash-generator.js +60 -4
  86. package/dist/core/completions/generators/fish-generator.js +3 -0
  87. package/dist/core/completions/generators/powershell-generator.d.ts +3 -0
  88. package/dist/core/completions/generators/powershell-generator.js +63 -4
  89. package/dist/core/completions/generators/zsh-generator.d.ts +3 -0
  90. package/dist/core/completions/generators/zsh-generator.js +46 -22
  91. package/dist/core/completions/installers/zsh-installer.d.ts +0 -8
  92. package/dist/core/completions/installers/zsh-installer.js +3 -31
  93. package/dist/core/completions/shared-flags.d.ts +12 -0
  94. package/dist/core/completions/shared-flags.js +28 -0
  95. package/dist/core/completions/templates/bash-templates.d.ts +1 -1
  96. package/dist/core/completions/templates/bash-templates.js +6 -0
  97. package/dist/core/completions/templates/fish-templates.d.ts +1 -1
  98. package/dist/core/completions/templates/fish-templates.js +6 -0
  99. package/dist/core/completions/templates/powershell-templates.d.ts +1 -1
  100. package/dist/core/completions/templates/powershell-templates.js +9 -0
  101. package/dist/core/completions/templates/zsh-templates.d.ts +1 -1
  102. package/dist/core/completions/templates/zsh-templates.js +9 -0
  103. package/dist/core/completions/types.d.ts +23 -1
  104. package/dist/core/config.js +3 -1
  105. package/dist/core/context-store/binding.d.ts +53 -0
  106. package/dist/core/context-store/binding.js +197 -0
  107. package/dist/core/context-store/errors.d.ts +20 -0
  108. package/dist/core/context-store/errors.js +22 -0
  109. package/dist/core/context-store/foundation.d.ts +55 -0
  110. package/dist/core/context-store/foundation.js +321 -0
  111. package/dist/core/context-store/index.d.ts +6 -0
  112. package/dist/core/context-store/index.js +6 -0
  113. package/dist/core/context-store/operations.d.ts +85 -0
  114. package/dist/core/context-store/operations.js +528 -0
  115. package/dist/core/context-store/registry.d.ts +45 -0
  116. package/dist/core/context-store/registry.js +229 -0
  117. package/dist/core/global-config.d.ts +6 -1
  118. package/dist/core/global-config.js +15 -16
  119. package/dist/core/index.d.ts +5 -1
  120. package/dist/core/index.js +4 -0
  121. package/dist/core/parsers/change-parser.js +7 -3
  122. package/dist/core/parsers/markdown-parser.d.ts +4 -0
  123. package/dist/core/parsers/markdown-parser.js +41 -1
  124. package/dist/core/parsers/requirement-blocks.js +5 -5
  125. package/dist/core/parsers/spec-structure.d.ts +9 -0
  126. package/dist/core/parsers/spec-structure.js +88 -0
  127. package/dist/core/planning-home.d.ts +21 -0
  128. package/dist/core/planning-home.js +108 -0
  129. package/dist/core/profiles.d.ts +1 -1
  130. package/dist/core/profiles.js +1 -1
  131. package/dist/core/specs-apply.js +9 -1
  132. package/dist/core/templates/workflows/apply-change.js +10 -4
  133. package/dist/core/templates/workflows/archive-change.js +21 -15
  134. package/dist/core/templates/workflows/bulk-archive-change.js +14 -10
  135. package/dist/core/templates/workflows/continue-change.js +6 -4
  136. package/dist/core/templates/workflows/explore.js +8 -10
  137. package/dist/core/templates/workflows/ff-change.js +8 -6
  138. package/dist/core/templates/workflows/new-change.js +6 -6
  139. package/dist/core/templates/workflows/onboard.js +13 -15
  140. package/dist/core/templates/workflows/propose.js +8 -6
  141. package/dist/core/templates/workflows/sync-specs.js +28 -10
  142. package/dist/core/templates/workflows/verify-change.js +16 -10
  143. package/dist/core/update.d.ts +5 -0
  144. package/dist/core/update.js +20 -0
  145. package/dist/core/validation/validator.d.ts +11 -0
  146. package/dist/core/validation/validator.js +28 -2
  147. package/dist/core/workspace/foundation.d.ts +67 -0
  148. package/dist/core/workspace/foundation.js +295 -0
  149. package/dist/core/workspace/index.d.ts +8 -0
  150. package/dist/core/workspace/index.js +8 -0
  151. package/dist/core/workspace/legacy-state.d.ts +28 -0
  152. package/dist/core/workspace/legacy-state.js +200 -0
  153. package/dist/core/workspace/link-input.d.ts +9 -0
  154. package/dist/core/workspace/link-input.js +32 -0
  155. package/dist/core/workspace/open-surface.d.ts +45 -0
  156. package/dist/core/workspace/open-surface.js +215 -0
  157. package/dist/core/workspace/openers.d.ts +21 -0
  158. package/dist/core/workspace/openers.js +124 -0
  159. package/dist/core/workspace/registry.d.ts +24 -0
  160. package/dist/core/workspace/registry.js +146 -0
  161. package/dist/core/workspace/skills.d.ts +57 -0
  162. package/dist/core/workspace/skills.js +334 -0
  163. package/dist/core/workspace/state-io.d.ts +10 -0
  164. package/dist/core/workspace/state-io.js +119 -0
  165. package/dist/telemetry/config.d.ts +7 -1
  166. package/dist/telemetry/config.js +87 -19
  167. package/dist/telemetry/index.js +19 -0
  168. package/dist/utils/change-metadata.d.ts +5 -2
  169. package/dist/utils/change-metadata.js +7 -13
  170. package/dist/utils/change-utils.d.ts +9 -0
  171. package/dist/utils/change-utils.js +7 -5
  172. package/dist/utils/file-system.d.ts +5 -0
  173. package/dist/utils/file-system.js +21 -1
  174. package/package.json +2 -1
  175. package/schemas/workspace-planning/schema.yaml +72 -0
  176. package/schemas/workspace-planning/templates/design.md +33 -0
  177. package/schemas/workspace-planning/templates/proposal.md +28 -0
  178. package/schemas/workspace-planning/templates/spec.md +9 -0
  179. package/schemas/workspace-planning/templates/tasks.md +15 -0
@@ -0,0 +1,175 @@
1
+ import * as nodeFs from 'node:fs';
2
+ import { INITIATIVE_COLLECTION_ID, INITIATIVE_FILE_NAME, parseInitiativeState, serializeInitiativeState, validateInitiativeId, } from './schema.js';
3
+ import { buildDefaultInitiativeFiles, } from './templates.js';
4
+ const fs = nodeFs.promises;
5
+ const nodeFileSystem = {
6
+ async mkdir(dirPath, options) {
7
+ await fs.mkdir(dirPath, options);
8
+ },
9
+ async writeFile(filePath, content, options) {
10
+ await fs.writeFile(filePath, content, {
11
+ encoding: 'utf-8',
12
+ flag: options.flag ?? 'w',
13
+ });
14
+ },
15
+ async readFile(filePath) {
16
+ return fs.readFile(filePath, 'utf-8');
17
+ },
18
+ async readdir(dirPath, options) {
19
+ return fs.readdir(dirPath, options);
20
+ },
21
+ async rm(dirPath, options) {
22
+ await fs.rm(dirPath, options);
23
+ },
24
+ };
25
+ function getCurrentDate() {
26
+ return new Date().toISOString().split('T')[0];
27
+ }
28
+ function getFileSystem(fileSystem) {
29
+ return fileSystem ?? nodeFileSystem;
30
+ }
31
+ function isFileNotFoundError(error) {
32
+ return (typeof error === 'object' &&
33
+ error !== null &&
34
+ 'code' in error &&
35
+ error.code === 'ENOENT');
36
+ }
37
+ function isPathExistsError(error) {
38
+ return (typeof error === 'object' &&
39
+ error !== null &&
40
+ 'code' in error &&
41
+ error.code === 'EEXIST');
42
+ }
43
+ function errorMessage(error) {
44
+ return error instanceof Error ? error.message : String(error);
45
+ }
46
+ function assertInitiativesCollection(collection) {
47
+ if (collection.collectionId !== INITIATIVE_COLLECTION_ID) {
48
+ throw new Error(`Expected mounted '${INITIATIVE_COLLECTION_ID}' collection, got '${collection.collectionId}'`);
49
+ }
50
+ }
51
+ function resolveInitiativeFilePath(collection, initiativeId, fileName) {
52
+ return collection.resolvePath(`${initiativeId}/${fileName}`);
53
+ }
54
+ function normalizeCreateState(input) {
55
+ return parseInitiativeState(serializeInitiativeState({
56
+ version: 1,
57
+ id: validateInitiativeId(input.id),
58
+ title: input.title,
59
+ summary: input.summary,
60
+ status: input.status ?? 'exploring',
61
+ created: (input.getCurrentDate ?? getCurrentDate)(),
62
+ owners: input.owners ?? [],
63
+ metadata: input.metadata ?? {},
64
+ }));
65
+ }
66
+ async function writeExclusiveFile(fileSystem, filePath, content) {
67
+ await fileSystem.writeFile(filePath, content, { flag: 'wx' });
68
+ }
69
+ async function cleanupCreatedInitiative(fileSystem, initiativeRoot, originalError, initiativeId) {
70
+ try {
71
+ await fileSystem.rm(initiativeRoot, { recursive: true, force: true });
72
+ }
73
+ catch (cleanupError) {
74
+ throw new Error(`Failed to create initiative '${initiativeId}' and cleanup failed: ${errorMessage(originalError)}; cleanup: ${errorMessage(cleanupError)}`);
75
+ }
76
+ throw new Error(`Failed to create initiative '${initiativeId}': ${errorMessage(originalError)}`);
77
+ }
78
+ export async function createInitiative(input) {
79
+ assertInitiativesCollection(input.collection);
80
+ const state = normalizeCreateState(input);
81
+ const fileSystem = getFileSystem(input.fileSystem);
82
+ const initiativeRoot = input.collection.resolvePath(state.id);
83
+ const buildTemplateFiles = input.buildTemplateFiles ?? buildDefaultInitiativeFiles;
84
+ try {
85
+ await fileSystem.mkdir(input.collection.resolvePath(), { recursive: true });
86
+ await fileSystem.mkdir(initiativeRoot, { recursive: false });
87
+ }
88
+ catch (error) {
89
+ if (isPathExistsError(error)) {
90
+ throw new Error(`Initiative '${state.id}' already exists at ${initiativeRoot}`);
91
+ }
92
+ throw new Error(`Failed to create initiative '${state.id}': ${errorMessage(error)}`);
93
+ }
94
+ try {
95
+ await writeExclusiveFile(fileSystem, resolveInitiativeFilePath(input.collection, state.id, INITIATIVE_FILE_NAME), serializeInitiativeState(state));
96
+ for (const templateFile of buildTemplateFiles(state)) {
97
+ await writeExclusiveFile(fileSystem, resolveInitiativeFilePath(input.collection, state.id, templateFile.fileName), templateFile.content);
98
+ }
99
+ }
100
+ catch (error) {
101
+ await cleanupCreatedInitiative(fileSystem, initiativeRoot, error, state.id);
102
+ }
103
+ return state;
104
+ }
105
+ export async function readInitiative(input) {
106
+ assertInitiativesCollection(input.collection);
107
+ const initiativeId = validateInitiativeId(input.id);
108
+ const fileSystem = getFileSystem(input.fileSystem);
109
+ const initiativeFilePath = resolveInitiativeFilePath(input.collection, initiativeId, INITIATIVE_FILE_NAME);
110
+ let content;
111
+ try {
112
+ content = await fileSystem.readFile(initiativeFilePath);
113
+ }
114
+ catch (error) {
115
+ if (isFileNotFoundError(error)) {
116
+ return null;
117
+ }
118
+ throw new Error(`Invalid initiative '${initiativeId}': failed to read ${INITIATIVE_FILE_NAME}: ${errorMessage(error)}`);
119
+ }
120
+ let state;
121
+ try {
122
+ state = parseInitiativeState(content);
123
+ }
124
+ catch (error) {
125
+ throw new Error(`Invalid initiative '${initiativeId}': ${errorMessage(error)}`);
126
+ }
127
+ if (state.id !== initiativeId) {
128
+ throw new Error(`Invalid initiative '${initiativeId}': ${INITIATIVE_FILE_NAME} id '${state.id}' must match folder name`);
129
+ }
130
+ return state;
131
+ }
132
+ export async function listInitiatives(input) {
133
+ assertInitiativesCollection(input.collection);
134
+ const fileSystem = getFileSystem(input.fileSystem);
135
+ let entries;
136
+ try {
137
+ entries = await fileSystem.readdir(input.collection.resolvePath(), { withFileTypes: true });
138
+ }
139
+ catch (error) {
140
+ if (isFileNotFoundError(error)) {
141
+ return [];
142
+ }
143
+ throw new Error(`Failed to list initiatives: ${errorMessage(error)}`);
144
+ }
145
+ const initiatives = [];
146
+ for (const entry of entries) {
147
+ if (!entry.isDirectory()) {
148
+ continue;
149
+ }
150
+ const initiativeFilePath = resolveInitiativeFilePath(input.collection, entry.name, INITIATIVE_FILE_NAME);
151
+ let content;
152
+ try {
153
+ content = await fileSystem.readFile(initiativeFilePath);
154
+ }
155
+ catch (error) {
156
+ if (isFileNotFoundError(error)) {
157
+ continue;
158
+ }
159
+ throw new Error(`Invalid initiative '${entry.name}': failed to read ${INITIATIVE_FILE_NAME}: ${errorMessage(error)}`);
160
+ }
161
+ let state;
162
+ try {
163
+ state = parseInitiativeState(content);
164
+ }
165
+ catch (error) {
166
+ throw new Error(`Invalid initiative '${entry.name}': ${errorMessage(error)}`);
167
+ }
168
+ if (state.id !== entry.name) {
169
+ throw new Error(`Invalid initiative '${entry.name}': ${INITIATIVE_FILE_NAME} id '${state.id}' must match folder name`);
170
+ }
171
+ initiatives.push(state);
172
+ }
173
+ return initiatives.sort((a, b) => a.id.localeCompare(b.id));
174
+ }
175
+ //# sourceMappingURL=operations.js.map
@@ -0,0 +1,87 @@
1
+ import { formatContextStoreSelector, type ContextStoreSelectorOptions, type ContextStoreSelectorSource, type SelectedContextStore } from '../../context-store/index.js';
2
+ import { type InitiativeState } from './schema.js';
3
+ export interface InitiativeSelectorOptions extends ContextStoreSelectorOptions {
4
+ json?: boolean;
5
+ }
6
+ export type { ContextStoreSelectorSource, SelectedContextStore };
7
+ export { formatContextStoreSelector };
8
+ export interface InitiativeResolutionMatch {
9
+ context_store: {
10
+ id: string;
11
+ root: string;
12
+ };
13
+ initiative: {
14
+ id: string;
15
+ title: string;
16
+ root: string;
17
+ };
18
+ }
19
+ export interface InitiativeResolutionDetails extends Record<string, unknown> {
20
+ matches?: InitiativeResolutionMatch[];
21
+ }
22
+ export declare class InitiativeResolutionError extends Error {
23
+ readonly code: string;
24
+ readonly target?: string;
25
+ readonly fix?: string;
26
+ readonly details?: InitiativeResolutionDetails;
27
+ constructor(message: string, code: string, options?: {
28
+ target?: string;
29
+ fix?: string;
30
+ details?: InitiativeResolutionDetails;
31
+ });
32
+ }
33
+ export interface InitiativeViewReference {
34
+ store: string;
35
+ storeSource: ContextStoreSelectorSource;
36
+ storeRoot: string;
37
+ id: string;
38
+ title: string;
39
+ summary: string;
40
+ created: string;
41
+ root: string;
42
+ storePath: string;
43
+ metadataPath: string;
44
+ }
45
+ export interface ListedInitiativeReference extends InitiativeViewReference {
46
+ status: InitiativeState['status'];
47
+ owners: InitiativeState['owners'];
48
+ metadata: InitiativeState['metadata'];
49
+ }
50
+ export type InitiativeDiagnosticSeverity = 'error' | 'warning';
51
+ export interface InitiativeDiagnostic {
52
+ severity: InitiativeDiagnosticSeverity;
53
+ code: string;
54
+ message: string;
55
+ target?: string;
56
+ fix?: string;
57
+ details?: InitiativeResolutionDetails;
58
+ }
59
+ export interface ContextStoreInitiativeListReference {
60
+ contextStore: SelectedContextStore;
61
+ initiatives: ListedInitiativeReference[];
62
+ status: InitiativeDiagnostic[];
63
+ }
64
+ export interface InitiativeListReferenceResult {
65
+ contextStore: SelectedContextStore | null;
66
+ contextStores: ContextStoreInitiativeListReference[];
67
+ initiatives: ListedInitiativeReference[];
68
+ status: InitiativeDiagnostic[];
69
+ }
70
+ export declare function initiativeDiagnosticFromError(error: unknown): InitiativeDiagnostic;
71
+ export declare function parseInitiativeReference(reference: string | undefined, options: InitiativeSelectorOptions): {
72
+ initiativeId: string;
73
+ options: InitiativeSelectorOptions;
74
+ };
75
+ export declare function resolveRegisteredInitiativeContextStore(storeId: string): Promise<SelectedContextStore>;
76
+ export declare function resolvePathInitiativeContextStore(storePath: string): Promise<SelectedContextStore>;
77
+ export declare function selectContextStoreForInitiative(options: InitiativeSelectorOptions, commandName: 'create' | 'list' | 'show'): Promise<SelectedContextStore>;
78
+ export declare function resolveSelectedInitiativeViewReference(selected: SelectedContextStore, initiativeId: string): Promise<InitiativeViewReference>;
79
+ export declare function listSelectedInitiativeViewReferences(selected: SelectedContextStore): Promise<ContextStoreInitiativeListReference>;
80
+ export declare function listInitiativeViewReferences(options?: InitiativeSelectorOptions): Promise<InitiativeListReferenceResult>;
81
+ export declare function resolveInitiativeViewReference(reference: string | undefined, options?: InitiativeSelectorOptions): Promise<InitiativeViewReference>;
82
+ export interface InitiativeLinkReference {
83
+ store: string;
84
+ id: string;
85
+ }
86
+ export declare function resolveInitiativeLinkReference(reference: string | undefined, options?: InitiativeSelectorOptions): Promise<InitiativeLinkReference>;
87
+ //# sourceMappingURL=resolution.d.ts.map
@@ -0,0 +1,374 @@
1
+ import { ContextStoreError, formatContextStoreSelector, listRegisteredContextStores, resolveSelectedContextStore, } from '../../context-store/index.js';
2
+ import { mountInitiativesCollection } from './collection.js';
3
+ import { listInitiatives, readInitiative } from './operations.js';
4
+ import { INITIATIVE_FILE_NAME } from './schema.js';
5
+ export { formatContextStoreSelector };
6
+ export class InitiativeResolutionError extends Error {
7
+ code;
8
+ target;
9
+ fix;
10
+ details;
11
+ constructor(message, code, options = {}) {
12
+ super(message);
13
+ this.code = code;
14
+ this.target = options.target;
15
+ this.fix = options.fix;
16
+ this.details = options.details;
17
+ }
18
+ }
19
+ function asErrorMessage(error) {
20
+ return error instanceof Error ? error.message : String(error);
21
+ }
22
+ function makeDiagnostic(severity, code, message, options = {}) {
23
+ return {
24
+ severity,
25
+ code,
26
+ message,
27
+ ...options,
28
+ };
29
+ }
30
+ const INITIATIVE_ALREADY_EXISTS_PREFIX = "Initiative '";
31
+ const INITIATIVE_ALREADY_EXISTS_MARKER = "' already exists";
32
+ export function initiativeDiagnosticFromError(error) {
33
+ if (error instanceof InitiativeResolutionError) {
34
+ return makeDiagnostic('error', error.code, error.message, {
35
+ target: error.target,
36
+ fix: error.fix,
37
+ details: error.details,
38
+ });
39
+ }
40
+ const message = asErrorMessage(error);
41
+ if (message.startsWith(INITIATIVE_ALREADY_EXISTS_PREFIX) &&
42
+ message.includes(INITIATIVE_ALREADY_EXISTS_MARKER, INITIATIVE_ALREADY_EXISTS_PREFIX.length)) {
43
+ return makeDiagnostic('error', 'initiative_already_exists', message, {
44
+ target: 'initiative.id',
45
+ fix: 'Choose a new initiative id or list existing initiatives first.',
46
+ });
47
+ }
48
+ if (message.startsWith('Initiative id ')) {
49
+ return makeDiagnostic('error', 'invalid_initiative_id', message, {
50
+ target: 'initiative.id',
51
+ fix: 'Use kebab-case with lowercase letters, numbers, and single hyphen separators.',
52
+ });
53
+ }
54
+ if (message.startsWith('Invalid initiative')) {
55
+ return makeDiagnostic('error', 'invalid_initiative', message, {
56
+ target: 'initiative',
57
+ fix: 'Fix the initiative folder state and retry.',
58
+ });
59
+ }
60
+ return makeDiagnostic('error', 'initiative_error', message);
61
+ }
62
+ function requireInitiativeId(id, commandName) {
63
+ if (id === undefined || id.trim().length === 0) {
64
+ throw new InitiativeResolutionError('Pass an initiative id.', 'initiative_id_required', {
65
+ target: 'initiative.id',
66
+ fix: `openspec initiative ${commandName} <id>`,
67
+ });
68
+ }
69
+ return id.trim();
70
+ }
71
+ export function parseInitiativeReference(reference, options) {
72
+ const initiativeId = requireInitiativeId(reference, 'show');
73
+ const parts = initiativeId.split('/');
74
+ if (parts.length === 1) {
75
+ return { initiativeId, options };
76
+ }
77
+ if (parts.length !== 2 || parts[0].length === 0 || parts[1].length === 0) {
78
+ throw new InitiativeResolutionError(`Invalid initiative reference '${initiativeId}'.`, 'invalid_initiative_reference', {
79
+ target: 'initiative.id',
80
+ fix: 'Use <initiative-id>, <store>/<initiative-id>, or <initiative-id> --store <store>.',
81
+ });
82
+ }
83
+ if (options.store !== undefined || options.storePath !== undefined) {
84
+ throw new InitiativeResolutionError('Pass either --initiative <store>/<id> or a context store selector, not both.', 'context_store_selector_conflict', {
85
+ target: 'context_store',
86
+ fix: 'Use --initiative <store>/<id> or --initiative <id> --store <store>.',
87
+ });
88
+ }
89
+ return {
90
+ initiativeId: parts[1],
91
+ options: {
92
+ ...options,
93
+ store: parts[0],
94
+ },
95
+ };
96
+ }
97
+ function contextStoreErrorAsInitiativeError(error) {
98
+ if (error instanceof ContextStoreError) {
99
+ return new InitiativeResolutionError(error.message, error.diagnostic.code, {
100
+ target: error.diagnostic.target,
101
+ fix: error.diagnostic.fix,
102
+ });
103
+ }
104
+ const message = asErrorMessage(error);
105
+ if (message.startsWith('Context store id ')) {
106
+ return new InitiativeResolutionError(message, 'invalid_context_store_id', {
107
+ target: 'context_store.id',
108
+ fix: 'Use kebab-case with lowercase letters, numbers, and single hyphen separators.',
109
+ });
110
+ }
111
+ return new InitiativeResolutionError(message, 'invalid_context_store', {
112
+ target: 'context_store',
113
+ fix: 'Fix the context store registry or pass --store-path <path>.',
114
+ });
115
+ }
116
+ export async function resolveRegisteredInitiativeContextStore(storeId) {
117
+ return selectContextStoreForInitiative({ store: storeId }, 'show');
118
+ }
119
+ export async function resolvePathInitiativeContextStore(storePath) {
120
+ return selectContextStoreForInitiative({ storePath }, 'show');
121
+ }
122
+ export async function selectContextStoreForInitiative(options, commandName) {
123
+ try {
124
+ return await resolveSelectedContextStore(options, `initiative ${commandName}`);
125
+ }
126
+ catch (error) {
127
+ throw contextStoreErrorAsInitiativeError(error);
128
+ }
129
+ }
130
+ function toInitiativeViewReference(selected, state) {
131
+ const collection = mountInitiativesCollection(selected.root);
132
+ return {
133
+ store: selected.id,
134
+ storeSource: selected.source,
135
+ storeRoot: selected.root,
136
+ id: state.id,
137
+ title: state.title,
138
+ summary: state.summary,
139
+ created: state.created,
140
+ root: collection.resolvePath(state.id),
141
+ storePath: collection.toStorePath(state.id),
142
+ metadataPath: collection.resolvePath(`${state.id}/${INITIATIVE_FILE_NAME}`),
143
+ };
144
+ }
145
+ function toResolutionMatch(selected, state) {
146
+ const reference = toInitiativeViewReference(selected, state);
147
+ return {
148
+ context_store: {
149
+ id: reference.store,
150
+ root: reference.storeRoot,
151
+ },
152
+ initiative: {
153
+ id: reference.id,
154
+ title: reference.title,
155
+ root: reference.root,
156
+ },
157
+ };
158
+ }
159
+ function toListedInitiativeReference(selected, state) {
160
+ return {
161
+ ...toInitiativeViewReference(selected, state),
162
+ status: state.status,
163
+ owners: state.owners,
164
+ metadata: state.metadata,
165
+ };
166
+ }
167
+ async function readSelectedInitiative(selected, initiativeId) {
168
+ return readInitiative({
169
+ collection: mountInitiativesCollection(selected.root),
170
+ id: initiativeId,
171
+ });
172
+ }
173
+ export async function resolveSelectedInitiativeViewReference(selected, initiativeId) {
174
+ const state = await readSelectedInitiative(selected, initiativeId);
175
+ if (!state) {
176
+ throw new InitiativeResolutionError(`Initiative '${initiativeId}' was not found in context store '${selected.id}'.`, 'initiative_not_found', {
177
+ target: 'initiative.id',
178
+ fix: `openspec initiative list ${formatContextStoreSelector(selected)}`,
179
+ });
180
+ }
181
+ return toInitiativeViewReference(selected, state);
182
+ }
183
+ export async function listSelectedInitiativeViewReferences(selected) {
184
+ const collection = mountInitiativesCollection(selected.root);
185
+ const initiatives = await listInitiatives({ collection });
186
+ return {
187
+ contextStore: selected,
188
+ initiatives: initiatives.map((initiative) => toListedInitiativeReference(selected, initiative)),
189
+ status: [],
190
+ };
191
+ }
192
+ async function scanRegisteredStoreForInitiativeList(entryId) {
193
+ let selected;
194
+ try {
195
+ selected = await resolveRegisteredInitiativeContextStore(entryId);
196
+ }
197
+ catch (error) {
198
+ return {
199
+ kind: 'store_unreadable',
200
+ entryId,
201
+ error,
202
+ };
203
+ }
204
+ try {
205
+ return {
206
+ kind: 'listed',
207
+ listed: await listSelectedInitiativeViewReferences(selected),
208
+ };
209
+ }
210
+ catch (error) {
211
+ return {
212
+ kind: 'initiative_collection_invalid',
213
+ selected,
214
+ error,
215
+ diagnostic: initiativeDiagnosticFromError(error),
216
+ };
217
+ }
218
+ }
219
+ async function scanRegisteredStoreForInitiative(entryId, initiativeId) {
220
+ let selected;
221
+ try {
222
+ selected = await resolveRegisteredInitiativeContextStore(entryId);
223
+ }
224
+ catch (error) {
225
+ return {
226
+ kind: 'store_unreadable',
227
+ entryId,
228
+ error,
229
+ };
230
+ }
231
+ try {
232
+ const state = await readSelectedInitiative(selected, initiativeId);
233
+ if (!state) {
234
+ return {
235
+ kind: 'missing',
236
+ selected,
237
+ };
238
+ }
239
+ return {
240
+ kind: 'match',
241
+ selected,
242
+ state,
243
+ diagnostic: toResolutionMatch(selected, state),
244
+ };
245
+ }
246
+ catch (error) {
247
+ return {
248
+ kind: 'initiative_invalid',
249
+ selected,
250
+ error,
251
+ };
252
+ }
253
+ }
254
+ async function scanRegisteredStoresForInitiativeLists() {
255
+ const registeredStores = await listRegisteredContextStores();
256
+ return Promise.all(registeredStores.map((entry) => scanRegisteredStoreForInitiativeList(entry.id)));
257
+ }
258
+ async function scanRegisteredStoresForInitiative(initiativeId) {
259
+ const registeredStores = await listRegisteredContextStores();
260
+ return Promise.all(registeredStores.map((entry) => scanRegisteredStoreForInitiative(entry.id, initiativeId)));
261
+ }
262
+ export async function listInitiativeViewReferences(options = {}) {
263
+ if (options.store !== undefined || options.storePath !== undefined) {
264
+ const selected = await selectContextStoreForInitiative(options, 'list');
265
+ const listed = await listSelectedInitiativeViewReferences(selected);
266
+ return {
267
+ contextStore: listed.contextStore,
268
+ contextStores: [listed],
269
+ initiatives: listed.initiatives,
270
+ status: [],
271
+ };
272
+ }
273
+ const outcomes = await scanRegisteredStoresForInitiativeLists();
274
+ if (outcomes.length === 0) {
275
+ return {
276
+ contextStore: null,
277
+ contextStores: [],
278
+ initiatives: [],
279
+ status: [],
280
+ };
281
+ }
282
+ const contextStores = outcomes
283
+ .filter((outcome) => outcome.kind === 'listed')
284
+ .map((outcome) => outcome.listed);
285
+ const invalidCollections = outcomes.filter((outcome) => outcome.kind === 'initiative_collection_invalid');
286
+ const unreadable = outcomes.filter((outcome) => outcome.kind === 'store_unreadable');
287
+ const contextStoreResults = [
288
+ ...contextStores,
289
+ ...invalidCollections.map((outcome) => ({
290
+ contextStore: outcome.selected,
291
+ initiatives: [],
292
+ status: [outcome.diagnostic],
293
+ })),
294
+ ];
295
+ if (contextStores.length === 0 && invalidCollections.length > 0) {
296
+ throw new InitiativeResolutionError('No initiatives could be read because registered context stores contain invalid initiatives.', 'initiative_collections_invalid', {
297
+ target: 'initiative',
298
+ fix: 'Fix the invalid initiative folder state and retry.',
299
+ });
300
+ }
301
+ if (contextStoreResults.length === 0) {
302
+ throw new InitiativeResolutionError('No initiatives could be read from registered context stores.', 'context_stores_unreadable', {
303
+ target: 'context_store',
304
+ fix: 'openspec context-store doctor',
305
+ });
306
+ }
307
+ const status = [];
308
+ if (unreadable.length > 0) {
309
+ status.push(makeDiagnostic('warning', 'context_stores_partially_unreadable', 'Some registered context stores could not be read.', {
310
+ target: 'context_store',
311
+ fix: 'openspec context-store doctor',
312
+ }));
313
+ }
314
+ if (invalidCollections.length > 0) {
315
+ status.push(makeDiagnostic('warning', 'initiative_collections_partially_invalid', 'Some registered context stores contain invalid initiatives.', {
316
+ target: 'initiative',
317
+ fix: 'Fix the invalid initiative folder state and retry.',
318
+ }));
319
+ }
320
+ return {
321
+ contextStore: null,
322
+ contextStores: contextStoreResults,
323
+ initiatives: contextStoreResults
324
+ .flatMap((store) => store.initiatives)
325
+ .sort((left, right) => left.store.localeCompare(right.store) || left.id.localeCompare(right.id)),
326
+ status,
327
+ };
328
+ }
329
+ export async function resolveInitiativeViewReference(reference, options = {}) {
330
+ const parsed = parseInitiativeReference(reference, options);
331
+ if (parsed.options.store !== undefined || parsed.options.storePath !== undefined) {
332
+ const selected = await selectContextStoreForInitiative(parsed.options, 'show');
333
+ return resolveSelectedInitiativeViewReference(selected, parsed.initiativeId);
334
+ }
335
+ const outcomes = await scanRegisteredStoresForInitiative(parsed.initiativeId);
336
+ const matches = outcomes.filter((outcome) => outcome.kind === 'match');
337
+ const unreadable = outcomes.filter((outcome) => outcome.kind === 'store_unreadable');
338
+ const invalidInitiatives = outcomes.filter((outcome) => outcome.kind === 'initiative_invalid');
339
+ if (invalidInitiatives.length > 0) {
340
+ throw invalidInitiatives[0].error;
341
+ }
342
+ if (unreadable.length > 0) {
343
+ throw new InitiativeResolutionError(`Initiative lookup for '${parsed.initiativeId}' is incomplete because some context stores could not be read.`, 'initiative_lookup_incomplete', {
344
+ target: 'context_store',
345
+ fix: 'openspec context-store doctor',
346
+ ...(matches.length > 0
347
+ ? { details: { matches: matches.map((match) => match.diagnostic) } }
348
+ : {}),
349
+ });
350
+ }
351
+ if (matches.length === 0) {
352
+ throw new InitiativeResolutionError(`Initiative '${parsed.initiativeId}' was not found in registered context stores.`, 'initiative_not_found', {
353
+ target: 'initiative.id',
354
+ fix: 'openspec initiative list',
355
+ });
356
+ }
357
+ if (matches.length > 1) {
358
+ throw new InitiativeResolutionError(`Initiative '${parsed.initiativeId}' exists in multiple context stores.`, 'initiative_ambiguous', {
359
+ target: 'initiative.id',
360
+ fix: `openspec initiative show ${parsed.initiativeId} --store <store>`,
361
+ details: { matches: matches.map((match) => match.diagnostic) },
362
+ });
363
+ }
364
+ const [match] = matches;
365
+ return toInitiativeViewReference(match.selected, match.state);
366
+ }
367
+ export async function resolveInitiativeLinkReference(reference, options = {}) {
368
+ const initiative = await resolveInitiativeViewReference(reference, options);
369
+ return {
370
+ store: initiative.store,
371
+ id: initiative.id,
372
+ };
373
+ }
374
+ //# sourceMappingURL=resolution.js.map
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ export declare const INITIATIVE_COLLECTION_ID = "initiatives";
3
+ export declare const INITIATIVE_FILE_NAME = "initiative.yaml";
4
+ export declare const INITIATIVE_REQUIREMENTS_FILE_NAME = "requirements.md";
5
+ export declare const INITIATIVE_DESIGN_FILE_NAME = "design.md";
6
+ export declare const INITIATIVE_DECISIONS_FILE_NAME = "decisions.md";
7
+ export declare const INITIATIVE_QUESTIONS_FILE_NAME = "questions.md";
8
+ export declare const INITIATIVE_TASKS_FILE_NAME = "tasks.md";
9
+ export declare const INITIATIVE_MARKDOWN_FILE_NAMES: readonly ["requirements.md", "design.md", "decisions.md", "questions.md", "tasks.md"];
10
+ export declare const INITIATIVE_FILE_NAMES: readonly ["initiative.yaml", "requirements.md", "design.md", "decisions.md", "questions.md", "tasks.md"];
11
+ export type InitiativeMarkdownFileName = typeof INITIATIVE_MARKDOWN_FILE_NAMES[number];
12
+ export type InitiativeFileName = typeof INITIATIVE_FILE_NAMES[number];
13
+ export declare const INITIATIVE_STATUSES: readonly ["exploring", "active", "complete", "archived"];
14
+ export type InitiativeStatus = typeof INITIATIVE_STATUSES[number];
15
+ export type InitiativeMetadataValue = string | number | boolean | null | InitiativeMetadataValue[] | {
16
+ [key: string]: InitiativeMetadataValue;
17
+ };
18
+ export type InitiativeMetadata = Record<string, InitiativeMetadataValue>;
19
+ declare const InitiativeStateSchema: z.ZodObject<{
20
+ version: z.ZodLiteral<1>;
21
+ id: z.ZodString;
22
+ title: z.ZodString;
23
+ summary: z.ZodString;
24
+ status: z.ZodEnum<{
25
+ complete: "complete";
26
+ active: "active";
27
+ exploring: "exploring";
28
+ archived: "archived";
29
+ }>;
30
+ created: z.ZodString;
31
+ owners: z.ZodDefault<z.ZodArray<z.ZodString>>;
32
+ metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<InitiativeMetadataValue, unknown, z.core.$ZodTypeInternals<InitiativeMetadataValue, unknown>>>>;
33
+ }, z.core.$strict>;
34
+ export type InitiativeStateInput = z.input<typeof InitiativeStateSchema>;
35
+ export type InitiativeState = z.output<typeof InitiativeStateSchema>;
36
+ export declare function validateInitiativeId(id: string): string;
37
+ export declare function isValidInitiativeId(id: string): boolean;
38
+ export declare function parseInitiativeState(content: string): InitiativeState;
39
+ export declare function serializeInitiativeState(state: InitiativeStateInput): string;
40
+ export {};
41
+ //# sourceMappingURL=schema.d.ts.map