Skip to content

Remove TOC.md files when toc setting is disabled#11

Merged
fbosch merged 11 commits intomasterfrom
copilot/replace-index-feature-with-toc
Feb 1, 2026
Merged

Remove TOC.md files when toc setting is disabled#11
fbosch merged 11 commits intomasterfrom
copilot/replace-index-feature-with-toc

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 1, 2026

Disabling toc on a source only prevented new TOC.md generation but left existing files orphaned in the cache.

Changes

  • src/toc.ts: Added cleanup path to delete TOC.md when toc: false
  • src/sync.ts: Always invoke writeToc instead of conditionally skipping when no sources have TOC enabled
  • tests/sync-toc.test.js: Added test verifying TOC.md removal on config change

Implementation

// src/toc.ts
if (sourceToc) {
  await writeFile(sourceTocPath, sourceTocContent, "utf8");
} else {
  await rm(sourceTocPath, { force: true });
}

The conditional guard in sync that only called writeToc when anySourceHasToc prevented cleanup from running when all sources disabled TOC. Removing this guard allows the function to handle both generation and removal.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Summary by CodeRabbit

  • New Features

    • Added configurable table of contents (TOC) generation with per-source TOC.md files
    • Introduced new "toc" option at defaults and per-source configuration levels
  • Documentation

    • Restructured configuration documentation with clearer Default Options and Source Options sections
    • Updated schema to reflect new defaults and per-source override semantics
  • Chores

    • Removed legacy index.json generation functionality

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI and others added 3 commits February 1, 2026 18:28
Co-authored-by: fbosch <6979916+fbosch@users.noreply.github.com>
Co-authored-by: fbosch <6979916+fbosch@users.noreply.github.com>
Co-authored-by: fbosch <6979916+fbosch@users.noreply.github.com>
Copilot AI changed the title [WIP] Replace index feature with global and per source TOC Replace index.json with markdown TOC generation Feb 1, 2026
Copilot AI requested a review from fbosch February 1, 2026 18:36
Co-authored-by: fbosch <6979916+fbosch@users.noreply.github.com>
Copilot AI changed the title Replace index.json with markdown TOC generation Move toc from root config to defaults, remove global TOC generation Feb 1, 2026
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Feb 1, 2026

Open in StackBlitz

npx https://pkg.pr.new/fbosch/docs-cache@11

commit: c706656

@fbosch
Copy link
Copy Markdown
Owner

fbosch commented Feb 1, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 1, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 1, 2026

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • 🔍 Trigger a full review
📝 Walkthrough

Walkthrough

Replaces the index.json feature with per-source TOC.md generation. Removes top-level index config, adds defaults.toc and sources[].toc, deletes writeIndex, adds writeToc that reads manifests and writes/removes per-source TOC files; updates schema, config handling, paths, sync flow, init prompts, and tests.

Changes

Cohort / File(s) Summary
Documentation & Config Example
README.md
Rewrote Options section to present global defaults and per-source overrides, replacing flat index semantics with toc and explicit defaults.
Schema
docs.config.schema.json
Removed top-level index; added defaults.toc and sources[].toc boolean properties in schema.
Config Types & Logic
src/config-schema.ts, src/config.ts
Added toc?: boolean to Defaults and Source schemas/types, removed top-level index from config types, set default defaults.toc, updated validation, resolution, and stripping logic to propagate toc.
CLI Init / Prompts
src/init.ts
Updated prompt text and answer handling from "Generate index.json" to "Generate TOC.md"; injects defaults.toc into created configs.
Add / Remove Workflows
src/add.ts, src/remove.ts
Removed propagation of rawConfig.index into nextConfig; adjusted add/remove flows to no longer carry index.
Paths & Constants
src/paths.ts
Removed DEFAULT_INDEX_FILENAME, added DEFAULT_TOC_FILENAME; getCacheLayout no longer returns indexPath.
Sync Flow
src/sync.ts
Replaced conditional writeIndex calls with invocation of new writeToc (now passed plan results); updated imports accordingly.
TOC Implementation
src/toc.ts
New module exporting writeToc(...). Reads per-source .manifest.jsonl, builds TOC entries, writes/removes TOC.md per resolved source toc setting, and skips missing dirs.
Removed Index Implementation
src/index.ts
Deleted writeIndex and related index types and logic.
Tests
tests/init.test.js, tests/edge-cases-validation.test.js, tests/sync-toc.test.js, tests/sync-index.test.js
Updated tests to use defaults.toc and sources[].toc; removed old sync-index.test.js; added sync-toc.test.js covering creation, removal, non-rewrite, and per-source behavior.

Sequence Diagram(s)

sequenceDiagram
  participant Sync as RunSync
  participant Plan as Plan/Lock
  participant Toc as writeToc
  participant FS as Filesystem
  participant Manifest as ManifestReader

  Sync->>Plan: prepare plan & results
  Sync->>Toc: writeToc(cacheDir, configPath, lock, sources, results)
  Toc->>Plan: iterate lock.sources
  Toc->>FS: check sourceDir exists
  alt sourceDir exists
    Toc->>Manifest: read .manifest.jsonl
    Manifest-->>Toc: list of file paths
    Toc->>FS: read existing TOC.md (if any)
    Toc->>FS: write or remove TOC.md based on toc flag & up-to-date check
  else missing sourceDir
    Toc-->>Sync: skip source
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through manifests late at night,
Swapped index for TOC with great delight,
Per-source trees in markdown light,
Files and links arranged just right,
A tiny rabbit cheers the new write! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: removing TOC.md files when the toc setting is disabled. It directly reflects the primary objective of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/sync.ts (1)

485-495: ⚠️ Potential issue | 🟠 Major

Missing inheritance from defaults.toc.

The current check source.toc === true doesn't account for the defaults.toc setting. If a user sets defaults: { toc: true } but doesn't explicitly set toc: true on each source, no TOC will be generated because source.toc will be undefined.

This same issue exists in src/toc.ts at line 87 where const sourceToc = source?.toc ?? false should inherit from defaults.

🐛 Proposed fix
 	// Check if any source has TOC enabled
-	const anySourceHasToc = plan.sources.some((source) => source.toc === true);
+	const defaultToc = plan.defaults.toc ?? false;
+	const anySourceHasToc = plan.sources.some(
+		(source) => (source.toc ?? defaultToc) === true
+	);

 	if (anySourceHasToc) {
 		await writeToc({
 			cacheDir: plan.cacheDir,
 			configPath: plan.configPath,
 			lock,
 			sources: plan.sources,
+			defaults: plan.defaults,
 		});
 	}

And in src/toc.ts, update the writeToc function to accept and use defaults:

 export const writeToc = async (params: {
 	cacheDir: string;
 	configPath: string;
 	lock: DocsCacheLock;
 	sources: DocsCacheResolvedSource[];
+	defaults: DocsCacheDefaults;
 }) => {
   // ...
-  const sourceToc = source?.toc ?? false;
+  const sourceToc = source?.toc ?? params.defaults.toc ?? false;
🤖 Fix all issues with AI agents
In `@src/paths.ts`:
- Around line 43-48: Remove the unused tocPath property from the cache layout
return object (stop returning tocPath alongside cacheDir and sourceDir) and
eliminate the duplicate DEFAULT_TOC_FILENAME constant by consolidating to a
single definition: delete the local DEFAULT_TOC_FILENAME in src/paths.ts and
import the canonical DEFAULT_TOC_FILENAME from the module that owns it (the
symbol DEFAULT_TOC_FILENAME in toc.ts) or move the single constant to a shared
module; ensure any remaining code references use the single exported
DEFAULT_TOC_FILENAME and that sourceDir remains the location for per-source
TOCs.

In `@src/toc.ts`:
- Line 7: DEFAULT_TOC_FILENAME is duplicated; remove the local definition in the
toc module and instead import the already exported DEFAULT_TOC_FILENAME from the
existing paths module (or re-export that symbol from this module if you need to
preserve the same export surface). Update imports/usages in this file to
reference the imported DEFAULT_TOC_FILENAME and delete the redundant declaration
to avoid inconsistency.
🧹 Nitpick comments (4)
src/paths.ts (1)

4-4: Duplicate constant export detected.

DEFAULT_TOC_FILENAME is exported from both src/paths.ts (line 4) and src/toc.ts (line 7). This duplication creates a maintenance risk—if one value is updated without the other, they will diverge. Consolidate to a single source of truth.

♻️ Suggested approach

Either:

  1. Keep the constant only in src/paths.ts and import it in src/toc.ts, or
  2. Keep it only in src/toc.ts (since it's TOC-specific) and import it in src/paths.ts
tests/sync-toc.test.js (1)

86-152: Consider adding global TOC absence check for consistency.

This test validates source-level toc: true behavior but doesn't verify that the global TOC is absent, unlike the first test. For consistency and to ensure the PR objective (removing global TOC) holds in both scenarios, consider adding the same assertion.

💡 Suggested addition
 	assert.ok(sourceToc.includes("- [README.md](./README.md)"));
 	assert.ok(sourceToc.includes("- [guide.md](./guide.md)"));
+
+	// Check global TOC does NOT exist
+	const globalTocPath = path.join(cacheDir, "TOC.md");
+	await assert.rejects(
+		() => readFile(globalTocPath, "utf8"),
+		/ENOENT/,
+		"Global TOC should not exist",
+	);
 });
src/toc.ts (2)

39-56: Silent error handling may mask data issues.

The catch block returns an empty array for all errors, including JSON parse failures from malformed manifest entries. This could silently mask data corruption. Consider distinguishing between file-not-found (acceptable) and parse errors (should log or propagate).

💡 Suggested improvement
 const readManifest = async (sourceDir: string): Promise<string[]> => {
 	const manifestPath = path.join(sourceDir, ".manifest.jsonl");
 	try {
 		const raw = await readFile(manifestPath, "utf8");
 		const files: string[] = [];
 		for (const line of raw.split("\n")) {
 			if (line.trim()) {
-				const entry = JSON.parse(line);
-				if (entry.path) {
-					files.push(entry.path);
+				try {
+					const entry = JSON.parse(line);
+					if (entry.path) {
+						files.push(entry.path);
+					}
+				} catch {
+					// Skip malformed lines but continue processing
 				}
 			}
 		}
 		return files;
-	} catch {
+	} catch (error) {
+		// File not found is expected for sources without manifests
+		if ((error as NodeJS.ErrnoException).code === "ENOENT") {
+			return [];
+		}
+		throw error;
 	}
-	return [];
 };

77-86: TocEntry contains unused fields.

fileCount, cachePath, and targetDir are populated in the entry but not used by generateSourceToc. If these are intended for future use, consider adding a comment. Otherwise, they can be removed to reduce complexity.

Comment thread src/paths.ts Outdated
Comment thread src/toc.ts Outdated
Repository owner deleted a comment from coderabbitai Bot Feb 1, 2026
Repository owner deleted a comment from coderabbitai Bot Feb 1, 2026
Repository owner deleted a comment from coderabbitai Bot Feb 1, 2026
Repository owner deleted a comment from coderabbitai Bot Feb 1, 2026
Co-authored-by: fbosch <6979916+fbosch@users.noreply.github.com>
Copilot AI changed the title Move toc from root config to defaults, remove global TOC generation Remove duplicate DEFAULT_TOC_FILENAME and unused tocPath property Feb 1, 2026
Co-authored-by: fbosch <6979916+fbosch@users.noreply.github.com>
Copilot AI changed the title Remove duplicate DEFAULT_TOC_FILENAME and unused tocPath property Set TOC generation to enabled by default Feb 1, 2026
Co-authored-by: fbosch <6979916+fbosch@users.noreply.github.com>
Copilot AI changed the title Set TOC generation to enabled by default Fix init flow TOC prompt logic after default changed to true Feb 1, 2026
Co-authored-by: fbosch <6979916+fbosch@users.noreply.github.com>
Copilot AI changed the title Fix init flow TOC prompt logic after default changed to true Remove TOC.md files when toc setting is disabled Feb 1, 2026
Repository owner deleted a comment from coderabbitai Bot Feb 1, 2026
@fbosch
Copy link
Copy Markdown
Owner

fbosch commented Feb 1, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 1, 2026

✅ Actions performed

Full review triggered.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@README.md`:
- Around line 76-95: The README claims the `toc` default is false but the actual
default in code is true; update one to match the other. Either change the code
in src/config.ts where DEFAULT_CONFIG.defaults.toc is set (toggle it to false)
or update the README text to state `toc` defaults to `true` (and adjust the
example list entry), making sure to reference DEFAULT_CONFIG.defaults.toc and
the README entry for `toc` so the documentation and implementation stay
consistent.

In `@src/config.ts`:
- Line 86: The default value for the "toc" setting in src/config.ts is currently
set to true but the README documents the default as false; decide which behavior
you want and make them consistent: either change the "toc" property in the
config export/object to false (replace toc: true with toc: false) or update the
README to state the default is true, and ensure any related constants, tests, or
consumers that rely on the default (e.g., DEFAULT_CONFIG or exported config
object) are updated accordingly so the code and documentation match.
🧹 Nitpick comments (1)
src/toc.ts (1)

28-69: Normalize/encode manifest paths before rendering TOC links.
Raw paths can contain backslashes or spaces, which can flatten the tree and break Markdown links on some platforms. Consider normalizing to POSIX and encoding link targets.

♻️ Suggested update
 const createTocTree = (files: string[]): TocTree => {
 	const root: TocTree = { dirs: new Map(), files: [] };

 	for (const file of files) {
-		const parts = file.split("/").filter(Boolean);
+		const normalizedPath = toPosixPath(file);
+		const parts = normalizedPath.split("/").filter(Boolean);
 		if (parts.length === 0) {
 			continue;
 		}
@@
-		const name = parts[parts.length - 1];
-		node.files.push({ name, path: file });
+		const name = parts[parts.length - 1];
+		node.files.push({ name, path: normalizedPath });
 	}

 	return root;
 };
@@
 	for (const file of files) {
-		lines.push(`${indent}- [${file.name}](./${file.path})`);
+		const linkPath = encodeURI(file.path);
+		lines.push(`${indent}- [${file.name}](./${linkPath})`);
 	}
 };

Comment thread README.md Outdated
Comment thread src/config.ts
@fbosch fbosch marked this pull request as ready for review February 1, 2026 22:40
Copilot AI review requested due to automatic review settings February 1, 2026 22:40
@fbosch fbosch merged commit 96c1239 into master Feb 1, 2026
11 checks passed
@fbosch fbosch deleted the copilot/replace-index-feature-with-toc branch February 1, 2026 22:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces the legacy index.json generation feature with a new per-source TOC.md (table of contents) feature that lists all documentation files in a structured markdown format. The key improvement is that TOC files are now properly cleaned up when the toc setting is disabled.

Changes:

  • Replaced global index config option with toc option in defaults and per-source configuration (defaults to true)
  • Added src/toc.ts with logic to generate per-source TOC.md files and clean them up when disabled
  • Updated sync process to always call writeToc to handle both generation and cleanup
  • Removed legacy src/index.ts and associated index.json generation

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/toc.ts New file implementing TOC generation and cleanup logic for per-source documentation indexes
src/sync.ts Replaced writeIndex call with writeToc, now always invoked to handle cleanup
src/config.ts Removed index field, added toc field to defaults and sources with default value true
src/config-schema.ts Updated Zod schema to replace index with toc field
src/init.ts Updated prompts to ask about TOC generation instead of index, with default true
src/paths.ts Renamed constant from DEFAULT_INDEX_FILENAME to DEFAULT_TOC_FILENAME
src/add.ts Removed handling of index field when adding sources
src/remove.ts Removed handling of index field when removing sources
src/index.ts Deleted file (legacy index.json generation)
docs.config.schema.json Updated JSON schema to remove index and add toc field
README.md Updated documentation to describe new TOC feature and remove index references
tests/sync-toc.test.js New comprehensive test suite for TOC generation, removal, and optimization
tests/sync-index.test.js Deleted file (legacy index.json tests)
tests/init.test.js Updated tests to verify TOC prompt and config generation behavior
tests/edge-cases-validation.test.js Updated validation tests for new toc field
Comments suppressed due to low confidence (1)

src/remove.ts:69

  • The initial value of config is unused, since it is always overwritten.
	let config = DEFAULT_CONFIG;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/toc.ts
Comment on lines +175 to +179
try {
await rm(sourceTocPath, { force: true });
} catch {
// Ignore errors if file doesn't exist
}
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

The try-catch block here is unnecessary. When the force: true option is passed to rm(), it will not throw an error if the file doesn't exist - it will simply succeed silently. The try-catch can be removed, and the code can just be:

await rm(sourceTocPath, { force: true });

Suggested change
try {
await rm(sourceTocPath, { force: true });
} catch {
// Ignore errors if file doesn't exist
}
await rm(sourceTocPath, { force: true });

Copilot uses AI. Check for mistakes.
Comment thread src/toc.ts
};

// Generate per-source TOC if the source has TOC enabled
const sourceToc = source?.toc ?? true;
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

When a source is removed from the config but still exists in the lock file (which happens until prune is run), this code will default sourceToc to true because source will be undefined. This means TOC files will be generated for sources that are no longer in the configuration.

Consider treating sources not present in the config as having toc: false to ensure their TOC files are cleaned up. For example:

const sourceToc = source ? (source.toc ?? true) : false;

Suggested change
const sourceToc = source?.toc ?? true;
const sourceToc = source ? (source.toc ?? true) : false;

Copilot uses AI. Check for mistakes.
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.

3 participants