@chronova/wiki-agent 1.1.4

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/README.md ADDED
@@ -0,0 +1,248 @@
1
+ # Wiki Agent
2
+
3
+ A standalone Ollama-only documentation agent. It inspects your source code and generates a wiki under `.wiki/` in your project root.
4
+
5
+ ## Features
6
+
7
+ - **Ollama-only** — uses the native `ollama` SDK, no LangChain dependency
8
+ - **Local or Cloud** — connect to a local Ollama server or Ollama Cloud with an API key
9
+ - **TUI + Headless** — interactive terminal UI or `--print` for CI/CD
10
+ - **Two commands** — `--init` to create docs from scratch, `--update` to refresh existing docs
11
+ - **Configurable** — global config in `~/.wiki/`, project config in `.wiki/`
12
+ - **GitHub Actions** — `--init` automatically creates a scheduled update workflow in your repo
13
+ - **Repo instructions** — reads `AGENTS.md` or `CLAUDE.md` from the project root and follows all conventions documented there
14
+ - **Change reports** — each run writes `.wiki/.last-update-report.md` with created/edited pages, used as the PR body in CI
15
+ - **Self-invocation guard** — the agent cannot recursively invoke `wiki` or `wiki-agent` via shell commands
16
+
17
+ ## Quickstart
18
+
19
+ ### 1. Install
20
+
21
+ Build from source and install globally with bun:
22
+
23
+ ```bash
24
+ git clone https://github.com/nx-solutions-ug/wiki-agent.git
25
+ cd wiki-agent
26
+ npm install
27
+ npx tsc -p tsconfig.json
28
+ bun pm pack
29
+ cd ~/.bun/install/global && bun add /path/to/wiki-agent/wiki-agent-0.1.0.tgz
30
+ ```
31
+
32
+ Verify the install:
33
+
34
+ ```bash
35
+ wiki --help
36
+ ```
37
+
38
+ ### 2. Configure
39
+
40
+ Run interactively once to set up credentials:
41
+
42
+ ```bash
43
+ cd your-project
44
+ wiki --init
45
+ ```
46
+
47
+ This launches the TUI where you select Ollama Local or Cloud and enter your API key (if cloud). The default model is `kimi-k2.7-code`.
48
+
49
+ ### 3. Use
50
+
51
+ ```bash
52
+ # Initialize documentation (also creates .github/workflows/update-wiki.yml)
53
+ wiki --init
54
+
55
+ # Update existing documentation
56
+ wiki --update
57
+
58
+ # Headless mode (for CI)
59
+ wiki --update --print
60
+
61
+ # Specify a model override
62
+ wiki --init --print --model llama3.2
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ ### Global config (`~/.wiki/config.json`)
68
+
69
+ ```json
70
+ {
71
+ "mode": "local",
72
+ "defaultModel": "kimi-k2.7-code"
73
+ }
74
+ ```
75
+
76
+ For cloud:
77
+
78
+ ```json
79
+ {
80
+ "mode": "cloud",
81
+ "apiKey": "your-api-key",
82
+ "defaultModel": "kimi-k2.7-code"
83
+ }
84
+ ```
85
+
86
+ ### Project config (`.wiki/config.json`)
87
+
88
+ ```json
89
+ {
90
+ "modelOverride": "llama3.2",
91
+ "lastUpdate": {
92
+ "commitSha": "abc1234",
93
+ "timestamp": "2026-07-17T00:00:00Z"
94
+ }
95
+ }
96
+ ```
97
+
98
+ ### Environment variables
99
+
100
+ | Variable | Description | Default |
101
+ |----------|-------------|---------|
102
+ | `WIKI_OLLAMA_MODE` | `"local"` or `"cloud"` | from config |
103
+ | `WIKI_OLLAMA_API_KEY` | API key for cloud mode | from config |
104
+ | `WIKI_OLLAMA_BASE_URL` | Override Ollama server URL | `http://localhost:11434` / `https://ollama.com` |
105
+ | `WIKI_MODEL` | Override model ID | from config |
106
+ | `WIKI_RECURSION_LIMIT` | Max agent iterations | `200` |
107
+
108
+ Environment variables take priority over config files.
109
+
110
+ ## GitHub Actions
111
+
112
+ Running `wiki --init` automatically creates `.github/workflows/update-wiki.yml` in your repo. The workflow:
113
+
114
+ 1. Generates a GitHub App token if `APP_CLIENT_ID` and `APP_PRIVATE_KEY` secrets are set (falls back to `GITHUB_TOKEN`)
115
+ 2. Checks out your repo
116
+ 3. Clones and builds wiki-agent from `nx-solutions-ug/wiki-agent`
117
+ 4. Runs `wiki --update --print` with your Ollama Cloud credentials
118
+ 5. Reads `.wiki/.last-update-report.md` and uses it as the PR body
119
+ 6. Creates a pull request with the `.wiki/` changes
120
+
121
+ ```yaml
122
+ name: Wiki Update
123
+ on:
124
+ workflow_dispatch:
125
+ push:
126
+ branches:
127
+ - main
128
+ schedule:
129
+ - cron: "0 8 * * *"
130
+ permissions:
131
+ contents: write
132
+ pull-requests: write
133
+ jobs:
134
+ update:
135
+ runs-on: ubuntu-latest
136
+ steps:
137
+ - name: Generate token
138
+ id: token
139
+ uses: actions/create-github-app-token@v3
140
+ with:
141
+ client-id: ${{ secrets.APP_CLIENT_ID }}
142
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
143
+ continue-on-error: true
144
+
145
+ - uses: actions/checkout@v7
146
+ with:
147
+ token: ${{ steps.token.outputs.token || secrets.GITHUB_TOKEN }}
148
+
149
+ - uses: actions/setup-node@v7
150
+ with:
151
+ node-version: "25"
152
+
153
+ - name: Build Wiki Agent
154
+ run: |
155
+ git clone --branch main --depth 1 https://github.com/nx-solutions-ug/wiki-agent.git /tmp/wiki-agent
156
+ cd /tmp/wiki-agent
157
+ npm install
158
+ npx tsc -p tsconfig.json
159
+
160
+ - name: Run Wiki Agent
161
+ run: node /tmp/wiki-agent/dist/cli.js --update --print
162
+ env:
163
+ WIKI_OLLAMA_MODE: cloud
164
+ WIKI_OLLAMA_API_KEY: ${{ secrets.WIKI_OLLAMA_API_KEY }}
165
+ WIKI_MODEL: ${{ vars.WIKI_MODEL || 'kimi-k2.7-code' }}
166
+
167
+ - name: Generate timestamp
168
+ id: timestamp
169
+ run: echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT
170
+
171
+ - name: Read update report
172
+ id: report
173
+ run: |
174
+ if [ -f .wiki/.last-update-report.md ]; then
175
+ echo "body<<EOF" >> $GITHUB_OUTPUT
176
+ cat .wiki/.last-update-report.md >> $GITHUB_OUTPUT
177
+ echo "EOF" >> $GITHUB_OUTPUT
178
+ else
179
+ echo "body=Automated wiki documentation update." >> $GITHUB_OUTPUT
180
+ fi
181
+
182
+ - uses: peter-evans/create-pull-request@v8
183
+ with:
184
+ token: ${{ steps.token.outputs.token || secrets.GITHUB_TOKEN }}
185
+ add-paths: .wiki
186
+ branch: wiki/update-${{ steps.timestamp.outputs.timestamp }}
187
+ commit-message: "docs: update wiki"
188
+ title: "docs: update wiki"
189
+ body: ${{ steps.report.outputs.body }}
190
+ ```
191
+
192
+ ### Required secrets
193
+
194
+ | Secret | Required | Description |
195
+ |--------|----------|-------------|
196
+ | `WIKI_OLLAMA_API_KEY` | Yes | Ollama Cloud API key |
197
+ | `APP_CLIENT_ID` | No | GitHub App client ID for token generation (falls back to `GITHUB_TOKEN`) |
198
+ | `APP_PRIVATE_KEY` | No | GitHub App private key |
199
+
200
+ ### Optional variables
201
+
202
+ | Variable | Default | Description |
203
+ |----------|---------|-------------|
204
+ | `WIKI_MODEL` | `kimi-k2.7-code` | Model ID override |
205
+
206
+ ## Output
207
+
208
+ Each run produces:
209
+
210
+ ```
211
+ .wiki/
212
+ ├── .last-updated.json # ISO timestamp of last run
213
+ ├── .last-update-report.md # Change report (created/edited pages)
214
+ ├── config.json # Project-specific config
215
+ ├── quickstart.md # Entry point
216
+ ├── architecture/
217
+ │ ├── index.md # Auto-generated directory index
218
+ │ └── overview.md
219
+ ├── cli/
220
+ │ ├── index.md
221
+ │ └── usage.md
222
+ └── index.md # Root directory index
223
+ ```
224
+
225
+ - `.last-updated.json` — `{ "lastUpdated": "2026-07-17T10:30:00.000Z" }`
226
+ - `.last-update-report.md` — markdown report listing created and edited pages, used as the PR body in CI
227
+ - `index.md` files — auto-generated for each directory, listing pages and subdirectories with frontmatter titles/descriptions
228
+
229
+ ## Development
230
+
231
+ ```bash
232
+ bun install
233
+ npx tsc -p tsconfig.json
234
+ npx vitest run
235
+ bun pm pack
236
+ ```
237
+
238
+ ## How it works
239
+
240
+ 1. The agent reads `AGENTS.md` or `CLAUDE.md` from the project root and follows all conventions documented there
241
+ 2. It inspects your source code using filesystem tools (read, grep, glob, execute)
242
+ 3. It generates wiki pages under `.wiki/` with YAML frontmatter
243
+ 4. After the run, `index.md` files are synchronized for each directory
244
+ 5. `.last-updated.json` and `.last-update-report.md` are written
245
+ 6. On `--init`, a GitHub Actions workflow is created for scheduled updates
246
+ 7. In update mode, only pages affected by recent changes are refreshed
247
+
248
+ The agent uses a manual tool-calling loop with the Ollama chat API — no LangChain or LangGraph dependency. The recursion limit prevents infinite loops. The `execute` tool blocks self-invocation to prevent recursive runs.
@@ -0,0 +1,35 @@
1
+ import { type WikiCommand } from "./prompt.js";
2
+ import { Ollama } from "ollama";
3
+ export type AgentEvent = {
4
+ type: "assistant";
5
+ content: string;
6
+ } | {
7
+ type: "tool";
8
+ name: string;
9
+ result: string;
10
+ } | {
11
+ type: "error";
12
+ message: string;
13
+ } | {
14
+ type: "done";
15
+ summary: string;
16
+ };
17
+ export interface RunOptions {
18
+ command: WikiCommand;
19
+ projectRoot: string;
20
+ model: string;
21
+ gitSummary?: string;
22
+ maxIterations?: number;
23
+ stream?: boolean;
24
+ onEvent?: (event: AgentEvent) => void;
25
+ }
26
+ export declare function runAgent(client: Ollama, options: RunOptions): Promise<void>;
27
+ /**
28
+ * Generates a markdown report of what changed during this run.
29
+ * Written to .wiki/.last-update-report.md and used as the PR body.
30
+ */
31
+ export declare function generateUpdateReport(command: WikiCommand, changedFiles: {
32
+ action: string;
33
+ path: string;
34
+ description?: string;
35
+ }[]): string;
package/dist/agent.js ADDED
@@ -0,0 +1,317 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { createSystemPrompt, createUserMessage } from "./prompt.js";
4
+ import { createTools, executeTool } from "./tools.js";
5
+ import { synchronizeWikiIndexes } from "./index-middleware.js";
6
+ const DEFAULT_MAX_ITERATIONS = 200;
7
+ function resolveMaxIterations() {
8
+ const env = process.env.WIKI_RECURSION_LIMIT;
9
+ if (!env) {
10
+ return DEFAULT_MAX_ITERATIONS;
11
+ }
12
+ const parsed = Number.parseInt(env, 10);
13
+ if (Number.isSafeInteger(parsed) && parsed > 0) {
14
+ return parsed;
15
+ }
16
+ return DEFAULT_MAX_ITERATIONS;
17
+ }
18
+ /**
19
+ * Normalizes tool call arguments to an object. The Ollama API returns
20
+ * arguments as a JSON string or as a parsed object depending on the model —
21
+ * handle both.
22
+ */
23
+ function normalizeToolCallArgs(args) {
24
+ if (args === null || args === undefined) {
25
+ return {};
26
+ }
27
+ if (typeof args === "string") {
28
+ try {
29
+ const parsed = JSON.parse(args);
30
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
31
+ return parsed;
32
+ }
33
+ }
34
+ catch {
35
+ // Malformed JSON — return empty so the tool gets called with no args
36
+ return {};
37
+ }
38
+ return {};
39
+ }
40
+ if (typeof args === "object" && !Array.isArray(args)) {
41
+ return args;
42
+ }
43
+ return {};
44
+ }
45
+ export async function runAgent(client, options) {
46
+ const { command, projectRoot, model, gitSummary, maxIterations, stream = false, onEvent = () => { }, } = options;
47
+ const maxIter = maxIterations ?? resolveMaxIterations();
48
+ const tools = createTools(projectRoot);
49
+ const systemPrompt = await createSystemPrompt(projectRoot);
50
+ const userMessage = createUserMessage(command, projectRoot, gitSummary);
51
+ const messages = [
52
+ { role: "system", content: systemPrompt },
53
+ { role: "user", content: userMessage },
54
+ ];
55
+ const changedFiles = [];
56
+ for (let i = 0; i < maxIter; i++) {
57
+ let assistantContent = "";
58
+ let toolCalls = [];
59
+ try {
60
+ if (stream) {
61
+ const streamResponse = await client.chat({
62
+ model,
63
+ messages: messages,
64
+ tools: tools.map((t) => t.definition),
65
+ stream: true,
66
+ });
67
+ for await (const chunk of streamResponse) {
68
+ if (chunk.message?.content) {
69
+ assistantContent += chunk.message.content;
70
+ onEvent({ type: "assistant", content: chunk.message.content });
71
+ }
72
+ if (chunk.message?.tool_calls) {
73
+ for (const tc of chunk.message.tool_calls) {
74
+ if (tc.function?.name) {
75
+ toolCalls.push({
76
+ function: {
77
+ name: tc.function.name,
78
+ arguments: normalizeToolCallArgs(tc.function.arguments),
79
+ },
80
+ });
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ else {
87
+ const result = await client.chat({
88
+ model,
89
+ messages: messages,
90
+ tools: tools.map((t) => t.definition),
91
+ stream: false,
92
+ });
93
+ const msgContent = result.message?.content;
94
+ assistantContent = typeof msgContent === "string" ? msgContent : "";
95
+ onEvent({ type: "assistant", content: assistantContent });
96
+ if (result.message?.tool_calls) {
97
+ for (const tc of result.message.tool_calls) {
98
+ if (tc.function?.name) {
99
+ toolCalls.push({
100
+ function: {
101
+ name: tc.function.name,
102
+ arguments: normalizeToolCallArgs(tc.function.arguments),
103
+ },
104
+ });
105
+ }
106
+ }
107
+ }
108
+ }
109
+ }
110
+ catch (error) {
111
+ const message = error instanceof Error ? error.message : String(error);
112
+ if (assistantContent) {
113
+ onEvent({
114
+ type: "done",
115
+ summary: `Agent completed with API warning: ${message}`,
116
+ });
117
+ break;
118
+ }
119
+ onEvent({ type: "error", message });
120
+ break;
121
+ }
122
+ const assistantMessage = {
123
+ role: "assistant",
124
+ content: assistantContent,
125
+ ...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
126
+ };
127
+ messages.push(assistantMessage);
128
+ if (toolCalls.length === 0) {
129
+ break;
130
+ }
131
+ for (const toolCall of toolCalls) {
132
+ const toolName = toolCall.function.name;
133
+ const args = toolCall.function.arguments;
134
+ onEvent({ type: "tool", name: toolName, result: "" });
135
+ const result = await executeTool(toolName, args, projectRoot);
136
+ if (toolName === "write_file" || toolName === "edit_file") {
137
+ const filePath = typeof args.path === "string" ? args.path : "unknown";
138
+ if (result.startsWith("Wrote ") || result.startsWith("Edited ")) {
139
+ // Use the assistant's prose preceding this tool call as the
140
+ // human-readable description of what changed. Falls back to the
141
+ // tool result if the model didn't narrate the change.
142
+ const description = assistantContent.trim() || result;
143
+ changedFiles.push({
144
+ action: toolName === "write_file" ? "created" : "edited",
145
+ path: filePath,
146
+ description,
147
+ });
148
+ }
149
+ }
150
+ onEvent({ type: "tool", name: toolName, result });
151
+ // Ollama uses tool_name (not tool_call_id) to associate tool results
152
+ messages.push({
153
+ role: "tool",
154
+ content: result,
155
+ tool_name: toolName,
156
+ });
157
+ }
158
+ }
159
+ await createWorkflowFile(projectRoot);
160
+ onEvent({ type: "tool", name: "create_workflow", result: "Created .github/workflows/update-wiki.yml" });
161
+ if (changedFiles.length === 0) {
162
+ onEvent({ type: "done", summary: "Wiki is already current. No files changed." });
163
+ return;
164
+ }
165
+ await synchronizeWikiIndexes(path.join(projectRoot, ".wiki"));
166
+ await writeFile(path.join(projectRoot, ".wiki", ".last-updated.json"), JSON.stringify({ lastUpdated: new Date().toISOString() }, null, 2) + "\n", "utf8");
167
+ const report = generateUpdateReport(command, changedFiles);
168
+ await writeFile(path.join(projectRoot, ".wiki", ".last-update-report.md"), report, "utf8");
169
+ onEvent({ type: "done", summary: "Agent run complete" });
170
+ }
171
+ /**
172
+ * Creates a GitHub Actions workflow file in the target repo that checks out
173
+ * the wiki-agent source, builds it, and runs --update --print on a schedule.
174
+ */
175
+ async function createWorkflowFile(projectRoot) {
176
+ const workflowsDir = path.join(projectRoot, ".github", "workflows");
177
+ const workflowPath = path.join(workflowsDir, "update-wiki.yml");
178
+ await mkdir(workflowsDir, { recursive: true });
179
+ const workflow = [
180
+ "name: Wiki Update",
181
+ "",
182
+ "on:",
183
+ " workflow_dispatch:",
184
+ " push:",
185
+ " branches:",
186
+ " - main",
187
+ " schedule:",
188
+ ' - cron: "0 8 * * *"',
189
+ "",
190
+ "permissions:",
191
+ " contents: write",
192
+ " pull-requests: write",
193
+ "",
194
+ "jobs:",
195
+ " update:",
196
+ " runs-on: ubuntu-latest",
197
+ " steps:",
198
+ " - name: Generate token",
199
+ " id: token",
200
+ " uses: actions/create-github-app-token@v3",
201
+ " with:",
202
+ " client-id: ${{ secrets.APP_CLIENT_ID }}",
203
+ " private-key: ${{ secrets.APP_PRIVATE_KEY }}",
204
+ " continue-on-error: true",
205
+ "",
206
+ " - name: Check out repository",
207
+ " uses: actions/checkout@v7",
208
+ " with:",
209
+ " token: ${{ steps.token.outputs.token || secrets.GITHUB_TOKEN }}",
210
+ "",
211
+ " - name: Set up Node.js",
212
+ " uses: actions/setup-node@v7",
213
+ " with:",
214
+ ' node-version: "25"',
215
+ "",
216
+ " - name: Build Wiki Agent",
217
+ " run: |",
218
+ " git clone --branch main --depth 1 https://github.com/nx-solutions-ug/wiki-agent.git /tmp/wiki-agent",
219
+ " cd /tmp/wiki-agent",
220
+ " npm install",
221
+ " npx tsc -p tsconfig.json",
222
+ "",
223
+ " - name: Run Wiki Agent",
224
+ " run: node /tmp/wiki-agent/dist/cli.js --update --print --verbose",
225
+ " env:",
226
+ " WIKI_OLLAMA_MODE: cloud",
227
+ ' WIKI_OLLAMA_API_KEY: ${{ secrets.WIKI_OLLAMA_API_KEY }}',
228
+ " WIKI_MODEL: ${{ vars.WIKI_MODEL || 'kimi-k2.7-code' }}",
229
+ " - name: Generate timestamp",
230
+ " id: timestamp",
231
+ " run: echo \"timestamp=$(date +%s)\" >> $GITHUB_OUTPUT",
232
+ "",
233
+ " - name: Check for changes",
234
+ " id: report",
235
+ " run: |",
236
+ " # Collect changes under .wiki (tracked + untracked), excluding",
237
+ " # the run metadata files. Only content changes open a PR.",
238
+ " changes=$(git status --porcelain .wiki | sed 's/^...//' | grep -vE '^\\.wiki/\\.(last-update-report\\.md|last-updated\\.json)$' | sed '/^[[:space:]]*$/d')",
239
+ " if [ -n \"$changes\" ]; then",
240
+ " echo \"has_changes=true\" >> $GITHUB_OUTPUT",
241
+ " echo \"body<<EOF\" >> $GITHUB_OUTPUT",
242
+ " cat .wiki/.last-update-report.md >> $GITHUB_OUTPUT",
243
+ " echo \"\" >> $GITHUB_OUTPUT",
244
+ " echo \"EOF\" >> $GITHUB_OUTPUT",
245
+ " else",
246
+ " echo \"has_changes=false\" >> $GITHUB_OUTPUT",
247
+ " fi",
248
+ "",
249
+ " - name: Create Wiki update pull request",
250
+ " uses: peter-evans/create-pull-request@v8",
251
+ " if: steps.report.outputs.has_changes == 'true'",
252
+ " with:",
253
+ " token: ${{ steps.token.outputs.token || secrets.GITHUB_TOKEN }}",
254
+ " branch: wiki/update-${{ steps.timestamp.outputs.timestamp }}",
255
+ " add-paths: .wiki",
256
+ ' title: "docs: update wiki"',
257
+ ' body: ${{ steps.report.outputs.body }}',
258
+ ].join("\n");
259
+ await writeFile(workflowPath, workflow, "utf8");
260
+ }
261
+ /**
262
+ * Generates a markdown report of what changed during this run.
263
+ * Written to .wiki/.last-update-report.md and used as the PR body.
264
+ */
265
+ export function generateUpdateReport(command, changedFiles) {
266
+ const timestamp = new Date().toISOString();
267
+ const actionLabel = command === "init" ? "Initialized" : "Updated";
268
+ if (changedFiles.length === 0) {
269
+ return [
270
+ `# Wiki ${actionLabel}`,
271
+ "",
272
+ "No files were changed. The wiki is already current.",
273
+ ].join("\n") + "\n";
274
+ }
275
+ const created = changedFiles.filter((f) => f.action === "created");
276
+ const edited = changedFiles.filter((f) => f.action === "edited");
277
+ const lines = [
278
+ `# Wiki ${actionLabel}`,
279
+ "",
280
+ `Run completed at ${timestamp}.`,
281
+ "",
282
+ ];
283
+ if (created.length > 0) {
284
+ lines.push("## New pages", "");
285
+ for (const f of created) {
286
+ lines.push(`- \`${f.path}\``);
287
+ if (f.description && f.description.trim()) {
288
+ lines.push(...formatDescription(f.description));
289
+ }
290
+ }
291
+ lines.push("");
292
+ }
293
+ if (edited.length > 0) {
294
+ lines.push("## Updated pages", "");
295
+ for (const f of edited) {
296
+ lines.push(`- \`${f.path}\``);
297
+ if (f.description && f.description.trim()) {
298
+ lines.push(...formatDescription(f.description));
299
+ }
300
+ }
301
+ lines.push("");
302
+ }
303
+ lines.push("## Summary", "", `This ${command === "init" ? "initialization" : "update"} run ${created.length > 0 ? `created ${created.length} page${created.length > 1 ? "s" : ""}` : ""}${created.length > 0 && edited.length > 0 ? " and " : ""}${edited.length > 0 ? `edited ${edited.length} page${edited.length > 1 ? "s" : ""}` : ""}.`);
304
+ return lines.join("\n") + "\n";
305
+ }
306
+ /**
307
+ * Formats a change description as indented markdown under a file listing.
308
+ * Collapses whitespace, truncates overly long prose, and wraps it as a
309
+ * blockquote so it renders cleanly under the `- \`path\`` bullet.
310
+ */
311
+ function formatDescription(description) {
312
+ const trimmed = description.trim().replace(/\s+/g, " ");
313
+ const maxLen = 500;
314
+ const text = trimmed.length > maxLen ? trimmed.slice(0, maxLen) + "…" : trimmed;
315
+ // Indent under the bullet as a nested blockquote
316
+ return ["", ` > ${text}`, ""];
317
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};