@fission-ai/openspec 1.4.1 → 1.6.0-beta.1
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 +37 -4
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +95 -95
- package/dist/commands/change.d.ts +5 -2
- package/dist/commands/change.js +23 -55
- package/dist/commands/config.js +6 -65
- package/dist/commands/context.d.ts +3 -0
- package/dist/commands/context.js +155 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.js +163 -0
- package/dist/commands/shared-gather.d.ts +14 -0
- package/dist/commands/shared-gather.js +31 -0
- package/dist/commands/shared-output.d.ts +18 -0
- package/dist/commands/shared-output.js +61 -0
- package/dist/commands/show.d.ts +11 -6
- package/dist/commands/show.js +70 -25
- package/dist/commands/spec.d.ts +5 -1
- package/dist/commands/spec.js +15 -4
- package/dist/commands/store.d.ts +3 -0
- package/dist/commands/store.js +547 -0
- package/dist/commands/validate.d.ts +10 -0
- package/dist/commands/validate.js +88 -40
- package/dist/commands/workflow/index.d.ts +0 -2
- package/dist/commands/workflow/index.js +0 -1
- package/dist/commands/workflow/instructions.d.ts +11 -1
- package/dist/commands/workflow/instructions.js +71 -22
- package/dist/commands/workflow/new-change.d.ts +6 -3
- package/dist/commands/workflow/new-change.js +42 -77
- package/dist/commands/workflow/shared.d.ts +15 -3
- package/dist/commands/workflow/shared.js +20 -3
- package/dist/commands/workflow/status.d.ts +2 -0
- package/dist/commands/workflow/status.js +19 -17
- package/dist/commands/workset-input.d.ts +19 -0
- package/dist/commands/workset-input.js +112 -0
- package/dist/commands/workset-prompts.d.ts +12 -0
- package/dist/commands/workset-prompts.js +143 -0
- package/dist/commands/workset.d.ts +25 -0
- package/dist/commands/workset.js +446 -0
- package/dist/core/archive.d.ts +17 -6
- package/dist/core/archive.js +220 -68
- package/dist/core/artifact-graph/index.d.ts +1 -1
- package/dist/core/artifact-graph/instruction-loader.d.ts +18 -13
- package/dist/core/artifact-graph/instruction-loader.js +8 -31
- package/dist/core/change-metadata/schema.d.ts +1 -0
- package/dist/core/change-metadata/schema.js +4 -2
- package/dist/core/change-status-policy.d.ts +5 -18
- package/dist/core/change-status-policy.js +2 -37
- package/dist/core/command-generation/adapters/bob.js +1 -14
- package/dist/core/command-generation/adapters/claude.d.ts +1 -1
- package/dist/core/command-generation/adapters/claude.js +4 -15
- package/dist/core/command-generation/adapters/cursor.js +1 -14
- package/dist/core/command-generation/adapters/index.d.ts +2 -0
- package/dist/core/command-generation/adapters/index.js +2 -0
- package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
- package/dist/core/command-generation/adapters/oh-my-pi.js +44 -0
- package/dist/core/command-generation/adapters/pi.js +1 -14
- package/dist/core/command-generation/adapters/trae.d.ts +13 -0
- package/dist/core/command-generation/adapters/trae.js +48 -0
- package/dist/core/command-generation/adapters/windsurf.js +1 -14
- package/dist/core/command-generation/registry.js +4 -0
- package/dist/core/command-generation/yaml.d.ts +22 -0
- package/dist/core/command-generation/yaml.js +38 -0
- package/dist/core/completions/command-registry.js +87 -289
- package/dist/core/completions/generators/zsh-generator.js +3 -1
- package/dist/core/completions/installers/bash-installer.js +3 -0
- package/dist/core/completions/installers/fish-installer.js +8 -0
- package/dist/core/completions/installers/powershell-installer.js +30 -2
- package/dist/core/completions/installers/zsh-installer.js +3 -0
- package/dist/core/completions/shared-flags.d.ts +1 -0
- package/dist/core/completions/shared-flags.js +5 -0
- package/dist/core/config-schema.d.ts +2 -1
- package/dist/core/config-schema.js +26 -0
- package/dist/core/config.js +1 -0
- package/dist/core/file-state.d.ts +36 -0
- package/dist/core/file-state.js +112 -0
- package/dist/core/global-config.d.ts +2 -0
- package/dist/core/id.d.ts +17 -0
- package/dist/core/id.js +30 -0
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +3 -3
- package/dist/core/init.js +27 -6
- package/dist/core/list.d.ts +2 -0
- package/dist/core/list.js +37 -14
- package/dist/core/openers.d.ts +77 -0
- package/dist/core/openers.js +251 -0
- package/dist/core/openspec-root.d.ts +45 -0
- package/dist/core/openspec-root.js +195 -0
- package/dist/core/parsers/change-parser.js +2 -1
- package/dist/core/parsers/markdown-parser.d.ts +0 -3
- package/dist/core/parsers/markdown-parser.js +5 -59
- package/dist/core/parsers/requirement-blocks.d.ts +11 -0
- package/dist/core/parsers/requirement-blocks.js +42 -8
- package/dist/core/parsers/requirement-text.d.ts +55 -0
- package/dist/core/parsers/requirement-text.js +136 -0
- package/dist/core/planning-home.d.ts +1 -6
- package/dist/core/planning-home.js +2 -43
- package/dist/core/profile-sync-drift.js +1 -0
- package/dist/core/profiles.d.ts +2 -2
- package/dist/core/profiles.js +2 -1
- package/dist/core/project-config.d.ts +44 -1
- package/dist/core/project-config.js +160 -10
- package/dist/core/references.d.ts +63 -0
- package/dist/core/references.js +310 -0
- package/dist/core/relationship-health.d.ts +65 -0
- package/dist/core/relationship-health.js +64 -0
- package/dist/core/root-selection.d.ts +122 -0
- package/dist/core/root-selection.js +337 -0
- package/dist/core/schemas/base.schema.js +7 -2
- package/dist/core/shared/allowed-tools.d.ts +12 -0
- package/dist/core/shared/allowed-tools.js +12 -0
- package/dist/core/shared/skill-generation.js +5 -1
- package/dist/core/shared/tool-detection.d.ts +2 -2
- package/dist/core/shared/tool-detection.js +2 -0
- package/dist/core/specs-apply.d.ts +6 -1
- package/dist/core/specs-apply.js +41 -5
- package/dist/core/store/errors.d.ts +20 -0
- package/dist/core/{context-store → store}/errors.js +3 -3
- package/dist/core/store/foundation.d.ts +56 -0
- package/dist/core/store/foundation.js +251 -0
- package/dist/core/store/git.d.ts +23 -0
- package/dist/core/store/git.js +137 -0
- package/dist/core/{context-store → store}/index.d.ts +0 -1
- package/dist/core/{context-store → store}/index.js +0 -1
- package/dist/core/store/operations.d.ts +114 -0
- package/dist/core/store/operations.js +803 -0
- package/dist/core/store/registry.d.ts +58 -0
- package/dist/core/store/registry.js +275 -0
- package/dist/core/templates/skill-templates.d.ts +1 -0
- package/dist/core/templates/skill-templates.js +1 -0
- package/dist/core/templates/workflows/apply-change.js +5 -4
- package/dist/core/templates/workflows/archive-change.js +5 -4
- package/dist/core/templates/workflows/bulk-archive-change.js +5 -4
- package/dist/core/templates/workflows/continue-change.js +7 -2
- package/dist/core/templates/workflows/explore.js +5 -0
- package/dist/core/templates/workflows/ff-change.js +5 -0
- package/dist/core/templates/workflows/new-change.js +5 -0
- package/dist/core/templates/workflows/onboard.js +4 -1
- package/dist/core/templates/workflows/propose.js +5 -0
- package/dist/core/templates/workflows/store-selection.d.ts +8 -0
- package/dist/core/templates/workflows/store-selection.js +8 -0
- package/dist/core/templates/workflows/sync-specs.js +5 -4
- package/dist/core/templates/workflows/update-change.d.ts +10 -0
- package/dist/core/templates/workflows/update-change.js +167 -0
- package/dist/core/templates/workflows/verify-change.js +5 -4
- package/dist/core/update.js +4 -4
- package/dist/core/validation/validator.d.ts +7 -0
- package/dist/core/validation/validator.js +101 -38
- package/dist/core/view.js +1 -1
- package/dist/core/working-set.d.ts +47 -0
- package/dist/core/working-set.js +43 -0
- package/dist/core/worksets.d.ts +75 -0
- package/dist/core/worksets.js +245 -0
- package/dist/core/zod-issues.d.ts +4 -0
- package/dist/core/zod-issues.js +10 -0
- package/dist/utils/change-metadata.d.ts +1 -0
- package/dist/utils/change-metadata.js +1 -1
- package/dist/utils/change-utils.js +15 -0
- package/dist/utils/file-system.js +36 -17
- package/dist/utils/task-progress.d.ts +10 -1
- package/dist/utils/task-progress.js +66 -2
- package/package.json +26 -23
- package/dist/commands/context-store.d.ts +0 -3
- package/dist/commands/context-store.js +0 -475
- package/dist/commands/initiative.d.ts +0 -13
- package/dist/commands/initiative.js +0 -318
- package/dist/commands/workflow/initiative-link.d.ts +0 -24
- package/dist/commands/workflow/initiative-link.js +0 -47
- package/dist/commands/workflow/set-change.d.ts +0 -13
- package/dist/commands/workflow/set-change.js +0 -87
- package/dist/commands/workspace/context-status.d.ts +0 -4
- package/dist/commands/workspace/context-status.js +0 -59
- package/dist/commands/workspace/open-target-selection.d.ts +0 -13
- package/dist/commands/workspace/open-target-selection.js +0 -146
- package/dist/commands/workspace/open-view.d.ts +0 -62
- package/dist/commands/workspace/open-view.js +0 -249
- package/dist/commands/workspace/open.d.ts +0 -37
- package/dist/commands/workspace/open.js +0 -110
- package/dist/commands/workspace/opener-selection.d.ts +0 -11
- package/dist/commands/workspace/opener-selection.js +0 -98
- package/dist/commands/workspace/operations.d.ts +0 -29
- package/dist/commands/workspace/operations.js +0 -543
- package/dist/commands/workspace/prompt-theme.d.ts +0 -29
- package/dist/commands/workspace/prompt-theme.js +0 -24
- package/dist/commands/workspace/registration.d.ts +0 -13
- package/dist/commands/workspace/registration.js +0 -84
- package/dist/commands/workspace/selection.d.ts +0 -8
- package/dist/commands/workspace/selection.js +0 -104
- package/dist/commands/workspace/setup-prompts.d.ts +0 -13
- package/dist/commands/workspace/setup-prompts.js +0 -121
- package/dist/commands/workspace/types.d.ts +0 -103
- package/dist/commands/workspace/types.js +0 -36
- package/dist/commands/workspace.d.ts +0 -5
- package/dist/commands/workspace.js +0 -574
- package/dist/core/collections/index.d.ts +0 -3
- package/dist/core/collections/index.js +0 -3
- package/dist/core/collections/initiatives/collection.d.ts +0 -4
- package/dist/core/collections/initiatives/collection.js +0 -17
- package/dist/core/collections/initiatives/index.d.ts +0 -6
- package/dist/core/collections/initiatives/index.js +0 -6
- package/dist/core/collections/initiatives/operations.d.ts +0 -49
- package/dist/core/collections/initiatives/operations.js +0 -175
- package/dist/core/collections/initiatives/resolution.d.ts +0 -87
- package/dist/core/collections/initiatives/resolution.js +0 -374
- package/dist/core/collections/initiatives/schema.d.ts +0 -41
- package/dist/core/collections/initiatives/schema.js +0 -134
- package/dist/core/collections/initiatives/templates.d.ts +0 -12
- package/dist/core/collections/initiatives/templates.js +0 -90
- package/dist/core/collections/runtime.d.ts +0 -46
- package/dist/core/collections/runtime.js +0 -194
- package/dist/core/context-store/binding.d.ts +0 -53
- package/dist/core/context-store/binding.js +0 -197
- package/dist/core/context-store/errors.d.ts +0 -20
- package/dist/core/context-store/foundation.d.ts +0 -55
- package/dist/core/context-store/foundation.js +0 -321
- package/dist/core/context-store/operations.d.ts +0 -85
- package/dist/core/context-store/operations.js +0 -528
- package/dist/core/context-store/registry.d.ts +0 -45
- package/dist/core/context-store/registry.js +0 -229
- package/dist/core/workspace/foundation.d.ts +0 -67
- package/dist/core/workspace/foundation.js +0 -295
- package/dist/core/workspace/index.d.ts +0 -8
- package/dist/core/workspace/index.js +0 -8
- package/dist/core/workspace/legacy-state.d.ts +0 -28
- package/dist/core/workspace/legacy-state.js +0 -200
- package/dist/core/workspace/link-input.d.ts +0 -9
- package/dist/core/workspace/link-input.js +0 -32
- package/dist/core/workspace/open-surface.d.ts +0 -45
- package/dist/core/workspace/open-surface.js +0 -215
- package/dist/core/workspace/openers.d.ts +0 -21
- package/dist/core/workspace/openers.js +0 -124
- package/dist/core/workspace/registry.d.ts +0 -24
- package/dist/core/workspace/registry.js +0 -146
- package/dist/core/workspace/skills.d.ts +0 -57
- package/dist/core/workspace/skills.js +0 -334
- package/dist/core/workspace/state-io.d.ts +0 -10
- package/dist/core/workspace/state-io.js +0 -121
- package/schemas/workspace-planning/schema.yaml +0 -72
- package/schemas/workspace-planning/templates/design.md +0 -33
- package/schemas/workspace-planning/templates/proposal.md +0 -28
- package/schemas/workspace-planning/templates/spec.md +0 -9
- package/schemas/workspace-planning/templates/tasks.md +0 -15
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { StoreError } from './store/errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Personal worksets (slice 7.1): purely local, manually composed,
|
|
4
|
+
* named working views. The whole feature's state lives under
|
|
5
|
+
* <globalDataDir>/worksets/ - the saved-views file plus the generated
|
|
6
|
+
* .code-workspace files - so deleting that one directory removes
|
|
7
|
+
* every trace. Nothing here is committed, shared, or derived from
|
|
8
|
+
* declarations, and nothing is ever written into a member folder.
|
|
9
|
+
*/
|
|
10
|
+
export declare const WORKSETS_DIR_NAME = "worksets";
|
|
11
|
+
export declare const WORKSETS_FILE_NAME = "worksets.yaml";
|
|
12
|
+
export interface WorksetPathOptions {
|
|
13
|
+
globalDataDir?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface WorksetMember {
|
|
16
|
+
/** Display label; the .code-workspace folder name. */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Absolute path to the member directory. */
|
|
19
|
+
path: string;
|
|
20
|
+
}
|
|
21
|
+
export interface Workset {
|
|
22
|
+
name: string;
|
|
23
|
+
/** Preferred opener id; validated only at open time. */
|
|
24
|
+
tool?: string;
|
|
25
|
+
/** Ordered; the first member is the primary (session cwd). */
|
|
26
|
+
members: WorksetMember[];
|
|
27
|
+
}
|
|
28
|
+
export interface WorksetsState {
|
|
29
|
+
version: 1;
|
|
30
|
+
worksets: Record<string, {
|
|
31
|
+
tool?: string;
|
|
32
|
+
members: WorksetMember[];
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export declare function getWorksetsDir(options?: WorksetPathOptions): string;
|
|
36
|
+
export declare function getWorksetsFilePath(options?: WorksetPathOptions): string;
|
|
37
|
+
export declare function getWorksetCodeWorkspacePath(name: string, options?: WorksetPathOptions): string;
|
|
38
|
+
export declare function validateWorksetName(name: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Returns a problem description for a member list, or null when valid.
|
|
41
|
+
* Shared by the file parser (wrapping as invalid_workset_file) and the
|
|
42
|
+
* compose flow (wrapping as workset_member_invalid).
|
|
43
|
+
*/
|
|
44
|
+
export declare function memberListProblem(members: WorksetMember[]): string | null;
|
|
45
|
+
export declare function memberLabelProblem(label: string): string | null;
|
|
46
|
+
export declare function parseWorksetsState(content: string, options?: WorksetPathOptions): WorksetsState;
|
|
47
|
+
export declare function serializeWorksetsState(state: WorksetsState, options?: WorksetPathOptions): string;
|
|
48
|
+
/** Absent file reads as the empty state; a corrupt file throws. */
|
|
49
|
+
export declare function readWorksetsState(options?: WorksetPathOptions): Promise<WorksetsState>;
|
|
50
|
+
export declare function updateWorksetsState(updater: (state: WorksetsState) => WorksetsState | Promise<WorksetsState>, options?: WorksetPathOptions): Promise<WorksetsState>;
|
|
51
|
+
/**
|
|
52
|
+
* Lock-scoped read without a write-back of the saved-views file.
|
|
53
|
+
* `open` uses this to read the state and regenerate the derived
|
|
54
|
+
* .code-workspace coherently; the lock is released before any spawn.
|
|
55
|
+
*/
|
|
56
|
+
export declare function withWorksetsLock<T>(fn: (state: WorksetsState) => T | Promise<T>, options?: WorksetPathOptions): Promise<T>;
|
|
57
|
+
export declare function worksetNotFoundError(name: string, state: WorksetsState): StoreError;
|
|
58
|
+
export declare function withWorkset(state: WorksetsState, workset: Workset): WorksetsState;
|
|
59
|
+
export declare function withoutWorkset(state: WorksetsState, name: string): WorksetsState;
|
|
60
|
+
/**
|
|
61
|
+
* Removes a saved workset and its derived .code-workspace under one
|
|
62
|
+
* lock. The derived-file cleanup runs AFTER the durable write (a
|
|
63
|
+
* failed write must not have already destroyed the artifact); a
|
|
64
|
+
* never-opened workset has no file - ENOENT is fine.
|
|
65
|
+
*/
|
|
66
|
+
export declare function removeWorkset(name: string, options?: WorksetPathOptions): Promise<void>;
|
|
67
|
+
export declare function listWorksets(state: WorksetsState): Workset[];
|
|
68
|
+
export declare function getWorkset(state: WorksetsState, name: string): Workset | null;
|
|
69
|
+
/**
|
|
70
|
+
* The generated .code-workspace content: members in saved order with
|
|
71
|
+
* their saved names, absolute paths, two-space JSON, trailing newline
|
|
72
|
+
* (the working-set builder's conventions).
|
|
73
|
+
*/
|
|
74
|
+
export declare function buildWorksetCodeWorkspaceJson(members: WorksetMember[]): string;
|
|
75
|
+
//# sourceMappingURL=worksets.d.ts.map
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import * as nodeFs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { getGlobalDataDir } from './global-config.js';
|
|
6
|
+
import { FileSystemUtils } from '../utils/file-system.js';
|
|
7
|
+
import { acquireFileLock, makeLockErrorFactory, pathIsFile, releaseFileLock, writeFileAtomically, } from './file-state.js';
|
|
8
|
+
import { StoreError } from './store/errors.js';
|
|
9
|
+
import { folderStyleNameProblem, isKebabId, KEBAB_ID_DESCRIPTION, KEBAB_ID_FIX, } from './id.js';
|
|
10
|
+
import { formatZodIssues } from './zod-issues.js';
|
|
11
|
+
const fs = nodeFs.promises;
|
|
12
|
+
/**
|
|
13
|
+
* Personal worksets (slice 7.1): purely local, manually composed,
|
|
14
|
+
* named working views. The whole feature's state lives under
|
|
15
|
+
* <globalDataDir>/worksets/ - the saved-views file plus the generated
|
|
16
|
+
* .code-workspace files - so deleting that one directory removes
|
|
17
|
+
* every trace. Nothing here is committed, shared, or derived from
|
|
18
|
+
* declarations, and nothing is ever written into a member folder.
|
|
19
|
+
*/
|
|
20
|
+
export const WORKSETS_DIR_NAME = 'worksets';
|
|
21
|
+
export const WORKSETS_FILE_NAME = 'worksets.yaml';
|
|
22
|
+
const CODE_WORKSPACE_EXTENSION = '.code-workspace';
|
|
23
|
+
export function getWorksetsDir(options = {}) {
|
|
24
|
+
return FileSystemUtils.joinPath(options.globalDataDir ?? getGlobalDataDir(), WORKSETS_DIR_NAME);
|
|
25
|
+
}
|
|
26
|
+
export function getWorksetsFilePath(options = {}) {
|
|
27
|
+
return FileSystemUtils.joinPath(getWorksetsDir(options), WORKSETS_FILE_NAME);
|
|
28
|
+
}
|
|
29
|
+
export function getWorksetCodeWorkspacePath(name, options = {}) {
|
|
30
|
+
return FileSystemUtils.joinPath(getWorksetsDir(options), `${name}${CODE_WORKSPACE_EXTENSION}`);
|
|
31
|
+
}
|
|
32
|
+
export function validateWorksetName(name) {
|
|
33
|
+
if (!isKebabId(name)) {
|
|
34
|
+
throw new StoreError(`Workset name '${name}' ${KEBAB_ID_DESCRIPTION}.`, 'invalid_workset_name', {
|
|
35
|
+
target: 'workset.name',
|
|
36
|
+
fix: KEBAB_ID_FIX,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return name;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Returns a problem description for a member list, or null when valid.
|
|
43
|
+
* Shared by the file parser (wrapping as invalid_workset_file) and the
|
|
44
|
+
* compose flow (wrapping as workset_member_invalid).
|
|
45
|
+
*/
|
|
46
|
+
export function memberListProblem(members) {
|
|
47
|
+
if (members.length === 0) {
|
|
48
|
+
return 'members must not be empty';
|
|
49
|
+
}
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
for (const member of members) {
|
|
52
|
+
const labelProblem = memberLabelProblem(member.name);
|
|
53
|
+
if (labelProblem !== null) {
|
|
54
|
+
return labelProblem;
|
|
55
|
+
}
|
|
56
|
+
if (seen.has(member.name)) {
|
|
57
|
+
return `duplicate member name '${member.name}' (use the name=path form to label members distinctly)`;
|
|
58
|
+
}
|
|
59
|
+
seen.add(member.name);
|
|
60
|
+
if (!path.isAbsolute(member.path)) {
|
|
61
|
+
return `member path '${member.path}' must be absolute`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
export function memberLabelProblem(label) {
|
|
67
|
+
return folderStyleNameProblem(label, 'member name');
|
|
68
|
+
}
|
|
69
|
+
const WorksetMemberSchema = z
|
|
70
|
+
.object({
|
|
71
|
+
name: z.string(),
|
|
72
|
+
path: z.string(),
|
|
73
|
+
})
|
|
74
|
+
.strict();
|
|
75
|
+
const WorksetEntrySchema = z
|
|
76
|
+
.object({
|
|
77
|
+
tool: z.string().min(1).optional(),
|
|
78
|
+
members: z.array(WorksetMemberSchema),
|
|
79
|
+
})
|
|
80
|
+
.strict();
|
|
81
|
+
const WorksetsStateSchema = z
|
|
82
|
+
.object({
|
|
83
|
+
version: z.literal(1),
|
|
84
|
+
worksets: z.record(z.string(), WorksetEntrySchema),
|
|
85
|
+
})
|
|
86
|
+
.strict();
|
|
87
|
+
function invalidWorksetsFileError(message, options) {
|
|
88
|
+
return new StoreError(`Invalid worksets file: ${message}`, 'invalid_workset_file', {
|
|
89
|
+
target: 'workset.file',
|
|
90
|
+
fix: `Repair or remove ${getWorksetsFilePath(options)}.`,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
export function parseWorksetsState(content, options = {}) {
|
|
94
|
+
let raw;
|
|
95
|
+
try {
|
|
96
|
+
raw = parseYaml(content);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
100
|
+
throw invalidWorksetsFileError(message, options);
|
|
101
|
+
}
|
|
102
|
+
const result = WorksetsStateSchema.safeParse(raw);
|
|
103
|
+
if (!result.success) {
|
|
104
|
+
throw invalidWorksetsFileError(formatZodIssues(result.error), options);
|
|
105
|
+
}
|
|
106
|
+
for (const [name, entry] of Object.entries(result.data.worksets)) {
|
|
107
|
+
if (!isKebabId(name)) {
|
|
108
|
+
throw invalidWorksetsFileError(`workset name '${name}' ${KEBAB_ID_DESCRIPTION}`, options);
|
|
109
|
+
}
|
|
110
|
+
const problem = memberListProblem(entry.members);
|
|
111
|
+
if (problem !== null) {
|
|
112
|
+
throw invalidWorksetsFileError(`workset '${name}': ${problem}`, options);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return result.data;
|
|
116
|
+
}
|
|
117
|
+
export function serializeWorksetsState(state, options = {}) {
|
|
118
|
+
const result = WorksetsStateSchema.safeParse(state);
|
|
119
|
+
if (!result.success) {
|
|
120
|
+
throw invalidWorksetsFileError(formatZodIssues(result.error), options);
|
|
121
|
+
}
|
|
122
|
+
// The strict schema already guarantees the entry shape; the sort is
|
|
123
|
+
// the only real work here.
|
|
124
|
+
return stringifyYaml({
|
|
125
|
+
version: 1,
|
|
126
|
+
worksets: Object.fromEntries(Object.entries(result.data.worksets).sort(([a], [b]) => a.localeCompare(b))),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/** Absent file reads as the empty state; a corrupt file throws. */
|
|
130
|
+
export async function readWorksetsState(options = {}) {
|
|
131
|
+
const filePath = getWorksetsFilePath(options);
|
|
132
|
+
if (!(await pathIsFile(filePath))) {
|
|
133
|
+
return { version: 1, worksets: {} };
|
|
134
|
+
}
|
|
135
|
+
return parseWorksetsState(await fs.readFile(filePath, 'utf-8'), options);
|
|
136
|
+
}
|
|
137
|
+
const worksetsLockError = makeLockErrorFactory({
|
|
138
|
+
createSubject: 'the worksets lock file',
|
|
139
|
+
busyMessage: 'The worksets file is busy.',
|
|
140
|
+
code: 'workset_file_busy',
|
|
141
|
+
target: 'workset.file',
|
|
142
|
+
});
|
|
143
|
+
export async function updateWorksetsState(updater, options = {}) {
|
|
144
|
+
return withWorksetsLock(async (state) => {
|
|
145
|
+
const next = await updater(state);
|
|
146
|
+
await writeFileAtomically(getWorksetsFilePath(options), serializeWorksetsState(next, options));
|
|
147
|
+
return next;
|
|
148
|
+
}, options);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Lock-scoped read without a write-back of the saved-views file.
|
|
152
|
+
* `open` uses this to read the state and regenerate the derived
|
|
153
|
+
* .code-workspace coherently; the lock is released before any spawn.
|
|
154
|
+
*/
|
|
155
|
+
export async function withWorksetsLock(fn, options = {}) {
|
|
156
|
+
const lockPath = `${getWorksetsFilePath(options)}.lock`;
|
|
157
|
+
const lock = await acquireFileLock({
|
|
158
|
+
lockPath,
|
|
159
|
+
errorFor: worksetsLockError,
|
|
160
|
+
});
|
|
161
|
+
try {
|
|
162
|
+
return await fn(await readWorksetsState(options));
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
await releaseFileLock(lock, lockPath);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export function worksetNotFoundError(name, state) {
|
|
169
|
+
const savedNames = Object.keys(state.worksets).sort((a, b) => a.localeCompare(b));
|
|
170
|
+
return new StoreError(`Workset '${name}' is not saved on this machine.`, 'workset_not_found', {
|
|
171
|
+
target: 'workset.name',
|
|
172
|
+
fix: savedNames.length > 0
|
|
173
|
+
? `Saved worksets: ${savedNames.join(', ')}. See them with: openspec workset list`
|
|
174
|
+
: `Create it first: openspec workset create ${name}`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
export function withWorkset(state, workset) {
|
|
178
|
+
if (state.worksets[workset.name] !== undefined) {
|
|
179
|
+
throw new StoreError(`Workset '${workset.name}' already exists.`, 'workset_exists', {
|
|
180
|
+
target: 'workset.name',
|
|
181
|
+
fix: `Choose another name, or remove it first: openspec workset remove ${workset.name}`,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
version: 1,
|
|
186
|
+
worksets: {
|
|
187
|
+
...state.worksets,
|
|
188
|
+
[workset.name]: {
|
|
189
|
+
...(workset.tool !== undefined ? { tool: workset.tool } : {}),
|
|
190
|
+
members: workset.members,
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export function withoutWorkset(state, name) {
|
|
196
|
+
if (state.worksets[name] === undefined) {
|
|
197
|
+
throw worksetNotFoundError(name, state);
|
|
198
|
+
}
|
|
199
|
+
const remaining = { ...state.worksets };
|
|
200
|
+
delete remaining[name];
|
|
201
|
+
return { version: 1, worksets: remaining };
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Removes a saved workset and its derived .code-workspace under one
|
|
205
|
+
* lock. The derived-file cleanup runs AFTER the durable write (a
|
|
206
|
+
* failed write must not have already destroyed the artifact); a
|
|
207
|
+
* never-opened workset has no file - ENOENT is fine.
|
|
208
|
+
*/
|
|
209
|
+
export async function removeWorkset(name, options = {}) {
|
|
210
|
+
await withWorksetsLock(async (state) => {
|
|
211
|
+
const next = withoutWorkset(state, name);
|
|
212
|
+
await writeFileAtomically(getWorksetsFilePath(options), serializeWorksetsState(next, options));
|
|
213
|
+
await fs.rm(getWorksetCodeWorkspacePath(name, options), { force: true });
|
|
214
|
+
}, options);
|
|
215
|
+
}
|
|
216
|
+
function toWorkset(name, entry) {
|
|
217
|
+
return {
|
|
218
|
+
name,
|
|
219
|
+
...(entry.tool !== undefined ? { tool: entry.tool } : {}),
|
|
220
|
+
members: entry.members,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
export function listWorksets(state) {
|
|
224
|
+
return Object.entries(state.worksets)
|
|
225
|
+
.map(([name, entry]) => toWorkset(name, entry))
|
|
226
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
227
|
+
}
|
|
228
|
+
export function getWorkset(state, name) {
|
|
229
|
+
const entry = state.worksets[name];
|
|
230
|
+
return entry === undefined ? null : toWorkset(name, entry);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* The generated .code-workspace content: members in saved order with
|
|
234
|
+
* their saved names, absolute paths, two-space JSON, trailing newline
|
|
235
|
+
* (the working-set builder's conventions).
|
|
236
|
+
*/
|
|
237
|
+
export function buildWorksetCodeWorkspaceJson(members) {
|
|
238
|
+
return (JSON.stringify({
|
|
239
|
+
folders: members.map((member) => ({
|
|
240
|
+
name: member.name,
|
|
241
|
+
path: member.path,
|
|
242
|
+
})),
|
|
243
|
+
}, null, 2) + '\n');
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=worksets.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** One rendering for zod issues across every state/config parser. */
|
|
2
|
+
export function formatZodIssues(error, fallbackLocation = 'root') {
|
|
3
|
+
return error.issues
|
|
4
|
+
.map((issue) => {
|
|
5
|
+
const location = issue.path.length > 0 ? issue.path.join('.') : fallbackLocation;
|
|
6
|
+
return `${location}: ${issue.message}`;
|
|
7
|
+
})
|
|
8
|
+
.join('; ');
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=zod-issues.js.map
|
|
@@ -4,7 +4,7 @@ import * as yaml from 'yaml';
|
|
|
4
4
|
import { ChangeMetadataSchema } from '../core/change-metadata/index.js';
|
|
5
5
|
import { listSchemas } from '../core/artifact-graph/resolver.js';
|
|
6
6
|
import { readProjectConfig } from '../core/project-config.js';
|
|
7
|
-
const METADATA_FILENAME = '.openspec.yaml';
|
|
7
|
+
export const METADATA_FILENAME = '.openspec.yaml';
|
|
8
8
|
/**
|
|
9
9
|
* Error thrown when change metadata validation fails.
|
|
10
10
|
*/
|
|
@@ -109,8 +109,23 @@ export async function createChange(projectRoot, name, options = {}) {
|
|
|
109
109
|
if (await FileSystemUtils.directoryExists(changeDir)) {
|
|
110
110
|
throw new Error(`Change '${name}' already exists at ${changeDir}`);
|
|
111
111
|
}
|
|
112
|
+
// Creating a change may scaffold or complete the root itself (an
|
|
113
|
+
// implicit root, or a config-only/incomplete clone). Never leave a
|
|
114
|
+
// half-root behind that doctor immediately calls unhealthy: ensure
|
|
115
|
+
// specs/ and changes/archive/ exist, and write a config only when
|
|
116
|
+
// none exists. The config records the PROJECT default schema, never
|
|
117
|
+
// a one-change --schema override.
|
|
118
|
+
const openspecDir = path.join(projectRoot, 'openspec');
|
|
112
119
|
// Create the directory (including parent directories if needed)
|
|
113
120
|
await FileSystemUtils.createDirectory(changeDir);
|
|
121
|
+
await FileSystemUtils.createDirectory(path.join(openspecDir, 'specs'));
|
|
122
|
+
await FileSystemUtils.createDirectory(path.join(openspecDir, 'changes', 'archive'));
|
|
123
|
+
const configPath = path.join(openspecDir, 'config.yaml');
|
|
124
|
+
const configYmlPath = path.join(openspecDir, 'config.yml');
|
|
125
|
+
if (!(await FileSystemUtils.fileExists(configPath)) &&
|
|
126
|
+
!(await FileSystemUtils.fileExists(configYmlPath))) {
|
|
127
|
+
await FileSystemUtils.writeFile(configPath, `schema: ${defaultSchema}\n`);
|
|
128
|
+
}
|
|
114
129
|
// Write metadata file with schema and creation date
|
|
115
130
|
const today = new Date().toISOString().split('T')[0];
|
|
116
131
|
writeChangeMetadata(changeDir, {
|
|
@@ -2,6 +2,36 @@ import * as nodeFs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
const fs = nodeFs.promises;
|
|
4
4
|
const { constants: fsConstants } = nodeFs;
|
|
5
|
+
function hasOwnerGroupOrOtherWriteBit(stats) {
|
|
6
|
+
return (stats.mode & 0o222) !== 0;
|
|
7
|
+
}
|
|
8
|
+
function hasOwnerGroupOrOtherExecuteBit(stats) {
|
|
9
|
+
return (stats.mode & 0o111) !== 0;
|
|
10
|
+
}
|
|
11
|
+
async function hasWritableModeAndAccess(targetPath) {
|
|
12
|
+
try {
|
|
13
|
+
const stats = await fs.stat(targetPath);
|
|
14
|
+
// POSIX root can often write despite mode bits, but OpenSpec should respect
|
|
15
|
+
// explicit read-only file/directory modes when deciding whether an install
|
|
16
|
+
// path is user-writable. This also keeps permission checks deterministic in
|
|
17
|
+
// root-run CI containers. On Windows, chmod mode bits are not authoritative,
|
|
18
|
+
// so rely on fs.access below.
|
|
19
|
+
if (process.platform !== 'win32' && !hasOwnerGroupOrOtherWriteBit(stats)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (process.platform !== 'win32' && stats.isDirectory() && !hasOwnerGroupOrOtherExecuteBit(stats)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const accessMode = stats.isDirectory()
|
|
26
|
+
? fsConstants.W_OK | fsConstants.X_OK
|
|
27
|
+
: fsConstants.W_OK;
|
|
28
|
+
await fs.access(targetPath, accessMode);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
5
35
|
function isMarkerOnOwnLine(content, markerIndex, markerLength) {
|
|
6
36
|
let leftIndex = markerIndex - 1;
|
|
7
37
|
while (leftIndex >= 0 && content[leftIndex] !== '\n') {
|
|
@@ -131,18 +161,13 @@ export class FileSystemUtils {
|
|
|
131
161
|
static async canWriteFile(filePath) {
|
|
132
162
|
try {
|
|
133
163
|
const stats = await fs.stat(filePath);
|
|
134
|
-
if (
|
|
135
|
-
return
|
|
164
|
+
if (stats.isDirectory()) {
|
|
165
|
+
return hasWritableModeAndAccess(filePath);
|
|
136
166
|
}
|
|
137
|
-
|
|
138
|
-
// Use fs.access with W_OK to check actual write permissions cross-platform.
|
|
139
|
-
try {
|
|
140
|
-
await fs.access(filePath, fsConstants.W_OK);
|
|
167
|
+
if (!stats.isFile()) {
|
|
141
168
|
return true;
|
|
142
169
|
}
|
|
143
|
-
|
|
144
|
-
return false;
|
|
145
|
-
}
|
|
170
|
+
return hasWritableModeAndAccess(filePath);
|
|
146
171
|
}
|
|
147
172
|
catch (error) {
|
|
148
173
|
if (error.code === 'ENOENT') {
|
|
@@ -153,14 +178,8 @@ export class FileSystemUtils {
|
|
|
153
178
|
// No existing parent directory found (edge case)
|
|
154
179
|
return false;
|
|
155
180
|
}
|
|
156
|
-
// Check if the existing parent directory is writable
|
|
157
|
-
|
|
158
|
-
await fs.access(existingDir, fsConstants.W_OK);
|
|
159
|
-
return true;
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
181
|
+
// Check if the existing parent directory is writable.
|
|
182
|
+
return hasWritableModeAndAccess(existingDir);
|
|
164
183
|
}
|
|
165
184
|
console.debug(`Unable to determine write permissions for ${filePath}: ${error.message}`);
|
|
166
185
|
return false;
|
|
@@ -3,6 +3,15 @@ export interface TaskProgress {
|
|
|
3
3
|
completed: number;
|
|
4
4
|
}
|
|
5
5
|
export declare function countTasksFromContent(content: string): TaskProgress;
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Computes a change's task progress by resolving its tracked-tasks artifact and
|
|
8
|
+
* counting checkboxes across every file matched by that artifact's `generates`
|
|
9
|
+
* glob — the same file-resolution `openspec status` uses to detect the tasks
|
|
10
|
+
* artifact (`resolveArtifactOutputs`) — so progress is no longer blind to nested
|
|
11
|
+
* `tasks.md` files (#1202). Falls back to a single top-level `tasks.md` (exactly
|
|
12
|
+
* as before) when the schema is unresolvable, no tracked-tasks artifact is found,
|
|
13
|
+
* or the glob matches no file. Never throws.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getTaskProgressForChange(changesDir: string, changeName: string, projectRoot: string): Promise<TaskProgress>;
|
|
7
16
|
export declare function formatTaskStatus(progress: TaskProgress): string;
|
|
8
17
|
//# sourceMappingURL=task-progress.d.ts.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { resolveArtifactOutputs, resolveSchema } from '../core/artifact-graph/index.js';
|
|
4
|
+
import { resolveSchemaForChange } from './change-metadata.js';
|
|
3
5
|
const TASK_PATTERN = /^[-*]\s+\[[\sx]\]/i;
|
|
4
6
|
const COMPLETED_TASK_PATTERN = /^[-*]\s+\[x\]/i;
|
|
5
7
|
export function countTasksFromContent(content) {
|
|
@@ -16,8 +18,37 @@ export function countTasksFromContent(content) {
|
|
|
16
18
|
}
|
|
17
19
|
return { total, completed };
|
|
18
20
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Identifies the change's tracked-tasks artifact: the artifact whose `generates`
|
|
23
|
+
* equals the schema's `apply.tracks` value, falling back to the artifact with id
|
|
24
|
+
* `tasks` when no `apply` block declares what it tracks. (`apply.tracks` is a
|
|
25
|
+
* filename that *selects* the artifact; the glob is that artifact's `generates`.)
|
|
26
|
+
*/
|
|
27
|
+
function findTrackedTasksArtifact(schema) {
|
|
28
|
+
const tracks = schema.apply?.tracks;
|
|
29
|
+
if (tracks != null) {
|
|
30
|
+
return schema.artifacts.find((a) => a.generates === tracks);
|
|
31
|
+
}
|
|
32
|
+
return schema.artifacts.find((a) => a.id === 'tasks');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolves the tracked-tasks artifact's output glob for a change, or undefined
|
|
36
|
+
* when the schema cannot be resolved or no tracked-tasks artifact exists.
|
|
37
|
+
* `resolveSchema` throws on an unresolvable/misnamed schema; we swallow that so
|
|
38
|
+
* the caller falls back to a single top-level `tasks.md` and never crashes.
|
|
39
|
+
*/
|
|
40
|
+
function resolveTrackedTasksGlob(changeDir, projectRoot) {
|
|
41
|
+
try {
|
|
42
|
+
const schemaName = resolveSchemaForChange(changeDir, undefined, projectRoot);
|
|
43
|
+
const schema = resolveSchema(schemaName, projectRoot);
|
|
44
|
+
return findTrackedTasksArtifact(schema)?.generates;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function countSingleTopLevelTasksFile(changeDir) {
|
|
51
|
+
const tasksPath = path.join(changeDir, 'tasks.md');
|
|
21
52
|
try {
|
|
22
53
|
const content = await fs.readFile(tasksPath, 'utf-8');
|
|
23
54
|
return countTasksFromContent(content);
|
|
@@ -26,6 +57,39 @@ export async function getTaskProgressForChange(changesDir, changeName) {
|
|
|
26
57
|
return { total: 0, completed: 0 };
|
|
27
58
|
}
|
|
28
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Computes a change's task progress by resolving its tracked-tasks artifact and
|
|
62
|
+
* counting checkboxes across every file matched by that artifact's `generates`
|
|
63
|
+
* glob — the same file-resolution `openspec status` uses to detect the tasks
|
|
64
|
+
* artifact (`resolveArtifactOutputs`) — so progress is no longer blind to nested
|
|
65
|
+
* `tasks.md` files (#1202). Falls back to a single top-level `tasks.md` (exactly
|
|
66
|
+
* as before) when the schema is unresolvable, no tracked-tasks artifact is found,
|
|
67
|
+
* or the glob matches no file. Never throws.
|
|
68
|
+
*/
|
|
69
|
+
export async function getTaskProgressForChange(changesDir, changeName, projectRoot) {
|
|
70
|
+
const changeDir = path.join(changesDir, changeName);
|
|
71
|
+
const generates = resolveTrackedTasksGlob(changeDir, projectRoot);
|
|
72
|
+
if (generates) {
|
|
73
|
+
const files = resolveArtifactOutputs(changeDir, generates);
|
|
74
|
+
if (files.length > 0) {
|
|
75
|
+
let total = 0;
|
|
76
|
+
let completed = 0;
|
|
77
|
+
for (const file of files) {
|
|
78
|
+
try {
|
|
79
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
80
|
+
const progress = countTasksFromContent(content);
|
|
81
|
+
total += progress.total;
|
|
82
|
+
completed += progress.completed;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Swallow files that vanish between glob and read, as before.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { total, completed };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return countSingleTopLevelTasksFile(changeDir);
|
|
92
|
+
}
|
|
29
93
|
export function formatTaskStatus(progress) {
|
|
30
94
|
if (progress.total === 0)
|
|
31
95
|
return 'No tasks';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fission-ai/openspec",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0-beta.1",
|
|
4
4
|
"description": "AI-native system for spec-driven development",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openspec",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"author": "OpenSpec Contributors",
|
|
19
19
|
"type": "module",
|
|
20
|
+
"packageManager": "pnpm@9.15.9",
|
|
20
21
|
"publishConfig": {
|
|
21
22
|
"access": "public"
|
|
22
23
|
},
|
|
@@ -38,6 +39,24 @@
|
|
|
38
39
|
"!dist/**/__tests__",
|
|
39
40
|
"!dist/**/*.map"
|
|
40
41
|
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"lint": "eslint src/",
|
|
44
|
+
"build": "node build.js",
|
|
45
|
+
"dev": "tsc --watch",
|
|
46
|
+
"dev:cli": "pnpm build && node bin/openspec.js",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:watch": "vitest",
|
|
49
|
+
"test:ui": "vitest --ui",
|
|
50
|
+
"test:coverage": "vitest --coverage",
|
|
51
|
+
"test:postinstall": "node scripts/postinstall.js",
|
|
52
|
+
"prepare": "pnpm run build",
|
|
53
|
+
"prepublishOnly": "pnpm run build",
|
|
54
|
+
"postinstall": "node scripts/postinstall.js",
|
|
55
|
+
"check:pack-version": "node scripts/pack-version-check.mjs",
|
|
56
|
+
"release": "pnpm run release:ci",
|
|
57
|
+
"release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
|
|
58
|
+
"changeset": "changeset"
|
|
59
|
+
},
|
|
41
60
|
"engines": {
|
|
42
61
|
"node": ">=20.19.0"
|
|
43
62
|
},
|
|
@@ -45,15 +64,15 @@
|
|
|
45
64
|
"@changesets/changelog-github": "^0.5.2",
|
|
46
65
|
"@changesets/cli": "^2.27.7",
|
|
47
66
|
"@types/node": "^24.2.0",
|
|
48
|
-
"@vitest/ui": "^3.2.
|
|
67
|
+
"@vitest/ui": "^3.2.6",
|
|
49
68
|
"eslint": "^9.39.2",
|
|
50
69
|
"typescript": "^5.9.3",
|
|
51
|
-
"typescript-eslint": "^8.
|
|
52
|
-
"vitest": "^3.2.
|
|
70
|
+
"typescript-eslint": "^8.62.0",
|
|
71
|
+
"vitest": "^3.2.6"
|
|
53
72
|
},
|
|
54
73
|
"dependencies": {
|
|
55
|
-
"@inquirer/core": "^10.
|
|
56
|
-
"@inquirer/prompts": "^7.
|
|
74
|
+
"@inquirer/core": "^10.3.2",
|
|
75
|
+
"@inquirer/prompts": "^7.10.1",
|
|
57
76
|
"chalk": "^5.5.0",
|
|
58
77
|
"commander": "^14.0.0",
|
|
59
78
|
"cross-spawn": "7.0.6",
|
|
@@ -62,21 +81,5 @@
|
|
|
62
81
|
"posthog-node": "^5.20.0",
|
|
63
82
|
"yaml": "^2.8.2",
|
|
64
83
|
"zod": "^4.0.17"
|
|
65
|
-
},
|
|
66
|
-
"scripts": {
|
|
67
|
-
"lint": "eslint src/",
|
|
68
|
-
"build": "node build.js",
|
|
69
|
-
"dev": "tsc --watch",
|
|
70
|
-
"dev:cli": "pnpm build && node bin/openspec.js",
|
|
71
|
-
"test": "vitest run",
|
|
72
|
-
"test:watch": "vitest",
|
|
73
|
-
"test:ui": "vitest --ui",
|
|
74
|
-
"test:coverage": "vitest --coverage",
|
|
75
|
-
"test:postinstall": "node scripts/postinstall.js",
|
|
76
|
-
"postinstall": "node scripts/postinstall.js",
|
|
77
|
-
"check:pack-version": "node scripts/pack-version-check.mjs",
|
|
78
|
-
"release": "pnpm run release:ci",
|
|
79
|
-
"release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
|
|
80
|
-
"changeset": "changeset"
|
|
81
84
|
}
|
|
82
|
-
}
|
|
85
|
+
}
|