@indigoai-us/cq 0.0.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.
- package/dist/__tests__/integration.test.d.ts +8 -0
- package/dist/__tests__/integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration.test.js +233 -0
- package/dist/__tests__/integration.test.js.map +1 -0
- package/dist/budget/__tests__/budget.test.d.ts +2 -0
- package/dist/budget/__tests__/budget.test.d.ts.map +1 -0
- package/dist/budget/__tests__/budget.test.js +238 -0
- package/dist/budget/__tests__/budget.test.js.map +1 -0
- package/dist/budget/__tests__/token-tracker.test.d.ts +2 -0
- package/dist/budget/__tests__/token-tracker.test.d.ts.map +1 -0
- package/dist/budget/__tests__/token-tracker.test.js +292 -0
- package/dist/budget/__tests__/token-tracker.test.js.map +1 -0
- package/dist/budget/index.d.ts +38 -0
- package/dist/budget/index.d.ts.map +1 -0
- package/dist/budget/index.js +192 -0
- package/dist/budget/index.js.map +1 -0
- package/dist/budget/token-tracker.d.ts +62 -0
- package/dist/budget/token-tracker.d.ts.map +1 -0
- package/dist/budget/token-tracker.js +152 -0
- package/dist/budget/token-tracker.js.map +1 -0
- package/dist/budget/types.d.ts +41 -0
- package/dist/budget/types.d.ts.map +1 -0
- package/dist/budget/types.js +5 -0
- package/dist/budget/types.js.map +1 -0
- package/dist/confidence/__tests__/confidence.test.d.ts +2 -0
- package/dist/confidence/__tests__/confidence.test.d.ts.map +1 -0
- package/dist/confidence/__tests__/confidence.test.js +336 -0
- package/dist/confidence/__tests__/confidence.test.js.map +1 -0
- package/dist/confidence/erosion.d.ts +33 -0
- package/dist/confidence/erosion.d.ts.map +1 -0
- package/dist/confidence/erosion.js +42 -0
- package/dist/confidence/erosion.js.map +1 -0
- package/dist/confidence/index.d.ts +42 -0
- package/dist/confidence/index.d.ts.map +1 -0
- package/dist/confidence/index.js +168 -0
- package/dist/confidence/index.js.map +1 -0
- package/dist/confidence/types.d.ts +29 -0
- package/dist/confidence/types.d.ts.map +1 -0
- package/dist/confidence/types.js +5 -0
- package/dist/confidence/types.js.map +1 -0
- package/dist/curiosity/__tests__/curiosity.test.d.ts +2 -0
- package/dist/curiosity/__tests__/curiosity.test.d.ts.map +1 -0
- package/dist/curiosity/__tests__/curiosity.test.js +280 -0
- package/dist/curiosity/__tests__/curiosity.test.js.map +1 -0
- package/dist/curiosity/dedup.d.ts +29 -0
- package/dist/curiosity/dedup.d.ts.map +1 -0
- package/dist/curiosity/dedup.js +64 -0
- package/dist/curiosity/dedup.js.map +1 -0
- package/dist/curiosity/index.d.ts +56 -0
- package/dist/curiosity/index.d.ts.map +1 -0
- package/dist/curiosity/index.js +163 -0
- package/dist/curiosity/index.js.map +1 -0
- package/dist/curiosity/types.d.ts +29 -0
- package/dist/curiosity/types.d.ts.map +1 -0
- package/dist/curiosity/types.js +5 -0
- package/dist/curiosity/types.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +7 -0
- package/dist/index.test.js.map +1 -0
- package/dist/init.d.ts +17 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +153 -0
- package/dist/init.js.map +1 -0
- package/dist/knowledge-tree/__tests__/knowledge-tree.test.d.ts +2 -0
- package/dist/knowledge-tree/__tests__/knowledge-tree.test.d.ts.map +1 -0
- package/dist/knowledge-tree/__tests__/knowledge-tree.test.js +317 -0
- package/dist/knowledge-tree/__tests__/knowledge-tree.test.js.map +1 -0
- package/dist/knowledge-tree/format.d.ts +17 -0
- package/dist/knowledge-tree/format.d.ts.map +1 -0
- package/dist/knowledge-tree/format.js +48 -0
- package/dist/knowledge-tree/format.js.map +1 -0
- package/dist/knowledge-tree/index.d.ts +33 -0
- package/dist/knowledge-tree/index.d.ts.map +1 -0
- package/dist/knowledge-tree/index.js +182 -0
- package/dist/knowledge-tree/index.js.map +1 -0
- package/dist/knowledge-tree/types.d.ts +37 -0
- package/dist/knowledge-tree/types.d.ts.map +1 -0
- package/dist/knowledge-tree/types.js +5 -0
- package/dist/knowledge-tree/types.js.map +1 -0
- package/dist/metadata/__tests__/metadata.test.d.ts +2 -0
- package/dist/metadata/__tests__/metadata.test.d.ts.map +1 -0
- package/dist/metadata/__tests__/metadata.test.js +196 -0
- package/dist/metadata/__tests__/metadata.test.js.map +1 -0
- package/dist/metadata/__tests__/search.test.d.ts +2 -0
- package/dist/metadata/__tests__/search.test.d.ts.map +1 -0
- package/dist/metadata/__tests__/search.test.js +227 -0
- package/dist/metadata/__tests__/search.test.js.map +1 -0
- package/dist/metadata/index.d.ts +29 -0
- package/dist/metadata/index.d.ts.map +1 -0
- package/dist/metadata/index.js +108 -0
- package/dist/metadata/index.js.map +1 -0
- package/dist/metadata/schema.d.ts +8 -0
- package/dist/metadata/schema.d.ts.map +1 -0
- package/dist/metadata/schema.js +31 -0
- package/dist/metadata/schema.js.map +1 -0
- package/dist/metadata/search.d.ts +27 -0
- package/dist/metadata/search.d.ts.map +1 -0
- package/dist/metadata/search.js +57 -0
- package/dist/metadata/search.js.map +1 -0
- package/dist/metadata/types.d.ts +17 -0
- package/dist/metadata/types.d.ts.map +1 -0
- package/dist/metadata/types.js +5 -0
- package/dist/metadata/types.js.map +1 -0
- package/dist/orchestrator/__tests__/orchestrator.test.d.ts +2 -0
- package/dist/orchestrator/__tests__/orchestrator.test.d.ts.map +1 -0
- package/dist/orchestrator/__tests__/orchestrator.test.js +354 -0
- package/dist/orchestrator/__tests__/orchestrator.test.js.map +1 -0
- package/dist/orchestrator/index.d.ts +23 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +302 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/scheduler.d.ts +32 -0
- package/dist/orchestrator/scheduler.d.ts.map +1 -0
- package/dist/orchestrator/scheduler.js +102 -0
- package/dist/orchestrator/scheduler.js.map +1 -0
- package/dist/orchestrator/types.d.ts +90 -0
- package/dist/orchestrator/types.d.ts.map +1 -0
- package/dist/orchestrator/types.js +5 -0
- package/dist/orchestrator/types.js.map +1 -0
- package/dist/pipeline/__tests__/pipeline.test.d.ts +2 -0
- package/dist/pipeline/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/pipeline/__tests__/pipeline.test.js +320 -0
- package/dist/pipeline/__tests__/pipeline.test.js.map +1 -0
- package/dist/pipeline/index.d.ts +23 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +212 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/types.d.ts +41 -0
- package/dist/pipeline/types.d.ts.map +1 -0
- package/dist/pipeline/types.js +5 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/providers/__tests__/providers.test.d.ts +2 -0
- package/dist/providers/__tests__/providers.test.d.ts.map +1 -0
- package/dist/providers/__tests__/providers.test.js +195 -0
- package/dist/providers/__tests__/providers.test.js.map +1 -0
- package/dist/providers/claude-model.d.ts +17 -0
- package/dist/providers/claude-model.d.ts.map +1 -0
- package/dist/providers/claude-model.js +119 -0
- package/dist/providers/claude-model.js.map +1 -0
- package/dist/providers/index.d.ts +22 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +38 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/openai-search.d.ts +14 -0
- package/dist/providers/openai-search.d.ts.map +1 -0
- package/dist/providers/openai-search.js +97 -0
- package/dist/providers/openai-search.js.map +1 -0
- package/dist/providers/types.d.ts +42 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +5 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/types.d.ts +122 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/vetter/__tests__/vetter.test.d.ts +2 -0
- package/dist/vetter/__tests__/vetter.test.d.ts.map +1 -0
- package/dist/vetter/__tests__/vetter.test.js +224 -0
- package/dist/vetter/__tests__/vetter.test.js.map +1 -0
- package/dist/vetter/index.d.ts +23 -0
- package/dist/vetter/index.d.ts.map +1 -0
- package/dist/vetter/index.js +177 -0
- package/dist/vetter/index.js.map +1 -0
- package/dist/vetter/resource-tracker.d.ts +49 -0
- package/dist/vetter/resource-tracker.d.ts.map +1 -0
- package/dist/vetter/resource-tracker.js +183 -0
- package/dist/vetter/resource-tracker.js.map +1 -0
- package/dist/vetter/types.d.ts +44 -0
- package/dist/vetter/types.d.ts.map +1 -0
- package/dist/vetter/types.js +5 -0
- package/dist/vetter/types.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtemp, rm, readFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { initKnowledgeTree, writeEntry, readEntry, listEntries, listCategories, removeEntry, createCategory, generateIndex, generateIndexRecursive, slugify, } from "../index.js";
|
|
6
|
+
let dataDir;
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
dataDir = await mkdtemp(join(tmpdir(), "cq-kt-"));
|
|
9
|
+
});
|
|
10
|
+
afterEach(async () => {
|
|
11
|
+
await rm(dataDir, { recursive: true, force: true });
|
|
12
|
+
});
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// slugify
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
describe("slugify", () => {
|
|
17
|
+
it("lowercases and replaces spaces with hyphens", () => {
|
|
18
|
+
expect(slugify("Hello World")).toBe("hello-world");
|
|
19
|
+
});
|
|
20
|
+
it("strips leading/trailing hyphens", () => {
|
|
21
|
+
expect(slugify("--foo--")).toBe("foo");
|
|
22
|
+
});
|
|
23
|
+
it("collapses non-alphanumeric runs", () => {
|
|
24
|
+
expect(slugify("What is AI?!")).toBe("what-is-ai");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// initKnowledgeTree
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
describe("initKnowledgeTree", () => {
|
|
31
|
+
it("creates the root directory", async () => {
|
|
32
|
+
const root = join(dataDir, "knowledge");
|
|
33
|
+
await initKnowledgeTree(root);
|
|
34
|
+
// Should not throw when reading the directory
|
|
35
|
+
const { readdir } = await import("node:fs/promises");
|
|
36
|
+
const entries = await readdir(root);
|
|
37
|
+
expect(entries).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
it("is idempotent", async () => {
|
|
40
|
+
const root = join(dataDir, "knowledge");
|
|
41
|
+
await initKnowledgeTree(root);
|
|
42
|
+
await initKnowledgeTree(root);
|
|
43
|
+
// No error
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// writeEntry / readEntry
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
describe("writeEntry + readEntry", () => {
|
|
50
|
+
it("creates a .md file with YAML frontmatter", async () => {
|
|
51
|
+
const slug = await writeEntry(dataDir, {
|
|
52
|
+
category: "science",
|
|
53
|
+
title: "Quantum Computing Basics",
|
|
54
|
+
content: "Qubits are the fundamental unit.",
|
|
55
|
+
tags: ["quantum", "computing"],
|
|
56
|
+
source: "https://example.com",
|
|
57
|
+
});
|
|
58
|
+
expect(slug).toBe("quantum-computing-basics");
|
|
59
|
+
const raw = await readFile(join(dataDir, "science", "quantum-computing-basics.md"), "utf-8");
|
|
60
|
+
expect(raw).toContain("---");
|
|
61
|
+
expect(raw).toContain("title: Quantum Computing Basics");
|
|
62
|
+
expect(raw).toContain("Qubits are the fundamental unit.");
|
|
63
|
+
});
|
|
64
|
+
it("reads back a written entry correctly", async () => {
|
|
65
|
+
await writeEntry(dataDir, {
|
|
66
|
+
category: "tech",
|
|
67
|
+
slug: "rust-lang",
|
|
68
|
+
title: "Rust Programming",
|
|
69
|
+
content: "Memory safety without GC.",
|
|
70
|
+
tags: ["rust", "systems"],
|
|
71
|
+
});
|
|
72
|
+
const entry = await readEntry(dataDir, "tech", "rust-lang");
|
|
73
|
+
expect(entry.title).toBe("Rust Programming");
|
|
74
|
+
expect(entry.content).toBe("Memory safety without GC.\n");
|
|
75
|
+
expect(entry.tags).toEqual(["rust", "systems"]);
|
|
76
|
+
expect(entry.category).toBe("tech");
|
|
77
|
+
expect(entry.slug).toBe("rust-lang");
|
|
78
|
+
expect(entry.source).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
it("preserves created date on update", async () => {
|
|
81
|
+
await writeEntry(dataDir, {
|
|
82
|
+
category: "tech",
|
|
83
|
+
slug: "node",
|
|
84
|
+
title: "Node.js",
|
|
85
|
+
content: "Version 1",
|
|
86
|
+
});
|
|
87
|
+
const first = await readEntry(dataDir, "tech", "node");
|
|
88
|
+
// Small delay to ensure timestamp changes
|
|
89
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
90
|
+
await writeEntry(dataDir, {
|
|
91
|
+
category: "tech",
|
|
92
|
+
slug: "node",
|
|
93
|
+
title: "Node.js Updated",
|
|
94
|
+
content: "Version 2",
|
|
95
|
+
});
|
|
96
|
+
const second = await readEntry(dataDir, "tech", "node");
|
|
97
|
+
expect(second.created).toBe(first.created);
|
|
98
|
+
expect(second.title).toBe("Node.js Updated");
|
|
99
|
+
});
|
|
100
|
+
it("uses explicit slug when provided", async () => {
|
|
101
|
+
const slug = await writeEntry(dataDir, {
|
|
102
|
+
category: "misc",
|
|
103
|
+
slug: "my-custom-slug",
|
|
104
|
+
title: "Custom Slug Entry",
|
|
105
|
+
content: "Body.",
|
|
106
|
+
});
|
|
107
|
+
expect(slug).toBe("my-custom-slug");
|
|
108
|
+
});
|
|
109
|
+
it("includes source in frontmatter when provided", async () => {
|
|
110
|
+
await writeEntry(dataDir, {
|
|
111
|
+
category: "links",
|
|
112
|
+
slug: "example",
|
|
113
|
+
title: "Example",
|
|
114
|
+
content: "Content.",
|
|
115
|
+
source: "https://example.com",
|
|
116
|
+
});
|
|
117
|
+
const entry = await readEntry(dataDir, "links", "example");
|
|
118
|
+
expect(entry.source).toBe("https://example.com");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// removeEntry
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
describe("removeEntry", () => {
|
|
125
|
+
it("deletes the entry file", async () => {
|
|
126
|
+
await writeEntry(dataDir, {
|
|
127
|
+
category: "temp",
|
|
128
|
+
slug: "delete-me",
|
|
129
|
+
title: "Delete Me",
|
|
130
|
+
content: "Gone soon.",
|
|
131
|
+
});
|
|
132
|
+
await removeEntry(dataDir, "temp", "delete-me");
|
|
133
|
+
await expect(readEntry(dataDir, "temp", "delete-me")).rejects.toThrow();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// listEntries
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
describe("listEntries", () => {
|
|
140
|
+
it("returns entries sorted by title", async () => {
|
|
141
|
+
await writeEntry(dataDir, {
|
|
142
|
+
category: "animals",
|
|
143
|
+
slug: "zebra",
|
|
144
|
+
title: "Zebra",
|
|
145
|
+
content: "Striped.",
|
|
146
|
+
});
|
|
147
|
+
await writeEntry(dataDir, {
|
|
148
|
+
category: "animals",
|
|
149
|
+
slug: "aardvark",
|
|
150
|
+
title: "Aardvark",
|
|
151
|
+
content: "Anteater.",
|
|
152
|
+
});
|
|
153
|
+
await writeEntry(dataDir, {
|
|
154
|
+
category: "animals",
|
|
155
|
+
slug: "meerkat",
|
|
156
|
+
title: "Meerkat",
|
|
157
|
+
content: "Social.",
|
|
158
|
+
});
|
|
159
|
+
const entries = await listEntries(dataDir, "animals");
|
|
160
|
+
expect(entries.map((e) => e.title)).toEqual([
|
|
161
|
+
"Aardvark",
|
|
162
|
+
"Meerkat",
|
|
163
|
+
"Zebra",
|
|
164
|
+
]);
|
|
165
|
+
});
|
|
166
|
+
it("excludes INDEX.md from results", async () => {
|
|
167
|
+
await writeEntry(dataDir, {
|
|
168
|
+
category: "test-cat",
|
|
169
|
+
slug: "entry",
|
|
170
|
+
title: "Entry",
|
|
171
|
+
content: "Content.",
|
|
172
|
+
});
|
|
173
|
+
await generateIndex(dataDir, "test-cat");
|
|
174
|
+
const entries = await listEntries(dataDir, "test-cat");
|
|
175
|
+
expect(entries).toHaveLength(1);
|
|
176
|
+
expect(entries[0].slug).toBe("entry");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// listCategories
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
describe("listCategories", () => {
|
|
183
|
+
it("returns categories with entry counts", async () => {
|
|
184
|
+
await writeEntry(dataDir, {
|
|
185
|
+
category: "alpha",
|
|
186
|
+
slug: "a1",
|
|
187
|
+
title: "A1",
|
|
188
|
+
content: ".",
|
|
189
|
+
});
|
|
190
|
+
await writeEntry(dataDir, {
|
|
191
|
+
category: "alpha",
|
|
192
|
+
slug: "a2",
|
|
193
|
+
title: "A2",
|
|
194
|
+
content: ".",
|
|
195
|
+
});
|
|
196
|
+
await writeEntry(dataDir, {
|
|
197
|
+
category: "beta",
|
|
198
|
+
slug: "b1",
|
|
199
|
+
title: "B1",
|
|
200
|
+
content: ".",
|
|
201
|
+
});
|
|
202
|
+
const categories = await listCategories(dataDir);
|
|
203
|
+
expect(categories).toEqual([
|
|
204
|
+
{ name: "alpha", entryCount: 2 },
|
|
205
|
+
{ name: "beta", entryCount: 1 },
|
|
206
|
+
]);
|
|
207
|
+
});
|
|
208
|
+
it("includes nested categories", async () => {
|
|
209
|
+
await writeEntry(dataDir, {
|
|
210
|
+
category: "social-media/instagram",
|
|
211
|
+
slug: "reels",
|
|
212
|
+
title: "Reels",
|
|
213
|
+
content: "Short video.",
|
|
214
|
+
});
|
|
215
|
+
const categories = await listCategories(dataDir);
|
|
216
|
+
expect(categories).toEqual([
|
|
217
|
+
{ name: "social-media/instagram", entryCount: 1 },
|
|
218
|
+
]);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// createCategory
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
describe("createCategory", () => {
|
|
225
|
+
it("creates the directory", async () => {
|
|
226
|
+
await createCategory(dataDir, "new-category");
|
|
227
|
+
const { readdir } = await import("node:fs/promises");
|
|
228
|
+
const entries = await readdir(join(dataDir, "new-category"));
|
|
229
|
+
expect(entries).toEqual([]);
|
|
230
|
+
});
|
|
231
|
+
it("supports nested paths", async () => {
|
|
232
|
+
await createCategory(dataDir, "parent/child/grandchild");
|
|
233
|
+
const { stat } = await import("node:fs/promises");
|
|
234
|
+
const info = await stat(join(dataDir, "parent/child/grandchild"));
|
|
235
|
+
expect(info.isDirectory()).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
// generateIndex
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
describe("generateIndex", () => {
|
|
242
|
+
it("generates an INDEX.md with entry titles and dates", async () => {
|
|
243
|
+
await writeEntry(dataDir, {
|
|
244
|
+
category: "cooking",
|
|
245
|
+
slug: "pasta",
|
|
246
|
+
title: "Pasta Recipe",
|
|
247
|
+
content: "Boil water.",
|
|
248
|
+
});
|
|
249
|
+
await writeEntry(dataDir, {
|
|
250
|
+
category: "cooking",
|
|
251
|
+
slug: "salad",
|
|
252
|
+
title: "Salad Recipe",
|
|
253
|
+
content: "Chop greens.",
|
|
254
|
+
});
|
|
255
|
+
const content = await generateIndex(dataDir, "cooking");
|
|
256
|
+
expect(content).toContain("# cooking");
|
|
257
|
+
expect(content).toContain("## Entries");
|
|
258
|
+
expect(content).toContain("| [Pasta Recipe](pasta.md)");
|
|
259
|
+
expect(content).toContain("| [Salad Recipe](salad.md)");
|
|
260
|
+
expect(content).toContain("Title");
|
|
261
|
+
expect(content).toContain("Updated");
|
|
262
|
+
// Verify it was written to disk
|
|
263
|
+
const onDisk = await readFile(join(dataDir, "cooking", "INDEX.md"), "utf-8");
|
|
264
|
+
expect(onDisk).toBe(content);
|
|
265
|
+
});
|
|
266
|
+
it("lists subcategories when present", async () => {
|
|
267
|
+
await writeEntry(dataDir, {
|
|
268
|
+
category: "social-media",
|
|
269
|
+
slug: "overview",
|
|
270
|
+
title: "Overview",
|
|
271
|
+
content: "Platforms.",
|
|
272
|
+
});
|
|
273
|
+
await createCategory(dataDir, "social-media/instagram");
|
|
274
|
+
await createCategory(dataDir, "social-media/twitter");
|
|
275
|
+
const content = await generateIndex(dataDir, "social-media");
|
|
276
|
+
expect(content).toContain("## Subcategories");
|
|
277
|
+
expect(content).toContain("[instagram/](instagram/INDEX.md)");
|
|
278
|
+
expect(content).toContain("[twitter/](twitter/INDEX.md)");
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
// ---------------------------------------------------------------------------
|
|
282
|
+
// generateIndexRecursive
|
|
283
|
+
// ---------------------------------------------------------------------------
|
|
284
|
+
describe("generateIndexRecursive", () => {
|
|
285
|
+
it("generates INDEX.md files for nested categories", async () => {
|
|
286
|
+
await writeEntry(dataDir, {
|
|
287
|
+
category: "social-media",
|
|
288
|
+
slug: "overview",
|
|
289
|
+
title: "Social Media Overview",
|
|
290
|
+
content: "Many platforms.",
|
|
291
|
+
});
|
|
292
|
+
await writeEntry(dataDir, {
|
|
293
|
+
category: "social-media/instagram",
|
|
294
|
+
slug: "reels",
|
|
295
|
+
title: "Instagram Reels",
|
|
296
|
+
content: "Short video format.",
|
|
297
|
+
});
|
|
298
|
+
await writeEntry(dataDir, {
|
|
299
|
+
category: "social-media/instagram",
|
|
300
|
+
slug: "stories",
|
|
301
|
+
title: "Instagram Stories",
|
|
302
|
+
content: "24-hour content.",
|
|
303
|
+
});
|
|
304
|
+
await generateIndexRecursive(dataDir, "social-media");
|
|
305
|
+
// Parent index
|
|
306
|
+
const parentIndex = await readFile(join(dataDir, "social-media", "INDEX.md"), "utf-8");
|
|
307
|
+
expect(parentIndex).toContain("# social-media");
|
|
308
|
+
expect(parentIndex).toContain("[instagram/](instagram/INDEX.md)");
|
|
309
|
+
expect(parentIndex).toContain("[Social Media Overview](overview.md)");
|
|
310
|
+
// Child index
|
|
311
|
+
const childIndex = await readFile(join(dataDir, "social-media", "instagram", "INDEX.md"), "utf-8");
|
|
312
|
+
expect(childIndex).toContain("# social-media/instagram");
|
|
313
|
+
expect(childIndex).toContain("[Instagram Reels](reels.md)");
|
|
314
|
+
expect(childIndex).toContain("[Instagram Stories](stories.md)");
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
//# sourceMappingURL=knowledge-tree.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"knowledge-tree.test.js","sourceRoot":"","sources":["../../../src/knowledge-tree/__tests__/knowledge-tree.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown + YAML frontmatter serialization / deserialization.
|
|
3
|
+
*/
|
|
4
|
+
import type { KnowledgeFrontmatter } from "./types.js";
|
|
5
|
+
/** Serialize frontmatter + content into a markdown string. */
|
|
6
|
+
export declare function serialize(frontmatter: KnowledgeFrontmatter, content: string): string;
|
|
7
|
+
/** Parse a markdown string into frontmatter + content. */
|
|
8
|
+
export declare function deserialize(raw: string): {
|
|
9
|
+
frontmatter: KnowledgeFrontmatter;
|
|
10
|
+
content: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Convert an arbitrary string into a filesystem-safe slug.
|
|
14
|
+
* Lowercases, replaces non-alphanumeric runs with hyphens, trims hyphens.
|
|
15
|
+
*/
|
|
16
|
+
export declare function slugify(input: string): string;
|
|
17
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/knowledge-tree/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAIvD,8DAA8D;AAC9D,wBAAgB,SAAS,CACvB,WAAW,EAAE,oBAAoB,EACjC,OAAO,EAAE,MAAM,GACd,MAAM,CAIR;AAED,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG;IACxC,WAAW,EAAE,oBAAoB,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;CACjB,CA8BA;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK7C"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown + YAML frontmatter serialization / deserialization.
|
|
3
|
+
*/
|
|
4
|
+
import { stringify as yamlStringify, parse as yamlParse } from "yaml";
|
|
5
|
+
const FRONTMATTER_FENCE = "---";
|
|
6
|
+
/** Serialize frontmatter + content into a markdown string. */
|
|
7
|
+
export function serialize(frontmatter, content) {
|
|
8
|
+
const yaml = yamlStringify(frontmatter).trimEnd();
|
|
9
|
+
const body = content.endsWith("\n") ? content : content + "\n";
|
|
10
|
+
return `${FRONTMATTER_FENCE}\n${yaml}\n${FRONTMATTER_FENCE}\n\n${body}`;
|
|
11
|
+
}
|
|
12
|
+
/** Parse a markdown string into frontmatter + content. */
|
|
13
|
+
export function deserialize(raw) {
|
|
14
|
+
const lines = raw.split("\n");
|
|
15
|
+
if (lines[0]?.trim() !== FRONTMATTER_FENCE) {
|
|
16
|
+
throw new Error("Missing opening frontmatter fence (---)");
|
|
17
|
+
}
|
|
18
|
+
let closingIndex = -1;
|
|
19
|
+
for (let i = 1; i < lines.length; i++) {
|
|
20
|
+
if (lines[i]?.trim() === FRONTMATTER_FENCE) {
|
|
21
|
+
closingIndex = i;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (closingIndex === -1) {
|
|
26
|
+
throw new Error("Missing closing frontmatter fence (---)");
|
|
27
|
+
}
|
|
28
|
+
const yamlBlock = lines.slice(1, closingIndex).join("\n");
|
|
29
|
+
const frontmatter = yamlParse(yamlBlock);
|
|
30
|
+
// Content starts after the closing fence + one blank line
|
|
31
|
+
let contentStart = closingIndex + 1;
|
|
32
|
+
if (lines[contentStart]?.trim() === "") {
|
|
33
|
+
contentStart++;
|
|
34
|
+
}
|
|
35
|
+
const content = lines.slice(contentStart).join("\n");
|
|
36
|
+
return { frontmatter, content };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert an arbitrary string into a filesystem-safe slug.
|
|
40
|
+
* Lowercases, replaces non-alphanumeric runs with hyphens, trims hyphens.
|
|
41
|
+
*/
|
|
42
|
+
export function slugify(input) {
|
|
43
|
+
return input
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
46
|
+
.replace(/^-+|-+$/g, "");
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/knowledge-tree/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAGtE,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEhC,8DAA8D;AAC9D,MAAM,UAAU,SAAS,CACvB,WAAiC,EACjC,OAAe;IAEf,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;IAClD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC;IAC/D,OAAO,GAAG,iBAAiB,KAAK,IAAI,KAAK,iBAAiB,OAAO,IAAI,EAAE,CAAC;AAC1E,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,WAAW,CAAC,GAAW;IAIrC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE9B,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,iBAAiB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,iBAAiB,EAAE,CAAC;YAC3C,YAAY,GAAG,CAAC,CAAC;YACjB,MAAM;QACR,CAAC;IACH,CAAC;IAED,IAAI,YAAY,KAAK,CAAC,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,SAAS,CAAC,SAAS,CAAyB,CAAC;IAEjE,0DAA0D;IAC1D,IAAI,YAAY,GAAG,YAAY,GAAG,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACvC,YAAY,EAAE,CAAC;IACjB,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAErD,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Knowledge Tree — hierarchical filesystem for YAML-frontmatter markdown entries.
|
|
3
|
+
*
|
|
4
|
+
* Stores knowledge as:
|
|
5
|
+
* {dataDir}/{category}/{slug}.md
|
|
6
|
+
*
|
|
7
|
+
* Each category can optionally contain an auto-generated INDEX.md.
|
|
8
|
+
* Nested categories (e.g., "social-media/instagram") are supported.
|
|
9
|
+
*/
|
|
10
|
+
import type { KnowledgeEntry, WriteEntryInput, CategoryInfo } from "./types.js";
|
|
11
|
+
export { slugify } from "./format.js";
|
|
12
|
+
export type { KnowledgeEntry, WriteEntryInput, CategoryInfo, KnowledgeFrontmatter, } from "./types.js";
|
|
13
|
+
/** Create the root knowledge directory if it doesn't exist. */
|
|
14
|
+
export declare function initKnowledgeTree(dataDir: string): Promise<void>;
|
|
15
|
+
/** Write (create or update) a knowledge entry. Returns the resolved slug. */
|
|
16
|
+
export declare function writeEntry(dataDir: string, input: WriteEntryInput): Promise<string>;
|
|
17
|
+
/** Read and parse a single knowledge entry. */
|
|
18
|
+
export declare function readEntry(dataDir: string, category: string, slug: string): Promise<KnowledgeEntry>;
|
|
19
|
+
/** Delete a knowledge entry. */
|
|
20
|
+
export declare function removeEntry(dataDir: string, category: string, slug: string): Promise<void>;
|
|
21
|
+
/** List all entries in a category, sorted by title. */
|
|
22
|
+
export declare function listEntries(dataDir: string, category: string): Promise<KnowledgeEntry[]>;
|
|
23
|
+
/** List all category directories (recursively) with entry counts. */
|
|
24
|
+
export declare function listCategories(dataDir: string): Promise<CategoryInfo[]>;
|
|
25
|
+
/** Create a new category directory. */
|
|
26
|
+
export declare function createCategory(dataDir: string, name: string): Promise<void>;
|
|
27
|
+
/** Generate an INDEX.md for a category listing its entries. */
|
|
28
|
+
export declare function generateIndex(dataDir: string, category: string): Promise<string>;
|
|
29
|
+
/**
|
|
30
|
+
* Recursively generate INDEX.md files for a category and all its subcategories.
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateIndexRecursive(dataDir: string, category: string): Promise<void>;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/knowledge-tree/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,YAAY,EAEb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,YAAY,EACV,cAAc,EACd,eAAe,EACf,YAAY,EACZ,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAMpB,+DAA+D;AAC/D,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAMD,6EAA6E;AAC7E,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,MAAM,CAAC,CA6BjB;AAED,+CAA+C;AAC/C,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,cAAc,CAAC,CAezB;AAED,gCAAgC;AAChC,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAGf;AAMD,uDAAuD;AACvD,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,EAAE,CAAC,CAe3B;AAED,qEAAqE;AACrE,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,EAAE,CAAC,CAIzB;AAkCD,uCAAuC;AACvC,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAEf;AAMD,+DAA+D;AAC/D,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAoCjB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Knowledge Tree — hierarchical filesystem for YAML-frontmatter markdown entries.
|
|
3
|
+
*
|
|
4
|
+
* Stores knowledge as:
|
|
5
|
+
* {dataDir}/{category}/{slug}.md
|
|
6
|
+
*
|
|
7
|
+
* Each category can optionally contain an auto-generated INDEX.md.
|
|
8
|
+
* Nested categories (e.g., "social-media/instagram") are supported.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdir, readdir, readFile, writeFile, rm } from "node:fs/promises";
|
|
11
|
+
import { join, relative, sep } from "node:path";
|
|
12
|
+
import { serialize, deserialize, slugify } from "./format.js";
|
|
13
|
+
export { slugify } from "./format.js";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Init
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/** Create the root knowledge directory if it doesn't exist. */
|
|
18
|
+
export async function initKnowledgeTree(dataDir) {
|
|
19
|
+
await mkdir(dataDir, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Write / Read / Remove
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
/** Write (create or update) a knowledge entry. Returns the resolved slug. */
|
|
25
|
+
export async function writeEntry(dataDir, input) {
|
|
26
|
+
const entrySlug = input.slug ?? slugify(input.title);
|
|
27
|
+
const categoryDir = join(dataDir, input.category);
|
|
28
|
+
await mkdir(categoryDir, { recursive: true });
|
|
29
|
+
const now = new Date().toISOString();
|
|
30
|
+
const filePath = join(categoryDir, `${entrySlug}.md`);
|
|
31
|
+
// Preserve original created date if file already exists
|
|
32
|
+
let created = now;
|
|
33
|
+
try {
|
|
34
|
+
const existing = await readFile(filePath, "utf-8");
|
|
35
|
+
const parsed = deserialize(existing);
|
|
36
|
+
created = parsed.frontmatter.created;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// New file — use now
|
|
40
|
+
}
|
|
41
|
+
const frontmatter = {
|
|
42
|
+
title: input.title,
|
|
43
|
+
tags: input.tags ?? [],
|
|
44
|
+
...(input.source != null ? { source: input.source } : {}),
|
|
45
|
+
created,
|
|
46
|
+
updated: now,
|
|
47
|
+
};
|
|
48
|
+
const markdown = serialize(frontmatter, input.content);
|
|
49
|
+
await writeFile(filePath, markdown, "utf-8");
|
|
50
|
+
return entrySlug;
|
|
51
|
+
}
|
|
52
|
+
/** Read and parse a single knowledge entry. */
|
|
53
|
+
export async function readEntry(dataDir, category, slug) {
|
|
54
|
+
const filePath = join(dataDir, category, `${slug}.md`);
|
|
55
|
+
const raw = await readFile(filePath, "utf-8");
|
|
56
|
+
const { frontmatter, content } = deserialize(raw);
|
|
57
|
+
return {
|
|
58
|
+
category,
|
|
59
|
+
slug,
|
|
60
|
+
title: frontmatter.title,
|
|
61
|
+
tags: frontmatter.tags ?? [],
|
|
62
|
+
source: frontmatter.source,
|
|
63
|
+
created: frontmatter.created,
|
|
64
|
+
updated: frontmatter.updated,
|
|
65
|
+
content,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Delete a knowledge entry. */
|
|
69
|
+
export async function removeEntry(dataDir, category, slug) {
|
|
70
|
+
const filePath = join(dataDir, category, `${slug}.md`);
|
|
71
|
+
await rm(filePath);
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// List
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/** List all entries in a category, sorted by title. */
|
|
77
|
+
export async function listEntries(dataDir, category) {
|
|
78
|
+
const categoryDir = join(dataDir, category);
|
|
79
|
+
const files = await readdir(categoryDir);
|
|
80
|
+
const mdFiles = files.filter((f) => f.endsWith(".md") && f !== "INDEX.md");
|
|
81
|
+
const entries = [];
|
|
82
|
+
for (const file of mdFiles) {
|
|
83
|
+
const slug = file.replace(/\.md$/, "");
|
|
84
|
+
const entry = await readEntry(dataDir, category, slug);
|
|
85
|
+
entries.push(entry);
|
|
86
|
+
}
|
|
87
|
+
return entries.sort((a, b) => a.title.localeCompare(b.title));
|
|
88
|
+
}
|
|
89
|
+
/** List all category directories (recursively) with entry counts. */
|
|
90
|
+
export async function listCategories(dataDir) {
|
|
91
|
+
const results = [];
|
|
92
|
+
await walkCategories(dataDir, dataDir, results);
|
|
93
|
+
return results.sort((a, b) => a.name.localeCompare(b.name));
|
|
94
|
+
}
|
|
95
|
+
async function walkCategories(rootDir, currentDir, results) {
|
|
96
|
+
let entries;
|
|
97
|
+
try {
|
|
98
|
+
entries = await readdir(currentDir, { withFileTypes: true });
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const mdFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "INDEX.md");
|
|
104
|
+
const subdirs = entries.filter((e) => e.isDirectory());
|
|
105
|
+
// Only include this directory as a category if it has .md files
|
|
106
|
+
if (mdFiles.length > 0) {
|
|
107
|
+
const categoryName = relative(rootDir, currentDir).split(sep).join("/");
|
|
108
|
+
results.push({ name: categoryName, entryCount: mdFiles.length });
|
|
109
|
+
}
|
|
110
|
+
for (const dir of subdirs) {
|
|
111
|
+
await walkCategories(rootDir, join(currentDir, dir.name), results);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Category management
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
/** Create a new category directory. */
|
|
118
|
+
export async function createCategory(dataDir, name) {
|
|
119
|
+
await mkdir(join(dataDir, name), { recursive: true });
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Index generation
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
/** Generate an INDEX.md for a category listing its entries. */
|
|
125
|
+
export async function generateIndex(dataDir, category) {
|
|
126
|
+
const categoryDir = join(dataDir, category);
|
|
127
|
+
const entries = await listEntries(dataDir, category);
|
|
128
|
+
// Check for subcategories
|
|
129
|
+
const dirEntries = await readdir(categoryDir, { withFileTypes: true });
|
|
130
|
+
const subdirs = dirEntries.filter((e) => e.isDirectory());
|
|
131
|
+
const lines = [];
|
|
132
|
+
lines.push(`# ${category}`);
|
|
133
|
+
lines.push("");
|
|
134
|
+
if (subdirs.length > 0) {
|
|
135
|
+
lines.push("## Subcategories");
|
|
136
|
+
lines.push("");
|
|
137
|
+
for (const dir of subdirs.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
138
|
+
lines.push(`- [${dir.name}/](${dir.name}/INDEX.md)`);
|
|
139
|
+
}
|
|
140
|
+
lines.push("");
|
|
141
|
+
}
|
|
142
|
+
if (entries.length > 0) {
|
|
143
|
+
lines.push("## Entries");
|
|
144
|
+
lines.push("");
|
|
145
|
+
lines.push("| Title | Updated |");
|
|
146
|
+
lines.push("|-------|---------|");
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
const dateStr = entry.updated.slice(0, 10);
|
|
149
|
+
lines.push(`| [${entry.title}](${entry.slug}.md) | ${dateStr} |`);
|
|
150
|
+
}
|
|
151
|
+
lines.push("");
|
|
152
|
+
}
|
|
153
|
+
const content = lines.join("\n");
|
|
154
|
+
await writeFile(join(categoryDir, "INDEX.md"), content, "utf-8");
|
|
155
|
+
return content;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Recursively generate INDEX.md files for a category and all its subcategories.
|
|
159
|
+
*/
|
|
160
|
+
export async function generateIndexRecursive(dataDir, category) {
|
|
161
|
+
const categoryDir = join(dataDir, category);
|
|
162
|
+
let dirEntries;
|
|
163
|
+
try {
|
|
164
|
+
dirEntries = await readdir(categoryDir, { withFileTypes: true });
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// Check if this directory has any .md files or subdirectories worth indexing
|
|
170
|
+
const hasMdFiles = dirEntries.some((e) => e.isFile() && e.name.endsWith(".md") && e.name !== "INDEX.md");
|
|
171
|
+
const subdirs = dirEntries.filter((e) => e.isDirectory());
|
|
172
|
+
// Recurse into subdirectories first
|
|
173
|
+
for (const dir of subdirs) {
|
|
174
|
+
const subCategory = category ? `${category}/${dir.name}` : dir.name;
|
|
175
|
+
await generateIndexRecursive(dataDir, subCategory);
|
|
176
|
+
}
|
|
177
|
+
// Generate index if there are entries or subcategories
|
|
178
|
+
if (hasMdFiles || subdirs.length > 0) {
|
|
179
|
+
await generateIndex(dataDir, category);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/knowledge-tree/index.ts"],"names":[],"mappings":"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"}
|