SuperAPI AI Directory

cawplan-knowledge

Data & analytics

View repository

Updated 10-15 20:11

About this skill

CawPlan Knowledge

Bootstrap

cawplan skill check

Navigation Model

The whole knowledge base is one continuous drillable tree: datasets → documents → headings → section content, plus search results, which are the same tree pre-filtered by relevance. The user never needs to know or type CLI flag names (--outline, --section, --dataset, --grep, ...) — those are implementation details you choose based on what level of the tree they're asking about. If a user pastes a literal cawplan ... command instead of a plain-language request, treat it as identifying what they want (a dataset, a document, a section), not as a script to execute verbatim; still pick the right underlying flags yourself.

At every level, decide the presentation by candidate-set size and how iterative the pick will be:

  • Bounded, one-shot pick (the current level's real candidates fit within AskUserQuestion's caps — at most 4 options × 4 questions per call, so ≤16 total — and the user is choosing once, not about to rapidly reselect/backtrack through many rounds): AskUserQuestion with preview is fine here. Example: picking among the handful of non-empty datasets, or among a short list of same-topic endpoints. If a level has more raw items than that (e.g. 96 datasets total, only 3 non-empty), filter down to the real candidates first — don't force a large list into AskUserQuestion across many chained calls just to avoid plain text.
  • Large or iterative browsing (a document's full heading tree, dozens of headings, the user needs to jump around and reselect quickly across many rounds without waiting each time): plain text, numbered/nested list, free-form reply for the deep/iterative parts — see step 5 for the one exception (the top level of the tree, when it's bounded) and why going deeper than that with AskUserQuestion was confirmed to be the wrong tool: modal round-trip per pick, no fast reselect/backtrack.

Either way, after showing a leaf-level result (a section's content, a document's full text), show the list at the level the user was just browsing again so they can keep going, rather than ending the turn on a one-shot answer.

Making the clickable path cheap

When you do use AskUserQuestion, these keep it from feeling slow or shallow:

  • A preview is always the node's own --outline preview string, verbatim. Copy that field in unchanged. Do not write your own preview text, and specifically do not substitute a structural summary (a list of the chapter's child headings, a count, a paraphrase) — the point of a preview is to show the section's actual opening body text so the user can judge the content before selecting. A child-heading list tells them nothing the option labels don't already say, and it costs a round trip of your own tokens to invent. Structure belongs in label / description; body text belongs in preview.
    • Already plain text, so paste it directly: --outline (and the CLI's own pickers) run every preview through markdownToPlainText (src/lib/knowledge/plaintext.ts) — headings, **bold**, `code` and table pipes are converted, tables become space-aligned columns. A preview panel renders its text verbatim, so raw Markdown would leak ###/**/| as visible noise (verified side by side; converted wins). Don't hand-clean it and don't re-add Markdown syntax.
    • A node whose preview is empty genuinely has no body of its own (e.g. a heading holding only subsections, or a figure). Say so plainly — don't backfill it with an invented summary.
  • Collapse 1:1 levels. Most datasets hold a single document, so dataset→document is a wasted click. Offer dataset / document as one combined option and skip a whole round trip.
  • Fill the call. One AskUserQuestion carries 4 questions × 4 options — up to 16 selections answered in a single round trip. Grouping related picks into one call beats chaining several.
  • Fetch before you ask, not after. Content is cached per document for 12h, and --outline already embeds previews, so pull what the menu needs up front; then a selection renders straight from what you have instead of paying a fetch on the click.
  • Re-offer the menu in the same turn. After rendering a selection, call AskUserQuestion again immediately with the same (or parent) level. That gives a real pick → view → pick loop — the thing plain text otherwise wins on — without the user retyping anything.
  1. List datasets — the root of the tree. When the user asks what's available, names a dataset you need to resolve to an id, or hasn't picked one yet and you need to offer a starting point:

    cawplan knowledge datasets list
    

    Returns every dataset accessible to the caller: {id, name, document_count}. The workspace can have many datasets total but few real (non-empty) ones — filter to document_count > 0 before deciding how to present the pick: if that filtered set is small (≤16), AskUserQuestion with preview (e.g. each option's description = a sample of its documents) is a fine one-shot picker here; for the full raw list (dozens+, mostly empty), fall back to a plain-text tree instead of forcing it through many chained AskUserQuestion calls.

    When the user names a CawPlan product instead of a dataset (e.g. "what's in the knowledge base for Access Card?" — product lookups themselves are /cawplan-product-insights's job, not this skill's; assume the product_id is already resolved by the time you get here), narrow the same root list to that product instead of listing everything:

    cawplan knowledge datasets list --product <product_id>
    

    This returns the datasets the product owns (created under it) plus the datasets linked to it — two different relationships, don't conflate them. Modules are per document, not per dataset: a product can optionally organize its knowledge under a module tree (product_modules — the same tree QA test-suites use), and each document sits in at most one module. Only documents in datasets the product owns can be placed under its modules; linked datasets never contribute to a module. Check the tree before deciding whether to narrow further (each node's knowledge lists the datasets that have at least one document under that node):

    cawplan knowledge datasets modules --product <product_id>
    
    • If that returns no modules, the product has none — just use the --product-only list above (owned + linked), no module step.
    • If it returns modules, and the user didn't already name one, offer them as a pick (bounded AskUserQuestion, same caps as any other level here — plain text if there are many), then narrow with both flags:
      cawplan knowledge datasets list --product <product_id> --module <module_id>
      
      --module keeps only the product's owned datasets that have at least one document under that module — linked datasets are excluded, so an empty result means "no owned dataset has a document in this module", not "nothing in the product". documents list items carry module_id/module_name (plus a display_id like KBD-00001), so after picking a dataset you can tell which of its documents are actually in the module. Do not use -i/--interactive on either command from an Agent — same restriction as browse below (step 5): it requires a real interactive terminal, which a human runs directly, not you.
  2. List documents in a dataset — second level of the tree, when browsing a specific dataset's contents (the user picked a dataset from step 1, or named one directly):

    cawplan knowledge documents list --dataset <id>
    # optional: --keyword <text> --page <n> --limit <n>
    

    <id> must come from step 1's output.

  3. Search — the default path for "find information about X" requests; conceptually the same tree filtered to relevance-ranked hits instead of everything:

    cawplan knowledge search --query "<text>"
    

    With no dataset selected, this searches every dataset the caller can access. Only add --dataset <id> when the user explicitly names or selects one or more datasets to narrow to — repeat the flag to select multiple:

    cawplan knowledge search --query "<text>" --dataset <id>
    cawplan knowledge search --query "<text>" --dataset <id-1> --dataset <id-2>
    

    Search returns ranked fragments, not the full document — it can miss sections that don't match the query terms. If the user wants the whole document, or search keeps missing a section you know exists, use step 4 instead of trying more search queries.

    Present multiple hits grouped by dataset → document (same tree shape as the rest of this skill), not a flat ranked list — so the user can drill from any hit straight into that document's outline (step 5) or full section (step 4) using the same reply-in-free-text pattern.

  4. Get full document content — when the user asks for a document's full text, or a specific document's content instead of matching fragments:

    cawplan knowledge documents get --dataset <id> --document <id>
    

    <dataset id> and <document id> must come from steps 1 and 2's output. Returns the raw source file content verbatim, not reassembled from search-index segments.

    For a large document, or when you only need one section, do not read the whole content into your own context — it can silently exceed the file-read tool's context window, and the section you want may still get lost in an oversized response even after getting the "full" content. If you already know (or can find, via --outline in step 5) the exact heading you want, use --section "<heading text>" instead — it returns that heading's complete, cleanly-bounded content (see step 5 for details) with no risk of cutting it short or bleeding into the next section:

    cawplan knowledge documents get --dataset <id> --document <id> --section "6.7 Fetch NFC Card"
    

    Only reach for --grep <pattern> --context <n> when you're searching for a pattern in body text rather than a known heading, or the source isn't cleanly headed with Markdown:

    cawplan knowledge documents get --dataset <id> --document <id> --grep "6.7 Fetch NFC Card" --context 20
    

    Widen or narrow --context (default 3 lines) to the section's expected size. If you also want the full document saved for later, add --output <path> in the same call — it writes the raw file to disk alongside the --section/--grep result. Only fall back to --output on its own (then grep/sed the file yourself) when you need to browse a section whose heading you don't know yet and --outline (step 5) doesn't cover it (e.g. non-Markdown source).

    The fetched content is cached locally per document (12h default TTL, same cache as other cawplan commands) — re-running documents get with a different --outline/--section/ --grep against the same --dataset/--document re-filters the cached copy instead of re-fetching, so trying several in a row to locate a section is cheap. Only add --refresh if the source document may have changed since the last fetch.

  5. Browse a large document with an index tree — when the user wants to explore an unfamiliar or long document:

    • Fetch the document's structure as a heading tree in one call, without pulling the whole content: cawplan knowledge documents get --dataset <id> --document <id> --outline This returns data.outline, a nested {level, title, line, preview, children} tree built server-side from the cached content — do not regex-parse data.content yourself for this; the CLI already does it (src/lib/knowledge/outline.ts). Each node's preview is its own first few non-blank body lines, so this single call already carries enough to describe every heading — there is no need for a separate fetch per heading just to build preview text for the menu. Do not re-fetch just to rebuild the tree.

    • Top level of the tree only: if the document's top-level headings (level 1) fit within AskUserQuestion's caps (≤16 total, ≤4 per question), offer them as a clickable AskUserQuestion pick, one option per top-level heading, preview = that heading's own --outline preview verbatim (see "Making the clickable path cheap" above — same rules apply: real preview text, not an invented child-heading summary). This gives the user a clickable first hop into a long document instead of always typing.

    • Below the top level, do NOT use AskUserQuestion — deeper levels of a document's heading tree are exactly the "large or iterative browsing" case from the Navigation Model above: dozens+ of headings, and the user needs to jump around and reselect quickly across many rounds. Claude Code has no native document-tree UI, and AskUserQuestion's modal round-trip per pick was confirmed clunky here specifically once a heading has more than a handful of children (e.g. a chapter with 20-30 subsections). Instead, render the remaining tree as an actual indented plain-text tree — every node on its own line, 2 spaces of indentation per nesting level, mirroring data.outline's children structure directly — and let the user reply freely with any section, heading, combination of sections, or other request. This is a real nested tree, not a single flattened summary line of top-level titles joined by commas/bullets; collapsing it into one line defeats the purpose of showing structure at all.

      By default render only the first two heading levels (# and ##) — deeper levels (### Request Header, ### Response Body, etc.) are the same boilerplate sub-fields repeated under nearly every endpoint and just add noise; skip them unless the user asks to go deeper on a specific node. Add each node's preview inline (e.g. as a short indented line under the title, or a trailing em-dash summary) wherever it helps distinguish similarly-named headings — it's already there in data.outline, no extra fetch needed.

      Example (as literally rendered, not summarized into one line):

      Document outline:
      
      1. Introduction
        1.1 Create API Token & Download API Documentation
        1.2 Obtain Your Hostname
      2. Overview
        2.1 API Token
      3. User
        3.1 Schemas
        3.2 User Registration
        3.3 Update User
      4. Access Policy
        4.1 Create
        4.2 Update
      

      The user can reply with 3.2, User, 3.2 + 4.1, or describe what they want.

    • When the user selects a section, fetch only that section's complete, cleanly-bounded text: cawplan knowledge documents get --dataset <id> --document <id> --section "<heading text>" This matches heading titles by case-insensitive substring (returns every match, so a query matching more than one heading yields multiple matches) and extracts each one's content from its own heading line up to (not including) the next heading at the same or a shallower level — it never bleeds into a neighboring section the way a fixed-line-count --grep --context <n> can. Prefer --section over --grep whenever you're targeting a specific heading from the outline; keep --grep for freeform pattern search when you don't know the exact heading or want to search body text, not titles.

    • After displaying the section, show the outline again so the user can continue browsing. Do not fetch or print the entire document again.

    • This is an Agent-side browsing pattern. Do NOT use --interactive, or run cawplan knowledge browse, from an Agent. Both require a real interactive terminal — a human running cawplan directly at one — and error out immediately when run as a subprocess (which is all an Agent ever is).

    • If the user explicitly wants terminal-based interactive browsing, tell them to run one of these themselves in a real terminal (do not attempt either on their behalf):

      • cawplan knowledge browse — the full picker: dataset → document (with live preview) → heading tree, Esc to go back a level. Best starting point if they haven't picked a document yet.
      • cawplan knowledge documents get --dataset <id> --document <id> — same heading-tree browser, scoped to a document they already picked.
  6. Create a dataset — when the user asks to create a new knowledge dataset:

    cawplan knowledge datasets create --name "<name>" [--description "<text>"] [--permission only_me|all_team_members|partial_members] [--owner-product <product_id>] [--product <product_id> ...]
    

    Confirm the dataset name with the user before creating — this is a create action, not idempotent. --owner-product makes that product the dataset's owner — required if its documents should go under that product's modules (an un-owned dataset can't use modules, and module uploads into it fail with BAD_REQUEST). --product (repeatable) only links the dataset to products for product-scoped search; it does not grant module use. Pass both with the same id when the user wants a product's dataset organized by module. The response includes the new dataset's id; use it directly for the next documents upload call instead of re-running datasets list to look it up.

  7. Upload documents into a dataset — when the user asks to add/upload files or text content into a dataset (existing or just-created):

    # real files (pdf/docx/markdown/...), batch: repeat --file for multiple in one dataset
    cawplan knowledge documents upload --dataset <id> --file <path> [--file <path> ...]
    
    # plain-text content from local files, batch: repeat --text-file for multiple
    cawplan knowledge documents upload --dataset <id> --text-file <path> [--text-file <path> ...]
    

    <id> must come from datasets list or datasets create. Do not mix --file and --text-file in one call — the CLI rejects that; run the command twice for a mixed batch. Confirm the target dataset and file list with the user before uploading, since this mutates the dataset.

    To upload into a module, add --module <module_id> (from datasets modules --product <owner_product_id>); it applies to every --file/--text-file in that call. The module must belong to the dataset's owner product (datasets create --owner-product), otherwise the API returns BAD_REQUEST.

    If the module doesn't exist yet, create it first (confirm the name, and the parent if any, with the user — this is a create action):

    cawplan knowledge modules create --product <owner_product_id> --name "<module name>"
    cawplan knowledge modules create --product <owner_product_id> --name "<sub-module name>" --parent <parent_module_id>
    

    --parent nests the new module under an existing one (ids from modules list --product <id>, same output as datasets modules); omit it for a top-level module. The tree is at most 5 levels deep. Use the returned data.id as --module for the upload.

    --file uploads are asynchronous server-side (each file runs a convert-to-markdown → index pipeline). By default the CLI submits one request per file and polls each to completion before returning, so the command can take a while for larger files — that's expected, not a hang. Do not re-run the command if it seems slow. If the user wants to fire-and-forget instead of waiting, add --no-wait; it returns immediately with job_ids per file, which can be checked later with:

    cawplan knowledge documents job-status --dataset <id> --job <job_id>
    

    --text-file uploads are synchronous (no job/polling involved) — the command returns as soon as each document is created.

  8. Update an existing document — when the user asks to edit/replace/re-sync a document that's already in the knowledge base (not a new upload):

    cawplan knowledge documents update --dataset <id> --document <id> --text-file <path>
    # or, for a real file:
    cawplan knowledge documents update --dataset <id> --document <id> --file <path>
    

    <dataset id> and <document id> must come from datasets list/documents list — confirm which existing document is being replaced before running this, since it overwrites content. To file the document under a different module use documents set-module (step 9), not update. Same async/sync split and --no-wait/--poll-interval/--poll-timeout flags as upload's --file case.

  9. Move a document into / out of a module — when the user asks to (re)file an existing document under a module, or take it out of one:

    cawplan knowledge documents set-module --dataset <id> --document <id> --module <module_id>
    cawplan knowledge documents set-module --dataset <id> --document <id> --clear
    

    Pass exactly one of --module/--clear. Same owner-product rule as upload's --module. Confirm the document and target module with the user first, since this mutates the document.

Output

Every level follows the Navigation Model above: AskUserQuestion for a bounded one-shot pick, plain-text numbered/nested list for large or iterative browsing. For a document's heading tree specifically, that split lands at the top level: AskUserQuestion for the level-1 headings when they fit the caps, plain-text free-form for everything below that. Either way, show the level the user was browsing again after a leaf result so they can continue.

  • Dataset listing: table/list of name, id, document_count — the root of the tree; filter to non-empty before offering a pick.
  • Document listing: table/list of document names/ids for the requested dataset, noting total count if paginated — second level.
  • Search results: grouped by dataset → document (not a flat ranked list), with source metadata per fragment; note whether the search was scoped to one dataset or ran across all accessible datasets. Each hit should be drillable into that document's outline or full section.
  • Document outline: level-1 headings as a clickable AskUserQuestion pick when they fit the caps (≤16 total, ≤4 per question); everything below the top level as a plain-text menu with free-form user selection — not raw content. Where a node's preview is worth inlining (to tell similarly-named headings apart), inline that field verbatim — never a summary you wrote yourself.
  • Document content: quote/summarize directly from the --section/--grep match you pulled, not from a re-assembly of prior search hits — the leaf of the tree.
  • Create/upload: confirm what was created/uploaded (dataset id/name, or per-document id/name from the response) — do not assume success without checking code in the response.

Decision Guide

  • User gives no starting point at all ("what's in the knowledge base?"): datasets list — the root of the tree; filter to non-empty datasets, then AskUserQuestion if that fits ≤16, else plain text.
  • User names a specific dataset (or a few) ("search the X dataset for..."): resolve each with datasets list first, then search --dataset <id> (repeat for multiple).
  • User names a CawPlan product instead of a dataset ("what's in the knowledge base for X?"): datasets list --product <product_id> (owned + linked datasets); first check datasets modules --product <product_id> — if it has modules and the user didn't name one, offer them as a pick, then add --module <module_id> to the list call (owned datasets with at least one document under that module; linked ones are excluded); if it has none, skip straight to the product-only list.
  • User asks a general question with no dataset context: go straight to search with no --dataset.
  • User asks "what documents are in X": documents list --dataset <id>, not search.
  • User asks for a document's full content/summary, or search results only surface a title/fragment with no body: documents get --dataset <id> --document <id>.
  • User wants to browse/explore a long or unfamiliar document rather than a known section: documents get --outline, offer the level-1 headings as an AskUserQuestion pick if they fit the caps (else plain text), then for any deeper level present the remaining tree as a plain-text menu with free-form selection, then loop --section "<heading>" → outline → section on request.
  • User pastes a literal cawplan knowledge ... command instead of describing what they want: read it as naming a dataset/document/section, not as a script to run verbatim — resolve missing ids yourself (steps 1–2) and pick whichever flags (--outline, --section, --grep, ...) actually fit what they're asking for.
  • User asks to create a new dataset: datasets create --name <name>, after confirming the name; add --owner-product <product_id> if it belongs to a product and should use that product's modules (plus --product <product_id> to also link it for product-scoped search).
  • User asks to upload/add files or text into a dataset: documents upload --dataset <id> --file <path>... or --text-file <path>..., after confirming target dataset and files; add --module <module_id> to place them under a module of the dataset's owner product.
  • User asks to create a module (or a sub-module) to organize a product's documents: modules create --product <owner_product_id> --name <name> [--parent <module_id>], after confirming the name and parent; then upload into it with documents upload --module <id>.
  • User asks to move a document into another module or out of its module: documents set-module --dataset <id> --document <id> --module <module_id> (or --clear), after confirming.
  • User asks to edit/replace/re-sync a document that already exists: documents update --dataset <id> --document <id> --file|--text-file <path>, after confirming which existing document. Organize documents with modules (--module / documents set-module), not folders.

References

  • references/CAWPLAN_OPEN_API.md — section 12) Knowledge APIs.