A spec-kit extension that injects architectural decisions, API contracts, and shared conventions from other Git repositories into your spec-kit workflow.
When working in a microservices or multi-repo environment, architectural decisions and API contracts are scattered across repositories. This extension lets you declare which repositories are "knowledge sources" for your project. During /speckit-specify, /speckit-clarify, /speckit-plan, and /speckit-tasks, the assistant automatically loads and surfaces relevant knowledge from those sources โ so the generated spec, plan, and tasks reflect the real contracts and decisions your team has already made.
Key features:
- ๐ Explicit opt-in: each project declares its own knowledge sources
- ๐ฟ Git-native: clones repositories with sparse checkout (only the paths you need)
โ๏ธ Offline-friendly: cached knowledge is used when sources are unreachable- ๐ Path-filtered: scope reads to
specs/,docs/decisions/, or any subdirectory โ ๏ธ Conflict-aware: duplicate paths across sources are flagged, both versions surfaced- ๐ Revision-pinned: lock a source to a branch, tag, or full commit SHA
- โณ Cache-aware: skip the network entirely while the cache is still current
- ๐ Budget-bounded: cap how much the agent is told to read โ nothing is withheld silently
- ๐ฆ Lifecycle-managed: remove, disable, or re-enable a source without hand-editing YAML
git >= 2.25(sparse-checkout support required)timeout,gtimeout, orperl, to bound network calls. Stock macOS shipsperl, which is enough; with none of the three, calls run unbounded and sync warns once.- spec-kit
>=0.15.2 - Platforms: macOS and Linux; Windows through WSL or Git Bash
specify extension search finds extensions in spec-kit's community catalog, but that catalog is searchable, not installable, by default. Install the published release from its archive:
specify extension add knowledge --from https://github.com/rnoap/spec-kit-shared-knowledge/archive/refs/tags/v1.10.2.zipspec-kit first shows an Untrusted Source prompt naming the URL. Confirm it to install.
A team that wants no prompt can list the extension in a catalog it controls and marks install-allowed. specify extension add knowledge then installs from that catalog without asking.
Install directly from a local checkout โ the path is the positional argument:
cd /path/to/your/spec-kit-project
specify extension add /path/to/spec-kit-shared-knowledge --devThis copies the five command files, the manifest, and config-template.yml
into .specify/extensions/knowledge/, scaffolds knowledge-config.yml,
registers the extension, and auto-registers the before_specify,
before_clarify, before_plan, and before_tasks hooks. The first
/speckit-knowledge-configure adds the cache entries to your .gitignore for
you โ see .gitignore.
Then reload your editor / AI agent so it picks up the new commands.
# 1a. Add a knowledge source from a REMOTE repository (single folder filter)
/speckit-knowledge-configure https://github.com/your-org/payment-service specs/
# 1b. โฆ or from a LOCAL repository on your machine
/speckit-knowledge-configure ~/repos/payment-service specs/
# 1c. โฆ or with NO filter (indexes every .md file in the repo, minus the default exclusions)
/speckit-knowledge-configure [email protected]:your-org/architecture-decisions.git
# 1d. โฆ or scoped to MULTIPLE folders
/speckit-knowledge-configure ~/repos/shared-contracts specs/ docs/decisions/
# 2. Sync (fetch & cache)
/speckit-knowledge-sync
# 3. Use โ run /speckit-specify (or clarify, plan, tasks), accept the sync prompt, and cross-repo context is injectedThe examples use the hyphenated form, such as /speckit-knowledge-sync. That is what agents that install commands as skills show, including GitHub Copilot and Claude Code. Agents that install slash commands show the dot form listed in the table below, such as /speckit.knowledge.sync; Codex shows $speckit-knowledge-sync, and Kimi /skill:speckit-knowledge-sync. spec-kit's own commands follow the same pattern: /speckit-specify or /speckit.specify.
| Command | Dot form | What it does |
|---|---|---|
๐ง /speckit-knowledge-configure |
/speckit.knowledge.configure |
Initialize or edit the knowledge source configuration |
๐ /speckit-knowledge-sync |
/speckit.knowledge.sync |
Refresh the local cache for all configured, enabled sources |
๐ /speckit-knowledge-search |
/speckit.knowledge.search |
Browse and search the knowledge corpus |
๐ /speckit-knowledge-status |
/speckit.knowledge.status |
Show reachability, revision, last sync, item count, budget, and cache age |
๐ /speckit-knowledge-remove |
/speckit.knowledge.remove |
Remove, disable, or re-enable a configured source |
๐ง /speckit-knowledge-configure [url-or-path] [--revision <rev>] [--label <label>] [path_filter ...]
Initialize or edit the knowledge source configuration for the current project. Accepts:
- A remote Git URL โ HTTPS (
https://github.com/your-org/your-repo) or SSH ([email protected]:your-org/your-repo). - A local filesystem path to a Git repository โ use this when the repo is already cloned on your machine and you want to avoid network roundtrips, or when it lives on a private network. Both absolute paths and
~-prefixed paths are supported (the tilde is expanded to$HOME). - An optional revision โ a branch, tag, or full commit SHA. See Pinning a revision.
- An optional label โ choose the name shown for the source. If omitted, the command derives it from the URL or local path.
- Zero or more path filters after the URL/path. With zero filters every
.mdfile in the repo is indexed, except root-level dot-directories (.specify/,.github/, โฆ) andAGENTS.md,CLAUDE.md,GEMINI.md. With one or more filters, indexing is restricted to the.mdfiles in those folders only.
Examples:
# Remote URL, single folder filter
/speckit-knowledge-configure https://github.com/your-org/payment-service specs/
# Choose a label, with no folder filter
/speckit-knowledge-configure https://github.com/your-org/payment-service --label payments-main
# Choose a label and multiple folder filters
/speckit-knowledge-configure https://github.com/your-org/payment-service --label payments-v2 specs/ docs/decisions/
# Local absolute path, single folder filter
/speckit-knowledge-configure /Users/devuser/repos/payment-service specs/
# Local path containing a space: quote it
/speckit-knowledge-configure '~/My Repos/kb' specs/
# Local path with tilde, NO filter (indexes every .md in the repo, minus the default exclusions)
/speckit-knowledge-configure ~/repos/architecture-decisions
# Local path, MULTIPLE folder filters
/speckit-knowledge-configure ~/repos/shared-contracts specs/ docs/decisions/
# Pinned to a tag
/speckit-knowledge-configure https://github.com/your-org/payments --revision v2.4.1 specs/
# Use the derived label by omitting the optional label
/speckit-knowledge-configure https://github.com/your-org/architecture-decisions specs/
# Same, using the @ shorthand (HTTPS and local paths only โ not SSH)
/speckit-knowledge-configure https://github.com/your-org/[email protected] specs/Quoting: arguments are split at spaces. A URL or path without spaces needs no quotes; quote a local path that contains one, with single or double quotes:
| You type | url |
path_filter |
|---|---|---|
~/repos/kb specs/ |
~/repos/kb |
specs/ |
'~/My Repos/kb' specs/ |
~/My Repos/kb |
specs/ |
~/My Repos/kb specs/ |
~/My, which does not exist, so nothing is written |
Repos/kb and specs/ |
Output:
โ
knowledge-config.yml updated.
โ
.gitignore updated with cache exclusion entries.
Configured sources:
1. payment-service โ https://github.com/your-org/payment-service (path: specs/)
2. architecture-decisions โ ~/repos/architecture-decisions (path: all .md files)
3. shared-contracts โ ~/repos/shared-contracts (paths: specs/, docs/decisions/)
Flags:
--verboseโ print full YAML after write--label <label>โ optional source name. It may appear before or after the path filters.
Refresh the local cache for all configured, enabled knowledge sources.
๐ Syncing cross-repo knowledge sources...
payment-service โ
fresh 12 items (synced 2026-06-11T14:00:00Z)
payments-v2 โญ๏ธ current 12 items (cached 12m ago; policy 4h โ no network)
โ
Knowledge index updated: 24 items from 2 sources.
โ .specify/extensions/knowledge/knowledge-index.md
Flags:
--verboseโ list all files loaded and items written to index--forceโ ignore the cache freshness policy and fetch every source--no-context-outputโ suppress the trailing context block for the AI agent (diagnostic use only)
Browse and search the knowledge corpus without triggering a full spec workflow.
๐ Search: "payment session"
Found 2 items across 1 source:
๐ payment-service โบ specs/events/payment-completed.md
Tags: specs, events, payment-completed
Excerpt: "Payment completed event fired after successful charge..."
Flags: --source <label>, --tag <tag>, --verbose
Display current state of all configured sources.
๐ Shared Knowledge Status
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโ
โ Source โ Reachability โ Revision โ Status โ Items โ Cache Age โ
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโผโโโโโโโโโโโโโค
โ payment-service โ โ
reachable โ default โ fresh โ 12 โ 14 min โ
โ payments-v2 โ โ
reachable โ v2.4.1 โ fresh โ 12 โ 3d โ
โโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโ
Freshness policy: payments-v2 7d (per-source) ยท others 4h (project)
With a context budget configured, Items shows indexed-of-total
and an extra Budget column names the limit that produced each number.
Flags: --verbose โ list all cached file paths per source
Remove, disable, or re-enable a configured source. Named for its destructive mode,
but it owns the whole activation lifecycle โ there is deliberately no second
command that also writes enabled.
# Delete the entry AND purge its cache
/speckit-knowledge-remove payments-v2
# Keep the entry and the cache; just stop consuming it
/speckit-knowledge-remove payments-v2 --disable
# Bring it back โ no re-download, because the cache was kept
/speckit-knowledge-remove payments-v2 --enable
# No identifier: pick from a list of configured sources
/speckit-knowledge-removeTakes a label, which is unique by construction. A URL is also accepted, but only when it resolves to exactly one source โ with revision pinning a URL can match several, and the command lists the candidates rather than guessing:
โ "https://github.com/org/payments" matches 2 configured sources:
payments-v2 (revision: v2.4.1)
payments-main (revision: main)
Re-run with a label to disambiguate. Nothing was changed.
Flags: --disable, --enable, --verbose
Stop, pause, or resume a source without editing YAML:
| You want to | Use | Entry | Cache |
|---|---|---|---|
| ๐๏ธ Stop consuming it permanently | remove <label> |
deleted | purged |
| โธ๏ธ Pause it temporarily | remove <label> --disable |
kept, enabled: false |
kept |
remove <label> --enable |
kept, enabled: true |
reused |
๐ก Prefer --disable over remove for a pause. The cache is retained, so
re-enabling costs no download at all. Removal purges immediately rather than
deferring to a cleanup step โ "remove" that leaves the bytes behind is the more
surprising reading.
When the last enabled source goes away, sync reports the project as unconfigured and deletes the knowledge index. A stale index would let an agent keep citing knowledge the project no longer declares โ a silent failure that looks like success.
Each source may pin a revision โ a branch, tag, or full commit SHA:
sources:
- url: https://github.com/org/payments
label: payments-v2
revision: v2.4.1
path_filter: specs/Omit it and the source tracks the remote's default revision.
- Two revisions of one repository can be configured side by side. They hold separate caches and cannot contaminate each other.
- Because a URL then no longer identifies one source, every source carries a
unique label, enforced when it is added. The label is what
removetakes. - Overlapping file paths between two revisions of the same repository are not reported as conflicts. Conflicts are reported only between sources whose repository identity differs โ identity ignores the access protocol, so HTTPS and SSH for the same repo are the same repo.
- Abbreviated SHAs are not supported. A 7-character prefix is not fetchable and is not stable โ one that is unique today can become ambiguous as the repo grows.
- SSH URLs already contain
@, so pass the revision with--revisionrather than the<url>@<rev>shorthand.
~/repos/payments and
https://github.com/org/payments, overlapping paths between them are reported
as conflicts. Reading the local clone's origin would fix this but adds a failure
mode and a trust question to a purely advisory report, so it is deliberately not
done.
A teammate's spec can be a source before it is merged, as long as its branch is pushed. Add the repository you share, pin the teammate's branch, and filter to the spec's folder:
/speckit-knowledge-configure https://github.com/your-org/your-repo --revision 012-order-tracking specs/012-order-tracking/- Filter to the spec's folder. The branch also carries everything already on
main; without a filter you index your own specs a second time. - Use the remote URL. A local path can only check out branches that exist locally in that clone.
- A branch moves. Every refresh fetches its latest commit. To keep the version you reviewed, pin its full commit SHA instead.
- Remove the source when either branch merges. After the teammate's branch
merges, the spec reaches you through
main. Merging yours with the source still configured makes every teammate sync a temporary branch. Run/speckit-knowledge-remove <label>. - A deleted branch is not an error. Sync keeps serving the last cached copy and
reports the source as
cached.
Set max_cache_age and a sync will skip the network entirely when the cached
content is still current:
max_cache_age: 4h # project-wide
sources:
- url: https://github.com/org/payments
label: payments-v2
max_cache_age: 7d # overrides the project value for this sourceFormat is <N><unit> with unit m, h, or d โ 30m, 4h, 7d.
This is what makes the four automatic hooks affordable. Without a policy, one
feature cycle (specify โ clarify โ plan โ tasks) refetches every source four
times. With 4h set and the cycle finished inside that window, it fetches once.
- Omitted is the default, and means no policy: every sync fetches.
- A skipped source is reported as
โญ๏ธ current, distinct fromfresh, so you can see no network access happened. --forceignores the policy. It is unreachable from an automatic trigger โ the hook entries declare no arguments โ so hook-driven syncs always respect the policy.- It is a hint, not an expiry. An over-age cache is still served when its source cannot be reached. Setting a policy never leaves you with less knowledge than you had without one.
Every sync tells the agent to read the index in full and open every file it references. That instruction has no upper bound, and the corpus grows with your team. Two optional ceilings give it one:
max_items: 120 # project-wide
max_bytes: 1mb # project-wide
sources:
- url: https://github.com/org/big-docs
label: big-docs
max_items: 40 # sub-ceiling for this sourcemax_items is a whole number โฅ 1. max_bytes is <N>kb or <N>mb in binary
units. Both are optional and independent โ set either, both, or neither.
A per-source ceiling is a sub-ceiling, not an override. It can only lower what
that source contributes, never raise it. This is deliberately unlike
max_cache_age, which sits in the same two positions in the file but whose
per-source value replaces the project value. A per-source budget above the
project ceiling is clamped down to it and the clamp reported โ it never
invalidates the source.
The rule is fixed, owned by the extension, and influenced by no configuration value โ so you can work out your own index on paper. Per dimension:
share = โceiling รท number of sources with itemsโ- Every source needing no more than its share is satisfied in full, and releases the remainder to the others. Repeat from 1 with what is left.
- When a pass satisfies nobody, everyone left takes
share. - Any remainder goes one item each, in ascending label order.
Within a source, items are taken in ascending path order โ not smallest-first. Smallest-first would fit more files, but adding one large document would silently change which unrelated small ones survive, and you could no longer predict your own corpus.
โ๏ธ Worked example. Ceiling 120 items; four sources holding 312 / 55 / 88 / 12.
Share is โ120/4โ = 30; the 12-item source is satisfied and releases 18;
โ108/3โ = 36 and nobody else fits, so the other three take 36 each.
36 + 36 + 36 + 12 = 120. A flat quarter-share each would have injected only
102 and stranded 18 โ releasing unused share is what fills the ceiling.
big-docs โ
fresh 36 items
โ๏ธ budget: 36 of 312 items (118kb of 3.4mb) โ limit 36 items (share of 120)
โ ๏ธ Withheld: big-docs โบ huge.md (2.4mb) exceeds the 1mb size ceiling on its own;
excluded without consuming the budget.
โ
Knowledge index updated: 120 items from 4 sources (347 withheld, 3.5mb).
- The index header carries the same counts, and marks itself
โ ๏ธ Partial. - The agent is told the corpus is partial, so it will not present it as complete.
--verboselists every withheld path./speckit-knowledge-searchstill finds withheld items. The budget bounds what is injected automatically, not what the project knows โ nothing leaves the cache.
Two versions of a conflicting path are kept or dropped together; the budget never picks a winner between two teams' versions of the same file.
No default is applied, so nothing is withheld. Once the corpus passes 200 items or 2 mb, sync says so once per run:
โ ๏ธ This project's knowledge corpus is 340 items / 2.7mb, past the advisory
threshold of 200 items / 2mb. An agent instructed to read all of it may
exhaust its context. Consider setting `max_items` or `max_bytes`.
It changes nothing it reports on โ same standing as the existing "more than ten sources" warning.
๐ No flag raises or bypasses the budget. --force overrides cache freshness
only. The automatic sync points are exactly where a context overflow does the most
damage, so there is no escape hatch reachable from them.
knowledge-config.yml is committed and shared. That is the point โ it declares
your team's knowledge sources โ but it also means a merged pull request adding one
url line causes that location to be fetched on every teammate's machine and
every automated environment that later runs a sync. Including via the four
automatic hooks, which the reviewer who approved the change may never invoke
themselves.
This section states what that grants, and what it does not.
Fetched content is read as data. It is never executed.
- No build step, script, or repository hook from a source is ever run.
- No configuration value and no fetched file name is ever run as a command.
- Clones use
--no-checkoutand--filter=blob:none; only.mdfiles under yourpath_filterare read. - Nothing from a source is written outside
.specify/extensions/knowledge/cache/<slug>/.
| Defense | Covers | Does not cover |
|---|---|---|
Values beginning with - are rejected |
Argument injection into git โ a revision of --upload-pack=<cmd> would be remote code execution |
A legitimately shaped value pointing somewhere hostile |
-- before every config-derived git operand |
The same, if validation is ever bypassed | As above |
| Validation runs on read, not just write | Values arriving by hand-edit or by PR | Values that are valid but unwanted |
url and path_filter accept only ASCII letters, digits and . _ ~ : / @ % + -, plus spaces in a local path |
A merged configuration change running commands on teammates' machines | A well-formed value pointing somewhere hostile |
| Every value reaches the shell inside single quotes, and fetched file names only through shell variables | The same, if validation is ever bypassed, and hostile file names in a source | As above |
--no-checkout + --filter=blob:none |
Fetching a working tree wholesale | The subsequent checkout, which does write the filtered files |
Every network call is time-bounded: 10 s, 30 s for a full-history fetch, 5 s in status |
A hung or tarpitting host | A fast host serving hostile content |
path_filter |
Corpus size and blast radius | Anything inside the filtered paths |
| Symbolic links are never followed: clones write them as plain files, and only regular files git does not record as links are indexed | A source pointing the agent at a file outside the cache, such as ~/.ssh/id_rsa or the project's .env |
Hostile content in an ordinary .md file |
Hooks are optional: true |
Unattended fetches โ you are prompted | A prompt you accept |
| Manifest SHA-256 integrity check | Local cache corruption | Content that was hostile when fetched |
Stated plainly, because a trust model that lists only its strengths is worse than none:
- Prompt injection. Fetched markdown enters the agent's context uninspected and unsanitized. A source can contain instructions aimed at your agent. There is no reliable general defense and this extension does not attempt one.
- Credential exposure. Your local git credential helper decides what to send to a configured host. The extension neither scopes nor filters it. A URL pointing at an attacker-controlled host may be offered credentials.
Treat it as a security-relevant change, not a config tweak. Before approving:
- Is the
urla repository your organisation controls, or one you have reason to trust with your credentials and your agent's context? - Is it reached over a host you expect? Check for lookalike domains.
- Is
revisionpinned to a tag or full SHA, rather than tracking a branch someone else can move under you? - Does
path_filterscope the read to what is actually needed? - Do you understand that this will fetch on your machine the next time you run any spec-kit command with a knowledge hook enabled?
If a source is outside your organisation, prefer a pinned revision and a narrow
path_filter, and read what it contains before merging.
The hooks declared in extension.yml are auto-registered by spec-kit when the extension is installed via specify extension add โ no manual edits to .specify/extensions.yml are required. See the spec-kit Extension Development Guide for the underlying mechanism.
| Hook | Runs before | Why |
|---|---|---|
before_specify |
/speckit-specify |
Spec drafting reflects current contracts |
before_clarify |
/speckit-clarify |
Ambiguities resolved against real decisions |
before_plan |
/speckit-plan |
Planning sees the current architecture |
before_tasks |
/speckit-tasks |
Task breakdown reflects current contracts |
All four are optional, so spec-kit prompts before each sync and you can decline. Set a max_cache_age and an accepted hook skips the network while the cache is still current โ with 4h and a feature cycle that fits inside it, you fetch once instead of four times.
The AI agent's knowledge-reading behavior is driven by the Context Output block emitted at the end of every successful or degraded sync (see commands/speckit.knowledge.sync.md ยง "10. Emit Context Output for AI Agents block").
๐ค No-op when not configured: If
.specify/extensions/knowledge/knowledge-config.ymldoes not exist, all commands exit 0 with a "not configured" message. Projects without the extension are completely unaffected.
Outside the spec-kit commands, the agent has no reason to look at the knowledge
index. The cache is git-ignored, so its file search usually skips it too. So every
sync keeps a short section in your project's root AGENTS.md, which agents such as
Copilot and Codex read in every chat:
<!-- KNOWLEDGE START -->
## Shared knowledge
For any question about another repository or a knowledge source listed below:
1. Read `.specify/extensions/knowledge/knowledge-index.md` first.
...
Sources:
- `payment-service`
<!-- KNOWLEDGE END -->Then ask "how does payment-service retry failed charges?" with no command and no
file named. The agent reads the index and cites payment-service โบ <path>.
- Sync creates
AGENTS.mdif needed, adds or refreshes the section, and writes nothing when it is already current. Nothing outside the markers is touched. - Remove,
--disableand--enablerefresh the label list at once, without a sync. The agent uses only the listed sources, so a disabled source stops being cited even though the index still lists it until the next sync. - The section is safe to commit. It holds no timestamps or machine paths, so teammates produce the same bytes. On a fresh clone without an index, it tells the agent to suggest a sync.
- Opt out with
agents_md: falseinknowledge-config.yml. The next sync removes the section, andAGENTS.mdis left alone from then on.
See config-template.yml for the full annotated configuration schema.
Top-level fields:
schema_versionโ required; exactly"1.1". Any other value stops every command before it uses a source.max_cache_ageโ optional; project-wide freshness policy (30m,4h,7d). See Cache freshness.max_items/max_bytesโ optional; project-wide context budget. See Context budget.agents_mdโ optional; defaulttrue. Exactlytrueorfalse.falseremoves the shared knowledge section fromAGENTS.mdand leaves the file alone; an invalid value leavesAGENTS.mduntouched. See Ask in plain language.sourcesโ required; the list of knowledge sources. May be empty.
Key fields per source entry:
-
sources[*].urlโ required. Only ASCII letters, digits and. _ ~ : / @ % + -. A local path starts with/,./,../or~/and may also contain spaces, but no space followed by-. Remote addresses write spaces as%20. Must not begin with-. One of:- HTTPS Git URL:
https://github.com/your-org/your-repo - SSH Git URL:
[email protected]:your-org/your-repo - SSH URL with a port:
ssh://[email protected]:2222/team/kb.git - Git+SSH URL:
git+ssh://[email protected]/~alice/kb.git - Local absolute path to a Git repository:
/Users/devuser/repos/your-repo - Local tilde path (expanded to
$HOME):~/repos/your-repo - Local relative path:
./vendor/kbor../kb - Local path containing spaces:
~/My Repos/kb; pass it to configure as'~/My Repos/kb' specs/so it stays one argument.
- HTTPS Git URL:
-
sources[*].labelโ optional; human-readable name used for attribution, and the identifierremovetakes. Must be unique and contain no whitespace. Defaults: for remote URLs,<host>/<org>/<repo>derived from the URL; for local paths, the last path component (e.g.~/repos/payment-serviceโpayment-service). -
sources[*].revisionโ optional; a branch, tag, or full commit SHA. Omitted โ the remote's default revision. See Pinning a revision. -
sources[*].path_filterโ optional folder names. Each entry accepts only ASCII letters, digits and. _ ~ : / @ % + -, with no leading/,~or-, no.., no spaces and no wildcards. Three forms:- Omitted โ every
.mdfile in the repo is indexed, except those under root-level dot-directories (.specify/,.github/,.wibey/,.claude/, โฆ) and files namedAGENTS.md,CLAUDE.mdorGEMINI.md. Those hold another project's tool and agent instructions, not shared knowledge. Root dotfiles and nested hidden folders are kept. - Single string โ
path_filter: specs/indexes only the.mdfiles underspecs/. - YAML list โ
path_filter: [specs/, docs/decisions/]indexes those two trees and nothing else.
An explicit filter is honored as written, so
path_filter: .github/docs/indexes that folder despite the default exclusions. Either way, only.mdfiles are cached: the sparse checkout never writes code, images or configuration to disk. - Omitted โ every
-
sources[*].enabledโ optional; defaulttrue; setfalseto skip a source without removing it from the file โ or run/speckit-knowledge-remove <label> --disable. -
sources[*].max_cache_ageโ optional; replaces the project-wide value for this source. -
sources[*].max_items/sources[*].max_bytesโ optional; a sub-ceiling that can only lower what this source contributes.
Every value is validated each time the file is read, before any shell use. A source
with an invalid value is skipped with a message naming its field and first
disallowed character; the other sources still sync. Messages show control
characters escaped, such as \n, rather than letting them rewrite the terminal.
The /speckit-knowledge-configure command automatically adds the required cache exclusion entries to your project's .gitignore when you first configure a source โ no manual step needed.
If you need to add them by hand (e.g. before running configure for the first time):
# knowledge extension cache (local only; do not commit)
.specify/extensions/knowledge/cache/
.specify/extensions/knowledge/knowledge-index.mdThe knowledge-config.yml config file should be committed โ it declares your team's knowledge sources and is shared across all developers.
๐ก Source unreachable: The command falls back to the existing cache and reports โ ๏ธ cached with the last-sync timestamp and cache age. The command always exits 0.
๐ฅ Corrupted cache: When .manifest.json no longer matches the cached files, the Cache Integrity Check fails. Status shows corrupted, search leaves the source out, and the next sync records it again. A cache directory left without a manifest or without its own .git, by a failed or stopped clone, is removed and cloned again. If the source is unreachable at that moment, it is marked โ unreachable.
๐ First sync after an upgrade: specify extension add recreates the extension's folder and keeps only knowledge-config.yml, so the cache and the index are gone. The next sync rebuilds every source.
โฏ๏ธ A sync that stopped part-way: Run /speckit-knowledge-sync again. Sources already recorded take the warm path, and every source left without a manifest is cloned again.
๐งพ corrupted right after upgrading from 1.9.1 or earlier: Sync now writes each manifest itself, in one exact layout, so a manifest an agent wrote before fails the check once. The next sync records it again and the row clears.
๐ค File skipped, name not valid UTF-8: JSON cannot hold that name, so sync leaves the file out of the manifest and the index and names it with \xHH escapes. Rename it in the source repository to bring it back.
๐ข More than 10 sources: A soft warning is emitted before the sync loop. The command continues normally โ the warning is informational only. Use path_filter to reduce per-source clone size.
๐ค Context not appearing in spec output: The hooks are optional โ check that you accepted the sync prompt. Then run /speckit-knowledge-status to confirm sources are reachable and knowledge-index.md exists. With no enabled sources left, sync deletes the index on purpose.
Contributions are welcome. To propose a change:
- ๐ฌ Open an issue describing the bug or enhancement before sending a PR for non-trivial changes
- ๐ด Fork the repo and create a feature branch (
feat/<short-name>orfix/<short-name>) - ๐ Update
CHANGELOG.mdunder[Unreleased]with your change - ๐งช Test the change with
specify extension add /path/to/spec-kit-shared-knowledge --devagainst a real spec-kit project - โ
Run
bash .github/scripts/validate-extension.sh(needspython3with PyYAML) โ CI runs the same checks, plus an install smoke test, on every pull request - ๐ Open a pull request and reference the issue
For larger architectural changes, please file a discussion first. See specs/ for the feature specs that document the current behavior.
MIT ยฉ Raรบl Noa Pedroso