@koffeekat/diana 0.0.0-stage → 0.1.0-beta.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 (53) hide show
  1. package/.claude-plugin/marketplace.json +13 -0
  2. package/.claude-plugin/plugin.json +9 -0
  3. package/LICENSE.md +21 -0
  4. package/README.md +165 -2
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +75 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/index.d.ts +4 -0
  9. package/dist/commands/index.js +19 -0
  10. package/dist/commands/index.js.map +1 -0
  11. package/dist/commands/init.d.ts +4 -0
  12. package/dist/commands/init.js +36 -0
  13. package/dist/commands/init.js.map +1 -0
  14. package/dist/commands/read.d.ts +13 -0
  15. package/dist/commands/read.js +74 -0
  16. package/dist/commands/read.js.map +1 -0
  17. package/dist/commands/search.d.ts +13 -0
  18. package/dist/commands/search.js +52 -0
  19. package/dist/commands/search.js.map +1 -0
  20. package/dist/indexer.d.ts +16 -0
  21. package/dist/indexer.js +258 -0
  22. package/dist/indexer.js.map +1 -0
  23. package/dist/sources.d.ts +11 -0
  24. package/dist/sources.js +85 -0
  25. package/dist/sources.js.map +1 -0
  26. package/dist/templates/config.template.d.ts +2 -0
  27. package/dist/templates/config.template.js +5 -0
  28. package/dist/templates/config.template.js.map +1 -0
  29. package/dist/templates/gitignore.template.d.ts +1 -0
  30. package/dist/templates/gitignore.template.js +5 -0
  31. package/dist/templates/gitignore.template.js.map +1 -0
  32. package/dist/types/source.types.d.ts +4 -0
  33. package/dist/types/source.types.js +2 -0
  34. package/dist/types/source.types.js.map +1 -0
  35. package/dist/types/workspace.types.d.ts +27 -0
  36. package/dist/types/workspace.types.js +12 -0
  37. package/dist/types/workspace.types.js.map +1 -0
  38. package/dist/utils/forbiddenDirs.d.ts +1 -0
  39. package/dist/utils/forbiddenDirs.js +2 -0
  40. package/dist/utils/forbiddenDirs.js.map +1 -0
  41. package/dist/utils/hasErrorCode.d.ts +1 -0
  42. package/dist/utils/hasErrorCode.js +4 -0
  43. package/dist/utils/hasErrorCode.js.map +1 -0
  44. package/dist/utils/pathExists.d.ts +1 -0
  45. package/dist/utils/pathExists.js +15 -0
  46. package/dist/utils/pathExists.js.map +1 -0
  47. package/dist/workspace.d.ts +14 -0
  48. package/dist/workspace.js +70 -0
  49. package/dist/workspace.js.map +1 -0
  50. package/docs/security.md +32 -0
  51. package/img/diana-banner.webp +0 -0
  52. package/package.json +61 -4
  53. package/skills/diana/SKILL.md +23 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "koffeeapps-diana",
3
+ "description": "Diana's shared knowledge-retrieval skill for Claude Code.",
4
+ "owner": {
5
+ "name": "KoffeeKat"
6
+ },
7
+ "plugins": [
8
+ {
9
+ "name": "diana",
10
+ "source": "./"
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "diana",
3
+ "version": "0.1.0-beta.1",
4
+ "description": "Search local Markdown knowledge through the read-only Diana CLI.",
5
+ "author": {
6
+ "name": "KoffeeKat"
7
+ },
8
+ "repository": "https://github.com/K0ffeeKat/diana"
9
+ }
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 KoffeeKat
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,166 @@
1
- # Temporary Holding Version
1
+ <div>
2
+ <img alt="diana-banner" src="./img/diana-banner.webp" />
3
+ </div>
2
4
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
5
+ <div align="center">
6
+ <p>&#8202;</p>
7
+ <h1 align="center">diana</h1>
8
+ <h4 align="center">A lightweight local Markdown search layer for agents</h4>
9
+ <p>&#8202;</p>
10
+ </div>
11
+
12
+ ### Search for relevant sections and read only what you need.
13
+
14
+ Backed by SQLite FTS5 and a selective indexing engine, Diana helps agents navigate large document collections with precision. It aims to reduce token usage and keep the context window uncluttered.
15
+
16
+ <p>&#8202;</p>
17
+
18
+ ## Installation
19
+
20
+ Requires **Node.js >=22.13.0**.
21
+
22
+ ```sh
23
+ npm install -g @koffeekat/diana@beta
24
+ ```
25
+
26
+ <p>&#8202;</p>
27
+
28
+ ## Quick start
29
+
30
+ From your project root:
31
+
32
+ ```sh
33
+ diana init
34
+ ```
35
+
36
+ [Add a source](#adding-sources), then index it:
37
+
38
+ ```sh
39
+ diana index
40
+ ```
41
+
42
+ Run Diana from this same root. `diana index` refreshes every configured source.
43
+
44
+ > Quick hint: consider adding `diana index` as a pre-commit hook.
45
+
46
+ <p>&#8202;</p>
47
+
48
+ ## Other CLI commands
49
+
50
+ | Command | What it does |
51
+ | ----------------------------------------------------------- | ---------------------------------------------------------- |
52
+ | `diana search "token rotation"` | Find matching sections. Words are ANDed; results are JSON. |
53
+ | `diana read notes/architecture.md` | Read a note with limited output, returned as JSON. |
54
+ | `diana read notes/architecture.md --section token-rotation` | Read one section using its search-result anchor. |
55
+ | `diana --help` | Show available commands. |
56
+
57
+ Search-result `path` values are relative to `sourceRoot`; join both for an absolute read path. If a read returns `truncated: true`, request a narrower section where possible.
58
+
59
+ <p>&#8202;</p>
60
+
61
+ ## Adding sources
62
+
63
+ Edit `diana.config.json` to share project sources:
64
+
65
+ ```json
66
+ {
67
+ "version": 1,
68
+ "sources": [
69
+ {
70
+ "path": "notes",
71
+ "include": ["**/*.md"],
72
+ "exclude": ["drafts/**"]
73
+ }
74
+ ]
75
+ }
76
+ ```
77
+
78
+ > The directory must already exist. Exclude patterns take priority. Use `exclude: []` when nothing should be excluded.
79
+
80
+ - **Project config — `diana.config.json`:** commit it so teammates share the same setup. Sources must stay inside the project.
81
+ - **Local config — `.diana/local.config.json`:** same format, for personal sources, including directories outside the project. Use an absolute path for an external source directory; `~` isn't expanded. Relative paths in either config start at the project root.
82
+
83
+ For example, `.diana/local.config.json` can point to a personal source directory:
84
+
85
+ ```json
86
+ {
87
+ "version": 1,
88
+ "sources": [
89
+ {
90
+ "path": "/Users/you/Documents/personal-notes",
91
+ "include": ["**/*.md"],
92
+ "exclude": [],
93
+ "autoReindex": true
94
+ }
95
+ ]
96
+ }
97
+ ```
98
+
99
+ Replace the path with your source directory's absolute path, then run `diana index` once.
100
+
101
+ - `autoReindex` - refresh this source before each search. Useful for sources updated independently of your project. Defaults to false.
102
+
103
+ > 💡 Prefer pre-commit/post-merge hooks when commit-level freshness is enough. Use autoReindex when searches should also pick up uncommitted changes; it reads and hashes the source’s included files on every search.
104
+
105
+ **Keep the generated `.diana/.gitignore`.** It excludes your local config and index, helping prevent personal paths and cached note content from being committed. Git ignore rules aren't access controls; review both configs in unfamiliar projects. See [security and privacy](docs/security.md).
106
+
107
+ <p>&#8202;</p>
108
+
109
+ ## Installing skills
110
+
111
+ Skills teach agents to search first and read only relevant sections. Install the CLI above first; your harness manages skill installation.
112
+
113
+ **Pi**
114
+
115
+ ```sh
116
+ pi install npm:@koffeekat/diana@beta
117
+ ```
118
+
119
+ Reload with `/reload` if needed; invoke `/skill:diana`.
120
+
121
+ **Claude Code**
122
+
123
+ ```text
124
+ /plugin marketplace add K0ffeeKat/diana
125
+ /plugin install diana@koffeeapps-diana
126
+ ```
127
+
128
+ Invoke `/diana:diana`.
129
+
130
+ **Codex** — ask `$skill-installer`:
131
+
132
+ ```text
133
+ Install skills/diana from the GitHub repository K0ffeeKat/diana.
134
+ ```
135
+
136
+ Invoke `$diana` once available.
137
+
138
+ <p>&#8202;</p>
139
+
140
+ ## Using Diana with Obsidian
141
+
142
+ Your vault can live inside the project:
143
+
144
+ ```text
145
+ project/
146
+ ├── diana.config.json # source path: "notes"
147
+ ├── .diana/ # local config and index
148
+ └── notes/ # open this folder as an Obsidian vault
149
+ ├── .obsidian/ # skipped by Diana
150
+ └── architecture.md
151
+ ```
152
+
153
+ Keep editing in Obsidian as usual. Diana indexes the Markdown, skips hidden entries and symlinks, and leaves the vault unchanged. An external vault works through the local config instead.
154
+
155
+ <p>&#8202;</p>
156
+
157
+ ## Troubleshooting
158
+
159
+ - **Command not found:** check the npm installation and your PATH. Installing a skill alone doesn't install the CLI.
160
+ - **No configuration:** run from the project root, where you ran `diana init`.
161
+ - **No sources/index, outdated schema, or stale results:** add a source if needed, check its patterns, and run `diana index`. Automatic refresh still needs an initial, compatible index.
162
+ - **Source outside project:** put it in the local config, not the project config.
163
+ - **Section not found:** reindex for current anchors; omit `--section` for headingless notes or pre-heading text.
164
+ - **File too large:** split notes larger than 1 MiB; requesting a section doesn't bypass this limit.
165
+
166
+ Removed sources disappear from cached results on the next successful search, even if no sources remain. To discard the cache entirely, stop Diana commands and delete only `.diana/index.sqlite`; preserve your local config.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import { initializeWorkspace } from "./commands/init.js";
3
+ import { indexWorkspace } from "./commands/index.js";
4
+ import { readWorkspace } from "./commands/read.js";
5
+ import { searchWorkspace } from "./commands/search.js";
6
+ const [command, ...arguments_] = process.argv.slice(2);
7
+ const USAGE_HINT = `Usage:
8
+ diana <command>
9
+
10
+ Commands:
11
+ init
12
+ index
13
+ search <query...>
14
+ read <path> [--section <anchor>]
15
+ `;
16
+ try {
17
+ if ((command === "init" ||
18
+ command === "index" ||
19
+ command === "--help" ||
20
+ command === "-h") &&
21
+ arguments_.length > 0) {
22
+ throw new Error(`Usage: diana ${command}.`);
23
+ }
24
+ switch (command) {
25
+ case undefined:
26
+ case "--help":
27
+ case "-h":
28
+ process.stdout.write(USAGE_HINT);
29
+ break;
30
+ case "init":
31
+ await initializeWorkspace(process.cwd());
32
+ process.stdout.write('Initialized Diana workspace.\nAdd sources to diana.config.json or .diana/local.config.json, then run "diana index".\n');
33
+ break;
34
+ case "index":
35
+ await indexWorkspace(process.cwd());
36
+ process.stdout.write("Index updated.\n");
37
+ break;
38
+ case "search": {
39
+ const results = await searchWorkspace(process.cwd(), arguments_.join(" "));
40
+ process.stdout.write(`${JSON.stringify(results)}\n`);
41
+ break;
42
+ }
43
+ case "read": {
44
+ const { path, section } = parseReadArguments(arguments_);
45
+ const result = await readWorkspace(process.cwd(), path, section);
46
+ process.stdout.write(`${JSON.stringify(result)}\n`);
47
+ break;
48
+ }
49
+ default:
50
+ throw new Error(`Unknown command: ${command}\nRun "diana --help" for usage.`);
51
+ }
52
+ }
53
+ catch (error) {
54
+ const message = error instanceof Error ? error.message.trim() : "Unexpected error.";
55
+ process.stderr.write(`Error: ${message}\n`);
56
+ process.exitCode = 1;
57
+ }
58
+ function parseReadArguments(arguments_) {
59
+ const [path, ...options] = arguments_;
60
+ if (path === undefined) {
61
+ throw new Error("Read path must not be empty.");
62
+ }
63
+ if (options.length === 0) {
64
+ return { path };
65
+ }
66
+ const section = options[1];
67
+ if (options.length === 2 &&
68
+ options[0] === "--section" &&
69
+ section !== undefined &&
70
+ section !== "") {
71
+ return { path, section };
72
+ }
73
+ throw new Error("Usage: diana read <path> [--section <anchor>].");
74
+ }
75
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export declare const COMMAND_INDEX_ERRORS: {
2
+ readonly noSourcesConfiguredError: "No sources configured. Add a source to diana.config.json or .diana/local.config.json.";
3
+ };
4
+ export declare function indexWorkspace(workspaceRoot: string): Promise<void>;
@@ -0,0 +1,19 @@
1
+ import { join } from "node:path";
2
+ import { updateIndex } from "../indexer.js";
3
+ import { resolveSources } from "../sources.js";
4
+ import { WORKSPACE_CONSTANTS, loadWorkspace } from "../workspace.js";
5
+ const { databaseFileName, internalDirName } = WORKSPACE_CONSTANTS;
6
+ export const COMMAND_INDEX_ERRORS = {
7
+ noSourcesConfiguredError: "No sources configured. Add a source to diana.config.json or .diana/local.config.json.",
8
+ };
9
+ const { noSourcesConfiguredError } = COMMAND_INDEX_ERRORS;
10
+ export async function indexWorkspace(workspaceRoot) {
11
+ const workspace = await loadWorkspace(workspaceRoot);
12
+ if (workspace.sources.length === 0) {
13
+ throw new Error(noSourcesConfiguredError);
14
+ }
15
+ const sources = await resolveSources(workspace);
16
+ const databasePath = join(workspace.root, internalDirName, databaseFileName);
17
+ await updateIndex(databasePath, sources);
18
+ }
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErE,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,mBAAmB,CAAC;AAElE,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,wBAAwB,EACtB,uFAAuF;CACjF,CAAC;AAEX,MAAM,EAAE,wBAAwB,EAAE,GAAG,oBAAoB,CAAC;AAE1D,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,aAAqB;IACxD,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,CAAC;IAErD,IAAI,SAAS,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC;IAEhD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC;IAC7E,MAAM,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const COMMAND_INIT_ERRORS: {
2
+ readonly dianaWorkspaceExistsError: (workspaceRoot: string) => string;
3
+ };
4
+ export declare function initializeWorkspace(workspaceRoot: string): Promise<void>;
@@ -0,0 +1,36 @@
1
+ import { join } from "node:path";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import { WORKSPACE_CONSTANTS } from "../workspace.js";
4
+ import { configTemplate } from "../templates/config.template.js";
5
+ import { gitIgnoreTemplate } from "../templates/gitignore.template.js";
6
+ import { pathExists } from "../utils/pathExists.js";
7
+ const { configFileName, internalDirName, internalGitIgnoreName, localConfigFileName, } = WORKSPACE_CONSTANTS;
8
+ export const COMMAND_INIT_ERRORS = {
9
+ dianaWorkspaceExistsError: (workspaceRoot) => `\nDiana workspace already exists at ${workspaceRoot}.\nRemove ${configFileName} and ${internalDirName}/, then call <diana init> for re-initialization.\n`,
10
+ };
11
+ const { dianaWorkspaceExistsError } = COMMAND_INIT_ERRORS;
12
+ export async function initializeWorkspace(workspaceRoot) {
13
+ const pathToSharedConfig = join(workspaceRoot, configFileName);
14
+ const pathToInternalDir = join(workspaceRoot, internalDirName);
15
+ const pathToGitIgnore = join(pathToInternalDir, internalGitIgnoreName);
16
+ const pathToLocalConfig = join(pathToInternalDir, localConfigFileName);
17
+ const [sharedConfigExists, internalDirExists] = await Promise.all([
18
+ pathExists(pathToSharedConfig),
19
+ pathExists(pathToInternalDir),
20
+ ]);
21
+ if (sharedConfigExists || internalDirExists) {
22
+ throw new Error(dianaWorkspaceExistsError(workspaceRoot));
23
+ }
24
+ const sharedConfig = `${JSON.stringify(configTemplate, null, 2)}\n`;
25
+ await writeFile(pathToSharedConfig, sharedConfig, {
26
+ flag: "wx",
27
+ });
28
+ await mkdir(pathToInternalDir);
29
+ await writeFile(pathToGitIgnore, gitIgnoreTemplate, {
30
+ flag: "wx",
31
+ });
32
+ await writeFile(pathToLocalConfig, sharedConfig, {
33
+ flag: "wx",
34
+ });
35
+ }
36
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,MAAM,EACJ,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,mBAAmB,GACpB,GAAG,mBAAmB,CAAC;AAExB,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,yBAAyB,EAAE,CAAC,aAAqB,EAAE,EAAE,CACnD,uCAAuC,aAAa,aAAa,cAAc,QAAQ,eAAe,oDAAoD;CACpJ,CAAC;AAEX,MAAM,EAAE,yBAAyB,EAAE,GAAG,mBAAmB,CAAC;AAE1D,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,aAAqB;IAErB,MAAM,kBAAkB,GAAG,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IAC/D,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IAC/D,MAAM,eAAe,GAAG,IAAI,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;IACvE,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,EAAE,mBAAmB,CAAC,CAAC;IAEvE,MAAM,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAChE,UAAU,CAAC,kBAAkB,CAAC;QAC9B,UAAU,CAAC,iBAAiB,CAAC;KAC9B,CAAC,CAAC;IAEH,IAAI,kBAAkB,IAAI,iBAAiB,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,aAAa,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,YAAY,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC;IAEpE,MAAM,SAAS,CAAC,kBAAkB,EAAE,YAAY,EAAE;QAChD,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC/B,MAAM,SAAS,CAAC,eAAe,EAAE,iBAAiB,EAAE;QAClD,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;IACH,MAAM,SAAS,CAAC,iBAAiB,EAAE,YAAY,EAAE;QAC/C,IAAI,EAAE,IAAI;KACX,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const COMMAND_READ_ERRORS: {
2
+ readonly emptyPathError: "Read path must not be empty.";
3
+ readonly fileTooLargeError: (filePath: string) => string;
4
+ readonly invalidPathError: (filePath: string) => string;
5
+ readonly sectionNotFoundError: (anchor: string) => string;
6
+ };
7
+ export type ReadResult = {
8
+ content: string;
9
+ path: string;
10
+ sourceRoot: string;
11
+ truncated: boolean;
12
+ };
13
+ export declare function readWorkspace(workspaceRoot: string, requestedPath: string, section?: string): Promise<ReadResult>;
@@ -0,0 +1,74 @@
1
+ import { readFile, realpath, stat } from "node:fs/promises";
2
+ import { isAbsolute, posix, relative, resolve, sep } from "node:path";
3
+ import { extractMarkdownSection } from "../indexer.js";
4
+ import { resolveSources } from "../sources.js";
5
+ import { loadWorkspace } from "../workspace.js";
6
+ const MAX_FILE_BYTES = 1024 * 1024;
7
+ const MAX_OUTPUT_CHARACTERS = 64 * 1024;
8
+ const PROTECTED_DIRECTORY_NAMES = new Set([".diana", ".git"]);
9
+ export const COMMAND_READ_ERRORS = {
10
+ emptyPathError: "Read path must not be empty.",
11
+ fileTooLargeError: (filePath) => `Source file exceeds the ${MAX_FILE_BYTES}-byte read limit: ${filePath}.`,
12
+ invalidPathError: (filePath) => `Path is not a readable Markdown file in a configured source: ${filePath}.`,
13
+ sectionNotFoundError: (anchor) => `Section not found: ${anchor}.`,
14
+ };
15
+ const { emptyPathError, fileTooLargeError, invalidPathError, sectionNotFoundError, } = COMMAND_READ_ERRORS;
16
+ export async function readWorkspace(workspaceRoot, requestedPath, section) {
17
+ if (requestedPath.trim() === "") {
18
+ throw new Error(emptyPathError);
19
+ }
20
+ const workspace = await loadWorkspace(workspaceRoot);
21
+ const configuredPath = resolve(workspace.root, requestedPath);
22
+ const filePath = await realpath(configuredPath);
23
+ const source = findContainingSource(filePath, await resolveSources(workspace));
24
+ if (source === undefined || !isReadableSourceFile(filePath, source)) {
25
+ throw new Error(invalidPathError(configuredPath));
26
+ }
27
+ const fileStats = await stat(filePath);
28
+ if (!fileStats.isFile()) {
29
+ throw new Error(invalidPathError(configuredPath));
30
+ }
31
+ if (fileStats.size > MAX_FILE_BYTES) {
32
+ throw new Error(fileTooLargeError(filePath));
33
+ }
34
+ const markdown = await readFile(filePath, "utf8");
35
+ const content = section === undefined ? markdown : readSection(markdown, section);
36
+ return {
37
+ content: content.slice(0, MAX_OUTPUT_CHARACTERS),
38
+ path: toDocumentPath(source.sourceRoot, filePath),
39
+ sourceRoot: source.sourceRoot,
40
+ truncated: content.length > MAX_OUTPUT_CHARACTERS,
41
+ };
42
+ }
43
+ function readSection(markdown, section) {
44
+ const content = extractMarkdownSection(markdown, section);
45
+ if (content === undefined) {
46
+ throw new Error(sectionNotFoundError(section));
47
+ }
48
+ return content;
49
+ }
50
+ function findContainingSource(filePath, sources) {
51
+ return sources.find((source) => isPathInside(source.sourceRoot, filePath));
52
+ }
53
+ function isReadableSourceFile(filePath, source) {
54
+ const documentPath = toDocumentPath(source.sourceRoot, filePath);
55
+ const pathSegments = documentPath.split("/");
56
+ return (filePath.endsWith(".md") &&
57
+ !pathSegments.some(isProtectedOrHidden) &&
58
+ source.include.some((pattern) => posix.matchesGlob(documentPath, pattern)) &&
59
+ !source.exclude.some((pattern) => posix.matchesGlob(documentPath, pattern)));
60
+ }
61
+ function isProtectedOrHidden(pathSegment) {
62
+ return (pathSegment.startsWith(".") || PROTECTED_DIRECTORY_NAMES.has(pathSegment));
63
+ }
64
+ function isPathInside(parentPath, childPath) {
65
+ const relativePath = relative(parentPath, childPath);
66
+ return (relativePath === "" ||
67
+ (relativePath !== ".." &&
68
+ !relativePath.startsWith(`..${sep}`) &&
69
+ !isAbsolute(relativePath)));
70
+ }
71
+ function toDocumentPath(sourceRoot, filePath) {
72
+ return relative(sourceRoot, filePath).split(sep).join("/");
73
+ }
74
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/commands/read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,MAAM,cAAc,GAAG,IAAI,GAAG,IAAI,CAAC;AACnC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,CAAC;AACxC,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAE9D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,cAAc,EAAE,8BAA8B;IAC9C,iBAAiB,EAAE,CAAC,QAAgB,EAAE,EAAE,CACtC,2BAA2B,cAAc,qBAAqB,QAAQ,GAAG;IAC3E,gBAAgB,EAAE,CAAC,QAAgB,EAAE,EAAE,CACrC,gEAAgE,QAAQ,GAAG;IAC7E,oBAAoB,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,sBAAsB,MAAM,GAAG;CACjE,CAAC;AAEX,MAAM,EACJ,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,GACrB,GAAG,mBAAmB,CAAC;AASxB,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,aAAqB,EACrB,aAAqB,EACrB,OAAgB;IAEhB,IAAI,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,CAAC;IACrD,MAAM,cAAc,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,cAAc,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,oBAAoB,CACjC,QAAQ,EACR,MAAM,cAAc,CAAC,SAAS,CAAC,CAChC,CAAC;IAEF,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEvC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,SAAS,CAAC,IAAI,GAAG,cAAc,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,OAAO,GACX,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEpE,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC;QAChD,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC;QACjD,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,SAAS,EAAE,OAAO,CAAC,MAAM,GAAG,qBAAqB;KAClD,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB,EAAE,OAAe;IACpD,MAAM,OAAO,GAAG,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAE1D,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,oBAAoB,CAC3B,QAAgB,EAChB,OAAyB;IAEzB,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,SAAS,oBAAoB,CAC3B,QAAgB,EAChB,MAAsB;IAEtB,MAAM,YAAY,GAAG,cAAc,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACjE,MAAM,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE7C,OAAO,CACL,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC;QACxB,CAAC,YAAY,CAAC,IAAI,CAAC,mBAAmB,CAAC;QACvC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAC9B,KAAK,CAAC,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,CACzC;QACD,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAC5E,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,WAAmB;IAC9C,OAAO,CACL,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,yBAAyB,CAAC,GAAG,CAAC,WAAW,CAAC,CAC1E,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,UAAkB,EAAE,SAAiB;IACzD,MAAM,YAAY,GAAG,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAErD,OAAO,CACL,YAAY,KAAK,EAAE;QACnB,CAAC,YAAY,KAAK,IAAI;YACpB,CAAC,YAAY,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC;YACpC,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAC7B,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,UAAkB,EAAE,QAAgB;IAC1D,OAAO,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const COMMAND_SEARCH_ERRORS: {
2
+ readonly emptyQueryError: "Search query must not be empty.";
3
+ readonly indexNotFoundError: "No index found. Run diana index first.";
4
+ };
5
+ export type SearchResult = {
6
+ anchor: string;
7
+ excerpt: string;
8
+ heading: string;
9
+ path: string;
10
+ sourceRoot: string;
11
+ title: string;
12
+ };
13
+ export declare function searchWorkspace(workspaceRoot: string, query: string): Promise<SearchResult[]>;
@@ -0,0 +1,52 @@
1
+ import { join } from "node:path";
2
+ import { DatabaseSync } from "node:sqlite";
3
+ import { updateIndex } from "../indexer.js";
4
+ import { resolveSources } from "../sources.js";
5
+ import { WORKSPACE_CONSTANTS, loadWorkspace } from "../workspace.js";
6
+ import { pathExists } from "../utils/pathExists.js";
7
+ const { databaseFileName, internalDirName } = WORKSPACE_CONSTANTS;
8
+ export const COMMAND_SEARCH_ERRORS = {
9
+ emptyQueryError: "Search query must not be empty.",
10
+ indexNotFoundError: "No index found. Run diana index first.",
11
+ };
12
+ const { emptyQueryError, indexNotFoundError } = COMMAND_SEARCH_ERRORS;
13
+ export async function searchWorkspace(workspaceRoot, query) {
14
+ if (query.trim() === "") {
15
+ throw new Error(emptyQueryError);
16
+ }
17
+ const workspace = await loadWorkspace(workspaceRoot);
18
+ const databasePath = join(workspace.root, internalDirName, databaseFileName);
19
+ if (!(await pathExists(databasePath))) {
20
+ throw new Error(indexNotFoundError);
21
+ }
22
+ await updateIndex(databasePath, await resolveSources(workspace), "auto");
23
+ const database = new DatabaseSync(databasePath, { readOnly: true });
24
+ try {
25
+ return database
26
+ .prepare(`
27
+ SELECT
28
+ source_root AS sourceRoot,
29
+ path,
30
+ title,
31
+ heading,
32
+ anchor,
33
+ snippet(sections, 5, '', '', '…', 24) AS excerpt
34
+ FROM sections
35
+ WHERE sections MATCH ?
36
+ ORDER BY bm25(sections)
37
+ LIMIT 10
38
+ `)
39
+ .all(toFtsQuery(query));
40
+ }
41
+ finally {
42
+ database.close();
43
+ }
44
+ }
45
+ function toFtsQuery(query) {
46
+ return query
47
+ .trim()
48
+ .split(/\s+/)
49
+ .map((term) => `"${term.replaceAll('"', '""')}"`)
50
+ .join(" AND ");
51
+ }
52
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,MAAM,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,mBAAmB,CAAC;AAElE,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,eAAe,EAAE,iCAAiC;IAClD,kBAAkB,EAAE,wCAAwC;CACpD,CAAC;AAEX,MAAM,EAAE,eAAe,EAAE,kBAAkB,EAAE,GAAG,qBAAqB,CAAC;AAWtE,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,aAAqB,EACrB,KAAa;IAEb,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,aAAa,CAAC,CAAC;IACrD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC;IAE7E,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,WAAW,CAAC,YAAY,EAAE,MAAM,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;IAEzE,MAAM,QAAQ,GAAG,IAAI,YAAY,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpE,IAAI,CAAC;QACH,OAAO,QAAQ;aACZ,OAAO,CACN;;;;;;;;;;;;OAYD,CACA;aACA,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAmB,CAAC;IAC9C,CAAC;YAAS,CAAC;QACT,QAAQ,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;SAChD,IAAI,CAAC,OAAO,CAAC,CAAC;AACnB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { ResolvedSource } from "./types/source.types.js";
2
+ export declare const INDEX_ERRORS: {
3
+ readonly invalidTextError: (filePath: string) => string;
4
+ readonly fileTooLargeError: (filePath: string) => string;
5
+ readonly schemaRebuildError: "Index schema changed. Run diana index to rebuild it.";
6
+ };
7
+ export type IndexedSection = {
8
+ anchor: string;
9
+ body: string;
10
+ heading: string;
11
+ title: string;
12
+ };
13
+ export declare function updateIndex(databasePath: string, sources: ResolvedSource[], mode?: "all" | "auto"): Promise<void>;
14
+ export declare function findMarkdownFiles(source: ResolvedSource): Promise<string[]>;
15
+ export declare function extractMarkdownSection(markdown: string, requestedAnchor: string): string | undefined;
16
+ export declare function splitMarkdownIntoSections(markdown: string, filePath: string): IndexedSection[];