2ndbrain 2026.1.30 → 2026.1.31
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/.claude/settings.local.json +16 -0
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/db/migrations/001_initial_schema.sql +91 -0
- package/doc/SPEC.md +896 -0
- package/hooks/auto-capture.sh +4 -0
- package/hooks/validate-command.sh +374 -0
- package/package.json +34 -20
- package/skills/journal/SKILL.md +112 -0
- package/skills/knowledge/SKILL.md +165 -0
- package/skills/project-manage/SKILL.md +216 -0
- package/skills/recall/SKILL.md +182 -0
- package/skills/system-ops/SKILL.md +161 -0
- package/src/attachments/store.js +167 -0
- package/src/claude/bridge.js +291 -0
- package/src/claude/conversation.js +219 -0
- package/src/config.js +90 -0
- package/src/db/migrate.js +94 -0
- package/src/db/pool.js +33 -0
- package/src/embeddings/engine.js +281 -0
- package/src/embeddings/worker.js +221 -0
- package/src/hooks/lifecycle.js +448 -0
- package/src/index.js +560 -0
- package/src/logging.js +91 -0
- package/src/mcp/config.js +75 -0
- package/src/mcp/embed-server.js +242 -0
- package/src/rate-limiter.js +114 -0
- package/src/telegram/bot.js +546 -0
- package/src/telegram/commands.js +440 -0
- package/src/web/server.js +880 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Skill: knowledge
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Manage a personal knowledge graph with nodes and edges. Nodes represent concepts, people, places, ideas, or anything worth tracking. Edges represent named, directed relationships between nodes (e.g., "works at", "related to", "depends on"). Use this skill to build, query, and traverse the user's personal knowledge graph.
|
|
6
|
+
|
|
7
|
+
## When to Activate
|
|
8
|
+
|
|
9
|
+
Activate this skill when any of the following conditions are met:
|
|
10
|
+
|
|
11
|
+
- The user's message begins with `/knowledge`
|
|
12
|
+
- The user mentions entities and wants them tracked (e.g., "remember that Alice works at Acme Corp", "Bob is my dentist")
|
|
13
|
+
- The user asks about relationships between things (e.g., "how is X related to Y", "what do I know about X", "who works at Y")
|
|
14
|
+
- The user wants to create, update, or explore connections between concepts
|
|
15
|
+
- The user asks to list or browse known entities
|
|
16
|
+
|
|
17
|
+
## Available Tools
|
|
18
|
+
|
|
19
|
+
- `mcp__pg__query` -- Execute SQL queries against the PostgreSQL database.
|
|
20
|
+
|
|
21
|
+
No other tools are permitted for this skill.
|
|
22
|
+
|
|
23
|
+
## Database Tables
|
|
24
|
+
|
|
25
|
+
### `knowledge_nodes`
|
|
26
|
+
|
|
27
|
+
| Column | Type | Description |
|
|
28
|
+
|--------|------|-------------|
|
|
29
|
+
| `id` | SERIAL PRIMARY KEY | Auto-incrementing identifier |
|
|
30
|
+
| `created_at` | TIMESTAMPTZ | Timestamp of creation |
|
|
31
|
+
| `updated_at` | TIMESTAMPTZ | Timestamp of last update |
|
|
32
|
+
| `name` | TEXT NOT NULL | Name of the entity (person, concept, place, etc.) |
|
|
33
|
+
| `note` | TEXT | Optional descriptive note about the entity |
|
|
34
|
+
|
|
35
|
+
### `knowledge_edges`
|
|
36
|
+
|
|
37
|
+
| Column | Type | Description |
|
|
38
|
+
|--------|------|-------------|
|
|
39
|
+
| `id` | SERIAL PRIMARY KEY | Auto-incrementing identifier |
|
|
40
|
+
| `created_at` | TIMESTAMPTZ | Timestamp of creation |
|
|
41
|
+
| `updated_at` | TIMESTAMPTZ | Timestamp of last update |
|
|
42
|
+
| `source_id` | INTEGER NOT NULL | FK to `knowledge_nodes.id` (the "from" node) |
|
|
43
|
+
| `target_id` | INTEGER NOT NULL | FK to `knowledge_nodes.id` (the "to" node) |
|
|
44
|
+
| `name` | TEXT NOT NULL | Relationship label (e.g., "works at", "is friend of") |
|
|
45
|
+
|
|
46
|
+
There is a unique constraint on `(source_id, target_id, name)` to prevent duplicate edges.
|
|
47
|
+
|
|
48
|
+
### `embeddings` (for post-create embedding queue only)
|
|
49
|
+
|
|
50
|
+
| Column | Type | Description |
|
|
51
|
+
|--------|------|-------------|
|
|
52
|
+
| `entity_type` | TEXT NOT NULL | Type of entity (use `'node'` for knowledge nodes) |
|
|
53
|
+
| `entity_id` | INTEGER NOT NULL | The `id` of the knowledge node |
|
|
54
|
+
|
|
55
|
+
## Operations
|
|
56
|
+
|
|
57
|
+
### Create a Node
|
|
58
|
+
|
|
59
|
+
When the user mentions a new concept, person, place, or idea to track.
|
|
60
|
+
|
|
61
|
+
```sql
|
|
62
|
+
INSERT INTO knowledge_nodes (name, note)
|
|
63
|
+
VALUES ('Alice', 'Friend from university, works in data science')
|
|
64
|
+
RETURNING id, name;
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Before creating a node, check if one with the same name already exists to avoid duplicates:
|
|
68
|
+
|
|
69
|
+
```sql
|
|
70
|
+
SELECT id, name, note FROM knowledge_nodes WHERE name ILIKE 'Alice';
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
If a match exists, ask the user whether to update the existing node or create a new one.
|
|
74
|
+
|
|
75
|
+
### Create an Edge
|
|
76
|
+
|
|
77
|
+
When the user describes a relationship between two entities.
|
|
78
|
+
|
|
79
|
+
```sql
|
|
80
|
+
INSERT INTO knowledge_edges (source_id, target_id, name)
|
|
81
|
+
VALUES (1, 2, 'works at')
|
|
82
|
+
ON CONFLICT (source_id, target_id, name) DO NOTHING;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Both the source and target nodes must exist before creating an edge. If either does not exist, create the missing node(s) first (with user confirmation if the intent is ambiguous).
|
|
86
|
+
|
|
87
|
+
### Query a Node by Name
|
|
88
|
+
|
|
89
|
+
When the user asks "what do I know about X" or mentions an entity by name.
|
|
90
|
+
|
|
91
|
+
```sql
|
|
92
|
+
SELECT id, name, note, created_at
|
|
93
|
+
FROM knowledge_nodes
|
|
94
|
+
WHERE name ILIKE '%' || 'search term' || '%';
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Traverse Edges from a Node
|
|
98
|
+
|
|
99
|
+
When the user asks about relationships or connections (e.g., "how is X connected", "what is related to X").
|
|
100
|
+
|
|
101
|
+
Outgoing relationships (what does X relate to):
|
|
102
|
+
|
|
103
|
+
```sql
|
|
104
|
+
SELECT kn.name AS target, ke.name AS relationship
|
|
105
|
+
FROM knowledge_edges ke
|
|
106
|
+
JOIN knowledge_nodes kn ON kn.id = ke.target_id
|
|
107
|
+
WHERE ke.source_id = <node_id>;
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Incoming relationships (what relates to X):
|
|
111
|
+
|
|
112
|
+
```sql
|
|
113
|
+
SELECT kn.name AS source, ke.name AS relationship
|
|
114
|
+
FROM knowledge_edges ke
|
|
115
|
+
JOIN knowledge_nodes kn ON kn.id = ke.source_id
|
|
116
|
+
WHERE ke.target_id = <node_id>;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
For a complete picture, run both queries and present all connections.
|
|
120
|
+
|
|
121
|
+
### Full-Text Search
|
|
122
|
+
|
|
123
|
+
Search across both the `name` and `note` fields of knowledge nodes.
|
|
124
|
+
|
|
125
|
+
```sql
|
|
126
|
+
SELECT id, name, note, created_at
|
|
127
|
+
FROM knowledge_nodes
|
|
128
|
+
WHERE name ILIKE '%' || 'search term' || '%'
|
|
129
|
+
OR note ILIKE '%' || 'search term' || '%'
|
|
130
|
+
ORDER BY updated_at DESC
|
|
131
|
+
LIMIT 10;
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Update a Node
|
|
135
|
+
|
|
136
|
+
When the user wants to add or change information about an existing entity.
|
|
137
|
+
|
|
138
|
+
```sql
|
|
139
|
+
UPDATE knowledge_nodes
|
|
140
|
+
SET note = 'Updated information here',
|
|
141
|
+
updated_at = NOW()
|
|
142
|
+
WHERE id = <node_id>;
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Post-Create: Queue for Embedding
|
|
146
|
+
|
|
147
|
+
After successfully creating a knowledge node, attempt to queue it for embedding generation.
|
|
148
|
+
|
|
149
|
+
```sql
|
|
150
|
+
INSERT INTO embeddings (entity_type, entity_id)
|
|
151
|
+
VALUES ('node', <new_node_id>)
|
|
152
|
+
ON CONFLICT DO NOTHING;
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Important:** The `embeddings` table only exists when the embeddings engine is enabled. If this query fails because the table does not exist, silently ignore the error and proceed normally. The knowledge node is already saved -- embedding is optional. Do not report the embedding queue failure to the user.
|
|
156
|
+
|
|
157
|
+
## Restrictions and Notes
|
|
158
|
+
|
|
159
|
+
- Always use `mcp__pg__query` for database operations. Do not use any other tool.
|
|
160
|
+
- Edges are directed: "Alice works at Acme" means Alice is the source and Acme is the target. Choose direction to reflect the natural reading of the relationship.
|
|
161
|
+
- Before creating nodes, always check for existing nodes with similar names to prevent duplicates.
|
|
162
|
+
- When presenting graph data to the user, format it clearly -- list relationships in a readable way rather than dumping raw SQL results.
|
|
163
|
+
- If the user describes multiple entities and relationships in one message, process them all (create nodes first, then edges).
|
|
164
|
+
- Never delete nodes or edges unless the user explicitly requests it. Deleting a node cascades to all its edges.
|
|
165
|
+
- When searching, if no exact match is found, try broader partial matches and present the closest results.
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Skill: project-manage
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Manage projects, specifications, and issues. Use this skill to create projects, add tasks and issues, write specifications, track progress, mark items complete, and get project status summaries. Supports hierarchical sub-tasks and nested specifications.
|
|
6
|
+
|
|
7
|
+
## When to Activate
|
|
8
|
+
|
|
9
|
+
Activate this skill when any of the following conditions are met:
|
|
10
|
+
|
|
11
|
+
- The user's message begins with `/project`
|
|
12
|
+
- The user mentions tasks, projects, or work items (e.g., "I need to do X", "add a task for Y", "create a project for Z")
|
|
13
|
+
- The user asks about project status (e.g., "what's the status of project Z", "what tasks are open", "what's left to do")
|
|
14
|
+
- The user wants to track issues, bugs, or blockers
|
|
15
|
+
- The user wants to document a specification, requirement, or architecture decision
|
|
16
|
+
- The user marks something as done or complete
|
|
17
|
+
|
|
18
|
+
## Available Tools
|
|
19
|
+
|
|
20
|
+
- `mcp__pg__query` -- Execute SQL queries against the PostgreSQL database.
|
|
21
|
+
|
|
22
|
+
No other tools are permitted for this skill.
|
|
23
|
+
|
|
24
|
+
## Database Tables
|
|
25
|
+
|
|
26
|
+
### `projects`
|
|
27
|
+
|
|
28
|
+
| Column | Type | Description |
|
|
29
|
+
|--------|------|-------------|
|
|
30
|
+
| `id` | SERIAL PRIMARY KEY | Auto-incrementing identifier |
|
|
31
|
+
| `created_at` | TIMESTAMPTZ | Timestamp of creation |
|
|
32
|
+
| `updated_at` | TIMESTAMPTZ | Timestamp of last update |
|
|
33
|
+
| `name` | TEXT NOT NULL | Name of the project |
|
|
34
|
+
|
|
35
|
+
### `issues`
|
|
36
|
+
|
|
37
|
+
| Column | Type | Description |
|
|
38
|
+
|--------|------|-------------|
|
|
39
|
+
| `id` | SERIAL PRIMARY KEY | Auto-incrementing identifier |
|
|
40
|
+
| `created_at` | TIMESTAMPTZ | Timestamp of creation |
|
|
41
|
+
| `updated_at` | TIMESTAMPTZ | Timestamp of last update |
|
|
42
|
+
| `project_id` | INTEGER | FK to `projects.id` (nullable -- issues can exist without a project) |
|
|
43
|
+
| `parent_id` | INTEGER | FK to `issues.id` (nullable -- for sub-task hierarchy) |
|
|
44
|
+
| `note` | TEXT NOT NULL | Description of the issue or task |
|
|
45
|
+
| `completed` | BOOLEAN NOT NULL DEFAULT FALSE | Whether the issue is resolved |
|
|
46
|
+
|
|
47
|
+
### `specifications`
|
|
48
|
+
|
|
49
|
+
| Column | Type | Description |
|
|
50
|
+
|--------|------|-------------|
|
|
51
|
+
| `id` | SERIAL PRIMARY KEY | Auto-incrementing identifier |
|
|
52
|
+
| `created_at` | TIMESTAMPTZ | Timestamp of creation |
|
|
53
|
+
| `updated_at` | TIMESTAMPTZ | Timestamp of last update |
|
|
54
|
+
| `project_id` | INTEGER NOT NULL | FK to `projects.id` |
|
|
55
|
+
| `parent_id` | INTEGER | FK to `specifications.id` (nullable -- for nested specs) |
|
|
56
|
+
| `note` | TEXT NOT NULL | Specification content |
|
|
57
|
+
|
|
58
|
+
## Operations
|
|
59
|
+
|
|
60
|
+
### Create a Project
|
|
61
|
+
|
|
62
|
+
When the user wants to start tracking a new project.
|
|
63
|
+
|
|
64
|
+
```sql
|
|
65
|
+
INSERT INTO projects (name)
|
|
66
|
+
VALUES ('My New Project')
|
|
67
|
+
RETURNING id, name, created_at;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Before creating, check if a project with the same name already exists:
|
|
71
|
+
|
|
72
|
+
```sql
|
|
73
|
+
SELECT id, name FROM projects WHERE name ILIKE 'My New Project';
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Create an Issue or Task
|
|
77
|
+
|
|
78
|
+
When the user wants to add a task, bug, blocker, or to-do item.
|
|
79
|
+
|
|
80
|
+
```sql
|
|
81
|
+
INSERT INTO issues (project_id, note)
|
|
82
|
+
VALUES (<project_id>, 'Description of the task')
|
|
83
|
+
RETURNING id, note, created_at;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
If the user does not specify a project, either ask which project it belongs to, or create the issue without a project (`project_id = NULL`).
|
|
87
|
+
|
|
88
|
+
### Create a Sub-Task
|
|
89
|
+
|
|
90
|
+
Issues support hierarchical nesting via `parent_id`. When the user wants to break a task into smaller pieces.
|
|
91
|
+
|
|
92
|
+
```sql
|
|
93
|
+
INSERT INTO issues (project_id, parent_id, note)
|
|
94
|
+
VALUES (<project_id>, <parent_issue_id>, 'Sub-task description')
|
|
95
|
+
RETURNING id, note, created_at;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Create a Specification
|
|
99
|
+
|
|
100
|
+
When the user wants to document a requirement, architecture decision, or design note for a project.
|
|
101
|
+
|
|
102
|
+
```sql
|
|
103
|
+
INSERT INTO specifications (project_id, note)
|
|
104
|
+
VALUES (<project_id>, 'Specification content here')
|
|
105
|
+
RETURNING id, note, created_at;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Specifications also support nesting via `parent_id` for hierarchical documentation:
|
|
109
|
+
|
|
110
|
+
```sql
|
|
111
|
+
INSERT INTO specifications (project_id, parent_id, note)
|
|
112
|
+
VALUES (<project_id>, <parent_spec_id>, 'Child specification detail')
|
|
113
|
+
RETURNING id, note, created_at;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Mark an Issue as Complete
|
|
117
|
+
|
|
118
|
+
When the user says something is done, finished, or complete.
|
|
119
|
+
|
|
120
|
+
```sql
|
|
121
|
+
UPDATE issues
|
|
122
|
+
SET completed = TRUE, updated_at = NOW()
|
|
123
|
+
WHERE id = <issue_id>;
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
To reopen an issue:
|
|
127
|
+
|
|
128
|
+
```sql
|
|
129
|
+
UPDATE issues
|
|
130
|
+
SET completed = FALSE, updated_at = NOW()
|
|
131
|
+
WHERE id = <issue_id>;
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Project Status Summary
|
|
135
|
+
|
|
136
|
+
When the user asks for an overview of a project or all projects. Show counts of open and closed issues.
|
|
137
|
+
|
|
138
|
+
For a single project:
|
|
139
|
+
|
|
140
|
+
```sql
|
|
141
|
+
SELECT
|
|
142
|
+
p.name AS project,
|
|
143
|
+
COUNT(i.id) FILTER (WHERE NOT i.completed) AS open_issues,
|
|
144
|
+
COUNT(i.id) FILTER (WHERE i.completed) AS closed_issues,
|
|
145
|
+
COUNT(i.id) AS total_issues
|
|
146
|
+
FROM projects p
|
|
147
|
+
LEFT JOIN issues i ON i.project_id = p.id
|
|
148
|
+
WHERE p.id = <project_id>
|
|
149
|
+
GROUP BY p.id, p.name;
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
For all projects:
|
|
153
|
+
|
|
154
|
+
```sql
|
|
155
|
+
SELECT
|
|
156
|
+
p.name AS project,
|
|
157
|
+
COUNT(i.id) FILTER (WHERE NOT i.completed) AS open_issues,
|
|
158
|
+
COUNT(i.id) FILTER (WHERE i.completed) AS closed_issues,
|
|
159
|
+
COUNT(i.id) AS total_issues
|
|
160
|
+
FROM projects p
|
|
161
|
+
LEFT JOIN issues i ON i.project_id = p.id
|
|
162
|
+
GROUP BY p.id, p.name
|
|
163
|
+
ORDER BY p.name;
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### List Open Issues for a Project
|
|
167
|
+
|
|
168
|
+
```sql
|
|
169
|
+
SELECT id, note, created_at, parent_id
|
|
170
|
+
FROM issues
|
|
171
|
+
WHERE project_id = <project_id>
|
|
172
|
+
AND completed = FALSE
|
|
173
|
+
ORDER BY created_at ASC;
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### View Sub-Task Hierarchy
|
|
177
|
+
|
|
178
|
+
To display an issue with its sub-tasks:
|
|
179
|
+
|
|
180
|
+
```sql
|
|
181
|
+
SELECT id, note, completed, parent_id, created_at
|
|
182
|
+
FROM issues
|
|
183
|
+
WHERE parent_id = <parent_issue_id>
|
|
184
|
+
ORDER BY created_at ASC;
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### List Specifications for a Project
|
|
188
|
+
|
|
189
|
+
```sql
|
|
190
|
+
SELECT id, note, parent_id, created_at
|
|
191
|
+
FROM specifications
|
|
192
|
+
WHERE project_id = <project_id>
|
|
193
|
+
ORDER BY created_at ASC;
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Search Across Issues
|
|
197
|
+
|
|
198
|
+
```sql
|
|
199
|
+
SELECT i.id, i.note, i.completed, p.name AS project
|
|
200
|
+
FROM issues i
|
|
201
|
+
LEFT JOIN projects p ON p.id = i.project_id
|
|
202
|
+
WHERE i.note ILIKE '%' || 'search term' || '%'
|
|
203
|
+
ORDER BY i.created_at DESC
|
|
204
|
+
LIMIT 10;
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Restrictions and Notes
|
|
208
|
+
|
|
209
|
+
- Always use `mcp__pg__query` for database operations. Do not use any other tool.
|
|
210
|
+
- When the user mentions a project by name, look it up first. If multiple projects match, list them and ask the user to clarify.
|
|
211
|
+
- When marking items complete, confirm which specific issue is being completed. If the user says "mark it done" ambiguously, ask which task they mean.
|
|
212
|
+
- Present project status in a clear, concise format. Use counts and lists, not raw SQL output.
|
|
213
|
+
- Issues can exist without a project (`project_id = NULL`). These are standalone tasks.
|
|
214
|
+
- When showing sub-tasks, indicate the hierarchy clearly (e.g., indent or label parent/child relationships).
|
|
215
|
+
- Never delete projects, issues, or specifications unless the user explicitly asks for deletion.
|
|
216
|
+
- When a project has many issues, summarize by status (open vs. closed) rather than listing every single one, unless the user asks for a full list.
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Skill: recall
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Search across all personal data using natural language. This skill provides unified search over journal entries, knowledge graph nodes, project issues, specifications, and conversation history. It uses semantic vector search when embeddings are available, and falls back to text-based search otherwise.
|
|
6
|
+
|
|
7
|
+
## When to Activate
|
|
8
|
+
|
|
9
|
+
Activate this skill automatically when the user expresses search or recall intent. Common triggers include:
|
|
10
|
+
|
|
11
|
+
- "Find..." or "Search for..."
|
|
12
|
+
- "Remember when..." or "What was that thing about..."
|
|
13
|
+
- "What did I say about..."
|
|
14
|
+
- "Do I have anything on..."
|
|
15
|
+
- "Look up..." or "Can you find..."
|
|
16
|
+
- Any question that requires searching across the user's stored data
|
|
17
|
+
|
|
18
|
+
This skill does not require a slash command prefix. It activates whenever the user appears to be searching their personal data.
|
|
19
|
+
|
|
20
|
+
## Available Tools
|
|
21
|
+
|
|
22
|
+
- `mcp__pg__query` -- Execute SQL queries against the PostgreSQL database.
|
|
23
|
+
- `embed_query` -- Convert a text string into a vector embedding for semantic search. Accepts `{ text: "search query" }` and returns `{ vector: [...], dimensions: N }`.
|
|
24
|
+
|
|
25
|
+
## Database Tables
|
|
26
|
+
|
|
27
|
+
### `embedding_config` (read-only, check availability)
|
|
28
|
+
|
|
29
|
+
| Column | Type | Description |
|
|
30
|
+
|--------|------|-------------|
|
|
31
|
+
| `id` | INTEGER | Always 1 (single-row table) |
|
|
32
|
+
| `provider` | TEXT | Embedding provider name |
|
|
33
|
+
| `model` | TEXT | Embedding model name |
|
|
34
|
+
| `dimensions` | INTEGER | Vector dimensions |
|
|
35
|
+
|
|
36
|
+
### `embeddings` (for semantic search)
|
|
37
|
+
|
|
38
|
+
| Column | Type | Description |
|
|
39
|
+
|--------|------|-------------|
|
|
40
|
+
| `entity_type` | TEXT | Type: `'journal'`, `'node'`, `'issue'`, `'message'`, etc. |
|
|
41
|
+
| `entity_id` | INTEGER | ID in the source entity table |
|
|
42
|
+
| `vector` | VECTOR | Embedding vector |
|
|
43
|
+
|
|
44
|
+
### Entity Tables (joined for full content)
|
|
45
|
+
|
|
46
|
+
- `journal` -- `id`, `note`, `created_at`
|
|
47
|
+
- `knowledge_nodes` -- `id`, `name`, `note`, `created_at`
|
|
48
|
+
- `issues` -- `id`, `note`, `completed`, `project_id`, `created_at`
|
|
49
|
+
- `specifications` -- `id`, `note`, `project_id`, `created_at`
|
|
50
|
+
- `conversation_messages` -- `id`, `content`, `role`, `created_at`
|
|
51
|
+
|
|
52
|
+
## Operations
|
|
53
|
+
|
|
54
|
+
### Step 1: Check Semantic Search Availability
|
|
55
|
+
|
|
56
|
+
Before attempting semantic search, check whether the embeddings engine is configured.
|
|
57
|
+
|
|
58
|
+
```sql
|
|
59
|
+
SELECT dimensions FROM embedding_config WHERE id = 1;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- If this query **succeeds and returns a row**, semantic search is available. Proceed to Step 2a.
|
|
63
|
+
- If this query **fails** (table does not exist) or **returns no rows**, semantic search is unavailable. Skip to Step 2b (text fallback).
|
|
64
|
+
|
|
65
|
+
### Step 2a: Semantic Search (preferred)
|
|
66
|
+
|
|
67
|
+
When semantic search is available, use the `embed_query` tool to vectorize the user's search query, then find the nearest matches.
|
|
68
|
+
|
|
69
|
+
1. Call `embed_query` with the user's search text:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
embed_query({ text: "the user's search query" })
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This returns `{ vector: [0.123, ...], dimensions: 1536 }`.
|
|
76
|
+
|
|
77
|
+
2. If `embed_query` succeeds, search for nearest neighbors using cosine distance:
|
|
78
|
+
|
|
79
|
+
```sql
|
|
80
|
+
SELECT entity_type, entity_id,
|
|
81
|
+
1 - (vector <=> '<vector_string>'::vector) AS similarity
|
|
82
|
+
FROM embeddings
|
|
83
|
+
WHERE vector IS NOT NULL
|
|
84
|
+
ORDER BY vector <=> '<vector_string>'::vector
|
|
85
|
+
LIMIT 10;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Replace `<vector_string>` with the vector array returned by `embed_query`, formatted as a string (e.g., `'[0.123, 0.456, ...]'`).
|
|
89
|
+
|
|
90
|
+
3. Join the results back to their source tables to retrieve the full content. Use the `entity_type` to determine which table to join:
|
|
91
|
+
|
|
92
|
+
For journal entries (`entity_type = 'journal'`):
|
|
93
|
+
```sql
|
|
94
|
+
SELECT j.id, j.note, j.created_at
|
|
95
|
+
FROM journal j WHERE j.id = <entity_id>;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
For knowledge nodes (`entity_type = 'node'`):
|
|
99
|
+
```sql
|
|
100
|
+
SELECT kn.id, kn.name, kn.note, kn.created_at
|
|
101
|
+
FROM knowledge_nodes kn WHERE kn.id = <entity_id>;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
For issues (`entity_type = 'issue'`):
|
|
105
|
+
```sql
|
|
106
|
+
SELECT i.id, i.note, i.completed, p.name AS project_name, i.created_at
|
|
107
|
+
FROM issues i
|
|
108
|
+
LEFT JOIN projects p ON p.id = i.project_id
|
|
109
|
+
WHERE i.id = <entity_id>;
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For specifications (`entity_type = 'spec'`):
|
|
113
|
+
```sql
|
|
114
|
+
SELECT s.id, s.note, p.name AS project_name, s.created_at
|
|
115
|
+
FROM specifications s
|
|
116
|
+
LEFT JOIN projects p ON p.id = s.project_id
|
|
117
|
+
WHERE s.id = <entity_id>;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
4. If `embed_query` fails (returns an error), fall through to Step 2b.
|
|
121
|
+
|
|
122
|
+
### Step 2b: Text Fallback Search
|
|
123
|
+
|
|
124
|
+
When semantic search is unavailable or `embed_query` fails, search across all entity tables using case-insensitive text matching.
|
|
125
|
+
|
|
126
|
+
**Important:** When using this fallback, inform the user: "Using text search (semantic search is not available)."
|
|
127
|
+
|
|
128
|
+
Search each table independently and combine results:
|
|
129
|
+
|
|
130
|
+
```sql
|
|
131
|
+
-- Journal entries
|
|
132
|
+
SELECT 'journal' AS source, id, note AS content, created_at
|
|
133
|
+
FROM journal
|
|
134
|
+
WHERE note ILIKE '%' || 'search term' || '%'
|
|
135
|
+
ORDER BY created_at DESC
|
|
136
|
+
LIMIT 5;
|
|
137
|
+
|
|
138
|
+
-- Knowledge nodes
|
|
139
|
+
SELECT 'knowledge' AS source, id, name || ': ' || COALESCE(note, '') AS content, created_at
|
|
140
|
+
FROM knowledge_nodes
|
|
141
|
+
WHERE name ILIKE '%' || 'search term' || '%'
|
|
142
|
+
OR note ILIKE '%' || 'search term' || '%'
|
|
143
|
+
ORDER BY created_at DESC
|
|
144
|
+
LIMIT 5;
|
|
145
|
+
|
|
146
|
+
-- Issues
|
|
147
|
+
SELECT 'issue' AS source, id, note AS content, created_at
|
|
148
|
+
FROM issues
|
|
149
|
+
WHERE note ILIKE '%' || 'search term' || '%'
|
|
150
|
+
ORDER BY created_at DESC
|
|
151
|
+
LIMIT 5;
|
|
152
|
+
|
|
153
|
+
-- Specifications
|
|
154
|
+
SELECT 'spec' AS source, id, note AS content, created_at
|
|
155
|
+
FROM specifications
|
|
156
|
+
WHERE note ILIKE '%' || 'search term' || '%'
|
|
157
|
+
ORDER BY created_at DESC
|
|
158
|
+
LIMIT 5;
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Step 3: Source Attribution
|
|
162
|
+
|
|
163
|
+
Always present results with clear attribution to their source. The user should know where each result came from.
|
|
164
|
+
|
|
165
|
+
Format examples:
|
|
166
|
+
- "From your journal on Jan 15: ..."
|
|
167
|
+
- "From your knowledge graph -- Alice: works at Acme Corp"
|
|
168
|
+
- "From project X, issue #3: ..."
|
|
169
|
+
- "From a specification in project Y: ..."
|
|
170
|
+
|
|
171
|
+
Include dates and context to help the user identify the result they are looking for.
|
|
172
|
+
|
|
173
|
+
## Restrictions and Notes
|
|
174
|
+
|
|
175
|
+
- Always try semantic search first when available. It produces better results for natural language queries.
|
|
176
|
+
- If semantic search is unavailable, always use the text fallback. Never tell the user that search is impossible.
|
|
177
|
+
- Always attribute results to their source table and include timestamps.
|
|
178
|
+
- If no results are found across any table, say so clearly and suggest alternative search terms or phrasing.
|
|
179
|
+
- Do not modify any data during recall operations. This skill is read-only -- it only searches.
|
|
180
|
+
- When presenting multiple results, rank them by relevance (similarity score for semantic, or recency for text fallback).
|
|
181
|
+
- If the user's query seems to target a specific data type (e.g., "my journal entries about X"), you may search only the relevant table rather than all tables.
|
|
182
|
+
- Keep the result presentation concise. Summarize long entries rather than dumping full content, unless the user asks for detail.
|