@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
|
@@ -0,0 +1,334 @@
|
|
|
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
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
|
@@ -0,0 +1,119 @@
|
|
|
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, 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
|
+
await FileSystemUtils.writeFile(getWorkspaceViewStatePath(workspaceRoot), serializeWorkspaceViewState(state));
|
|
115
|
+
}
|
|
116
|
+
export async function workspaceChangesDirExists(workspaceRoot) {
|
|
117
|
+
return pathIsDirectory(getWorkspaceChangesDir(workspaceRoot));
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=state-io.js.map
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export declare const CONFIG_DIR_NAME = "openspec";
|
|
2
|
+
export declare const CONFIG_FILE_NAME = "config.json";
|
|
1
3
|
export interface TelemetryConfig {
|
|
2
4
|
anonymousId?: string;
|
|
3
5
|
noticeSeen?: boolean;
|
|
@@ -8,7 +10,11 @@ export interface GlobalConfig {
|
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Get the path to the global config file.
|
|
11
|
-
*
|
|
13
|
+
* Follows XDG Base Directory Specification and platform conventions.
|
|
14
|
+
*
|
|
15
|
+
* - All platforms: $XDG_CONFIG_HOME/openspec/ if XDG_CONFIG_HOME is set
|
|
16
|
+
* - Unix/macOS fallback: ~/.config/openspec/
|
|
17
|
+
* - Windows fallback: %APPDATA%/openspec/
|
|
12
18
|
*/
|
|
13
19
|
export declare function getConfigPath(): string;
|
|
14
20
|
/**
|
package/dist/telemetry/config.js
CHANGED
|
@@ -1,17 +1,96 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Global configuration for telemetry state.
|
|
3
|
-
* Stores anonymous ID and notice-seen flag in
|
|
3
|
+
* Stores anonymous ID and notice-seen flag in the platform-appropriate config directory.
|
|
4
4
|
*/
|
|
5
5
|
import { promises as fs } from 'fs';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import os from 'os';
|
|
8
|
+
import { GLOBAL_CONFIG_DIR_NAME, GLOBAL_CONFIG_FILE_NAME, getGlobalConfigDir, } from '../core/global-config.js';
|
|
9
|
+
// Constants
|
|
10
|
+
export const CONFIG_DIR_NAME = GLOBAL_CONFIG_DIR_NAME;
|
|
11
|
+
export const CONFIG_FILE_NAME = GLOBAL_CONFIG_FILE_NAME;
|
|
12
|
+
function getConfigDir() {
|
|
13
|
+
return getGlobalConfigDir();
|
|
14
|
+
}
|
|
15
|
+
function getLegacyConfigPath() {
|
|
16
|
+
return path.join(os.homedir(), '.config', CONFIG_DIR_NAME, CONFIG_FILE_NAME);
|
|
17
|
+
}
|
|
18
|
+
async function readConfigFile(configPath) {
|
|
19
|
+
try {
|
|
20
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
21
|
+
return { status: 'ok', config: JSON.parse(content) };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (error.code === 'ENOENT') {
|
|
25
|
+
return { status: 'missing' };
|
|
26
|
+
}
|
|
27
|
+
// If parse fails or another read error occurs, ignore the file.
|
|
28
|
+
return { status: 'invalid', config: {} };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function writeConfigFile(configPath, config) {
|
|
32
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
33
|
+
await fs.writeFile(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
34
|
+
}
|
|
35
|
+
function hasMissingTelemetryFields(config) {
|
|
36
|
+
const telemetry = config.telemetry;
|
|
37
|
+
return (!telemetry ||
|
|
38
|
+
telemetry.anonymousId === undefined ||
|
|
39
|
+
telemetry.noticeSeen === undefined);
|
|
40
|
+
}
|
|
41
|
+
function mergeLegacyTelemetry(config, legacyConfig) {
|
|
42
|
+
const legacyTelemetry = legacyConfig.telemetry;
|
|
43
|
+
if (!legacyTelemetry) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const currentTelemetry = config.telemetry ?? {};
|
|
47
|
+
const shouldMigrate = (currentTelemetry.anonymousId === undefined && legacyTelemetry.anonymousId !== undefined) ||
|
|
48
|
+
(currentTelemetry.noticeSeen === undefined && legacyTelemetry.noticeSeen !== undefined);
|
|
49
|
+
if (!shouldMigrate) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
...config,
|
|
54
|
+
telemetry: {
|
|
55
|
+
...legacyTelemetry,
|
|
56
|
+
...currentTelemetry,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function migrateLegacyTelemetryConfig(configPath, config, persist) {
|
|
61
|
+
const legacyConfigPath = getLegacyConfigPath();
|
|
62
|
+
if (path.resolve(configPath) === path.resolve(legacyConfigPath) || !hasMissingTelemetryFields(config)) {
|
|
63
|
+
return config;
|
|
64
|
+
}
|
|
65
|
+
const legacyRead = await readConfigFile(legacyConfigPath);
|
|
66
|
+
if (legacyRead.status !== 'ok') {
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
const migrated = mergeLegacyTelemetry(config, legacyRead.config);
|
|
70
|
+
if (!migrated) {
|
|
71
|
+
return config;
|
|
72
|
+
}
|
|
73
|
+
if (persist) {
|
|
74
|
+
try {
|
|
75
|
+
await writeConfigFile(configPath, migrated);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Preserve telemetry for this run even if the one-time migration cannot be persisted.
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return migrated;
|
|
82
|
+
}
|
|
8
83
|
/**
|
|
9
84
|
* Get the path to the global config file.
|
|
10
|
-
*
|
|
85
|
+
* Follows XDG Base Directory Specification and platform conventions.
|
|
86
|
+
*
|
|
87
|
+
* - All platforms: $XDG_CONFIG_HOME/openspec/ if XDG_CONFIG_HOME is set
|
|
88
|
+
* - Unix/macOS fallback: ~/.config/openspec/
|
|
89
|
+
* - Windows fallback: %APPDATA%/openspec/
|
|
11
90
|
*/
|
|
12
91
|
export function getConfigPath() {
|
|
13
|
-
const configDir =
|
|
14
|
-
return path.join(configDir,
|
|
92
|
+
const configDir = getConfigDir();
|
|
93
|
+
return path.join(configDir, CONFIG_FILE_NAME);
|
|
15
94
|
}
|
|
16
95
|
/**
|
|
17
96
|
* Read the global config file.
|
|
@@ -19,17 +98,9 @@ export function getConfigPath() {
|
|
|
19
98
|
*/
|
|
20
99
|
export async function readConfig() {
|
|
21
100
|
const configPath = getConfigPath();
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
if (error.code === 'ENOENT') {
|
|
28
|
-
return {};
|
|
29
|
-
}
|
|
30
|
-
// If parse fails or other error, return empty config
|
|
31
|
-
return {};
|
|
32
|
-
}
|
|
101
|
+
const read = await readConfigFile(configPath);
|
|
102
|
+
const config = read.status === 'ok' ? read.config : {};
|
|
103
|
+
return migrateLegacyTelemetryConfig(configPath, config, read.status !== 'invalid');
|
|
33
104
|
}
|
|
34
105
|
/**
|
|
35
106
|
* Write to the global config file.
|
|
@@ -37,9 +108,6 @@ export async function readConfig() {
|
|
|
37
108
|
*/
|
|
38
109
|
export async function writeConfig(updates) {
|
|
39
110
|
const configPath = getConfigPath();
|
|
40
|
-
const configDir = path.dirname(configPath);
|
|
41
|
-
// Ensure directory exists
|
|
42
|
-
await fs.mkdir(configDir, { recursive: true });
|
|
43
111
|
// Read existing config and merge
|
|
44
112
|
const existing = await readConfig();
|
|
45
113
|
const merged = { ...existing, ...updates };
|
|
@@ -47,7 +115,7 @@ export async function writeConfig(updates) {
|
|
|
47
115
|
if (updates.telemetry && existing.telemetry) {
|
|
48
116
|
merged.telemetry = { ...existing.telemetry, ...updates.telemetry };
|
|
49
117
|
}
|
|
50
|
-
await
|
|
118
|
+
await writeConfigFile(configPath, merged);
|
|
51
119
|
}
|
|
52
120
|
/**
|
|
53
121
|
* Get the telemetry config section.
|
package/dist/telemetry/index.js
CHANGED
|
@@ -16,8 +16,21 @@ import { getTelemetryConfig, updateTelemetryConfig } from './config.js';
|
|
|
16
16
|
const POSTHOG_API_KEY = 'phc_Hthu8YvaIJ9QaFKyTG4TbVwkbd5ktcAFzVTKeMmoW2g';
|
|
17
17
|
// Using reverse proxy to avoid ad blockers and keep traffic on our domain
|
|
18
18
|
const POSTHOG_HOST = 'https://edge.openspec.dev';
|
|
19
|
+
const TELEMETRY_REQUEST_TIMEOUT_MS = 1000;
|
|
19
20
|
let posthogClient = null;
|
|
20
21
|
let anonymousId = null;
|
|
22
|
+
async function safeTelemetryFetch(url, options) {
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(url, options);
|
|
25
|
+
if (response.ok) {
|
|
26
|
+
return response;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Silent failure - telemetry should never surface network noise
|
|
31
|
+
}
|
|
32
|
+
return new Response(null, { status: 204 });
|
|
33
|
+
}
|
|
21
34
|
/**
|
|
22
35
|
* Check if telemetry is enabled.
|
|
23
36
|
*
|
|
@@ -71,6 +84,12 @@ function getClient() {
|
|
|
71
84
|
host: POSTHOG_HOST,
|
|
72
85
|
flushAt: 1, // Send immediately, don't batch
|
|
73
86
|
flushInterval: 0, // No timer-based flushing
|
|
87
|
+
fetchRetryCount: 0,
|
|
88
|
+
requestTimeout: TELEMETRY_REQUEST_TIMEOUT_MS,
|
|
89
|
+
preloadFeatureFlags: false,
|
|
90
|
+
disableRemoteConfig: true,
|
|
91
|
+
disableSurveys: true,
|
|
92
|
+
fetch: safeTelemetryFetch,
|
|
74
93
|
});
|
|
75
94
|
}
|
|
76
95
|
return posthogClient;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ChangeMetadata } from '../core/
|
|
1
|
+
import { type ChangeMetadata } from '../core/change-metadata/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Error thrown when change metadata validation fails.
|
|
4
4
|
*/
|
|
@@ -34,6 +34,9 @@ export declare function writeChangeMetadata(changeDir: string, metadata: ChangeM
|
|
|
34
34
|
* @throws ChangeMetadataError if the file exists but is invalid
|
|
35
35
|
*/
|
|
36
36
|
export declare function readChangeMetadata(changeDir: string, projectRoot?: string): ChangeMetadata | null;
|
|
37
|
+
export interface ResolveSchemaForChangeOptions {
|
|
38
|
+
metadata?: ChangeMetadata | null;
|
|
39
|
+
}
|
|
37
40
|
/**
|
|
38
41
|
* Resolves the schema for a change, with explicit override taking precedence.
|
|
39
42
|
*
|
|
@@ -47,5 +50,5 @@ export declare function readChangeMetadata(changeDir: string, projectRoot?: stri
|
|
|
47
50
|
* @param explicitSchema - Optional explicit schema override
|
|
48
51
|
* @returns The resolved schema name
|
|
49
52
|
*/
|
|
50
|
-
export declare function resolveSchemaForChange(changeDir: string, explicitSchema?: string): string;
|
|
53
|
+
export declare function resolveSchemaForChange(changeDir: string, explicitSchema?: string, projectRootOverride?: string, options?: ResolveSchemaForChangeOptions): string;
|
|
51
54
|
//# sourceMappingURL=change-metadata.d.ts.map
|