@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.
- package/dist/cli/index.js +11 -4
- package/dist/commands/workflow/index.d.ts +2 -2
- package/dist/commands/workflow/index.js +1 -1
- package/dist/commands/workflow/instructions.d.ts +21 -1
- package/dist/commands/workflow/instructions.js +197 -32
- package/dist/commands/workflow/shared.d.ts +2 -0
- package/dist/core/artifact-graph/graph.d.ts +9 -0
- package/dist/core/artifact-graph/graph.js +37 -7
- package/dist/core/change-status-policy.js +1 -1
- package/dist/core/completions/command-registry.js +20 -0
- package/dist/core/profile-sync-drift.js +1 -0
- package/dist/core/profiles.d.ts +2 -2
- package/dist/core/profiles.js +2 -1
- package/dist/core/shared/skill-generation.js +3 -1
- package/dist/core/shared/tool-detection.d.ts +2 -2
- package/dist/core/shared/tool-detection.js +2 -0
- package/dist/core/templates/skill-templates.d.ts +1 -0
- package/dist/core/templates/skill-templates.js +1 -0
- package/dist/core/templates/workflows/modify-change.d.ts +7 -0
- package/dist/core/templates/workflows/modify-change.js +132 -0
- package/dist/core/templates/workflows/user-prompt-guidance.d.ts +1 -0
- package/dist/core/templates/workflows/user-prompt-guidance.js +5 -0
- package/dist/telemetry/caller.d.ts +5 -0
- package/dist/telemetry/caller.js +29 -0
- package/dist/telemetry/client.d.ts +5 -1
- package/dist/telemetry/client.js +11 -2
- package/dist/telemetry/command-context.d.ts +13 -0
- package/dist/telemetry/command-context.js +59 -0
- package/dist/telemetry/comprehension.d.ts +44 -0
- package/dist/telemetry/comprehension.js +105 -0
- package/dist/telemetry/content.d.ts +10 -0
- package/dist/telemetry/content.js +56 -0
- package/dist/telemetry/identify-cache.d.ts +7 -0
- package/dist/telemetry/identify-cache.js +47 -0
- package/dist/telemetry/index.d.ts +7 -3
- package/dist/telemetry/index.js +12 -3
- package/dist/telemetry/input.d.ts +3 -0
- package/dist/telemetry/input.js +15 -3
- package/dist/telemetry/marker.d.ts +13 -0
- package/dist/telemetry/workflow.d.ts +14 -2
- package/dist/telemetry/workflow.js +87 -12
- 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
|
|
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,
|
|
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
|
package/dist/telemetry/index.js
CHANGED
|
@@ -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
|
|
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,
|
|
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: {
|
package/dist/telemetry/input.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
+
}
|