@dnai/dynamicllm 0.1.1 → 0.2.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 (58) hide show
  1. package/README.md +64 -0
  2. package/dist/bin/dynamicllm.js +7 -0
  3. package/dist/cli/formatters.d.ts +11 -0
  4. package/dist/cli/formatters.js +138 -0
  5. package/dist/cli/index.d.ts +1 -0
  6. package/dist/cli/index.js +345 -0
  7. package/dist/cli/init.d.ts +1 -0
  8. package/dist/{setup.js → cli/init.js} +29 -48
  9. package/dist/core/bootstrap.d.ts +14 -0
  10. package/dist/core/bootstrap.js +38 -0
  11. package/dist/core/constants.d.ts +4 -0
  12. package/dist/core/constants.js +71 -0
  13. package/dist/{lib → core}/fetcher.d.ts +6 -7
  14. package/dist/core/fetcher.js +247 -0
  15. package/dist/core/index.d.ts +9 -0
  16. package/dist/core/index.js +8 -0
  17. package/dist/{lib → core}/lint.js +0 -5
  18. package/dist/core/log.d.ts +13 -0
  19. package/dist/core/log.js +65 -0
  20. package/dist/core/report.d.ts +5 -0
  21. package/dist/{lib → core}/report.js +7 -14
  22. package/dist/{lib → core}/types.d.ts +43 -0
  23. package/dist/core/types.js +2 -0
  24. package/dist/{server.js → mcp/server.js} +48 -68
  25. package/framework/CONVENTIONS.md +40 -0
  26. package/framework/SYSTEM_PROMPT.md +203 -0
  27. package/framework/antidotes/constructive-optimism.md +37 -0
  28. package/framework/antidotes/liberated-agentism.md +37 -0
  29. package/framework/antidotes/objective-fallibilism.md +39 -0
  30. package/framework/antidotes/oxidative-creativism.md +41 -0
  31. package/framework/antidotes/polycentric-nodalism.md +38 -0
  32. package/framework/antidotes/vertical-authenticism.md +39 -0
  33. package/framework/lenses/logos.md +32 -0
  34. package/framework/lenses/mythos.md +32 -0
  35. package/framework/lenses/pathos.md +32 -0
  36. package/framework/qualities/beauty.md +29 -0
  37. package/framework/qualities/infinity.md +29 -0
  38. package/framework/qualities/love.md +29 -0
  39. package/framework/qualities/mystery.md +29 -0
  40. package/framework/qualities/play.md +29 -0
  41. package/framework/qualities/story.md +29 -0
  42. package/package.json +29 -5
  43. package/dist/cli.js +0 -20
  44. package/dist/lib/fetcher.js +0 -210
  45. package/dist/lib/report.d.ts +0 -19
  46. package/dist/lib/types.js +0 -1
  47. package/dist/setup.d.ts +0 -1
  48. /package/dist/{cli.d.ts → bin/dynamicllm.d.ts} +0 -0
  49. /package/dist/{lib → core}/cache.d.ts +0 -0
  50. /package/dist/{lib → core}/cache.js +0 -0
  51. /package/dist/{lib → core}/context.d.ts +0 -0
  52. /package/dist/{lib → core}/context.js +0 -0
  53. /package/dist/{lib → core}/lint.d.ts +0 -0
  54. /package/dist/{lib → core}/search.d.ts +0 -0
  55. /package/dist/{lib → core}/search.js +0 -0
  56. /package/dist/{index.d.ts → mcp/index.d.ts} +0 -0
  57. /package/dist/{index.js → mcp/index.js} +0 -0
  58. /package/dist/{server.d.ts → mcp/server.d.ts} +0 -0
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # DynamicLLM
2
+
3
+ Perspective engine for general intelligence. MCP server + CLI providing three analysis modes for any text.
4
+
5
+ ## Modes
6
+
7
+ - **Lens Analysis** — Evaluate balance between Logos (logic), Pathos (emotion), and their synthesis Mythos
8
+ - **Quality Cultivation** — Develop one of six qualities: Play, Beauty, Mystery, Love, Infinity, Story
9
+ - **Virus Scan** — Detect memetic patterns across six axes from Liberated Agentism to Oxidative Creativism
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm i -g @dnai/dynamicllm && dynamicllm init
15
+ ```
16
+
17
+ This installs the CLI and walks you through agent registration (Claude Code, Cursor, Windsurf, Codex).
18
+
19
+ CLI commands work immediately without `init` — defaults are created on first use.
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ # Or register manually with Claude Code
25
+ claude mcp add --scope user dynamicllm -- dynamicllm mcp
26
+ ```
27
+
28
+ ## CLI
29
+
30
+ ```bash
31
+ dynamicllm help # Show all commands
32
+ dynamicllm list # List all network nodes
33
+ dynamicllm search <query> # Search the network
34
+ dynamicllm load <slug> # Load a node's content
35
+ dynamicllm lint # Validate network integrity
36
+ dynamicllm ingest <name> # Generate frontmatter for a new node
37
+ dynamicllm save <slug> <file> # Save a node to your local network
38
+ dynamicllm report <mode> # Generate an analysis report
39
+ dynamicllm log # View activity log
40
+ dynamicllm mcp # Start MCP server (stdio transport)
41
+ ```
42
+
43
+ ## Architecture
44
+
45
+ Content is organized in three layers:
46
+
47
+ 1. **Framework** (bundled) — System prompt, conventions, 3 lenses, 6 qualities, 6 antidotes in `framework/`
48
+ 2. **User Network** (local) — Your nodes in `DYNAMICLLM_NETWORK_DIR`, managed via CLI or MCP
49
+ 3. **DNA Network** (optional) — Curated thinker profiles deployed to R2 for the web demo
50
+
51
+ Framework slugs are protected — user files cannot override bundled content.
52
+
53
+ ```
54
+ framework/
55
+ SYSTEM_PROMPT.md
56
+ CONVENTIONS.md
57
+ lenses/ logos.md, pathos.md, mythos.md
58
+ qualities/ play.md, beauty.md, story.md, infinity.md, mystery.md, love.md
59
+ antidotes/ liberated-agentism.md, constructive-optimism.md, ...
60
+ ```
61
+
62
+ ## License
63
+
64
+ MIT
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "../cli/index.js";
3
+ const args = process.argv.slice(2);
4
+ runCli(args).catch((error) => {
5
+ console.error("Fatal error:", error instanceof Error ? error.message : error);
6
+ process.exit(1);
7
+ });
@@ -0,0 +1,11 @@
1
+ import type { SearchResult, LintReport, LogEntry } from "../core/types.js";
2
+ export declare function formatHelp(): string;
3
+ export declare function formatSearchResults(results: SearchResult[]): string;
4
+ export declare function formatNodeList(entries: {
5
+ slug: string;
6
+ displayName: string;
7
+ type: string;
8
+ tags: string[];
9
+ }[]): string;
10
+ export declare function formatLintReport(report: LintReport): string;
11
+ export declare function formatLogEntries(entries: LogEntry[]): string;
@@ -0,0 +1,138 @@
1
+ const BOLD = "\x1b[1m";
2
+ const DIM = "\x1b[2m";
3
+ const RESET = "\x1b[0m";
4
+ const GREEN = "\x1b[32m";
5
+ const YELLOW = "\x1b[33m";
6
+ const RED = "\x1b[31m";
7
+ const CYAN = "\x1b[36m";
8
+ export function formatHelp() {
9
+ return `
10
+ ${BOLD}DynamicLLM${RESET} — perspective engine for general intelligence
11
+
12
+ ${BOLD}USAGE${RESET}
13
+ dynamicllm <command> [options]
14
+
15
+ ${BOLD}COMMANDS${RESET}
16
+ ${CYAN}mcp${RESET} Start MCP server (for AI tool registration)
17
+ ${CYAN}init${RESET} Interactive setup (Claude Code, Cursor, Windsurf, Codex)
18
+ ${CYAN}search${RESET} <query> Search network by tags/names
19
+ ${CYAN}load${RESET} <slug> Display a node's content
20
+ ${CYAN}list${RESET} [--type=X] List all nodes
21
+ ${CYAN}lint${RESET} Validate network health
22
+ ${CYAN}ingest${RESET} Format content as v5-conformant node (interactive)
23
+ ${CYAN}save${RESET} <slug> <file> Persist a node to local network
24
+ ${CYAN}source-search${RESET} <query> Search within source works
25
+ ${CYAN}source-excerpt${RESET} <slug> <q> Retrieve passage from a source
26
+ ${CYAN}report${RESET} Generate styled analysis report
27
+ ${CYAN}log${RESET} [--limit=N] View activity log
28
+
29
+ ${BOLD}ENVIRONMENT${RESET}
30
+ DYNAMICLLM_NETWORK_DIR Path to user's network directory (default: ~/.dynamicllm/network/)
31
+
32
+ ${BOLD}EXAMPLES${RESET}
33
+ ${DIM}# Start MCP server for Claude Code${RESET}
34
+ dynamicllm mcp
35
+
36
+ ${DIM}# Search for nodes about epistemology${RESET}
37
+ dynamicllm search epistemology
38
+
39
+ ${DIM}# Load a specific thinker${RESET}
40
+ dynamicllm load david-deutsch
41
+
42
+ ${DIM}# List all antidotes${RESET}
43
+ dynamicllm list --type=antidote
44
+
45
+ ${DIM}# Run network health check${RESET}
46
+ dynamicllm lint
47
+
48
+ ${DIM}# Register with Claude Code${RESET}
49
+ dynamicllm init
50
+ `;
51
+ }
52
+ export function formatSearchResults(results) {
53
+ if (results.length === 0)
54
+ return `${DIM}No results found.${RESET}`;
55
+ const lines = [`${BOLD}${results.length} result(s):${RESET}`, ""];
56
+ for (const r of results) {
57
+ const typeLabel = `[${r.type}]`.padEnd(12);
58
+ lines.push(` ${CYAN}${r.slug}${RESET} ${DIM}${typeLabel}${RESET} ${r.displayName} ${DIM}(score: ${r.score})${RESET}`);
59
+ if (r.tags.length > 0) {
60
+ lines.push(` ${DIM}tags: ${r.tags.slice(0, 8).join(", ")}${r.tags.length > 8 ? ", ..." : ""}${RESET}`);
61
+ }
62
+ }
63
+ return lines.join("\n");
64
+ }
65
+ export function formatNodeList(entries) {
66
+ if (entries.length === 0)
67
+ return `${DIM}No nodes found.${RESET}`;
68
+ const lines = [`${BOLD}${entries.length} node(s):${RESET}`, ""];
69
+ // Group by type
70
+ const byType = new Map();
71
+ for (const e of entries) {
72
+ const list = byType.get(e.type) ?? [];
73
+ list.push(e);
74
+ byType.set(e.type, list);
75
+ }
76
+ for (const [type, items] of byType) {
77
+ lines.push(` ${BOLD}${type}${RESET} (${items.length})`);
78
+ for (const item of items) {
79
+ lines.push(` ${CYAN}${item.slug}${RESET} ${DIM}${item.displayName}${RESET}`);
80
+ }
81
+ lines.push("");
82
+ }
83
+ return lines.join("\n");
84
+ }
85
+ export function formatLintReport(report) {
86
+ const lines = [];
87
+ lines.push(`${BOLD}Network Health Report${RESET}`);
88
+ lines.push(`${"─".repeat(40)}`);
89
+ lines.push(` Total nodes: ${report.stats.totalNodes}`);
90
+ for (const [type, count] of Object.entries(report.stats.byType)) {
91
+ lines.push(` ${type}: ${count}`);
92
+ }
93
+ lines.push("");
94
+ if (report.errors.length > 0) {
95
+ lines.push(`${RED}${BOLD}Errors (${report.errors.length}):${RESET}`);
96
+ for (const e of report.errors) {
97
+ lines.push(` ${RED}✗${RESET} ${CYAN}${e.slug}${RESET}: ${e.message}`);
98
+ }
99
+ lines.push("");
100
+ }
101
+ if (report.warnings.length > 0) {
102
+ lines.push(`${YELLOW}${BOLD}Warnings (${report.warnings.length}):${RESET}`);
103
+ for (const w of report.warnings) {
104
+ lines.push(` ${YELLOW}!${RESET} ${CYAN}${w.slug}${RESET}: ${w.message}`);
105
+ }
106
+ lines.push("");
107
+ }
108
+ if (report.tensions.length > 0) {
109
+ lines.push(`${GREEN}${BOLD}Productive Tensions (${report.tensions.length}):${RESET}`);
110
+ for (const t of report.tensions) {
111
+ lines.push(` ${GREEN}⟷${RESET} ${t.description}`);
112
+ lines.push(` ${DIM}shared: ${t.sharedTags.join(", ")}${RESET}`);
113
+ }
114
+ lines.push("");
115
+ }
116
+ if (report.stats.orphanTags.length > 0) {
117
+ lines.push(`${DIM}Orphan tags (used by only 1 node): ${report.stats.orphanTags.slice(0, 10).join(", ")}${report.stats.orphanTags.length > 10 ? `, ... (${report.stats.orphanTags.length} total)` : ""}${RESET}`);
118
+ }
119
+ const status = report.errors.length === 0
120
+ ? `${GREEN}${BOLD}Network healthy${RESET}`
121
+ : `${RED}${BOLD}${report.errors.length} error(s) found${RESET}`;
122
+ lines.push("");
123
+ lines.push(status);
124
+ return lines.join("\n");
125
+ }
126
+ export function formatLogEntries(entries) {
127
+ if (entries.length === 0)
128
+ return `${DIM}No activity log entries.${RESET}`;
129
+ const lines = [`${BOLD}Activity Log (${entries.length} entries):${RESET}`, ""];
130
+ for (const e of entries) {
131
+ const modeStr = e.mode ? ` ${DIM}[${e.mode}]${RESET}` : "";
132
+ lines.push(` ${DIM}${e.date}${RESET} ${CYAN}${e.action}${RESET} ${e.slug}${modeStr}`);
133
+ if (e.summary) {
134
+ lines.push(` ${DIM}${e.summary}${RESET}`);
135
+ }
136
+ }
137
+ return lines.join("\n");
138
+ }
@@ -0,0 +1 @@
1
+ export declare function runCli(args: string[]): Promise<void>;
@@ -0,0 +1,345 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { NetworkFetcher, FRAMEWORK_SLUGS } from "../core/fetcher.js";
4
+ import { bootstrap } from "../core/bootstrap.js";
5
+ import { search } from "../core/search.js";
6
+ import { lint } from "../core/lint.js";
7
+ import { generateReport } from "../core/report.js";
8
+ import { appendLog, readLog } from "../core/log.js";
9
+ import { formatHelp, formatSearchResults, formatNodeList, formatLintReport, formatLogEntries, } from "./formatters.js";
10
+ function createFetcher(hint) {
11
+ const { networkDir, created } = bootstrap();
12
+ if (hint && created) {
13
+ process.stderr.write("DynamicLLM: created default config. Run `dynamicllm init` to customize.\n");
14
+ }
15
+ return new NetworkFetcher({ networkDir });
16
+ }
17
+ /** Parse --key=value flags from argv */
18
+ function parseFlags(args) {
19
+ const flags = {};
20
+ const positional = [];
21
+ for (const arg of args) {
22
+ if (arg.startsWith("--")) {
23
+ const eq = arg.indexOf("=");
24
+ if (eq > 0) {
25
+ flags[arg.slice(2, eq)] = arg.slice(eq + 1);
26
+ }
27
+ else {
28
+ flags[arg.slice(2)] = "true";
29
+ }
30
+ }
31
+ else {
32
+ positional.push(arg);
33
+ }
34
+ }
35
+ return { flags, positional };
36
+ }
37
+ export async function runCli(args) {
38
+ const command = args[0];
39
+ const restArgs = args.slice(1);
40
+ const { flags, positional } = parseFlags(restArgs);
41
+ switch (command) {
42
+ case "mcp": {
43
+ const { startServer } = await import("../mcp/server.js");
44
+ await startServer();
45
+ break;
46
+ }
47
+ case "init": {
48
+ const { init } = await import("./init.js");
49
+ await init();
50
+ break;
51
+ }
52
+ case "search": {
53
+ const query = positional.join(" ");
54
+ if (!query) {
55
+ console.error("Usage: dynamicllm search <query> [--type=X] [--limit=N]");
56
+ process.exit(1);
57
+ }
58
+ const fetcher = createFetcher(true);
59
+ const index = await fetcher.fetchIndex();
60
+ const types = flags.type ? [flags.type] : undefined;
61
+ const limit = flags.limit ? parseInt(flags.limit, 10) : 10;
62
+ const results = search(index, query, { types, limit });
63
+ console.log(formatSearchResults(results));
64
+ break;
65
+ }
66
+ case "load": {
67
+ const slug = positional[0];
68
+ if (!slug) {
69
+ console.error("Usage: dynamicllm load <slug>");
70
+ process.exit(1);
71
+ }
72
+ const fetcher = createFetcher(true);
73
+ const index = await fetcher.fetchIndex();
74
+ const isNode = index.nodes.some((n) => n.slug === slug);
75
+ const isSource = index.sources.some((s) => s.slug === slug);
76
+ const isMeta = index.meta.some((m) => m.slug === slug);
77
+ let content;
78
+ if (isNode) {
79
+ content = await fetcher.fetchNode(slug);
80
+ }
81
+ else if (isSource) {
82
+ content = await fetcher.fetchSource(slug);
83
+ }
84
+ else if (isMeta) {
85
+ content = await fetcher.fetchMeta(slug);
86
+ }
87
+ else {
88
+ console.error(`Node '${slug}' not found in the DynamicLLM network.`);
89
+ process.exit(1);
90
+ }
91
+ console.log(content);
92
+ break;
93
+ }
94
+ case "list": {
95
+ const fetcher = createFetcher(true);
96
+ const index = await fetcher.fetchIndex();
97
+ let entries = [
98
+ ...index.nodes.map((n) => ({
99
+ slug: n.slug,
100
+ displayName: n.displayName,
101
+ type: n.type,
102
+ tags: n.tags,
103
+ })),
104
+ ...index.sources.map((s) => ({
105
+ slug: s.slug,
106
+ displayName: s.displayName,
107
+ type: s.type,
108
+ tags: s.tags,
109
+ })),
110
+ ];
111
+ if (flags.type) {
112
+ entries = entries.filter((e) => e.type === flags.type);
113
+ }
114
+ console.log(formatNodeList(entries));
115
+ break;
116
+ }
117
+ case "lint": {
118
+ const fetcher = createFetcher(true);
119
+ const index = await fetcher.fetchIndex();
120
+ const report = lint(index);
121
+ appendLog("lint", "network", { summary: `Errors: ${report.errors.length}, Warnings: ${report.warnings.length}` });
122
+ console.log(formatLintReport(report));
123
+ process.exit(report.errors.length > 0 ? 1 : 0);
124
+ break;
125
+ }
126
+ case "ingest": {
127
+ // Interactive ingest — read from stdin or provided arguments
128
+ const type = flags.type;
129
+ const name = positional[0] || flags.name;
130
+ if (!type || !name) {
131
+ console.error("Usage: dynamicllm ingest <name> --type=<type> [--tags=a,b,c] [--mind_virus=X] [--derived_from=a,b]");
132
+ console.error(" Reads content from stdin.");
133
+ console.error(" Types: person, idea, antidote, lens, quality, source, synthesis");
134
+ process.exit(1);
135
+ }
136
+ // Read content from stdin
137
+ const chunks = [];
138
+ for await (const chunk of process.stdin) {
139
+ chunks.push(chunk);
140
+ }
141
+ const content = Buffer.concat(chunks).toString("utf-8").trim();
142
+ if (!content) {
143
+ console.error("No content provided on stdin.");
144
+ process.exit(1);
145
+ }
146
+ const slug = name
147
+ .toLowerCase()
148
+ .replace(/[^a-z0-9]+/g, "-")
149
+ .replace(/^-|-$/g, "");
150
+ const tags = flags.tags?.split(",").map((t) => t.trim()).filter(Boolean) ?? [];
151
+ let frontmatter = `---\ntype: ${type}\nname: ${name}\n`;
152
+ if (type === "antidote" && flags.mind_virus) {
153
+ frontmatter += `mind_virus: ${flags.mind_virus}\n`;
154
+ }
155
+ if (tags.length > 0) {
156
+ frontmatter += `tags: [${tags.join(", ")}]\n`;
157
+ }
158
+ if (type === "synthesis" && flags.derived_from) {
159
+ const derived = flags.derived_from.split(",").map((s) => s.trim()).filter(Boolean);
160
+ frontmatter += `derived_from: [${derived.join(", ")}]\n`;
161
+ }
162
+ frontmatter += "---\n\n";
163
+ const output = `${frontmatter}# ${name}\n\n${content}`;
164
+ appendLog("ingest", slug, { summary: `Type: ${type}` });
165
+ console.log(output);
166
+ break;
167
+ }
168
+ case "save": {
169
+ const slug = positional[0];
170
+ const filePath = positional[1];
171
+ if (!slug || !filePath) {
172
+ console.error("Usage: dynamicllm save <slug> <file>");
173
+ console.error(" Or pipe content: echo '...' | dynamicllm save <slug> --stdin");
174
+ process.exit(1);
175
+ }
176
+ const { networkDir, created } = bootstrap();
177
+ if (created) {
178
+ process.stderr.write("DynamicLLM: created default config. Run `dynamicllm init` to customize.\n");
179
+ }
180
+ if (FRAMEWORK_SLUGS.has(slug)) {
181
+ console.error(`Error: "${slug}" is a protected framework slug and cannot be overridden.`);
182
+ process.exit(1);
183
+ }
184
+ const { mkdirSync, writeFileSync } = await import("node:fs");
185
+ if (!existsSync(networkDir)) {
186
+ mkdirSync(networkDir, { recursive: true });
187
+ }
188
+ let content;
189
+ if (flags.stdin === "true") {
190
+ const chunks = [];
191
+ for await (const chunk of process.stdin) {
192
+ chunks.push(chunk);
193
+ }
194
+ content = Buffer.concat(chunks).toString("utf-8");
195
+ }
196
+ else {
197
+ if (!existsSync(filePath)) {
198
+ console.error(`File not found: ${filePath}`);
199
+ process.exit(1);
200
+ }
201
+ content = readFileSync(filePath, "utf-8");
202
+ }
203
+ const savePath = join(networkDir, `${slug}.md`);
204
+ writeFileSync(savePath, content, "utf-8");
205
+ appendLog("save", slug, { summary: `Saved: ${slug}.md` });
206
+ console.log(`Saved ${slug}.md to ${savePath}`);
207
+ break;
208
+ }
209
+ case "source-search": {
210
+ const query = positional.join(" ");
211
+ if (!query) {
212
+ console.error("Usage: dynamicllm source-search <query> [--source=slug] [--limit=N]");
213
+ process.exit(1);
214
+ }
215
+ const fetcher = createFetcher(true);
216
+ const index = await fetcher.fetchIndex();
217
+ if (index.sources.length === 0) {
218
+ console.log("No source works are available in the network yet.");
219
+ break;
220
+ }
221
+ const sources = flags.source
222
+ ? index.sources.filter((s) => s.slug === flags.source)
223
+ : index.sources;
224
+ const queryLower = query.toLowerCase();
225
+ const maxResults = flags.limit ? parseInt(flags.limit, 10) : 5;
226
+ const results = [];
227
+ for (const source of sources) {
228
+ if (results.length >= maxResults)
229
+ break;
230
+ const content = await fetcher.fetchSource(source.slug);
231
+ const lines = content.split("\n");
232
+ for (let i = 0; i < lines.length; i++) {
233
+ if (results.length >= maxResults)
234
+ break;
235
+ if (lines[i].toLowerCase().includes(queryLower)) {
236
+ const start = Math.max(0, i - 2);
237
+ const end = Math.min(lines.length, i + 3);
238
+ results.push({
239
+ source: source.displayName,
240
+ excerpt: lines.slice(start, end).join("\n"),
241
+ });
242
+ }
243
+ }
244
+ }
245
+ if (results.length === 0) {
246
+ console.log(`No matches found for "${query}" in source works.`);
247
+ }
248
+ else {
249
+ for (const r of results) {
250
+ console.log(`\n\x1b[1m${r.source}\x1b[0m`);
251
+ console.log(r.excerpt);
252
+ }
253
+ }
254
+ break;
255
+ }
256
+ case "source-excerpt": {
257
+ const sourceSlug = positional[0];
258
+ const query = positional.slice(1).join(" ");
259
+ if (!sourceSlug || !query) {
260
+ console.error("Usage: dynamicllm source-excerpt <slug> <query>");
261
+ process.exit(1);
262
+ }
263
+ const fetcher = createFetcher(true);
264
+ const index = await fetcher.fetchIndex();
265
+ const source = index.sources.find((s) => s.slug === sourceSlug);
266
+ if (!source) {
267
+ console.error(`Source '${sourceSlug}' not found.`);
268
+ process.exit(1);
269
+ }
270
+ const content = await fetcher.fetchSource(sourceSlug);
271
+ const lines = content.split("\n");
272
+ const queryLower = query.toLowerCase();
273
+ let found = false;
274
+ for (let i = 0; i < lines.length; i++) {
275
+ if (lines[i].toLowerCase().includes(queryLower)) {
276
+ const start = Math.max(0, i - 5);
277
+ const end = Math.min(lines.length, i + 10);
278
+ console.log(`\n\x1b[1m${source.displayName}\x1b[0m (around line ${i + 1}):\n`);
279
+ console.log(lines.slice(start, end).join("\n"));
280
+ found = true;
281
+ break;
282
+ }
283
+ }
284
+ if (!found) {
285
+ console.log(`No passage matching "${query}" found in ${source.displayName}.`);
286
+ }
287
+ break;
288
+ }
289
+ case "report": {
290
+ const mode = flags.mode;
291
+ if (!mode) {
292
+ console.error("Usage: dynamicllm report --mode=<virus-scan|lens|quality>");
293
+ console.error(" Reads analysis text from stdin.");
294
+ process.exit(1);
295
+ }
296
+ const chunks = [];
297
+ for await (const chunk of process.stdin) {
298
+ chunks.push(chunk);
299
+ }
300
+ const analysisText = Buffer.concat(chunks).toString("utf-8").trim();
301
+ if (!analysisText) {
302
+ console.error("No analysis text provided on stdin.");
303
+ process.exit(1);
304
+ }
305
+ // Parse scores from --scores flag if provided (JSON string)
306
+ let scores;
307
+ if (flags.scores) {
308
+ try {
309
+ scores = JSON.parse(flags.scores);
310
+ }
311
+ catch {
312
+ console.error("Invalid --scores JSON.");
313
+ process.exit(1);
314
+ }
315
+ }
316
+ const result = generateReport({
317
+ mode,
318
+ analysisText,
319
+ contentLabel: flags.label,
320
+ quality: flags.quality,
321
+ scores,
322
+ });
323
+ appendLog("report", mode, { summary: `Report: ${result.filePath}` });
324
+ console.log(`Report saved to: ${result.filePath}`);
325
+ break;
326
+ }
327
+ case "log": {
328
+ const limit = flags.limit ? parseInt(flags.limit, 10) : 20;
329
+ const action = flags.action;
330
+ const entries = readLog({ limit, action });
331
+ console.log(formatLogEntries(entries));
332
+ break;
333
+ }
334
+ case "help":
335
+ case "--help":
336
+ case "-h":
337
+ case undefined:
338
+ console.log(formatHelp());
339
+ break;
340
+ default:
341
+ console.error(`Unknown command: ${command}`);
342
+ console.error("Run 'dynamicllm help' for available commands.");
343
+ process.exit(1);
344
+ }
345
+ }
@@ -0,0 +1 @@
1
+ export declare function init(): Promise<void>;