| name | knowledge-wiki-cluster |
| description | Find groups of concepts that share an implied parent slug and handle them: if the parent does not exist, create it; if it already exists, link unconnected children to it. For each child, fold (merge content into parent and delete child), link (keep as standalone, linked to parent), or merge into a sibling that already covers its content. Net result: fewer total concepts and a fully linked hierarchy. Run after accumulating new concepts or when the wiki has clusters of narrowly-named sub-concepts without a parent. |
Knowledge Wiki Cluster
Detect clusters of concepts that share an implied parent slug, then decide for each child whether to fold (merge content into parent and delete the child) or link (keep the child as a standalone concept linked to the parent). The goal is to reduce total concept count by absorbing thin, redundant, or retired sub-concepts into a parent article.
Two cluster types are handled:
- New-parent (
parentExists: false): the implied parent concept does not exist yet — create it, then fold/link/merge children.
- Existing-parent (
parentExists: true): the implied parent already exists and these children have not yet been dismissed — fold/link/merge each one.
For new-parent clusters, each concept is grouped under its non-existing prefix ancestors up to (but not including) its nearest existing ancestor — so apple-watch-ultra forms an [apple-watch] cluster when apple-watch.md is absent, even if apple.md exists. For existing-parent clusters, the concept is grouped directly at its nearest existing ancestor. Presents one cluster at a time with a batch recommendation — you confirm, override, or dismiss.
Within a cluster, children may also be merged into each other (sibling merging) before any folding into the parent. A child judged not worth folding into the parent — an Anchor — can absorb another child's content if that child — a Fold-candidate — is a strict subset of the Anchor's content. The absorbed Fold-candidate is merged into the Anchor and deleted; the Anchor is recommended to stay standalone and is not expected to be folded into the parent.
Steps
1. Establish the working directory
The knowledge base root is the Git repository root. Run git rev-parse --show-toplevel and store the result as KNOWLEDGE_PATH.
2. Find clusters
Run:
node {KNOWLEDGE_PATH}/scripts/wiki/candidates.mjs find-implied-parent-concepts
This outputs { "clusters": [...] } sorted deepest first (most hyphens in impliedParent), with ties broken by cluster size descending. Both new-parent and existing-parent clusters are interleaved in this single ordering. Each entry has:
impliedParent — the parent slug
children — array of { path, dismissed } objects, where path is the concept file path (e.g. Wiki/Concepts/audi-etron.md) and dismissed is true if this child was previously dismissed from this cluster pair, false otherwise
parentExists — false if the parent concept needs to be created; true if it already exists
A cluster appears only when it has at least the usual number of dismissed: false children (≥2 for new-parent, ≥1 for existing-parent). Previously dismissed children are included with dismissed: true as potential Anchor targets for sibling merges.
Derive each child's slug from its path field: Wiki/Concepts/audi-etron.md → audi-etron. Use this slug wherever {child-slug} appears below.
If the clusters array is empty, print No clusters found. and stop.
3. LLM pre-filter
For new-parent clusters only (parentExists: false), auto-dismiss clusters where the implied parent is a common English modifier rather than a meaningful proper noun or specific topic — e.g. smart grouping smart-home with smart-money, or the grouping the-economist with the-expanse. Children spanning clearly unrelated domains are a reliable signal for auto-dismissal. Skip this filter for existing-parent clusters — the parent's existence already confirms it is a real topic.
For each auto-dismissed cluster, run once per child:
node {KNOWLEDGE_PATH}/scripts/wiki/wiki-state.mjs dismiss-pair knowledge-wiki-cluster "Wiki/Concepts/{impliedParent}.md" "Wiki/Concepts/{child-slug}.md"
Be conservative — wrongly dismissed pairs require manually editing Wiki/.state.json to recover.
4. Present and resolve each cluster
Process one cluster at a time. Use a separate interaction for each cluster — never combine multiple clusters into a single question, even if you intend to recommend the same action for several in a row. When using AskUserQuestion, pass exactly one question per call. Never use the multi-question form to batch clusters.
Maintain an in-memory processed set of impliedParent slugs handled in this session. After creating a parent, exclude already-processed slugs from the refreshed cluster list.
4a. Read, summarize, and assess
Determine the Display Name for the implied parent before presenting:
- If
parentExists is true, read the parent concept file and take the Display Name from its H1 heading (# …). This preserves the exact capitalisation already in use (e.g. ios → "iOS", macos → "macOS").
- If
parentExists is false, derive a human-readable name from the slug (e.g. audi → Audi, apple-watch → Apple Watch, career → Career).
Read every child concept file, then classify each one in two passes:
- Fold vs. Anchor.
dismissed: true children are Anchors automatically — the previous decision that they don't belong as primary children of this parent stands; no independent judgment needed. For dismissed: false children, independently judge each as a Fold-candidate (thin, narrow, or low-standalone-value — would fold into the parent) or an Anchor (substantive: rich prose, multiple sources, or broad cross-links — would stay standalone). This judgment must not depend on any other child in the cluster.
- Sibling merges. For each
dismissed: false Fold-candidate, check whether its content is a strict subset of any Anchor's content in the same cluster (whether that Anchor is dismissed: true or dismissed: false) — i.e. the Fold-candidate adds nothing the Anchor doesn't already cover, not merely "related" or "similar". If so, recommend Merge into {anchor-slug} instead of Fold. Anchors only absorb — an Anchor is recommended to stay standalone and is not expected to be folded or merged away.
Present a cluster summary as a 3-column table with each child's description and recommendation inline. Output the table as a normal assistant message (not in a code block) so it renders — this message must be sent before calling any interactive question tool:
Cluster: {Display Name} ({N} children, {existing parent | new parent})
| Child | Description | Recommendation |
|---|
{child-slug} | {one-sentence description} | Fold — {reason} |
{child-slug} | {one-sentence description} | Link — {reason} |
{child-slug} (dismissed) | {one-sentence description} | Link — previously dismissed; auto-classified as Anchor |
{child-slug} | {one-sentence description} | Merge into {anchor-slug} — {reason: what subset of the anchor's content this duplicates} |
Then write 1–2 sentences of reasoning: what the children have in common, whether creating a parent would add meaningful value, and call out any proposed sibling merges.
4b. Ask what to do
Use "What would you like to do with the {Display Name} cluster?" as the question text. Never put (Recommended) on Dismiss. The remaining guidance differs by cluster type:
If parentExists is false (new-parent cluster): Add (Recommended) to Proceed only when the cluster is a clear brand, product line, or named topic with 3+ children that obviously belong under it and at least one child is recommended for folding or merging into a sibling.
| # | Option | Description |
|---|
| 1 | Proceed | Create "{Display Name}" and apply the recommendations above (fold, link, or merge into a sibling) |
| 2 | Link all | Create "{Display Name}" and link all children to it, keeping them standalone (overrides any sibling-merge recommendations) |
| 3 | Fold all | Create "{Display Name}" and fold every child into it (overrides any sibling-merge recommendations) |
| 4 | Dismiss | These don't belong together; never show this cluster again (no parent created) |
Users may also type skip (leave for now; show again next run), review one by one (create parent then decide each child individually), or stop (halt all remaining clusters) in the Other field.
If parentExists is true (existing-parent cluster): Add (Recommended) to Proceed when at least one child is recommended for folding or merging into a sibling.
| # | Option | Description |
|---|
| 1 | Proceed | Ensure bidirectional links to "{Display Name}" and apply the recommendations above (fold, link, or merge into a sibling) |
| 2 | Link all | Link all children to "{Display Name}" but keep them standalone (overrides any sibling-merge recommendations) |
| 3 | Fold all | Link all children to "{Display Name}" and fold every child into it (overrides any sibling-merge recommendations) |
| 4 | Dismiss | The slug prefix is coincidental; never show this cluster again (no links created) |
Users may also type skip (leave for now; show again next run), review one by one (link to parent then decide each child individually), or stop (halt all remaining clusters) in the Other field.
When using an interactive question/options tool, keep the question text short: "What would you like to do with the {Display Name} cluster?" Do not cram the summary, reasoning, or option details into one long question line.
The user must still be able to see the full decision context while choosing: the cluster summary, recommendation reasoning, all four options, and the extra accepted inputs. This context may be in the tool's body/details area, or in an immediately preceding assistant message if that message remains expanded and visible while the dialog is open. If the available question tool cannot keep that context visible together with the options, do not use it for this prompt. Instead, render the summary, reasoning, numbered options, and reply instructions as one normal markdown message, then wait for the user's reply. Accept 1, 2, 3, or 4; also accept skip, review one by one, or stop to halt all remaining clusters.
4c. Collecting and executing decisions
Do not create or edit any files until all decisions are collected. If stop is entered at any point during decision collection, proceed to section 4f without creating any files.
4c.1 If Proceed
Record the 4a table recommendations as final decisions — Fold into the parent, Link (Anchor), or Merge into {anchor-slug} for each child. Proceed to 4c.5.
4c.2 If Link all
Record every dismissed: false child as Link (standalone), overriding any sibling-merge or fold recommendations. dismissed: true children are unaffected — they remain background Anchors. Proceed to 4c.5.
4c.3 If Fold all
Record every dismissed: false child as Fold into the parent, overriding any sibling-merge recommendations. dismissed: true children are never folded — they are Anchors by definition and remain standalone. Proceed to 4c.5.
4c.4 If Review one by one
Process sibling-merge candidates first, then remaining children.
For each Anchor that has one or more Fold-candidates recommended to merge into it (per the 4a table), present your reasoning, then ask:
When using an interactive question/options tool, keep the question text short: "Merge {Fold-candidate display name(s)} into {Anchor display name}?" Do not cram the recommendation, reasoning, or option details into one long question line.
The user must still be able to see the full decision context while choosing: the recommendation, reasoning, both options, and the done/stop instructions. This context may be in the tool's body/details area, or in an immediately preceding assistant message if that message remains expanded and visible while the dialog is open. If the available question tool cannot keep that context visible together with the options, do not use it for this prompt. Instead, render the recommendation, reasoning, numbered options, and reply instructions as one normal markdown message, then wait for the user's reply.
Use these options (add (Recommended) to whichever applies):
| # | Option | Description |
|---|
| 1 | Merge into "{Anchor display name}" | Merge the listed child(ren)'s content into "{Anchor display name}", then delete them. "{Anchor display name}" stays standalone |
| 2 | Decline | Keep the listed child(ren) separate; each will be reviewed individually below |
Accept: 1 (Merge), 2 (Decline), done (proceed with decisions collected so far — unprocessed Anchors are treated as Decline and unprocessed remaining children are treated as Link), or stop.
If stop was entered, proceed to section 4f without creating any files.
If declined, each listed Fold-candidate falls back to individual review below.
For every remaining dismissed: false child — Fold-candidates not absorbed by an Anchor (via Decline or no Anchor match), and dismissed: false Anchors — process one at a time. (dismissed: true Anchors are not re-evaluated here; they only appear as sibling-merge targets in the Anchor-confirmation step above.) Present your recommendation and reasoning for that child, then ask:
When using an interactive question/options tool, keep the question text short: "What should happen to {child-display-name}?" Do not cram the recommendation, reasoning, or option details into one long question line.
The user must still be able to see the full decision context while choosing: the child recommendation, reasoning, both options, and the done/stop instructions. This context may be in the tool's body/details area, or in an immediately preceding assistant message if that message remains expanded and visible while the dialog is open. If the available question tool cannot keep that context visible together with the options, do not use it for this prompt. Instead, render the recommendation, reasoning, numbered options, and reply instructions as one normal markdown message, then wait for the user's reply.
Use these options (add (Recommended) to whichever applies):
| # | Option | Description |
|---|
| 1 | Fold "{child-display-name}" into "{Display Name}" | Merge child's content into parent, then delete child |
| 2 | Link "{child-display-name}" | Keep child standalone; will be linked to parent |
Accept: 1 (Fold), 2 (Link), done (proceed with decisions collected so far — unprocessed children will be treated as Link), or stop.
If stop was entered, proceed to section 4f without creating any files.
4c.5 After collecting decisions
If parentExists is true, the parent concept file already exists — skip ahead to Execute sibling merges.
If parentExists is false and at least one dismissed: false child has a final outcome of Fold or Link, create the parent concept:
node {KNOWLEDGE_PATH}/scripts/wiki/wiki-concept.mjs create "{impliedParent}" "{Display Name}" --type Synthesis --icon notepad
This creates Wiki/Concepts/{impliedParent}.md.
Read the file, then insert a 1–3 paragraph topic overview between # {Display Name} and ## Sources. Write it as a factual reference — what this topic is and what sub-concepts exist under it. Keep it concise; each Fold will enrich the body incrementally. Use American English spelling. Update the existing tags: [] field with the union of tags from the child files, keeping only those that genuinely describe the parent topic.
node {KNOWLEDGE_PATH}/scripts/wiki/wiki-index.mjs upsert-concept "{impliedParent}" "{Display Name}" "{one-line English description}"
Execute sibling merges:
For each child whose final outcome is Merge into {anchor-slug}, execute the knowledge-wiki-merge step 3c with this mapping:
- primary = anchor: slug
{anchor-slug}, display name {anchor-display-name}, path Wiki/Concepts/{anchor-slug}.md
- secondary = child: slug
{child-slug}, display name {child-display-name}, path Wiki/Concepts/{child-slug}.md
Run this before linking or folding — a merged-away child is deleted and must not be linked to the parent or folded into it.
Link children bidirectionally (idempotent):
For each dismissed: false child with final outcome Link, derive its display name from its H1 heading (# …). Run both commands — the first adds the child to the parent's Connected Concepts, the second adds the parent to the child's:
node {KNOWLEDGE_PATH}/scripts/wiki/wiki-concept.mjs insert-connected-concept "{impliedParent}" "{child-slug}" "{child-display-name}"
node {KNOWLEDGE_PATH}/scripts/wiki/wiki-concept.mjs insert-connected-concept "{child-slug}" "{impliedParent}" "{Display Name}"
dismissed: true Anchors are never linked to the parent, even if they absorbed a sibling merge — the previous decision not to connect them stands.
Record linked children:
For each dismissed: false child where Link was chosen, record a dismissed pair so the child is not re-evaluated in future runs. (dismissed: true children already have a dismissed pair recorded — do not run this for them.) Run this after the link commands above succeed:
node {KNOWLEDGE_PATH}/scripts/wiki/wiki-state.mjs dismiss-pair knowledge-wiki-cluster "Wiki/Concepts/{impliedParent}.md" "Wiki/Concepts/{child-slug}.md"
Execute folds:
For each dismissed: false child where Fold was chosen, execute the knowledge-wiki-merge step 3c with this mapping:
- primary = parent: slug
{impliedParent}, display name {Display Name}, path Wiki/Concepts/{impliedParent}.md
- secondary = child: slug
{child-slug}, display name {child-display-name}, path Wiki/Concepts/{child-slug}.md
Wrap up:
This runs whether or not all children were processed. Add {impliedParent} to the in-memory processed set. Re-run:
node {KNOWLEDGE_PATH}/scripts/wiki/candidates.mjs find-implied-parent-concepts
Replace your working cluster list with this fresh output, excluding any slug already in the processed set.
4d. If Dismiss or "skip" was selected
If Dismiss: run dismiss-pair once per child so each individual relationship is suppressed rather than the whole parent slug:
node {KNOWLEDGE_PATH}/scripts/wiki/wiki-state.mjs dismiss-pair knowledge-wiki-cluster "Wiki/Concepts/{impliedParent}.md" "Wiki/Concepts/{child-slug}.md"
If new children appear under the same parent later, they will resurface — only the specific children dismissed here are suppressed.
If "skip" (leave for now; show again next run): make no state changes. Simply add {impliedParent} to the in-memory processed set for this session so it is not presented again during the current run.
Add {impliedParent} to the processed set and continue. (No refresh needed — no concept files were created.)
4f. If "stop"
Exit the loop and proceed to step 5.
5. Print summary
Knowledge Wiki Cluster
Auto-dismissed {N} cluster(s) (meaningless prefix):
- [{impliedParent}]
Created {N} concept(s):
- {impliedParent} — {Display Name}
Folded {Nf} child(ren): {child-slug}, {child-slug}, ...
Not folded {Nn} child(ren): {child-slug}, {child-slug}, ...
Merged {N} child(ren) into sibling(s):
- {child-slug} → {anchor-slug}
Dismissed {N} cluster(s):
- [{impliedParent}]
Skipped {N} cluster(s).
[Omit any section with 0 items.]