@goondocks/myco 0.2.14 → 0.3.1

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 (101) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/commands/init.md +33 -22
  4. package/dist/{chunk-BXFS4PCJ.js → chunk-2QEJKG7R.js} +2 -2
  5. package/dist/{chunk-MAFUTKOZ.js → chunk-2TKJPRZL.js} +2 -2
  6. package/dist/chunk-3JCXYLHD.js +33 -0
  7. package/dist/chunk-3JCXYLHD.js.map +1 -0
  8. package/dist/chunk-5EZ7QF6J.js +96 -0
  9. package/dist/chunk-5EZ7QF6J.js.map +1 -0
  10. package/dist/chunk-6FQISQNA.js +61 -0
  11. package/dist/chunk-6FQISQNA.js.map +1 -0
  12. package/dist/chunk-72OAG4SF.js +3692 -0
  13. package/dist/chunk-72OAG4SF.js.map +1 -0
  14. package/dist/{chunk-S4WBXXO6.js → chunk-BMJX2IDQ.js} +2 -2
  15. package/dist/chunk-EF4JVH24.js +7299 -0
  16. package/dist/chunk-EF4JVH24.js.map +1 -0
  17. package/dist/{chunk-YXZEP5U6.js → chunk-ISCT2SI6.js} +11 -7301
  18. package/dist/chunk-ISCT2SI6.js.map +1 -0
  19. package/dist/chunk-JIQISBPI.js +362 -0
  20. package/dist/chunk-JIQISBPI.js.map +1 -0
  21. package/dist/{chunk-6C26YFOA.js → chunk-N6IAW33G.js} +248 -4306
  22. package/dist/chunk-N6IAW33G.js.map +1 -0
  23. package/dist/{chunk-C2YPBQQM.js → chunk-NTYYYC32.js} +3 -3
  24. package/dist/{chunk-NKJIZSPD.js → chunk-P2Q77C5F.js} +3 -3
  25. package/dist/chunk-PAUPHPOC.js +111 -0
  26. package/dist/chunk-PAUPHPOC.js.map +1 -0
  27. package/dist/chunk-PZUWP5VK.js +44 -0
  28. package/dist/{chunk-O5VSPHDL.js → chunk-Q7BEFSOV.js} +3 -40
  29. package/dist/{chunk-O5VSPHDL.js.map → chunk-Q7BEFSOV.js.map} +1 -1
  30. package/dist/chunk-RGVBGTD6.js +21 -0
  31. package/dist/chunk-RGVBGTD6.js.map +1 -0
  32. package/dist/chunk-SAKJMNSR.js +50 -0
  33. package/dist/chunk-SAKJMNSR.js.map +1 -0
  34. package/dist/{chunk-UUFDD2FB.js → chunk-TJJRIVZ7.js} +2 -2
  35. package/dist/chunk-XQXXF6MU.js +96 -0
  36. package/dist/chunk-XQXXF6MU.js.map +1 -0
  37. package/dist/chunk-XW3OL55U.js +160 -0
  38. package/dist/chunk-XW3OL55U.js.map +1 -0
  39. package/dist/cli-ERAS5H43.js +79 -0
  40. package/dist/cli-ERAS5H43.js.map +1 -0
  41. package/dist/client-HORA3CC4.js +11 -0
  42. package/dist/client-HORA3CC4.js.map +1 -0
  43. package/dist/config-MD4XMLUS.js +101 -0
  44. package/dist/config-MD4XMLUS.js.map +1 -0
  45. package/dist/detect-providers-6RQCQZOI.js +35 -0
  46. package/dist/detect-providers-6RQCQZOI.js.map +1 -0
  47. package/dist/init-LLLHUNSY.js +120 -0
  48. package/dist/init-LLLHUNSY.js.map +1 -0
  49. package/dist/logs-BSTBZHDR.js +84 -0
  50. package/dist/logs-BSTBZHDR.js.map +1 -0
  51. package/dist/{main-UJAXPP6S.js → main-D4X6XWRT.js} +241 -542
  52. package/dist/main-D4X6XWRT.js.map +1 -0
  53. package/dist/rebuild-3367GP5R.js +85 -0
  54. package/dist/rebuild-3367GP5R.js.map +1 -0
  55. package/dist/reprocess-EM5RIRH4.js +199 -0
  56. package/dist/reprocess-EM5RIRH4.js.map +1 -0
  57. package/dist/restart-NH5MX45I.js +50 -0
  58. package/dist/restart-NH5MX45I.js.map +1 -0
  59. package/dist/search-W3ECVSTH.js +120 -0
  60. package/dist/search-W3ECVSTH.js.map +1 -0
  61. package/dist/{server-J3AQ3YFA.js → server-I7MRMIOP.js} +41 -21
  62. package/dist/{server-J3AQ3YFA.js.map → server-I7MRMIOP.js.map} +1 -1
  63. package/dist/session-5GI2YU6R.js +44 -0
  64. package/dist/session-5GI2YU6R.js.map +1 -0
  65. package/dist/{session-start-BEC4JMNZ.js → session-start-DECLNJDI.js} +8 -6
  66. package/dist/{session-start-BEC4JMNZ.js.map → session-start-DECLNJDI.js.map} +1 -1
  67. package/dist/src/cli.js +5 -4
  68. package/dist/src/cli.js.map +1 -1
  69. package/dist/src/daemon/main.js +5 -4
  70. package/dist/src/daemon/main.js.map +1 -1
  71. package/dist/src/hooks/post-tool-use.js +5 -4
  72. package/dist/src/hooks/post-tool-use.js.map +1 -1
  73. package/dist/src/hooks/session-end.js +5 -4
  74. package/dist/src/hooks/session-end.js.map +1 -1
  75. package/dist/src/hooks/session-start.js +5 -4
  76. package/dist/src/hooks/session-start.js.map +1 -1
  77. package/dist/src/hooks/stop.js +7 -5
  78. package/dist/src/hooks/stop.js.map +1 -1
  79. package/dist/src/hooks/user-prompt-submit.js +5 -4
  80. package/dist/src/hooks/user-prompt-submit.js.map +1 -1
  81. package/dist/src/mcp/server.js +5 -4
  82. package/dist/src/mcp/server.js.map +1 -1
  83. package/dist/stats-7VEZN2WF.js +77 -0
  84. package/dist/stats-7VEZN2WF.js.map +1 -0
  85. package/dist/verify-HN5DWV2H.js +50 -0
  86. package/dist/verify-HN5DWV2H.js.map +1 -0
  87. package/package.json +1 -1
  88. package/skills/myco/SKILL.md +33 -0
  89. package/dist/chunk-6C26YFOA.js.map +0 -1
  90. package/dist/chunk-YXZEP5U6.js.map +0 -1
  91. package/dist/cli-KMWJFK5Y.js +0 -623
  92. package/dist/cli-KMWJFK5Y.js.map +0 -1
  93. package/dist/client-TEUHXGOY.js +0 -10
  94. package/dist/main-UJAXPP6S.js.map +0 -1
  95. /package/dist/{chunk-BXFS4PCJ.js.map → chunk-2QEJKG7R.js.map} +0 -0
  96. /package/dist/{chunk-MAFUTKOZ.js.map → chunk-2TKJPRZL.js.map} +0 -0
  97. /package/dist/{chunk-S4WBXXO6.js.map → chunk-BMJX2IDQ.js.map} +0 -0
  98. /package/dist/{chunk-C2YPBQQM.js.map → chunk-NTYYYC32.js.map} +0 -0
  99. /package/dist/{chunk-NKJIZSPD.js.map → chunk-P2Q77C5F.js.map} +0 -0
  100. /package/dist/{client-TEUHXGOY.js.map → chunk-PZUWP5VK.js.map} +0 -0
  101. /package/dist/{chunk-UUFDD2FB.js.map → chunk-TJJRIVZ7.js.map} +0 -0
@@ -12,7 +12,7 @@
12
12
  "source": {
13
13
  "source": "npm",
14
14
  "package": "@goondocks/myco",
15
- "version": "0.2.13"
15
+ "version": "0.3.0"
16
16
  },
17
17
  "description": "Collective agent intelligence — captures session knowledge and serves it back via MCP",
18
18
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myco",
3
- "version": "0.2.14",
3
+ "version": "0.3.1",
4
4
  "description": "Collective agent intelligence — captures session knowledge and serves it back to your team via MCP",
5
5
  "author": {
6
6
  "name": "goondocks-co",
package/commands/init.md CHANGED
@@ -5,11 +5,21 @@ description: Initialize Myco in the current project — sets up vault, config, a
5
5
 
6
6
  # Initialize Myco
7
7
 
8
- Guide the user through setup, then run the CLI to create the vault. **Do NOT create files manually — the CLI handles all vault creation, config writing, and env configuration.**
8
+ Guide the user through setup using the composable CLI commands. **Do NOT create files manually — the CLI handles all vault creation, config writing, and env configuration.**
9
9
 
10
10
  **Ask each question one at a time using AskUserQuestion with selectable options.** Wait for the user's answer before proceeding to the next question. Do NOT combine multiple questions into one message.
11
11
 
12
- ## Step 1: Choose vault location
12
+ ## Step 1: Detect available providers
13
+
14
+ Run the provider detection command to see what's available:
15
+
16
+ ```bash
17
+ node ${CLAUDE_PLUGIN_ROOT}/dist/src/cli.js detect-providers
18
+ ```
19
+
20
+ Parse the JSON output. This tells you which providers are running and what models are available.
21
+
22
+ ## Step 2: Choose vault location
13
23
 
14
24
  Ask the user:
15
25
 
@@ -22,32 +32,26 @@ Ask the user:
22
32
 
23
33
  If the user picks "Custom path", ask them to type the path.
24
34
 
25
- ## Step 2: Choose LLM provider
26
-
27
- First, detect available providers by checking local endpoints:
35
+ ## Step 3: Choose LLM provider
28
36
 
29
- - **Ollama** `curl -s http://localhost:11434/api/tags` list model names
30
- - **LM Studio** — `curl -s http://localhost:1234/v1/models` — list model IDs
31
- - **Anthropic** — check if `ANTHROPIC_API_KEY` is set
32
-
33
- Then ask the user:
37
+ Using the detected providers from Step 1, ask the user:
34
38
 
35
39
  **Question:** "Which LLM provider for summarization?"
36
40
 
37
- **Options:** List only providers that are actually running, with recommended models noted. Example:
41
+ **Options:** List only providers where `available` is `true`, with recommended models. Example:
38
42
  - "Ollama — gpt-oss (recommended)"
39
43
  - "LM Studio — openai/gpt-oss-20b"
40
44
  - "Anthropic"
41
45
 
42
- After the user picks a provider, ask them to choose a specific model from the available models on that provider.
46
+ After the user picks a provider, ask them to choose a specific model from that provider's model list (from the detect-providers output).
43
47
 
44
- ## Step 3: Choose embedding provider
48
+ ## Step 4: Choose embedding provider
45
49
 
46
50
  Ask the user:
47
51
 
48
52
  **Question:** "Which embedding provider?"
49
53
 
50
- **Options:** List only providers that are running and support embeddings (Anthropic does not). Example:
54
+ **Options:** List only providers where `available` is `true` and that support embeddings (Anthropic does not). Example:
51
55
  - "Ollama — bge-m3 (recommended)"
52
56
  - "LM Studio — text-embedding-bge-m3"
53
57
 
@@ -56,9 +60,9 @@ After the user picks a provider, ask them to choose a specific embedding model.
56
60
  If the recommended embedding model isn't available, offer to pull it:
57
61
  - **Ollama**: `ollama pull bge-m3`
58
62
 
59
- ## Step 4: Run the CLI
63
+ ## Step 5: Run init with all gathered inputs
60
64
 
61
- Run the init command with all gathered inputs. The CLI creates the vault, writes config, sets up the FTS index, and configures `MYCO_VAULT_DIR` if the vault is external:
65
+ Pass everything to the init command in a single call:
62
66
 
63
67
  ```bash
64
68
  node ${CLAUDE_PLUGIN_ROOT}/dist/src/cli.js init \
@@ -71,17 +75,24 @@ node ${CLAUDE_PLUGIN_ROOT}/dist/src/cli.js init \
71
75
  --embedding-url <base-url>
72
76
  ```
73
77
 
74
- ## Step 5: Verify
78
+ The CLI creates the vault structure, writes myco.yaml, .gitignore, _dashboard.md, initializes the FTS index, and configures MYCO_VAULT_DIR if the vault is external.
79
+
80
+ ## Step 6: Verify connectivity
81
+
82
+ Run the verify command to confirm providers are reachable:
83
+
84
+ ```bash
85
+ node ${CLAUDE_PLUGIN_ROOT}/dist/src/cli.js verify
86
+ ```
87
+
88
+ If verification fails, help the user troubleshoot (check if the provider is running, model is loaded, etc.).
75
89
 
76
- After the CLI completes, confirm providers are reachable:
90
+ ## Step 7: Display summary
77
91
 
78
- 1. Test the LLM — send a short prompt and verify a response
79
- 2. Test embeddings — generate a test embedding and report dimensions
80
- 3. Display a setup summary table
92
+ Show the user a setup summary table:
81
93
 
82
94
  | Setting | Value |
83
95
  |---------|-------|
84
96
  | Vault path | `<resolved path>` |
85
97
  | LLM provider | `<provider>` / `<model>` |
86
98
  | Embedding provider | `<provider>` / `<model>` |
87
- | Context window | `<context_window>` |
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  PROMPT_PREVIEW_CHARS
4
- } from "./chunk-O5VSPHDL.js";
4
+ } from "./chunk-Q7BEFSOV.js";
5
5
 
6
6
  // src/agents/adapter.ts
7
7
  import fs from "fs";
@@ -351,4 +351,4 @@ export {
351
351
  claudeCodeAdapter,
352
352
  AgentRegistry
353
353
  };
354
- //# sourceMappingURL=chunk-BXFS4PCJ.js.map
354
+ //# sourceMappingURL=chunk-2QEJKG7R.js.map
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
2
  import {
3
3
  STDIN_TIMEOUT_MS
4
- } from "./chunk-O5VSPHDL.js";
4
+ } from "./chunk-Q7BEFSOV.js";
5
5
 
6
6
  // src/hooks/read-stdin.ts
7
7
  function readStdin() {
@@ -18,4 +18,4 @@ function readStdin() {
18
18
  export {
19
19
  readStdin
20
20
  };
21
- //# sourceMappingURL=chunk-MAFUTKOZ.js.map
21
+ //# sourceMappingURL=chunk-2TKJPRZL.js.map
@@ -0,0 +1,33 @@
1
+ import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
+
3
+ // src/intelligence/batch.ts
4
+ var LLM_BATCH_CONCURRENCY = 3;
5
+ var EMBEDDING_BATCH_CONCURRENCY = 4;
6
+ async function batchExecute(items, fn, options) {
7
+ const { concurrency, onProgress } = options;
8
+ let succeeded = 0;
9
+ let failed = 0;
10
+ const results = [];
11
+ for (let i = 0; i < items.length; i += concurrency) {
12
+ const batch = items.slice(i, i + concurrency);
13
+ const settled = await Promise.allSettled(batch.map(fn));
14
+ for (const result of settled) {
15
+ if (result.status === "fulfilled") {
16
+ succeeded++;
17
+ results.push({ status: "fulfilled", value: result.value });
18
+ } else {
19
+ failed++;
20
+ results.push({ status: "rejected", reason: result.reason?.message ?? String(result.reason) });
21
+ }
22
+ }
23
+ onProgress?.(succeeded + failed, items.length);
24
+ }
25
+ return { succeeded, failed, results };
26
+ }
27
+
28
+ export {
29
+ LLM_BATCH_CONCURRENCY,
30
+ EMBEDDING_BATCH_CONCURRENCY,
31
+ batchExecute
32
+ };
33
+ //# sourceMappingURL=chunk-3JCXYLHD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/intelligence/batch.ts"],"sourcesContent":["/**\n * Batch execution utilities for LLM and embedding operations.\n *\n * Provides concurrency-limited parallel execution for bulk operations\n * like reprocessing, rebuilding, and any future batch pipeline.\n */\n\n/** Default concurrency for LLM calls (heavier, single-threaded backends). */\nexport const LLM_BATCH_CONCURRENCY = 3;\n/** Default concurrency for embedding calls (lighter, can run more in parallel). */\nexport const EMBEDDING_BATCH_CONCURRENCY = 4;\n\nexport interface BatchResult<T> {\n succeeded: number;\n failed: number;\n results: Array<{ status: 'fulfilled'; value: T } | { status: 'rejected'; reason: string }>;\n}\n\n/**\n * Execute async tasks with a concurrency limit.\n * Reports progress via an optional callback.\n */\nexport async function batchExecute<I, O>(\n items: I[],\n fn: (item: I) => Promise<O>,\n options: {\n concurrency: number;\n onProgress?: (completed: number, total: number) => void;\n },\n): Promise<BatchResult<O>> {\n const { concurrency, onProgress } = options;\n let succeeded = 0;\n let failed = 0;\n const results: BatchResult<O>['results'] = [];\n\n for (let i = 0; i < items.length; i += concurrency) {\n const batch = items.slice(i, i + concurrency);\n const settled = await Promise.allSettled(batch.map(fn));\n\n for (const result of settled) {\n if (result.status === 'fulfilled') {\n succeeded++;\n results.push({ status: 'fulfilled', value: result.value });\n } else {\n failed++;\n results.push({ status: 'rejected', reason: (result.reason as Error)?.message ?? String(result.reason) });\n }\n }\n\n onProgress?.(succeeded + failed, items.length);\n }\n\n return { succeeded, failed, results };\n}\n"],"mappings":";;;AAQO,IAAM,wBAAwB;AAE9B,IAAM,8BAA8B;AAY3C,eAAsB,aACpB,OACA,IACA,SAIyB;AACzB,QAAM,EAAE,aAAa,WAAW,IAAI;AACpC,MAAI,YAAY;AAChB,MAAI,SAAS;AACb,QAAM,UAAqC,CAAC;AAE5C,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,aAAa;AAClD,UAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,WAAW;AAC5C,UAAM,UAAU,MAAM,QAAQ,WAAW,MAAM,IAAI,EAAE,CAAC;AAEtD,eAAW,UAAU,SAAS;AAC5B,UAAI,OAAO,WAAW,aAAa;AACjC;AACA,gBAAQ,KAAK,EAAE,QAAQ,aAAa,OAAO,OAAO,MAAM,CAAC;AAAA,MAC3D,OAAO;AACL;AACA,gBAAQ,KAAK,EAAE,QAAQ,YAAY,QAAS,OAAO,QAAkB,WAAW,OAAO,OAAO,MAAM,EAAE,CAAC;AAAA,MACzG;AAAA,IACF;AAEA,iBAAa,YAAY,QAAQ,MAAM,MAAM;AAAA,EAC/C;AAEA,SAAO,EAAE,WAAW,QAAQ,QAAQ;AACtC;","names":[]}
@@ -0,0 +1,96 @@
1
+ import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
+
3
+ // src/daemon/logger.ts
4
+ import fs from "fs";
5
+ import path from "path";
6
+ var LEVEL_ORDER = {
7
+ debug: 0,
8
+ info: 1,
9
+ warn: 2,
10
+ error: 3
11
+ };
12
+ var DaemonLogger = class {
13
+ logPath;
14
+ fd = null;
15
+ currentSize = 0;
16
+ level;
17
+ maxSize;
18
+ maxFiles;
19
+ logDir;
20
+ constructor(logDir, options = {}) {
21
+ this.logDir = logDir;
22
+ this.logPath = path.join(logDir, "daemon.log");
23
+ this.level = options.level ?? "info";
24
+ this.maxSize = options.maxSize ?? 5242880;
25
+ this.maxFiles = options.maxFiles ?? 3;
26
+ fs.mkdirSync(logDir, { recursive: true });
27
+ this.fd = fs.openSync(this.logPath, "a");
28
+ try {
29
+ this.currentSize = fs.fstatSync(this.fd).size;
30
+ } catch {
31
+ this.currentSize = 0;
32
+ }
33
+ }
34
+ debug(component, message, data) {
35
+ this.write("debug", component, message, data);
36
+ }
37
+ info(component, message, data) {
38
+ this.write("info", component, message, data);
39
+ }
40
+ warn(component, message, data) {
41
+ this.write("warn", component, message, data);
42
+ }
43
+ error(component, message, data) {
44
+ this.write("error", component, message, data);
45
+ }
46
+ close() {
47
+ if (this.fd !== null) {
48
+ fs.closeSync(this.fd);
49
+ this.fd = null;
50
+ }
51
+ }
52
+ write(level, component, message, data) {
53
+ if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) return;
54
+ const entry = {
55
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
56
+ level,
57
+ component,
58
+ message,
59
+ ...data
60
+ };
61
+ const line = JSON.stringify(entry) + "\n";
62
+ const bytes = Buffer.byteLength(line);
63
+ if (this.currentSize + bytes > this.maxSize) {
64
+ this.rotate();
65
+ }
66
+ if (this.fd !== null) {
67
+ fs.writeSync(this.fd, line);
68
+ this.currentSize += bytes;
69
+ }
70
+ }
71
+ rotate() {
72
+ this.close();
73
+ for (let i = this.maxFiles - 1; i >= 1; i--) {
74
+ const from = path.join(this.logDir, `daemon.${i}.log`);
75
+ const to = path.join(this.logDir, `daemon.${i + 1}.log`);
76
+ if (fs.existsSync(from)) {
77
+ if (i + 1 > this.maxFiles) {
78
+ fs.unlinkSync(from);
79
+ } else {
80
+ fs.renameSync(from, to);
81
+ }
82
+ }
83
+ }
84
+ if (fs.existsSync(this.logPath)) {
85
+ fs.renameSync(this.logPath, path.join(this.logDir, "daemon.1.log"));
86
+ }
87
+ this.fd = fs.openSync(this.logPath, "a");
88
+ this.currentSize = 0;
89
+ }
90
+ };
91
+
92
+ export {
93
+ LEVEL_ORDER,
94
+ DaemonLogger
95
+ };
96
+ //# sourceMappingURL=chunk-5EZ7QF6J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/daemon/logger.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nexport interface LogEntry {\n timestamp: string;\n level: string;\n component: string;\n message: string;\n [key: string]: unknown;\n}\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nexport const LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0, info: 1, warn: 2, error: 3,\n};\n\ninterface LoggerOptions {\n level?: LogLevel;\n maxSize?: number;\n maxFiles?: number;\n}\n\nexport class DaemonLogger {\n private logPath: string;\n private fd: number | null = null;\n private currentSize = 0;\n private level: LogLevel;\n private maxSize: number;\n private maxFiles: number;\n private logDir: string;\n\n constructor(logDir: string, options: LoggerOptions = {}) {\n this.logDir = logDir;\n this.logPath = path.join(logDir, 'daemon.log');\n this.level = options.level ?? 'info';\n this.maxSize = options.maxSize ?? 5_242_880;\n this.maxFiles = options.maxFiles ?? 3;\n\n fs.mkdirSync(logDir, { recursive: true });\n this.fd = fs.openSync(this.logPath, 'a');\n try {\n this.currentSize = fs.fstatSync(this.fd).size;\n } catch {\n this.currentSize = 0;\n }\n }\n\n debug(component: string, message: string, data?: Record<string, unknown>): void {\n this.write('debug', component, message, data);\n }\n\n info(component: string, message: string, data?: Record<string, unknown>): void {\n this.write('info', component, message, data);\n }\n\n warn(component: string, message: string, data?: Record<string, unknown>): void {\n this.write('warn', component, message, data);\n }\n\n error(component: string, message: string, data?: Record<string, unknown>): void {\n this.write('error', component, message, data);\n }\n\n close(): void {\n if (this.fd !== null) {\n fs.closeSync(this.fd);\n this.fd = null;\n }\n }\n\n private write(level: LogLevel, component: string, message: string, data?: Record<string, unknown>): void {\n if (LEVEL_ORDER[level] < LEVEL_ORDER[this.level]) return;\n\n const entry: LogEntry = {\n timestamp: new Date().toISOString(),\n level,\n component,\n message,\n ...data,\n };\n\n const line = JSON.stringify(entry) + '\\n';\n const bytes = Buffer.byteLength(line);\n\n if (this.currentSize + bytes > this.maxSize) {\n this.rotate();\n }\n\n if (this.fd !== null) {\n fs.writeSync(this.fd, line);\n this.currentSize += bytes;\n }\n }\n\n private rotate(): void {\n this.close();\n\n for (let i = this.maxFiles - 1; i >= 1; i--) {\n const from = path.join(this.logDir, `daemon.${i}.log`);\n const to = path.join(this.logDir, `daemon.${i + 1}.log`);\n if (fs.existsSync(from)) {\n if (i + 1 > this.maxFiles) {\n fs.unlinkSync(from);\n } else {\n fs.renameSync(from, to);\n }\n }\n }\n\n if (fs.existsSync(this.logPath)) {\n fs.renameSync(this.logPath, path.join(this.logDir, 'daemon.1.log'));\n }\n\n this.fd = fs.openSync(this.logPath, 'a');\n this.currentSize = 0;\n }\n}\n"],"mappings":";;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAYV,IAAM,cAAwC;AAAA,EACnD,OAAO;AAAA,EAAG,MAAM;AAAA,EAAG,MAAM;AAAA,EAAG,OAAO;AACrC;AAQO,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EACA,KAAoB;AAAA,EACpB,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAAgB,UAAyB,CAAC,GAAG;AACvD,SAAK,SAAS;AACd,SAAK,UAAU,KAAK,KAAK,QAAQ,YAAY;AAC7C,SAAK,QAAQ,QAAQ,SAAS;AAC9B,SAAK,UAAU,QAAQ,WAAW;AAClC,SAAK,WAAW,QAAQ,YAAY;AAEpC,OAAG,UAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AACxC,SAAK,KAAK,GAAG,SAAS,KAAK,SAAS,GAAG;AACvC,QAAI;AACF,WAAK,cAAc,GAAG,UAAU,KAAK,EAAE,EAAE;AAAA,IAC3C,QAAQ;AACN,WAAK,cAAc;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAM,WAAmB,SAAiB,MAAsC;AAC9E,SAAK,MAAM,SAAS,WAAW,SAAS,IAAI;AAAA,EAC9C;AAAA,EAEA,KAAK,WAAmB,SAAiB,MAAsC;AAC7E,SAAK,MAAM,QAAQ,WAAW,SAAS,IAAI;AAAA,EAC7C;AAAA,EAEA,KAAK,WAAmB,SAAiB,MAAsC;AAC7E,SAAK,MAAM,QAAQ,WAAW,SAAS,IAAI;AAAA,EAC7C;AAAA,EAEA,MAAM,WAAmB,SAAiB,MAAsC;AAC9E,SAAK,MAAM,SAAS,WAAW,SAAS,IAAI;AAAA,EAC9C;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,OAAO,MAAM;AACpB,SAAG,UAAU,KAAK,EAAE;AACpB,WAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAAA,EAEQ,MAAM,OAAiB,WAAmB,SAAiB,MAAsC;AACvG,QAAI,YAAY,KAAK,IAAI,YAAY,KAAK,KAAK,EAAG;AAElD,UAAM,QAAkB;AAAA,MACtB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAG;AAAA,IACL;AAEA,UAAM,OAAO,KAAK,UAAU,KAAK,IAAI;AACrC,UAAM,QAAQ,OAAO,WAAW,IAAI;AAEpC,QAAI,KAAK,cAAc,QAAQ,KAAK,SAAS;AAC3C,WAAK,OAAO;AAAA,IACd;AAEA,QAAI,KAAK,OAAO,MAAM;AACpB,SAAG,UAAU,KAAK,IAAI,IAAI;AAC1B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA,EAEQ,SAAe;AACrB,SAAK,MAAM;AAEX,aAAS,IAAI,KAAK,WAAW,GAAG,KAAK,GAAG,KAAK;AAC3C,YAAM,OAAO,KAAK,KAAK,KAAK,QAAQ,UAAU,CAAC,MAAM;AACrD,YAAM,KAAK,KAAK,KAAK,KAAK,QAAQ,UAAU,IAAI,CAAC,MAAM;AACvD,UAAI,GAAG,WAAW,IAAI,GAAG;AACvB,YAAI,IAAI,IAAI,KAAK,UAAU;AACzB,aAAG,WAAW,IAAI;AAAA,QACpB,OAAO;AACL,aAAG,WAAW,MAAM,EAAE;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAEA,QAAI,GAAG,WAAW,KAAK,OAAO,GAAG;AAC/B,SAAG,WAAW,KAAK,SAAS,KAAK,KAAK,KAAK,QAAQ,cAAc,CAAC;AAAA,IACpE;AAEA,SAAK,KAAK,GAAG,SAAS,KAAK,SAAS,GAAG;AACvC,SAAK,cAAc;AAAA,EACrB;AACF;","names":[]}
@@ -0,0 +1,61 @@
1
+ import { createRequire as __cr } from 'node:module'; const require = __cr(import.meta.url);
2
+
3
+ // src/index/fts.ts
4
+ function initFts(index) {
5
+ const db = index.getDb();
6
+ db.exec(`
7
+ CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(
8
+ path UNINDEXED,
9
+ type UNINDEXED,
10
+ id UNINDEXED,
11
+ title,
12
+ content,
13
+ content='notes',
14
+ content_rowid='rowid'
15
+ );
16
+
17
+ CREATE TRIGGER IF NOT EXISTS notes_ai AFTER INSERT ON notes BEGIN
18
+ INSERT INTO notes_fts(rowid, path, type, id, title, content)
19
+ VALUES (new.rowid, new.path, new.type, new.id, new.title, new.content);
20
+ END;
21
+
22
+ CREATE TRIGGER IF NOT EXISTS notes_ad AFTER DELETE ON notes BEGIN
23
+ INSERT INTO notes_fts(notes_fts, rowid, path, type, id, title, content)
24
+ VALUES ('delete', old.rowid, old.path, old.type, old.id, old.title, old.content);
25
+ END;
26
+
27
+ CREATE TRIGGER IF NOT EXISTS notes_au AFTER UPDATE ON notes BEGIN
28
+ INSERT INTO notes_fts(notes_fts, rowid, path, type, id, title, content)
29
+ VALUES ('delete', old.rowid, old.path, old.type, old.id, old.title, old.content);
30
+ INSERT INTO notes_fts(rowid, path, type, id, title, content)
31
+ VALUES (new.rowid, new.path, new.type, new.id, new.title, new.content);
32
+ END;
33
+ `);
34
+ }
35
+ function searchFts(index, query, options = {}) {
36
+ const db = index.getDb();
37
+ const limit = options.limit ?? 20;
38
+ const params = [query];
39
+ let typeFilter = "";
40
+ if (options.type) {
41
+ typeFilter = " AND type = ?";
42
+ params.push(options.type);
43
+ }
44
+ params.push(limit);
45
+ const sql = `
46
+ SELECT path, type, id, title,
47
+ snippet(notes_fts, 4, '<mark>', '</mark>', '...', 40) AS snippet,
48
+ rank
49
+ FROM notes_fts
50
+ WHERE notes_fts MATCH ?${typeFilter}
51
+ ORDER BY rank
52
+ LIMIT ?
53
+ `;
54
+ return db.prepare(sql).all(...params);
55
+ }
56
+
57
+ export {
58
+ initFts,
59
+ searchFts
60
+ };
61
+ //# sourceMappingURL=chunk-6FQISQNA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index/fts.ts"],"sourcesContent":["import type { MycoIndex } from './sqlite.js';\n\nexport interface FtsResult {\n path: string;\n type: string;\n id: string;\n title: string;\n snippet: string;\n rank: number;\n}\n\nexport interface FtsSearchOptions {\n type?: string;\n limit?: number;\n}\n\nexport function initFts(index: MycoIndex): void {\n const db = index.getDb();\n db.exec(`\n CREATE VIRTUAL TABLE IF NOT EXISTS notes_fts USING fts5(\n path UNINDEXED,\n type UNINDEXED,\n id UNINDEXED,\n title,\n content,\n content='notes',\n content_rowid='rowid'\n );\n\n CREATE TRIGGER IF NOT EXISTS notes_ai AFTER INSERT ON notes BEGIN\n INSERT INTO notes_fts(rowid, path, type, id, title, content)\n VALUES (new.rowid, new.path, new.type, new.id, new.title, new.content);\n END;\n\n CREATE TRIGGER IF NOT EXISTS notes_ad AFTER DELETE ON notes BEGIN\n INSERT INTO notes_fts(notes_fts, rowid, path, type, id, title, content)\n VALUES ('delete', old.rowid, old.path, old.type, old.id, old.title, old.content);\n END;\n\n CREATE TRIGGER IF NOT EXISTS notes_au AFTER UPDATE ON notes BEGIN\n INSERT INTO notes_fts(notes_fts, rowid, path, type, id, title, content)\n VALUES ('delete', old.rowid, old.path, old.type, old.id, old.title, old.content);\n INSERT INTO notes_fts(rowid, path, type, id, title, content)\n VALUES (new.rowid, new.path, new.type, new.id, new.title, new.content);\n END;\n `);\n}\n\nexport function searchFts(index: MycoIndex, query: string, options: FtsSearchOptions = {}): FtsResult[] {\n const db = index.getDb();\n const limit = options.limit ?? 20;\n const params: unknown[] = [query];\n\n let typeFilter = '';\n if (options.type) {\n typeFilter = ' AND type = ?';\n params.push(options.type);\n }\n params.push(limit);\n\n const sql = `\n SELECT path, type, id, title,\n snippet(notes_fts, 4, '<mark>', '</mark>', '...', 40) AS snippet,\n rank\n FROM notes_fts\n WHERE notes_fts MATCH ?${typeFilter}\n ORDER BY rank\n LIMIT ?\n `;\n\n return db.prepare(sql).all(...params) as FtsResult[];\n}\n"],"mappings":";;;AAgBO,SAAS,QAAQ,OAAwB;AAC9C,QAAM,KAAK,MAAM,MAAM;AACvB,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GA2BP;AACH;AAEO,SAAS,UAAU,OAAkB,OAAe,UAA4B,CAAC,GAAgB;AACtG,QAAM,KAAK,MAAM,MAAM;AACvB,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAoB,CAAC,KAAK;AAEhC,MAAI,aAAa;AACjB,MAAI,QAAQ,MAAM;AAChB,iBAAa;AACb,WAAO,KAAK,QAAQ,IAAI;AAAA,EAC1B;AACA,SAAO,KAAK,KAAK;AAEjB,QAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,6BAKe,UAAU;AAAA;AAAA;AAAA;AAKrC,SAAO,GAAG,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AACtC;","names":[]}