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
11 changes: 11 additions & 0 deletions .context/LEARNINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ DO NOT UPDATE FOR:
<!-- INDEX:START -->
| Date | Learning |
|----|--------|
| 2026-05-11 | Naive Markdown line-sweep corrupts multi-line code spans and YAML lists |
| 2026-05-11 | tsc cross-tree include resolves node_modules from source file, not tsconfig |
| 2026-05-10 | Go compile/tool version mismatch comes from the cached toolchain, not the system Go |
| 2026-05-10 | An ongoing user's concrete workaround tax is the strongest validation evidence |
Expand Down Expand Up @@ -141,6 +142,16 @@ DO NOT UPDATE FOR:

---

## [2026-05-11-231025] Naive Markdown line-sweep corrupts multi-line code spans and YAML lists

**Context**: Performed a programmatic typographic sweep across docs/*.md to wrap bare 'ctx' tokens in backticks (commit 61aab858). 81 source files, 236 lines changed. First pass corrupted two indented JSON snippets in MkDocs admonitions because the fence regex anchored to start-of-line and missed admonition-indented fences. After fixing the fence regex, two more corruptions surfaced (multi-line inline-code spans where the opening backtick is on line N and the closing on line N+1: the line-at-a-time transformer treated each line independently, leading to misjudged span boundaries on the second line). After post-sweep validation, a YAML parse error on docs/blog/2026-02-03-the-attention-budget.md surfaced one more breakage: a 'topics:' list-item like '- ctx primitives' got wrapped to '- `ctx` primitives', which is invalid YAML (a value starting with backtick is not a valid unquoted scalar). Total: 2 multi-line span corruptions + 1 YAML breakage, all detected only by post-sweep validation (make site + grep audit), not by the dry-run.

**Lesson**: A naive line-at-a-time regex sweep across Markdown documents must respect a wider 'skip' set than the obvious cases. The full safe-skip list is: (1) triple-backtick fenced code blocks, BOTH root-level and indented inside MkDocs admonitions or list items (fence regex must allow leading whitespace, e.g. '^\\s*```'); (2) inline backtick spans on the same line; (3) multi-line inline-code spans crossing line boundaries (line-at-a-time logic cannot detect both ends, so either track fence-like 'odd-count' state across lines or treat any unclosed-on-line backtick as 'protect rest of line'); (4) the ENTIRE YAML frontmatter block (delimited by '---' at top and next '---'), not just specific keys like title/description/icon, because list-item values under topics/tags/keywords are also YAML and break on a leading backtick; (5) image alt-text '![alt]' (alt-text does not render in monotype); (6) link-reference definitions '[name]: url "title"'; (7) project copyright header comment blocks. Dry-run output never catches YAML or multi-line span breakage; validation MUST include a parser-level check (make site for YAML, post-grep for '``name`' double-backtick patterns near the wrapped token).

**Application**: When designing any future programmatic sweep across docs/ (typography passes, internationalization, brand renames, em-dash replacement, link-text rewrites): (1) implement the full skip set above, not a subset; (2) for fence detection use '^\\s*```', not '^```'; (3) for the frontmatter, detect the entire block between '---' delimiters, not specific keys; (4) for multi-line inline-code, choose between cross-line backtick-pair tracking (complex but correct) or the simpler 'unclosed backtick protects rest of line' heuristic (corrupts ~1 per 100 files but recoverable manually); (5) ALWAYS validate post-sweep with 'make site' (zensical surfaces YAML errors) and a grep for '``\\w' double-backtick patterns near the wrapped token; (6) commit only after both validations are clean. For one-shot sweeps the script can be ad-hoc, but record the validation gate as part of the commit message so the next contributor knows what to check.

---

## [2026-05-11-202124] tsc cross-tree include resolves node_modules from source file, not tsconfig

**Context**: Set up tsc --noEmit gate for the embedded OpenCode plugin. tsconfig lived in tools/typecheck/opencode/; include pointed at internal/assets/integrations/opencode/plugin/index.ts via relative path. First run failed with 'Cannot find module @opencode-ai/plugin' even though node_modules was correctly populated in tools/typecheck/opencode/.
Expand Down
80 changes: 40 additions & 40 deletions .context/steering/product.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,47 @@ description: Product context, goals, and target users
inclusion: always
priority: 10
---
<!--
This is a ctx steering file: persistent behavioral
rules prepended to prompts based on the frontmatter
above.

inclusion (when the rule fires):
always → injected on EVERY tool call. On Claude
Code this is the only mode that fires
AUTOMATICALLY (the PreToolUse hook
passes an empty prompt to ctx agent).
Use for invariants and for any rule
that MUST fire reliably.
auto → injected when the prompt matches the
`description` field above.
- Cursor / Cline / Kiro: native.
- Claude Code: Claude calls the
ctx_steering_get MCP tool on its
own when it decides the rule is
relevant. The ctx plugin ships
the MCP auto-registration; verify
with `claude mcp list`.
manual → only when the file is explicitly named
(e.g. via the MCP tool or a skill).

priority (ordering within a tier):
Lower numbers inject first. 10 is a reasonable
default for invariants; use 50 for normal rules.

tools (scope the rule to specific AI tools):
Empty list = applies to all tools (default).
Example: tools: [claude, cursor]

Edit the body below, then delete this comment.
See docs/cli/steering.md for the full reference.
-->

# Product Context

Describe the product, its goals, and target users.
`ctx` is **persistent context for AI coding sessions**. It gives
the AI memory across sessions by writing project state to
git-versioned Markdown files in `.context/` and feeding that
state back to the AI on every turn.

- **What is this project?**
- **Who uses it?**
- **What problem does it solve?**
- **What is explicitly out of scope?**
## Target users

Developers using AI coding tools (Claude Code, Cursor, OpenCode,
Copilot CLI, Aider, Cline, Kiro, Codex) who want their AI to
remember decisions, conventions, and learnings across sessions
without re-explaining the project every time.

## Load-bearing constraints

These shape every design decision; treat them as invariants when
proposing features:

- **Local-first.** All state lives in the user's filesystem. No
hosted service, no cloud account, no network call required for
normal operation.
- **Single statically-linked binary.** No runtime dependency
tree, no package manager, no install step beyond "drop the
binary on PATH."
- **Git-friendly.** Context is plain Markdown with stable
ordering; diffs are human-readable. Designed so context
history lives in the same repo as the code it describes.
- **Tool-agnostic.** ctx integrates with multiple AI tools as
symmetric peers. No tool is the "primary"; new tools land via
the same `ctx setup <tool>` and `ctx steering sync` paths.
- **No telemetry, no anonymous data collection.** Period.

## Out of scope

- Cloud-hosted state, SaaS sync, or any solution that requires a
network round-trip during normal use. If a proposal needs a
server, it's the wrong proposal for ctx.
- Embedding an LLM into ctx. ctx is the persistence layer; the
LLM lives in the user's chosen AI tool.
- AI-tool lock-in. Features must work across at least two of the
supported tool families (hook-based + native-rules), not be
Claude-Code-only or Cursor-only by design.
95 changes: 56 additions & 39 deletions .context/steering/structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,63 @@ description: Project structure and directory conventions
inclusion: always
priority: 10
---
<!--
This is a ctx steering file: persistent behavioral
rules prepended to prompts based on the frontmatter
above.

inclusion (when the rule fires):
always → injected on EVERY tool call. On Claude
Code this is the only mode that fires
AUTOMATICALLY (the PreToolUse hook
passes an empty prompt to ctx agent).
Use for invariants and for any rule
that MUST fire reliably.
auto → injected when the prompt matches the
`description` field above.
- Cursor / Cline / Kiro: native.
- Claude Code: Claude calls the
ctx_steering_get MCP tool on its
own when it decides the rule is
relevant. The ctx plugin ships
the MCP auto-registration; verify
with `claude mcp list`.
manual → only when the file is explicitly named
(e.g. via the MCP tool or a skill).

priority (ordering within a tier):
Lower numbers inject first. 10 is a reasonable
default for invariants; use 50 for normal rules.

tools (scope the rule to specific AI tools):
Empty list = applies to all tools (default).
Example: tools: [claude, cursor]

Edit the body below, then delete this comment.
See docs/cli/steering.md for the full reference.
-->

# Project Structure

Describe the project layout and directory conventions.
## Top-level layout

- **Top-level directories and their purpose**
- **Where new files should go** (and where they should not)
- **Naming conventions** for files, packages, modules
| Path | What it is |
|------|-----------|
| `cmd/ctx/` | Cobra entry point. One main package; thin. |
| `internal/` | Private Go packages (compiler-enforced no-external-import). |
| `editors/<editor>/` | Separately-published editor integrations (currently `editors/vscode/`). NOT embedded. |
| `tools/<tool>/` | Dev tooling for embedded assets, sitting outside the embed tree (currently `tools/typecheck/opencode/`). |
| `docs/` | Source for the docs site at https://ctx.ist. |
| `site/` | Built output of `docs/` via `make site` (zensical). Committed. |
| `specs/` | Feature specs; every commit gets a `Spec: specs/<name>.md` trailer. |
| `.context/` | This project's own ctx context (CONSTITUTION, TASKS, DECISIONS, LEARNINGS, CONVENTIONS, steering, journal). |
| `hack/` | Project shell scripts (release, lint helpers, detectors). |
| `ideas/` | Drafts and unscoped exploration; not authoritative. |

## Inside `internal/`

- Organized by **domain**, one package per concern. The split is
read/write/config/err/cli/etc., not "by layer."
- `internal/assets/` is the embed payload root. **Everything
under it is `//go:embed`-ed into the binary.** Read
`internal/assets/README.md` before adding files there: the
layout has a contract (embedded vs. separately-published) that
is easy to violate.
- `internal/cli/<domain>/` mirrors the Cobra command tree. New
commands land in their domain package, not as siblings of the
root.

## Where new files go

- **New Go domain logic** → existing `internal/<domain>/` if it
exists. `ls internal/` and read the candidate's `doc.go`
before creating a new package; extending the existing package
is the default.
- **New embedded asset** → under `internal/assets/<domain>/`,
with a matching `//go:embed` directive added in
`internal/assets/embed.go`. Add a presence test in
`embed_test.go` at minimum.
- **Dev tooling for an embedded asset** (linters, type-checkers,
package.json/tsconfig.json) → `tools/typecheck/<asset>/` or
similar sibling. Never inside `internal/assets/` itself; the
embed contract forbids it.
- **New separately-published deliverable** (e.g. a new editor
extension) → `editors/<editor>/`, with its own pipeline. Not
under `internal/`.
- **User-facing documentation** → `docs/`, then `make site`.
Each tool that warrants a guide gets `docs/home/<tool>.md`.

## Where new files do NOT go

- Not in the repo root unless they are project-wide config
(`Makefile`, `go.mod`, `zensical.toml`, etc.).
- Not in `internal/assets/` if they are not actually embedded.
Foreign-language source belongs only when `embed.go` references
it; tooling about embedded assets belongs in `tools/`.
- Not under `internal/` at all if they are deliverables to an
external channel (marketplace, npm registry, etc.).
93 changes: 53 additions & 40 deletions .context/steering/tech.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,47 +4,60 @@ description: Technology stack, constraints, and dependencies
inclusion: always
priority: 10
---
<!--
This is a ctx steering file: persistent behavioral
rules prepended to prompts based on the frontmatter
above.

inclusion (when the rule fires):
always → injected on EVERY tool call. On Claude
Code this is the only mode that fires
AUTOMATICALLY (the PreToolUse hook
passes an empty prompt to ctx agent).
Use for invariants and for any rule
that MUST fire reliably.
auto → injected when the prompt matches the
`description` field above.
- Cursor / Cline / Kiro: native.
- Claude Code: Claude calls the
ctx_steering_get MCP tool on its
own when it decides the rule is
relevant. The ctx plugin ships
the MCP auto-registration; verify
with `claude mcp list`.
manual → only when the file is explicitly named
(e.g. via the MCP tool or a skill).

priority (ordering within a tier):
Lower numbers inject first. 10 is a reasonable
default for invariants; use 50 for normal rules.

tools (scope the rule to specific AI tools):
Empty list = applies to all tools (default).
Example: tools: [claude, cursor]

Edit the body below, then delete this comment.
See docs/cli/steering.md for the full reference.
-->

# Technology Stack

Describe the technology stack, constraints, and key dependencies.
## Primary

- **Languages and versions**
- **Frameworks and key libraries**
- **Runtime / deployment target**
- **Hard constraints** (e.g. no CGO, no network at test time)
- **Go 1.26+**, statically linked (`CGO_ENABLED=0`). The `ctx`
binary is the entire deliverable for the core; everything else
ships as embedded bytes inside it.
- **Cobra** for the CLI command surface.
- **`embed.FS`** for shipping foreign-language assets (TypeScript,
Bash, PowerShell, Markdown, JSON, YAML) inside the Go binary.
See `internal/assets/README.md` for the embed contract; the
hard `//go:embed` no-`../` rule shapes the directory layout.

## Separately-published

- **VS Code extension** at `editors/vscode/` ships as a `.vsix`
to the VS Code Marketplace under publisher `activememory`. It
is NOT embedded; it has its own `package.json`, `tsconfig.json`,
and CI guardrails (`vscode-extension` job).
- The embedded **OpenCode plugin** at
`internal/assets/integrations/opencode/plugin/index.ts` has its
type-check tooling outside the embed tree at
`tools/typecheck/opencode/`.

## Hard constraints

- **No runtime dependencies.** No package manager, no network
fetch on install. If a feature needs a daemon or a service,
it's the wrong feature.
- **No CGO.** Build must succeed with `CGO_ENABLED=0` on every
supported platform (Linux/macOS/Windows × amd64/arm64).
- **No network calls during normal operation.** Tests included.
Operations that genuinely need network (e.g. GitHub release
download in the VS Code extension auto-bootstrap) are scoped
and opt-in.
- **Foreign-language assets ship embedded, not at install time.**
TypeScript / Bash / PowerShell that integrates with external
tools is baked into the Go binary at compile time and written
out to the user's filesystem by `ctx setup <tool>`.

## Companion tooling

- **GitNexus** (`mcp__gitnexus__*`) — code intelligence MCP
server for impact analysis, route maps, and shape checks.
- **Gemini Search** — preferred over built-in web search for
faster, more accurate results.

## Build / test / lint

- `make build`, `make test`, `make lint` are the canonical
entrypoints. CI runs the same.
- `make site` rebuilds `site/` from `docs/` via zensical.
- The TS type-check for embedded OpenCode plugin lives at
`tools/typecheck/opencode/`; `npx tsc --noEmit` is the gate.
- The VS Code extension gate runs `npm ci && npm run build &&
npx tsc --noEmit -p tsconfig.ci.json` in CI.
Loading
Loading