memonaut 0.1.0 → 0.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-main.d.ts +12 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli-main.js +185 -25
- package/dist/cli-main.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +13 -0
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/model.d.ts +23 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/regex.d.ts +75 -0
- package/dist/regex.d.ts.map +1 -0
- package/dist/regex.js +242 -0
- package/dist/regex.js.map +1 -0
- package/dist/resources.d.ts +2 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +25 -0
- package/dist/resources.js.map +1 -0
- package/dist/ripgrep.d.ts +52 -0
- package/dist/ripgrep.d.ts.map +1 -0
- package/dist/ripgrep.js +217 -0
- package/dist/ripgrep.js.map +1 -0
- package/dist/search.d.ts +22 -1
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +47 -23
- package/dist/search.js.map +1 -1
- package/dist/skills.d.ts +34 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +59 -0
- package/dist/skills.js.map +1 -0
- package/package.json +4 -3
- package/skills/memonaut/SKILL.md +120 -0
- package/src/cli-main.ts +230 -30
- package/src/cli.ts +1 -1
- package/src/format.ts +12 -0
- package/src/index.ts +10 -0
- package/src/model.ts +23 -1
- package/src/regex.ts +378 -0
- package/src/resources.ts +26 -0
- package/src/ripgrep.ts +263 -0
- package/src/search.ts +68 -26
- package/src/skills.ts +86 -0
package/src/search.ts
CHANGED
|
@@ -70,6 +70,40 @@ export function quoteQuery(text: string): string {
|
|
|
70
70
|
return quoted.join(' ');
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Which transcripts a query is allowed to see.
|
|
75
|
+
*
|
|
76
|
+
* Visibility is decided per THREAD, because a lineage can in principle span
|
|
77
|
+
* working directories; the lineage set is only a prefilter. Both the FTS and
|
|
78
|
+
* the regex path go through here, so `private` and the cwd/project filters
|
|
79
|
+
* cannot drift apart between them.
|
|
80
|
+
*/
|
|
81
|
+
export function visibleSets(
|
|
82
|
+
files: Map<number, FileRecord>,
|
|
83
|
+
query: Pick<SearchQuery, 'cwd' | 'project' | 'includePrivate'>,
|
|
84
|
+
): {files: Set<number>; lineages: Set<number>; totalLineages: Set<number>} {
|
|
85
|
+
const cwdMatch = query.cwd && query.cwd.length ? matcher(query.cwd) : null;
|
|
86
|
+
const projectMatch =
|
|
87
|
+
query.project && query.project.length ? new Set(query.project) : null;
|
|
88
|
+
const visibleFiles = new Set<number>();
|
|
89
|
+
const visibleLineages = new Set<number>();
|
|
90
|
+
const totalLineages = new Set<number>();
|
|
91
|
+
for (const file of files.values()) {
|
|
92
|
+
if (file.lineage_id !== null) totalLineages.add(Number(file.lineage_id));
|
|
93
|
+
if (!query.includePrivate && file.private) continue;
|
|
94
|
+
if (cwdMatch && !cwdMatch(file.cwd)) continue;
|
|
95
|
+
if (projectMatch && !(file.project && projectMatch.has(file.project)))
|
|
96
|
+
continue;
|
|
97
|
+
visibleFiles.add(Number(file.id));
|
|
98
|
+
if (file.lineage_id !== null) visibleLineages.add(Number(file.lineage_id));
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
files: visibleFiles,
|
|
102
|
+
lineages: visibleLineages,
|
|
103
|
+
totalLineages,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
73
107
|
function inList(
|
|
74
108
|
column: string,
|
|
75
109
|
values: string[] | undefined,
|
|
@@ -109,25 +143,11 @@ export function search(
|
|
|
109
143
|
const threadLimit = query.threadLimit ?? 3;
|
|
110
144
|
const files = loadFiles(db);
|
|
111
145
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const visibleFiles = new Set<number>();
|
|
118
|
-
const visibleLineages = new Set<number>();
|
|
119
|
-
for (const file of files.values()) {
|
|
120
|
-
if (!query.includePrivate && file.private) continue;
|
|
121
|
-
if (cwdMatch && !cwdMatch(file.cwd)) continue;
|
|
122
|
-
if (projectMatch && !(file.project && projectMatch.has(file.project)))
|
|
123
|
-
continue;
|
|
124
|
-
visibleFiles.add(Number(file.id));
|
|
125
|
-
if (file.lineage_id !== null) visibleLineages.add(Number(file.lineage_id));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const totalLineages = new Set<number>();
|
|
129
|
-
for (const file of files.values())
|
|
130
|
-
if (file.lineage_id !== null) totalLineages.add(Number(file.lineage_id));
|
|
146
|
+
const {
|
|
147
|
+
files: visibleFiles,
|
|
148
|
+
lineages: visibleLineages,
|
|
149
|
+
totalLineages,
|
|
150
|
+
} = visibleSets(files, query);
|
|
131
151
|
|
|
132
152
|
const clauses: string[] = [];
|
|
133
153
|
const args: unknown[] = [];
|
|
@@ -259,16 +279,20 @@ export function search(
|
|
|
259
279
|
};
|
|
260
280
|
}
|
|
261
281
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Fan one entry back out to every visible thread that carries it.
|
|
284
|
+
*
|
|
285
|
+
* Shared by the FTS and the regex path, because they must produce identical
|
|
286
|
+
* thread lists: an entry is stored once and `membership` is what turns it back
|
|
287
|
+
* into "the twelve threads that inherited this".
|
|
288
|
+
*/
|
|
289
|
+
export function collectThreads(
|
|
290
|
+
entryId: number,
|
|
266
291
|
memberStmt: ReturnType<DB['prepare']>,
|
|
267
292
|
files: Map<number, FileRecord>,
|
|
268
293
|
visibleFiles: Set<number>,
|
|
269
|
-
|
|
270
|
-
)
|
|
271
|
-
const members = memberStmt.all(row.entry_id) as unknown as Array<{
|
|
294
|
+
): ThreadRef[] {
|
|
295
|
+
const members = memberStmt.all(entryId) as unknown as Array<{
|
|
272
296
|
file_id: number;
|
|
273
297
|
seq: number;
|
|
274
298
|
}>;
|
|
@@ -297,6 +321,24 @@ function buildHit(
|
|
|
297
321
|
threads.sort((a, b) =>
|
|
298
322
|
(b.lastActivity ?? '').localeCompare(a.lastActivity ?? ''),
|
|
299
323
|
);
|
|
324
|
+
return threads;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function buildHit(
|
|
328
|
+
row: RawHit,
|
|
329
|
+
kind: ChunkKind,
|
|
330
|
+
score: number,
|
|
331
|
+
memberStmt: ReturnType<DB['prepare']>,
|
|
332
|
+
files: Map<number, FileRecord>,
|
|
333
|
+
visibleFiles: Set<number>,
|
|
334
|
+
threadLimit: number,
|
|
335
|
+
): SearchHit | null {
|
|
336
|
+
const threads = collectThreads(
|
|
337
|
+
Number(row.entry_id),
|
|
338
|
+
memberStmt,
|
|
339
|
+
files,
|
|
340
|
+
visibleFiles,
|
|
341
|
+
);
|
|
300
342
|
if (threads.length === 0) return null;
|
|
301
343
|
|
|
302
344
|
return {
|
package/src/skills.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {resolvePackageResource} from './resources.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Installing the agent skill that ships with this package.
|
|
8
|
+
*
|
|
9
|
+
* pi loads the skill itself from `memonaut-pi` (its `pi.skills` entry), so this command exists for
|
|
10
|
+
* every other agent: the skill is the same file, and `~/.agents/skills/<name>` is where agents that
|
|
11
|
+
* are not pi look for it. One directory, no fan-out to twenty agent-specific locations, and the
|
|
12
|
+
* report names every path written.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export interface Skill {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
from: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Scope = 'user' | 'project';
|
|
22
|
+
|
|
23
|
+
/** Where skills live, by convention. `project` keeps them beside the code that needs them. */
|
|
24
|
+
export function destinationFor(scope: Scope, cwd = process.cwd()): string {
|
|
25
|
+
return scope === 'user'
|
|
26
|
+
? path.join(os.homedir(), '.agents', 'skills')
|
|
27
|
+
: path.join(cwd, '.agents', 'skills');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function skillsSource(): string | undefined {
|
|
31
|
+
return resolvePackageResource('skills/');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Name and description come from the SKILL.md front matter, so there is one source of truth. */
|
|
35
|
+
export function availableSkills(): Skill[] {
|
|
36
|
+
const root = skillsSource();
|
|
37
|
+
if (!root) return [];
|
|
38
|
+
const skills: Skill[] = [];
|
|
39
|
+
for (const entry of fs.readdirSync(root, {withFileTypes: true})) {
|
|
40
|
+
if (!entry.isDirectory()) continue;
|
|
41
|
+
const manifest = path.join(root, entry.name, 'SKILL.md');
|
|
42
|
+
if (!fs.existsSync(manifest)) continue;
|
|
43
|
+
const text = fs.readFileSync(manifest, 'utf8');
|
|
44
|
+
skills.push({
|
|
45
|
+
name: /^name:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? entry.name,
|
|
46
|
+
description: /^description:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? '',
|
|
47
|
+
from: path.join(root, entry.name),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return skills.sort((a, b) => a.name.localeCompare(b.name));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface Installed {
|
|
54
|
+
name: string;
|
|
55
|
+
to: string;
|
|
56
|
+
replaced: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Copies rather than symlinks, so an installed skill survives `node_modules` being deleted.
|
|
61
|
+
*
|
|
62
|
+
* The cost is that upgrading the package does not upgrade the installed skill, which is why the
|
|
63
|
+
* report says `replaced` versus `installed`: a skill that is quietly stale is worse than one you
|
|
64
|
+
* know you have to refresh.
|
|
65
|
+
*/
|
|
66
|
+
export function installSkills(scope: Scope, cwd = process.cwd()): Installed[] {
|
|
67
|
+
const destination = destinationFor(scope, cwd);
|
|
68
|
+
const installed: Installed[] = [];
|
|
69
|
+
for (const skill of availableSkills()) {
|
|
70
|
+
const to = path.join(destination, skill.name);
|
|
71
|
+
const replaced = fs.existsSync(to);
|
|
72
|
+
if (replaced) fs.rmSync(to, {recursive: true, force: true});
|
|
73
|
+
fs.mkdirSync(destination, {recursive: true});
|
|
74
|
+
fs.cpSync(skill.from, to, {recursive: true});
|
|
75
|
+
installed.push({name: skill.name, to, replaced});
|
|
76
|
+
}
|
|
77
|
+
return installed;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function isInstalled(
|
|
81
|
+
skill: Skill,
|
|
82
|
+
scope: Scope,
|
|
83
|
+
cwd = process.cwd(),
|
|
84
|
+
): boolean {
|
|
85
|
+
return fs.existsSync(path.join(destinationFor(scope, cwd), skill.name));
|
|
86
|
+
}
|