@c4a/context-cli 0.5.36-beta.1 → 0.5.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +10094 -3891
- package/package.json +2 -1
- package/plugin/README.md +1 -1
- package/plugin/README_CN.md +1 -1
- package/plugin/commands/align.md +3 -0
- package/plugin/commands/capture.md +7 -5
- package/plugin/commands/compile.md +14 -4
- package/plugin/commands/init.md +11 -12
- package/plugin/commands/status.md +2 -0
- package/plugin/skills/skill-align-workflow/SKILL.md +4 -1
- package/plugin/skills/skill-compile-close/SKILL.md +5 -3
- package/plugin/skills/skill-compile-draft/SKILL.md +27 -12
- package/plugin/skills/skill-compile-draft/references/notes.md +3 -3
- package/plugin/skills/skill-compile-draft/references/refresh-and-update.md +5 -5
- package/plugin/skills/skill-compile-judge/SKILL.md +2 -1
- package/plugin/skills/skill-semantic-reconcile/SKILL.md +4 -4
- package/templates/aspect-runtime/aspectRunnerSdk.ts +749 -0
- package/templates/aspects/README.md +515 -0
- package/templates/aspects/code/aspect.yaml +2 -2
- package/templates/aspects/code/prompt.md +13 -12
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# Aspect Extension Guide
|
|
2
|
+
|
|
3
|
+
> Generated by `context init`. This document is business-agnostic — a complete reference for extending knowledge graphs via Aspects.
|
|
4
|
+
|
|
5
|
+
For the protocol and bundled code runner package, see [`@c4a/extract`](https://www.npmjs.com/package/@c4a/extract).
|
|
6
|
+
|
|
7
|
+
## What Is an Aspect
|
|
8
|
+
|
|
9
|
+
An Aspect is a **structured data source** for the knowledge graph. Each aspect extracts information from a specific origin (source code, docs, APIs, config files, etc.) and outputs standardized Sections, anchored to Entity nodes in the knowledge graph.
|
|
10
|
+
|
|
11
|
+
Unlike the align/compile pipeline (LLM-driven, designed for unstructured documents), aspects are **fully deterministic, zero LLM**, suitable for extraction tasks with stable, well-defined rules.
|
|
12
|
+
|
|
13
|
+
### Aspect vs Align/Compile
|
|
14
|
+
|
|
15
|
+
| | Aspect Projection | Align/Compile |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Input | Structured sources (code, DSL, YAML, token tables) | Unstructured sources (Markdown, docs, chat logs) |
|
|
18
|
+
| Extraction | `extract.ts` script, pure algorithm | LLM semantic classification + alignment |
|
|
19
|
+
| Speed | Seconds | Minutes (depends on LLM call count) |
|
|
20
|
+
| Repeatability / traceability | Repeatable output with source-bound evidence | Depends on LLM quality and human review |
|
|
21
|
+
| Best for | Source symbols, API references, design tokens, structured FAQ | Product docs, meeting notes, articles |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Directory Structure
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
.context/aspects/
|
|
29
|
+
├── README.md ← This file
|
|
30
|
+
├── code/
|
|
31
|
+
│ ├── aspect.yaml # Built-in code aspect (generated when selected during init)
|
|
32
|
+
│ └── prompt.md # Agent/human hint only; CLI does not read it
|
|
33
|
+
├── <your-aspect>/
|
|
34
|
+
│ ├── aspect.yaml # Required: aspect definition
|
|
35
|
+
│ ├── extract.ts # Aspect plugin module (local mode)
|
|
36
|
+
│ └── prompt.md # Optional: hint for humans/agents
|
|
37
|
+
└── <another-aspect>/
|
|
38
|
+
└── ...
|
|
39
|
+
|
|
40
|
+
CLI runtime
|
|
41
|
+
└── C4A_ASPECT_RUNNER_SDK # env var containing the CLI-provided SDK module URL
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`context init` generates `.context/aspects/README.md` from this guide. The runtime SDK is provided by CLI at capture time; teams should not copy protocol IO helpers into every business aspect.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## aspect.yaml — Complete Reference
|
|
49
|
+
|
|
50
|
+
### Local Script Mode (`schema_version: "context.aspect.v2"`)
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
schema_version: "context.aspect.v2"
|
|
54
|
+
name: my-aspect # Unique identifier; recommended to match directory name
|
|
55
|
+
|
|
56
|
+
runner:
|
|
57
|
+
script: ./extract.ts # Relative to the aspect.yaml directory
|
|
58
|
+
|
|
59
|
+
output:
|
|
60
|
+
bucket: raw/aspect/<name>/<source-slug>/<snapshot-id>/
|
|
61
|
+
files: # Final bucket file list
|
|
62
|
+
- source.yaml # Generated by CLI
|
|
63
|
+
- manifest.json # Generated by CLI
|
|
64
|
+
- source-files.jsonl # source_path/hash_id/bucket_path mapping
|
|
65
|
+
- sections.jsonl # section-projection.v2 rows
|
|
66
|
+
|
|
67
|
+
projection:
|
|
68
|
+
protocol: "section-projection.v2"
|
|
69
|
+
anchor_policy: best-effort # best-effort = warn and skip missing nodes; strict = fail
|
|
70
|
+
allow_empty: false # reject row_count=0 by default
|
|
71
|
+
large_deprecate_threshold: 0.5 # reject >50% row drop by default
|
|
72
|
+
|
|
73
|
+
evidence:
|
|
74
|
+
mode: bucket-files # bucket-files: source_ref resolves against bucket files
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Bundled Runner Mode (`schema_version: "context.aspect.v2"` — code aspect only)
|
|
78
|
+
|
|
79
|
+
```yaml
|
|
80
|
+
schema_version: "context.aspect.v2"
|
|
81
|
+
name: code
|
|
82
|
+
|
|
83
|
+
runner:
|
|
84
|
+
package: "@c4a/extract" # npm package name
|
|
85
|
+
bin: c4a-extract-code # binary name
|
|
86
|
+
|
|
87
|
+
plugins: # Optional: language plugins
|
|
88
|
+
- package: "@c4a/extract-ts"
|
|
89
|
+
export: TypeScriptPlugin
|
|
90
|
+
|
|
91
|
+
output:
|
|
92
|
+
bucket: raw/aspect/code/<source-slug>/<snapshot-id>/
|
|
93
|
+
files:
|
|
94
|
+
- source.yaml
|
|
95
|
+
- manifest.json
|
|
96
|
+
- digests.jsonl
|
|
97
|
+
- source-files.jsonl
|
|
98
|
+
- packages.jsonl
|
|
99
|
+
- symbols.jsonl
|
|
100
|
+
- edges.jsonl
|
|
101
|
+
- _meta.yaml
|
|
102
|
+
|
|
103
|
+
evidence:
|
|
104
|
+
mode: none # none: no evidence files; uses built-in code projection
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> Custom aspects and the built-in `code` aspect both use `schema_version: "context.aspect.v2"`. Local script mode is expressed by `runner.script`; bundled runner mode is expressed by `runner.package` + `runner.bin`. Generic Section projection is selected by `projection.protocol: "section-projection.v2"`.
|
|
108
|
+
|
|
109
|
+
### Field Reference
|
|
110
|
+
|
|
111
|
+
| Field | Required | Description |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| `name` | Yes | Unique aspect identifier; used as the argument to `capture --aspect <name>` and `compile --aspect <name>` |
|
|
114
|
+
| `runner.script` | Yes for local plugin mode | Local plugin module path, relative to aspect.yaml directory; CLI loads it through the aspect host |
|
|
115
|
+
| `runner.package` | Yes for bundled runner mode | npm package name |
|
|
116
|
+
| `runner.bin` | Yes for bundled runner mode | Binary name from the npm package |
|
|
117
|
+
| `output.bucket` | Yes | Raw snapshot storage path template; `<source-slug>` and `<snapshot-id>` substituted by CLI |
|
|
118
|
+
| `output.files` | Yes | Final bucket file list. CLI generates `source.yaml`, `manifest.json`, `source-files.jsonl`, and `sections.jsonl` from rows emitted by the aspect plugin |
|
|
119
|
+
| `projection.protocol` | Yes for Section projection | Fixed value `"section-projection.v2"`; absence is only valid for built-in code projection |
|
|
120
|
+
| `projection.anchor_policy` | Yes | `strict` = fail on unknown node; `best-effort` = warn, record `skipped_anchor_missing[]`, and skip the row |
|
|
121
|
+
| `projection.allow_empty` | Optional | Defaults to `false`; reject empty `sections.jsonl` to prevent accidental mass deprecations. This does not bypass `large_deprecate_threshold` |
|
|
122
|
+
| `projection.large_deprecate_threshold` | Optional | Defaults to `0.5`; reject a `sections.jsonl` row-count drop beyond this ratio unless `--allow-large-deprecate` is explicit |
|
|
123
|
+
| `evidence.mode` | Yes | `bucket-files` = resolve source_ref from `source-files.jsonl` and read copied evidence files from the bucket; `none` = no evidence |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Aspect Plugin Contract
|
|
128
|
+
|
|
129
|
+
### Invocation
|
|
130
|
+
|
|
131
|
+
CLI starts the CLI-provided aspect host and passes the aspect module as a plugin. The aspect file is not a self-executing script; it exports a lifecycle object:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
export default defineAspect({
|
|
135
|
+
async capture(ctx) {
|
|
136
|
+
// read source files through ctx.source
|
|
137
|
+
// emit rows through ctx.emit.section
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Plugin Context
|
|
143
|
+
|
|
144
|
+
The plugin does not receive `workspace_root`, `repo_root`, `bucket_dir`, raw bucket paths, or knowledge file paths. Those are host-internal details. The only workspace access surface is the host API:
|
|
145
|
+
|
|
146
|
+
| Context API | Description |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `ctx.aspect` | Logical aspect metadata: `name`, `source_slug`, `source_id`, `snapshot_id` |
|
|
149
|
+
| `ctx.source.glob({ root, extensions, recursive })` | List source files by normalized POSIX-relative path under the configured repo root |
|
|
150
|
+
| `ctx.source.immediateFiles(root, extensions)` | List immediate files under one source directory |
|
|
151
|
+
| `ctx.source.readText(fileOrPath)` | Read a source file through the CLI host boundary |
|
|
152
|
+
| `ctx.source.exists(fileOrPath)` | Check whether a source file exists through the CLI host boundary |
|
|
153
|
+
| `ctx.code.packages` / `ctx.code.symbols` | Active code aspect index for resolving package and symbol node slugs; may be empty before code compile |
|
|
154
|
+
| `ctx.code.findPackage(...)` / `ctx.code.findSymbol(...)` | Convenience lookup over the active code index |
|
|
155
|
+
| `ctx.emit.node(row)` | Emit one deterministic documentation node. CLI later materializes `nodes.jsonl` and upserts the node before Section projection |
|
|
156
|
+
| `ctx.emit.section(row)` | Emit one projected Section row. CLI later materializes `sections.jsonl`, `source-files.jsonl`, hashes, and `source_ref` |
|
|
157
|
+
| `ctx.emit.warning(...)` | Emit a non-fatal diagnostic for the capture result |
|
|
158
|
+
|
|
159
|
+
### Plugin Responsibilities
|
|
160
|
+
|
|
161
|
+
Business aspect plugins own only source selection and business mapping:
|
|
162
|
+
|
|
163
|
+
1. Import the CLI SDK URL from `process.env.C4A_ASPECT_RUNNER_SDK`
|
|
164
|
+
2. Export `defineAspect({ capture(ctx) { ... } })`
|
|
165
|
+
3. Read source content only through `ctx.source`
|
|
166
|
+
4. Emit deterministic documentation nodes through `ctx.emit.node({ node_slug, title, type, tags, parent_slug })` when source files should become standalone knowledge files
|
|
167
|
+
5. Emit rows through `ctx.emit.section({ node_slug, kind, summary, content, source/source_path, artifact })`
|
|
168
|
+
6. Return normally for success; throw for failure
|
|
169
|
+
|
|
170
|
+
Plugins must not read or write `.context`, raw buckets, knowledge files, `source-files.jsonl`, or `sections.jsonl` directly. Local execution cannot fully sandbox arbitrary Node APIs, but the protocol boundary deliberately avoids passing workspace file-system paths to the plugin so the same aspect can later run behind a remote or isolated host.
|
|
171
|
+
|
|
172
|
+
The SDK does not export product-specific constants such as package slug maps. Keep those mappings in the business aspect, or derive them from `ctx.code.packages`.
|
|
173
|
+
|
|
174
|
+
### CLI Host Responsibilities
|
|
175
|
+
|
|
176
|
+
| Host capability | Why it belongs in CLI |
|
|
177
|
+
|---|---|
|
|
178
|
+
| Source file I/O | Keeps workspace access behind one boundary and allows future remote/sandbox hosts |
|
|
179
|
+
| source path normalization and boundary checks | Prevents `../`, absolute paths, symlink escapes, and platform-specific drift |
|
|
180
|
+
| hash and `source_ref` generation | Keeps `file:<path>#<artifact>@<hash12>` canonical |
|
|
181
|
+
| `nodes.jsonl`, `sections.jsonl`, and `source-files.jsonl` generation | Prevents each business plugin from reimplementing JSONL and bucket rules |
|
|
182
|
+
| raw bucket publish | Ensures validation passes before atomic rename to `raw/aspect/...` |
|
|
183
|
+
| `code_index` package/symbol lookup | Lets custom aspects attach to code aspect nodes without parsing `_sources.yaml` |
|
|
184
|
+
| common AST helpers | Keeps TSX/JS/MDX/SCSS parsing deterministic and reusable |
|
|
185
|
+
|
|
186
|
+
### Example: Lifecycle-Based extract.ts
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const sdkUrl = process.env.C4A_ASPECT_RUNNER_SDK;
|
|
190
|
+
if (!sdkUrl) throw new Error("C4A_ASPECT_RUNNER_SDK is required; run through context capture --aspect");
|
|
191
|
+
|
|
192
|
+
const { artifactFromRelPath, defineAspect, parseMdxDocument } = await import(sdkUrl);
|
|
193
|
+
|
|
194
|
+
export default defineAspect({
|
|
195
|
+
async capture(ctx) {
|
|
196
|
+
for (const file of await ctx.source.glob({ root: "docs", extensions: [".md", ".mdx"] })) {
|
|
197
|
+
const doc = parseMdxDocument(await ctx.source.readText(file), file.path);
|
|
198
|
+
ctx.emit.section({
|
|
199
|
+
node_slug: "target-node",
|
|
200
|
+
kind: "description",
|
|
201
|
+
summary: doc.title,
|
|
202
|
+
content: doc.markdown,
|
|
203
|
+
source: file,
|
|
204
|
+
artifact: artifactFromRelPath(file.path),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Section Projection Protocol (`section-projection.v2`)
|
|
214
|
+
|
|
215
|
+
### nodes.jsonl Row Schema (Optional)
|
|
216
|
+
|
|
217
|
+
```jsonc
|
|
218
|
+
{
|
|
219
|
+
"node_slug": "pkg-slug/colors",
|
|
220
|
+
"title": "Colors",
|
|
221
|
+
"type": "entity",
|
|
222
|
+
"tags": ["module"],
|
|
223
|
+
"summary": "Color design language for this package.",
|
|
224
|
+
"parent_slug": "pkg-slug",
|
|
225
|
+
"code_package": "pkg-slug",
|
|
226
|
+
"language": "English"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Use `nodes.jsonl` only for deterministic documentation nodes whose slug and parent are derived mechanically from source paths or fixed project rules. `language` may be set when source-bound documentation uses a different language than the workspace generation language. Compile upserts these nodes before projecting Sections. Missing anchors that are referenced only by `sections.jsonl` are still handled by `anchor_policy`; they are not created implicitly.
|
|
231
|
+
|
|
232
|
+
### sections.jsonl Row Schema
|
|
233
|
+
|
|
234
|
+
```jsonc
|
|
235
|
+
{
|
|
236
|
+
"node_slug": "pkg-slug/symbol/target", // Target Entity slug
|
|
237
|
+
"kind": "example", // Existing SectionKind enum value
|
|
238
|
+
"summary": "basic.tsx", // Optional: existing Section.summary
|
|
239
|
+
"content": "# basic.tsx\n\n...", // Full section text; enters the full-text index
|
|
240
|
+
"source_ref": "file:rel/path/file.tsx#basic@ab12cd34ef56" // Traceability reference
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`kind` must be one of the existing C4A SectionKind values: `description`, `spec`, `warning`, `principle`, `decision`, `incident`, `example`, `changelog`, `comparison`, or `faq`. Project-specific labels such as `design-system-color` or `component-description` belong in `summary` / `content` or the `source_ref` fragment, not in `Section.kind`.
|
|
245
|
+
|
|
246
|
+
`source_ref` uses an alias-free existing code-style locator: `file:<rel-path>[#artifact]@<hash12>`. The fragment is required when one file produces more than one Section. During compile, C4A adds the aspect source to the target node's `sources[]` and persists a canonical reference like `src-N#file:<rel-path>[#artifact]@<hash12>`.
|
|
247
|
+
|
|
248
|
+
This does not add a new persisted `source_ref` format. Persisted Sections still use the existing code-style `src-N#file:...@hash` form. The plugin emits `source_path + artifact`; CLI generates the alias-free `file:...@hash` form and compile assigns `src-N#` because that alias depends on the target node's `sources[]` order.
|
|
249
|
+
|
|
250
|
+
Canonical grammar:
|
|
251
|
+
|
|
252
|
+
```text
|
|
253
|
+
file:<escaped-posix-rel-path>[#<escaped-fragment>]@<hash12>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The verifier must percent-decode the source_ref path first, then compare it by byte-for-byte string equality: `decode(path) === source-files.jsonl.source_path`. Decode failure, a decoded path that is not a normalized POSIX relative path, absolute paths, `..`, NUL, or no matching `source-files.jsonl` row must fail fast. `/` remains the path separator; reserved characters inside path segments and fragments, including whitespace, `#`, `@`, and `%`, must use URL-style percent encoding. Windows backslashes, unescaped whitespace, unescaped `#` / `@`, and hashes other than 12 lowercase hex characters are protocol errors.
|
|
257
|
+
|
|
258
|
+
Verify, query, source_ref resolution, and code projection drift checks must not classify `src-N#file:...@hash` as a code source_ref by syntax alone. They must first read the target node's `sources[src-N]`: `aspect:code:*` uses the existing code verifier, while `aspect:<custom>:*` uses the `section-projection.v2` bucket-files verifier. Code ownership must not be inferred from `parseCodeSourceRef(...) !== null` alone.
|
|
259
|
+
|
|
260
|
+
### Anchor Levels
|
|
261
|
+
|
|
262
|
+
`node_slug` can target existing code/manual nodes or deterministic documentation nodes declared in `nodes.jsonl`:
|
|
263
|
+
|
|
264
|
+
| Level | slug Example | Suitable Content |
|
|
265
|
+
|---|---|---|
|
|
266
|
+
| package root | `my-pkg-slug` | FAQ and short package-level notes |
|
|
267
|
+
| deterministic doc | `my-pkg-slug/colors` | Design language pages, token tables, guides |
|
|
268
|
+
| module | `my-pkg-slug/module-name` | Code aspect module notes |
|
|
269
|
+
| symbol | `my-pkg-slug/symbol/myfunction` | Function/class/component examples and descriptions |
|
|
270
|
+
|
|
271
|
+
### Projection Identity
|
|
272
|
+
|
|
273
|
+
No separate projection key field is added to the persisted Section schema. Compile derives identity from existing durable fields:
|
|
274
|
+
|
|
275
|
+
```text
|
|
276
|
+
projection_key = source_id + "\0" + node_slug + "\0" + kind + "\0" + alias_free_locator
|
|
277
|
+
alias_free_locator = file:<rel-path>[#artifact]
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`alias_free_locator` comes from the CLI-generated alias-free `source_ref` without the trailing `@hash12`; it does not include `src-N#`. `src-N` is a persisted alias derived from node `sources[]` and can change when source ordering changes, so it must not enter the projection key. Projection keys must be unique within the same `sections.jsonl`. A duplicate key is a plugin conflict, not a merge or last-wins case; compile must fail fast and report the duplicate key with row numbers.
|
|
281
|
+
|
|
282
|
+
During compile, each projection key is compared against existing knowledge Sections:
|
|
283
|
+
|
|
284
|
+
| in jsonl? | in knowledge? | projected Section payload | Action |
|
|
285
|
+
|---|---|---|---|
|
|
286
|
+
| Yes | No | — | add (create) |
|
|
287
|
+
| Yes | Yes | same | noop (skip) |
|
|
288
|
+
| Yes | Yes | different | update |
|
|
289
|
+
| No | Yes | — | deprecate (reason: source-removed) |
|
|
290
|
+
|
|
291
|
+
Projection is applied as a batch. CLI reads workspace nodes once, computes the diff, mutates sources and Sections in memory, sorts touched nodes, and then atomically rewrites each touched knowledge root file once. It must not call row-level mdrive write commands for every projected row. Protocol or validation failures stop before any knowledge rewrite; unchanged rows do not rewrite knowledge files.
|
|
292
|
+
|
|
293
|
+
To avoid mass deprecations caused by plugin or host bugs, compile applies mechanical guards before diffing: `row_count=0` is rejected by default unless `projection.allow_empty: true`; a `sections.jsonl` row-count drop beyond `projection.large_deprecate_threshold` is still rejected unless the user explicitly passes `--allow-large-deprecate`. `projection.allow_empty` only says that an empty capture result is valid; it does not authorize deleting a large set of existing Sections. When the large-deprecate guard fires, the error envelope's `next_action.command` must provide the same-aspect command: `context compile --aspect <name> --allow-large-deprecate`.
|
|
294
|
+
|
|
295
|
+
### kind Convention
|
|
296
|
+
|
|
297
|
+
`kind` is not a free-form string. It controls renderer grouping, mount validation, verification, and query behavior. Put project-specific taxonomy in `summary`, content headings, or the `source_ref` fragment.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## config.yaml Variable Mechanism
|
|
302
|
+
|
|
303
|
+
### Syntax
|
|
304
|
+
|
|
305
|
+
```yaml
|
|
306
|
+
# .context/config.yaml
|
|
307
|
+
workspace:
|
|
308
|
+
name: my-project
|
|
309
|
+
language: English
|
|
310
|
+
repo_root: ${REPO_ROOT:-../}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
`${VAR:-default}` — uses `default` when `VAR` is not set or is an empty string. Relative values are resolved to absolute paths relative to the workspace root, i.e. the parent directory of `.context/` in embedded layout.
|
|
314
|
+
|
|
315
|
+
### Variable Source Priority
|
|
316
|
+
|
|
317
|
+
1. Shell environment variables (`process.env`) — highest priority, for temporary overrides
|
|
318
|
+
2. `.context/.env` file — persistent defaults
|
|
319
|
+
3. `${VAR:-default}` fallback — last resort
|
|
320
|
+
|
|
321
|
+
`.context/.env` is loaded into a scoped env map only; CLI must not mutate global `process.env`. Expansion uses an effective env map (`.context/.env` as the base, shell env overriding duplicate keys), and the same effective env is passed to the aspect host subprocess.
|
|
322
|
+
|
|
323
|
+
### `.env` File
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# .context/.env
|
|
327
|
+
# Generated by context init. Leave empty to use config.yaml's fallback.
|
|
328
|
+
REPO_ROOT=
|
|
329
|
+
|
|
330
|
+
# Override with an absolute source repository path when needed.
|
|
331
|
+
# REPO_ROOT=/abs/path/to/source/repo
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Usage in Plugins
|
|
335
|
+
|
|
336
|
+
Plugins do not receive `repo_root`. They use POSIX-relative source paths through `ctx.source`; CLI resolves those paths against the configured repo root:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
const files = await ctx.source.glob({ root: "docs", extensions: [".mdx"] });
|
|
340
|
+
const text = await ctx.source.readText(files[0]);
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Command Reference
|
|
346
|
+
|
|
347
|
+
### capture
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# Capture a single aspect:
|
|
351
|
+
context capture --aspect my-aspect
|
|
352
|
+
|
|
353
|
+
# Capture multiple aspects (sequential):
|
|
354
|
+
context capture --aspect my-aspect another-aspect
|
|
355
|
+
|
|
356
|
+
# Code capture keeps its existing command and options:
|
|
357
|
+
context capture --code <target-or-module-options>
|
|
358
|
+
# Requires `.context/aspects/code/aspect.yaml`; if it is missing, CLI asks to install the code aspect.
|
|
359
|
+
|
|
360
|
+
# --force: re-run plugin even if snapshot unchanged
|
|
361
|
+
context capture --aspect my-aspect --force
|
|
362
|
+
|
|
363
|
+
# --quiet: suppress progress output
|
|
364
|
+
context capture --aspect my-aspect --quiet
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### compile (Project to Knowledge Graph)
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
# Compile a single aspect:
|
|
371
|
+
context compile --aspect my-aspect
|
|
372
|
+
|
|
373
|
+
# Global compile orchestrator: project all aspects, then inspect/advance existing LLM compile work:
|
|
374
|
+
context compile --all
|
|
375
|
+
|
|
376
|
+
# The only entry for code projection:
|
|
377
|
+
context compile --aspect code
|
|
378
|
+
|
|
379
|
+
# Explicitly allow this run after confirming a large deprecate is expected:
|
|
380
|
+
context compile --aspect my-aspect --allow-large-deprecate
|
|
381
|
+
|
|
382
|
+
# Compile specific aspects:
|
|
383
|
+
context compile --aspect my-aspect
|
|
384
|
+
|
|
385
|
+
# No argument to --aspect → list all available aspects:
|
|
386
|
+
context compile --aspect
|
|
387
|
+
# → Available aspects: code, my-aspect, another-aspect
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
`compile --aspect <name...>` is pure TypeScript, zero LLM: read `sections.jsonl` → diff by projection key → batch-apply add/update/deprecate/noop in memory → atomically rewrite touched knowledge roots + append changelog.
|
|
391
|
+
|
|
392
|
+
`context compile --all` is the global compile orchestrator. It first runs the aspect projection subphase in a fixed order: `code` first, then custom aspects by lexicographic `aspect.name`; then it inspects the current workspace for an existing LLM compile workflow / pending semantic compile work. If there is no remaining work, it returns a noop summary and exits 0. If the remaining state can be mechanically closed or advanced, CLI does so. If Agent-authored draft, judge input, or semantic answers are required, CLI stops, returns the single canonical `next_action.command` plus `input_schema`, and exits non-zero; it does not make semantic decisions for the Agent.
|
|
393
|
+
|
|
394
|
+
> `context compile` (without flags) is the existing LLM-driven compile entry/help. `context compile --all` orchestrates both aspect projection and the LLM compile workflow; it is not an aspect-only shortcut.
|
|
395
|
+
> Agent command/router may accept inputs such as `compile -all` or `compile all`, but must normalize them to the CLI command `context compile --all`.
|
|
396
|
+
|
|
397
|
+
**Code aspect note**: `code` has no `projection` section in its aspect.yaml. CLI detects this and uses the built-in code projection (maps `symbols.jsonl` / `packages.jsonl` / `edges.jsonl` to Sections). The command form, projection identity model, and zero-LLM guarantee are identical to `section-projection.v2`.
|
|
398
|
+
|
|
399
|
+
### Inspect Aspect State
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
# List all sources (including aspects):
|
|
403
|
+
context source list
|
|
404
|
+
|
|
405
|
+
# View a specific source:
|
|
406
|
+
context source get aspect:<name>:<slug>
|
|
407
|
+
|
|
408
|
+
# Workspace status (includes knowledge stats):
|
|
409
|
+
context status
|
|
410
|
+
|
|
411
|
+
# Validate integrity:
|
|
412
|
+
context verify
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Query
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
# Aspect-produced Sections are queryable just like any other Section:
|
|
419
|
+
context query "button example basic"
|
|
420
|
+
context query "color tokens"
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Relationship with Align / Compile / Query
|
|
426
|
+
|
|
427
|
+
```
|
|
428
|
+
┌──────────────────────────────────────────────────────────┐
|
|
429
|
+
│ Knowledge Graph Consumption │
|
|
430
|
+
│ context query ←── Full-text index ──→ All Sections │
|
|
431
|
+
└──────────────────────────────────────────────────────────┘
|
|
432
|
+
▲
|
|
433
|
+
┌──────────────────┴──────────────────┐
|
|
434
|
+
│ │
|
|
435
|
+
┌───────┴────────┐ ┌───────┴────────┐
|
|
436
|
+
│ Aspect Project │ │ Align/Compile │
|
|
437
|
+
│ (deterministic)│ │ (LLM-driven) │
|
|
438
|
+
│ │ │ │
|
|
439
|
+
│ extract.ts │ │ capture raw │
|
|
440
|
+
│ ↓ │ │ ↓ │
|
|
441
|
+
│ sections.jsonl │ │ align │
|
|
442
|
+
│ ↓ │ │ ↓ │
|
|
443
|
+
│ compile --asp │ │ compile │
|
|
444
|
+
│ ↓ │ │ ↓ │
|
|
445
|
+
│ Section[] │ │ Section[] │
|
|
446
|
+
└───────┬────────┘ └───────┬────────┘
|
|
447
|
+
│ │
|
|
448
|
+
└──────────────────┬──────────────────┘
|
|
449
|
+
▼
|
|
450
|
+
┌───────────────────────┐
|
|
451
|
+
│ knowledge/entity/ │
|
|
452
|
+
│ <slug>.md │
|
|
453
|
+
│ changelog.md │
|
|
454
|
+
└───────────────────────┘
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
- **capture**: Acquire raw data. For aspects, this means running `extract.ts` and writing to `raw/`. For align/compile, this means capturing URLs, documents, or Markdown.
|
|
458
|
+
- **compile**: Raw data → knowledge Sections. Aspects use deterministic diff; align outputs go through an LLM review pipeline.
|
|
459
|
+
- **query**: Sections from both sources are indexed and retrieved uniformly — consumers do not distinguish provenance.
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Updating the Code Aspect Template
|
|
464
|
+
|
|
465
|
+
When the user chooses to install the code aspect, `context init` generates `aspects/code/aspect.yaml` from the template bundled with the CLI. When you upgrade the CLI, the template may have been updated.
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
# View the current CLI-bundled template (read-only):
|
|
469
|
+
context aspect template code
|
|
470
|
+
|
|
471
|
+
# Write the latest template to aspects/code/ (overwrites aspect.yaml, preserves prompt.md):
|
|
472
|
+
context aspect template code --write
|
|
473
|
+
|
|
474
|
+
# Diff current file against the latest template:
|
|
475
|
+
context aspect template code --diff
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
> `--write` will not overwrite `prompt.md` (user-edited agent hints). To reset prompt.md, delete it first, then `--write`.
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## FAQ
|
|
483
|
+
|
|
484
|
+
### Can aspects and align/compile coexist?
|
|
485
|
+
|
|
486
|
+
Yes. The same Entity can have Sections from both aspect projection and align/compile. Aspect compile only touches Sections whose source namespace belongs to the selected aspect.
|
|
487
|
+
|
|
488
|
+
### Can an aspect create new Entities?
|
|
489
|
+
|
|
490
|
+
Yes, but only when the plugin explicitly emits deterministic documentation nodes through `ctx.emit.node`, which become `nodes.jsonl`. This is for mechanically named pages such as `pkg/colors` or `pkg/quick-start`.
|
|
491
|
+
|
|
492
|
+
`sections.jsonl` alone never creates missing nodes. If a Section points at a node that is neither pre-existing nor declared in `nodes.jsonl`, `anchor_policy: strict` fails and `best-effort` records `skipped_anchor_missing[]`.
|
|
493
|
+
|
|
494
|
+
### Can sections.jsonl be incrementally updated?
|
|
495
|
+
|
|
496
|
+
No. Every `context capture --aspect` produces a **full** `sections.jsonl`. The compile step automatically diffs by projection key: unchanged Sections are untouched, new rows are added, changed rows are updated, and removed rows are deprecated.
|
|
497
|
+
|
|
498
|
+
### How do I debug extract.ts?
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
# Run through the CLI host so ctx.source and ctx.emit are available:
|
|
502
|
+
context capture --aspect my-aspect --force --verbose
|
|
503
|
+
|
|
504
|
+
# Inspect the materialized snapshot:
|
|
505
|
+
cat .context/raw/aspect/my-aspect/my-aspect/latest/nodes.jsonl | jq .
|
|
506
|
+
cat .context/raw/aspect/my-aspect/my-aspect/latest/sections.jsonl | jq .
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Are aspect-produced Sections searchable?
|
|
510
|
+
|
|
511
|
+
Yes. Aspect-created Node title/summary and all Section `content` enter the index. Retrieval does not distinguish between aspect, align, or mdrive provenance.
|
|
512
|
+
|
|
513
|
+
### Can multiple aspects run in parallel?
|
|
514
|
+
|
|
515
|
+
`context capture --aspect a b c` and `context compile --aspect a b c` execute sequentially. Different aspects operate on different namespaces and could theoretically run in parallel — a future CLI version may add a `--parallel` flag.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
schema_version:
|
|
1
|
+
schema_version: context.aspect.v2
|
|
2
2
|
name: code
|
|
3
3
|
runner:
|
|
4
4
|
package: "@c4a/extract"
|
|
@@ -7,7 +7,7 @@ plugins:
|
|
|
7
7
|
- package: "@c4a/extract-ts"
|
|
8
8
|
export: TypeScriptPlugin
|
|
9
9
|
output:
|
|
10
|
-
bucket: raw/aspect/code
|
|
10
|
+
bucket: raw/aspect/code/{source_slug}/{snapshot_id}
|
|
11
11
|
files:
|
|
12
12
|
- source.yaml
|
|
13
13
|
- manifest.json
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
## Target
|
|
6
6
|
|
|
7
7
|
Extract package, symbol, edge, digest, and source-file rows from the local
|
|
8
|
-
worktree.
|
|
9
|
-
context CLI owns `.context` writes
|
|
8
|
+
worktree. This aspect uses bundled runner mode (`runner.package` + `runner.bin`),
|
|
9
|
+
not local plugin mode. The context CLI owns `.context` writes and publishes the
|
|
10
|
+
raw bucket atomically.
|
|
10
11
|
|
|
11
12
|
## Raw Snapshot
|
|
12
13
|
|
|
@@ -18,14 +19,14 @@ raw/aspect/code/<source-slug>/<snapshot-id>/
|
|
|
18
19
|
|
|
19
20
|
The bucket contains:
|
|
20
21
|
|
|
21
|
-
- `source.yaml`
|
|
22
|
-
- `manifest.json`
|
|
23
|
-
- `digests.jsonl`
|
|
24
|
-
- `source-files.jsonl`
|
|
25
|
-
- `packages.jsonl`
|
|
26
|
-
- `symbols.jsonl`
|
|
27
|
-
- `edges.jsonl`
|
|
28
|
-
- `_meta.yaml`
|
|
22
|
+
- `source.yaml` - local source identity and publish upsert hints
|
|
23
|
+
- `manifest.json` - snapshot identity, runner hash, counts, and content hash
|
|
24
|
+
- `digests.jsonl` - module digest rows with `hash_id`, `module_path`, and `dir_commit`
|
|
25
|
+
- `source-files.jsonl` - service-compatible source file mapping
|
|
26
|
+
- `packages.jsonl` - flat package/module view
|
|
27
|
+
- `symbols.jsonl` - flat symbol view, with nested members flattened
|
|
28
|
+
- `edges.jsonl` - flat code relation view
|
|
29
|
+
- `_meta.yaml` - compact copy of snapshot metadata for legacy readers
|
|
29
30
|
|
|
30
31
|
## Notes
|
|
31
32
|
|
|
@@ -37,8 +38,8 @@ The bucket contains:
|
|
|
37
38
|
- The code aspect uses `evidence.mode: none`: it does not generate
|
|
38
39
|
`raw/.evidence` block manifests. Symbols, files, and edges are already
|
|
39
40
|
represented by the bucket JSONL indexes.
|
|
40
|
-
- `context compile code` converts package and symbol rows into
|
|
41
|
-
Sections with code `source_ref` values such as
|
|
41
|
+
- `context compile --aspect code` converts package and symbol rows into
|
|
42
|
+
code-owned Sections with code `source_ref` values such as
|
|
42
43
|
`src-1#package:<package>@<hash>` and
|
|
43
44
|
`src-1#symbol:<locator>:<kind>@<hash>`. These refs resolve against the raw
|
|
44
45
|
code snapshot JSONL indexes, not against prose evidence blocks.
|