@lcvbeek/patina 0.1.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 (58) hide show
  1. package/README.md +195 -0
  2. package/dist/commands/apply.d.ts +2 -0
  3. package/dist/commands/apply.d.ts.map +1 -0
  4. package/dist/commands/apply.js +186 -0
  5. package/dist/commands/apply.js.map +1 -0
  6. package/dist/commands/capture.d.ts +5 -0
  7. package/dist/commands/capture.d.ts.map +1 -0
  8. package/dist/commands/capture.js +88 -0
  9. package/dist/commands/capture.js.map +1 -0
  10. package/dist/commands/diff.d.ts +2 -0
  11. package/dist/commands/diff.d.ts.map +1 -0
  12. package/dist/commands/diff.js +43 -0
  13. package/dist/commands/diff.js.map +1 -0
  14. package/dist/commands/ingest.d.ts +14 -0
  15. package/dist/commands/ingest.d.ts.map +1 -0
  16. package/dist/commands/ingest.js +111 -0
  17. package/dist/commands/ingest.js.map +1 -0
  18. package/dist/commands/init.d.ts +2 -0
  19. package/dist/commands/init.d.ts.map +1 -0
  20. package/dist/commands/init.js +165 -0
  21. package/dist/commands/init.js.map +1 -0
  22. package/dist/commands/layers.d.ts +2 -0
  23. package/dist/commands/layers.d.ts.map +1 -0
  24. package/dist/commands/layers.js +141 -0
  25. package/dist/commands/layers.js.map +1 -0
  26. package/dist/commands/onboard.d.ts +2 -0
  27. package/dist/commands/onboard.d.ts.map +1 -0
  28. package/dist/commands/onboard.js +275 -0
  29. package/dist/commands/onboard.js.map +1 -0
  30. package/dist/commands/run.d.ts +4 -0
  31. package/dist/commands/run.d.ts.map +1 -0
  32. package/dist/commands/run.js +526 -0
  33. package/dist/commands/run.js.map +1 -0
  34. package/dist/commands/status.d.ts +2 -0
  35. package/dist/commands/status.d.ts.map +1 -0
  36. package/dist/commands/status.js +121 -0
  37. package/dist/commands/status.js.map +1 -0
  38. package/dist/index.d.ts +3 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +108 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/lib/claude.d.ts +12 -0
  43. package/dist/lib/claude.d.ts.map +1 -0
  44. package/dist/lib/claude.js +70 -0
  45. package/dist/lib/claude.js.map +1 -0
  46. package/dist/lib/metrics.d.ts +29 -0
  47. package/dist/lib/metrics.d.ts.map +1 -0
  48. package/dist/lib/metrics.js +96 -0
  49. package/dist/lib/metrics.js.map +1 -0
  50. package/dist/lib/parser.d.ts +28 -0
  51. package/dist/lib/parser.d.ts.map +1 -0
  52. package/dist/lib/parser.js +226 -0
  53. package/dist/lib/parser.js.map +1 -0
  54. package/dist/lib/storage.d.ts +126 -0
  55. package/dist/lib/storage.d.ts.map +1 -0
  56. package/dist/lib/storage.js +201 -0
  57. package/dist/lib/storage.js.map +1 -0
  58. package/package.json +45 -0
@@ -0,0 +1,226 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { z } from 'zod';
4
+ // ---------------------------------------------------------------------------
5
+ // Claude Code JSONL message schema (permissive — we only extract what we need)
6
+ // ---------------------------------------------------------------------------
7
+ // Each line in a Claude Code conversation file is one of these entry types.
8
+ const MessageEntrySchema = z.object({
9
+ type: z.string(),
10
+ uuid: z.string().optional(),
11
+ sessionId: z.string().optional(),
12
+ timestamp: z.string().optional(),
13
+ message: z
14
+ .object({
15
+ role: z.enum(['user', 'assistant']).optional(),
16
+ content: z
17
+ .union([
18
+ z.string(),
19
+ z.array(z.union([
20
+ z.object({
21
+ type: z.string(),
22
+ text: z.string().optional(),
23
+ name: z.string().optional(),
24
+ input: z.unknown().optional(),
25
+ }),
26
+ z.unknown(),
27
+ ])),
28
+ ])
29
+ .optional(),
30
+ })
31
+ .optional(),
32
+ // Some entries use a top-level content array (e.g. tool_result lines)
33
+ content: z
34
+ .union([
35
+ z.string(),
36
+ z.array(z.unknown()),
37
+ ])
38
+ .optional(),
39
+ });
40
+ // ---------------------------------------------------------------------------
41
+ // Heuristics
42
+ // ---------------------------------------------------------------------------
43
+ // Phrases that suggest the assistant is correcting itself
44
+ const REWORK_PHRASES = [
45
+ /let me try again/i,
46
+ /actually[,\s]/i,
47
+ /i made a mistake/i,
48
+ /i was wrong/i,
49
+ /apologies[,\s]/i,
50
+ /i apologise/i,
51
+ /i apologize/i,
52
+ /let me reconsider/i,
53
+ /i need to correct/i,
54
+ /i got that wrong/i,
55
+ /that was incorrect/i,
56
+ /allow me to redo/i,
57
+ /let me redo/i,
58
+ /i misunderstood/i,
59
+ /i overlooked/i,
60
+ ];
61
+ function detectRework(text) {
62
+ return REWORK_PHRASES.some((re) => re.test(text));
63
+ }
64
+ function extractTextFromContent(content) {
65
+ if (!content)
66
+ return '';
67
+ if (typeof content === 'string')
68
+ return content;
69
+ if (Array.isArray(content)) {
70
+ return content
71
+ .map((block) => {
72
+ if (block &&
73
+ typeof block === 'object' &&
74
+ 'type' in block &&
75
+ block.type === 'text' &&
76
+ 'text' in block) {
77
+ return block.text ?? '';
78
+ }
79
+ return '';
80
+ })
81
+ .join(' ');
82
+ }
83
+ return '';
84
+ }
85
+ function extractToolName(content) {
86
+ if (!Array.isArray(content))
87
+ return [];
88
+ const names = [];
89
+ for (const block of content) {
90
+ if (block &&
91
+ typeof block === 'object' &&
92
+ 'type' in block &&
93
+ block.type === 'tool_use' &&
94
+ 'name' in block) {
95
+ names.push(block.name);
96
+ }
97
+ }
98
+ return names;
99
+ }
100
+ // ---------------------------------------------------------------------------
101
+ // JSONL parsing
102
+ // ---------------------------------------------------------------------------
103
+ function parseJsonlFile(filePath) {
104
+ const raw = fs.readFileSync(filePath, 'utf-8');
105
+ const entries = [];
106
+ for (const line of raw.split('\n')) {
107
+ const trimmed = line.trim();
108
+ if (!trimmed)
109
+ continue;
110
+ try {
111
+ const parsed = JSON.parse(trimmed);
112
+ const result = MessageEntrySchema.safeParse(parsed);
113
+ if (result.success) {
114
+ entries.push(result.data);
115
+ }
116
+ // Silently skip lines that don't match — real logs have metadata entries too
117
+ }
118
+ catch {
119
+ // Malformed JSON line — skip
120
+ }
121
+ }
122
+ return entries;
123
+ }
124
+ /**
125
+ * Parse a single JSONL conversation file and return one ParsedSession per
126
+ * unique sessionId found. Claude Code can store multiple sessions in the same
127
+ * file, so we group by sessionId.
128
+ */
129
+ export function parseConversationFile(filePath, projectName) {
130
+ const entries = parseJsonlFile(filePath);
131
+ // Group entries by sessionId
132
+ const bySession = new Map();
133
+ for (const entry of entries) {
134
+ // sessionId can live on the entry directly or be derived from the uuid
135
+ const sid = entry.sessionId ??
136
+ (entry.uuid ? entry.uuid.split('-').slice(0, 3).join('-') : null);
137
+ if (!sid)
138
+ continue;
139
+ if (!bySession.has(sid)) {
140
+ bySession.set(sid, []);
141
+ }
142
+ bySession.get(sid).push(entry);
143
+ }
144
+ // If nothing was grouped (no sessionId fields), treat the whole file as one
145
+ // session using the filename as the id.
146
+ if (bySession.size === 0 && entries.length > 0) {
147
+ const fallbackId = path.basename(filePath, path.extname(filePath));
148
+ bySession.set(fallbackId, entries);
149
+ }
150
+ const sessions = [];
151
+ for (const [session_id, sessionEntries] of bySession) {
152
+ let turn_count = 0;
153
+ let char_count = 0;
154
+ const tool_calls = {};
155
+ let had_rework = false;
156
+ let earliest_timestamp = '';
157
+ for (const entry of sessionEntries) {
158
+ // Count message turns
159
+ if (entry.message) {
160
+ const role = entry.message.role;
161
+ if (role === 'user' || role === 'assistant') {
162
+ turn_count++;
163
+ }
164
+ const content = entry.message.content;
165
+ const text = extractTextFromContent(content);
166
+ char_count += text.length;
167
+ // Rework detection on assistant messages
168
+ if (role === 'assistant' && text && detectRework(text)) {
169
+ had_rework = true;
170
+ }
171
+ // Tool call counting from assistant messages
172
+ if (role === 'assistant') {
173
+ const toolNames = extractToolName(content);
174
+ for (const name of toolNames) {
175
+ tool_calls[name] = (tool_calls[name] ?? 0) + 1;
176
+ }
177
+ }
178
+ }
179
+ // Track earliest timestamp for the session
180
+ if (entry.timestamp && !earliest_timestamp) {
181
+ earliest_timestamp = entry.timestamp;
182
+ }
183
+ }
184
+ sessions.push({
185
+ session_id,
186
+ project: projectName,
187
+ timestamp: earliest_timestamp || new Date().toISOString(),
188
+ turn_count,
189
+ estimated_tokens: Math.ceil(char_count / 4),
190
+ tool_calls,
191
+ had_rework,
192
+ });
193
+ }
194
+ return sessions;
195
+ }
196
+ /**
197
+ * Scan ~/.claude/projects/ and return all projects that have a conversations
198
+ * JSONL file. Claude Code stores conversations at different paths depending on
199
+ * the version:
200
+ * ~/.claude/projects/<slug>/conversations.jsonl (older)
201
+ * ~/.claude/projects/<slug>/<uuid>.jsonl (newer, one file per session)
202
+ */
203
+ export function discoverProjects(claudeDir) {
204
+ const baseDir = claudeDir ?? path.join(process.env.HOME ?? '~', '.claude', 'projects');
205
+ if (!fs.existsSync(baseDir)) {
206
+ return [];
207
+ }
208
+ const projects = [];
209
+ const projectDirs = fs.readdirSync(baseDir, { withFileTypes: true });
210
+ for (const dirent of projectDirs) {
211
+ if (!dirent.isDirectory())
212
+ continue;
213
+ const projectPath = path.join(baseDir, dirent.name);
214
+ const projectName = dirent.name;
215
+ // Collect all JSONL files in this project directory
216
+ const files = fs.readdirSync(projectPath).filter((f) => f.endsWith('.jsonl'));
217
+ for (const file of files) {
218
+ projects.push({
219
+ name: projectName,
220
+ conversationFile: path.join(projectPath, file),
221
+ });
222
+ }
223
+ }
224
+ return projects;
225
+ }
226
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/lib/parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ import { z } from 'zod';
2
+ export declare const PATINA_DIR = ".patina";
3
+ export declare const SESSIONS_DIR: string;
4
+ export declare const CYCLES_DIR: string;
5
+ export declare const CAPTURES_DIR: string;
6
+ export declare const METRICS_FILE: string;
7
+ export declare const LIVING_DOC_FILE: string;
8
+ export declare function patinaExists(cwd?: string): boolean;
9
+ export declare function assertInitialised(cwd?: string): void;
10
+ export declare const SessionSummarySchema: z.ZodObject<{
11
+ session_id: z.ZodString;
12
+ project: z.ZodString;
13
+ timestamp: z.ZodString;
14
+ turn_count: z.ZodNumber;
15
+ estimated_tokens: z.ZodNumber;
16
+ tool_calls: z.ZodRecord<z.ZodString, z.ZodNumber>;
17
+ had_rework: z.ZodBoolean;
18
+ ingested_at: z.ZodString;
19
+ }, "strip", z.ZodTypeAny, {
20
+ session_id: string;
21
+ project: string;
22
+ timestamp: string;
23
+ turn_count: number;
24
+ estimated_tokens: number;
25
+ tool_calls: Record<string, number>;
26
+ had_rework: boolean;
27
+ ingested_at: string;
28
+ }, {
29
+ session_id: string;
30
+ project: string;
31
+ timestamp: string;
32
+ turn_count: number;
33
+ estimated_tokens: number;
34
+ tool_calls: Record<string, number>;
35
+ had_rework: boolean;
36
+ ingested_at: string;
37
+ }>;
38
+ export type SessionSummary = z.infer<typeof SessionSummarySchema>;
39
+ export declare const MetricsSchema: z.ZodObject<{
40
+ last_updated: z.ZodString;
41
+ cycles: z.ZodArray<z.ZodObject<{
42
+ cycle_id: z.ZodString;
43
+ created_at: z.ZodString;
44
+ session_count: z.ZodNumber;
45
+ total_tokens: z.ZodNumber;
46
+ rework_count: z.ZodNumber;
47
+ }, "strip", z.ZodTypeAny, {
48
+ cycle_id: string;
49
+ created_at: string;
50
+ session_count: number;
51
+ total_tokens: number;
52
+ rework_count: number;
53
+ }, {
54
+ cycle_id: string;
55
+ created_at: string;
56
+ session_count: number;
57
+ total_tokens: number;
58
+ rework_count: number;
59
+ }>, "many">;
60
+ }, "strip", z.ZodTypeAny, {
61
+ cycles: {
62
+ cycle_id: string;
63
+ created_at: string;
64
+ session_count: number;
65
+ total_tokens: number;
66
+ rework_count: number;
67
+ }[];
68
+ last_updated: string;
69
+ }, {
70
+ cycles: {
71
+ cycle_id: string;
72
+ created_at: string;
73
+ session_count: number;
74
+ total_tokens: number;
75
+ rework_count: number;
76
+ }[];
77
+ last_updated: string;
78
+ }>;
79
+ export type Metrics = z.infer<typeof MetricsSchema>;
80
+ export declare function ensureDir(dirPath: string): void;
81
+ export declare function writeJson(filePath: string, data: unknown): void;
82
+ export declare function readJson<T>(filePath: string): T;
83
+ export declare function fileExists(filePath: string): boolean;
84
+ export declare function sessionFilePath(sessionId: string, cwd?: string): string;
85
+ export declare function sessionExists(sessionId: string, cwd?: string): boolean;
86
+ export declare function writeSession(summary: SessionSummary, cwd?: string): void;
87
+ export declare function readAllSessions(cwd?: string): SessionSummary[];
88
+ export declare function readMetrics(cwd?: string): Metrics;
89
+ export declare function writeMetrics(metrics: Metrics, cwd?: string): void;
90
+ export interface PendingDiff {
91
+ section: string;
92
+ rationale: string;
93
+ diff: string;
94
+ timestamp: string;
95
+ }
96
+ export declare const PENDING_DIFF_FILE: string;
97
+ export declare function writePendingDiff(diff: PendingDiff, cwd?: string): void;
98
+ export declare function readPendingDiff(cwd?: string): PendingDiff | null;
99
+ export declare function cycleFilePath(date: string, cwd?: string): string;
100
+ export declare function getLatestCycleDate(cwd?: string): string | null;
101
+ export declare function writeCycleFile(date: string, content: string, cwd?: string): void;
102
+ export declare const CAPTURE_TAGS: readonly ["near-miss", "went-well", "frustration", "pattern", "other"];
103
+ export type CaptureTag = typeof CAPTURE_TAGS[number];
104
+ export declare const CaptureSchema: z.ZodObject<{
105
+ id: z.ZodString;
106
+ text: z.ZodString;
107
+ tag: z.ZodOptional<z.ZodEnum<["near-miss", "went-well", "frustration", "pattern", "other"]>>;
108
+ author: z.ZodString;
109
+ timestamp: z.ZodString;
110
+ }, "strip", z.ZodTypeAny, {
111
+ timestamp: string;
112
+ id: string;
113
+ text: string;
114
+ author: string;
115
+ tag?: "near-miss" | "went-well" | "frustration" | "pattern" | "other" | undefined;
116
+ }, {
117
+ timestamp: string;
118
+ id: string;
119
+ text: string;
120
+ author: string;
121
+ tag?: "near-miss" | "went-well" | "frustration" | "pattern" | "other" | undefined;
122
+ }>;
123
+ export type Capture = z.infer<typeof CaptureSchema>;
124
+ export declare function writeCapture(capture: Capture, cwd?: string): void;
125
+ export declare function readCaptures(cwd?: string, sinceDate?: string | null): Capture[];
126
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/lib/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,201 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { z } from 'zod';
4
+ // ---------------------------------------------------------------------------
5
+ // Paths
6
+ // ---------------------------------------------------------------------------
7
+ export const PATINA_DIR = '.patina';
8
+ export const SESSIONS_DIR = path.join(PATINA_DIR, 'sessions');
9
+ export const CYCLES_DIR = path.join(PATINA_DIR, 'cycles');
10
+ export const CAPTURES_DIR = path.join(PATINA_DIR, 'captures');
11
+ export const METRICS_FILE = path.join(PATINA_DIR, 'metrics.json');
12
+ export const LIVING_DOC_FILE = path.join(PATINA_DIR, 'patina.md');
13
+ export function patinaExists(cwd = process.cwd()) {
14
+ return fs.existsSync(path.join(cwd, PATINA_DIR));
15
+ }
16
+ export function assertInitialised(cwd = process.cwd()) {
17
+ if (!patinaExists(cwd)) {
18
+ console.error('Error: .patina/ not found. Run `patina init` first.');
19
+ process.exit(1);
20
+ }
21
+ }
22
+ // ---------------------------------------------------------------------------
23
+ // Session summary schema
24
+ // ---------------------------------------------------------------------------
25
+ export const SessionSummarySchema = z.object({
26
+ session_id: z.string(),
27
+ project: z.string(),
28
+ timestamp: z.string(), // ISO-8601
29
+ turn_count: z.number().int().nonnegative(),
30
+ estimated_tokens: z.number().int().nonnegative(),
31
+ tool_calls: z.record(z.string(), z.number().int().nonnegative()),
32
+ had_rework: z.boolean(),
33
+ ingested_at: z.string(), // ISO-8601
34
+ });
35
+ // ---------------------------------------------------------------------------
36
+ // Metrics schema
37
+ // ---------------------------------------------------------------------------
38
+ export const MetricsSchema = z.object({
39
+ last_updated: z.string(),
40
+ cycles: z.array(z.object({
41
+ cycle_id: z.string(),
42
+ created_at: z.string(),
43
+ session_count: z.number(),
44
+ total_tokens: z.number(),
45
+ rework_count: z.number(),
46
+ })),
47
+ });
48
+ // ---------------------------------------------------------------------------
49
+ // File I/O helpers
50
+ // ---------------------------------------------------------------------------
51
+ export function ensureDir(dirPath) {
52
+ fs.mkdirSync(dirPath, { recursive: true });
53
+ }
54
+ export function writeJson(filePath, data) {
55
+ ensureDir(path.dirname(filePath));
56
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
57
+ }
58
+ export function readJson(filePath) {
59
+ const raw = fs.readFileSync(filePath, 'utf-8');
60
+ return JSON.parse(raw);
61
+ }
62
+ export function fileExists(filePath) {
63
+ return fs.existsSync(filePath);
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // Session helpers
67
+ // ---------------------------------------------------------------------------
68
+ export function sessionFilePath(sessionId, cwd = process.cwd()) {
69
+ // Use a safe filename derived from the session_id
70
+ const safe = sessionId.replace(/[^a-zA-Z0-9_-]/g, '_');
71
+ return path.join(cwd, SESSIONS_DIR, `${safe}.json`);
72
+ }
73
+ export function sessionExists(sessionId, cwd = process.cwd()) {
74
+ return fileExists(sessionFilePath(sessionId, cwd));
75
+ }
76
+ export function writeSession(summary, cwd = process.cwd()) {
77
+ const validated = SessionSummarySchema.parse(summary);
78
+ writeJson(sessionFilePath(summary.session_id, cwd), validated);
79
+ }
80
+ export function readAllSessions(cwd = process.cwd()) {
81
+ const dir = path.join(cwd, SESSIONS_DIR);
82
+ if (!fs.existsSync(dir))
83
+ return [];
84
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
85
+ const sessions = [];
86
+ for (const file of files) {
87
+ try {
88
+ const raw = readJson(path.join(dir, file));
89
+ const parsed = SessionSummarySchema.safeParse(raw);
90
+ if (parsed.success) {
91
+ sessions.push(parsed.data);
92
+ }
93
+ else {
94
+ console.warn(`Warning: skipping malformed session file: ${file}`);
95
+ }
96
+ }
97
+ catch {
98
+ console.warn(`Warning: could not read session file: ${file}`);
99
+ }
100
+ }
101
+ return sessions;
102
+ }
103
+ export function readMetrics(cwd = process.cwd()) {
104
+ const file = path.join(cwd, METRICS_FILE);
105
+ if (!fileExists(file))
106
+ return { last_updated: new Date().toISOString(), cycles: [] };
107
+ try {
108
+ const raw = readJson(file);
109
+ return MetricsSchema.parse(raw);
110
+ }
111
+ catch {
112
+ return { last_updated: new Date().toISOString(), cycles: [] };
113
+ }
114
+ }
115
+ export function writeMetrics(metrics, cwd = process.cwd()) {
116
+ const validated = MetricsSchema.parse(metrics);
117
+ writeJson(path.join(cwd, METRICS_FILE), validated);
118
+ }
119
+ export const PENDING_DIFF_FILE = path.join(PATINA_DIR, 'pending-diff.json');
120
+ export function writePendingDiff(diff, cwd = process.cwd()) {
121
+ writeJson(path.join(cwd, PENDING_DIFF_FILE), diff);
122
+ }
123
+ export function readPendingDiff(cwd = process.cwd()) {
124
+ const file = path.join(cwd, PENDING_DIFF_FILE);
125
+ if (!fileExists(file))
126
+ return null;
127
+ try {
128
+ return readJson(file);
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ // ---------------------------------------------------------------------------
135
+ // Cycle file helpers
136
+ // ---------------------------------------------------------------------------
137
+ export function cycleFilePath(date, cwd = process.cwd()) {
138
+ return path.join(cwd, CYCLES_DIR, `${date}.md`);
139
+ }
140
+ export function getLatestCycleDate(cwd = process.cwd()) {
141
+ const dir = path.join(cwd, CYCLES_DIR);
142
+ if (!fs.existsSync(dir))
143
+ return null;
144
+ const files = fs
145
+ .readdirSync(dir)
146
+ .filter((f) => /^\d{4}-\d{2}-\d{2}\.md$/.test(f))
147
+ .sort();
148
+ if (files.length === 0)
149
+ return null;
150
+ // Remove .md suffix to return just the date string
151
+ return files[files.length - 1].replace(/\.md$/, '');
152
+ }
153
+ export function writeCycleFile(date, content, cwd = process.cwd()) {
154
+ const filePath = cycleFilePath(date, cwd);
155
+ ensureDir(path.dirname(filePath));
156
+ fs.writeFileSync(filePath, content, 'utf-8');
157
+ }
158
+ // ---------------------------------------------------------------------------
159
+ // Capture helpers (event-driven capture between retro cycles)
160
+ // ---------------------------------------------------------------------------
161
+ export const CAPTURE_TAGS = ['near-miss', 'went-well', 'frustration', 'pattern', 'other'];
162
+ export const CaptureSchema = z.object({
163
+ id: z.string(),
164
+ text: z.string(),
165
+ tag: z.enum(CAPTURE_TAGS).optional(),
166
+ author: z.string(),
167
+ timestamp: z.string(), // ISO-8601
168
+ });
169
+ export function writeCapture(capture, cwd = process.cwd()) {
170
+ const validated = CaptureSchema.parse(capture);
171
+ const safe = capture.id.replace(/[^a-zA-Z0-9_-]/g, '_');
172
+ const filePath = path.join(cwd, CAPTURES_DIR, `${safe}.json`);
173
+ ensureDir(path.dirname(filePath));
174
+ writeJson(filePath, validated);
175
+ }
176
+ export function readCaptures(cwd = process.cwd(), sinceDate) {
177
+ const dir = path.join(cwd, CAPTURES_DIR);
178
+ if (!fs.existsSync(dir))
179
+ return [];
180
+ const files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
181
+ const captures = [];
182
+ for (const file of files) {
183
+ try {
184
+ const raw = readJson(path.join(dir, file));
185
+ const parsed = CaptureSchema.safeParse(raw);
186
+ if (parsed.success) {
187
+ captures.push(parsed.data);
188
+ }
189
+ }
190
+ catch {
191
+ // skip malformed capture files
192
+ }
193
+ }
194
+ const sorted = captures.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
195
+ if (sinceDate) {
196
+ const cutoff = new Date(sinceDate + 'T00:00:00Z').getTime();
197
+ return sorted.filter((c) => new Date(c.timestamp).getTime() > cutoff);
198
+ }
199
+ return sorted;
200
+ }
201
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/lib/storage.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@lcvbeek/patina",
3
+ "version": "0.1.0",
4
+ "description": "AI-assisted retrospective tool for Claude Code teams",
5
+ "type": "module",
6
+ "bin": {
7
+ "patina": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist/",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/lcvbeek/patina.git"
17
+ },
18
+ "homepage": "https://github.com/lcvbeek/patina#readme",
19
+ "bugs": {
20
+ "url": "https://github.com/lcvbeek/patina/issues"
21
+ },
22
+ "keywords": [
23
+ "ai",
24
+ "retrospective",
25
+ "claude",
26
+ "developer-tools",
27
+ "team"
28
+ ],
29
+ "scripts": {
30
+ "dev": "tsx src/index.ts",
31
+ "build": "tsc",
32
+ "start": "node dist/index.js",
33
+ "prepublishOnly": "npm run build"
34
+ },
35
+ "dependencies": {
36
+ "@anthropic-ai/sdk": "^0.37.0",
37
+ "commander": "^12.1.0",
38
+ "zod": "^3.23.8"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^22.10.0",
42
+ "tsx": "^4.19.2",
43
+ "typescript": "^5.7.2"
44
+ }
45
+ }