Constructdocs
section · guides

Multi-project context — register, synthesize, contribute

Register other repos and directories as context targets, search and synthesize across them with cited provenance, contribute governed issue proposals back to Jira, and keep model cost under control — end to end.

4 min read·guides / cookbook / multi-project-context

Construct can treat other projects — local checkouts and remote repos — as first-class context. Register them once, then search across them, synthesize cited answers, bind them to orchestration runs, and contribute analyzed work back to an external tracker. Every cross-project claim keeps a re-verifiable origin, and you control model cost end to end.

This guide walks the whole capability. It assumes you have run construct init in your project.

1. Register context targets

A directory target reads content — docs and code — in place (no network, no clone). A corpus target clones a remote repo into a machine-local cache under the state root (never your project tree).

# A local checkout — read its docs where they sit
construct sources add directory proj-app '{"path":"~/Developer/Projects/app/docs"}'

# A remote repo — cloned into ~/.construct/projects/<key>/context-repos/<id>/
construct sources add github proj-sdk '{"repo":"acme/sdk","content":{"mode":"corpus","ref":"main"}}'
construct sources sync proj-sdk          # clone on first run, incremental fetch after

# A tracker to contribute back to later
construct sources add jira jira-core '{"project":"CORE"}'

construct sources list                    # shows provider, content mode, cache freshness
construct sources validate                # rejects a directory path that doesn't exist

construct doctor now watches these: it flags a directory whose path was deleted and a corpus cache older than its TTL (with a construct sources sync \<id> hint), using filesystem and env checks only — no outbound network.

2. Search across projects with provenance

Registered targets join the knowledge corpus — markdown docs AND code files. Code coverage means every file with a UTF-8 text extension (.js/.mjs/.ts/.tsx/.jsx/.py/.go/.rs/.sh plus config, data, and markup formats — the UTF8_TEXT_EXTS list in lib/document-extract.mjs), with vendored/build directories (.git, node_modules, dist, build, vendor, .venv, __pycache__) and binary files excluded. Every hit carries a structured origin{targetId, provider, projectKey, relPath, ref, kind}, with code hits tagged kind: 'code' — so you can attribute and filter results.

construct knowledge search "auth strategy"                       # host + all registered projects
construct knowledge search "auth strategy" --projects=proj-app   # just one project
construct knowledge search "payment handler" --projects=all      # code files hit too, attributed per repo

An unknown project id is a hard error listing the known projects — never a silent empty result.

3. Query each repo's code map

construct graph build-targets derives a static import graph per registered target — one graph per repo, persisted under .cx/graph/targets/\<targetId>/ so it survives session restarts — and construct graph query scopes to it with the same --projects semantics as knowledge search.

construct graph build-targets                                    # one import graph per registered target
construct graph query file:src/app.mjs --projects=proj-app       # dependencies + dependents in one repo
construct graph query file:src/app.mjs --projects=all            # fan the query across every target graph

The code map covers JavaScript-family sources only (.js/.mjs/.cjs, resolved from relative import/export/require specifiers — lib/graph/build-import-graph.mjs); files in other languages are searchable in the knowledge corpus but do not appear in the import graph. Graph nodes carry the same origin attribution (targetId/projectKey/kind: 'code') as knowledge-search hits.

4. Synthesize across projects

construct synthesize runs a map-reduce: a retrieval-only map pass extracts each project's most relevant content (attributed to project:path), and a single reduce pass answers your question with every cross-project claim cited to its origin.

construct synthesize --ask "summarize each project's docs" --projects=all --dry-run
construct synthesize --ask "how do these strategies converge" --projects=proj-app,proj-sdk --template strategy-comparison

--dry-run prints the fully assembled per-project context, the citation table, and the reduce prompt with no model call — a deterministic preview you can inspect before spending tokens.

5. Bind context to an orchestration run

Point a run at specific projects. Ids are validated at plan time (unknown → hard error before any task is built); the resolved bindings are recorded on the run.

construct orchestrate run "align the roadmaps" --context=proj-app,proj-sdk,jira-core:tracker

The same binding is available on the MCP orchestration_run tool via context_targets, and on author_artifact — an authored artifact can draw on and cite each bound project.

6. Contribute governed issue proposals back to Jira

Analyze your registered projects against a tracker and propose new issues — evidence-cited, deduped against existing issues, and never written without approval.

construct tracker contribute --target jira-core --against proj-app,proj-sdk
#  → a proposal artifact: N proposed issues (each citing project:path), plus a dedupe report

construct tracker contribute --apply <proposal-id>                    # dry-run: renders payloads, writes nothing
construct tracker contribute --apply <proposal-id> --approve <token>  # governed write batch

Dry-run is the default and only path without a token. Re-applying the same proposal is idempotent — it creates no duplicates. Beads (bd) stay your internal tracker; this pipeline only proposes to the external one.

7. Control model cost

None of the above needs a frontier model. Set a policy once:

construct models policy set budget     # rank by live pricing, exclude frontier ids
construct models policy set free        # only :free slugs
construct models policy show            # the effective per-tier resolution + why

Cross-project synthesis and tracker analysis run their map passes on the fast/standard tier by default — the deterministic dry-runs cost nothing at all.

Where the state lives

  • Registered targets: sources.targets[] in your project config.
  • Corpus caches: ~/.construct/projects/\<key>/context-repos/\<targetId>/ (state root, never your repo). See Knowledge layout and Project scopes.
  • Proposal artifacts: .cx/tracker/proposals/\<id>.{json,md} (local runtime state).