@ai-sdlc/orchestrator 0.5.0 → 0.6.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 (47) hide show
  1. package/dist/action-enforcement.d.ts +26 -0
  2. package/dist/action-enforcement.js +70 -0
  3. package/dist/admission-score.d.ts +58 -0
  4. package/dist/admission-score.js +164 -0
  5. package/dist/cycle-utils.d.ts +51 -0
  6. package/dist/cycle-utils.js +77 -0
  7. package/dist/defaults.d.ts +5 -0
  8. package/dist/defaults.js +5 -0
  9. package/dist/execute.js +121 -26
  10. package/dist/fix-ci.js +32 -2
  11. package/dist/fix-review.d.ts +66 -0
  12. package/dist/fix-review.js +441 -0
  13. package/dist/index.d.ts +10 -2
  14. package/dist/index.js +13 -1
  15. package/dist/pipeline-cycle-detector.d.ts +70 -0
  16. package/dist/pipeline-cycle-detector.js +111 -0
  17. package/dist/priority.d.ts +2 -76
  18. package/dist/review.d.ts +31 -0
  19. package/dist/review.js +74 -0
  20. package/dist/runners/claude-code.js +314 -32
  21. package/dist/runners/index.d.ts +2 -1
  22. package/dist/runners/index.js +1 -0
  23. package/dist/runners/review-agent.d.ts +47 -0
  24. package/dist/runners/review-agent.js +220 -0
  25. package/dist/runners/security-triage.js +4 -0
  26. package/dist/runners/types.d.ts +19 -0
  27. package/dist/state/index.d.ts +1 -1
  28. package/dist/state/schema.d.ts +2 -1
  29. package/dist/state/schema.js +54 -1
  30. package/dist/state/store.d.ts +17 -1
  31. package/dist/state/store.js +122 -0
  32. package/dist/state/types.d.ts +35 -0
  33. package/dist/workflow-patterns/artifact-writer.d.ts +16 -0
  34. package/dist/workflow-patterns/artifact-writer.js +34 -0
  35. package/dist/workflow-patterns/classifiers.d.ts +10 -0
  36. package/dist/workflow-patterns/classifiers.js +72 -0
  37. package/dist/workflow-patterns/detector.d.ts +27 -0
  38. package/dist/workflow-patterns/detector.js +186 -0
  39. package/dist/workflow-patterns/index.d.ts +8 -0
  40. package/dist/workflow-patterns/index.js +7 -0
  41. package/dist/workflow-patterns/proposal-generator.d.ts +15 -0
  42. package/dist/workflow-patterns/proposal-generator.js +183 -0
  43. package/dist/workflow-patterns/telemetry-ingest.d.ts +27 -0
  44. package/dist/workflow-patterns/telemetry-ingest.js +103 -0
  45. package/dist/workflow-patterns/types.d.ts +61 -0
  46. package/dist/workflow-patterns/types.js +11 -0
  47. package/package.json +2 -2
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Pattern type classifiers — refine detected patterns into
3
+ * specific automation types based on their structure.
4
+ */
5
+ /**
6
+ * Classify a detected pattern into a specific type and suggest an artifact.
7
+ */
8
+ export function classifyPattern(pattern) {
9
+ // Check classifiers in priority order
10
+ if (isCopyPasteCycle(pattern.steps)) {
11
+ return {
12
+ ...pattern,
13
+ patternType: 'copy-paste-cycle',
14
+ suggestedArtifactType: 'skill',
15
+ };
16
+ }
17
+ if (isPeriodicTask(pattern)) {
18
+ return {
19
+ ...pattern,
20
+ patternType: 'periodic-task',
21
+ suggestedArtifactType: 'workflow',
22
+ };
23
+ }
24
+ // Default: command sequence
25
+ return {
26
+ ...pattern,
27
+ patternType: 'command-sequence',
28
+ suggestedArtifactType: 'command',
29
+ };
30
+ }
31
+ /**
32
+ * Copy-paste cycle: Read from one file, then Write/Edit a different file type.
33
+ * Suggests the user is copying boilerplate or scaffolding from templates.
34
+ */
35
+ function isCopyPasteCycle(steps) {
36
+ let hasRead = false;
37
+ let hasWriteAfterRead = false;
38
+ for (const step of steps) {
39
+ if (step.category === 'read') {
40
+ hasRead = true;
41
+ }
42
+ else if (hasRead && step.category === 'write') {
43
+ hasWriteAfterRead = true;
44
+ }
45
+ }
46
+ // Must have at least one read→write transition
47
+ if (!hasWriteAfterRead)
48
+ return false;
49
+ // The write should target a different file than the read
50
+ const readActions = steps.filter((s) => s.category === 'read').map((s) => s.action);
51
+ const writeActions = steps.filter((s) => s.category === 'write').map((s) => s.action);
52
+ // If reads and writes target different extensions, it's likely scaffolding
53
+ const readExts = new Set(readActions.map((a) => a.split(':').pop()));
54
+ const writeExts = new Set(writeActions.map((a) => a.split(':').pop()));
55
+ // Different file types or at least some diversity
56
+ const hasDistinctTargets = readExts.size > 0 && writeExts.size > 0 && ![...readExts].every((e) => writeExts.has(e));
57
+ return hasDistinctTargets || (readActions.length > 0 && writeActions.length > 1);
58
+ }
59
+ /**
60
+ * Periodic task: same pattern occurs with regular time spacing.
61
+ * Requires firstSeen and lastSeen spanning 7+ days.
62
+ */
63
+ function isPeriodicTask(pattern) {
64
+ if (!pattern.firstSeen || !pattern.lastSeen)
65
+ return false;
66
+ const first = new Date(pattern.firstSeen).getTime();
67
+ const last = new Date(pattern.lastSeen).getTime();
68
+ const spanDays = (last - first) / (1000 * 60 * 60 * 24);
69
+ // Must span at least 7 days with regular occurrence
70
+ return spanDays >= 7 && pattern.sessionCount >= 3;
71
+ }
72
+ //# sourceMappingURL=classifiers.js.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Workflow pattern detector — mines frequent n-gram sequences
3
+ * from tool call histories across Claude Code sessions.
4
+ */
5
+ import type { ToolSequenceEvent } from '../state/types.js';
6
+ import type { CanonicalStep, NGram, DetectedPattern, DetectionOptions } from './types.js';
7
+ /**
8
+ * Convert a raw tool event to a canonical step.
9
+ */
10
+ export declare function canonicalizeStep(event: ToolSequenceEvent): CanonicalStep;
11
+ /**
12
+ * Hash a sequence of canonical steps for grouping.
13
+ */
14
+ export declare function hashSequence(steps: CanonicalStep[]): string;
15
+ /**
16
+ * Group tool sequence events by session, ordered by timestamp.
17
+ */
18
+ export declare function extractSessionSequences(events: ToolSequenceEvent[]): Map<string, CanonicalStep[]>;
19
+ /**
20
+ * Generate all contiguous n-grams from a sequence.
21
+ */
22
+ export declare function generateNGrams(sequence: CanonicalStep[], sessionId: string, minN: number, maxN: number): NGram[];
23
+ /**
24
+ * Mine frequent patterns from tool sequence events.
25
+ */
26
+ export declare function mineFrequentPatterns(events: ToolSequenceEvent[], opts?: Partial<DetectionOptions>): DetectedPattern[];
27
+ //# sourceMappingURL=detector.d.ts.map
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Workflow pattern detector — mines frequent n-gram sequences
3
+ * from tool call histories across Claude Code sessions.
4
+ */
5
+ import { createHash } from 'node:crypto';
6
+ import { categorizeAction } from './telemetry-ingest.js';
7
+ import { DEFAULT_DETECTION_OPTIONS } from './types.js';
8
+ /**
9
+ * Convert a raw tool event to a canonical step.
10
+ */
11
+ export function canonicalizeStep(event) {
12
+ return {
13
+ tool: event.toolName,
14
+ action: event.actionCanonical,
15
+ category: categorizeAction(event.toolName, event.actionCanonical),
16
+ };
17
+ }
18
+ /**
19
+ * Hash a sequence of canonical steps for grouping.
20
+ */
21
+ export function hashSequence(steps) {
22
+ const key = steps.map((s) => `${s.tool}:${s.action}`).join('|');
23
+ return createHash('sha256').update(key).digest('hex').slice(0, 16);
24
+ }
25
+ /**
26
+ * Group tool sequence events by session, ordered by timestamp.
27
+ */
28
+ export function extractSessionSequences(events) {
29
+ const sessions = new Map();
30
+ for (const event of events) {
31
+ const existing = sessions.get(event.sessionId) ?? [];
32
+ existing.push(event);
33
+ sessions.set(event.sessionId, existing);
34
+ }
35
+ const result = new Map();
36
+ for (const [sessionId, sessionEvents] of sessions) {
37
+ const sorted = sessionEvents.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
38
+ result.set(sessionId, sorted.map(canonicalizeStep));
39
+ }
40
+ return result;
41
+ }
42
+ /**
43
+ * Generate all contiguous n-grams from a sequence.
44
+ */
45
+ export function generateNGrams(sequence, sessionId, minN, maxN) {
46
+ const ngrams = [];
47
+ for (let n = minN; n <= Math.min(maxN, sequence.length); n++) {
48
+ for (let i = 0; i <= sequence.length - n; i++) {
49
+ const steps = sequence.slice(i, i + n);
50
+ ngrams.push({
51
+ steps,
52
+ hash: hashSequence(steps),
53
+ sessionId,
54
+ });
55
+ }
56
+ }
57
+ return ngrams;
58
+ }
59
+ /**
60
+ * Mine frequent patterns from tool sequence events.
61
+ */
62
+ export function mineFrequentPatterns(events, opts) {
63
+ const options = { ...DEFAULT_DETECTION_OPTIONS, ...opts };
64
+ // Filter by project and time
65
+ let filtered = events;
66
+ if (options.projectFilter) {
67
+ filtered = filtered.filter((e) => e.projectPath === options.projectFilter);
68
+ }
69
+ if (options.since) {
70
+ const sinceDate = new Date(options.since).getTime();
71
+ filtered = filtered.filter((e) => new Date(e.timestamp).getTime() >= sinceDate);
72
+ }
73
+ if (filtered.length === 0)
74
+ return [];
75
+ // Group by session
76
+ const sessions = extractSessionSequences(filtered);
77
+ const totalSessions = sessions.size;
78
+ if (totalSessions < options.minSessionCount)
79
+ return [];
80
+ // Generate n-grams across all sessions
81
+ const aggregates = new Map();
82
+ for (const [sessionId, sequence] of sessions) {
83
+ const ngrams = generateNGrams(sequence, sessionId, options.minSequenceLength, options.maxSequenceLength);
84
+ // Deduplicate within a session (same hash counted once per session)
85
+ const seenInSession = new Set();
86
+ for (const ngram of ngrams) {
87
+ if (seenInSession.has(ngram.hash))
88
+ continue;
89
+ seenInSession.add(ngram.hash);
90
+ const existing = aggregates.get(ngram.hash);
91
+ if (existing) {
92
+ existing.frequency++;
93
+ existing.sessionIds.add(sessionId);
94
+ }
95
+ else {
96
+ aggregates.set(ngram.hash, {
97
+ steps: ngram.steps,
98
+ hash: ngram.hash,
99
+ frequency: 1,
100
+ sessionIds: new Set([sessionId]),
101
+ timestamps: [],
102
+ });
103
+ }
104
+ }
105
+ }
106
+ // Filter by minimum frequency and session count
107
+ let patterns = [];
108
+ for (const agg of aggregates.values()) {
109
+ if (agg.frequency >= options.minFrequency && agg.sessionIds.size >= options.minSessionCount) {
110
+ patterns.push(agg);
111
+ }
112
+ }
113
+ // Compute confidence and find max frequency for normalization
114
+ const maxFrequency = Math.max(1, ...patterns.map((p) => p.frequency));
115
+ // Remove subsumed patterns (shorter patterns contained in longer ones with similar frequency)
116
+ patterns = removeSubsumedPatterns(patterns);
117
+ // Build detected patterns with confidence scoring
118
+ const detected = [];
119
+ for (const p of patterns) {
120
+ const confidence = Math.min(1.0, (p.sessionIds.size / totalSessions) * (p.frequency / maxFrequency));
121
+ if (confidence < options.minConfidence)
122
+ continue;
123
+ detected.push({
124
+ hash: p.hash,
125
+ steps: p.steps,
126
+ frequency: p.frequency,
127
+ sessionCount: p.sessionIds.size,
128
+ confidence,
129
+ patternType: 'command-sequence', // Default — classifiers refine this
130
+ suggestedArtifactType: 'command',
131
+ firstSeen: '', // Populated by caller from event timestamps
132
+ lastSeen: '',
133
+ exampleSessionIds: Array.from(p.sessionIds).slice(0, 5),
134
+ });
135
+ }
136
+ // Sort by confidence * length descending
137
+ detected.sort((a, b) => b.confidence * b.steps.length - a.confidence * a.steps.length);
138
+ return detected;
139
+ }
140
+ /**
141
+ * Remove patterns that are subsumed by longer patterns with similar frequency.
142
+ * A 3-gram is subsumed by a 5-gram if the 3-gram's steps appear contiguously
143
+ * in the 5-gram and the 5-gram has >= 70% of the 3-gram's frequency.
144
+ */
145
+ function removeSubsumedPatterns(patterns) {
146
+ // Sort by length descending so longer patterns take priority
147
+ const sorted = [...patterns].sort((a, b) => b.steps.length - a.steps.length);
148
+ const kept = [];
149
+ const removed = new Set();
150
+ for (const pattern of sorted) {
151
+ if (removed.has(pattern.hash))
152
+ continue;
153
+ kept.push(pattern);
154
+ // Mark shorter patterns that are subsumed
155
+ for (const other of sorted) {
156
+ if (other.hash === pattern.hash || removed.has(other.hash))
157
+ continue;
158
+ if (other.steps.length >= pattern.steps.length)
159
+ continue;
160
+ if (isSubsequence(other.steps, pattern.steps) && pattern.frequency >= other.frequency * 0.7) {
161
+ removed.add(other.hash);
162
+ }
163
+ }
164
+ }
165
+ return kept;
166
+ }
167
+ /**
168
+ * Check if `short` appears as a contiguous subsequence in `long`.
169
+ */
170
+ function isSubsequence(short, long) {
171
+ if (short.length > long.length)
172
+ return false;
173
+ for (let i = 0; i <= long.length - short.length; i++) {
174
+ let match = true;
175
+ for (let j = 0; j < short.length; j++) {
176
+ if (short[j].tool !== long[i + j].tool || short[j].action !== long[i + j].action) {
177
+ match = false;
178
+ break;
179
+ }
180
+ }
181
+ if (match)
182
+ return true;
183
+ }
184
+ return false;
185
+ }
186
+ //# sourceMappingURL=detector.js.map
@@ -0,0 +1,8 @@
1
+ export { readToolSequenceJSONL, readSessionMetaFiles, sessionMetaToEvents, categorizeAction, } from './telemetry-ingest.js';
2
+ export { canonicalizeStep, hashSequence, extractSessionSequences, generateNGrams, mineFrequentPatterns, } from './detector.js';
3
+ export { classifyPattern } from './classifiers.js';
4
+ export { generateProposal, generateName } from './proposal-generator.js';
5
+ export { writeArtifact, type WriteResult } from './artifact-writer.js';
6
+ export type { CanonicalStep, NGram, DetectedPattern, DetectionOptions, RawToolSequenceEntry, SessionMeta, } from './types.js';
7
+ export { DEFAULT_DETECTION_OPTIONS } from './types.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,7 @@
1
+ export { readToolSequenceJSONL, readSessionMetaFiles, sessionMetaToEvents, categorizeAction, } from './telemetry-ingest.js';
2
+ export { canonicalizeStep, hashSequence, extractSessionSequences, generateNGrams, mineFrequentPatterns, } from './detector.js';
3
+ export { classifyPattern } from './classifiers.js';
4
+ export { generateProposal, generateName } from './proposal-generator.js';
5
+ export { writeArtifact } from './artifact-writer.js';
6
+ export { DEFAULT_DETECTION_OPTIONS } from './types.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Proposal generator — creates draft automation artifacts from detected patterns.
3
+ * Each pattern type maps to a specific artifact: command, skill, hook, or workflow.
4
+ */
5
+ import type { DetectedPattern } from './types.js';
6
+ import type { PatternProposal } from '../state/types.js';
7
+ /**
8
+ * Generate an automation proposal from a detected pattern.
9
+ */
10
+ export declare function generateProposal(pattern: DetectedPattern): Omit<PatternProposal, 'patternId'>;
11
+ /**
12
+ * Generate a kebab-case name from the pattern's steps.
13
+ */
14
+ export declare function generateName(pattern: DetectedPattern): string;
15
+ //# sourceMappingURL=proposal-generator.d.ts.map
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Proposal generator — creates draft automation artifacts from detected patterns.
3
+ * Each pattern type maps to a specific artifact: command, skill, hook, or workflow.
4
+ */
5
+ /**
6
+ * Generate an automation proposal from a detected pattern.
7
+ */
8
+ export function generateProposal(pattern) {
9
+ const name = generateName(pattern);
10
+ switch (pattern.suggestedArtifactType) {
11
+ case 'command':
12
+ return {
13
+ proposalType: pattern.patternType,
14
+ artifactType: 'command',
15
+ artifactPath: `.claude/commands/${name}.md`,
16
+ draftContent: generateCommandTemplate(name, pattern),
17
+ confidence: computeProposalConfidence(pattern),
18
+ status: 'pending',
19
+ };
20
+ case 'skill':
21
+ return {
22
+ proposalType: pattern.patternType,
23
+ artifactType: 'skill',
24
+ artifactPath: `.claude/skills/${name}/SKILL.md`,
25
+ draftContent: generateSkillTemplate(name, pattern),
26
+ confidence: computeProposalConfidence(pattern),
27
+ status: 'pending',
28
+ };
29
+ case 'workflow':
30
+ return {
31
+ proposalType: pattern.patternType,
32
+ artifactType: 'workflow',
33
+ artifactPath: `.github/workflows/auto-${name}.yml`,
34
+ draftContent: generateWorkflowTemplate(name, pattern),
35
+ confidence: computeProposalConfidence(pattern),
36
+ status: 'pending',
37
+ };
38
+ case 'hook':
39
+ return {
40
+ proposalType: pattern.patternType,
41
+ artifactType: 'hook',
42
+ artifactPath: `.claude/hooks/${name}.sh`,
43
+ draftContent: generateHookTemplate(name, pattern),
44
+ confidence: computeProposalConfidence(pattern),
45
+ status: 'pending',
46
+ };
47
+ default:
48
+ return {
49
+ proposalType: pattern.patternType,
50
+ artifactType: 'command',
51
+ artifactPath: `.claude/commands/${name}.md`,
52
+ draftContent: generateCommandTemplate(name, pattern),
53
+ confidence: computeProposalConfidence(pattern),
54
+ status: 'pending',
55
+ };
56
+ }
57
+ }
58
+ /**
59
+ * Generate a kebab-case name from the pattern's steps.
60
+ */
61
+ export function generateName(pattern) {
62
+ // Take the most distinctive tool+action pairs
63
+ const parts = [];
64
+ for (const step of pattern.steps) {
65
+ const action = step.action
66
+ .replace(/[^a-zA-Z0-9\s]/g, '')
67
+ .trim()
68
+ .split(/\s+/)
69
+ .slice(0, 2)
70
+ .join('-')
71
+ .toLowerCase();
72
+ if (action && !parts.includes(action)) {
73
+ parts.push(action);
74
+ }
75
+ if (parts.length >= 3)
76
+ break;
77
+ }
78
+ const name = parts.length > 0 ? `auto-${parts.join('-')}` : `auto-pattern-${pattern.hash.slice(0, 8)}`;
79
+ return name.slice(0, 50);
80
+ }
81
+ function stepsToMarkdown(steps) {
82
+ return steps
83
+ .map((s, i) => {
84
+ const desc = s.action.includes(':') ? `${s.tool}: ${s.action}` : `Run \`${s.action}\``;
85
+ return `${i + 1}. ${desc}`;
86
+ })
87
+ .join('\n');
88
+ }
89
+ function generateCommandTemplate(name, pattern) {
90
+ return `---
91
+ name: ${name}
92
+ description: Auto-generated from detected workflow pattern (${pattern.patternType})
93
+ argument-hint: [optional-args]
94
+ ---
95
+
96
+ # ${name}
97
+
98
+ > Auto-generated from pattern detection. Confidence: ${(pattern.confidence * 100).toFixed(0)}%
99
+ > Observed ${pattern.frequency} times across ${pattern.sessionCount} sessions.
100
+
101
+ ## Steps
102
+
103
+ ${stepsToMarkdown(pattern.steps)}
104
+
105
+ ## Notes
106
+
107
+ This command was auto-generated by the workflow pattern detection engine.
108
+ Review and customize before using in production.
109
+ `;
110
+ }
111
+ function generateSkillTemplate(name, pattern) {
112
+ return `---
113
+ name: ${name}
114
+ description: Auto-generated skill from detected ${pattern.patternType} pattern
115
+ ---
116
+
117
+ # ${name}
118
+
119
+ > Auto-generated from pattern detection. Confidence: ${(pattern.confidence * 100).toFixed(0)}%
120
+ > Observed ${pattern.frequency} times across ${pattern.sessionCount} sessions.
121
+
122
+ ## Workflow
123
+
124
+ ${stepsToMarkdown(pattern.steps)}
125
+
126
+ ## Customization
127
+
128
+ This skill was auto-generated. Modify the steps above to match your
129
+ specific use case.
130
+ `;
131
+ }
132
+ function generateWorkflowTemplate(name, pattern) {
133
+ const schedule = '0 9 * * 1'; // Default: Monday 9am
134
+ return `# Auto-generated workflow from pattern detection
135
+ # Pattern: ${pattern.patternType}
136
+ # Confidence: ${(pattern.confidence * 100).toFixed(0)}%
137
+ # Observed ${pattern.frequency} times across ${pattern.sessionCount} sessions
138
+
139
+ name: Auto ${name}
140
+
141
+ on:
142
+ schedule:
143
+ - cron: '${schedule}'
144
+ workflow_dispatch:
145
+
146
+ jobs:
147
+ run:
148
+ runs-on: ubuntu-latest
149
+ steps:
150
+ - uses: actions/checkout@v4
151
+ ${pattern.steps.map((s) => ` - name: ${s.tool} ${s.action}\n run: echo "TODO: implement ${s.action}"`).join('\n')}
152
+ `;
153
+ }
154
+ function generateHookTemplate(name, pattern) {
155
+ return `#!/bin/bash
156
+ # Auto-generated hook from pattern detection
157
+ # Pattern: ${pattern.patternType}
158
+ # Confidence: ${(pattern.confidence * 100).toFixed(0)}%
159
+
160
+ set -euo pipefail
161
+
162
+ # TODO: Implement hook logic for pattern:
163
+ ${pattern.steps.map((s) => `# - ${s.tool}: ${s.action}`).join('\n')}
164
+
165
+ exit 0
166
+ `;
167
+ }
168
+ function computeProposalConfidence(pattern) {
169
+ // Base confidence from pattern detection
170
+ let confidence = pattern.confidence;
171
+ // Template fit: commands and skills have better template mapping
172
+ if (pattern.suggestedArtifactType === 'command' || pattern.suggestedArtifactType === 'skill') {
173
+ confidence *= 1.0; // Perfect fit
174
+ }
175
+ else if (pattern.suggestedArtifactType === 'workflow') {
176
+ confidence *= 0.8; // Periodic tasks need schedule tuning
177
+ }
178
+ else {
179
+ confidence *= 0.7; // Hooks need manual implementation
180
+ }
181
+ return Math.min(1.0, confidence);
182
+ }
183
+ //# sourceMappingURL=proposal-generator.js.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Telemetry ingestion — reads tool sequence JSONL and session-meta
3
+ * JSON files into ToolSequenceEvent format for pattern detection.
4
+ */
5
+ import type { ToolSequenceEvent } from '../state/types.js';
6
+ import type { SessionMeta, CanonicalStep } from './types.js';
7
+ /**
8
+ * Read tool sequence entries from a JSONL file.
9
+ * Each line is a JSON object with ts, sid, tool, action, project.
10
+ */
11
+ export declare function readToolSequenceJSONL(filePath: string): ToolSequenceEvent[];
12
+ /**
13
+ * Read session metadata from the Claude Code usage-data directory.
14
+ * Returns metadata for all sessions found.
15
+ */
16
+ export declare function readSessionMetaFiles(usageDataDir: string): SessionMeta[];
17
+ /**
18
+ * Convert session-meta tool_counts into synthetic ToolSequenceEvents.
19
+ * Since session-meta only has aggregate counts (no ordering), these
20
+ * events are useful for frequency analysis but not sequence mining.
21
+ */
22
+ export declare function sessionMetaToEvents(meta: SessionMeta): ToolSequenceEvent[];
23
+ /**
24
+ * Categorize a tool action into a workflow category.
25
+ */
26
+ export declare function categorizeAction(tool: string, action: string): CanonicalStep['category'];
27
+ //# sourceMappingURL=telemetry-ingest.d.ts.map
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Telemetry ingestion — reads tool sequence JSONL and session-meta
3
+ * JSON files into ToolSequenceEvent format for pattern detection.
4
+ */
5
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ /**
8
+ * Read tool sequence entries from a JSONL file.
9
+ * Each line is a JSON object with ts, sid, tool, action, project.
10
+ */
11
+ export function readToolSequenceJSONL(filePath) {
12
+ if (!existsSync(filePath))
13
+ return [];
14
+ const content = readFileSync(filePath, 'utf-8');
15
+ const events = [];
16
+ for (const line of content.split('\n')) {
17
+ const trimmed = line.trim();
18
+ if (!trimmed)
19
+ continue;
20
+ try {
21
+ const raw = JSON.parse(trimmed);
22
+ events.push({
23
+ sessionId: raw.sid,
24
+ toolName: raw.tool,
25
+ actionCanonical: raw.action,
26
+ projectPath: raw.project,
27
+ timestamp: raw.ts,
28
+ });
29
+ }
30
+ catch {
31
+ // Skip malformed lines
32
+ }
33
+ }
34
+ return events;
35
+ }
36
+ /**
37
+ * Read session metadata from the Claude Code usage-data directory.
38
+ * Returns metadata for all sessions found.
39
+ */
40
+ export function readSessionMetaFiles(usageDataDir) {
41
+ const metaDir = join(usageDataDir, 'session-meta');
42
+ if (!existsSync(metaDir))
43
+ return [];
44
+ const files = readdirSync(metaDir).filter((f) => f.endsWith('.json'));
45
+ const sessions = [];
46
+ for (const file of files) {
47
+ try {
48
+ const content = readFileSync(join(metaDir, file), 'utf-8');
49
+ const meta = JSON.parse(content);
50
+ if (meta.session_id && meta.tool_counts) {
51
+ sessions.push(meta);
52
+ }
53
+ }
54
+ catch {
55
+ // Skip malformed files
56
+ }
57
+ }
58
+ return sessions;
59
+ }
60
+ /**
61
+ * Convert session-meta tool_counts into synthetic ToolSequenceEvents.
62
+ * Since session-meta only has aggregate counts (no ordering), these
63
+ * events are useful for frequency analysis but not sequence mining.
64
+ */
65
+ export function sessionMetaToEvents(meta) {
66
+ const events = [];
67
+ const timestamp = meta.start_time;
68
+ for (const [tool, count] of Object.entries(meta.tool_counts)) {
69
+ for (let i = 0; i < count; i++) {
70
+ events.push({
71
+ sessionId: meta.session_id,
72
+ toolName: tool,
73
+ actionCanonical: tool.toLowerCase(),
74
+ projectPath: meta.project_path,
75
+ timestamp,
76
+ });
77
+ }
78
+ }
79
+ return events;
80
+ }
81
+ /**
82
+ * Categorize a tool action into a workflow category.
83
+ */
84
+ export function categorizeAction(tool, action) {
85
+ const lower = action.toLowerCase();
86
+ if (tool === 'Read' || lower.startsWith('read:'))
87
+ return 'read';
88
+ if (tool === 'Edit' ||
89
+ tool === 'Write' ||
90
+ lower.startsWith('edit:') ||
91
+ lower.startsWith('write:'))
92
+ return 'write';
93
+ if (lower.includes('test') || lower.includes('vitest') || lower.includes('jest'))
94
+ return 'test';
95
+ if (lower.includes('build') || lower.includes('tsc') || lower.includes('compile'))
96
+ return 'build';
97
+ if (lower.startsWith('git ') || lower.startsWith('gh '))
98
+ return 'git';
99
+ if (tool === 'Grep' || tool === 'Glob' || lower.startsWith('grep:') || lower.startsWith('glob:'))
100
+ return 'search';
101
+ return 'other';
102
+ }
103
+ //# sourceMappingURL=telemetry-ingest.js.map