docs: audit fixes - correct createPlugin signature, add store/runtime/tui docs, fix broken links#68
docs: audit fixes - correct createPlugin signature, add store/runtime/tui docs, fix broken links#68github-actions[bot] wants to merge 1 commit into
Conversation
…/tui docs, fix broken links
- api/plugins.mdx: fix createPlugin overload order (factory first)
- api/create-cli.mdx: fix CLI generic default (any -> {})
- core-concepts/commands.mdx: mark context as optional (already accurate)
- core-concepts/configuration.mdx: fix bufferMode default comment (standard not alternate)
- examples/git-tool.mdx: fix broken Next Steps links
- examples/dev-server.mdx: fix broken Next Steps links
- guides/building-your-first-cli.mdx: remove non-existent todo-cli reference
- guides/guides/meta.json: remove orphan generated-types.mdx
- packages/meta.json: add store, runtime, tui to navigation
- packages/store.mdx: document @bunli/store package (new)
- packages/runtime.mdx: document @bunli/runtime package (new)
- packages/tui.mdx: document @bunli/tui package (new)
|
| ```typescript | ||
| const store = createStore({ | ||
| dirPath: "./data", | ||
| fields: { | ||
| theme: { | ||
| type: "string", | ||
| default: "dark", | ||
| description: "Color theme to use", | ||
| }, | ||
| }, | ||
| }); | ||
| ``` | ||
| interface StoreInstance<TFields extends FieldsDef> { | ||
| /** Get a field value */ | ||
| get<K extends keyof TFields>(key: K): InferStoreConfig<TFields>[K]; | ||
|
|
||
| ## Store Options | ||
| /** Get all field values */ | ||
| getAll(): InferStoreConfig<TFields>; | ||
|
|
||
| ```typescript | ||
| interface StoreOptions<F extends FieldsDef> { | ||
| /** Directory path for the store file */ | ||
| dirPath: string; | ||
| /** Update one or more fields */ | ||
| update(updates: Partial<InferStoreConfig<TFields>>): void; | ||
|
|
||
| /** Store file name (without .json). Default: 'config' */ | ||
| name?: string; | ||
| /** Reset all fields to defaults */ | ||
| reset(): void; | ||
|
|
||
| /** Field definitions */ | ||
| fields: F; | ||
| fields: TFields; | ||
|
|
||
| /** Remove unknown fields on read. Default: true */ | ||
| pruneUnknown?: boolean; | ||
| /** Store file path */ | ||
| path: string; | ||
| } | ||
| ``` |
There was a problem hiding this comment.
🔴 store.mdx documents a completely fabricated StoreInstance API that doesn't match the source code
The new StoreInstance interface in the docs shows get(), getAll(), synchronous update(partial), synchronous reset(), plus fields and path properties. The actual StoreInstance at packages/store/src/types.ts:85-91 has entirely different methods: read(): Promise<TConfig>, write(), update(updater: (current) => TConfig), patch(), reset() — all returning Promise. Users following this documentation will write code that fails at runtime.
Actual StoreInstance interface from source
export interface StoreInstance<TConfig> {
read(): Promise<TConfig>;
write(config: NoInfer<TConfig>): Promise<void>;
update(updater: StoreUpdater<TConfig>): Promise<void>;
patch(partial: Partial<NoInfer<TConfig>>): Promise<void>;
reset(): Promise<void>;
}Prompt for agents
The StoreInstance interface documentation in store.mdx (lines 62-82) is completely fabricated and does not match the actual source code in packages/store/src/types.ts. The actual interface has: read(): Promise<TConfig>, write(config): Promise<void>, update(updater: (current) => TConfig): Promise<void>, patch(partial): Promise<void>, reset(): Promise<void>. There are no get(), getAll() methods, no fields or path properties, and all methods are async. The entire StoreInstance section, and all code examples that use the non-existent methods (like configStore.get() and configStore.update({...})), need to be rewritten to match the actual API.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const configStore = createStore({ | ||
| path: "./.mycli", | ||
| fields: { | ||
| theme: { type: "string", default: "dark" }, | ||
| port: { type: "number", default: 3000 }, | ||
| verbose: { type: "boolean", default: false }, | ||
| apiKey: { type: "string" }, | ||
| debug: { type: "boolean", default: false }, | ||
| maxRetries: { type: "number", default: 3 }, | ||
| allowedHosts: { type: "string", array: true }, | ||
| }, | ||
| }); | ||
|
|
||
| // Read current config | ||
| const config = await store.read(); | ||
| // { theme: 'dark', port: 3000, verbose: false } | ||
|
|
||
| // Update the entire config | ||
| await store.write({ theme: "light", port: 8080, verbose: true }); | ||
|
|
||
| // Patch specific fields | ||
| await store.patch({ port: 9000 }); | ||
|
|
||
| // Update using a function | ||
| await store.update((current) => ({ | ||
| ...current, | ||
| port: current.port + 1, | ||
| })); | ||
| // Read values | ||
| const apiKey = configStore.get("apiKey"); | ||
| const debug = configStore.get("debug"); | ||
|
|
||
| // Reset to defaults (deletes the file) | ||
| await store.reset(); | ||
| // Update values | ||
| configStore.update({ apiKey: "new-key", debug: true }); | ||
| ``` |
There was a problem hiding this comment.
🔴 store.mdx Quick Start uses path parameter and non-existent get()/update() methods
The Quick Start code at line 23 passes path: "./.mycli" to createStore, but the actual StoreOptions type (packages/store/src/types.ts:80-84) uses dirPath: string and name?: string. Lines 33-34 call configStore.get("apiKey") which doesn't exist — the actual method is await configStore.read() returning the full config object. Line 37 calls configStore.update({ apiKey: "new-key", debug: true }) but the actual update() takes a function (current) => newConfig, not a partial object (the partial-object method is patch()).
Prompt for agents
The Quick Start code example in store.mdx uses wrong parameter names and non-existent methods. The actual createStore takes { dirPath, name?, fields, pruneUnknown? } (not path). The actual StoreInstance has read() (async, returns full config), write(), update(fn), patch(partial), reset() — no get() or getAll() methods. Rewrite the Quick Start to use the actual API, e.g.:
const configStore = createStore({
dirPath: "./.mycli",
name: "settings",
fields: { ... },
});
const config = await configStore.read();
const apiKey = config.apiKey;
await configStore.patch({ apiKey: "new-key", debug: true });
Was this helpful? React with 👍 or 👎 to provide feedback.
| ```typescript | ||
| interface RuntimeTransport { | ||
| /** Send data through the transport */ | ||
| send(data: unknown): void; | ||
|
|
||
| /** Subscribe to transport events */ | ||
| subscribe(observer: RuntimeTransportObserver): void; | ||
|
|
||
| /** Close the transport */ | ||
| close(): void; | ||
| } | ||
| ``` |
There was a problem hiding this comment.
🔴 runtime.mdx documents fabricated RuntimeTransport interface with non-existent subscribe() and close() methods
The documented RuntimeTransport interface (lines 86-95) shows send(data: unknown), subscribe(observer), and close() methods. The actual interface at packages/runtime/src/transport.ts:3-5 only has send(event: RuntimeEvent): void | Promise<void>. There is no subscribe() or close() method. The Custom Transport example (lines 175-187) that implements this fabricated interface would also fail to compile against the real type.
| ```typescript | |
| interface RuntimeTransport { | |
| /** Send data through the transport */ | |
| send(data: unknown): void; | |
| /** Subscribe to transport events */ | |
| subscribe(observer: RuntimeTransportObserver): void; | |
| /** Close the transport */ | |
| close(): void; | |
| } | |
| ``` | |
| ```typescript | |
| interface RuntimeTransport { | |
| /** Send a runtime event through the transport */ | |
| send(event: RuntimeEvent): void | Promise<void>; | |
| } |
<!-- devin-review-badge-begin -->
<a href="https://app.devin.ai/review/aryalabshq/bunli/pull/68" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
---
*Was this helpful? React with 👍 or 👎 to provide feedback.*
| ```typescript | ||
| import { detectImageCapability, renderImage } from "@bunli/runtime/image"; | ||
| interface RuntimeTransportObserver { | ||
| onSend?(data: unknown): void; | ||
| onError?(error: Error): void; | ||
| onClose?(): void; | ||
| } | ||
| ``` |
There was a problem hiding this comment.
🔴 runtime.mdx documents fabricated RuntimeTransportObserver interface with non-existent methods
The documented RuntimeTransportObserver (lines 103-107) shows onSend?, onError?, onClose? methods. The actual interface at packages/runtime/src/transport.ts:7-9 only has onTransportError?(error: unknown, event: RuntimeEvent): void. Users implementing this observer will have none of their handlers called.
| ```typescript | |
| import { detectImageCapability, renderImage } from "@bunli/runtime/image"; | |
| interface RuntimeTransportObserver { | |
| onSend?(data: unknown): void; | |
| onError?(error: Error): void; | |
| onClose?(): void; | |
| } | |
| ``` | |
| ```typescript | |
| interface RuntimeTransportObserver { | |
| onTransportError?(error: unknown, event: RuntimeEvent): void; | |
| } |
<!-- devin-review-badge-begin -->
<a href="https://app.devin.ai/review/aryalabshq/bunli/pull/68" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open in Devin Review">
</picture>
</a>
<!-- devin-review-badge-end -->
---
*Was this helpful? React with 👍 or 👎 to provide feedback.*
| | RuntimePromptStartedEvent | ||
| | RuntimePromptCancelledEvent | ||
| | RuntimeTransportErrorEvent; | ||
| import { RuntimeRendererStartedEventSchema } from "@bunli/runtime"; |
There was a problem hiding this comment.
🔴 runtime.mdx imports RuntimeRendererStartedEventSchema from root path which only exports types
Line 63 shows import { RuntimeRendererStartedEventSchema } from "@bunli/runtime", but the root export at packages/runtime/src/index.ts only re-exports types (using export type), not value exports like Zod schemas. The schema is only available from the subpath @bunli/runtime/events. This import would fail at runtime.
| import { RuntimeRendererStartedEventSchema } from "@bunli/runtime"; | |
| import { RuntimeRendererStartedEventSchema } from "@bunli/runtime/events"; |
Was this helpful? React with 👍 or 👎 to provide feedback.
| const batch = createSyncBatcher({ | ||
| onUpdate: (state) => { | ||
| // Apply batched update | ||
| }, | ||
| }); | ||
|
|
||
| function App() { | ||
| return ( | ||
| <ThemeProvider theme={custom}> | ||
| <YourUI /> | ||
| </ThemeProvider> | ||
| ); | ||
| } | ||
| // Queue updates (batched) | ||
| batch.update({ count: 1 }); | ||
| batch.update({ count: 2 }); | ||
| batch.update({ count: 3 }); | ||
|
|
||
| // Flush pending updates | ||
| batch.flush(); | ||
| ``` | ||
|
|
||
| **Options:** | ||
|
|
||
| | Option | Type | Description | | ||
| |--------|------|-------------| | ||
| | `onUpdate` | `(state: T) => void` | Callback when updates are flushed | | ||
| | `schedule` | `number` | Schedule delay in ms (default: 16) | |
There was a problem hiding this comment.
🔴 tui.mdx documents completely wrong createSyncBatcher API signature and usage
The docs (lines 185-205) show createSyncBatcher({ onUpdate: (state) => {...} }) with batch.update() calls and options { onUpdate, schedule }. The actual API at packages/tui/src/utils/sync-batcher.ts takes (applyBatch: (actions: T[]) => void, options?: SyncBatcherOptions) as arguments. The methods are enqueue(), flush(), clear(), size(), dispose() — there is no update() method. Options are { mode?: "sync" | "microtask" | "timeout", delayMs?: number }, not { onUpdate, schedule }.
Prompt for agents
The createSyncBatcher documentation in tui.mdx (lines 185-205) is completely wrong. The actual API (packages/tui/src/utils/sync-batcher.ts) is:
createSyncBatcher<TAction>(applyBatch: (actions: TAction[]) => void, options?: SyncBatcherOptions): SyncBatcher<TAction>
SyncBatcherOptions: { mode?: 'sync' | 'microtask' | 'timeout', delayMs?: number }
SyncBatcher methods: enqueue(action), flush(), clear(), size(), dispose()
Rewrite the section to show the correct signature and usage, e.g.:
const batch = createSyncBatcher((actions) => {
for (const action of actions) { /* apply */ }
}, { mode: 'microtask' });
batch.enqueue({ type: 'increment' });
batch.flush();
Was this helpful? React with 👍 or 👎 to provide feedback.
| try { | ||
| const config = await store.read(); | ||
| configStore.update({ apiKey: "new-key" }); | ||
| } catch (error) { | ||
| if (error instanceof StoreReadError) { | ||
| console.error(`Failed to read: ${error.path}`, error.cause); | ||
| console.error("Failed to read store file"); | ||
| } else if (error instanceof StoreWriteError) { | ||
| console.error("Failed to write store file"); | ||
| } else if (error instanceof StoreParseError) { | ||
| console.error(`Malformed JSON: ${error.path}`); | ||
| console.error("Store file is corrupted"); | ||
| } else if (error instanceof StoreValidationError) { | ||
| console.error(`Validation failed for ${error.field}:`, error.message); | ||
| console.error("Invalid field value"); | ||
| } | ||
| } | ||
| ``` |
There was a problem hiding this comment.
🔴 store.mdx error handling example uses non-existent synchronous update() call
The error handling example at line 141 shows configStore.update({ apiKey: "new-key" }) as a synchronous call. As noted in the StoreInstance issues, update() is async and takes a function, not a partial object. Even if this were patch(), it would need await. The try/catch would not catch the async errors without await.
Prompt for agents
The error handling example shows configStore.update({ apiKey: 'new-key' }) as synchronous. The actual update() is async and takes a function. Use await configStore.patch({ apiKey: 'new-key' }) instead, inside an async context.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Documentation audit found and fixed issues across the codebase. Implementation is the source of truth.
Critical Fixes
createPluginoverload order — The factory overload is actually the first overload in source, not the secondexamples/todo-clipath doesn't existHigh Priority Fixes
/docs/guides/command-organization,/docs/guides/external-tools,/docs/guides/shell-integration(don't exist)/docs/guides/plugin-system,/docs/guides/long-running,/docs/guides/real-time(don't exist)generated-types.mdxexisted but wasn't in navigation; removed to avoid confusion@bunli/store,@bunli/runtime, and@bunli/tuipackagesMedium Priority Fixes
CLI<TStore = any>toCLI<TStore = {}>to match implementationstandard, notalternateChanges
apps/web/content/docs/api/create-cli.mdxCLI<TStore = any>→CLI<TStore = {}>apps/web/content/docs/api/plugins.mdxcreatePluginoverload orderapps/web/content/docs/core-concepts/configuration.mdxbufferModedefault commentapps/web/content/docs/examples/dev-server.mdxapps/web/content/docs/examples/git-tool.mdxapps/web/content/docs/guides/building-your-first-cli.mdxapps/web/content/docs/guides/generated-types.mdxapps/web/content/docs/packages/meta.jsonapps/web/content/docs/packages/runtime.mdxapps/web/content/docs/packages/store.mdxapps/web/content/docs/packages/tui.mdxRemaining Issues
The audit report (
.scratchpad/docs-audit/report.md) documents additional findings that require further review or code changes (not just doc fixes). Key items:plugins.mdx:CommandContextaccessor methods (getStoreValue, etc.) — These exist in types but actual implementation uses directcontext.storeaccessplugins.mdx:createTestPluginandcomposePlugins— These ARE exported from the index, the audit was wrong about this