Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ jobs:
run: bun install --frozen-lockfile --ignore-scripts

- name: Build workspace
run: bun run build
run: |
rustup toolchain install 1.89.0 --profile minimal --target wasm32-unknown-unknown
bun run build

- name: Verify generated plugin projection
run: >-
Expand All @@ -63,6 +65,7 @@ jobs:

- name: Verify Node consumers
run: |
node --test packages/context-cli/scripts/evidence-wasm.test.mjs
bun run test:dist
node packages/context-cli/dist/cli.js --version
node packages/context-cli/dist/cli.js --help
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ jobs:
>/dev/null

- name: Build workspace
run: bun run build
run: |
rustup toolchain install 1.89.0 --profile minimal --target wasm32-unknown-unknown
bun run build

- name: Prepare publish artifacts
run: bun run release:prepare
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/verify-full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile --ignore-scripts
- name: Build workspace
run: bun run build
run: |
rustup toolchain install 1.89.0 --profile minimal --target wasm32-unknown-unknown
bun run build
- name: Verification
env:
VERIFY_SCRIPT: ${{ inputs.full && 'verify:full' || 'verify' }}
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to Context are documented here.

## 0.7.42 - 2026-09-30

- Read approved knowledge and recorded code evidence remotely without requiring a checkout; preserve local and offline retrieval paths.
- Bundle a read-only repository Wasm plugin that attaches section evidence and commit-specific source URLs to knowledge reads.
- Add `context evidence install` and workspace initialization support with explicit nested-repository scope selection and protection for custom plugins.
- Reuse ready evidence links without repeated source-registry lookup or link formatting, while retaining source verification and partial-result boundaries.

## 0.7.40 - 2026-09-29

- Use optional read-only remote code evidence at recorded repository baselines before retrieving unavailable local sources.
Expand Down
3 changes: 3 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ and the same bundled plugin tree that is shipped to users.
## Prerequisites

- Bun for installing workspace dependencies, building, and running tests.
- Rust 1.89.0 with `wasm32-unknown-unknown` for building the evidence plugin
(`rustup toolchain install 1.89.0 --profile minimal --target wasm32-unknown-unknown`).
Published CLI users do not need Rust. See [the ABI](packages/context-evidence-wasm/README.md).
- Node.js and npm for the globally linked or published CLI surface.
- Claude Code or Codex only when testing the corresponding agent plugin.

Expand Down
30 changes: 15 additions & 15 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "context",
"version": "0.7.40",
"version": "0.7.42",
"packageManager": "bun@1.3.9",
"repository": {
"type": "git",
Expand Down
17 changes: 17 additions & 0 deletions packages/context-cli/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Context Agent Runtime

## Repository evidence plugin

New workspace initialization includes `context-evidence.sourcegraph.wasm` when
the workspace is the repository root (or has no enclosing Git repository).
For existing workspaces run `context evidence install [project-dir] --format json`.
Nested workspaces require explicit `--repository-root <git-root>` for full-repo
hosting, or `--plugin-root <registered-content-root>` for scoped hosting. Use the
workspace directory relative to that plugin root as `workspace_root`.
Unknown or modified same-name files are preserved. The installer never commits
or pushes; the artifact must be committed and synchronized before remote use.

On a compatible read-only MCP host, read/read_many can request
`plugins: [{"name":"context-evidence"}]` to attach registered section sources.
This does not verify original source contents or change local retrieval and
production. Missing enhancement falls back to ordinary metadata reads.
See the [plugin ABI and build instructions](../context-evidence-wasm/README.md).

[简体中文](./README.zh-CN.md)

`@c4a/context-cli` ships the local runtime and Agent integration for the
Expand Down
14 changes: 14 additions & 0 deletions packages/context-cli/README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Context Agent 运行时

## 仓库证据插件

新建工作区位于 Git 根目录(或尚无上级 Git 仓库)时,初始化会附带
`context-evidence.sourcegraph.wasm`。存量工作区运行
`context evidence install [project-dir] --format json`;嵌套工作区需显式指定
`--repository-root <git-root>`(全仓托管)或 `--plugin-root <登记内容根目录>`
(范围托管),远程读取的 `workspace_root` 是工作区相对插件目录的位置。
未知或用户修改的同名文件会保留,不自动提交或推送。

制品正常提交并同步后,兼容的只读 MCP 宿主可在 read/read_many 中传
`plugins: [{"name":"context-evidence"}]`,随正文返回登记的章节来源。
这不表示已经复核原始来源,也不改变本地查询或生产流程;增强不可用时保留原读取路径。
使用者无需安装 Rust。参见[插件 ABI 与构建说明](../context-evidence-wasm/README.md)。

[English](./README.md)

`@c4a/context-cli` 提供 Context 知识生产工作流的本地运行时和 Agent 接入。虽然
Expand Down
2 changes: 1 addition & 1 deletion packages/context-cli/context-workflow/provider.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
schema: agent-graph.provider.v1
id: c4a/context
version: 0.7.40
version: 0.7.42
name: Context workflow
description: Internal work contract for Context knowledge workspaces.
graphs:
Expand Down
8 changes: 5 additions & 3 deletions packages/context-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@c4a/context-cli",
"description": "Local runtime and Agent integration for traceable knowledge production",
"version": "0.7.40",
"version": "0.7.42",
"type": "module",
"license": "MIT",
"engines": {
Expand Down Expand Up @@ -35,14 +35,16 @@
"README.zh-CN.md"
],
"scripts": {
"build": "rm -rf dist && bun run ../build.ts src/cli.ts src/parserEntryWorker.ts --shebang=node && bun run scripts/build-workflow.ts && bun run scripts/build-plugin.ts && bun run scripts/build-indexers.ts && bun run scripts/build-diagrams.ts && cp ../../LICENSE dist/LICENSE",
"build": "rm -rf dist && bun run ../build.ts src/cli.ts src/parserEntryWorker.ts --shebang=node && bun run scripts/build-evidence.ts && bun run scripts/build-workflow.ts && bun run scripts/build-plugin.ts && bun run scripts/build-indexers.ts && bun run scripts/build-diagrams.ts && cp ../../LICENSE dist/LICENSE",
"build:evidence": "bun run scripts/build-evidence.ts",
"test:evidence": "bun run build:evidence && node --test scripts/evidence-wasm.test.mjs",
"build:indexers": "bun run scripts/build-indexers.ts",
"generate:article-templates": "bun run scripts/generate-article-templates.ts",
"build:workflow": "bun run scripts/build-workflow.ts",
"build:plugin": "bun run scripts/build-plugin.ts",
"postinstall": "node scripts/postinstall.mjs",
"typecheck": "tsc --noEmit",
"test": "node --test scripts/test-shards.test.mjs && CONTEXT_RUNTIME_EVENTS_DISABLED=1 bun run scripts/run-unit-tests.mjs --max-concurrency=1 --timeout=120000",
"test": "node --test scripts/test-shards.test.mjs scripts/evidence-wasm.test.mjs && CONTEXT_RUNTIME_EVENTS_DISABLED=1 bun run scripts/run-unit-tests.mjs --max-concurrency=1 --timeout=120000",
"test:full": "CONTEXT_RUNTIME_EVENTS_DISABLED=1 bun run scripts/run-unit-tests.mjs --full --max-concurrency=1 --timeout=1200000",
"test:full-only": "CONTEXT_RUNTIME_EVENTS_DISABLED=1 bun run scripts/run-unit-tests.mjs --full-only --max-concurrency=1 --timeout=1200000",
"lint": "eslint src --cache --cache-location .tmp/eslint-cache",
Expand Down
54 changes: 54 additions & 0 deletions packages/context-cli/scripts/build-evidence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { execFileSync } from "node:child_process";
import { createHash } from "node:crypto";
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";

const root = resolve(import.meta.dir, "..");
const crate = resolve(root, "../context-evidence-wasm");
const cargo = JSON.parse(execFileSync("cargo", ["metadata", "--locked", "--format-version", "1"], { cwd: crate, encoding: "utf8" }));
const remaps = cargo.packages.map((pkg: { manifest_path: string; name: string; version: string }) =>
`--remap-path-prefix=${dirname(pkg.manifest_path)}=/sources/${pkg.name}-${pkg.version}`);
execFileSync("cargo", ["build", "--locked", "--release", "--target", "wasm32-unknown-unknown"], {
cwd: crate, stdio: "inherit", env: { ...process.env, CARGO_ENCODED_RUSTFLAGS: remaps.join("\x1f") },
});
const bytes = await readFile(resolve(crate, "target/wasm32-unknown-unknown/release/context_evidence_wasm.wasm"));
const module = new WebAssembly.Module(bytes);
const metadata = WebAssembly.Module.customSections(module, "sourcegraph.plugin.v1");
if (metadata.length !== 1 || JSON.parse(new TextDecoder().decode(metadata[0])).name !== "context-evidence") {
throw new Error("Missing evidence plugin metadata");
}
const imports = WebAssembly.Module.imports(module);
if (imports.some(item => item.module !== "sourcegraph" || !["read_file", "last_error"].includes(item.name))) {
throw new Error("Unexpected evidence plugin imports");
}
const output = resolve(root, "dist/evidence");
await mkdir(output, { recursive: true });
const license = await readFile(resolve(root, "../../LICENSE"), "utf8");
await writeFile(resolve(output, "LICENSE"), license);
// Retain dependency licenses in the distributed tool, not runtime registry paths.
const notices: string[] = [license];
for (const pkg of cargo.packages) {
if (pkg.name === "context-evidence-wasm") continue;
notices.push(`${pkg.name} ${pkg.version} — ${pkg.license ?? "See license below"}`);
for (const file of (await readdir(dirname(pkg.manifest_path))).sort()) {
if (/^(LICENSE|COPYING|NOTICE)([.-]|$)/iu.test(file)) {
try { notices.push(await readFile(resolve(dirname(pkg.manifest_path), file), "utf8")); } catch { /* license directory */ }
}
}
}
await writeFile(resolve(output, "THIRD-PARTY-NOTICES.txt"), notices.join("\n\n"));
// A knowledge repository redistributes a single Wasm: embed the notices so they
// travel with the binary, not only with the CLI used to install it.
function leb(value: number): Buffer {
const result: number[] = [];
do { const byte = value & 127; value >>>= 7; result.push(byte | (value ? 128 : 0)); } while (value);
return Buffer.from(result);
}
const sectionName = Buffer.from("context.licenses");
const section = Buffer.concat([leb(sectionName.length), sectionName, Buffer.from(notices.join("\n\n"))]);
const artifact = Buffer.concat([bytes, Buffer.from([0]), leb(section.length), section]);
if (!WebAssembly.validate(artifact)) throw new Error("Invalid evidence Wasm artifact");
await writeFile(resolve(output, "context-evidence.sourcegraph.wasm"), artifact);
const previous = JSON.parse(await readFile(resolve(crate, "official-digests.json"), "utf8")) as string[];
await writeFile(resolve(output, "manifest.json"), `${JSON.stringify({ sha256: createHash("sha256").update(artifact).digest("hex"), previous }, null, 2)}\n`);
console.log(`Evidence Wasm: ${artifact.length} bytes`);
27 changes: 27 additions & 0 deletions packages/context-cli/scripts/evidence-artifact-smoke.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import assert from 'node:assert/strict';
import { execFileSync } from 'node:child_process';
import { existsSync, mkdirSync, mkdtempSync, readFileSync } from 'node:fs';
import { resolve, join } from 'node:path';
import { fileURLToPath } from 'node:url';

// Verifies the actual package projection with Node, not a source-mode installer.
const archive = process.argv[2];
if (!archive) throw new Error('Pass the CLI tarball path');
const root = resolve(fileURLToPath(new URL('..', import.meta.url)), '.tmp/evidence-artifact');
mkdirSync(root, { recursive: true });
const work = mkdtempSync(join(root, 'case-'));
execFileSync('tar', ['-xzf', resolve(archive), '-C', work]);
const packageRoot = join(work, 'package');
const runtimeRoot = existsSync(join(packageRoot, 'cli.js')) ? packageRoot : join(packageRoot, 'dist');
const cli = join(runtimeRoot, 'cli.js');
const artifact = join(runtimeRoot, 'evidence/context-evidence.sourcegraph.wasm');
const bytes = readFileSync(artifact);
const project = join(work, 'knowledge-repo');
mkdirSync(join(project, '.git'), { recursive: true });
const env = { ...process.env, PATH: join(work, 'no-executables'), CONTEXT_RUNTIME_EVENTS_DISABLED: '1' };
const run = () => JSON.parse(execFileSync(process.execPath, [cli, 'evidence', 'install', project, '--format', 'json'], { encoding: 'utf8', env }));
assert.equal(run().status, 'installed');
assert.deepEqual(readFileSync(join(project, 'context-evidence.sourcegraph.wasm')), bytes);
assert.equal(run().status, 'unchanged');
assert.ok(readFileSync(join(runtimeRoot, 'evidence/THIRD-PARTY-NOTICES.txt'), 'utf8').includes('serde'));
console.log(JSON.stringify({ runtime: process.version, wasmBytes: bytes.length, artifactInstall: 'passed', rustAndGitOnPath: false }));
Loading
Loading