@bbyqtbean/taco-helper 0.1.2 → 0.2.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.
@@ -0,0 +1,77 @@
1
+ /**
2
+ * SQLite-backed comment store — used by the daemon.
3
+ *
4
+ * Replaces the in-memory CommentStore for daemon mode.
5
+ * Comments persist across daemon restarts in ~/.taco/comments.db.
6
+ *
7
+ * See: daemon-mcp-strategy.md §Database Schema
8
+ */
9
+ import { EventEmitter } from 'events';
10
+ /** The Taco data directory */
11
+ export declare const TACO_DIR: string;
12
+ /** Path to the SQLite database */
13
+ export declare const DB_PATH: string;
14
+ export interface SqliteComment {
15
+ id: string;
16
+ url: string;
17
+ port: number;
18
+ selector: string;
19
+ text: string;
20
+ screenshot: string | null;
21
+ resolved: number;
22
+ created_at: string;
23
+ }
24
+ export declare class SqliteStore extends EventEmitter {
25
+ private db;
26
+ constructor(dbPath?: string);
27
+ /**
28
+ * Extract port from a URL string.
29
+ * e.g. "http://localhost:3000/dashboard" → 3000
30
+ */
31
+ private extractPort;
32
+ /**
33
+ * Add a comment to the store.
34
+ * If a comment with the same ID already exists, it is replaced.
35
+ */
36
+ addComment(comment: {
37
+ id: string;
38
+ url: string;
39
+ text: string;
40
+ selector: string;
41
+ screenshot?: string | null;
42
+ }): void;
43
+ /**
44
+ * Add multiple comments. Returns the number added.
45
+ */
46
+ addComments(comments: Array<{
47
+ id: string;
48
+ url: string;
49
+ text: string;
50
+ selector: string;
51
+ screenshot?: string | null;
52
+ }>): number;
53
+ /**
54
+ * Get comments, optionally filtered by port.
55
+ * By default returns only unresolved comments.
56
+ */
57
+ getComments(opts?: {
58
+ includeResolved?: boolean;
59
+ port?: number;
60
+ }): SqliteComment[];
61
+ /**
62
+ * Mark a comment as resolved by ID.
63
+ * Returns true if the comment was found and updated.
64
+ */
65
+ markResolved(id: string): boolean;
66
+ /**
67
+ * Mark a comment as actioned (in-progress) by ID.
68
+ * For now we track this as metadata — the resolved column stays 0.
69
+ * Emits comment-actioned event for SSE broadcast.
70
+ */
71
+ markActioned(id: string): boolean;
72
+ /**
73
+ * Close the database connection.
74
+ */
75
+ close(): void;
76
+ }
77
+ //# sourceMappingURL=sqlite-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-store.d.ts","sourceRoot":"","sources":["../src/sqlite-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAKtC,8BAA8B;AAC9B,eAAO,MAAM,QAAQ,QAA2B,CAAC;AAEjD,kCAAkC;AAClC,eAAO,MAAM,OAAO,QAAgC,CAAC;AAErD,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,WAAY,SAAQ,YAAY;IACzC,OAAO,CAAC,EAAE,CAAoB;gBAElB,MAAM,GAAE,MAAgB;IAgCpC;;;OAGG;IACH,OAAO,CAAC,WAAW;IASnB;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE;QAChB,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,GAAG,IAAI;IAoBR;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC;QACxB,EAAE,EAAE,MAAM,CAAC;QACX,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,CAAC,GAAG,MAAM;IAUX;;;OAGG;IACH,WAAW,CAAC,IAAI,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,aAAa,EAAE;IAwBrF;;;OAGG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IASjC;;;;OAIG;IACH,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAQjC;;OAEG;IACH,KAAK,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * SQLite-backed comment store — used by the daemon.
3
+ *
4
+ * Replaces the in-memory CommentStore for daemon mode.
5
+ * Comments persist across daemon restarts in ~/.taco/comments.db.
6
+ *
7
+ * See: daemon-mcp-strategy.md §Database Schema
8
+ */
9
+ import Database from 'better-sqlite3';
10
+ import { EventEmitter } from 'events';
11
+ import { mkdirSync, existsSync } from 'fs';
12
+ import { join } from 'path';
13
+ import { homedir } from 'os';
14
+ /** The Taco data directory */
15
+ export const TACO_DIR = join(homedir(), '.taco');
16
+ /** Path to the SQLite database */
17
+ export const DB_PATH = join(TACO_DIR, 'comments.db');
18
+ export class SqliteStore extends EventEmitter {
19
+ db;
20
+ constructor(dbPath = DB_PATH) {
21
+ super();
22
+ // Ensure ~/.taco/ exists
23
+ const dir = join(dbPath, '..');
24
+ if (!existsSync(dir)) {
25
+ mkdirSync(dir, { recursive: true });
26
+ }
27
+ this.db = new Database(dbPath);
28
+ // Enable WAL mode for better concurrent read performance
29
+ this.db.pragma('journal_mode = WAL');
30
+ // Create schema
31
+ this.db.exec(`
32
+ CREATE TABLE IF NOT EXISTS comments (
33
+ id TEXT PRIMARY KEY,
34
+ url TEXT NOT NULL,
35
+ port INTEGER NOT NULL,
36
+ selector TEXT NOT NULL,
37
+ text TEXT NOT NULL,
38
+ screenshot TEXT,
39
+ resolved INTEGER NOT NULL DEFAULT 0,
40
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
41
+ );
42
+
43
+ CREATE INDEX IF NOT EXISTS idx_comments_port ON comments(port);
44
+ CREATE INDEX IF NOT EXISTS idx_comments_resolved ON comments(resolved);
45
+ `);
46
+ }
47
+ /**
48
+ * Extract port from a URL string.
49
+ * e.g. "http://localhost:3000/dashboard" → 3000
50
+ */
51
+ extractPort(url) {
52
+ try {
53
+ const parsed = new URL(url);
54
+ return parsed.port ? parseInt(parsed.port, 10) : (parsed.protocol === 'https:' ? 443 : 80);
55
+ }
56
+ catch {
57
+ return 0;
58
+ }
59
+ }
60
+ /**
61
+ * Add a comment to the store.
62
+ * If a comment with the same ID already exists, it is replaced.
63
+ */
64
+ addComment(comment) {
65
+ const port = this.extractPort(comment.url);
66
+ const stmt = this.db.prepare(`
67
+ INSERT OR REPLACE INTO comments (id, url, port, selector, text, screenshot, resolved, created_at)
68
+ VALUES (?, ?, ?, ?, ?, ?, 0, datetime('now'))
69
+ `);
70
+ stmt.run(comment.id, comment.url, port, comment.selector, comment.text, comment.screenshot ?? null);
71
+ this.emit('comment-received', { id: comment.id });
72
+ }
73
+ /**
74
+ * Add multiple comments. Returns the number added.
75
+ */
76
+ addComments(comments) {
77
+ const transaction = this.db.transaction(() => {
78
+ for (const c of comments) {
79
+ this.addComment(c);
80
+ }
81
+ });
82
+ transaction();
83
+ return comments.length;
84
+ }
85
+ /**
86
+ * Get comments, optionally filtered by port.
87
+ * By default returns only unresolved comments.
88
+ */
89
+ getComments(opts = {}) {
90
+ const { includeResolved = false, port } = opts;
91
+ let query = 'SELECT * FROM comments';
92
+ const conditions = [];
93
+ const params = [];
94
+ if (!includeResolved) {
95
+ conditions.push('resolved = 0');
96
+ }
97
+ if (port !== undefined) {
98
+ conditions.push('port = ?');
99
+ params.push(port);
100
+ }
101
+ if (conditions.length > 0) {
102
+ query += ' WHERE ' + conditions.join(' AND ');
103
+ }
104
+ query += ' ORDER BY created_at ASC';
105
+ return this.db.prepare(query).all(...params);
106
+ }
107
+ /**
108
+ * Mark a comment as resolved by ID.
109
+ * Returns true if the comment was found and updated.
110
+ */
111
+ markResolved(id) {
112
+ const result = this.db.prepare('UPDATE comments SET resolved = 1 WHERE id = ? AND resolved = 0').run(id);
113
+ if (result.changes > 0) {
114
+ this.emit('comment-resolved', { id });
115
+ return true;
116
+ }
117
+ return false;
118
+ }
119
+ /**
120
+ * Mark a comment as actioned (in-progress) by ID.
121
+ * For now we track this as metadata — the resolved column stays 0.
122
+ * Emits comment-actioned event for SSE broadcast.
123
+ */
124
+ markActioned(id) {
125
+ // Verify the comment exists
126
+ const comment = this.db.prepare('SELECT id FROM comments WHERE id = ?').get(id);
127
+ if (!comment)
128
+ return false;
129
+ this.emit('comment-actioned', { id });
130
+ return true;
131
+ }
132
+ /**
133
+ * Close the database connection.
134
+ */
135
+ close() {
136
+ this.db.close();
137
+ }
138
+ }
139
+ //# sourceMappingURL=sqlite-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-store.js","sourceRoot":"","sources":["../src/sqlite-store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbyqtbean/taco-helper",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server + CLI for connecting the Taco Chrome extension to AI agents",
5
5
  "mcpName": "taco-helper",
6
6
  "type": "module",
@@ -21,9 +21,11 @@
21
21
  ],
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "@modelcontextprotocol/sdk": "^1.26.0"
24
+ "@modelcontextprotocol/sdk": "^1.26.0",
25
+ "better-sqlite3": "^12.6.2"
25
26
  },
26
27
  "devDependencies": {
28
+ "@types/better-sqlite3": "^7.6.13",
27
29
  "@types/node": "^20.11.0",
28
30
  "typescript": "^5.3.3"
29
31
  },