@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.
- package/README.md +9 -6
- package/bin/openspec.js +3 -1
- package/dist/cli/index.d.ts +4 -1
- package/dist/cli/index.js +46 -2
- package/dist/commands/completion.js +7 -0
- package/dist/commands/config.js +64 -5
- package/dist/commands/context-store.d.ts +3 -0
- package/dist/commands/context-store.js +475 -0
- package/dist/commands/initiative.d.ts +13 -0
- package/dist/commands/initiative.js +318 -0
- package/dist/commands/workflow/index.d.ts +2 -0
- package/dist/commands/workflow/index.js +1 -0
- package/dist/commands/workflow/initiative-link.d.ts +24 -0
- package/dist/commands/workflow/initiative-link.js +47 -0
- package/dist/commands/workflow/instructions.d.ts +1 -1
- package/dist/commands/workflow/instructions.js +46 -83
- package/dist/commands/workflow/new-change.d.ts +6 -0
- package/dist/commands/workflow/new-change.js +114 -17
- package/dist/commands/workflow/set-change.d.ts +13 -0
- package/dist/commands/workflow/set-change.js +87 -0
- package/dist/commands/workflow/shared.d.ts +5 -3
- package/dist/commands/workflow/shared.js +6 -6
- package/dist/commands/workflow/status.js +24 -9
- package/dist/commands/workflow/templates.js +5 -4
- package/dist/commands/workspace/context-status.d.ts +4 -0
- package/dist/commands/workspace/context-status.js +59 -0
- package/dist/commands/workspace/open-target-selection.d.ts +13 -0
- package/dist/commands/workspace/open-target-selection.js +146 -0
- package/dist/commands/workspace/open-view.d.ts +62 -0
- package/dist/commands/workspace/open-view.js +249 -0
- package/dist/commands/workspace/open.d.ts +37 -0
- package/dist/commands/workspace/open.js +110 -0
- package/dist/commands/workspace/opener-selection.d.ts +11 -0
- package/dist/commands/workspace/opener-selection.js +98 -0
- package/dist/commands/workspace/operations.d.ts +29 -0
- package/dist/commands/workspace/operations.js +543 -0
- package/dist/commands/workspace/prompt-theme.d.ts +29 -0
- package/dist/commands/workspace/prompt-theme.js +24 -0
- package/dist/commands/workspace/registration.d.ts +13 -0
- package/dist/commands/workspace/registration.js +84 -0
- package/dist/commands/workspace/selection.d.ts +9 -0
- package/dist/commands/workspace/selection.js +115 -0
- package/dist/commands/workspace/setup-prompts.d.ts +13 -0
- package/dist/commands/workspace/setup-prompts.js +121 -0
- package/dist/commands/workspace/types.d.ts +103 -0
- package/dist/commands/workspace/types.js +36 -0
- package/dist/commands/workspace.d.ts +6 -0
- package/dist/commands/workspace.js +587 -0
- package/dist/core/artifact-graph/index.d.ts +3 -1
- package/dist/core/artifact-graph/index.js +1 -0
- package/dist/core/artifact-graph/instruction-loader.d.ts +41 -1
- package/dist/core/artifact-graph/instruction-loader.js +51 -9
- package/dist/core/artifact-graph/outputs.d.ts +14 -0
- package/dist/core/artifact-graph/outputs.js +39 -0
- package/dist/core/artifact-graph/state.js +2 -25
- package/dist/core/artifact-graph/types.d.ts +0 -5
- package/dist/core/artifact-graph/types.js +0 -14
- package/dist/core/change-metadata/index.d.ts +2 -0
- package/dist/core/change-metadata/index.js +2 -0
- package/dist/core/change-metadata/schema.d.ts +18 -0
- package/dist/core/change-metadata/schema.js +28 -0
- package/dist/core/change-status-policy.d.ts +50 -0
- package/dist/core/change-status-policy.js +70 -0
- package/dist/core/collections/index.d.ts +3 -0
- package/dist/core/collections/index.js +3 -0
- package/dist/core/collections/initiatives/collection.d.ts +4 -0
- package/dist/core/collections/initiatives/collection.js +17 -0
- package/dist/core/collections/initiatives/index.d.ts +6 -0
- package/dist/core/collections/initiatives/index.js +6 -0
- package/dist/core/collections/initiatives/operations.d.ts +49 -0
- package/dist/core/collections/initiatives/operations.js +175 -0
- package/dist/core/collections/initiatives/resolution.d.ts +87 -0
- package/dist/core/collections/initiatives/resolution.js +374 -0
- package/dist/core/collections/initiatives/schema.d.ts +41 -0
- package/dist/core/collections/initiatives/schema.js +134 -0
- package/dist/core/collections/initiatives/templates.d.ts +12 -0
- package/dist/core/collections/initiatives/templates.js +90 -0
- package/dist/core/collections/runtime.d.ts +46 -0
- package/dist/core/collections/runtime.js +194 -0
- package/dist/core/completions/command-registry.d.ts +1 -5
- package/dist/core/completions/command-registry.js +532 -32
- package/dist/core/completions/completion-provider.d.ts +11 -0
- package/dist/core/completions/completion-provider.js +27 -0
- package/dist/core/completions/generators/bash-generator.d.ts +3 -0
- package/dist/core/completions/generators/bash-generator.js +60 -4
- package/dist/core/completions/generators/fish-generator.js +3 -0
- package/dist/core/completions/generators/powershell-generator.d.ts +3 -0
- package/dist/core/completions/generators/powershell-generator.js +63 -4
- package/dist/core/completions/generators/zsh-generator.d.ts +3 -0
- package/dist/core/completions/generators/zsh-generator.js +46 -22
- package/dist/core/completions/installers/zsh-installer.d.ts +0 -8
- package/dist/core/completions/installers/zsh-installer.js +3 -31
- package/dist/core/completions/shared-flags.d.ts +12 -0
- package/dist/core/completions/shared-flags.js +28 -0
- package/dist/core/completions/templates/bash-templates.d.ts +1 -1
- package/dist/core/completions/templates/bash-templates.js +6 -0
- package/dist/core/completions/templates/fish-templates.d.ts +1 -1
- package/dist/core/completions/templates/fish-templates.js +6 -0
- package/dist/core/completions/templates/powershell-templates.d.ts +1 -1
- package/dist/core/completions/templates/powershell-templates.js +9 -0
- package/dist/core/completions/templates/zsh-templates.d.ts +1 -1
- package/dist/core/completions/templates/zsh-templates.js +9 -0
- package/dist/core/completions/types.d.ts +23 -1
- package/dist/core/config.js +3 -1
- package/dist/core/context-store/binding.d.ts +53 -0
- package/dist/core/context-store/binding.js +197 -0
- package/dist/core/context-store/errors.d.ts +20 -0
- package/dist/core/context-store/errors.js +22 -0
- package/dist/core/context-store/foundation.d.ts +55 -0
- package/dist/core/context-store/foundation.js +321 -0
- package/dist/core/context-store/index.d.ts +6 -0
- package/dist/core/context-store/index.js +6 -0
- package/dist/core/context-store/operations.d.ts +85 -0
- package/dist/core/context-store/operations.js +528 -0
- package/dist/core/context-store/registry.d.ts +45 -0
- package/dist/core/context-store/registry.js +229 -0
- package/dist/core/global-config.d.ts +6 -1
- package/dist/core/global-config.js +15 -16
- package/dist/core/index.d.ts +5 -1
- package/dist/core/index.js +4 -0
- package/dist/core/parsers/change-parser.js +7 -3
- package/dist/core/parsers/markdown-parser.d.ts +4 -0
- package/dist/core/parsers/markdown-parser.js +41 -1
- package/dist/core/parsers/requirement-blocks.js +5 -5
- package/dist/core/parsers/spec-structure.d.ts +9 -0
- package/dist/core/parsers/spec-structure.js +88 -0
- package/dist/core/planning-home.d.ts +21 -0
- package/dist/core/planning-home.js +108 -0
- package/dist/core/profiles.d.ts +1 -1
- package/dist/core/profiles.js +1 -1
- package/dist/core/specs-apply.js +9 -1
- package/dist/core/templates/workflows/apply-change.js +10 -4
- package/dist/core/templates/workflows/archive-change.js +21 -15
- package/dist/core/templates/workflows/bulk-archive-change.js +14 -10
- package/dist/core/templates/workflows/continue-change.js +6 -4
- package/dist/core/templates/workflows/explore.js +8 -10
- package/dist/core/templates/workflows/ff-change.js +8 -6
- package/dist/core/templates/workflows/new-change.js +6 -6
- package/dist/core/templates/workflows/onboard.js +13 -15
- package/dist/core/templates/workflows/propose.js +8 -6
- package/dist/core/templates/workflows/sync-specs.js +28 -10
- package/dist/core/templates/workflows/verify-change.js +16 -10
- package/dist/core/update.d.ts +5 -0
- package/dist/core/update.js +20 -0
- package/dist/core/validation/validator.d.ts +11 -0
- package/dist/core/validation/validator.js +28 -2
- package/dist/core/workspace/foundation.d.ts +67 -0
- package/dist/core/workspace/foundation.js +295 -0
- package/dist/core/workspace/index.d.ts +8 -0
- package/dist/core/workspace/index.js +8 -0
- package/dist/core/workspace/legacy-state.d.ts +28 -0
- package/dist/core/workspace/legacy-state.js +200 -0
- package/dist/core/workspace/link-input.d.ts +9 -0
- package/dist/core/workspace/link-input.js +32 -0
- package/dist/core/workspace/open-surface.d.ts +45 -0
- package/dist/core/workspace/open-surface.js +215 -0
- package/dist/core/workspace/openers.d.ts +21 -0
- package/dist/core/workspace/openers.js +124 -0
- package/dist/core/workspace/registry.d.ts +24 -0
- package/dist/core/workspace/registry.js +146 -0
- package/dist/core/workspace/skills.d.ts +57 -0
- package/dist/core/workspace/skills.js +334 -0
- package/dist/core/workspace/state-io.d.ts +10 -0
- package/dist/core/workspace/state-io.js +119 -0
- package/dist/telemetry/config.d.ts +7 -1
- package/dist/telemetry/config.js +87 -19
- package/dist/telemetry/index.js +19 -0
- package/dist/utils/change-metadata.d.ts +5 -2
- package/dist/utils/change-metadata.js +7 -13
- package/dist/utils/change-utils.d.ts +9 -0
- package/dist/utils/change-utils.js +7 -5
- package/dist/utils/file-system.d.ts +5 -0
- package/dist/utils/file-system.js +21 -1
- package/package.json +2 -1
- package/schemas/workspace-planning/schema.yaml +72 -0
- package/schemas/workspace-planning/templates/design.md +33 -0
- package/schemas/workspace-planning/templates/proposal.md +28 -0
- package/schemas/workspace-planning/templates/spec.md +9 -0
- 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 {
|
|
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
|
|
36
|
-
if (!fs.existsSync(
|
|
37
|
-
throw new TemplateLoadError(`Template not found: ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,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,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,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
|