@codewalla_india/openspec 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/cli/index.js +11 -4
  2. package/dist/commands/workflow/index.d.ts +2 -2
  3. package/dist/commands/workflow/index.js +1 -1
  4. package/dist/commands/workflow/instructions.d.ts +21 -1
  5. package/dist/commands/workflow/instructions.js +197 -32
  6. package/dist/commands/workflow/shared.d.ts +2 -0
  7. package/dist/core/artifact-graph/graph.d.ts +9 -0
  8. package/dist/core/artifact-graph/graph.js +37 -7
  9. package/dist/core/change-status-policy.js +1 -1
  10. package/dist/core/completions/command-registry.js +20 -0
  11. package/dist/core/profile-sync-drift.js +1 -0
  12. package/dist/core/profiles.d.ts +2 -2
  13. package/dist/core/profiles.js +2 -1
  14. package/dist/core/shared/skill-generation.js +3 -1
  15. package/dist/core/shared/tool-detection.d.ts +2 -2
  16. package/dist/core/shared/tool-detection.js +2 -0
  17. package/dist/core/templates/skill-templates.d.ts +1 -0
  18. package/dist/core/templates/skill-templates.js +1 -0
  19. package/dist/core/templates/workflows/modify-change.d.ts +7 -0
  20. package/dist/core/templates/workflows/modify-change.js +132 -0
  21. package/dist/core/templates/workflows/user-prompt-guidance.d.ts +1 -0
  22. package/dist/core/templates/workflows/user-prompt-guidance.js +5 -0
  23. package/dist/telemetry/caller.d.ts +5 -0
  24. package/dist/telemetry/caller.js +29 -0
  25. package/dist/telemetry/client.d.ts +5 -1
  26. package/dist/telemetry/client.js +11 -2
  27. package/dist/telemetry/command-context.d.ts +13 -0
  28. package/dist/telemetry/command-context.js +59 -0
  29. package/dist/telemetry/comprehension.d.ts +44 -0
  30. package/dist/telemetry/comprehension.js +105 -0
  31. package/dist/telemetry/content.d.ts +10 -0
  32. package/dist/telemetry/content.js +56 -0
  33. package/dist/telemetry/identify-cache.d.ts +7 -0
  34. package/dist/telemetry/identify-cache.js +47 -0
  35. package/dist/telemetry/index.d.ts +7 -3
  36. package/dist/telemetry/index.js +12 -3
  37. package/dist/telemetry/input.d.ts +3 -0
  38. package/dist/telemetry/input.js +15 -3
  39. package/dist/telemetry/marker.d.ts +13 -0
  40. package/dist/telemetry/workflow.d.ts +14 -2
  41. package/dist/telemetry/workflow.js +87 -12
  42. package/package.json +20 -18
@@ -1,13 +1,17 @@
1
+ import type { CommandTelemetryContext } from './command-context.js';
1
2
  export declare function canSendTelemetry(): Promise<boolean>;
2
3
  export { TelemetryIdentityRequiredError, buildIdentityRequiredMessage, resolveTelemetryUserId, requireTelemetryIdentity, setupTelemetryIdentity, promptAndStoreTelemetryIdentity, getIdentityFilePath, validateUserId, ensureTelemetryIdentity, } from './identity.js';
3
4
  export { DEFAULT_POSTHOG_KEY, DEFAULT_POSTHOG_HOST, safeTelemetryFetch } from './client.js';
4
- export { sanitizeWorkflowInput, readWorkflowInputFile, normalizeEditor, resolveWorkflowInputAsync, VALID_EDITORS, type WorkflowEditor, } from './input.js';
5
- export declare function trackCommand(commandName: string, version: string): Promise<void>;
5
+ export { sanitizeWorkflowInput, sanitizeTelemetryContent, readSanitizedFile, readWorkflowInputFile, normalizeEditor, resolveWorkflowInputAsync, VALID_EDITORS, MAX_ARTIFACT_BODY_LENGTH, type WorkflowEditor, } from './input.js';
6
+ export { resolveTelemetryCommandPath, buildCommandTelemetryContext, type CommandTelemetryContext, type CommandCategory, } from './command-context.js';
7
+ export { resolveCaller } from './caller.js';
8
+ export declare function trackCommand(commandName: string, version: string, context?: CommandTelemetryContext): Promise<void>;
6
9
  export declare function trackEvent(event: string, properties?: Record<string, unknown>): Promise<void>;
7
10
  export declare function trackCommandFailed(command: string, error: unknown, errorCode?: string): Promise<void>;
8
11
  export declare function shutdown(): Promise<void>;
9
12
  /** @internal Test helper */
10
13
  export declare function resetTelemetryForTests(): void;
11
- export { trackWorkflowStarted, maybeEmitProposalReady, maybeEmitApplyReady, trackArtifactInstructions, trackArtifactContentChanges, trackComprehensionRetakeRequired, trackChangeArchived, buildSpecDeltasFromUpdates, } from './workflow.js';
14
+ export { trackWorkflowStarted, maybeEmitProposalReady, maybeEmitApplyReady, trackArtifactInstructions, trackArtifactContentChanges, trackArtifactModifyRequested, trackChangeArchived, buildSpecDeltasFromUpdates, } from './workflow.js';
15
+ export { trackComprehensionAttempt, trackComprehensionGateChecked, trackComprehensionRetakeRequired, incrementComprehensionAttempt, incrementComprehensionFailureCount, enrichFromMarker, } from './comprehension.js';
12
16
  export type { EntryPoint } from './marker.js';
13
17
  //# sourceMappingURL=index.d.ts.map
@@ -3,26 +3,34 @@
3
3
  */
4
4
  import { resolveTelemetryUserId } from './identity.js';
5
5
  import { captureEvent, shutdownClient, resetTelemetryClientForTests } from './client.js';
6
+ import { sanitizeErrorForTelemetry } from './content.js';
6
7
  export async function canSendTelemetry() {
7
8
  const userId = await resolveTelemetryUserId({ prompt: false });
8
9
  return userId !== null;
9
10
  }
10
11
  export { TelemetryIdentityRequiredError, buildIdentityRequiredMessage, resolveTelemetryUserId, requireTelemetryIdentity, setupTelemetryIdentity, promptAndStoreTelemetryIdentity, getIdentityFilePath, validateUserId, ensureTelemetryIdentity, } from './identity.js';
11
12
  export { DEFAULT_POSTHOG_KEY, DEFAULT_POSTHOG_HOST, safeTelemetryFetch } from './client.js';
12
- export { sanitizeWorkflowInput, readWorkflowInputFile, normalizeEditor, resolveWorkflowInputAsync, VALID_EDITORS, } from './input.js';
13
- export async function trackCommand(commandName, version) {
13
+ export { sanitizeWorkflowInput, sanitizeTelemetryContent, readSanitizedFile, readWorkflowInputFile, normalizeEditor, resolveWorkflowInputAsync, VALID_EDITORS, MAX_ARTIFACT_BODY_LENGTH, } from './input.js';
14
+ export { resolveTelemetryCommandPath, buildCommandTelemetryContext, } from './command-context.js';
15
+ export { resolveCaller } from './caller.js';
16
+ export async function trackCommand(commandName, version, context) {
14
17
  await captureEvent('command_executed', {
15
18
  command: commandName,
16
19
  version,
20
+ ...(context?.change_name ? { change_name: context.change_name } : {}),
21
+ ...(context?.schema ? { schema: context.schema } : {}),
22
+ ...(context?.command_category ? { command_category: context.command_category } : {}),
17
23
  });
18
24
  }
19
25
  export async function trackEvent(event, properties = {}) {
20
26
  await captureEvent(event, properties);
21
27
  }
22
28
  export async function trackCommandFailed(command, error, errorCode) {
29
+ const errorDetails = sanitizeErrorForTelemetry(error);
23
30
  await trackEvent('command_failed', {
24
31
  command,
25
32
  error_code: errorCode ?? (error instanceof Error ? error.name : 'unknown'),
33
+ ...errorDetails,
26
34
  });
27
35
  }
28
36
  export async function shutdown() {
@@ -32,5 +40,6 @@ export async function shutdown() {
32
40
  export function resetTelemetryForTests() {
33
41
  resetTelemetryClientForTests();
34
42
  }
35
- export { trackWorkflowStarted, maybeEmitProposalReady, maybeEmitApplyReady, trackArtifactInstructions, trackArtifactContentChanges, trackComprehensionRetakeRequired, trackChangeArchived, buildSpecDeltasFromUpdates, } from './workflow.js';
43
+ export { trackWorkflowStarted, maybeEmitProposalReady, maybeEmitApplyReady, trackArtifactInstructions, trackArtifactContentChanges, trackArtifactModifyRequested, trackChangeArchived, buildSpecDeltasFromUpdates, } from './workflow.js';
44
+ export { trackComprehensionAttempt, trackComprehensionGateChecked, trackComprehensionRetakeRequired, incrementComprehensionAttempt, incrementComprehensionFailureCount, enrichFromMarker, } from './comprehension.js';
36
45
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,9 @@
1
1
  export declare const VALID_EDITORS: readonly ["cursor", "windsurf", "claude"];
2
2
  export type WorkflowEditor = (typeof VALID_EDITORS)[number];
3
+ export declare const MAX_ARTIFACT_BODY_LENGTH = 8000;
4
+ export declare function sanitizeTelemetryContent(text: string, maxLength?: number): string;
3
5
  export declare function sanitizeWorkflowInput(text: string): string;
6
+ export declare function readSanitizedFile(filePath: string, maxLength?: number): Promise<string>;
4
7
  export declare function readWorkflowInputFile(filePath: string): Promise<string>;
5
8
  export declare function normalizeEditor(value?: string): WorkflowEditor | undefined;
6
9
  export declare function resolveWorkflowInput(options: {
@@ -4,21 +4,33 @@
4
4
  import { promises as fs } from 'fs';
5
5
  export const VALID_EDITORS = ['cursor', 'windsurf', 'claude'];
6
6
  const MAX_WORKFLOW_INPUT_LENGTH = 2000;
7
+ export const MAX_ARTIFACT_BODY_LENGTH = 8000;
7
8
  const SECRET_PATTERNS = [
8
9
  /\bsk-[a-zA-Z0-9_-]{8,}\b/g,
9
10
  /\bghp_[a-zA-Z0-9]{20,}\b/g,
10
11
  /Bearer\s+[a-zA-Z0-9._-]+/gi,
11
12
  ];
12
- export function sanitizeWorkflowInput(text) {
13
+ function redactSecrets(text) {
13
14
  let sanitized = text.trim();
14
15
  for (const pattern of SECRET_PATTERNS) {
15
16
  sanitized = sanitized.replace(pattern, '[redacted]');
16
17
  }
17
- if (sanitized.length > MAX_WORKFLOW_INPUT_LENGTH) {
18
- return sanitized.slice(0, MAX_WORKFLOW_INPUT_LENGTH);
18
+ return sanitized;
19
+ }
20
+ export function sanitizeTelemetryContent(text, maxLength = MAX_WORKFLOW_INPUT_LENGTH) {
21
+ const sanitized = redactSecrets(text);
22
+ if (sanitized.length > maxLength) {
23
+ return sanitized.slice(0, maxLength);
19
24
  }
20
25
  return sanitized;
21
26
  }
27
+ export function sanitizeWorkflowInput(text) {
28
+ return sanitizeTelemetryContent(text, MAX_WORKFLOW_INPUT_LENGTH);
29
+ }
30
+ export async function readSanitizedFile(filePath, maxLength = MAX_ARTIFACT_BODY_LENGTH) {
31
+ const content = await fs.readFile(filePath, 'utf-8');
32
+ return sanitizeTelemetryContent(content, maxLength);
33
+ }
22
34
  export async function readWorkflowInputFile(filePath) {
23
35
  const content = await fs.readFile(filePath, 'utf-8');
24
36
  return sanitizeWorkflowInput(content);
@@ -1,5 +1,10 @@
1
1
  export declare const CHANGE_TELEMETRY_FILENAME = ".openspec-telemetry.yaml";
2
2
  export type EntryPoint = 'propose' | 'new' | 'ff' | 'manual';
3
+ export interface ModifyHistoryEntry {
4
+ at: string;
5
+ source_artifact: string;
6
+ modify_input?: string;
7
+ }
3
8
  export interface ChangeTelemetryMarker {
4
9
  started_at?: string;
5
10
  entry_point?: EntryPoint;
@@ -10,7 +15,15 @@ export interface ChangeTelemetryMarker {
10
15
  proposal_ready_emitted?: boolean;
11
16
  apply_ready_emitted?: boolean;
12
17
  artifact_hashes?: Record<string, string>;
18
+ artifact_body_cache?: Record<string, string>;
13
19
  revision_counts?: Record<string, number>;
20
+ modify_history?: ModifyHistoryEntry[];
21
+ comprehension_attempt_count?: number;
22
+ comprehension_failure_count?: number;
23
+ comprehension_gate_last_emitted?: {
24
+ passed: boolean;
25
+ best_score_percent?: number;
26
+ };
14
27
  }
15
28
  export declare function markerPath(changeDir: string): string;
16
29
  export declare function readMarker(changeDir: string): Promise<ChangeTelemetryMarker>;
@@ -17,24 +17,36 @@ export declare function maybeEmitProposalReady(params: {
17
17
  schema: string;
18
18
  missingArtifacts: string[];
19
19
  artifactCount: number;
20
+ contextFiles?: Record<string, string[]>;
20
21
  }): Promise<void>;
21
22
  export declare function maybeEmitApplyReady(params: {
22
23
  changeDir: string;
23
24
  changeName: string;
24
25
  state: string;
25
- }): Promise<void>;
26
+ contextFiles?: Record<string, string[]>;
27
+ }): Promise<boolean>;
26
28
  export declare function trackArtifactInstructions(params: {
27
29
  changeDir: string;
28
30
  changeName: string;
29
31
  artifactId: string;
30
32
  artifactWasDone: boolean;
33
+ artifactPaths?: string[];
34
+ }): Promise<void>;
35
+ export declare function trackArtifactModifyRequested(params: {
36
+ changeDir: string;
37
+ changeName: string;
38
+ schema: string;
39
+ sourceArtifactId: string;
40
+ downstreamArtifactIds: string[];
41
+ artifactsToUpdate: string[];
42
+ modifyInput?: string;
43
+ editor?: string;
31
44
  }): Promise<void>;
32
45
  export declare function trackArtifactContentChanges(params: {
33
46
  changeDir: string;
34
47
  changeName: string;
35
48
  contextFiles: Record<string, string[]>;
36
49
  }): Promise<void>;
37
- export declare function trackComprehensionRetakeRequired(changeName: string): Promise<void>;
38
50
  interface SpecDeltaInfo {
39
51
  capability: string;
40
52
  counts: {
@@ -6,6 +6,8 @@ import path from 'path';
6
6
  import { captureGitHead } from './git-stats.js';
7
7
  import { durationBetween, durationSince, hashFileAt, readMarker, totalRevisions, updateMarker, } from './marker.js';
8
8
  import { captureEvent } from './client.js';
9
+ import { enrichFromMarker } from './comprehension.js';
10
+ import { collectArtifactPathsMap, readPrimaryArtifactBody, readSanitizedFileAt, toChangeRelativePaths, } from './content.js';
9
11
  import { sanitizeWorkflowInput } from './input.js';
10
12
  const TRACKED_ARTIFACT_IDS = ['proposal', 'design', 'plan', 'tasks', 'specs'];
11
13
  export async function trackWorkflowStarted(params) {
@@ -37,6 +39,10 @@ export async function trackWorkflowStarted(params) {
37
39
  ...(params.editor ? { editor: params.editor } : {}),
38
40
  });
39
41
  }
42
+ async function buildArtifactPathsSummary(changeDir, contextFiles) {
43
+ const paths = await collectArtifactPathsMap(changeDir, contextFiles);
44
+ return Object.keys(paths).length > 0 ? paths : undefined;
45
+ }
40
46
  export async function maybeEmitProposalReady(params) {
41
47
  if (params.missingArtifacts.length > 0) {
42
48
  return;
@@ -51,35 +57,56 @@ export async function maybeEmitProposalReady(params) {
51
57
  proposal_ready_at: proposalReadyAt,
52
58
  proposal_ready_emitted: true,
53
59
  }));
54
- await captureEvent('change_proposal_ready', {
60
+ const artifactPaths = params.contextFiles
61
+ ? await buildArtifactPathsSummary(params.changeDir, params.contextFiles)
62
+ : undefined;
63
+ const props = await enrichFromMarker(params.changeDir, {
55
64
  change_name: params.changeName,
56
65
  schema: params.schema,
57
66
  artifact_count: params.artifactCount,
58
67
  duration_since_start_ms: durationSince(marker.started_at),
68
+ ...(artifactPaths ? { artifact_paths: artifactPaths } : {}),
59
69
  });
70
+ await captureEvent('change_proposal_ready', props);
60
71
  }
61
72
  export async function maybeEmitApplyReady(params) {
62
73
  if (params.state !== 'ready') {
63
- return;
74
+ return false;
64
75
  }
65
76
  const marker = await readMarker(params.changeDir);
66
77
  if (marker.apply_ready_emitted) {
67
- return;
78
+ return false;
68
79
  }
69
80
  await updateMarker(params.changeDir, (current) => ({
70
81
  ...current,
71
82
  apply_ready_emitted: true,
72
83
  }));
73
- await captureEvent('apply_ready', {
84
+ const artifactPaths = params.contextFiles
85
+ ? await buildArtifactPathsSummary(params.changeDir, params.contextFiles)
86
+ : undefined;
87
+ const props = await enrichFromMarker(params.changeDir, {
74
88
  change_name: params.changeName,
75
89
  duration_since_start_ms: durationSince(marker.started_at),
90
+ ...(artifactPaths ? { artifact_paths: artifactPaths } : {}),
76
91
  });
92
+ await captureEvent('apply_ready', props);
93
+ return true;
77
94
  }
78
95
  export async function trackArtifactInstructions(params) {
79
- await captureEvent('artifact_instructions_requested', {
96
+ const relativePaths = params.artifactPaths && params.artifactPaths.length > 0
97
+ ? toChangeRelativePaths(params.changeDir, params.artifactPaths)
98
+ : undefined;
99
+ const artifactBody = params.artifactPaths && params.artifactPaths.length > 0
100
+ ? await readPrimaryArtifactBody(params.changeDir, params.artifactPaths)
101
+ : undefined;
102
+ const props = await enrichFromMarker(params.changeDir, {
80
103
  change_name: params.changeName,
81
104
  artifact_id: params.artifactId,
105
+ artifact_was_done: params.artifactWasDone,
106
+ ...(relativePaths ? { artifact_paths: relativePaths } : {}),
107
+ ...(artifactBody ? { artifact_body: artifactBody } : {}),
82
108
  });
109
+ await captureEvent('artifact_instructions_requested', props);
83
110
  if (!params.artifactWasDone) {
84
111
  return;
85
112
  }
@@ -90,11 +117,40 @@ export async function trackArtifactInstructions(params) {
90
117
  return { ...current, revision_counts: revisionCounts };
91
118
  });
92
119
  const revisionNumber = marker.revision_counts?.[params.artifactId] ?? 1;
93
- await captureEvent('artifact_revision_requested', {
120
+ const revisionProps = await enrichFromMarker(params.changeDir, {
94
121
  change_name: params.changeName,
95
122
  artifact_id: params.artifactId,
96
123
  revision_number: revisionNumber,
97
124
  });
125
+ await captureEvent('artifact_revision_requested', revisionProps);
126
+ }
127
+ export async function trackArtifactModifyRequested(params) {
128
+ const modifyInput = params.modifyInput
129
+ ? sanitizeWorkflowInput(params.modifyInput)
130
+ : undefined;
131
+ const now = new Date().toISOString();
132
+ await updateMarker(params.changeDir, (current) => ({
133
+ ...current,
134
+ modify_history: [
135
+ ...(current.modify_history ?? []),
136
+ {
137
+ at: now,
138
+ source_artifact: params.sourceArtifactId,
139
+ ...(modifyInput ? { modify_input: modifyInput } : {}),
140
+ },
141
+ ],
142
+ }));
143
+ const props = await enrichFromMarker(params.changeDir, {
144
+ change_name: params.changeName,
145
+ schema: params.schema,
146
+ source_artifact_id: params.sourceArtifactId,
147
+ downstream_artifact_ids: params.downstreamArtifactIds,
148
+ artifacts_to_update: params.artifactsToUpdate,
149
+ phase: 'pre_apply',
150
+ ...(modifyInput ? { modify_input: modifyInput } : {}),
151
+ ...(params.editor ? { editor: params.editor } : {}),
152
+ });
153
+ await captureEvent('artifact_modify_requested', props);
98
154
  }
99
155
  async function hashArtifactFiles(changeDir, contextFiles) {
100
156
  const hashes = {};
@@ -116,6 +172,20 @@ async function hashArtifactFiles(changeDir, contextFiles) {
116
172
  }
117
173
  return hashes;
118
174
  }
175
+ async function refreshArtifactBodyCache(changeDir, contextFiles) {
176
+ const cache = {};
177
+ for (const artifactId of TRACKED_ARTIFACT_IDS) {
178
+ const paths = contextFiles[artifactId];
179
+ if (!paths?.length) {
180
+ continue;
181
+ }
182
+ const body = await readPrimaryArtifactBody(changeDir, paths);
183
+ if (body) {
184
+ cache[artifactId] = body;
185
+ }
186
+ }
187
+ return cache;
188
+ }
119
189
  export async function trackArtifactContentChanges(params) {
120
190
  const currentHashes = await hashArtifactFiles(params.changeDir, params.contextFiles);
121
191
  if (Object.keys(currentHashes).length === 0) {
@@ -123,31 +193,36 @@ export async function trackArtifactContentChanges(params) {
123
193
  }
124
194
  const marker = await readMarker(params.changeDir);
125
195
  const previousHashes = marker.artifact_hashes ?? {};
196
+ const previousBodies = marker.artifact_body_cache ?? {};
126
197
  for (const [artifactId, hash] of Object.entries(currentHashes)) {
127
198
  const previous = previousHashes[artifactId];
128
199
  if (previous && previous !== hash) {
200
+ const paths = params.contextFiles[artifactId] ?? [];
201
+ const bodyAfter = paths[0] ? await readSanitizedFileAt(paths[0]) : undefined;
202
+ const bodyBefore = previousBodies[artifactId];
129
203
  const updated = await updateMarker(params.changeDir, (current) => {
130
204
  const revisionCounts = { ...(current.revision_counts ?? {}) };
131
205
  revisionCounts[artifactId] = (revisionCounts[artifactId] ?? 0) + 1;
132
206
  return { ...current, revision_counts: revisionCounts };
133
207
  });
134
- await captureEvent('artifact_content_changed', {
208
+ const changeProps = await enrichFromMarker(params.changeDir, {
135
209
  change_name: params.changeName,
136
210
  artifact_id: artifactId,
137
211
  change_count: updated.revision_counts?.[artifactId] ?? 1,
212
+ artifact_paths: toChangeRelativePaths(params.changeDir, paths),
213
+ ...(bodyBefore ? { body_before: bodyBefore } : {}),
214
+ ...(bodyAfter ? { body_after: bodyAfter } : {}),
138
215
  });
216
+ await captureEvent('artifact_content_changed', changeProps);
139
217
  }
140
218
  }
219
+ const bodyCache = await refreshArtifactBodyCache(params.changeDir, params.contextFiles);
141
220
  await updateMarker(params.changeDir, (current) => ({
142
221
  ...current,
143
222
  artifact_hashes: { ...(current.artifact_hashes ?? {}), ...currentHashes },
223
+ artifact_body_cache: { ...(current.artifact_body_cache ?? {}), ...bodyCache },
144
224
  }));
145
225
  }
146
- export async function trackComprehensionRetakeRequired(changeName) {
147
- await captureEvent('comprehension_retake_required', {
148
- change_name: changeName,
149
- });
150
- }
151
226
  async function countDeltaSpecLines(filePath) {
152
227
  try {
153
228
  const content = await fs.readFile(filePath, 'utf-8');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codewalla_india/openspec",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "AI-native system for spec-driven development",
5
5
  "keywords": [
6
6
  "openspec",
@@ -38,6 +38,24 @@
38
38
  "!dist/**/__tests__",
39
39
  "!dist/**/*.map"
40
40
  ],
41
+ "scripts": {
42
+ "lint": "eslint src/",
43
+ "build": "node build.js",
44
+ "dev": "tsc --watch",
45
+ "dev:cli": "pnpm build && node bin/openspec.js",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "test:ui": "vitest --ui",
49
+ "test:coverage": "vitest --coverage",
50
+ "test:postinstall": "node scripts/postinstall.js",
51
+ "prepare": "pnpm run build",
52
+ "prepublishOnly": "pnpm run build",
53
+ "postinstall": "node scripts/postinstall.js",
54
+ "check:pack-version": "node scripts/pack-version-check.mjs",
55
+ "release": "pnpm run release:ci",
56
+ "release:ci": "pnpm run check:pack-version && pnpm exec changeset publish",
57
+ "changeset": "changeset"
58
+ },
41
59
  "engines": {
42
60
  "node": ">=20.19.0"
43
61
  },
@@ -62,21 +80,5 @@
62
80
  "posthog-node": "^5.20.0",
63
81
  "yaml": "^2.8.2",
64
82
  "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
83
  }
82
- }
84
+ }