@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.
Files changed (241) hide show
  1. package/README.md +37 -4
  2. package/dist/cli/index.d.ts +5 -0
  3. package/dist/cli/index.js +95 -95
  4. package/dist/commands/change.d.ts +5 -2
  5. package/dist/commands/change.js +23 -55
  6. package/dist/commands/config.js +6 -65
  7. package/dist/commands/context.d.ts +3 -0
  8. package/dist/commands/context.js +155 -0
  9. package/dist/commands/doctor.d.ts +8 -0
  10. package/dist/commands/doctor.js +163 -0
  11. package/dist/commands/shared-gather.d.ts +14 -0
  12. package/dist/commands/shared-gather.js +31 -0
  13. package/dist/commands/shared-output.d.ts +18 -0
  14. package/dist/commands/shared-output.js +61 -0
  15. package/dist/commands/show.d.ts +11 -6
  16. package/dist/commands/show.js +70 -25
  17. package/dist/commands/spec.d.ts +5 -1
  18. package/dist/commands/spec.js +15 -4
  19. package/dist/commands/store.d.ts +3 -0
  20. package/dist/commands/store.js +547 -0
  21. package/dist/commands/validate.d.ts +10 -0
  22. package/dist/commands/validate.js +88 -40
  23. package/dist/commands/workflow/index.d.ts +0 -2
  24. package/dist/commands/workflow/index.js +0 -1
  25. package/dist/commands/workflow/instructions.d.ts +11 -1
  26. package/dist/commands/workflow/instructions.js +71 -22
  27. package/dist/commands/workflow/new-change.d.ts +6 -3
  28. package/dist/commands/workflow/new-change.js +42 -77
  29. package/dist/commands/workflow/shared.d.ts +15 -3
  30. package/dist/commands/workflow/shared.js +20 -3
  31. package/dist/commands/workflow/status.d.ts +2 -0
  32. package/dist/commands/workflow/status.js +19 -17
  33. package/dist/commands/workset-input.d.ts +19 -0
  34. package/dist/commands/workset-input.js +112 -0
  35. package/dist/commands/workset-prompts.d.ts +12 -0
  36. package/dist/commands/workset-prompts.js +143 -0
  37. package/dist/commands/workset.d.ts +25 -0
  38. package/dist/commands/workset.js +446 -0
  39. package/dist/core/archive.d.ts +17 -6
  40. package/dist/core/archive.js +220 -68
  41. package/dist/core/artifact-graph/index.d.ts +1 -1
  42. package/dist/core/artifact-graph/instruction-loader.d.ts +18 -13
  43. package/dist/core/artifact-graph/instruction-loader.js +8 -31
  44. package/dist/core/change-metadata/schema.d.ts +1 -0
  45. package/dist/core/change-metadata/schema.js +4 -2
  46. package/dist/core/change-status-policy.d.ts +5 -18
  47. package/dist/core/change-status-policy.js +2 -37
  48. package/dist/core/command-generation/adapters/bob.js +1 -14
  49. package/dist/core/command-generation/adapters/claude.d.ts +1 -1
  50. package/dist/core/command-generation/adapters/claude.js +4 -15
  51. package/dist/core/command-generation/adapters/cursor.js +1 -14
  52. package/dist/core/command-generation/adapters/index.d.ts +2 -0
  53. package/dist/core/command-generation/adapters/index.js +2 -0
  54. package/dist/core/command-generation/adapters/oh-my-pi.d.ts +21 -0
  55. package/dist/core/command-generation/adapters/oh-my-pi.js +44 -0
  56. package/dist/core/command-generation/adapters/pi.js +1 -14
  57. package/dist/core/command-generation/adapters/trae.d.ts +13 -0
  58. package/dist/core/command-generation/adapters/trae.js +48 -0
  59. package/dist/core/command-generation/adapters/windsurf.js +1 -14
  60. package/dist/core/command-generation/registry.js +4 -0
  61. package/dist/core/command-generation/yaml.d.ts +22 -0
  62. package/dist/core/command-generation/yaml.js +38 -0
  63. package/dist/core/completions/command-registry.js +87 -289
  64. package/dist/core/completions/generators/zsh-generator.js +3 -1
  65. package/dist/core/completions/installers/bash-installer.js +3 -0
  66. package/dist/core/completions/installers/fish-installer.js +8 -0
  67. package/dist/core/completions/installers/powershell-installer.js +30 -2
  68. package/dist/core/completions/installers/zsh-installer.js +3 -0
  69. package/dist/core/completions/shared-flags.d.ts +1 -0
  70. package/dist/core/completions/shared-flags.js +5 -0
  71. package/dist/core/config-schema.d.ts +2 -1
  72. package/dist/core/config-schema.js +26 -0
  73. package/dist/core/config.js +1 -0
  74. package/dist/core/file-state.d.ts +36 -0
  75. package/dist/core/file-state.js +112 -0
  76. package/dist/core/global-config.d.ts +2 -0
  77. package/dist/core/id.d.ts +17 -0
  78. package/dist/core/id.js +30 -0
  79. package/dist/core/index.d.ts +3 -3
  80. package/dist/core/index.js +3 -3
  81. package/dist/core/init.js +27 -6
  82. package/dist/core/list.d.ts +2 -0
  83. package/dist/core/list.js +37 -14
  84. package/dist/core/openers.d.ts +77 -0
  85. package/dist/core/openers.js +251 -0
  86. package/dist/core/openspec-root.d.ts +45 -0
  87. package/dist/core/openspec-root.js +195 -0
  88. package/dist/core/parsers/change-parser.js +2 -1
  89. package/dist/core/parsers/markdown-parser.d.ts +0 -3
  90. package/dist/core/parsers/markdown-parser.js +5 -59
  91. package/dist/core/parsers/requirement-blocks.d.ts +11 -0
  92. package/dist/core/parsers/requirement-blocks.js +42 -8
  93. package/dist/core/parsers/requirement-text.d.ts +55 -0
  94. package/dist/core/parsers/requirement-text.js +136 -0
  95. package/dist/core/planning-home.d.ts +1 -6
  96. package/dist/core/planning-home.js +2 -43
  97. package/dist/core/profile-sync-drift.js +1 -0
  98. package/dist/core/profiles.d.ts +2 -2
  99. package/dist/core/profiles.js +2 -1
  100. package/dist/core/project-config.d.ts +44 -1
  101. package/dist/core/project-config.js +160 -10
  102. package/dist/core/references.d.ts +63 -0
  103. package/dist/core/references.js +310 -0
  104. package/dist/core/relationship-health.d.ts +65 -0
  105. package/dist/core/relationship-health.js +64 -0
  106. package/dist/core/root-selection.d.ts +122 -0
  107. package/dist/core/root-selection.js +337 -0
  108. package/dist/core/schemas/base.schema.js +7 -2
  109. package/dist/core/shared/allowed-tools.d.ts +12 -0
  110. package/dist/core/shared/allowed-tools.js +12 -0
  111. package/dist/core/shared/skill-generation.js +5 -1
  112. package/dist/core/shared/tool-detection.d.ts +2 -2
  113. package/dist/core/shared/tool-detection.js +2 -0
  114. package/dist/core/specs-apply.d.ts +6 -1
  115. package/dist/core/specs-apply.js +41 -5
  116. package/dist/core/store/errors.d.ts +20 -0
  117. package/dist/core/{context-store → store}/errors.js +3 -3
  118. package/dist/core/store/foundation.d.ts +56 -0
  119. package/dist/core/store/foundation.js +251 -0
  120. package/dist/core/store/git.d.ts +23 -0
  121. package/dist/core/store/git.js +137 -0
  122. package/dist/core/{context-store → store}/index.d.ts +0 -1
  123. package/dist/core/{context-store → store}/index.js +0 -1
  124. package/dist/core/store/operations.d.ts +114 -0
  125. package/dist/core/store/operations.js +803 -0
  126. package/dist/core/store/registry.d.ts +58 -0
  127. package/dist/core/store/registry.js +275 -0
  128. package/dist/core/templates/skill-templates.d.ts +1 -0
  129. package/dist/core/templates/skill-templates.js +1 -0
  130. package/dist/core/templates/workflows/apply-change.js +5 -4
  131. package/dist/core/templates/workflows/archive-change.js +5 -4
  132. package/dist/core/templates/workflows/bulk-archive-change.js +5 -4
  133. package/dist/core/templates/workflows/continue-change.js +7 -2
  134. package/dist/core/templates/workflows/explore.js +5 -0
  135. package/dist/core/templates/workflows/ff-change.js +5 -0
  136. package/dist/core/templates/workflows/new-change.js +5 -0
  137. package/dist/core/templates/workflows/onboard.js +4 -1
  138. package/dist/core/templates/workflows/propose.js +5 -0
  139. package/dist/core/templates/workflows/store-selection.d.ts +8 -0
  140. package/dist/core/templates/workflows/store-selection.js +8 -0
  141. package/dist/core/templates/workflows/sync-specs.js +5 -4
  142. package/dist/core/templates/workflows/update-change.d.ts +10 -0
  143. package/dist/core/templates/workflows/update-change.js +167 -0
  144. package/dist/core/templates/workflows/verify-change.js +5 -4
  145. package/dist/core/update.js +4 -4
  146. package/dist/core/validation/validator.d.ts +7 -0
  147. package/dist/core/validation/validator.js +101 -38
  148. package/dist/core/view.js +1 -1
  149. package/dist/core/working-set.d.ts +47 -0
  150. package/dist/core/working-set.js +43 -0
  151. package/dist/core/worksets.d.ts +75 -0
  152. package/dist/core/worksets.js +245 -0
  153. package/dist/core/zod-issues.d.ts +4 -0
  154. package/dist/core/zod-issues.js +10 -0
  155. package/dist/utils/change-metadata.d.ts +1 -0
  156. package/dist/utils/change-metadata.js +1 -1
  157. package/dist/utils/change-utils.js +15 -0
  158. package/dist/utils/file-system.js +36 -17
  159. package/dist/utils/task-progress.d.ts +10 -1
  160. package/dist/utils/task-progress.js +66 -2
  161. package/package.json +26 -23
  162. package/dist/commands/context-store.d.ts +0 -3
  163. package/dist/commands/context-store.js +0 -475
  164. package/dist/commands/initiative.d.ts +0 -13
  165. package/dist/commands/initiative.js +0 -318
  166. package/dist/commands/workflow/initiative-link.d.ts +0 -24
  167. package/dist/commands/workflow/initiative-link.js +0 -47
  168. package/dist/commands/workflow/set-change.d.ts +0 -13
  169. package/dist/commands/workflow/set-change.js +0 -87
  170. package/dist/commands/workspace/context-status.d.ts +0 -4
  171. package/dist/commands/workspace/context-status.js +0 -59
  172. package/dist/commands/workspace/open-target-selection.d.ts +0 -13
  173. package/dist/commands/workspace/open-target-selection.js +0 -146
  174. package/dist/commands/workspace/open-view.d.ts +0 -62
  175. package/dist/commands/workspace/open-view.js +0 -249
  176. package/dist/commands/workspace/open.d.ts +0 -37
  177. package/dist/commands/workspace/open.js +0 -110
  178. package/dist/commands/workspace/opener-selection.d.ts +0 -11
  179. package/dist/commands/workspace/opener-selection.js +0 -98
  180. package/dist/commands/workspace/operations.d.ts +0 -29
  181. package/dist/commands/workspace/operations.js +0 -543
  182. package/dist/commands/workspace/prompt-theme.d.ts +0 -29
  183. package/dist/commands/workspace/prompt-theme.js +0 -24
  184. package/dist/commands/workspace/registration.d.ts +0 -13
  185. package/dist/commands/workspace/registration.js +0 -84
  186. package/dist/commands/workspace/selection.d.ts +0 -8
  187. package/dist/commands/workspace/selection.js +0 -104
  188. package/dist/commands/workspace/setup-prompts.d.ts +0 -13
  189. package/dist/commands/workspace/setup-prompts.js +0 -121
  190. package/dist/commands/workspace/types.d.ts +0 -103
  191. package/dist/commands/workspace/types.js +0 -36
  192. package/dist/commands/workspace.d.ts +0 -5
  193. package/dist/commands/workspace.js +0 -574
  194. package/dist/core/collections/index.d.ts +0 -3
  195. package/dist/core/collections/index.js +0 -3
  196. package/dist/core/collections/initiatives/collection.d.ts +0 -4
  197. package/dist/core/collections/initiatives/collection.js +0 -17
  198. package/dist/core/collections/initiatives/index.d.ts +0 -6
  199. package/dist/core/collections/initiatives/index.js +0 -6
  200. package/dist/core/collections/initiatives/operations.d.ts +0 -49
  201. package/dist/core/collections/initiatives/operations.js +0 -175
  202. package/dist/core/collections/initiatives/resolution.d.ts +0 -87
  203. package/dist/core/collections/initiatives/resolution.js +0 -374
  204. package/dist/core/collections/initiatives/schema.d.ts +0 -41
  205. package/dist/core/collections/initiatives/schema.js +0 -134
  206. package/dist/core/collections/initiatives/templates.d.ts +0 -12
  207. package/dist/core/collections/initiatives/templates.js +0 -90
  208. package/dist/core/collections/runtime.d.ts +0 -46
  209. package/dist/core/collections/runtime.js +0 -194
  210. package/dist/core/context-store/binding.d.ts +0 -53
  211. package/dist/core/context-store/binding.js +0 -197
  212. package/dist/core/context-store/errors.d.ts +0 -20
  213. package/dist/core/context-store/foundation.d.ts +0 -55
  214. package/dist/core/context-store/foundation.js +0 -321
  215. package/dist/core/context-store/operations.d.ts +0 -85
  216. package/dist/core/context-store/operations.js +0 -528
  217. package/dist/core/context-store/registry.d.ts +0 -45
  218. package/dist/core/context-store/registry.js +0 -229
  219. package/dist/core/workspace/foundation.d.ts +0 -67
  220. package/dist/core/workspace/foundation.js +0 -295
  221. package/dist/core/workspace/index.d.ts +0 -8
  222. package/dist/core/workspace/index.js +0 -8
  223. package/dist/core/workspace/legacy-state.d.ts +0 -28
  224. package/dist/core/workspace/legacy-state.js +0 -200
  225. package/dist/core/workspace/link-input.d.ts +0 -9
  226. package/dist/core/workspace/link-input.js +0 -32
  227. package/dist/core/workspace/open-surface.d.ts +0 -45
  228. package/dist/core/workspace/open-surface.js +0 -215
  229. package/dist/core/workspace/openers.d.ts +0 -21
  230. package/dist/core/workspace/openers.js +0 -124
  231. package/dist/core/workspace/registry.d.ts +0 -24
  232. package/dist/core/workspace/registry.js +0 -146
  233. package/dist/core/workspace/skills.d.ts +0 -57
  234. package/dist/core/workspace/skills.js +0 -334
  235. package/dist/core/workspace/state-io.d.ts +0 -10
  236. package/dist/core/workspace/state-io.js +0 -121
  237. package/schemas/workspace-planning/schema.yaml +0 -72
  238. package/schemas/workspace-planning/templates/design.md +0 -33
  239. package/schemas/workspace-planning/templates/proposal.md +0 -28
  240. package/schemas/workspace-planning/templates/spec.md +0 -9
  241. package/schemas/workspace-planning/templates/tasks.md +0 -15
@@ -1,146 +0,0 @@
1
- import * as nodeFs from 'node:fs';
2
- import { z } from 'zod';
3
- import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
4
- import { getGlobalDataDir } from '../global-config.js';
5
- import { FileSystemUtils } from '../../utils/file-system.js';
6
- import { validateWorkspaceName } from './foundation.js';
7
- import { isWorkspaceRoot, readWorkspaceViewState } from './state-io.js';
8
- const fs = nodeFs.promises;
9
- export const MANAGED_WORKSPACES_DIR_NAME = 'workspaces';
10
- export const WORKSPACE_REGISTRY_FILE_NAME = 'registry.yaml';
11
- function joinWorkspacePath(basePath, ...segments) {
12
- return FileSystemUtils.joinPath(basePath, ...segments);
13
- }
14
- async function pathIsFile(filePath) {
15
- try {
16
- return (await fs.stat(filePath)).isFile();
17
- }
18
- catch {
19
- return false;
20
- }
21
- }
22
- async function pathIsDirectory(dirPath) {
23
- try {
24
- return (await fs.stat(dirPath)).isDirectory();
25
- }
26
- catch {
27
- return false;
28
- }
29
- }
30
- function formatZodIssues(error) {
31
- return error.issues
32
- .map((issue) => {
33
- const location = issue.path.length > 0 ? issue.path.join('.') : 'root';
34
- return `${location}: ${issue.message}`;
35
- })
36
- .join('; ');
37
- }
38
- function parseYamlObject(content, label) {
39
- try {
40
- return parseYaml(content);
41
- }
42
- catch (error) {
43
- const message = error instanceof Error ? error.message : String(error);
44
- throw new Error(`Invalid ${label}: ${message}`);
45
- }
46
- }
47
- function assertValidMapKeys(keys, validator, label) {
48
- for (const key of keys) {
49
- try {
50
- validator(key);
51
- }
52
- catch (error) {
53
- const message = error instanceof Error ? error.message : String(error);
54
- throw new Error(`Invalid ${label} '${key}': ${message}`);
55
- }
56
- }
57
- }
58
- const RegistryStateSchema = z.object({
59
- version: z.literal(1),
60
- workspaces: z.record(z.string(), z.string()),
61
- }).strict();
62
- export function getManagedWorkspacesDir(options = {}) {
63
- return joinWorkspacePath(options.globalDataDir ?? getGlobalDataDir(), MANAGED_WORKSPACES_DIR_NAME);
64
- }
65
- export function getManagedWorkspaceRoot(workspaceName, options = {}) {
66
- validateWorkspaceName(workspaceName);
67
- return joinWorkspacePath(getManagedWorkspacesDir(options), workspaceName);
68
- }
69
- export function getWorkspaceRegistryPath(options = {}) {
70
- return joinWorkspacePath(getManagedWorkspacesDir(options), WORKSPACE_REGISTRY_FILE_NAME);
71
- }
72
- export function parseWorkspaceRegistryState(content) {
73
- const raw = parseYamlObject(content, 'workspace registry state');
74
- const result = RegistryStateSchema.safeParse(raw);
75
- if (!result.success) {
76
- throw new Error(`Invalid workspace registry state: ${formatZodIssues(result.error)}`);
77
- }
78
- assertValidMapKeys(Object.keys(result.data.workspaces), validateWorkspaceName, 'workspace registry name');
79
- return {
80
- version: 1,
81
- workspaces: result.data.workspaces,
82
- };
83
- }
84
- export function serializeWorkspaceRegistryState(state) {
85
- assertValidMapKeys(Object.keys(state.workspaces), validateWorkspaceName, 'workspace registry name');
86
- for (const [workspaceName, workspaceRoot] of Object.entries(state.workspaces)) {
87
- if (typeof workspaceRoot !== 'string') {
88
- throw new Error(`Invalid workspace registry entry '${workspaceName}': path must be a string`);
89
- }
90
- }
91
- return stringifyYaml({
92
- version: 1,
93
- workspaces: state.workspaces,
94
- });
95
- }
96
- export function listWorkspaceRegistryEntries(registry) {
97
- return Object.entries(registry.workspaces)
98
- .map(([name, workspaceRoot]) => ({ name, workspaceRoot }))
99
- .sort((a, b) => a.name.localeCompare(b.name));
100
- }
101
- export async function listKnownWorkspaceEntries(options = {}) {
102
- const legacyRegistry = await readWorkspaceRegistryState(options);
103
- const workspaces = new Map(Object.entries(legacyRegistry?.workspaces ?? {}));
104
- for (const entry of await listManagedWorkspaceEntries(options)) {
105
- workspaces.set(entry.name, entry.workspaceRoot);
106
- }
107
- return [...workspaces.entries()]
108
- .map(([name, workspaceRoot]) => ({ name, workspaceRoot }))
109
- .sort((a, b) => a.name.localeCompare(b.name));
110
- }
111
- export async function listManagedWorkspaceEntries(options = {}) {
112
- const workspacesDir = getManagedWorkspacesDir(options);
113
- if (!(await pathIsDirectory(workspacesDir))) {
114
- return [];
115
- }
116
- const entries = await fs.readdir(workspacesDir, { withFileTypes: true });
117
- const workspaces = [];
118
- for (const entry of entries) {
119
- if (!entry.isDirectory()) {
120
- continue;
121
- }
122
- const workspaceRoot = FileSystemUtils.canonicalizeExistingPath(joinWorkspacePath(workspacesDir, entry.name));
123
- if (!(await isWorkspaceRoot(workspaceRoot))) {
124
- continue;
125
- }
126
- try {
127
- const state = await readWorkspaceViewState(workspaceRoot);
128
- workspaces.push({ name: state.name, workspaceRoot });
129
- }
130
- catch {
131
- workspaces.push({ name: entry.name, workspaceRoot });
132
- }
133
- }
134
- return workspaces.sort((a, b) => a.name.localeCompare(b.name));
135
- }
136
- export async function readWorkspaceRegistryState(options = {}) {
137
- const registryPath = getWorkspaceRegistryPath(options);
138
- if (!(await pathIsFile(registryPath))) {
139
- return null;
140
- }
141
- return parseWorkspaceRegistryState(await fs.readFile(registryPath, 'utf-8'));
142
- }
143
- export async function writeWorkspaceRegistryState(state, options = {}) {
144
- await FileSystemUtils.writeFile(getWorkspaceRegistryPath(options), serializeWorkspaceRegistryState(state));
145
- }
146
- //# sourceMappingURL=registry.js.map
@@ -1,57 +0,0 @@
1
- import { type AIToolOption } from '../config.js';
2
- import { type Delivery, type Profile } from '../global-config.js';
3
- import type { WorkspaceSkillState } from './foundation.js';
4
- export interface WorkspaceSkillAgentResult {
5
- tool_id: string;
6
- name: string;
7
- skills_path: string;
8
- workflow_ids: string[];
9
- }
10
- export interface WorkspaceSkillRemovedResult extends WorkspaceSkillAgentResult {
11
- reason: 'agent_unselected' | 'workflow_unselected';
12
- }
13
- export interface WorkspaceSkillSkippedResult {
14
- tool_id?: string;
15
- name?: string;
16
- reason: string;
17
- message: string;
18
- }
19
- export interface WorkspaceSkillFailedResult {
20
- tool_id: string;
21
- name: string;
22
- error: string;
23
- }
24
- export interface WorkspaceSkillInstallationReport {
25
- profile: Profile;
26
- delivery: Delivery;
27
- workflow_ids: string[];
28
- selected_agents: string[];
29
- skills_only: true;
30
- delivery_notice: string | null;
31
- generated: WorkspaceSkillAgentResult[];
32
- added: WorkspaceSkillAgentResult[];
33
- refreshed: WorkspaceSkillAgentResult[];
34
- removed: WorkspaceSkillRemovedResult[];
35
- skipped: WorkspaceSkillSkippedResult[];
36
- failed: WorkspaceSkillFailedResult[];
37
- }
38
- type WorkspaceSkillCapableTool = AIToolOption & {
39
- skillsDir: string;
40
- };
41
- export declare function getCurrentWorkspaceSkillProfileSelection(): {
42
- profile: Profile;
43
- delivery: Delivery;
44
- workflow_ids: string[];
45
- };
46
- export declare function hasWorkspaceSkillProfileDrift(state: {
47
- workspace_skills?: WorkspaceSkillState;
48
- } | null | undefined): boolean;
49
- export declare function getWorkspaceSkillCapableTools(): WorkspaceSkillCapableTool[];
50
- export declare function getWorkspaceSkillToolIds(): string[];
51
- export declare function parseWorkspaceSkillToolsValue(rawTools: string): string[];
52
- export declare function createWorkspaceSkillSkippedReport(reason: string, message: string): WorkspaceSkillInstallationReport;
53
- export declare function getWorkspaceSkillDirectory(workspaceRoot: string, toolId: string): string;
54
- export declare function generateWorkspaceAgentSkills(workspaceRoot: string, selectedAgentIds: string[]): Promise<WorkspaceSkillInstallationReport>;
55
- export declare function updateWorkspaceAgentSkills(workspaceRoot: string, selectedAgentIds: string[], previousSkillState?: WorkspaceSkillState): Promise<WorkspaceSkillInstallationReport>;
56
- export {};
57
- //# sourceMappingURL=skills.d.ts.map
@@ -1,334 +0,0 @@
1
- import * as nodeFs from 'node:fs';
2
- import { createRequire } from 'node:module';
3
- import { FileSystemUtils } from '../../utils/file-system.js';
4
- import { transformToHyphenCommands } from '../../utils/command-references.js';
5
- import { AI_TOOLS } from '../config.js';
6
- import { getGlobalConfig } from '../global-config.js';
7
- import { getProfileWorkflows } from '../profiles.js';
8
- import { generateSkillContent, getSkillTemplates, getToolSkillStatus, getToolsWithSkillsDir, extractGeneratedByVersion, } from '../shared/index.js';
9
- const require = createRequire(import.meta.url);
10
- const { version: OPENSPEC_VERSION } = require('../../../package.json');
11
- const fs = nodeFs.promises;
12
- function resolveWorkspaceSkillProfileContext() {
13
- const globalConfig = getGlobalConfig();
14
- const profile = globalConfig.profile ?? 'core';
15
- const delivery = globalConfig.delivery ?? 'both';
16
- const workflowIds = [...getProfileWorkflows(profile, globalConfig.workflows)];
17
- const deliveryNotice = delivery === 'skills'
18
- ? null
19
- : 'Workspace setup installs skills only; workspace command generation is not part of this slice.';
20
- return {
21
- profile,
22
- delivery,
23
- workflowIds,
24
- deliveryNotice,
25
- };
26
- }
27
- export function getCurrentWorkspaceSkillProfileSelection() {
28
- const profileContext = resolveWorkspaceSkillProfileContext();
29
- return {
30
- profile: profileContext.profile,
31
- delivery: profileContext.delivery,
32
- workflow_ids: profileContext.workflowIds,
33
- };
34
- }
35
- function arraysEqual(left, right) {
36
- const leftValues = left ?? [];
37
- if (leftValues.length !== right.length) {
38
- return false;
39
- }
40
- const leftSet = new Set(leftValues);
41
- const rightSet = new Set(right);
42
- if (leftSet.size !== rightSet.size) {
43
- return false;
44
- }
45
- return [...leftSet].every((value) => rightSet.has(value));
46
- }
47
- export function hasWorkspaceSkillProfileDrift(state) {
48
- const workspaceSkills = state?.workspace_skills;
49
- if (!workspaceSkills) {
50
- return false;
51
- }
52
- const current = getCurrentWorkspaceSkillProfileSelection();
53
- return (workspaceSkills.last_applied_profile !== current.profile ||
54
- workspaceSkills.last_applied_delivery !== current.delivery ||
55
- !arraysEqual(workspaceSkills.last_applied_workflow_ids, current.workflow_ids));
56
- }
57
- function makeBaseWorkspaceSkillReport(selectedAgentIds, profileContext = resolveWorkspaceSkillProfileContext()) {
58
- return {
59
- profile: profileContext.profile,
60
- delivery: profileContext.delivery,
61
- workflow_ids: profileContext.workflowIds,
62
- selected_agents: selectedAgentIds,
63
- skills_only: true,
64
- delivery_notice: profileContext.deliveryNotice,
65
- generated: [],
66
- added: [],
67
- refreshed: [],
68
- removed: [],
69
- skipped: [],
70
- failed: [],
71
- };
72
- }
73
- export function getWorkspaceSkillCapableTools() {
74
- return AI_TOOLS.filter((tool) => Boolean(tool.skillsDir));
75
- }
76
- export function getWorkspaceSkillToolIds() {
77
- return getToolsWithSkillsDir();
78
- }
79
- export function parseWorkspaceSkillToolsValue(rawTools) {
80
- const raw = rawTools.trim();
81
- if (raw.length === 0) {
82
- throw new Error('The --tools option requires a value. Use "all", "none", or a comma-separated list of agent IDs.');
83
- }
84
- const availableTools = getWorkspaceSkillToolIds();
85
- const availableSet = new Set(availableTools);
86
- const availableList = ['all', 'none', ...availableTools].join(', ');
87
- const lowerRaw = raw.toLowerCase();
88
- if (lowerRaw === 'all') {
89
- return availableTools;
90
- }
91
- if (lowerRaw === 'none') {
92
- return [];
93
- }
94
- const tokens = raw
95
- .split(',')
96
- .map((token) => token.trim())
97
- .filter((token) => token.length > 0);
98
- if (tokens.length === 0) {
99
- throw new Error('The --tools option requires at least one agent ID when not using "all" or "none".');
100
- }
101
- const normalizedTokens = tokens.map((token) => token.toLowerCase());
102
- if (normalizedTokens.some((token) => token === 'all' || token === 'none')) {
103
- throw new Error('Cannot combine reserved values "all" or "none" with specific agent IDs.');
104
- }
105
- const invalidTokens = tokens.filter((_token, index) => !availableSet.has(normalizedTokens[index]));
106
- if (invalidTokens.length > 0) {
107
- throw new Error(`Invalid agent(s): ${invalidTokens.join(', ')}. Available values: ${availableList}`);
108
- }
109
- const deduped = [];
110
- for (const token of normalizedTokens) {
111
- if (!deduped.includes(token)) {
112
- deduped.push(token);
113
- }
114
- }
115
- return deduped;
116
- }
117
- export function createWorkspaceSkillSkippedReport(reason, message) {
118
- const report = makeBaseWorkspaceSkillReport([]);
119
- report.skipped.push({
120
- reason,
121
- message,
122
- });
123
- return report;
124
- }
125
- function getWorkspaceSkillTool(toolId) {
126
- const tool = getWorkspaceSkillCapableTools().find((candidate) => candidate.value === toolId);
127
- if (!tool) {
128
- throw new Error(`Unknown workspace skill agent '${toolId}'.`);
129
- }
130
- return tool;
131
- }
132
- function getWorkspaceSkillDirectoryForTool(workspaceRoot, tool) {
133
- return FileSystemUtils.joinPath(workspaceRoot, tool.skillsDir, 'skills');
134
- }
135
- export function getWorkspaceSkillDirectory(workspaceRoot, toolId) {
136
- return getWorkspaceSkillDirectoryForTool(workspaceRoot, getWorkspaceSkillTool(toolId));
137
- }
138
- function makeAgentResult(workspaceRoot, tool, workflowIds) {
139
- return {
140
- tool_id: tool.value,
141
- name: tool.name,
142
- skills_path: getWorkspaceSkillDirectoryForTool(workspaceRoot, tool),
143
- workflow_ids: workflowIds,
144
- };
145
- }
146
- function getManagedWorkspaceSkillEntries() {
147
- return getSkillTemplates().map(({ workflowId, dirName }) => ({ workflowId, dirName }));
148
- }
149
- async function pathExists(targetPath) {
150
- try {
151
- await fs.access(targetPath);
152
- return true;
153
- }
154
- catch {
155
- return false;
156
- }
157
- }
158
- function isOpenSpecManagedSkillDir(skillDir) {
159
- const skillFile = FileSystemUtils.joinPath(skillDir, 'SKILL.md');
160
- return extractGeneratedByVersion(skillFile) !== null;
161
- }
162
- async function removeManagedWorkflowSkillDirs(workspaceRoot, tool, desiredWorkflowIds, reason) {
163
- const desiredSet = new Set(desiredWorkflowIds);
164
- const skillsDir = getWorkspaceSkillDirectoryForTool(workspaceRoot, tool);
165
- const removedWorkflowIds = [];
166
- for (const { workflowId, dirName } of getManagedWorkspaceSkillEntries()) {
167
- if (desiredSet.has(workflowId)) {
168
- continue;
169
- }
170
- const skillDir = FileSystemUtils.joinPath(skillsDir, dirName);
171
- if (!(await pathExists(skillDir))) {
172
- continue;
173
- }
174
- if (!isOpenSpecManagedSkillDir(skillDir)) {
175
- continue;
176
- }
177
- await fs.rm(skillDir, { recursive: true, force: true });
178
- removedWorkflowIds.push(workflowId);
179
- }
180
- if (removedWorkflowIds.length === 0) {
181
- return null;
182
- }
183
- return {
184
- ...makeAgentResult(workspaceRoot, tool, removedWorkflowIds),
185
- reason,
186
- };
187
- }
188
- export async function generateWorkspaceAgentSkills(workspaceRoot, selectedAgentIds) {
189
- const profileContext = resolveWorkspaceSkillProfileContext();
190
- const report = makeBaseWorkspaceSkillReport(selectedAgentIds, profileContext);
191
- if (selectedAgentIds.length === 0) {
192
- report.skipped.push({
193
- reason: 'no_agents_selected',
194
- message: 'No workspace agent skills were selected.',
195
- });
196
- return report;
197
- }
198
- const skillTemplates = getSkillTemplates(profileContext.workflowIds);
199
- if (skillTemplates.length === 0) {
200
- for (const toolId of selectedAgentIds) {
201
- const tool = getWorkspaceSkillTool(toolId);
202
- report.skipped.push({
203
- tool_id: tool.value,
204
- name: tool.name,
205
- reason: 'no_profile_workflows',
206
- message: 'The active global profile does not select any workflows.',
207
- });
208
- }
209
- return report;
210
- }
211
- for (const toolId of selectedAgentIds) {
212
- const tool = getWorkspaceSkillTool(toolId);
213
- const wasConfigured = getToolSkillStatus(workspaceRoot, tool.value).configured;
214
- try {
215
- const skillsDir = getWorkspaceSkillDirectoryForTool(workspaceRoot, tool);
216
- const transformer = tool.value === 'opencode' || tool.value === 'pi' ? transformToHyphenCommands : undefined;
217
- for (const { template, dirName } of skillTemplates) {
218
- const skillFile = FileSystemUtils.joinPath(skillsDir, dirName, 'SKILL.md');
219
- const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
220
- await FileSystemUtils.writeFile(skillFile, skillContent);
221
- }
222
- const result = makeAgentResult(workspaceRoot, tool, profileContext.workflowIds);
223
- if (wasConfigured) {
224
- report.refreshed.push(result);
225
- }
226
- else {
227
- report.generated.push(result);
228
- }
229
- }
230
- catch (error) {
231
- report.failed.push({
232
- tool_id: tool.value,
233
- name: tool.name,
234
- error: error instanceof Error ? error.message : String(error),
235
- });
236
- }
237
- }
238
- return report;
239
- }
240
- export async function updateWorkspaceAgentSkills(workspaceRoot, selectedAgentIds, previousSkillState) {
241
- const profileContext = resolveWorkspaceSkillProfileContext();
242
- const report = makeBaseWorkspaceSkillReport(selectedAgentIds, profileContext);
243
- const previousSelectedAgentIds = previousSkillState?.selected_agents ?? [];
244
- const previousSelectedSet = new Set(previousSelectedAgentIds);
245
- const selectedSet = new Set(selectedAgentIds);
246
- const skillTemplates = getSkillTemplates(profileContext.workflowIds);
247
- for (const toolId of previousSelectedAgentIds) {
248
- if (selectedSet.has(toolId)) {
249
- continue;
250
- }
251
- const tool = getWorkspaceSkillTool(toolId);
252
- try {
253
- const removed = await removeManagedWorkflowSkillDirs(workspaceRoot, tool, [], 'agent_unselected');
254
- if (removed) {
255
- report.removed.push(removed);
256
- }
257
- }
258
- catch (error) {
259
- report.failed.push({
260
- tool_id: tool.value,
261
- name: tool.name,
262
- error: error instanceof Error ? error.message : String(error),
263
- });
264
- }
265
- }
266
- if (selectedAgentIds.length === 0) {
267
- if (report.removed.length === 0) {
268
- report.skipped.push({
269
- reason: previousSkillState ? 'no_agents_selected' : 'no_stored_agent_selection',
270
- message: previousSkillState
271
- ? 'No workspace agent skills were selected.'
272
- : 'No workspace agent skill selection is stored. Pass --tools <ids> to install skills.',
273
- });
274
- }
275
- return report;
276
- }
277
- if (skillTemplates.length === 0) {
278
- for (const toolId of selectedAgentIds) {
279
- const tool = getWorkspaceSkillTool(toolId);
280
- try {
281
- const removed = await removeManagedWorkflowSkillDirs(workspaceRoot, tool, [], 'workflow_unselected');
282
- if (removed) {
283
- report.removed.push(removed);
284
- }
285
- }
286
- catch (error) {
287
- report.failed.push({
288
- tool_id: tool.value,
289
- name: tool.name,
290
- error: error instanceof Error ? error.message : String(error),
291
- });
292
- }
293
- report.skipped.push({
294
- tool_id: tool.value,
295
- name: tool.name,
296
- reason: 'no_profile_workflows',
297
- message: 'The active global profile does not select any workflows.',
298
- });
299
- }
300
- return report;
301
- }
302
- for (const toolId of selectedAgentIds) {
303
- const tool = getWorkspaceSkillTool(toolId);
304
- try {
305
- const skillsDir = getWorkspaceSkillDirectoryForTool(workspaceRoot, tool);
306
- const transformer = tool.value === 'opencode' || tool.value === 'pi' ? transformToHyphenCommands : undefined;
307
- for (const { template, dirName } of skillTemplates) {
308
- const skillFile = FileSystemUtils.joinPath(skillsDir, dirName, 'SKILL.md');
309
- const skillContent = generateSkillContent(template, OPENSPEC_VERSION, transformer);
310
- await FileSystemUtils.writeFile(skillFile, skillContent);
311
- }
312
- const removed = await removeManagedWorkflowSkillDirs(workspaceRoot, tool, profileContext.workflowIds, 'workflow_unselected');
313
- if (removed) {
314
- report.removed.push(removed);
315
- }
316
- const result = makeAgentResult(workspaceRoot, tool, profileContext.workflowIds);
317
- if (previousSelectedSet.has(toolId)) {
318
- report.refreshed.push(result);
319
- }
320
- else {
321
- report.added.push(result);
322
- }
323
- }
324
- catch (error) {
325
- report.failed.push({
326
- tool_id: tool.value,
327
- name: tool.name,
328
- error: error instanceof Error ? error.message : String(error),
329
- });
330
- }
331
- }
332
- return report;
333
- }
334
- //# sourceMappingURL=skills.js.map
@@ -1,10 +0,0 @@
1
- import { type WorkspaceViewState } from './foundation.js';
2
- export declare function isWorkspaceRoot(candidateRoot: string): Promise<boolean>;
3
- export declare function findWorkspaceRoot(startPath?: string): Promise<string | null>;
4
- export declare function workspaceStateFileExistsSync(workspaceRoot: string): boolean;
5
- export declare function readWorkspaceViewState(workspaceRoot: string): Promise<WorkspaceViewState>;
6
- export declare function readWorkspaceViewStateSync(workspaceRoot: string): WorkspaceViewState | null;
7
- export declare function readOptionalWorkspaceViewState(workspaceRoot: string): Promise<WorkspaceViewState | null>;
8
- export declare function writeWorkspaceViewState(workspaceRoot: string, state: WorkspaceViewState): Promise<void>;
9
- export declare function workspaceChangesDirExists(workspaceRoot: string): Promise<boolean>;
10
- //# sourceMappingURL=state-io.d.ts.map
@@ -1,121 +0,0 @@
1
- import * as nodeFs from 'node:fs';
2
- import * as path from 'node:path';
3
- import { FileSystemUtils } from '../../utils/file-system.js';
4
- import { getWorkspaceChangesDir, getWorkspaceMetadataDir, getWorkspaceViewStatePath, parseWorkspaceViewState, serializeWorkspaceViewState, } from './foundation.js';
5
- import { getWorkspaceLegacyLocalStatePath, getWorkspaceLegacySharedStatePath, parseWorkspaceLocalState, parseWorkspaceSharedState, workspaceStatePartsToViewState, } from './legacy-state.js';
6
- const fs = nodeFs.promises;
7
- async function pathIsFile(filePath) {
8
- try {
9
- return (await fs.stat(filePath)).isFile();
10
- }
11
- catch {
12
- return false;
13
- }
14
- }
15
- async function pathIsDirectory(dirPath) {
16
- try {
17
- return (await fs.stat(dirPath)).isDirectory();
18
- }
19
- catch {
20
- return false;
21
- }
22
- }
23
- function pathExistsAsFile(filePath) {
24
- try {
25
- return nodeFs.statSync(filePath).isFile();
26
- }
27
- catch {
28
- return false;
29
- }
30
- }
31
- function isFileNotFoundError(error) {
32
- return (typeof error === 'object' &&
33
- error !== null &&
34
- 'code' in error &&
35
- error.code === 'ENOENT');
36
- }
37
- async function getSearchStartDirectory(startPath) {
38
- const resolvedStart = path.resolve(startPath);
39
- try {
40
- const stats = await fs.stat(resolvedStart);
41
- const searchStart = stats.isDirectory() ? resolvedStart : path.dirname(resolvedStart);
42
- return FileSystemUtils.canonicalizeExistingPath(searchStart);
43
- }
44
- catch {
45
- return resolvedStart;
46
- }
47
- }
48
- export async function isWorkspaceRoot(candidateRoot) {
49
- return ((await pathIsFile(getWorkspaceViewStatePath(candidateRoot))) ||
50
- (await pathIsFile(getWorkspaceLegacySharedStatePath(candidateRoot))));
51
- }
52
- export async function findWorkspaceRoot(startPath = process.cwd()) {
53
- let currentDir = await getSearchStartDirectory(startPath);
54
- while (true) {
55
- if (await isWorkspaceRoot(currentDir)) {
56
- return FileSystemUtils.canonicalizeExistingPath(currentDir);
57
- }
58
- const parentDir = path.dirname(currentDir);
59
- if (parentDir === currentDir) {
60
- return null;
61
- }
62
- currentDir = parentDir;
63
- }
64
- }
65
- export function workspaceStateFileExistsSync(workspaceRoot) {
66
- return (pathExistsAsFile(getWorkspaceViewStatePath(workspaceRoot)) ||
67
- pathExistsAsFile(getWorkspaceLegacySharedStatePath(workspaceRoot)));
68
- }
69
- export async function readWorkspaceViewState(workspaceRoot) {
70
- const viewStatePath = getWorkspaceViewStatePath(workspaceRoot);
71
- if (await pathIsFile(viewStatePath)) {
72
- return parseWorkspaceViewState(await fs.readFile(viewStatePath, 'utf-8'));
73
- }
74
- const legacySharedState = parseWorkspaceSharedState(await fs.readFile(getWorkspaceLegacySharedStatePath(workspaceRoot), 'utf-8'));
75
- let legacyLocalState = null;
76
- try {
77
- legacyLocalState = parseWorkspaceLocalState(await fs.readFile(getWorkspaceLegacyLocalStatePath(workspaceRoot), 'utf-8'));
78
- }
79
- catch (error) {
80
- if (!isFileNotFoundError(error)) {
81
- throw error;
82
- }
83
- }
84
- return workspaceStatePartsToViewState(legacySharedState, legacyLocalState);
85
- }
86
- export function readWorkspaceViewStateSync(workspaceRoot) {
87
- const viewStatePath = getWorkspaceViewStatePath(workspaceRoot);
88
- if (pathExistsAsFile(viewStatePath)) {
89
- return parseWorkspaceViewState(nodeFs.readFileSync(viewStatePath, 'utf-8'));
90
- }
91
- const legacySharedPath = getWorkspaceLegacySharedStatePath(workspaceRoot);
92
- if (!pathExistsAsFile(legacySharedPath)) {
93
- return null;
94
- }
95
- const legacySharedState = parseWorkspaceSharedState(nodeFs.readFileSync(legacySharedPath, 'utf-8'));
96
- const legacyLocalPath = getWorkspaceLegacyLocalStatePath(workspaceRoot);
97
- const legacyLocalState = pathExistsAsFile(legacyLocalPath)
98
- ? parseWorkspaceLocalState(nodeFs.readFileSync(legacyLocalPath, 'utf-8'))
99
- : null;
100
- return workspaceStatePartsToViewState(legacySharedState, legacyLocalState);
101
- }
102
- export async function readOptionalWorkspaceViewState(workspaceRoot) {
103
- try {
104
- return await readWorkspaceViewState(workspaceRoot);
105
- }
106
- catch (error) {
107
- if (isFileNotFoundError(error)) {
108
- return null;
109
- }
110
- throw error;
111
- }
112
- }
113
- export async function writeWorkspaceViewState(workspaceRoot, state) {
114
- const content = serializeWorkspaceViewState(state);
115
- await FileSystemUtils.createDirectory(getWorkspaceMetadataDir(workspaceRoot));
116
- await FileSystemUtils.writeFile(getWorkspaceViewStatePath(workspaceRoot), content);
117
- }
118
- export async function workspaceChangesDirExists(workspaceRoot) {
119
- return pathIsDirectory(getWorkspaceChangesDir(workspaceRoot));
120
- }
121
- //# sourceMappingURL=state-io.js.map