@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
@@ -3,7 +3,10 @@ import * as path from 'node:path';
3
3
  import { getSchemaDir, resolveSchema } from './resolver.js';
4
4
  import { ArtifactGraph } from './graph.js';
5
5
  import { detectCompleted } from './state.js';
6
- import { resolveSchemaForChange } from '../../utils/change-metadata.js';
6
+ import { resolveArtifactOutputs } from './outputs.js';
7
+ import { readChangeMetadata, resolveSchemaForChange } from '../../utils/change-metadata.js';
8
+ import { FileSystemUtils } from '../../utils/file-system.js';
9
+ import { buildActionContext, buildNextSteps, summarizeAffectedAreas, summarizePlanningHome, } from '../change-status-policy.js';
7
10
  import { readProjectConfig, validateConfigRules } from '../project-config.js';
8
11
  // Session-level cache for validation warnings (avoid repeating same warnings)
9
12
  const shownWarnings = new Set();
@@ -32,10 +35,11 @@ export function loadTemplate(schemaName, templatePath, projectRoot) {
32
35
  if (!schemaDir) {
33
36
  throw new TemplateLoadError(`Schema '${schemaName}' not found`, templatePath);
34
37
  }
35
- const fullPath = path.join(schemaDir, 'templates', templatePath);
36
- if (!fs.existsSync(fullPath)) {
37
- throw new TemplateLoadError(`Template not found: ${fullPath}`, fullPath);
38
+ const templatePathOnDisk = path.join(schemaDir, 'templates', templatePath);
39
+ if (!fs.existsSync(templatePathOnDisk)) {
40
+ throw new TemplateLoadError(`Template not found: ${templatePathOnDisk}`, templatePathOnDisk);
38
41
  }
42
+ const fullPath = FileSystemUtils.canonicalizeExistingPath(templatePathOnDisk);
39
43
  try {
40
44
  return fs.readFileSync(fullPath, 'utf-8');
41
45
  }
@@ -57,10 +61,12 @@ export function loadTemplate(schemaName, templatePath, projectRoot) {
57
61
  * @param schemaName - Optional schema name override. If not provided, auto-detected from metadata.
58
62
  * @returns Change context with graph, completed set, and metadata
59
63
  */
60
- export function loadChangeContext(projectRoot, changeName, schemaName) {
61
- const changeDir = path.join(projectRoot, 'openspec', 'changes', changeName);
62
- // Resolve schema: explicit > metadata > default
63
- const resolvedSchemaName = resolveSchemaForChange(changeDir, schemaName);
64
+ export function loadChangeContext(projectRoot, changeName, schemaName, options = {}) {
65
+ const changeDir = FileSystemUtils.canonicalizeExistingPath(options.changeDir ?? path.join(projectRoot, 'openspec', 'changes', changeName));
66
+ const metadata = readChangeMetadata(changeDir, projectRoot) ?? undefined;
67
+ const resolvedSchemaName = resolveSchemaForChange(changeDir, schemaName, projectRoot, {
68
+ metadata: metadata ?? null,
69
+ });
64
70
  const schema = resolveSchema(resolvedSchemaName, projectRoot);
65
71
  const graph = ArtifactGraph.fromSchema(schema);
66
72
  const completed = detectCompleted(graph, changeDir);
@@ -71,6 +77,9 @@ export function loadChangeContext(projectRoot, changeName, schemaName) {
71
77
  changeName,
72
78
  changeDir,
73
79
  projectRoot,
80
+ ...(options.planningHome ? { planningHome: options.planningHome } : {}),
81
+ ...(metadata ? { metadata } : {}),
82
+ ...(metadata?.initiative ? { initiative: metadata.initiative } : {}),
74
83
  };
75
84
  }
76
85
  /**
@@ -128,7 +137,11 @@ export function generateInstructions(context, artifactId, projectRoot) {
128
137
  artifactId: artifact.id,
129
138
  schemaName: context.schemaName,
130
139
  changeDir: context.changeDir,
140
+ planningHome: summarizePlanningHome(context.planningHome),
141
+ ...(context.initiative ? { initiative: context.initiative } : {}),
131
142
  outputPath: artifact.generates,
143
+ resolvedOutputPath: path.join(context.changeDir, artifact.generates),
144
+ existingOutputPaths: resolveArtifactOutputs(context.changeDir, artifact.generates),
132
145
  description: artifact.description,
133
146
  instruction: artifact.instruction,
134
147
  context: configContext,
@@ -177,7 +190,13 @@ export function formatChangeStatus(context) {
177
190
  const artifacts = context.graph.getAllArtifacts();
178
191
  const ready = new Set(context.graph.getNextArtifacts(context.completed));
179
192
  const blocked = context.graph.getBlocked(context.completed);
193
+ const artifactPaths = {};
180
194
  const artifactStatuses = artifacts.map(artifact => {
195
+ artifactPaths[artifact.id] = {
196
+ outputPath: artifact.generates,
197
+ resolvedOutputPath: path.join(context.changeDir, artifact.generates),
198
+ existingOutputPaths: resolveArtifactOutputs(context.changeDir, artifact.generates),
199
+ };
181
200
  if (context.completed.has(artifact.id)) {
182
201
  return {
183
202
  id: artifact.id,
@@ -203,11 +222,34 @@ export function formatChangeStatus(context) {
203
222
  const buildOrder = context.graph.getBuildOrder();
204
223
  const orderMap = new Map(buildOrder.map((id, idx) => [id, idx]));
205
224
  artifactStatuses.sort((a, b) => (orderMap.get(a.id) ?? 0) - (orderMap.get(b.id) ?? 0));
225
+ const affectedAreas = summarizeAffectedAreas({
226
+ planningHome: context.planningHome,
227
+ metadata: context.metadata,
228
+ });
229
+ const isComplete = context.graph.isComplete(context.completed);
230
+ const artifactIds = artifactStatuses.map((artifact) => artifact.id);
206
231
  return {
207
232
  changeName: context.changeName,
208
233
  schemaName: context.schemaName,
209
- isComplete: context.graph.isComplete(context.completed),
234
+ planningHome: summarizePlanningHome(context.planningHome),
235
+ ...(context.initiative ? { initiative: context.initiative } : {}),
236
+ changeRoot: context.changeDir,
237
+ artifactPaths,
238
+ affectedAreas,
239
+ isComplete,
210
240
  applyRequires,
241
+ nextSteps: buildNextSteps({
242
+ changeName: context.changeName,
243
+ planningHome: context.planningHome,
244
+ artifactStatuses,
245
+ affectedAreas,
246
+ allArtifactsComplete: isComplete,
247
+ }),
248
+ actionContext: buildActionContext({
249
+ planningHome: context.planningHome,
250
+ projectRoot: context.projectRoot,
251
+ artifactIds,
252
+ }),
211
253
  artifacts: artifactStatuses,
212
254
  };
213
255
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Checks if a path contains glob pattern characters.
3
+ */
4
+ export declare function isGlobPattern(pattern: string): boolean;
5
+ /**
6
+ * Resolves an artifact's output path(s) to concrete files that currently exist.
7
+ * Returns absolute file paths. Glob matches are sorted for deterministic output.
8
+ */
9
+ export declare function resolveArtifactOutputs(changeDir: string, generates: string): string[];
10
+ /**
11
+ * Checks if an artifact has at least one resolved output file.
12
+ */
13
+ export declare function artifactOutputExists(changeDir: string, generates: string): boolean;
14
+ //# sourceMappingURL=outputs.d.ts.map
@@ -0,0 +1,39 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import fg from 'fast-glob';
4
+ import { FileSystemUtils } from '../../utils/file-system.js';
5
+ /**
6
+ * Checks if a path contains glob pattern characters.
7
+ */
8
+ export function isGlobPattern(pattern) {
9
+ return pattern.includes('*') || pattern.includes('?') || pattern.includes('[');
10
+ }
11
+ /**
12
+ * Resolves an artifact's output path(s) to concrete files that currently exist.
13
+ * Returns absolute file paths. Glob matches are sorted for deterministic output.
14
+ */
15
+ export function resolveArtifactOutputs(changeDir, generates) {
16
+ if (!isGlobPattern(generates)) {
17
+ const fullPath = path.join(changeDir, generates);
18
+ try {
19
+ return fs.statSync(fullPath).isFile()
20
+ ? [FileSystemUtils.canonicalizeExistingPath(fullPath)]
21
+ : [];
22
+ }
23
+ catch {
24
+ return [];
25
+ }
26
+ }
27
+ const normalizedPattern = FileSystemUtils.toPosixPath(generates);
28
+ const matches = fg
29
+ .sync(normalizedPattern, { cwd: changeDir, onlyFiles: true, absolute: true })
30
+ .map((match) => FileSystemUtils.canonicalizeExistingPath(path.normalize(match)));
31
+ return Array.from(new Set(matches)).sort();
32
+ }
33
+ /**
34
+ * Checks if an artifact has at least one resolved output file.
35
+ */
36
+ export function artifactOutputExists(changeDir, generates) {
37
+ return resolveArtifactOutputs(changeDir, generates).length > 0;
38
+ }
39
+ //# sourceMappingURL=outputs.js.map
@@ -1,7 +1,5 @@
1
1
  import * as fs from 'node:fs';
2
- import * as path from 'node:path';
3
- import fg from 'fast-glob';
4
- import { FileSystemUtils } from '../../utils/file-system.js';
2
+ import { artifactOutputExists } from './outputs.js';
5
3
  /**
6
4
  * Detects which artifacts are completed by checking file existence in the change directory.
7
5
  * Returns a Set of completed artifact IDs.
@@ -28,27 +26,6 @@ export function detectCompleted(graph, changeDir) {
28
26
  * Supports both simple paths and glob patterns.
29
27
  */
30
28
  function isArtifactComplete(generates, changeDir) {
31
- const fullPattern = path.join(changeDir, generates);
32
- // Check if it's a glob pattern
33
- if (isGlobPattern(generates)) {
34
- return hasGlobMatches(fullPattern);
35
- }
36
- // Simple file path - check if file exists
37
- return fs.existsSync(fullPattern);
38
- }
39
- /**
40
- * Checks if a path contains glob pattern characters.
41
- */
42
- function isGlobPattern(pattern) {
43
- return pattern.includes('*') || pattern.includes('?') || pattern.includes('[');
44
- }
45
- /**
46
- * Checks if a glob pattern has any matches.
47
- * Normalizes Windows backslashes to forward slashes for cross-platform glob compatibility.
48
- */
49
- function hasGlobMatches(pattern) {
50
- const normalizedPattern = FileSystemUtils.toPosixPath(pattern);
51
- const matches = fg.sync(normalizedPattern, { onlyFiles: true });
52
- return matches.length > 0;
29
+ return artifactOutputExists(changeDir, generates);
53
30
  }
54
31
  //# sourceMappingURL=state.js.map
@@ -33,11 +33,6 @@ export declare const SchemaYamlSchema: z.ZodObject<{
33
33
  export type Artifact = z.infer<typeof ArtifactSchema>;
34
34
  export type ApplyPhase = z.infer<typeof ApplyPhaseSchema>;
35
35
  export type SchemaYaml = z.infer<typeof SchemaYamlSchema>;
36
- export declare const ChangeMetadataSchema: z.ZodObject<{
37
- schema: z.ZodString;
38
- created: z.ZodOptional<z.ZodString>;
39
- }, z.core.$strip>;
40
- export type ChangeMetadata = z.infer<typeof ChangeMetadataSchema>;
41
36
  export type CompletedSet = Set<string>;
42
37
  export interface BlockedArtifacts {
43
38
  [artifactId: string]: string[];
@@ -26,18 +26,4 @@ export const SchemaYamlSchema = z.object({
26
26
  // Optional apply phase configuration (for schema-aware apply instructions)
27
27
  apply: ApplyPhaseSchema.optional(),
28
28
  });
29
- // Per-change metadata schema
30
- // Note: schema field is validated at parse time against available schemas
31
- // using a lazy import to avoid circular dependencies
32
- export const ChangeMetadataSchema = z.object({
33
- // Required: which workflow schema this change uses
34
- schema: z.string().min(1, { message: 'schema is required' }),
35
- // Optional: creation timestamp (ISO date string)
36
- created: z
37
- .string()
38
- .regex(/^\d{4}-\d{2}-\d{2}$/, {
39
- message: 'created must be YYYY-MM-DD format',
40
- })
41
- .optional(),
42
- });
43
29
  //# sourceMappingURL=types.js.map
@@ -0,0 +1,2 @@
1
+ export * from './schema.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,2 @@
1
+ export * from './schema.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+ export declare const InitiativeLinkSchema: z.ZodObject<{
3
+ store: z.ZodString;
4
+ id: z.ZodString;
5
+ }, z.core.$strict>;
6
+ export type InitiativeLink = z.infer<typeof InitiativeLinkSchema>;
7
+ export declare const ChangeMetadataSchema: z.ZodObject<{
8
+ schema: z.ZodString;
9
+ created: z.ZodOptional<z.ZodString>;
10
+ goal: z.ZodOptional<z.ZodString>;
11
+ affected_areas: z.ZodOptional<z.ZodArray<z.ZodString>>;
12
+ initiative: z.ZodOptional<z.ZodObject<{
13
+ store: z.ZodString;
14
+ id: z.ZodString;
15
+ }, z.core.$strict>>;
16
+ }, z.core.$strip>;
17
+ export type ChangeMetadata = z.infer<typeof ChangeMetadataSchema>;
18
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1,28 @@
1
+ import { z } from 'zod';
2
+ const KebabIdentifierSchema = (label) => z.string().superRefine((value, ctx) => {
3
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(value)) {
4
+ ctx.addIssue({
5
+ code: 'custom',
6
+ message: `${label} must be kebab-case with lowercase letters, numbers, and single hyphen separators`,
7
+ });
8
+ }
9
+ });
10
+ export const InitiativeLinkSchema = z.object({
11
+ store: KebabIdentifierSchema('Context store id'),
12
+ id: KebabIdentifierSchema('Initiative id'),
13
+ }).strict();
14
+ // Per-change metadata schema. The schema field is validated against available
15
+ // workflow schemas when metadata is read or written.
16
+ export const ChangeMetadataSchema = z.object({
17
+ schema: z.string().min(1, { message: 'schema is required' }),
18
+ created: z
19
+ .string()
20
+ .regex(/^\d{4}-\d{2}-\d{2}$/, {
21
+ message: 'created must be YYYY-MM-DD format',
22
+ })
23
+ .optional(),
24
+ goal: z.string().min(1).optional(),
25
+ affected_areas: z.array(z.string().min(1)).optional(),
26
+ initiative: InitiativeLinkSchema.optional(),
27
+ });
28
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,50 @@
1
+ import type { ChangeMetadata } from './change-metadata/index.js';
2
+ import type { PlanningHome } from './planning-home.js';
3
+ export interface PlanningHomeSummary {
4
+ kind: 'repo' | 'workspace';
5
+ root: string;
6
+ changesDir: string;
7
+ defaultSchema: string;
8
+ workspaceName?: string;
9
+ }
10
+ export interface AffectedAreasSummary {
11
+ known: string[];
12
+ unresolved: boolean;
13
+ invalid: string[];
14
+ }
15
+ export interface ActionContext {
16
+ mode: 'repo-local' | 'workspace-planning';
17
+ sourceOfTruth: 'repo' | 'workspace-local';
18
+ planningArtifacts: string[];
19
+ linkedContext: Array<{
20
+ name: string;
21
+ }>;
22
+ allowedEditRoots: string[];
23
+ requiresAffectedAreaSelection: boolean;
24
+ constraints: string[];
25
+ }
26
+ export interface ChangeStatusPolicyArtifact {
27
+ id: string;
28
+ status: 'done' | 'ready' | 'blocked';
29
+ }
30
+ export interface AffectedAreasInput {
31
+ planningHome?: PlanningHome;
32
+ metadata?: ChangeMetadata;
33
+ }
34
+ export interface ChangeNextStepsInput {
35
+ changeName: string;
36
+ planningHome?: PlanningHome;
37
+ artifactStatuses: ChangeStatusPolicyArtifact[];
38
+ affectedAreas?: AffectedAreasSummary;
39
+ allArtifactsComplete: boolean;
40
+ }
41
+ export interface ActionContextInput {
42
+ planningHome?: PlanningHome;
43
+ projectRoot: string;
44
+ artifactIds: string[];
45
+ }
46
+ export declare function summarizePlanningHome(planningHome: PlanningHome | undefined): PlanningHomeSummary | undefined;
47
+ export declare function summarizeAffectedAreas(input: AffectedAreasInput): AffectedAreasSummary | undefined;
48
+ export declare function buildActionContext(input: ActionContextInput): ActionContext;
49
+ export declare function buildNextSteps(input: ChangeNextStepsInput): string[];
50
+ //# sourceMappingURL=change-status-policy.d.ts.map
@@ -0,0 +1,70 @@
1
+ export function summarizePlanningHome(planningHome) {
2
+ if (!planningHome) {
3
+ return undefined;
4
+ }
5
+ return {
6
+ kind: planningHome.kind,
7
+ root: planningHome.root,
8
+ changesDir: planningHome.changesDir,
9
+ defaultSchema: planningHome.defaultSchema,
10
+ ...(planningHome.workspace ? { workspaceName: planningHome.workspace.name } : {}),
11
+ };
12
+ }
13
+ export function summarizeAffectedAreas(input) {
14
+ if (input.planningHome?.kind !== 'workspace') {
15
+ return undefined;
16
+ }
17
+ const known = Array.from(new Set(input.metadata?.affected_areas ?? [])).sort((a, b) => a.localeCompare(b));
18
+ const validAreas = new Set(input.planningHome.workspace?.links ?? []);
19
+ const invalid = known.filter((areaName) => validAreas.size > 0 && !validAreas.has(areaName));
20
+ return {
21
+ known,
22
+ unresolved: known.length === 0,
23
+ invalid,
24
+ };
25
+ }
26
+ export function buildActionContext(input) {
27
+ if (input.planningHome?.kind === 'workspace') {
28
+ return {
29
+ mode: 'workspace-planning',
30
+ sourceOfTruth: 'workspace-local',
31
+ planningArtifacts: input.artifactIds,
32
+ linkedContext: (input.planningHome.workspace?.links ?? []).map((name) => ({ name })),
33
+ allowedEditRoots: [],
34
+ requiresAffectedAreaSelection: true,
35
+ constraints: [
36
+ 'Treat workspace-local planning artifacts as compatibility context for this local view.',
37
+ 'Use initiatives for durable coordination when initiative context exists.',
38
+ 'Treat linked repos and folders as context until an explicit edit root is selected.',
39
+ 'Do not make implementation edits without an explicit allowed edit root.',
40
+ ],
41
+ };
42
+ }
43
+ return {
44
+ mode: 'repo-local',
45
+ sourceOfTruth: 'repo',
46
+ planningArtifacts: input.artifactIds,
47
+ linkedContext: [],
48
+ allowedEditRoots: [input.projectRoot],
49
+ requiresAffectedAreaSelection: false,
50
+ constraints: ['Repo-local change artifacts and implementation edits are scoped to this project.'],
51
+ };
52
+ }
53
+ export function buildNextSteps(input) {
54
+ const readyArtifact = input.artifactStatuses.find((artifact) => artifact.status === 'ready');
55
+ const steps = [];
56
+ if (readyArtifact) {
57
+ steps.push(`Run openspec instructions ${readyArtifact.id} --change "${input.changeName}" --json before writing that artifact.`);
58
+ }
59
+ else if (input.allArtifactsComplete) {
60
+ steps.push('All planning artifacts are complete; review tasks before implementation.');
61
+ }
62
+ if (input.planningHome?.kind === 'workspace') {
63
+ if (input.affectedAreas?.unresolved) {
64
+ steps.push('Identify affected areas in change metadata or coordination tasks as planning continues.');
65
+ }
66
+ steps.push('Select an affected area and allowed edit root before implementation edits.');
67
+ }
68
+ return steps;
69
+ }
70
+ //# sourceMappingURL=change-status-policy.js.map
@@ -0,0 +1,3 @@
1
+ export * from './runtime.js';
2
+ export * from './initiatives/index.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export * from './runtime.js';
2
+ export * from './initiatives/index.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import { type CollectionRegistry, type MountedCollection } from '../runtime.js';
2
+ export declare function createInitiativesCollectionRegistry(): CollectionRegistry;
3
+ export declare function mountInitiativesCollection(storeRoot: string): MountedCollection;
4
+ //# sourceMappingURL=collection.d.ts.map
@@ -0,0 +1,17 @@
1
+ import { createCollectionRegistry, mountCollections, } from '../runtime.js';
2
+ import { INITIATIVE_COLLECTION_ID } from './schema.js';
3
+ export function createInitiativesCollectionRegistry() {
4
+ return createCollectionRegistry([
5
+ {
6
+ id: INITIATIVE_COLLECTION_ID,
7
+ mount: INITIATIVE_COLLECTION_ID,
8
+ },
9
+ ]);
10
+ }
11
+ export function mountInitiativesCollection(storeRoot) {
12
+ return mountCollections({
13
+ storeRoot,
14
+ collections: createInitiativesCollectionRegistry(),
15
+ }).require(INITIATIVE_COLLECTION_ID);
16
+ }
17
+ //# sourceMappingURL=collection.js.map
@@ -0,0 +1,6 @@
1
+ export * from './collection.js';
2
+ export * from './schema.js';
3
+ export * from './templates.js';
4
+ export * from './operations.js';
5
+ export * from './resolution.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ export * from './collection.js';
2
+ export * from './schema.js';
3
+ export * from './templates.js';
4
+ export * from './operations.js';
5
+ export * from './resolution.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,49 @@
1
+ import * as nodeFs from 'node:fs';
2
+ import type { MountedCollection } from '../runtime.js';
3
+ import { type InitiativeMetadata, type InitiativeState, type InitiativeStatus } from './schema.js';
4
+ import { type InitiativeTemplateFile } from './templates.js';
5
+ export interface InitiativeDirectoryEntry {
6
+ name: string;
7
+ isDirectory(): boolean;
8
+ }
9
+ export interface InitiativeOperationsFileSystem {
10
+ mkdir(dirPath: string, options: {
11
+ recursive?: boolean;
12
+ }): Promise<void>;
13
+ writeFile(filePath: string, content: string, options: {
14
+ flag?: nodeFs.OpenMode;
15
+ }): Promise<void>;
16
+ readFile(filePath: string): Promise<string>;
17
+ readdir(dirPath: string, options: {
18
+ withFileTypes: true;
19
+ }): Promise<readonly InitiativeDirectoryEntry[]>;
20
+ rm(dirPath: string, options: {
21
+ recursive?: boolean;
22
+ force?: boolean;
23
+ }): Promise<void>;
24
+ }
25
+ export interface InitiativeOperationDependencies {
26
+ fileSystem?: InitiativeOperationsFileSystem;
27
+ }
28
+ export interface CreateInitiativeInput extends InitiativeOperationDependencies {
29
+ collection: MountedCollection;
30
+ id: string;
31
+ title: string;
32
+ summary: string;
33
+ status?: InitiativeStatus;
34
+ owners?: string[];
35
+ metadata?: InitiativeMetadata;
36
+ getCurrentDate?: () => string;
37
+ buildTemplateFiles?: (state: InitiativeState) => readonly InitiativeTemplateFile[];
38
+ }
39
+ export interface ListInitiativesInput extends InitiativeOperationDependencies {
40
+ collection: MountedCollection;
41
+ }
42
+ export interface ReadInitiativeInput extends InitiativeOperationDependencies {
43
+ collection: MountedCollection;
44
+ id: string;
45
+ }
46
+ export declare function createInitiative(input: CreateInitiativeInput): Promise<InitiativeState>;
47
+ export declare function readInitiative(input: ReadInitiativeInput): Promise<InitiativeState | null>;
48
+ export declare function listInitiatives(input: ListInitiativesInput): Promise<InitiativeState[]>;
49
+ //# sourceMappingURL=operations.d.ts.map