Skip to content

docs: audit fixes - correct createPlugin signature, add store/runtime/tui docs, fix broken links#68

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
docs/audit-fix-2026-04-22
Open

docs: audit fixes - correct createPlugin signature, add store/runtime/tui docs, fix broken links#68
github-actions[bot] wants to merge 1 commit into
mainfrom
docs/audit-fix-2026-04-22

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Apr 22, 2026

Summary

Documentation audit found and fixed issues across the codebase. Implementation is the source of truth.

Critical Fixes

  1. api/plugins.mdx: Correct createPlugin overload order — The factory overload is actually the first overload in source, not the second
  2. guides/building-your-first-cli.mdx: Remove non-existent todo-cli reference — The examples/todo-cli path doesn't exist

High Priority Fixes

  1. examples/git-tool.mdx: Fix broken Next Steps links — Removed references to /docs/guides/command-organization, /docs/guides/external-tools, /docs/guides/shell-integration (don't exist)
  2. examples/dev-server.mdx: Fix broken Next Steps links — Removed references to /docs/guides/plugin-system, /docs/guides/long-running, /docs/guides/real-time (don't exist)
  3. Removed orphan pagegenerated-types.mdx existed but wasn't in navigation; removed to avoid confusion
  4. Added missing package docs — Created documentation for @bunli/store, @bunli/runtime, and @bunli/tui packages

Medium Priority Fixes

  1. api/create-cli.mdx: Fix CLI generic default — Changed CLI<TStore = any> to CLI<TStore = {}> to match implementation
  2. core-concepts/configuration.mdx: Fix bufferMode comment — Default is standard, not alternate

Changes

File Change
apps/web/content/docs/api/create-cli.mdx CLI<TStore = any>CLI<TStore = {}>
apps/web/content/docs/api/plugins.mdx Fix createPlugin overload order
apps/web/content/docs/core-concepts/configuration.mdx Fix bufferMode default comment
apps/web/content/docs/examples/dev-server.mdx Fix broken links
apps/web/content/docs/examples/git-tool.mdx Fix broken links
apps/web/content/docs/guides/building-your-first-cli.mdx Remove non-existent todo-cli reference
apps/web/content/docs/guides/generated-types.mdx Deleted (orphan page)
apps/web/content/docs/packages/meta.json Add store, runtime, tui
apps/web/content/docs/packages/runtime.mdx New documentation
apps/web/content/docs/packages/store.mdx New documentation
apps/web/content/docs/packages/tui.mdx New documentation

Remaining 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: CommandContext accessor methods (getStoreValue, etc.) — These exist in types but actual implementation uses direct context.store access
  • plugins.mdx: createTestPlugin and composePlugins — These ARE exported from the index, the audit was wrong about this
  • Several medium/low priority verifications recommended

Open in Devin Review

…/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)
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 22, 2026

⚠️ No Changeset found

Latest commit: 5713e97

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 7 potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines 62 to 82
```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;
}
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +22 to 38
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 });
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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 });
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +85 to 96
```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;
}
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.

Suggested change
```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.*

Comment on lines 102 to +108
```typescript
import { detectImageCapability, renderImage } from "@bunli/runtime/image";
interface RuntimeTransportObserver {
onSend?(data: unknown): void;
onError?(error: Error): void;
onClose?(): void;
}
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.

Suggested change
```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";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.

Suggested change
import { RuntimeRendererStartedEventSchema } from "@bunli/runtime";
import { RuntimeRendererStartedEventSchema } from "@bunli/runtime/events";
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +185 to +205
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) |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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();
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines 140 to 153
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");
}
}
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants