Imported from leeovery/portal (
.claude/skills/workflow-knowledge/SKILL.md). Install upstream withnpx skills add leeovery/portal --skill workflow-knowledge. Copyright stays with the author.
Workflow Knowledge
CLI tool for querying the workflow knowledge base — a retrieval-augmented store of all completed workflow artifacts, searchable by semantics or keyword. This file is the API documentation layer. Load it when you need to construct a query or interpret results.
What the knowledge base is
A local semantic-search index over every completed research, discussion, investigation, and specification artifact in .workflows/, plus user-supplied imports and promoted seeds indexed as they land, the gap-analysis cache indexed when topic-discovery rewrites it, epic discovery session logs indexed at each harvest, the product-road session logs and roadmap imports (.workflows/.roadmap/) indexed at each session close and landing, and the project baseline docs indexed as each area completes. Content is stored at full fidelity — chunks are the actual text, not summaries — with provenance metadata attached: which work unit, which phase, which topic, and the source document's date.
Why it exists: to surface prior context that would otherwise be lost across work units or forgotten within one. A spec written three months ago, a discussion that rejected an approach, an investigation that ruled out a cause — all remain queryable.
What is indexed:
research(low confidence — exploratory, may contain dead ends)discussion(low-medium — conversational, may contain corrected assumptions)investigation(medium — diagnostic, tied to specific symptoms)specification(high — validated decisions, "what we decided to build")imports(low — user-shared reference material, often loose, may contain multiple topics)seeds(low — the work unit's origin: the promoted inbox item(s), verbatim capture)analysis(low — the gap-analysis cache, a meta-summary derived from low-confidence material)discovery(low — epic exploration logs: the running record, not validated decisions; topic = session, so a work unit's whole discovery is--phase discovery --work-unit {wu})baseline(low — the project baseline: brownfield assessment docs at.workflows/.baseline/{topic}.md, project-level rather than per-work-unit. Chunks carry the reserved pseudo-identitybaselinefor both work unit and work type. Observed and user-stated context about the pre-existing codebase — it informs, but is never a settled call a phase may lean on silently)roadmap(low — the product-road session logs at.workflows/.roadmap/sessions/session-NNN.md: the running record of product-altitude conversation, graded like discovery logs; topic = session. Project-level — chunks carry the reserved pseudo-identityroadmapfor both work unit and work type. Roadmap imports land at.workflows/.roadmap/imports/{name}.mdunder the same identity with phaseimports)
What is NOT indexed: planning, implementation, review. These phases describe execution, not knowledge. Searching them would surface task IDs and code fragments, not insight. Operational .state/ files (migrations, environment-setup) are also excluded — only the gap-analysis cache filename is accepted from .state/.
Invocation
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs <command> [args]
Every skill that calls this must declare Bash(node .claude/skills/workflow-knowledge/scripts/knowledge.cjs) in its allowed-tools frontmatter.
To list commands and options, use --help / -h / help — writes usage to stdout, exits 0. Invoking the CLI with no arguments writes usage to stderr and exits 1.
query — search the knowledge base
Single query
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs query "<search term>" [flags]
Batch query
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs query "<term1>" "<term2>" "<termN>" [flags]
Multiple positional arguments run separate searches in one invocation, merge the results, deduplicate by chunk ID (highest score wins), then apply --limit to the merged set. Efficient — one store load, multiple searches. Encouraged when you want to attack the same topic from different angles.
Flags
| Flag | Behaviour |
|---|---|
--work-unit <wu> |
Filter to one or more work units. Comma-separated list accepted. Hard filter — non-matching chunks excluded |
--work-type <type> |
Filter results to a work type. Comma-separated list accepted (e.g., --work-type cross-cutting or --work-type epic,feature). Hard filter |
--phase <phase> |
Filter to one or more phases. Same comma-separated syntax. Hard filter |
--topic <topic> |
Filter to one or more topics. Same comma-separated syntax. Hard filter |
--boost:<field> <value> |
Re-ranking hint, NOT a filter. Boosts chunks where <field> equals <value> by +0.1 per match, additive. Repeatable. Valid fields: work-unit, work-type, phase, topic, confidence. Use it to say "I'm currently working in auth-flow, prefer its context" via --boost:work-unit auth-flow — results from other work units still appear, just ranked lower |
--limit <n> |
Cap result count after merge + re-rank. Default 10 |
Search modes
Two modes, auto-selected based on project config:
- Hybrid (default when an embedding provider is configured): keyword + vector search combined, results re-ranked by any
--boost:<field>directives you pass, plus an always-on confidence-tier boost and a progress-based decay that down-ranks units the project has moved past. - Keyword-only (when no provider is configured): full-text search only. Still useful — you lose semantic expansion but exact-term queries work. The output prepends a note:
[keyword-only mode — configure embedding provider for semantic search]. This is a supported degraded mode, not a broken state.
Query construction
Use natural language, not topic slugs. A query is a short description of what you're looking for, framed the way the original author would have written about it.
- Good:
"OAuth2 PKCE flow for mobile clients" - Good:
"why we ruled out email as a primary identity field" - Poor:
"auth-flow"(topic slug — weak semantic signal) - Poor:
"auth"(too broad — matches everything auth-related)
Descriptive, specific, phrased in the language likely to appear in the source material. Multiple queries from different angles are encouraged — one for the decision, one for the constraint, one for the rejected alternative.
Output format
[3 results]
[specification | auth-flow/auth-flow | high | 2026-03-15]
User identity uses UUID v7. Email is a profile attribute, not an identifier.
Source: .workflows/auth-flow/specification/auth-flow/specification.md
[discussion | payments-overhaul/data-model | low-medium | 2026-03-10]
Debated UUID vs email for identity. UUID won because email changes are common.
Source: .workflows/payments-overhaul/discussion/data-model.md
[research | payments-overhaul/identity | low | 2026-02-28]
Explored identity approaches. Email-based ruled out due to GDPR right-to-erasure.
Source: .workflows/payments-overhaul/research/identity.md
- Header line:
[N results]where N is the merged, deduplicated, re-ranked count after--limit. - Provenance line (per chunk):
[phase | work_unit/topic | confidence | YYYY-MM-DD]. Date is the source document's date (file mtime). - Content: the chunk text verbatim. No summarisation, no truncation.
- Source line: the path to the source artifact. Use this with the two-step retrieval pattern below.
- Blank line between chunks.
- Empty results:
[0 results]— no provenance lines, nothing else. Treat as "no prior context found" — move on. - Stub-mode note (when applicable): prepended as the first line before the header —
[keyword-only mode — configure embedding provider for semantic search].
Confidence tiers — how to weigh results
Confidence is intrinsic to the source phase. It tells you how much weight to give the content, not whether to use it.
| Tier | Meaning |
|---|---|
high |
Specification — a decision that was validated and written down. Trust the what, verify the why against the source if it matters |
medium |
Investigation — diagnostic work tied to specific symptoms. Trust the diagnosis, but check whether the symptom is still current |
low-medium |
Discussion — conversational, may contain assumptions that were corrected later in the same file. Read for context, not conclusions |
low |
Research, Imports, Seeds, Analysis, Discovery, Roadmap, or Baseline — research is exploratory (may be a dead end, rejected path, or unvalidated idea); imports are user-supplied reference material (often loose, may cover multiple topics surface-level); seeds are raw inbox captures (the work unit's origin, unrefined); analysis caches are meta-summaries derived from research/discussion (themes and gaps surfaced, not validated decisions); discovery logs are the running exploration record; roadmap session logs are the product-altitude exploration record, the same grade one level up; baseline docs are observed/stated context about the pre-existing codebase, reference never record. Disambiguate via the provenance line's phase field |
Low confidence is not low value. A research chunk that rejected an approach prevents the next work unit from re-exploring the same dead end. A discussion chunk showing a corrected assumption explains why the spec says what it says. Don't filter out low-confidence results — weigh them.
Two-step retrieval pattern
- Query returns chunks with provenance. Lightweight — lands in your context window.
- Read the source file (from the
Source:line) only if a chunk looks load-bearing for what you're doing.
Don't read source files for every result. Most queries produce a couple of chunks that are mildly relevant and one that's directly relevant — read the one, skim the rest from the chunk text alone. This keeps context lean while preserving full-fidelity access on demand.
What NOT to do
- Do not dump large result sets speculatively.
--limit 50with a vague query produces noise. Prefer a focused query with the default limit. - Do not use topic slugs as search terms.
"auth-flow"is a weak semantic signal. Describe the thing, don't name it. - Do not query while authoring the spec. Spec turns discussion decisions into a golden document. Cross-cutting concerns merge at planning time via an explicit cross-cutting query, not during spec authoring. Querying mid-spec pulls the spec away from its own source material. The lone exception is the grouping/consolidation analysis at specification entry, which may run one advisory
--phase discussionquery to surface candidate consult references — that is intake (choosing inputs), not authoring, and never injects content into the spec body. - Do not prepend metadata to the query string. The CLI already filters by
work-unit,work-type,phase,topicvia flags."auth-flow specification UUID identity"is worse than"UUID identity"with--phase specification. - Reach for
--boost:<field>before--work-unit. Filtering by work unit excludes cross-work-unit context — usually the opposite of what you want.--boost:work-unit <current>nudges results toward your current work unit while keeping prior work from other units in the pool. Stack multiple boosts (--boost:work-unit X --boost:phase specification) when your query wants multi-dimensional preference, not exclusion.
check — readiness probe
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs check
Exit code is always 0 (unless the filesystem itself is unreadable). Output on stdout:
ready— knowledge base is initialised and the store is loadablenot-ready— missing directory, missing config, missing store, or unloadable store
Skills branch on the stdout string, not the exit code. Used in Step 0 of entry-point skills (via engine boot) to detect an uninitialised knowledge base and route into the knowledge gate, which drives knowledge setup through its non-interactive forms.
index — write to the store
# Single artifact (used by phase-completion steps)
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs index <path/to/artifact.md>
# Bulk catch-up (no args — finds all unindexed completed artifacts)
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs index
- With a file: re-indexing replaces existing chunks for that file (idempotent). The path must match
.workflows/{work_unit}/{phase}/...so identity can be derived. For imports, the path is.workflows/{work_unit}/imports/{filename}.mdand the topic is the filename basename without extension. For the gap-analysis cache, the path is.workflows/{work_unit}/.state/discovery-gap-analysis.md; the phase isanalysisand the topic isgap-analysis. For baseline docs, the path is.workflows/.baseline/{topic}.md; the work unit and phase are bothbaseline(removal isremove --work-unit baseline [--phase baseline --topic <t>]). For roadmap material, the paths are.workflows/.roadmap/sessions/session-NNN.md(work unitroadmap, phaseroadmap, topic = session basename) and.workflows/.roadmap/imports/{name}.md(work unitroadmap, phaseimports); removal isremove --work-unit roadmap [--phase … --topic …]. - Without args: discovers every completed artifact across all work units and indexes anything missing. Used by setup and manual catch-up.
- Failures are retried (exponential backoff). Files that still fail are pushed to a pending queue and retried on the next
indexcall. - Exits non-zero if the file doesn't exist or the path can't be parsed.
Typically invoked by processing skills at phase completion — not queried by Claude during a phase.
remove — remove chunks
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs remove --work-unit <wu> [--phase <p>] [--topic <t>] [--dry-run]
Removes chunks matching the given filter. Granularity:
--work-unit <wu>alone — removes every chunk for that work unit--work-unit <wu> --phase <p>— narrows to one phase--work-unit <wu> --phase <p> --topic <t>— narrows to one topic
Used when a spec is superseded or promoted, when a work unit is cancelled, or when catching up after a manifest change. --topic requires --phase. --dry-run counts what the filter matches and reports it without touching the store.
Output: Removed N chunks for {scope}. Exits non-zero on usage errors.
status — full health report
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs status
Human-readable report of the store's state: chunk counts by work unit, phase, and work type; last-indexed timestamp; provider info; pending queue; provider-mismatch warnings; orphan detection; unindexed completed artifacts; manifest-knowledge consistency checks. Not used in skill automation — intended for debugging and user inspection.
rebuild and compact — maintenance commands
rebuild— destructive. Deletes the existing index and re-indexes everything currently discoverable: completed phase artifacts (research, discussion, investigation, specification), all entries on each work unit'simports[]andseeds[]arrays, epic discovery session logs (discovery/sessions/session-NNN.md), any present gap-analysis caches (.state/discovery-gap-analysis.md), the roadmap sessions and imports (.workflows/.roadmap/), and the project baseline docs (.workflows/.baseline/*.md). Prompts the user to typerebuildliterally to confirm. Human-only — Claude cannot run it (interactive prompt). Non-deterministic: rebuilt chunks won't match the originals (embedding variance, edited artifacts).compact [--dry-run]— storage backstop. Removes a work unit's non-spec chunks once their retrievabilityRhas decayed belowdecay_prune_below— i.e. once enough later work has completed that they're effectively unreachable in query ranking. Decay is progress-based (how much work completed after the unit, weighted by work type), not wall-clock; specifications are exempt;falsedisables it.--dry-runpreviews without deleting.
Skills do not call these directly during normal operation. Users run them manually.
setup — initialise the knowledge base
Handles system config (~/.config/workflows/config.json), project init (.workflows/.knowledge/), and initial indexing of all completed artifacts. Config resolves defaults ← system ← project; null unsets a key. Setup writes provider identity only — the tuning keys similarity_threshold (the vector leg's cosine floor, 0.3), decay_prune_below (0.05; false disables), decay_base_stability (5) and decay_weights (per work type) are honoured as overrides in either file and never written. Two surfaces: an interactive wizard (no flags) and non-interactive forms (flag-dispatched) that skills can run.
The API key never passes through a flag, a chat, or stdout. There is deliberately no --key flag — any setup invocation carrying one is refused (argv lands in shell history and process listings). Keys resolve from the provider env var ($OPENAI_API_KEY — wins) or ~/.config/workflows/credentials.json (mode 0600, written by --key-only or the wizard). Setup output names active settings only (provider · model), never key material.
Non-interactive forms
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs setup --from-system
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs setup --keyword-only
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs setup --provider openai --model <m> [--dimensions <d>]
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs setup --provider openai-compatible --base-url <u> --model <m> --dimensions <d>
node .claude/skills/workflow-knowledge/scripts/knowledge.cjs setup --key-only [--provider <id>]
--from-system— reuse the existing system config: resolve the key (env → credentials; providerless configs need none), validate provider configs with one test embed, create the project store, bulk-index existing artifacts, print the active-settings summary. Refuses clearly when the system config is missing/invalid or the openai key is unresolvable (the message names the env var and the--key-onlyremedy).--keyword-only— project-level keyword-only init: project config, empty store, provider-null metadata. Never touches the system config — when the system layer names a provider, the project config pinsprovider: nullso this project genuinely runs keyword-only. Idempotent; partial states fill in; a store without metadata refuses towardknowledge rebuild.--provider ...— write the system config from flags (validation embed first — a broken provider never lands on disk), then proceed as--from-system. Every system-config write rewrites theknowledgekey whole — provider identity only, so a tuning override set there (similarity_threshold,decay_prune_below) does not carry across a provider change; other subsystems' top-level keys (e.g.session) are preserved.openairequires a resolvable key;openai-compatibleallows keyless endpoints and requires--dimensions(must match the model's native output).--key-only— the terminal detour: a masked readline prompt for the key alone (TTY-required; non-TTY aborts), written tocredentials.json(mode 0600), then exit.--providerselects which provider the key is stored under (defaultopenai).
Interactive wizard
setup with no flags runs the guided wizard — human-only, prompts throughout via readline; non-TTY invocations abort with knowledge setup requires an interactive terminal. Safe to re-run: per-step prompts detect existing state and offer skip or reconfigure; the bulk index at the end only processes missing artifacts.
The provider menu offers openai (cloud, requires an API key), openai-compatible (any local/self-hosted OpenAI-compatible /v1/embeddings endpoint — LM Studio, Ollama, vLLM, LiteLLM), or skip (keyword-only). For openai-compatible, the wizard collects base_url (required), model, and dimensions; the API key is optional (press Enter to omit for open servers) and is stored only in credentials.json — there is no env-var override. base_url is consumed only under the openai-compatible provider and ignored under openai. Configured dimensions must match the local model's native output; the validation embed fails loudly on a mismatch.
Exit codes
0— success, orcheckreporting either state- Non-zero — usage error, file not found, unparseable path, lock contention exceeded, or unrecoverable provider mismatch
query with zero results exits 0 and prints [0 results]. check exits 0 for both ready and not-ready. Both semantics are intentional — skills branch on output, not on the exit code.