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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

All notable changes to Context are documented here.

## 0.7.50 - 2026-10-03

- Register same-repository documentation and authored Skills without copying their bodies into knowledge; maintain safe relative symlink entrances.
- Preserve historical repository paths and commits in evidence, detect relocated document regions and changes across entire Skill directories.
- Add optional repository entrance pages, original-source links, localized navigation and offline build fallbacks.
- Support repository-content references in the bundled evidence plugin and local or remote query workflows, including incomplete symlink-index coverage.
- Keep invalid-registration maintenance advisory, provide actionable repair guidance and reject invalid build inputs without replacing prior outputs.

## 0.7.43 - 2026-10-01

- Upgrade repository evidence enrichment to ABI 2, attaching compact references to individual read items without replacing original content.
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.43",
"version": "0.7.50",
"packageManager": "bun@1.3.9",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/context-cli/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ block 标题用 `**Label**:` 或 `**Label** (meta):`,统一英文(中文标
构建不变量:

- Claude/Cursor commands 发布生产、项目规划与显式查询入口,并提供 Indexer 创建 Skill。项目规划 command 转交完整 `context-plan` Skill,保留其参考文件和模板。
- `dist/plugins/codex/skills/` 包含 `context`、`context-plan`、`context-inspect-search` 和 `context-indexer-create`;宿主 plugin root 不内嵌 lifecycle Provider。
- `dist/plugins/codex/skills/` 包含 `context`、`context-plan`、`context-inspect-search`、`context-repo-content` 和 `context-indexer-create`;宿主 plugin root 不内嵌 lifecycle Provider。`context-repo-content` 独立编辑/登记同仓原文,不启动知识生产。
- `dist/plugins/skills/` 直接投影根级全部 Skills;安装器把其中 lifecycle Provider 原子复制到 `~/.agents/skills` 和 `~/.claude/skills`,而不是复制进 Host plugin root。
- `dist/plugins/{claude,codex,cursor}/` 各带 generated guard(`CLAUDE.md` 或 `AGENTS.md` + `.generated`);看到 guard 不要编辑 build 产物。`dist/plugins/skills/` 顶层 README 统一说明。
- 生命周期规则、长诊断、Schema 发现说明和语义规则统一住在
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ files:
- resources/manuals/guides/source-batches.md
- resources/manuals/reference/code-extractors.md
- resources/manuals/guides/knowledge-updates.md
- resources/manuals/guides/repo-content.md
- resources/manuals/guides/note.md
- resources/manuals/guides/sessions.md
- resources/manuals/guides/workspace-prepare.md
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.43
version: 0.7.50
name: Context workflow
description: Internal work contract for Context knowledge workspaces.
graphs:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ actual behavior, a confirmed decision, and a proposal that is not implemented.

## Keep planning local to the change

Same-repository originals use `repo-content.yaml` rather than another capture.
For editing/registering them, use the `context-repo-content` Skill without
starting production. An explicitly requested source update can select
`repo-content:<entry-id>` in its confirmed requirement and update scope. The
prepared update includes current local changes and chapter impact candidates.
Docs compare cited regions; Skills compare the whole owning Skill directory,
including untracked additions. Missing historical objects are unknown, not an
empty diff. Inspect/edit does not advance references; delivery or an explicit
no-impact outcome settles the selected scope. Historical locators always store
the real repository path at the cited commit, not today's registration path.

Before starting production, compare the proposed content with the workspace's
reader purpose. For clearly unrelated anecdotes or personal rankings, briefly
recommend leaving them out of the formal manual or saving them separately because
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ package index, and section fingerprint rebuilds are not current close output.

## Default New-Workspace Outputs: Knowledge Base + Website

Same-repository originals have an optional [repository entrance](repo-content.md):
`repoContentPage: true` in a KB declaration projects README and Skill summaries,
not the full document tree. It does not expose that page on a configured website;
use `repoContentPage: { site: true }` to opt in. Existing declarations are kept.

Output channels support multiple selection. In a new workspace without explicit
preferences, the Agent proposes and configures KB + website as the default. Honor
user feedback, session authority and existing workspace declarations; LLMS is an
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
id: context.sdk.repo-content
kind: procedure
mediaType: text/markdown
---

# Same-repository content

Keep project documentation and authored Skills at their original locations.
Context registers an entrance without copying their bodies to knowledge or
source snapshots. For selection and editing use `context-repo-content`.

At the Context workspace root, create `repo-content.yaml`:

```yaml
protocol: context.repo-content/v1
entries:
docs:
kind: docs
path: docs
tools:
kind: skills
path: .agents/skills
```

Paths are Git-root-relative, not workspace-relative. Kinds are `docs`,
`document`, `skills`, and `skill`. Optional `group` is one module level;
`mount` overrides the default `[group/]<basename>`. Optional `exclude` globs
are relative to the entry; they are not access control. Overlapping entries,
outside targets, nested repositories and cycles are rejected.

`context source ensure repo-content --format json` maintains relative symlinks
in `repo-content/`; `source inspect repo-content` and `status` inspect without
repairing. Ordinary files are not overwritten. With disabled symlinks, search
real paths from the registry rather than reading Git placeholders. No Git
configuration or index is changed. Registry and relative links can be committed
by the user; originals remain the only authoring location.

Invalid registration is advisory in `status` and automatic pre-operation link
maintenance: correct `repo-content.yaml`; existing links are left unchanged.
The repair command is suggested only for missing or misdirected symlinks, not
invalid registration, missing targets, ordinary-file conflicts or disabled
symlinks. Fix those reported conditions rather than repeatedly running ensure.
Explicit repository-content updates and `context build` require valid
registration. An invalid registry stops build before replacing existing package
outputs; it is not treated as an empty registry. Missing source files and
unavailable Git objects follow the documented fallback behavior instead.

Article navigation uses `[Guide](context:repo/docs/guide.md)`. Build projects
this to an upstream link when available, otherwise a plain location. Evidence
instead records `repo-content:docs@<full-commit>` plus real historical path,
line range and content digest. Uncommitted evidence uses `<commit>+worktree`.
The evidence Wasm supports these references without treating their existence
as proof that the source was read in the current query.

## Optional entrance page

```ts
kbPackage({
name: "project-kb",
template: { path: "src/package-templates/kb" },
repoContentPage: true,
});
```

SDK default is off. New initialization with an existing nonempty registry
creates a KB declaration with the entrance enabled; existing declarations are
preserved. It projects a group's README and Skill names/descriptions under
`wikis/repo-content.md` and optional group pages, not all docs or Skill scripts.
HEAD objects are preferred without network fetching, with declared worktree
fallback when unavailable. Review README sensitivity before exposing it.
With a configured website, `{ site: true }` opts into a top-level site entrance;
plain `true` keeps these pages out of the site.

Other repositories and externally installed Skills are not same-repository
content. Registering an entrance does not imply installation, execution,
production or publication authority.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ actual behavior, a confirmed decision, and a proposal that is not implemented.

## Keep planning local to the change

Same-repository originals use `repo-content.yaml` rather than another capture.
For editing/registering them, use the `context-repo-content` Skill without
starting production. An explicitly requested source update can select
`repo-content:<entry-id>` in its confirmed requirement and update scope. The
prepared update includes current local changes and chapter impact candidates.
Docs compare cited regions; Skills compare the whole owning Skill directory,
including untracked additions. Missing historical objects are unknown, not an
empty diff. Inspect/edit does not advance references; delivery or an explicit
no-impact outcome settles the selected scope. Historical locators always store
the real repository path at the cited commit, not today's registration path.

Before starting production, compare the proposed content with the workspace's
reader purpose. For clearly unrelated anecdotes or personal rankings, briefly
recommend leaving them out of the formal manual or saving them separately because
Expand Down
2 changes: 1 addition & 1 deletion 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.43",
"version": "0.7.50",
"type": "module",
"license": "MIT",
"engines": {
Expand Down
18 changes: 10 additions & 8 deletions packages/context-cli/scripts/build-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,15 @@ function stripHtmlComments(markdown: string): string {
}

async function readCommands(): Promise<CommandSource[]> {
return Promise.all(["context", "context-inspect-search", "context-plan"].map(async (slug) => {
return Promise.all(["context", "context-inspect-search", "context-plan", "context-repo-content"].map(async (slug) => {
const file = join(PLUGIN_SOURCE_ROOT, "skills", slug, "SKILL.md");
const { frontmatter, body } = parseFrontmatter(await readFile(file, "utf8"), file);
return { slug, title: titleFromSlug(slug),
description: frontmatterValue(frontmatter, "description", file),
body: slug === "context-plan" ? [
"# Context Plan",
body: ["context-plan", "context-repo-content"].includes(slug) ? [
`# ${titleFromSlug(slug)}`,
"",
"Read the installed `context-plan` skill at `../skills/context-plan/SKILL.md`",
`Read the installed \`${slug}\` skill at \`../skills/${slug}/SKILL.md\``,
"relative to this command file, then follow its instructions for the user's request.",
"Resolve its references and templates from that skill directory.",
].join("\n") : body,
Expand Down Expand Up @@ -312,14 +312,16 @@ async function copyAuthoringSkill(outputRoot: string): Promise<void> {

async function copyHostRoutedSkills(outputRoot: string): Promise<void> {
await copyAuthoringSkill(outputRoot);
for (const slug of ["context-inspect-search", "context-plan"]) {
for (const slug of ["context-inspect-search", "context-plan", "context-repo-content"]) {
await copyDir(join(PLUGIN_SOURCE_ROOT, "skills", slug), join(outputRoot, "skills", slug));
}
// The command is the visible entry; keep the underlying Skill available to
// model routing and relative resource loading without a duplicate command.
const planningEntry = join(outputRoot, "skills", "context-plan", "SKILL.md");
const { frontmatter, body } = parseFrontmatter(await readFile(planningEntry, "utf8"), planningEntry);
await writeFile(planningEntry, `---\nuser-invocable: false\n${frontmatter.replace(/^user-invocable:.*\n?/mu, "")}\n---\n\n${body}`, "utf8");
for (const slug of ["context-plan", "context-repo-content"]) {
const entry = join(outputRoot, "skills", slug, "SKILL.md");
const { frontmatter, body } = parseFrontmatter(await readFile(entry, "utf8"), entry);
await writeFile(entry, `---\nuser-invocable: false\n${frontmatter.replace(/^user-invocable:.*\n?/mu, "")}\n---\n\n${body}`, "utf8");
}
}

async function copyContextEntrySkill(outputRoot: string): Promise<void> {
Expand Down
1 change: 1 addition & 0 deletions packages/context-cli/scripts/build-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const sdkManuals = [
"guides/lark-resources.md",
"guides/source-batches.md",
"guides/knowledge-updates.md",
"guides/repo-content.md",
"guides/workspace-prepare.md",
"guides/workspace-commit.md",
"guides/workspace-restore.md",
Expand Down
26 changes: 26 additions & 0 deletions packages/context-cli/scripts/evidence-wasm.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,32 @@ test('optional digest and monorepo root', () => {
request.files[0].path = 'docs/knowledge/example.md';
assert.equal(refs(host(files).run(request))[0].content_digest, reference().content_digest);
});

test('repo-content uses its historical repository path and source commit without registry joins', () => {
const sha = 'c'.repeat(40);
const ref = { ...reference(`repo-content:docs@${sha}`), locator: { path: 'packages/cli/docs/old.md', start_line: 3, end_line: 9 } };
const files = Object.fromEntries(Object.entries(fixtures([ref])).map(([p, v]) => [`workspace/${p}`, v]));
delete files['workspace/sources/repo/index.yaml'];
const request = input(2, 2, { workspace_root: 'workspace' });
request.repository = 'https://example.org/team/project.git';
request.commit = 'd'.repeat(40);
request.files[0].path = 'workspace/knowledge/example.md';
const h = host(files);
assert.deepEqual(refs(h.run(request)), [`https://example.org/team/project/blob/${sha}/packages/cli/docs/old.md#L3-L9`]);
assert.deepEqual(h.calls.sort(), ['workspace/knowledge/example.md', 'workspace/knowledge/structure.yaml']);
});

test('repo-content retains worktree uncertainty and does not invent a URL from a repository name', () => {
for (const worktree of [false, true]) {
const sourceRef = `repo-content:docs@${'c'.repeat(40)}${worktree ? '+worktree' : ''}`;
const request = input(); request.repository = 'team/project';
const result = refs(host(fixtures([reference(sourceRef)])).run(request))[0];
assert.equal(result.source_ref, sourceRef);
assert.equal(result.path, 'src/example.ts');
assert.equal(result.url, undefined);
if (worktree) assert.equal(result.content_digest, reference().content_digest);
}
});
test('source URLs encode file segments and normalize credential-free Git transports', () => {
for (const remote of ['https://github.com/team/source.git', 'git@github.com:team/source.git', 'ssh://git@github.com/team/source.git']) {
const ref = reference(); ref.locator = { path: 'src/a #中文%.ts', start_line: 3, end_line: 3 };
Expand Down
Loading
Loading