diff --git a/.claude/skills/_ctx-release-notes/SKILL.md b/.claude/skills/_ctx-release-notes/SKILL.md
index 193b034b9..e586dc835 100644
--- a/.claude/skills/_ctx-release-notes/SKILL.md
+++ b/.claude/skills/_ctx-release-notes/SKILL.md
@@ -27,7 +27,9 @@ Generate release notes for the next release of Context CLI.
1. **Read version and find baseline**:
```bash
cat VERSION
-git describe --tags --abbrev=0 2>/dev/null
+# Use the latest tag globally, not ancestry-based git describe
+# (git describe follows reachability and misses tags on other branches)
+git tag --sort=-v:refname | head -1
```
2. **Gather commits since last tag** (filter out noise):
diff --git a/.claude/skills/_ctx-release/SKILL.md b/.claude/skills/_ctx-release/SKILL.md
index 7e227f9f6..404468a72 100644
--- a/.claude/skills/_ctx-release/SKILL.md
+++ b/.claude/skills/_ctx-release/SKILL.md
@@ -13,8 +13,8 @@ All three prerequisites must be true:
2. **`dist/RELEASE_NOTES.md`** exists (generate with `/_ctx-release-notes`)
3. **Working tree is clean** (all changes committed)
-If any prerequisite fails, stop — running the release script with
-missing notes or a dirty tree can produce an incomplete or unsigned
+If any prerequisite fails, stop. Running the release script with
+missing notes or a dirty tree produces an incomplete or unsigned
tag that must be manually deleted.
## When to Use
@@ -42,17 +42,23 @@ make release
```
This script:
-- Updates version references in `docs/index.md`
+- Updates version in 4 config files (plugin.json, marketplace.json, VS Code package.json + lock)
+- Updates download URLs in 3 doc files (index.md, getting-started.md, integrations.md)
+- Adds new row to versions.md
- Rebuilds the documentation site
-- Commits the docs update
+- Commits the version and docs update
- Runs tests and smoke tests
-- Builds binaries for all platforms
+- Builds binaries for all 6 platforms
- Creates and pushes a signed git tag
- Updates the `latest` tag
-3. **After completion**, tell the user to create the GitHub release
- at the URL shown in the script output and upload binaries from
- `dist/`.
+3. **After completion**, verify the GitHub release was created by CI
+ at `https://github.com/ActiveMemory/ctx/releases`.
+
+## Full Runbook
+
+See [Cutting a Release](https://ctx.ist/operations/release/) for the
+complete step-by-step guide including troubleshooting.
## Quality Checklist
@@ -61,4 +67,4 @@ This script:
- [ ] Working tree is clean
- [ ] Tests and smoke tests pass
- [ ] Tag is pushed to origin
-- [ ] User reminded to create GitHub release
+- [ ] GitHub release created by CI with all 6 binaries
diff --git a/.context/DECISIONS.md b/.context/DECISIONS.md
index aa5a1f6bb..90aca4797 100644
--- a/.context/DECISIONS.md
+++ b/.context/DECISIONS.md
@@ -3,6 +3,7 @@
| Date | Decision |
|------|--------|
+| 2026-03-23 | Pre/pre HTML tags promoted to shared constants in config/marker |
| 2026-03-23 | Pure-data param structs in entity — replace function pointers with text keys |
| 2026-03-22 | No runtime pluralization — use singular/plural text key pairs |
| 2026-03-22 | Output functions belong in write/, never in core/ or cmd/ |
@@ -62,6 +63,20 @@
| 2026-02-27 | Webhook and notification design (consolidated) |
+## [2026-03-23-165612] Pre/pre HTML tags promoted to shared constants in config/marker
+
+**Status**: Accepted
+
+**Context**: Two packages (normalize and format) used hardcoded pre strings independently
+
+**Decision**: Pre/pre HTML tags promoted to shared constants in config/marker
+
+**Rationale**: Cross-package magic strings belong in config constants per CONVENTIONS.md
+
+**Consequence**: marker.TagPre and marker.TagPreClose are the canonical references; package-local constants deleted
+
+---
+
## [2026-03-23-003346] Pure-data param structs in entity — replace function pointers with text keys
**Status**: Accepted
diff --git a/.context/LEARNINGS.md b/.context/LEARNINGS.md
index 79900dace..ad70bc894 100644
--- a/.context/LEARNINGS.md
+++ b/.context/LEARNINGS.md
@@ -3,6 +3,8 @@
| Date | Learning |
|------|--------|
+| 2026-03-23 | Typography detection script needs exclusion lists for intentional uses |
+| 2026-03-23 | Subagents rename functions and restructure code beyond their scope |
| 2026-03-23 | Splitting core/ into subpackages reveals hidden structure |
| 2026-03-23 | Higher-order callbacks in param structs are a code smell |
| 2026-03-22 | Types in god-object files create circular dependencies |
@@ -91,6 +93,26 @@
---
+## [2026-03-23-165611] Typography detection script needs exclusion lists for intentional uses
+
+**Context**: detect-ai-typography.sh flagged config/token/delim.go (intentional delimiter constants) and test files (test data containing em-dashes)
+
+**Lesson**: Detection scripts for convention enforcement need exclusion patterns for files where the flagged patterns are intentional data, not prose
+
+**Application**: Add exclusion patterns proactively when creating detection scripts; *_test.go and constant-definition files are common false positive sources
+
+---
+
+## [2026-03-23-165610] Subagents rename functions and restructure code beyond their scope
+
+**Context**: Agents tasked with fixing em-dashes in comments also renamed exported functions, changed import aliases, and modified function signatures
+
+**Lesson**: Always diff-audit agent output for structural changes before accepting edits, even when the task is narrowly scoped
+
+**Application**: After any agent batch edit, run git diff --stat and scan for non-comment changes before staging
+
+---
+
## [2026-03-23-003544] Splitting core/ into subpackages reveals hidden structure
**Context**: init core/ was a flat bag of domain objects — splitting into backup/, claude/, entry/, merge/, plan/, plugin/, project/, prompt/, tpl/, validate/ exposed duplicated logic, misplaced types, and function-pointer smuggling that were invisible in the flat layout
diff --git a/.context/TASKS.md b/.context/TASKS.md
index 2e76538bb..10e20e9ed 100644
--- a/.context/TASKS.md
+++ b/.context/TASKS.md
@@ -41,6 +41,8 @@ checks field presence, not content safety.
- [ ] Make TitleSlugMaxLen configurable via .ctxrc #added:2026-03-21-070944
+- [ ] Replace hack/lint-drift.sh with AST-based Go tests in internal/audit/. Spec: `specs/ast-audit-tests.md` #added:2026-03-23-210000
+
- [ ] Add AST-based lint test to detect exported functions with no external callers #added:2026-03-21-070357
- [ ] Audit exported functions used only within their own package and make them private #added:2026-03-21-070346
diff --git a/.context/scratchpad.enc b/.context/scratchpad.enc
index d6ba937ff..1e5bb130e 100644
Binary files a/.context/scratchpad.enc and b/.context/scratchpad.enc differ
diff --git a/CONTRIBUTING-SKILLS.md b/CONTRIBUTING-SKILLS.md
index 129a34b3a..1f49586ed 100644
--- a/CONTRIBUTING-SKILLS.md
+++ b/CONTRIBUTING-SKILLS.md
@@ -6,7 +6,7 @@ session. This page is for contributors who want to understand what each
skill does without reading individual `SKILL.md` files.
For ctx plugin skills (`/ctx-status`, `/ctx-recall`, etc.), see the
-[ctx skills documentation](docs/skills.md).
+[ctx skills documentation](docs/reference/skills.md).
## Skill Catalog
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f22ebc802..574a8959d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,7 +4,7 @@
Thank you for your interest in contributing to `ctx`.
-**Full contributing guide**: https://ctx.ist/contributing/
+**Full contributing guide**: https://ctx.ist/home/contributing/
The guide covers:
diff --git a/docs/cli/tools.md b/docs/cli/tools.md
index f4fd3f790..9c541c1f6 100644
--- a/docs/cli/tools.md
+++ b/docs/cli/tools.md
@@ -821,7 +821,7 @@ ctx prompt rm
- `name`: Template name (without `.md` extension)
-**See also**: [Prompt Templates](../recipes/prompts.md)
+**See also**: [Prompt Templates](../recipes/prompt-templates.md)
---
diff --git a/docs/home/about.md b/docs/home/about.md
index 059bc7f63..44b21e31d 100644
--- a/docs/home/about.md
+++ b/docs/home/about.md
@@ -135,7 +135,7 @@ makes the *next* session **smarter**.
**Connect with `ctx`**
* [Join the Community →](community.md): ask questions, share workflows, and help shape what comes next
-* [Read the Blog →](../../blog/): real-world patterns, ponderings, and lessons learned from building `ctx` using `ctx`
+* [Read the Blog →](../blog/): real-world patterns, ponderings, and lessons learned from building `ctx` using `ctx`
----
diff --git a/docs/home/contributing.md b/docs/home/contributing.md
index 2fe46d771..878276462 100644
--- a/docs/home/contributing.md
+++ b/docs/home/contributing.md
@@ -120,7 +120,7 @@ ctx/
| Directory | What lives here | Distributed to users? |
|----------------------------------|-------------------------------------------------|-----------------------|
-| `internal/assets/claude/skills/` | The 29 `ctx-*` skills that ship with the plugin | Yes |
+| `internal/assets/claude/skills/` | The 39 `ctx-*` skills that ship with the plugin | Yes |
| `.claude/skills/` | Dev-only skills (release, QA, backup, etc.) | No |
**`internal/assets/claude/skills/`** is the single source of truth for
@@ -164,7 +164,7 @@ and are now available to all ctx users: `/ctx-brainstorm`, `/ctx-check-links`,
6. Add a section to the appropriate CLI doc page in `docs/cli/`.
Pattern to follow: `internal/cli/pad/pad.go` (parent with subcommands) or
-`internal/cli/complete/complete.go` (single command).
+`internal/cli/drift/drift.go` (single command).
### Adding a New Session Parser
diff --git a/docs/operations/index.md b/docs/operations/index.md
index 950fbca8b..90e5c7088 100644
--- a/docs/operations/index.md
+++ b/docs/operations/index.md
@@ -31,6 +31,13 @@ Windsurf, and other AI coding tools.
---
+### [Cutting a Release](release.md)
+
+Step-by-step runbook for maintainers: bump version, generate
+release notes, run the release script, and verify the result.
+
+---
+
### [Autonomous Loops](autonomous-loop.md)
Run an unattended AI agent that works through tasks overnight,
diff --git a/docs/operations/release.md b/docs/operations/release.md
new file mode 100644
index 000000000..bae4496ce
--- /dev/null
+++ b/docs/operations/release.md
@@ -0,0 +1,178 @@
+---
+# / ctx: https://ctx.ist
+# ,'`./ do you remember?
+# `.,'\
+# \ Copyright 2026-present Context contributors.
+# SPDX-License-Identifier: Apache-2.0
+
+title: Cutting a Release
+icon: lucide/package
+---
+
+
+
+## Prerequisites
+
+Before you can cut a release you need:
+
+* Push access to `origin` (GitHub)
+* GPG signing configured (`make gpg-test`)
+* [Go](https://go.dev/) installed (version in `go.mod`)
+* [Zensical](https://github.com/zensical/zensical) installed (`make site-setup`)
+* A clean working tree (`git status` shows nothing to commit)
+
+---
+
+## Step-by-Step
+
+### 1. Update the VERSION File
+
+```bash
+echo "0.9.0" > VERSION
+git add VERSION
+git commit -m "chore: bump version to 0.9.0"
+```
+
+The VERSION file uses bare semver (`0.9.0`), no `v` prefix.
+The release script adds the `v` prefix for git tags.
+
+### 2. Generate Release Notes
+
+In Claude Code:
+
+```
+/_ctx-release-notes
+```
+
+This analyzes commits since the last tag and writes
+`dist/RELEASE_NOTES.md`. The release script refuses to
+proceed without this file.
+
+### 3. Commit Any Remaining Changes
+
+```bash
+git status # must be clean
+make audit # full check: fmt, vet, lint, test
+```
+
+### 4. Run the Release
+
+```bash
+make release
+```
+
+Or, if you are in a Claude Code session:
+
+```
+/_ctx-release
+```
+
+The release script does everything in order:
+
+| Step | What happens |
+|------|-------------|
+| 1 | Reads `VERSION`, verifies release notes exist |
+| 2 | Verifies working tree is clean |
+| 3 | Updates version in 4 config files (plugin.json, marketplace.json, VS Code package.json + lock) |
+| 4 | Updates download URLs in 3 doc files (index.md, getting-started.md, integrations.md) |
+| 5 | Adds new row to versions.md |
+| 6 | Rebuilds the documentation site (`make site`) |
+| 7 | Commits all version and docs updates |
+| 8 | Runs `make test` and `make smoke` |
+| 9 | Builds binaries for all 6 platforms via `hack/build-all.sh` |
+| 10 | Creates a signed git tag (`v0.9.0`) |
+| 11 | Pushes the tag to origin |
+| 12 | Updates and pushes the `latest` tag |
+
+### 5. GitHub CI Takes Over
+
+Pushing a `v*` tag triggers `.github/workflows/release.yml`:
+
+1. Checks out the tagged commit
+2. Runs the full test suite
+3. Builds binaries for all platforms
+4. Creates a GitHub Release with auto-generated notes
+5. Uploads binaries and SHA256 checksums
+
+### 6. Verify
+
+- [ ] [GitHub Releases](https://github.com/ActiveMemory/ctx/releases) shows the new version
+- [ ] All 6 binaries are attached (linux/darwin x amd64/arm64, windows x amd64)
+- [ ] SHA256 files are attached
+- [ ] Release notes look correct
+
+---
+
+## What Gets Updated Automatically
+
+The release script updates 8 files so you do not have to:
+
+| File | What changes |
+|------|-------------|
+| `internal/assets/claude/.claude-plugin/plugin.json` | Plugin version |
+| `.claude-plugin/marketplace.json` | Marketplace version (2 fields) |
+| `editors/vscode/package.json` | VS Code extension version |
+| `editors/vscode/package-lock.json` | VS Code lock version (2 fields) |
+| `docs/index.md` | Download URLs |
+| `docs/home/getting-started.md` | Download URLs |
+| `docs/operations/integrations.md` | VSIX filename version |
+| `docs/reference/versions.md` | New version row + latest pointer |
+
+The Go binary version is injected at build time via `-ldflags`
+from the VERSION file. No source file needs editing.
+
+---
+
+## Build Targets Reference
+
+| Target | What it does |
+|--------|-------------|
+| `make release` | Full release (script + tag + push) |
+| `make build` | Build binary for current platform |
+| `make build-all` | Build all 6 platform binaries |
+| `make test` | Unit tests |
+| `make smoke` | Integration smoke tests |
+| `make audit` | Full check (fmt + vet + lint + drift + docs + test) |
+| `make site` | Rebuild documentation site |
+
+---
+
+## Troubleshooting
+
+### "Release notes not found"
+
+```
+ERROR: dist/RELEASE_NOTES.md not found.
+```
+
+Run `/_ctx-release-notes` in Claude Code first, or write
+`dist/RELEASE_NOTES.md` manually.
+
+### "Working tree is not clean"
+
+```
+ERROR: Working tree is not clean.
+```
+
+Commit or stash all changes before running `make release`.
+
+### "Tag already exists"
+
+```
+ERROR: Tag v0.9.0 already exists.
+```
+
+You cannot release the same version twice. Either bump VERSION
+to a new version, or delete the old tag if the release was
+incomplete:
+
+```bash
+git tag -d v0.9.0
+git push origin :refs/tags/v0.9.0
+```
+
+### CI build fails after tag push
+
+The tag is already published. Fix the issue, bump to a patch
+version (e.g. `0.9.1`), and release again. Do not force-push
+tags that others may have already fetched.
diff --git a/editors/vscode/package.json b/editors/vscode/package.json
index d8e3656a1..5457ed034 100644
--- a/editors/vscode/package.json
+++ b/editors/vscode/package.json
@@ -2,7 +2,7 @@
"name": "ctx-context",
"displayName": "ctx — Persistent Context for AI",
"description": "Chat participant (@ctx) for persistent project context across AI coding sessions",
- "version": "0.7.0",
+ "version": "0.8.0",
"publisher": "activememory",
"license": "Apache-2.0",
"homepage": "https://github.com/ActiveMemory/ctx",
@@ -86,17 +86,13 @@
"description": "Reconcile context with codebase"
},
{
- "name": "complete",
- "description": "Mark a task as completed"
+ "name": "task",
+ "description": "Task operations: complete, archive, snapshot"
},
{
"name": "remind",
"description": "Manage session-scoped reminders"
},
- {
- "name": "tasks",
- "description": "Archive or snapshot tasks"
- },
{
"name": "pad",
"description": "Encrypted scratchpad for sensitive notes"
@@ -108,6 +104,66 @@
{
"name": "system",
"description": "System diagnostics and bootstrap"
+ },
+ {
+ "name": "memory",
+ "description": "Memory bridge operations (sync, status, diff, import, publish)"
+ },
+ {
+ "name": "journal",
+ "description": "Journal management (site, obsidian)"
+ },
+ {
+ "name": "doctor",
+ "description": "Context health diagnostics"
+ },
+ {
+ "name": "config",
+ "description": "Runtime configuration (switch, status, schema)"
+ },
+ {
+ "name": "prompt",
+ "description": "Prompt templates (list, add, show, rm)"
+ },
+ {
+ "name": "why",
+ "description": "Show rationale for context design decisions"
+ },
+ {
+ "name": "change",
+ "description": "Show recent codebase changes"
+ },
+ {
+ "name": "dep",
+ "description": "Show project dependencies"
+ },
+ {
+ "name": "guide",
+ "description": "Quick start guide"
+ },
+ {
+ "name": "permission",
+ "description": "Permission snapshot and restore"
+ },
+ {
+ "name": "site",
+ "description": "Documentation site (feed)"
+ },
+ {
+ "name": "loop",
+ "description": "Generate autonomous iteration scripts"
+ },
+ {
+ "name": "pause",
+ "description": "Pause context hooks for current session"
+ },
+ {
+ "name": "resume",
+ "description": "Resume context hooks"
+ },
+ {
+ "name": "reindex",
+ "description": "Rebuild context file indices"
}
],
"disambiguation": [
diff --git a/editors/vscode/src/extension.ts b/editors/vscode/src/extension.ts
index 99391619f..87feb6f6e 100644
--- a/editors/vscode/src/extension.ts
+++ b/editors/vscode/src/extension.ts
@@ -584,40 +584,82 @@ async function handleSync(
return { metadata: { command: "sync" } };
}
-async function handleComplete(
+async function handleTask(
stream: vscode.ChatResponseStream,
prompt: string,
cwd: string,
token: vscode.CancellationToken
): Promise {
- const taskRef = prompt.trim();
- if (!taskRef) {
- stream.markdown(
- "**Usage:** `@ctx /complete `\n\n" +
- "Example: `@ctx /complete 3` or `@ctx /complete Fix login bug`"
- );
- return { metadata: { command: "complete" } };
+ const parts = prompt.trim().split(/\s+/);
+ const subcmd = parts[0]?.toLowerCase();
+ const rest = parts.slice(1).join(" ");
+
+ let args: string[];
+ let progressMsg: string;
+
+ switch (subcmd) {
+ case "complete": {
+ const taskRef = rest.trim();
+ if (!taskRef) {
+ stream.markdown(
+ "**Usage:** `@ctx /task complete `\n\n" +
+ "Example: `@ctx /task complete 3` or " +
+ "`@ctx /task complete Fix login bug`"
+ );
+ return { metadata: { command: "task" } };
+ }
+ args = ["task", "complete", taskRef];
+ progressMsg = "Marking task as completed...";
+ break;
+ }
+ case "archive":
+ args = ["task", "archive"];
+ progressMsg = "Archiving completed tasks...";
+ break;
+ case "snapshot":
+ args = rest ? ["task", "snapshot", rest] : ["task", "snapshot"];
+ progressMsg = "Creating task snapshot...";
+ break;
+ default:
+ stream.markdown(
+ "**Usage:** `@ctx /task `\n\n" +
+ "| Subcommand | Description |\n" +
+ "|------------|-------------|\n" +
+ "| `complete [` | Mark a task as completed |\n" +
+ "| `archive` | Move completed tasks to archive |\n" +
+ "| `snapshot [name]` | Create point-in-time snapshot |\n\n" +
+ "Example: `@ctx /task complete 3` or " +
+ "`@ctx /task archive`"
+ );
+ return { metadata: { command: "task" } };
}
+ args.push("--no-color");
- stream.progress("Marking task as completed...");
+ stream.progress(progressMsg);
try {
- const { stdout, stderr } = await runCtx(
- ["complete", taskRef, "--no-color"],
- cwd,
- token
- );
+ const { stdout, stderr } = await runCtx(args, cwd, token);
const output = (stdout + stderr).trim();
if (output) {
stream.markdown("```\n" + output + "\n```");
} else {
- stream.markdown(`Task **${taskRef}** marked as completed.`);
+ switch (subcmd) {
+ case "complete":
+ stream.markdown(`Task **${rest.trim()}** marked as completed.`);
+ break;
+ case "archive":
+ stream.markdown("Completed tasks archived.");
+ break;
+ default:
+ stream.markdown("Task snapshot created.");
+ break;
+ }
}
} catch (err: unknown) {
stream.markdown(
- `**Error:** Failed to complete task.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ `**Error:** Failed to ${subcmd} task.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
);
}
- return { metadata: { command: "complete" } };
+ return { metadata: { command: "task" } };
}
async function handleRemind(
@@ -678,62 +720,6 @@ async function handleRemind(
return { metadata: { command: "remind" } };
}
-async function handleTasks(
- stream: vscode.ChatResponseStream,
- prompt: string,
- cwd: string,
- token: vscode.CancellationToken
-): Promise {
- const parts = prompt.trim().split(/\s+/);
- const subcmd = parts[0]?.toLowerCase();
- const rest = parts.slice(1).join(" ");
-
- let args: string[];
- let progressMsg: string;
-
- switch (subcmd) {
- case "archive":
- args = ["tasks", "archive"];
- progressMsg = "Archiving completed tasks...";
- break;
- case "snapshot":
- args = rest ? ["tasks", "snapshot", rest] : ["tasks", "snapshot"];
- progressMsg = "Creating task snapshot...";
- break;
- default:
- stream.markdown(
- "**Usage:** `@ctx /tasks `\n\n" +
- "| Subcommand | Description |\n" +
- "|------------|-------------|\n" +
- "| `archive` | Move completed tasks to archive |\n" +
- "| `snapshot [name]` | Create point-in-time snapshot |\n\n" +
- "Example: `@ctx /tasks archive` or `@ctx /tasks snapshot pre-refactor`"
- );
- return { metadata: { command: "tasks" } };
- }
- args.push("--no-color");
-
- stream.progress(progressMsg);
- try {
- const { stdout, stderr } = await runCtx(args, cwd, token);
- const output = (stdout + stderr).trim();
- if (output) {
- stream.markdown("```\n" + output + "\n```");
- } else {
- stream.markdown(
- subcmd === "archive"
- ? "Completed tasks archived."
- : "Task snapshot created."
- );
- }
- } catch (err: unknown) {
- stream.markdown(
- `**Error:** Failed to ${subcmd} tasks.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
- );
- }
- return { metadata: { command: "tasks" } };
-}
-
async function handlePad(
stream: vscode.ChatResponseStream,
prompt: string,
@@ -813,7 +799,6 @@ async function handleNotify(
): Promise {
const parts = prompt.trim().split(/\s+/);
const subcmd = parts[0]?.toLowerCase();
- const rest = parts.slice(1).join(" ");
let args: string[];
let progressMsg: string;
@@ -903,7 +888,7 @@ async function handleSystem(
"|------------|-------------|\n" +
"| `resources` | Show system resource usage |\n" +
"| `bootstrap` | Print context location for AI agents |\n" +
- "| `message list\|show\|edit\|reset` | Manage hook messages |\n\n" +
+ "| `message list|show|edit|reset` | Manage hook messages |\n\n" +
"Example: `@ctx /system resources` or `@ctx /system bootstrap`"
);
return { metadata: { command: "system" } };
@@ -927,6 +912,553 @@ async function handleSystem(
return { metadata: { command: "system" } };
}
+async function handleMemory(
+ stream: vscode.ChatResponseStream,
+ prompt: string,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ const parts = prompt.trim().split(/\s+/);
+ const subcmd = parts[0]?.toLowerCase();
+
+ let args: string[];
+ let progressMsg: string;
+
+ switch (subcmd) {
+ case "sync":
+ args = ["memory", "sync"];
+ progressMsg = "Syncing memory bridge...";
+ break;
+ case "status":
+ args = ["memory", "status"];
+ progressMsg = "Checking memory status...";
+ break;
+ case "diff":
+ args = ["memory", "diff"];
+ progressMsg = "Diffing memory state...";
+ break;
+ case "import":
+ args = ["memory", "import"];
+ progressMsg = "Importing memory...";
+ break;
+ case "publish":
+ args = ["memory", "publish"];
+ progressMsg = "Publishing memory...";
+ break;
+ case "unpublish":
+ args = ["memory", "unpublish"];
+ progressMsg = "Unpublishing memory...";
+ break;
+ default:
+ stream.markdown(
+ "**Usage:** `@ctx /memory `\n\n" +
+ "| Subcommand | Description |\n" +
+ "|------------|-------------|\n" +
+ "| `sync` | Synchronize memory bridge |\n" +
+ "| `status` | Show memory bridge status |\n" +
+ "| `diff` | Show memory diff |\n" +
+ "| `import` | Import external memory |\n" +
+ "| `publish` | Publish curated context |\n" +
+ "| `unpublish` | Remove published context |\n\n" +
+ "Example: `@ctx /memory status` or `@ctx /memory sync`"
+ );
+ return { metadata: { command: "memory" } };
+ }
+ args.push("--no-color");
+
+ stream.progress(progressMsg);
+ try {
+ const { stdout, stderr } = await runCtx(args, cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown(`Memory ${subcmd} completed.`);
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to run memory ${subcmd}.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "memory" } };
+}
+
+async function handleJournal(
+ stream: vscode.ChatResponseStream,
+ prompt: string,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ const parts = prompt.trim().split(/\s+/);
+ const subcmd = parts[0]?.toLowerCase();
+
+ let args: string[];
+ let progressMsg: string;
+
+ switch (subcmd) {
+ case "site":
+ args = ["journal", "site"];
+ progressMsg = "Generating journal site...";
+ break;
+ case "obsidian":
+ args = ["journal", "obsidian"];
+ progressMsg = "Exporting journal to Obsidian...";
+ break;
+ default:
+ stream.markdown(
+ "**Usage:** `@ctx /journal `\n\n" +
+ "| Subcommand | Description |\n" +
+ "|------------|-------------|\n" +
+ "| `site` | Generate journal site |\n" +
+ "| `obsidian` | Export journal to Obsidian |\n\n" +
+ "Example: `@ctx /journal site`"
+ );
+ return { metadata: { command: "journal" } };
+ }
+ args.push("--no-color");
+
+ stream.progress(progressMsg);
+ try {
+ const { stdout, stderr } = await runCtx(args, cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown(`Journal ${subcmd} completed.`);
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to run journal ${subcmd}.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "journal" } };
+}
+
+async function handleDoctor(
+ stream: vscode.ChatResponseStream,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ stream.progress("Running context health diagnostics...");
+ try {
+ const { stdout, stderr } = await runCtx(["doctor", "--no-color"], cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown("Context health check passed.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to run doctor.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "doctor" } };
+}
+
+async function handleConfig(
+ stream: vscode.ChatResponseStream,
+ prompt: string,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ const parts = prompt.trim().split(/\s+/);
+ const subcmd = parts[0]?.toLowerCase();
+ const rest = parts.slice(1).join(" ");
+
+ let args: string[];
+ let progressMsg: string;
+
+ switch (subcmd) {
+ case "switch":
+ args = rest ? ["config", "switch", rest] : ["config", "switch"];
+ progressMsg = "Switching configuration...";
+ break;
+ case "status":
+ args = ["config", "status"];
+ progressMsg = "Checking configuration status...";
+ break;
+ case "schema":
+ args = ["config", "schema"];
+ progressMsg = "Showing configuration schema...";
+ break;
+ default:
+ stream.markdown(
+ "**Usage:** `@ctx /config `\n\n" +
+ "| Subcommand | Description |\n" +
+ "|------------|-------------|\n" +
+ "| `switch` | Switch active configuration |\n" +
+ "| `status` | Show current configuration |\n" +
+ "| `schema` | Show configuration schema |\n\n" +
+ "Example: `@ctx /config status` or `@ctx /config switch minimal`"
+ );
+ return { metadata: { command: "config" } };
+ }
+ args.push("--no-color");
+
+ stream.progress(progressMsg);
+ try {
+ const { stdout, stderr } = await runCtx(args, cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown(`Config ${subcmd} completed.`);
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to run config ${subcmd}.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "config" } };
+}
+
+async function handlePrompt(
+ stream: vscode.ChatResponseStream,
+ prompt: string,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ const parts = prompt.trim().split(/\s+/);
+ const subcmd = parts[0]?.toLowerCase();
+ const rest = parts.slice(1).join(" ");
+
+ let args: string[];
+ let progressMsg: string;
+
+ switch (subcmd) {
+ case "list":
+ case "ls":
+ args = ["prompt", "list"];
+ progressMsg = "Listing prompt templates...";
+ break;
+ case "add":
+ args = rest ? ["prompt", "add", rest] : ["prompt", "add"];
+ progressMsg = "Adding prompt template...";
+ break;
+ case "show":
+ args = rest ? ["prompt", "show", rest] : ["prompt", "show"];
+ progressMsg = "Showing prompt template...";
+ break;
+ case "rm":
+ args = rest ? ["prompt", "rm", rest] : ["prompt", "rm"];
+ progressMsg = "Removing prompt template...";
+ break;
+ default:
+ stream.markdown(
+ "**Usage:** `@ctx /prompt `\n\n" +
+ "| Subcommand | Description |\n" +
+ "|------------|-------------|\n" +
+ "| `list` | List prompt templates |\n" +
+ "| `add ` | Add a prompt template |\n" +
+ "| `show ` | Show a prompt template |\n" +
+ "| `rm ` | Remove a prompt template |\n\n" +
+ "Example: `@ctx /prompt list` or `@ctx /prompt show review`"
+ );
+ return { metadata: { command: "prompt" } };
+ }
+ args.push("--no-color");
+
+ stream.progress(progressMsg);
+ try {
+ const { stdout, stderr } = await runCtx(args, cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown(`Prompt ${subcmd} completed.`);
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to run prompt ${subcmd}.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "prompt" } };
+}
+
+async function handleWhy(
+ stream: vscode.ChatResponseStream,
+ prompt: string,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ const filename = prompt.trim();
+ const args = filename ? ["why", filename] : ["why"];
+ args.push("--no-color");
+
+ stream.progress("Looking up design rationale...");
+ try {
+ const { stdout, stderr } = await runCtx(args, cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown("No rationale found.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to look up rationale.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "why" } };
+}
+
+async function handleChange(
+ stream: vscode.ChatResponseStream,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ stream.progress("Checking recent codebase changes...");
+ try {
+ const { stdout, stderr } = await runCtx(["change", "--no-color"], cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown("No recent changes found.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to check changes.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "change" } };
+}
+
+async function handleDep(
+ stream: vscode.ChatResponseStream,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ stream.progress("Checking project dependencies...");
+ try {
+ const { stdout, stderr } = await runCtx(["dep", "--no-color"], cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown("No dependencies found.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to check dependencies.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "dep" } };
+}
+
+async function handleGuide(
+ stream: vscode.ChatResponseStream,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ stream.progress("Loading quick start guide...");
+ try {
+ const { stdout, stderr } = await runCtx(["guide", "--no-color"], cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown(output);
+ } else {
+ stream.markdown("No guide content available.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to load guide.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "guide" } };
+}
+
+async function handlePermission(
+ stream: vscode.ChatResponseStream,
+ prompt: string,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ const parts = prompt.trim().split(/\s+/);
+ const subcmd = parts[0]?.toLowerCase();
+
+ let args: string[];
+ let progressMsg: string;
+
+ switch (subcmd) {
+ case "snapshot":
+ args = ["permission", "snapshot"];
+ progressMsg = "Taking permission snapshot...";
+ break;
+ case "restore":
+ args = ["permission", "restore"];
+ progressMsg = "Restoring permissions...";
+ break;
+ default:
+ stream.markdown(
+ "**Usage:** `@ctx /permission `\n\n" +
+ "| Subcommand | Description |\n" +
+ "|------------|-------------|\n" +
+ "| `snapshot` | Capture current file permissions |\n" +
+ "| `restore` | Restore saved permissions |\n\n" +
+ "Example: `@ctx /permission snapshot`"
+ );
+ return { metadata: { command: "permission" } };
+ }
+ args.push("--no-color");
+
+ stream.progress(progressMsg);
+ try {
+ const { stdout, stderr } = await runCtx(args, cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown(`Permission ${subcmd} completed.`);
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to ${subcmd} permissions.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "permission" } };
+}
+
+async function handleSite(
+ stream: vscode.ChatResponseStream,
+ prompt: string,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ const parts = prompt.trim().split(/\s+/);
+ const subcmd = parts[0]?.toLowerCase();
+
+ let args: string[];
+ let progressMsg: string;
+
+ switch (subcmd) {
+ case "feed":
+ args = ["site", "feed"];
+ progressMsg = "Generating site feed...";
+ break;
+ default:
+ stream.markdown(
+ "**Usage:** `@ctx /site `\n\n" +
+ "| Subcommand | Description |\n" +
+ "|------------|-------------|\n" +
+ "| `feed` | Generate documentation site feed |\n\n" +
+ "Example: `@ctx /site feed`"
+ );
+ return { metadata: { command: "site" } };
+ }
+ args.push("--no-color");
+
+ stream.progress(progressMsg);
+ try {
+ const { stdout, stderr } = await runCtx(args, cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown(`Site ${subcmd} completed.`);
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to run site ${subcmd}.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "site" } };
+}
+
+async function handleLoop(
+ stream: vscode.ChatResponseStream,
+ prompt: string,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ const toolName = prompt.trim();
+ const args = toolName ? ["loop", toolName] : ["loop"];
+ args.push("--no-color");
+
+ stream.progress("Generating iteration script...");
+ try {
+ const { stdout, stderr } = await runCtx(args, cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown("No loop script generated.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to generate loop script.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "loop" } };
+}
+
+async function handlePause(
+ stream: vscode.ChatResponseStream,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ stream.progress("Pausing context hooks...");
+ try {
+ const { stdout, stderr } = await runCtx(["pause", "--no-color"], cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown("Context hooks paused for this session.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to pause hooks.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "pause" } };
+}
+
+async function handleResume(
+ stream: vscode.ChatResponseStream,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ stream.progress("Resuming context hooks...");
+ try {
+ const { stdout, stderr } = await runCtx(["resume", "--no-color"], cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown("Context hooks resumed.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to resume hooks.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "resume" } };
+}
+
+async function handleReindex(
+ stream: vscode.ChatResponseStream,
+ cwd: string,
+ token: vscode.CancellationToken
+): Promise {
+ stream.progress("Rebuilding context file indices...");
+ try {
+ const { stdout, stderr } = await runCtx(["reindex", "--no-color"], cwd, token);
+ const output = (stdout + stderr).trim();
+ if (output) {
+ stream.markdown("```\n" + output + "\n```");
+ } else {
+ stream.markdown("Context indices rebuilt.");
+ }
+ } catch (err: unknown) {
+ stream.markdown(
+ `**Error:** Failed to reindex.\n\n\`\`\`\n${err instanceof Error ? err.message : String(err)}\n\`\`\``
+ );
+ }
+ return { metadata: { command: "reindex" } };
+}
+
async function handleFreeform(
request: vscode.ChatRequest,
stream: vscode.ChatResponseStream,
@@ -949,13 +1481,13 @@ async function handleFreeform(
return handleRecall(stream, request.prompt, cwd, token);
}
if (prompt.includes("complete") || prompt.includes("done") || prompt.includes("finish")) {
- return handleComplete(stream, request.prompt, cwd, token);
+ return handleTask(stream, "complete " + request.prompt, cwd, token);
}
if (prompt.includes("remind")) {
return handleRemind(stream, request.prompt, cwd, token);
}
if (prompt.includes("task")) {
- return handleTasks(stream, request.prompt, cwd, token);
+ return handleTask(stream, request.prompt, cwd, token);
}
if (prompt.includes("pad") || prompt.includes("scratchpad") || prompt.includes("scratch")) {
return handlePad(stream, request.prompt, cwd, token);
@@ -966,10 +1498,55 @@ async function handleFreeform(
if (prompt.includes("system") || prompt.includes("resource") || prompt.includes("bootstrap")) {
return handleSystem(stream, request.prompt, cwd, token);
}
+ if (prompt.includes("memory")) {
+ return handleMemory(stream, request.prompt, cwd, token);
+ }
+ if (prompt.includes("journal")) {
+ return handleJournal(stream, request.prompt, cwd, token);
+ }
+ if (prompt.includes("doctor") || prompt.includes("health")) {
+ return handleDoctor(stream, cwd, token);
+ }
+ if (prompt.includes("config") || prompt.includes("configuration")) {
+ return handleConfig(stream, request.prompt, cwd, token);
+ }
+ if (prompt.includes("prompt") || prompt.includes("template")) {
+ return handlePrompt(stream, request.prompt, cwd, token);
+ }
+ if (prompt.includes("why") || prompt.includes("rationale")) {
+ return handleWhy(stream, request.prompt, cwd, token);
+ }
+ if (prompt.includes("change") || prompt.includes("recent")) {
+ return handleChange(stream, cwd, token);
+ }
+ if (prompt.includes("dep") || prompt.includes("dependenc")) {
+ return handleDep(stream, cwd, token);
+ }
+ if (prompt.includes("guide") || prompt.includes("quickstart") || prompt.includes("getting started")) {
+ return handleGuide(stream, cwd, token);
+ }
+ if (prompt.includes("permission")) {
+ return handlePermission(stream, request.prompt, cwd, token);
+ }
+ if (prompt.includes("site") || prompt.includes("feed")) {
+ return handleSite(stream, request.prompt, cwd, token);
+ }
+ if (prompt.includes("loop") || prompt.includes("iterate")) {
+ return handleLoop(stream, request.prompt, cwd, token);
+ }
+ if (prompt.includes("pause")) {
+ return handlePause(stream, cwd, token);
+ }
+ if (prompt.includes("resume")) {
+ return handleResume(stream, cwd, token);
+ }
+ if (prompt.includes("reindex") || prompt.includes("rebuild")) {
+ return handleReindex(stream, cwd, token);
+ }
// Default: show help with available commands
stream.markdown(
- "## ctx — Persistent Context for AI\n\n" +
+ "## ctx -- Persistent Context for AI\n\n" +
"Available commands:\n\n" +
"| Command | Description |\n" +
"|---------|-------------|\n" +
@@ -983,12 +1560,26 @@ async function handleFreeform(
"| `/load` | Output assembled context |\n" +
"| `/compact` | Archive completed tasks |\n" +
"| `/sync` | Reconcile context with codebase |\n" +
- "| `/complete` | Mark a task as completed |\n" +
+ "| `/task` | Task operations (complete, archive, snapshot) |\n" +
"| `/remind` | Manage session reminders |\n" +
- "| `/tasks` | Archive or snapshot tasks |\n" +
"| `/pad` | Encrypted scratchpad |\n" +
"| `/notify` | Webhook notifications |\n" +
- "| `/system` | System diagnostics |\n\n" +
+ "| `/system` | System diagnostics |\n" +
+ "| `/memory` | Memory bridge operations |\n" +
+ "| `/journal` | Journal management |\n" +
+ "| `/doctor` | Context health diagnostics |\n" +
+ "| `/config` | Runtime configuration |\n" +
+ "| `/prompt` | Prompt templates |\n" +
+ "| `/why` | Design rationale for context files |\n" +
+ "| `/change` | Recent codebase changes |\n" +
+ "| `/dep` | Project dependencies |\n" +
+ "| `/guide` | Quick start guide |\n" +
+ "| `/permission` | Permission snapshot/restore |\n" +
+ "| `/site` | Documentation site |\n" +
+ "| `/loop` | Generate iteration scripts |\n" +
+ "| `/pause` | Pause context hooks |\n" +
+ "| `/resume` | Resume context hooks |\n" +
+ "| `/reindex` | Rebuild context indices |\n\n" +
"Example: `@ctx /status` or `@ctx /add task Fix login bug`"
);
return { metadata: { command: "help" } };
@@ -1043,18 +1634,46 @@ const handler: vscode.ChatRequestHandler = async (
return handleCompact(stream, cwd, token);
case "sync":
return handleSync(stream, cwd, token);
- case "complete":
- return handleComplete(stream, request.prompt, cwd, token);
+ case "task":
+ return handleTask(stream, request.prompt, cwd, token);
case "remind":
return handleRemind(stream, request.prompt, cwd, token);
- case "tasks":
- return handleTasks(stream, request.prompt, cwd, token);
case "pad":
return handlePad(stream, request.prompt, cwd, token);
case "notify":
return handleNotify(stream, request.prompt, cwd, token);
case "system":
return handleSystem(stream, request.prompt, cwd, token);
+ case "memory":
+ return handleMemory(stream, request.prompt, cwd, token);
+ case "journal":
+ return handleJournal(stream, request.prompt, cwd, token);
+ case "doctor":
+ return handleDoctor(stream, cwd, token);
+ case "config":
+ return handleConfig(stream, request.prompt, cwd, token);
+ case "prompt":
+ return handlePrompt(stream, request.prompt, cwd, token);
+ case "why":
+ return handleWhy(stream, request.prompt, cwd, token);
+ case "change":
+ return handleChange(stream, cwd, token);
+ case "dep":
+ return handleDep(stream, cwd, token);
+ case "guide":
+ return handleGuide(stream, cwd, token);
+ case "permission":
+ return handlePermission(stream, request.prompt, cwd, token);
+ case "site":
+ return handleSite(stream, request.prompt, cwd, token);
+ case "loop":
+ return handleLoop(stream, request.prompt, cwd, token);
+ case "pause":
+ return handlePause(stream, cwd, token);
+ case "resume":
+ return handleResume(stream, cwd, token);
+ case "reindex":
+ return handleReindex(stream, cwd, token);
default:
return handleFreeform(request, stream, cwd, token);
}
@@ -1099,7 +1718,8 @@ export function activate(extensionContext: vscode.ExtensionContext) {
case "status":
followups.push(
{ prompt: "Detect context drift", command: "drift" },
- { prompt: "Load full context", command: "load" }
+ { prompt: "Load full context", command: "load" },
+ { prompt: "Run health check", command: "doctor" }
);
break;
case "drift":
@@ -1108,10 +1728,10 @@ export function activate(extensionContext: vscode.ExtensionContext) {
{ prompt: "Show context status", command: "status" }
);
break;
- case "complete":
+ case "task":
followups.push(
{ prompt: "Show context status", command: "status" },
- { prompt: "Archive completed tasks", command: "tasks" }
+ { prompt: "Compact context", command: "compact" }
);
break;
case "remind":
@@ -1119,21 +1739,48 @@ export function activate(extensionContext: vscode.ExtensionContext) {
{ prompt: "Show context status", command: "status" }
);
break;
- case "tasks":
+ case "pad":
+ followups.push(
+ { prompt: "List scratchpad", command: "pad" }
+ );
+ break;
+ case "memory":
+ followups.push(
+ { prompt: "Check memory status", command: "memory" },
+ { prompt: "Show context status", command: "status" }
+ );
+ break;
+ case "doctor":
followups.push(
{ prompt: "Show context status", command: "status" },
- { prompt: "Compact context", command: "compact" }
+ { prompt: "Detect drift", command: "drift" }
);
break;
- case "pad":
+ case "config":
followups.push(
- { prompt: "List scratchpad", command: "pad" }
+ { prompt: "Show config status", command: "config" }
+ );
+ break;
+ case "change":
+ followups.push(
+ { prompt: "Show context status", command: "status" }
+ );
+ break;
+ case "pause":
+ followups.push(
+ { prompt: "Resume hooks", command: "resume" }
+ );
+ break;
+ case "resume":
+ followups.push(
+ { prompt: "Show context status", command: "status" }
);
break;
case "help":
followups.push(
{ prompt: "Initialize project context", command: "init" },
- { prompt: "Show context status", command: "status" }
+ { prompt: "Show context status", command: "status" },
+ { prompt: "Quick start guide", command: "guide" }
);
break;
}
@@ -1152,12 +1799,26 @@ export {
ensureCtxAvailable,
bootstrap,
getPlatformInfo,
- handleComplete,
+ handleTask,
handleRemind,
- handleTasks,
handlePad,
handleNotify,
handleSystem,
+ handleMemory,
+ handleJournal,
+ handleDoctor,
+ handleConfig,
+ handlePrompt,
+ handleWhy,
+ handleChange,
+ handleDep,
+ handleGuide,
+ handlePermission,
+ handleSite,
+ handleLoop,
+ handlePause,
+ handleResume,
+ handleReindex,
};
export function deactivate() {}
diff --git a/hack/lint-drift.sh b/hack/lint-drift.sh
index 08f83326a..8bb2be8de 100755
--- a/hack/lint-drift.sh
+++ b/hack/lint-drift.sh
@@ -13,6 +13,7 @@
# 3. Magic directory strings that have config.Dir* constants
# 4. Literal ".md" (should use config.ExtMarkdown)
# 5. Go DescKey ↔ YAML key linkage (embed/{cmd,flag,text} vs commands/)
+# Only DescKey* constants are checked — Use* and ExampleKey* are excluded.
#
# Exit code: number of issues found (0 = clean).
@@ -47,7 +48,7 @@ drift_count() {
# ── 1. Literal "\n" ─────────────────────────────────────────────────
# Match "\n" as a Go string (not inside comments or imports).
# Skip config/token.go where the constant is defined.
-hits=$(drift_grep '"\\n"' 'token.go')
+hits=$(drift_grep '"\\n"' 'whitespace.go')
count=$(drift_count "$hits")
if [ "$count" -gt 0 ]; then
echo "==> Literal \"\\n\" found ($count occurrences, use config.NewlineLF):"
@@ -82,7 +83,7 @@ done
# ── 4. Literal ".md" ────────────────────────────────────────────────
# Skip config/file.go where ExtMarkdown is defined.
-hits=$(drift_grep '"\.md"' 'file.go')
+hits=$(drift_grep '"\.md"' 'ext.go')
count=$(drift_count "$hits")
if [ "$count" -gt 0 ]; then
echo "==> Literal \".md\" found ($count occurrences, use config.ExtMarkdown):"
@@ -130,11 +131,11 @@ check_linkage() {
}
# cmd constants ↔ commands.yaml
-# ExampleKey* constants map to examples.yaml, not commands.yaml — exclude them.
+# Only DescKey* constants are YAML lookup keys.
+# Use* constants are cobra Use fields; ExampleKey* map to examples.yaml.
cmd_go_dir="internal/config/embed/cmd"
-cmd_go_keys=$(grep -rh '= "' "$cmd_go_dir"/*.go 2>/dev/null \
+cmd_go_keys=$(grep -rh 'DescKey.*= "' "$cmd_go_dir"/*.go 2>/dev/null \
| grep -v '//' \
- | grep -v 'ExampleKey' \
| sed 's/.*= "//; s/"//' \
| sort -u)
cmd_yaml_keys=$(grep -E '^[a-z]' "internal/assets/commands/commands.yaml" \
@@ -170,20 +171,10 @@ check_linkage "text↔text/*.yaml" \
"$text_yaml_merged"
rm -f "$text_yaml_merged"
-# Cross-namespace duplicates (same key value in cmd + flag + text)
-dupes=$(
- grep -rh '= "' internal/config/embed/cmd/*.go internal/config/embed/flag/*.go internal/config/embed/text/*.go 2>/dev/null \
- | grep -v '//' \
- | sed 's/.*= "//; s/"//' \
- | sort | uniq -d
-)
-if [ -n "$dupes" ]; then
- dupe_count=$(echo "$dupes" | wc -l | tr -d ' ')
- echo "==> Cross-namespace duplicates ($dupe_count key(s) appear in multiple embed packages):"
- echo "$dupes" | sed 's/^/ /'
- echo ""
- issues=$((issues + dupe_count))
-fi
+# Cross-namespace duplicates: cmd, flag, and text are separate YAML files
+# with separate Go packages — the same key in each is by design (e.g. "agent"
+# appears as a command description, a flag description, and a text string).
+# No check needed.
# ── Summary ──────────────────────────────────────────────────────────
if [ "$issues" -eq 0 ]; then
diff --git a/hack/release.sh b/hack/release.sh
index 1771c5e51..d9885be21 100755
--- a/hack/release.sh
+++ b/hack/release.sh
@@ -100,15 +100,31 @@ rm -f "${PLUGIN_JSON}.bak"
sed -i.bak -E "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+\"/\"version\": \"${VERSION_NUM}\"/" "${MARKETPLACE_JSON}"
rm -f "${MARKETPLACE_JSON}.bak"
+# Update VS Code extension version
+VSCODE_PKG="editors/vscode/package.json"
+VSCODE_LOCK="editors/vscode/package-lock.json"
+echo "Updating VS Code extension version in ${VSCODE_PKG} and ${VSCODE_LOCK}..."
+sed -i.bak -E "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+\"/\"version\": \"${VERSION_NUM}\"/" "${VSCODE_PKG}"
+rm -f "${VSCODE_PKG}.bak"
+sed -i.bak -E "s/\"version\": \"[0-9]+\.[0-9]+\.[0-9]+\"/\"version\": \"${VERSION_NUM}\"/" "${VSCODE_LOCK}"
+rm -f "${VSCODE_LOCK}.bak"
+
# Update version references in documentation
-echo "Updating version references in docs/index.md..."
-sed -i.bak -E "s|/v[0-9]+\.[0-9]+\.[0-9]+/ctx-[0-9]+\.[0-9]+\.[0-9]+|/v${VERSION_NUM}/ctx-${VERSION_NUM}|g" docs/index.md
-sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-linux-amd64|ctx-${VERSION_NUM}-linux-amd64|g" docs/index.md
-sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-linux-arm64|ctx-${VERSION_NUM}-linux-arm64|g" docs/index.md
-sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-darwin-amd64|ctx-${VERSION_NUM}-darwin-amd64|g" docs/index.md
-sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-darwin-arm64|ctx-${VERSION_NUM}-darwin-arm64|g" docs/index.md
-sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-windows-amd64|ctx-${VERSION_NUM}-windows-amd64|g" docs/index.md
-rm -f docs/index.md.bak
+echo "Updating version references in docs/index.md and docs/home/getting-started.md..."
+for doc in docs/index.md docs/home/getting-started.md; do
+ sed -i.bak -E "s|/v[0-9]+\.[0-9]+\.[0-9]+/ctx-[0-9]+\.[0-9]+\.[0-9]+|/v${VERSION_NUM}/ctx-${VERSION_NUM}|g" "${doc}"
+ sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-linux-amd64|ctx-${VERSION_NUM}-linux-amd64|g" "${doc}"
+ sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-linux-arm64|ctx-${VERSION_NUM}-linux-arm64|g" "${doc}"
+ sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-darwin-amd64|ctx-${VERSION_NUM}-darwin-amd64|g" "${doc}"
+ sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-darwin-arm64|ctx-${VERSION_NUM}-darwin-arm64|g" "${doc}"
+ sed -i.bak -E "s|ctx-[0-9]+\.[0-9]+\.[0-9]+-windows-amd64|ctx-${VERSION_NUM}-windows-amd64|g" "${doc}"
+ rm -f "${doc}.bak"
+done
+
+# Update VSIX version in integrations doc
+echo "Updating VSIX version in docs/operations/integrations.md..."
+sed -i.bak -E "s|ctx-context-[0-9]+\.[0-9]+\.[0-9]+\.vsix|ctx-context-${VERSION_NUM}.vsix|g" docs/operations/integrations.md
+rm -f docs/operations/integrations.md.bak
# Update versions.md with new release
echo "Updating docs/versions.md..."
@@ -129,7 +145,7 @@ make site
# Commit docs and site updates
echo "Committing documentation updates..."
-git add docs/index.md docs/versions.md site/ "${PLUGIN_JSON}"
+git add docs/index.md docs/home/getting-started.md docs/operations/integrations.md docs/versions.md site/ "${PLUGIN_JSON}" "${MARKETPLACE_JSON}" "${VSCODE_PKG}" "${VSCODE_LOCK}"
git diff --cached --quiet || git commit -m "docs: update download links and versions page for ${VERSION}"
echo ""
diff --git a/internal/assets/hooks/messages/doc.go b/internal/assets/hooks/messages/doc.go
new file mode 100644
index 000000000..02c753ed4
--- /dev/null
+++ b/internal/assets/hooks/messages/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package messages provides metadata for hook message templates.
+package messages
diff --git a/internal/assets/hooks/messages/registry.go b/internal/assets/hooks/messages/registry.go
index 7bd757fb6..fb8f17715 100644
--- a/internal/assets/hooks/messages/registry.go
+++ b/internal/assets/hooks/messages/registry.go
@@ -4,19 +4,14 @@
// \ Copyright 2026-present Context contributors.
// SPDX-License-Identifier: Apache-2.0
-// Package messages provides metadata for hook message templates.
-//
-// The registry is parsed from an embedded registry.yaml that lives
-// alongside the .txt template files. It is the metadata layer over
-// the embedded FS and changes only when hooks are added or removed.
package messages
import (
"sync"
"github.com/ActiveMemory/ctx/internal/assets/read/hook"
- fserr "github.com/ActiveMemory/ctx/internal/err/fs"
- errparser "github.com/ActiveMemory/ctx/internal/err/parser"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
+ errParser "github.com/ActiveMemory/ctx/internal/err/parser"
"gopkg.in/yaml.v3"
)
@@ -59,12 +54,12 @@ func Registry() []HookMessageInfo {
registryOnce.Do(func() {
raw, readErr := hook.MessageRegistry()
if readErr != nil {
- registryErr = fserr.FileRead("registry.yaml", readErr)
+ registryErr = errFs.FileRead("registry.yaml", readErr)
return
}
var entries []HookMessageInfo
if unmarshalErr := yaml.Unmarshal(raw, &entries); unmarshalErr != nil {
- registryErr = errparser.ParseFile("registry.yaml", unmarshalErr)
+ registryErr = errParser.ParseFile("registry.yaml", unmarshalErr)
return
}
registryData = entries
diff --git a/internal/assets/why/about.md b/internal/assets/why/about.md
index 059bc7f63..44b21e31d 100644
--- a/internal/assets/why/about.md
+++ b/internal/assets/why/about.md
@@ -135,7 +135,7 @@ makes the *next* session **smarter**.
**Connect with `ctx`**
* [Join the Community →](community.md): ask questions, share workflows, and help shape what comes next
-* [Read the Blog →](../../blog/): real-world patterns, ponderings, and lessons learned from building `ctx` using `ctx`
+* [Read the Blog →](../blog/): real-world patterns, ponderings, and lessons learned from building `ctx` using `ctx`
----
diff --git a/internal/bootstrap/cmd.go b/internal/bootstrap/cmd.go
index 7dab83c9b..870d82d06 100644
--- a/internal/bootstrap/cmd.go
+++ b/internal/bootstrap/cmd.go
@@ -20,7 +20,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/flag"
ctxContext "github.com/ActiveMemory/ctx/internal/context/validate"
"github.com/ActiveMemory/ctx/internal/err/fs"
- errInitialize "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/validation"
)
@@ -89,7 +89,7 @@ func RootCmd() *cobra.Command {
// Require initialization.
if !ctxContext.Initialized(rc.ContextDir()) {
- return errInitialize.NotInitialized()
+ return errInit.NotInitialized()
}
return nil
diff --git a/internal/claude/claude_test.go b/internal/claude/claude_test.go
index 2a5d55349..6100557cd 100644
--- a/internal/claude/claude_test.go
+++ b/internal/claude/claude_test.go
@@ -13,7 +13,7 @@ import (
"testing"
"github.com/ActiveMemory/ctx/internal/assets"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/skill"
+ errSkill "github.com/ActiveMemory/ctx/internal/err/skill"
)
func TestSkills(t *testing.T) {
@@ -98,29 +98,29 @@ func TestSettingsStructure(t *testing.T) {
func TestErrSkillList(t *testing.T) {
cause := errors.New("read dir failed")
- err := ctxerr.List(cause)
+ err := errSkill.List(cause)
if err == nil {
- t.Fatal("ctxerr.List() returned nil")
+ t.Fatal("errSkill.List() returned nil")
}
if !strings.Contains(err.Error(), "failed to list skills") {
- t.Errorf("ctxerr.List() error missing prefix: %v", err)
+ t.Errorf("errSkill.List() error missing prefix: %v", err)
}
if !errors.Is(err, cause) {
- t.Error("ctxerr.List() does not wrap the cause error")
+ t.Error("errSkill.List() does not wrap the cause error")
}
}
func TestErrSkillRead(t *testing.T) {
cause := errors.New("not found")
- err := ctxerr.Read("my-skill", cause)
+ err := errSkill.Read("my-skill", cause)
if err == nil {
- t.Fatal("ctxerr.Read() returned nil")
+ t.Fatal("errSkill.Read() returned nil")
}
if !strings.Contains(err.Error(), "my-skill") {
- t.Errorf("ctxerr.Read() error missing skill name: %v", err)
+ t.Errorf("errSkill.Read() error missing skill name: %v", err)
}
if !errors.Is(err, cause) {
- t.Error("ctxerr.Read() does not wrap the cause error")
+ t.Error("errSkill.Read() does not wrap the cause error")
}
}
diff --git a/internal/cli/add/cmd/coverage_test.go b/internal/cli/add/cmd/coverage_test.go
index dd25a7978..76cdc8148 100644
--- a/internal/cli/add/cmd/coverage_test.go
+++ b/internal/cli/add/cmd/coverage_test.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/cli/add/cmd/root"
- entry2 "github.com/ActiveMemory/ctx/internal/cli/add/core/entry"
+ coreEntry "github.com/ActiveMemory/ctx/internal/cli/add/core/entry"
"github.com/ActiveMemory/ctx/internal/cli/add/core/example"
"github.com/ActiveMemory/ctx/internal/cli/add/core/extract"
"github.com/ActiveMemory/ctx/internal/cli/add/core/format"
@@ -27,7 +27,7 @@ import (
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/cli/initialize"
- entrytype "github.com/ActiveMemory/ctx/internal/config/entry"
+ entryType "github.com/ActiveMemory/ctx/internal/config/entry"
"github.com/ActiveMemory/ctx/internal/entity"
"github.com/ActiveMemory/ctx/internal/entry"
)
@@ -44,7 +44,7 @@ func TestErrNoContent(t *testing.T) {
}
func TestErrNoContentProvided(t *testing.T) {
- for _, fType := range []string{entrytype.Decision, entrytype.Task, entrytype.Learning, entrytype.Convention, entrytype.Unknown} {
+ for _, fType := range []string{entryType.Decision, entryType.Task, entryType.Learning, entryType.Convention, entryType.Unknown} {
t.Run(fType, func(t *testing.T) {
err := add.ErrNoContentProvided(fType, example.ForType(fType))
if err == nil {
@@ -152,11 +152,11 @@ func TestExamplesForType(t *testing.T) {
fType string
contains string
}{
- {entrytype.Decision, "ctx add decision"},
- {entrytype.Task, "ctx add task"},
- {entrytype.Learning, "ctx add learning"},
- {entrytype.Convention, "ctx add convention"},
- {entrytype.Unknown, "ctx add "},
+ {entryType.Decision, "ctx add decision"},
+ {entryType.Task, "ctx add task"},
+ {entryType.Learning, "ctx add learning"},
+ {entryType.Convention, "ctx add convention"},
+ {entryType.Unknown, "ctx add "},
}
for _, tt := range tests {
t.Run(tt.fType, func(t *testing.T) {
@@ -742,23 +742,23 @@ func TestRun_TaskWithSection(t *testing.T) {
func TestPredicates(t *testing.T) {
// Test plural forms
- if !entry2.FileTypeIsTask("tasks") {
+ if !coreEntry.FileTypeIsTask("tasks") {
t.Error("FileTypeIsTask should accept 'tasks'")
}
- if !entry2.FileTypeIsDecision("decisions") {
+ if !coreEntry.FileTypeIsDecision("decisions") {
t.Error("FileTypeIsDecision should accept 'decisions'")
}
- if !entry2.FileTypeIsLearning("learnings") {
+ if !coreEntry.FileTypeIsLearning("learnings") {
t.Error("FileTypeIsLearning should accept 'learnings'")
}
// Test negative cases
- if entry2.FileTypeIsTask("decision") {
+ if coreEntry.FileTypeIsTask("decision") {
t.Error("FileTypeIsTask should reject 'decision'")
}
- if entry2.FileTypeIsDecision("task") {
+ if coreEntry.FileTypeIsDecision("task") {
t.Error("FileTypeIsDecision should reject 'task'")
}
- if entry2.FileTypeIsLearning("convention") {
+ if coreEntry.FileTypeIsLearning("convention") {
t.Error("FileTypeIsLearning should reject 'convention'")
}
}
diff --git a/internal/cli/agent/agent.go b/internal/cli/agent/agent.go
index ef4806f99..832baa3b2 100644
--- a/internal/cli/agent/agent.go
+++ b/internal/cli/agent/agent.go
@@ -9,10 +9,10 @@ package agent
import (
"github.com/spf13/cobra"
- agentroot "github.com/ActiveMemory/ctx/internal/cli/agent/cmd/root"
+ agentRoot "github.com/ActiveMemory/ctx/internal/cli/agent/cmd/root"
)
// Cmd returns the "ctx agent" command for generating AI-ready context packets.
func Cmd() *cobra.Command {
- return agentroot.Cmd()
+ return agentRoot.Cmd()
}
diff --git a/internal/cli/agent/cmd/root/run.go b/internal/cli/agent/cmd/root/run.go
index 345cce07d..00f7973e5 100644
--- a/internal/cli/agent/cmd/root/run.go
+++ b/internal/cli/agent/cmd/root/run.go
@@ -17,7 +17,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/fmt"
"github.com/ActiveMemory/ctx/internal/context/load"
errCtx "github.com/ActiveMemory/ctx/internal/err/context"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
)
// Run executes the agent command logic.
@@ -53,7 +53,7 @@ func Run(
if err != nil {
var notFoundError *errCtx.NotFoundError
if errors.As(err, ¬FoundError) {
- return ctxErr.NotInitialized()
+ return errInit.NotInitialized()
}
return err
}
diff --git a/internal/cli/agent/core/budget/assemble.go b/internal/cli/agent/core/budget/assemble.go
index 082246e15..24d189040 100644
--- a/internal/cli/agent/core/budget/assemble.go
+++ b/internal/cli/agent/core/budget/assemble.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/agent/core/score"
"github.com/ActiveMemory/ctx/internal/cli/agent/core/sort"
"github.com/ActiveMemory/ctx/internal/config/agent"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
ctxToken "github.com/ActiveMemory/ctx/internal/context/token"
"github.com/ActiveMemory/ctx/internal/entity"
@@ -85,8 +85,8 @@ func AssemblePacket(ctx *entity.Context, budget int) *assembledPacket {
keywords := score.ExtractTaskKeywords(pkt.Tasks)
// Tier 4+5: Decisions + Learnings (share remaining budget)
- decisionBlocks := ParseEntryBlocks(ctx, ctxCfg.Decision)
- learningBlocks := ParseEntryBlocks(ctx, ctxCfg.Learning)
+ decisionBlocks := ParseEntryBlocks(ctx, cfgCtx.Decision)
+ learningBlocks := ParseEntryBlocks(ctx, cfgCtx.Learning)
scoredDecisions := score.ScoreEntries(decisionBlocks, keywords, now)
scoredLearnings := score.ScoreEntries(learningBlocks, keywords, now)
diff --git a/internal/cli/agent/core/budget/parse.go b/internal/cli/agent/core/budget/parse.go
index a44e02d89..ded1bb501 100644
--- a/internal/cli/agent/core/budget/parse.go
+++ b/internal/cli/agent/core/budget/parse.go
@@ -9,7 +9,7 @@ package budget
import (
"github.com/ActiveMemory/ctx/internal/cli/agent/core/extract"
"github.com/ActiveMemory/ctx/internal/config/agent"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/entity"
"github.com/ActiveMemory/ctx/internal/index"
)
@@ -38,7 +38,7 @@ func ParseEntryBlocks(ctx *entity.Context, fileName string) []index.EntryBlock {
// Returns:
// - []string: All convention bullet items; nil if the file is not found
func ExtractAllConventions(ctx *entity.Context) []string {
- if f := ctx.File(ctxCfg.Convention); f != nil {
+ if f := ctx.File(cfgCtx.Convention); f != nil {
return extract.BulletItems(string(f.Content), agent.BulletItemLimit)
}
return nil
diff --git a/internal/cli/agent/core/extract/extract.go b/internal/cli/agent/core/extract/extract.go
index 9db92f83f..8070ced9e 100644
--- a/internal/cli/agent/core/extract/extract.go
+++ b/internal/cli/agent/core/extract/extract.go
@@ -9,7 +9,7 @@ package extract
import (
"strings"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/marker"
"github.com/ActiveMemory/ctx/internal/config/regex"
"github.com/ActiveMemory/ctx/internal/config/token"
@@ -69,7 +69,7 @@ func CheckboxItems(content string) []string {
// Returns:
// - []string: List of constitution rules; nil if the file is not found
func ConstitutionRules(ctx *entity.Context) []string {
- if f := ctx.File(ctxCfg.Constitution); f != nil {
+ if f := ctx.File(cfgCtx.Constitution); f != nil {
return CheckboxItems(string(f.Content))
}
return nil
@@ -105,7 +105,7 @@ func UncheckedTasks(content string) []string {
// - []string: List of active tasks with "- [ ]" prefix; nil if
// the file is not found
func ActiveTasks(ctx *entity.Context) []string {
- if f := ctx.File(ctxCfg.Task); f != nil {
+ if f := ctx.File(cfgCtx.Task); f != nil {
return UncheckedTasks(string(f.Content))
}
return nil
diff --git a/internal/cli/agent/core/sort/sort.go b/internal/cli/agent/core/sort/sort.go
index 68e78aa46..6fc0b5bb0 100644
--- a/internal/cli/agent/core/sort/sort.go
+++ b/internal/cli/agent/core/sort/sort.go
@@ -9,7 +9,7 @@ package sort
import (
"path/filepath"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/entity"
)
@@ -26,7 +26,7 @@ import (
// - []string: File paths in reading order (e.g., ".context/CONSTITUTION.md")
func ReadOrder(ctx *entity.Context) []string {
var order []string
- for _, name := range ctxCfg.ReadOrder {
+ for _, name := range cfgCtx.ReadOrder {
if f := ctx.File(name); f != nil && !f.IsEmpty {
order = append(order, filepath.Join(ctx.Dir, f.Name))
}
diff --git a/internal/cli/change/cmd/root/run.go b/internal/cli/change/cmd/root/run.go
index 303b9a14a..9df4e665e 100644
--- a/internal/cli/change/cmd/root/run.go
+++ b/internal/cli/change/cmd/root/run.go
@@ -12,7 +12,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/change/core/scan"
"github.com/spf13/cobra"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
writeChange "github.com/ActiveMemory/ctx/internal/write/change"
)
@@ -30,7 +30,7 @@ import (
func Run(cmd *cobra.Command, since string) error {
refTime, refLabel, err := detect.ReferenceTime(since)
if err != nil {
- return ctxErr.DetectReferenceTime(err)
+ return errInit.DetectReferenceTime(err)
}
ctxChanges, _ := scan.FindContextChanges(refTime)
diff --git a/internal/cli/change/core/cmd_test.go b/internal/cli/change/core/cmd_test.go
index 680f1743b..fe09f7310 100644
--- a/internal/cli/change/core/cmd_test.go
+++ b/internal/cli/change/core/cmd_test.go
@@ -15,7 +15,7 @@ import (
"time"
"github.com/ActiveMemory/ctx/internal/cli/change/core/detect"
- format2 "github.com/ActiveMemory/ctx/internal/cli/change/core/render"
+ coreRender "github.com/ActiveMemory/ctx/internal/cli/change/core/render"
"github.com/ActiveMemory/ctx/internal/cli/change/core/scan"
"github.com/ActiveMemory/ctx/internal/format"
@@ -134,7 +134,7 @@ func TestRenderChanges(t *testing.T) {
Authors: []string{"Volkan"},
}
- out := format2.Changes("6 hours ago", ctxChanges, code)
+ out := coreRender.Changes("6 hours ago", ctxChanges, code)
if !strings.Contains(out, "## Changes Since Last Session") {
t.Error("missing header")
}
@@ -155,7 +155,7 @@ func TestRenderChangesForHook(t *testing.T) {
}
code := entity.CodeSummary{CommitCount: 3, LatestMsg: "Fix bug"}
- out := format2.ChangesForHook("2 hours ago", ctxChanges, code)
+ out := coreRender.ChangesForHook("2 hours ago", ctxChanges, code)
if !strings.Contains(out, "Changes since last session") {
t.Error("missing hook header")
}
@@ -164,14 +164,14 @@ func TestRenderChangesForHook(t *testing.T) {
}
// Empty case.
- out = format2.ChangesForHook("1 hour ago", nil, entity.CodeSummary{})
+ out = coreRender.ChangesForHook("1 hour ago", nil, entity.CodeSummary{})
if out != "" {
t.Errorf("expected empty for no changes, got: %q", out)
}
}
func TestRenderChanges_NoChanges(t *testing.T) {
- out := format2.Changes("1 hour ago", nil, entity.CodeSummary{})
+ out := coreRender.Changes("1 hour ago", nil, entity.CodeSummary{})
if !strings.Contains(out, "No changes detected") {
t.Error("expected 'No changes detected' message")
}
diff --git a/internal/cli/change/core/detect/doc.go b/internal/cli/change/core/detect/doc.go
new file mode 100644
index 000000000..7157ad832
--- /dev/null
+++ b/internal/cli/change/core/detect/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package detect determines the reference time for change detection using markers, events, or flags.
+package detect
diff --git a/internal/cli/change/core/render/doc.go b/internal/cli/change/core/render/doc.go
new file mode 100644
index 000000000..4b18e9ce1
--- /dev/null
+++ b/internal/cli/change/core/render/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package render formats change summaries into human-readable Markdown output.
+package render
diff --git a/internal/cli/change/core/scan/doc.go b/internal/cli/change/core/scan/doc.go
new file mode 100644
index 000000000..a40734733
--- /dev/null
+++ b/internal/cli/change/core/scan/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package scan finds context and code files modified after a reference time.
+package scan
diff --git a/internal/cli/compact/cmd/root/run.go b/internal/cli/compact/cmd/root/run.go
index 06b26a7d8..da110b7b6 100644
--- a/internal/cli/compact/cmd/root/run.go
+++ b/internal/cli/compact/cmd/root/run.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/context/load"
errCtx "github.com/ActiveMemory/ctx/internal/err/context"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/tidy"
writeCompact "github.com/ActiveMemory/ctx/internal/write/compact"
@@ -38,7 +38,7 @@ func Run(cmd *cobra.Command, archive bool) error {
if err != nil {
var notFoundError *errCtx.NotFoundError
if errors.As(err, ¬FoundError) {
- return ctxErr.ContextNotInitialized()
+ return errInit.ContextNotInitialized()
}
return err
}
diff --git a/internal/cli/compact/compact_test.go b/internal/cli/compact/compact_test.go
index dc0746d90..bb3af9540 100644
--- a/internal/cli/compact/compact_test.go
+++ b/internal/cli/compact/compact_test.go
@@ -12,7 +12,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/add"
"github.com/ActiveMemory/ctx/internal/cli/initialize"
- taskcomplete "github.com/ActiveMemory/ctx/internal/cli/task/cmd/complete"
+ taskComplete "github.com/ActiveMemory/ctx/internal/cli/task/cmd/complete"
)
// TestCompactCommand tests the compact command.
@@ -72,7 +72,7 @@ func TestCompactWithTasks(t *testing.T) {
t.Fatalf("add task failed: %v", err)
}
- completeCmd := taskcomplete.Cmd()
+ completeCmd := taskComplete.Cmd()
completeCmd.SetArgs([]string{"Task to complete"})
if err := completeCmd.Execute(); err != nil {
t.Fatalf("complete task failed: %v", err)
diff --git a/internal/cli/compact/core/task.go b/internal/cli/compact/core/task.go
index 3274061c1..572be40f6 100644
--- a/internal/cli/compact/core/task.go
+++ b/internal/cli/compact/core/task.go
@@ -12,7 +12,7 @@ import (
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
- archiveCfg "github.com/ActiveMemory/ctx/internal/config/archive"
+ cfgArchive "github.com/ActiveMemory/ctx/internal/config/archive"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/config/token"
@@ -82,7 +82,7 @@ func CompactTasks(
archiveContent += block.BlockContent() + nl + nl
}
if archiveFile, archiveErr := tidy.WriteArchive(
- archiveCfg.ArchiveScopeTasks,
+ cfgArchive.ArchiveScopeTasks,
desc.Text(text.DescKeyHeadingArchivedTasks),
archiveContent,
); archiveErr == nil {
diff --git a/internal/cli/config/cmd/status/cmd.go b/internal/cli/config/cmd/status/cmd.go
index 63d1a59d6..4520db746 100644
--- a/internal/cli/config/cmd/status/cmd.go
+++ b/internal/cli/config/cmd/status/cmd.go
@@ -8,7 +8,7 @@ package status
import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
- internalConfig "github.com/ActiveMemory/ctx/internal/config/cli"
+ cfgCli "github.com/ActiveMemory/ctx/internal/config/cli"
"github.com/ActiveMemory/ctx/internal/config/embed/cmd"
"github.com/spf13/cobra"
@@ -25,7 +25,7 @@ func Cmd() *cobra.Command {
return &cobra.Command{
Use: cmd.UseConfigStatus,
Short: short,
- Annotations: map[string]string{internalConfig.AnnotationSkipInit: ""},
+ Annotations: map[string]string{cfgCli.AnnotationSkipInit: ""},
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
root, rootErr := core.GitRoot()
diff --git a/internal/cli/config/cmd/switchcmd/cmd.go b/internal/cli/config/cmd/switchcmd/cmd.go
index 5b4498c0b..a54527de7 100644
--- a/internal/cli/config/cmd/switchcmd/cmd.go
+++ b/internal/cli/config/cmd/switchcmd/cmd.go
@@ -11,7 +11,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/cli/config/core"
- internalConfig "github.com/ActiveMemory/ctx/internal/config/cli"
+ cfgCli "github.com/ActiveMemory/ctx/internal/config/cli"
"github.com/ActiveMemory/ctx/internal/config/embed/cmd"
)
@@ -25,7 +25,7 @@ func Cmd() *cobra.Command {
return &cobra.Command{
Use: cmd.UseConfigSwitch,
Short: short,
- Annotations: map[string]string{internalConfig.AnnotationSkipInit: ""},
+ Annotations: map[string]string{cfgCli.AnnotationSkipInit: ""},
Long: long,
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
diff --git a/internal/cli/config/cmd/switchcmd/run.go b/internal/cli/config/cmd/switchcmd/run.go
index dcf454932..6afdfbab8 100644
--- a/internal/cli/config/cmd/switchcmd/run.go
+++ b/internal/cli/config/cmd/switchcmd/run.go
@@ -11,7 +11,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/config/core"
"github.com/ActiveMemory/ctx/internal/config/file"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/config"
+ errConfig "github.com/ActiveMemory/ctx/internal/err/config"
writeConfig "github.com/ActiveMemory/ctx/internal/write/config"
)
@@ -50,7 +50,7 @@ func Run(cmd *cobra.Command, root string, args []string) error {
profile = file.ProfileDev
}
default:
- return ctxErr.UnknownProfile(target)
+ return errConfig.UnknownProfile(target)
}
msg, switchErr := core.SwitchTo(root, profile)
diff --git a/internal/cli/config/core/core.go b/internal/cli/config/core/core.go
index 7fda5308d..b45e9e07f 100644
--- a/internal/cli/config/core/core.go
+++ b/internal/cli/config/core/core.go
@@ -20,7 +20,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/fs"
cfgGit "github.com/ActiveMemory/ctx/internal/config/git"
"github.com/ActiveMemory/ctx/internal/err/config"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/git"
+ errGit "github.com/ActiveMemory/ctx/internal/err/git"
"github.com/ActiveMemory/ctx/internal/io"
"github.com/ActiveMemory/ctx/internal/rc"
)
@@ -95,14 +95,14 @@ func SwitchTo(root, profile string) (string, error) {
// degrade gracefully when this returns an error.
func GitRoot() (string, error) {
if _, lookErr := exec.LookPath(cfgGit.Binary); lookErr != nil {
- return "", ctxErr.NotFound()
+ return "", errGit.NotFound()
}
out, execErr := exec.Command( //nolint:gosec // args are literal constants
cfgGit.Binary, cfgGit.RevParse, cfgGit.FlagShowToplevel,
).Output()
if execErr != nil {
- return "", ctxErr.NotInRepo(execErr)
+ return "", errGit.NotInRepo(execErr)
}
return strings.TrimSpace(string(out)), nil
}
diff --git a/internal/cli/dep/cmd/root/run.go b/internal/cli/dep/cmd/root/run.go
index 07ccf5349..6c97aa95f 100644
--- a/internal/cli/dep/cmd/root/run.go
+++ b/internal/cli/dep/cmd/root/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/dep/core"
"github.com/ActiveMemory/ctx/internal/config/fmt"
"github.com/ActiveMemory/ctx/internal/config/token"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/config"
+ errConfig "github.com/ActiveMemory/ctx/internal/err/config"
"github.com/ActiveMemory/ctx/internal/write/deps"
)
@@ -37,14 +37,14 @@ func Run(cmd *cobra.Command, format string, external bool, projType string) erro
switch format {
case fmt.FormatMermaid, fmt.FormatTable, fmt.FormatJSON:
default:
- return ctxErr.UnknownFormat(format, supportedFormats)
+ return errConfig.UnknownFormat(format, supportedFormats)
}
var builder core.GraphBuilder
if projType != "" {
builder = core.FindBuilder(projType)
if builder == nil {
- return ctxErr.UnknownProjectType(projType, strings.Join(core.BuilderNames(), token.CommaSpace))
+ return errConfig.UnknownProjectType(projType, strings.Join(core.BuilderNames(), token.CommaSpace))
}
} else {
builder = core.DetectBuilder()
diff --git a/internal/cli/drift/cmd/root/run.go b/internal/cli/drift/cmd/root/run.go
index 200af357d..0209ab97b 100644
--- a/internal/cli/drift/cmd/root/run.go
+++ b/internal/cli/drift/cmd/root/run.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/context/load"
"github.com/ActiveMemory/ctx/internal/drift"
errCtx "github.com/ActiveMemory/ctx/internal/err/context"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
writeDrift "github.com/ActiveMemory/ctx/internal/write/drift"
)
@@ -37,7 +37,7 @@ func Run(cmd *cobra.Command, jsonOutput, fix bool) error {
if err != nil {
var notFoundError *errCtx.NotFoundError
if errors.As(err, ¬FoundError) {
- return ctxErr.NotInitialized()
+ return errInit.NotInitialized()
}
return err
}
diff --git a/internal/cli/drift/core/fix.go b/internal/cli/drift/core/fix.go
index 7bf1989b7..fe304e029 100644
--- a/internal/cli/drift/core/fix.go
+++ b/internal/cli/drift/core/fix.go
@@ -17,7 +17,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/assets/read/template"
"github.com/ActiveMemory/ctx/internal/config/archive"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/config/marker"
@@ -27,7 +27,7 @@ import (
"github.com/ActiveMemory/ctx/internal/entity"
errFs "github.com/ActiveMemory/ctx/internal/err/fs"
"github.com/ActiveMemory/ctx/internal/err/prompt"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/task"
+ errTask "github.com/ActiveMemory/ctx/internal/err/task"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/task"
"github.com/ActiveMemory/ctx/internal/tidy"
@@ -106,10 +106,10 @@ func ApplyFixes(
// Returns:
// - error: Non-nil if file operations fail
func FixStaleness(cmd *cobra.Command, ctx *entity.Context) error {
- tasksFile := ctx.File(ctxCfg.Task)
+ tasksFile := ctx.File(cfgCtx.Task)
if tasksFile == nil {
- return ctxErr.FileNotFound()
+ return errTask.FileNotFound()
}
nl := token.NewlineLF
@@ -145,7 +145,7 @@ func FixStaleness(cmd *cobra.Command, ctx *entity.Context) error {
}
if len(completedTasks) == 0 {
- return ctxErr.NoneCompleted()
+ return errTask.NoneCompleted()
}
// Build archive content
@@ -167,7 +167,7 @@ func FixStaleness(cmd *cobra.Command, ctx *entity.Context) error {
if writeErr := os.WriteFile(
tasksFile.Path, []byte(newContent), fs.PermFile,
); writeErr != nil {
- return ctxErr.FileWrite(writeErr)
+ return errTask.FileWrite(writeErr)
}
writeDrift.Archived(cmd, len(completedTasks), archiveFile)
diff --git a/internal/cli/drift/core/out.go b/internal/cli/drift/core/out.go
index 891a9b527..18d15bb14 100644
--- a/internal/cli/drift/core/out.go
+++ b/internal/cli/drift/core/out.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/drift"
- errdrift "github.com/ActiveMemory/ctx/internal/err/drift"
+ errDrift "github.com/ActiveMemory/ctx/internal/err/drift"
writeDrift "github.com/ActiveMemory/ctx/internal/write/drift"
)
@@ -119,7 +119,7 @@ func OutputDriftText(cmd *cobra.Command, report *drift.Report) error {
switch status {
case drift.StatusViolation:
writeDrift.StatusViolation(cmd)
- return errdrift.Violations()
+ return errDrift.Violations()
case drift.StatusWarning:
writeDrift.StatusWarning(cmd)
default:
diff --git a/internal/cli/hook/cmd/root/run.go b/internal/cli/hook/cmd/root/run.go
index 34ecc9084..7204f6dd9 100644
--- a/internal/cli/hook/cmd/root/run.go
+++ b/internal/cli/hook/cmd/root/run.go
@@ -18,11 +18,11 @@ import (
"github.com/ActiveMemory/ctx/internal/config/dir"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/fs"
- hookCfg "github.com/ActiveMemory/ctx/internal/config/hook"
+ cfgHook "github.com/ActiveMemory/ctx/internal/config/hook"
"github.com/ActiveMemory/ctx/internal/config/marker"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/err/config"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
writeErr "github.com/ActiveMemory/ctx/internal/write/err"
"github.com/ActiveMemory/ctx/internal/write/hook"
)
@@ -44,16 +44,16 @@ func Run(cmd *cobra.Command, args []string, writeFile bool) error {
tool := strings.ToLower(args[0])
switch tool {
- case hookCfg.ToolClaudeCode, hookCfg.ToolClaude:
+ case cfgHook.ToolClaudeCode, cfgHook.ToolClaude:
hook.InfoTool(cmd, desc.Text(text.DescKeyHookClaude))
- case hookCfg.ToolCursor:
+ case cfgHook.ToolCursor:
hook.InfoTool(cmd, desc.Text(text.DescKeyHookCursor))
- case hookCfg.ToolAider:
+ case cfgHook.ToolAider:
hook.InfoTool(cmd, desc.Text(text.DescKeyHookAider))
- case hookCfg.ToolCopilot:
+ case cfgHook.ToolCopilot:
if writeFile {
return WriteCopilotInstructions(cmd)
}
@@ -65,7 +65,7 @@ func Run(cmd *cobra.Command, args []string, writeFile bool) error {
}
hook.Content(cmd, string(content))
- case hookCfg.ToolWindsurf:
+ case cfgHook.ToolWindsurf:
hook.InfoTool(cmd, desc.Text(text.DescKeyHookWindsurf))
default:
@@ -89,11 +89,11 @@ func Run(cmd *cobra.Command, args []string, writeFile bool) error {
// Returns:
// - error: Non-nil if directory creation or file write fails
func WriteCopilotInstructions(cmd *cobra.Command) error {
- targetFile := filepath.Join(hookCfg.DirGitHub, hookCfg.FileCopilotInstructions)
+ targetFile := filepath.Join(cfgHook.DirGitHub, cfgHook.FileCopilotInstructions)
// Create .github/ directory if needed
- if err := os.MkdirAll(hookCfg.DirGitHub, fs.PermExec); err != nil {
- return ctxErr.Mkdir(hookCfg.DirGitHub, err)
+ if err := os.MkdirAll(cfgHook.DirGitHub, fs.PermExec); err != nil {
+ return errFs.Mkdir(cfgHook.DirGitHub, err)
}
// Load the copilot instructions from embedded assets
@@ -116,7 +116,7 @@ func WriteCopilotInstructions(cmd *cobra.Command) error {
// File exists without ctx markers: append ctx content
merged := existingStr + token.NewlineLF + string(instructions)
if wErr := os.WriteFile(targetFile, []byte(merged), fs.PermFile); wErr != nil {
- return ctxErr.FileWrite(targetFile, wErr)
+ return errFs.FileWrite(targetFile, wErr)
}
hook.InfoCopilotMerged(cmd, targetFile)
return nil
@@ -126,7 +126,7 @@ func WriteCopilotInstructions(cmd *cobra.Command) error {
if wErr := os.WriteFile(
targetFile, instructions, fs.PermFile,
); wErr != nil {
- return ctxErr.FileWrite(targetFile, wErr)
+ return errFs.FileWrite(targetFile, wErr)
}
hook.InfoCopilotCreated(cmd, targetFile)
diff --git a/internal/cli/hook/hook.go b/internal/cli/hook/hook.go
index fea9ee339..2a661ccf4 100644
--- a/internal/cli/hook/hook.go
+++ b/internal/cli/hook/hook.go
@@ -9,10 +9,10 @@ package hook
import (
"github.com/spf13/cobra"
- hookroot "github.com/ActiveMemory/ctx/internal/cli/hook/cmd/root"
+ hookRoot "github.com/ActiveMemory/ctx/internal/cli/hook/cmd/root"
)
// Cmd returns the "ctx hook" command for generating AI tool integrations.
func Cmd() *cobra.Command {
- return hookroot.Cmd()
+ return hookRoot.Cmd()
}
diff --git a/internal/cli/hook/hook_test.go b/internal/cli/hook/hook_test.go
index 0b1bc75a2..11afc4ab1 100644
--- a/internal/cli/hook/hook_test.go
+++ b/internal/cli/hook/hook_test.go
@@ -13,7 +13,7 @@ import (
"strings"
"testing"
- hookroot "github.com/ActiveMemory/ctx/internal/cli/hook/cmd/root"
+ hookRoot "github.com/ActiveMemory/ctx/internal/cli/hook/cmd/root"
"github.com/spf13/cobra"
)
@@ -70,7 +70,7 @@ func hookCmdOutput(cmd *cobra.Command) string {
func TestWriteCopilotInstructions_NewFile(t *testing.T) {
tmpDir := t.TempDir()
- // hookroot.WriteCopilotInstructions uses relative paths, so chdir.
+ // hookRoot.WriteCopilotInstructions uses relative paths, so chdir.
origDir, wdErr := os.Getwd()
if wdErr != nil {
t.Fatal(wdErr)
@@ -83,8 +83,8 @@ func TestWriteCopilotInstructions_NewFile(t *testing.T) {
})
cmd := newHookTestCmd()
- if runErr := hookroot.WriteCopilotInstructions(cmd); runErr != nil {
- t.Fatalf("hookroot.WriteCopilotInstructions failed: %v", runErr)
+ if runErr := hookRoot.WriteCopilotInstructions(cmd); runErr != nil {
+ t.Fatalf("hookRoot.WriteCopilotInstructions failed: %v", runErr)
}
targetFile := filepath.Join(tmpDir, ".github", "copilot-instructions.md")
@@ -129,8 +129,8 @@ func TestWriteCopilotInstructions_ExistingWithMarker(t *testing.T) {
}
cmd := newHookTestCmd()
- if runErr := hookroot.WriteCopilotInstructions(cmd); runErr != nil {
- t.Fatalf("hookroot.WriteCopilotInstructions failed: %v", runErr)
+ if runErr := hookRoot.WriteCopilotInstructions(cmd); runErr != nil {
+ t.Fatalf("hookRoot.WriteCopilotInstructions failed: %v", runErr)
}
// File should be unchanged (skipped).
@@ -175,8 +175,8 @@ func TestWriteCopilotInstructions_ExistingWithoutMarker(t *testing.T) {
}
cmd := newHookTestCmd()
- if runErr := hookroot.WriteCopilotInstructions(cmd); runErr != nil {
- t.Fatalf("hookroot.WriteCopilotInstructions failed: %v", runErr)
+ if runErr := hookRoot.WriteCopilotInstructions(cmd); runErr != nil {
+ t.Fatalf("hookRoot.WriteCopilotInstructions failed: %v", runErr)
}
data, readErr := os.ReadFile(targetFile)
diff --git a/internal/cli/initialize/cmd/root/run.go b/internal/cli/initialize/cmd/root/run.go
index 5907484ee..b32e811a5 100644
--- a/internal/cli/initialize/cmd/root/run.go
+++ b/internal/cli/initialize/cmd/root/run.go
@@ -12,12 +12,12 @@ import (
"path/filepath"
"strings"
- claude2 "github.com/ActiveMemory/ctx/internal/cli/initialize/core/claude"
+ coreClaude "github.com/ActiveMemory/ctx/internal/cli/initialize/core/claude"
"github.com/ActiveMemory/ctx/internal/cli/initialize/core/entry"
coreMerge "github.com/ActiveMemory/ctx/internal/cli/initialize/core/merge"
"github.com/ActiveMemory/ctx/internal/cli/initialize/core/plan"
"github.com/ActiveMemory/ctx/internal/cli/initialize/core/plugin"
- project2 "github.com/ActiveMemory/ctx/internal/cli/initialize/core/project"
+ coreProject "github.com/ActiveMemory/ctx/internal/cli/initialize/core/project"
"github.com/ActiveMemory/ctx/internal/cli/initialize/core/prompt"
"github.com/ActiveMemory/ctx/internal/cli/initialize/core/validate"
"github.com/spf13/cobra"
@@ -39,7 +39,7 @@ import (
"github.com/ActiveMemory/ctx/internal/crypto"
errCrypto "github.com/ActiveMemory/ctx/internal/err/crypto"
errFs "github.com/ActiveMemory/ctx/internal/err/fs"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/prompt"
+ errPrompt "github.com/ActiveMemory/ctx/internal/err/prompt"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/write/initialize"
)
@@ -102,7 +102,7 @@ func Run(
var listErr error
templatesToCreate, listErr = catalog.List()
if listErr != nil {
- return ctxErr.ListTemplates(listErr)
+ return errPrompt.ListTemplates(listErr)
}
}
@@ -118,7 +118,7 @@ func Run(
content, err := template.Template(name)
if err != nil {
- return ctxErr.ReadTemplate(name, err)
+ return errPrompt.ReadTemplate(name, err)
}
if err := os.WriteFile(targetPath, content, fs.PermFile); err != nil {
@@ -152,7 +152,7 @@ func Run(
initialize.InfoCreatingRootFiles(cmd)
// Create specs/ and ideas/ directories with README.md
- if err := project2.CreateProjectDirs(cmd); err != nil {
+ if err := coreProject.CreateProjectDirs(cmd); err != nil {
initialize.InfoWarnNonFatal(cmd, desc.Text(text.DescKeyInitLabelProjectDirs), err)
}
@@ -184,13 +184,13 @@ func Run(
}
// Handle CLAUDE.md creation/merge
- if err := claude2.HandleClaudeMd(cmd, force, merge); err != nil {
+ if err := coreClaude.HandleClaudeMd(cmd, force, merge); err != nil {
// Non-fatal: warn but continue
initialize.InfoWarnNonFatal(cmd, claude.Md, err)
}
// Deploy Makefile.ctx and amend user Makefile
- if err := project2.HandleMakefileCtx(cmd); err != nil {
+ if err := coreProject.HandleMakefileCtx(cmd); err != nil {
// Non-fatal: warn but continue
initialize.InfoWarnNonFatal(cmd, sync.PatternMakefile, err)
}
diff --git a/internal/cli/initialize/core/backup/file.go b/internal/cli/initialize/core/backup/file.go
index a9185f9ce..05a9d778c 100644
--- a/internal/cli/initialize/core/backup/file.go
+++ b/internal/cli/initialize/core/backup/file.go
@@ -13,7 +13,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/file"
"github.com/ActiveMemory/ctx/internal/config/fs"
- backupPkg "github.com/ActiveMemory/ctx/internal/err/backup"
+ errBackup "github.com/ActiveMemory/ctx/internal/err/backup"
"github.com/ActiveMemory/ctx/internal/write/initialize"
"github.com/spf13/cobra"
)
@@ -31,7 +31,7 @@ func File(cmd *cobra.Command, filename string, content []byte) error {
timestamp := time.Now().Unix()
backupName := fmt.Sprintf(file.BackupFormat, filename, timestamp)
if writeErr := os.WriteFile(backupName, content, fs.PermFile); writeErr != nil {
- return backupPkg.Create(backupName, writeErr)
+ return errBackup.Create(backupName, writeErr)
}
initialize.Backup(cmd, backupName)
return nil
diff --git a/internal/cli/initialize/core/claude/claude.go b/internal/cli/initialize/core/claude/claude.go
index 3ace980a8..f87bc42d7 100644
--- a/internal/cli/initialize/core/claude/claude.go
+++ b/internal/cli/initialize/core/claude/claude.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/marker"
"github.com/ActiveMemory/ctx/internal/entity"
- errInitialize "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
"github.com/ActiveMemory/ctx/internal/write/initialize"
)
@@ -32,7 +32,7 @@ import (
func HandleClaudeMd(cmd *cobra.Command, force, autoMerge bool) error {
templateContent, err := readClaude.Md()
if err != nil {
- return errInitialize.ReadTemplate(claude.Md, err)
+ return errInit.ReadTemplate(claude.Md, err)
}
created, mergeErr := merge.CreateOrMerge(cmd, entity.MergeParams{
diff --git a/internal/cli/initialize/core/merge/create.go b/internal/cli/initialize/core/merge/create.go
index 2e3aef0a4..26438450a 100644
--- a/internal/cli/initialize/core/merge/create.go
+++ b/internal/cli/initialize/core/merge/create.go
@@ -20,7 +20,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/entity"
errFs "github.com/ActiveMemory/ctx/internal/err/fs"
- promptErr "github.com/ActiveMemory/ctx/internal/err/prompt"
+ errPrompt "github.com/ActiveMemory/ctx/internal/err/prompt"
"github.com/ActiveMemory/ctx/internal/write/initialize"
)
@@ -126,7 +126,7 @@ func UpdateMarkedSection(
) error {
startIdx := strings.Index(existing, markerStart)
if startIdx == -1 {
- return promptErr.MarkerNotFound(filename)
+ return errPrompt.MarkerNotFound(filename)
}
endIdx := strings.Index(existing, markerEnd)
@@ -140,7 +140,7 @@ func UpdateMarkedSection(
templateStart := strings.Index(templateStr, markerStart)
templateEnd := strings.Index(templateStr, markerEnd)
if templateStart == -1 || templateEnd == -1 {
- return promptErr.TemplateMissingMarkers(filename)
+ return errPrompt.TemplateMissingMarkers(filename)
}
sectionContent := templateStr[templateStart : templateEnd+len(markerEnd)]
diff --git a/internal/cli/initialize/core/project/dir.go b/internal/cli/initialize/core/project/dir.go
index d254e642b..3869204fe 100644
--- a/internal/cli/initialize/core/project/dir.go
+++ b/internal/cli/initialize/core/project/dir.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/file"
"github.com/ActiveMemory/ctx/internal/config/fs"
errFs "github.com/ActiveMemory/ctx/internal/err/fs"
- errInitialize "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
"github.com/ActiveMemory/ctx/internal/write/initialize"
)
@@ -42,7 +42,7 @@ func CreateProjectDirs(cmd *cobra.Command) error {
readme, readErr := project.Readme(d)
if readErr != nil {
- return errInitialize.ReadProjectReadme(d, readErr)
+ return errInit.ReadProjectReadme(d, readErr)
}
readmePath := filepath.Join(d, file.Readme)
diff --git a/internal/cli/initialize/core/project/makefile.go b/internal/cli/initialize/core/project/makefile.go
index 87961b2bb..2881a8e84 100644
--- a/internal/cli/initialize/core/project/makefile.go
+++ b/internal/cli/initialize/core/project/makefile.go
@@ -17,7 +17,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/project"
"github.com/ActiveMemory/ctx/internal/config/token"
errFs "github.com/ActiveMemory/ctx/internal/err/fs"
- errInitialize "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
"github.com/ActiveMemory/ctx/internal/write/initialize"
)
@@ -32,7 +32,7 @@ func HandleMakefileCtx(cmd *cobra.Command) error {
content, err := makefile.Ctx()
if err != nil {
- return errInitialize.ReadTemplate(project.MakefileCtx, err)
+ return errInit.ReadTemplate(project.MakefileCtx, err)
}
if err = os.WriteFile(project.MakefileCtx, content, fs.PermFile); err != nil {
@@ -47,7 +47,7 @@ func HandleMakefileCtx(cmd *cobra.Command) error {
if err := os.WriteFile(
project.Makefile, []byte(minimal), fs.PermFile,
); err != nil {
- return errInitialize.CreateMakefile(err)
+ return errInit.CreateMakefile(err)
}
initialize.MakefileCreated(cmd)
return nil
diff --git a/internal/cli/initialize/init_test.go b/internal/cli/initialize/init_test.go
index 72959fb1d..fa5336f4e 100644
--- a/internal/cli/initialize/init_test.go
+++ b/internal/cli/initialize/init_test.go
@@ -14,7 +14,7 @@ import (
"testing"
"github.com/ActiveMemory/ctx/internal/claude"
- claude2 "github.com/ActiveMemory/ctx/internal/config/claude"
+ cfgClaude "github.com/ActiveMemory/ctx/internal/config/claude"
"github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/env"
)
@@ -417,7 +417,7 @@ func TestRunInit_Merge(t *testing.T) {
t.Setenv("HOME", tmpDir)
t.Setenv(env.SkipPathCheck, env.True)
- if err = os.WriteFile(claude2.Md, []byte("# My Project\n\nExisting.\n"), 0600); err != nil {
+ if err = os.WriteFile(cfgClaude.Md, []byte("# My Project\n\nExisting.\n"), 0600); err != nil {
t.Fatal(err)
}
@@ -427,7 +427,7 @@ func TestRunInit_Merge(t *testing.T) {
t.Fatalf("init --merge failed: %v", err)
}
- content, _ := os.ReadFile(claude2.Md)
+ content, _ := os.ReadFile(cfgClaude.Md)
if !strings.Contains(string(content), "My Project") {
t.Error("original content lost with --merge")
}
diff --git a/internal/cli/journal/cmd/site/doc.go b/internal/cli/journal/cmd/site/doc.go
new file mode 100644
index 000000000..8dfc31aa1
--- /dev/null
+++ b/internal/cli/journal/cmd/site/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package site provides the journal site subcommand for generating a static site from journal entries.
+package site
diff --git a/internal/cli/journal/core/collapse/doc.go b/internal/cli/journal/core/collapse/doc.go
new file mode 100644
index 000000000..97dedf946
--- /dev/null
+++ b/internal/cli/journal/core/collapse/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package collapse wraps long tool output turns in collapsible HTML details blocks.
+package collapse
diff --git a/internal/cli/journal/core/consolidate/doc.go b/internal/cli/journal/core/consolidate/doc.go
new file mode 100644
index 000000000..16501781a
--- /dev/null
+++ b/internal/cli/journal/core/consolidate/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package consolidate collapses consecutive identical tool turns into a single turn with a count.
+package consolidate
diff --git a/internal/cli/journal/core/format/doc.go b/internal/cli/journal/core/format/doc.go
new file mode 100644
index 000000000..5409c6e20
--- /dev/null
+++ b/internal/cli/journal/core/format/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package format renders file sizes and other journal metadata in human-readable form.
+package format
diff --git a/internal/cli/journal/core/frontmatter/doc.go b/internal/cli/journal/core/frontmatter/doc.go
new file mode 100644
index 000000000..58106e3dd
--- /dev/null
+++ b/internal/cli/journal/core/frontmatter/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package frontmatter transforms journal YAML frontmatter into Obsidian-compatible format.
+package frontmatter
diff --git a/internal/cli/journal/core/generate/doc.go b/internal/cli/journal/core/generate/doc.go
new file mode 100644
index 000000000..229306a14
--- /dev/null
+++ b/internal/cli/journal/core/generate/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package generate builds site index pages, READMEs, and navigation for journal static sites.
+package generate
diff --git a/internal/cli/journal/core/group/doc.go b/internal/cli/journal/core/group/doc.go
new file mode 100644
index 000000000..2cb6b9618
--- /dev/null
+++ b/internal/cli/journal/core/group/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package group organizes journal entries by month for chronological display.
+package group
diff --git a/internal/cli/journal/core/moc/doc.go b/internal/cli/journal/core/moc/doc.go
new file mode 100644
index 000000000..5bfc4898b
--- /dev/null
+++ b/internal/cli/journal/core/moc/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package moc generates Obsidian Maps of Content for journal vault navigation.
+package moc
diff --git a/internal/cli/journal/core/normalize/doc.go b/internal/cli/journal/core/normalize/doc.go
new file mode 100644
index 000000000..226f807cf
--- /dev/null
+++ b/internal/cli/journal/core/normalize/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package normalize sanitizes journal Markdown for static site rendering by fixing fences, headings, and list formatting.
+package normalize
diff --git a/internal/cli/journal/core/parse/doc.go b/internal/cli/journal/core/parse/doc.go
new file mode 100644
index 000000000..f5470bec1
--- /dev/null
+++ b/internal/cli/journal/core/parse/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package parse scans journal directories and extracts entry metadata from Markdown files.
+package parse
diff --git a/internal/cli/journal/core/reduce/doc.go b/internal/cli/journal/core/reduce/doc.go
new file mode 100644
index 000000000..ecbe1cb35
--- /dev/null
+++ b/internal/cli/journal/core/reduce/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package reduce strips code fence markers from journal content to eliminate nesting conflicts.
+package reduce
diff --git a/internal/cli/journal/core/section/doc.go b/internal/cli/journal/core/section/doc.go
new file mode 100644
index 000000000..6874c4ed7
--- /dev/null
+++ b/internal/cli/journal/core/section/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package section builds topic indexes and section pages for journal site navigation.
+package section
diff --git a/internal/cli/journal/core/section/section.go b/internal/cli/journal/core/section/section.go
index 1b360f342..72ba972d5 100644
--- a/internal/cli/journal/core/section/section.go
+++ b/internal/cli/journal/core/section/section.go
@@ -22,7 +22,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/regex"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/entity"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
)
// WriteSection creates a subdirectory, writes its index page, and calls
@@ -43,14 +43,14 @@ func WriteSection(
) error {
dir := filepath.Join(docsDir, subdir)
if mkErr := os.MkdirAll(dir, fs.PermExec); mkErr != nil {
- return ctxErr.Mkdir(dir, mkErr)
+ return errFs.Mkdir(dir, mkErr)
}
indexPath := filepath.Join(dir, file.Index)
if writeErr := os.WriteFile(
indexPath, []byte(indexContent), fs.PermFile,
); writeErr != nil {
- return ctxErr.FileWrite(indexPath, writeErr)
+ return errFs.FileWrite(indexPath, writeErr)
}
writePages(dir)
diff --git a/internal/cli/journal/core/session/doc.go b/internal/cli/journal/core/session/doc.go
new file mode 100644
index 000000000..df428b764
--- /dev/null
+++ b/internal/cli/journal/core/session/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package session counts unique sessions across topic data.
+package session
diff --git a/internal/cli/journal/core/turn/doc.go b/internal/cli/journal/core/turn/doc.go
new file mode 100644
index 000000000..a5f9366c7
--- /dev/null
+++ b/internal/cli/journal/core/turn/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package turn extracts and merges conversation turn bodies from journal entries.
+package turn
diff --git a/internal/cli/journal/core/wikilink/doc.go b/internal/cli/journal/core/wikilink/doc.go
new file mode 100644
index 000000000..2182cdb51
--- /dev/null
+++ b/internal/cli/journal/core/wikilink/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package wikilink converts standard Markdown links to Obsidian wikilink format.
+package wikilink
diff --git a/internal/cli/journal/core/wrap/doc.go b/internal/cli/journal/core/wrap/doc.go
new file mode 100644
index 000000000..b1b1c3b6b
--- /dev/null
+++ b/internal/cli/journal/core/wrap/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package wrap soft-wraps long lines in journal files to approximately 80 characters.
+package wrap
diff --git a/internal/cli/load/cmd/root/run.go b/internal/cli/load/cmd/root/run.go
index 06a5f6274..927172adf 100644
--- a/internal/cli/load/cmd/root/run.go
+++ b/internal/cli/load/cmd/root/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/load/core"
"github.com/ActiveMemory/ctx/internal/context/load"
errCtx "github.com/ActiveMemory/ctx/internal/err/context"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
writeLoad "github.com/ActiveMemory/ctx/internal/write/load"
)
@@ -35,7 +35,7 @@ func Run(cmd *cobra.Command, budget int, raw bool) error {
if err != nil {
var notFoundError *errCtx.NotFoundError
if errors.As(err, ¬FoundError) {
- return ctxErr.NotInitialized()
+ return errInit.NotInitialized()
}
return err
}
diff --git a/internal/cli/loop/cmd/root/run.go b/internal/cli/loop/cmd/root/run.go
index 82af6928e..c92a6f68b 100644
--- a/internal/cli/loop/cmd/root/run.go
+++ b/internal/cli/loop/cmd/root/run.go
@@ -14,9 +14,9 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/fs"
- loopCfg "github.com/ActiveMemory/ctx/internal/config/loop"
+ cfgLoop "github.com/ActiveMemory/ctx/internal/config/loop"
"github.com/ActiveMemory/ctx/internal/err/config"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
"github.com/ActiveMemory/ctx/internal/write/loop"
)
@@ -41,7 +41,7 @@ func Run(
maxIterations int,
completionMsg, outputFile string,
) error {
- if !loopCfg.ValidTools[tool] {
+ if !cfgLoop.ValidTools[tool] {
return config.InvalidTool(tool)
}
@@ -50,7 +50,7 @@ func Run(
if writeErr := os.WriteFile(
outputFile, []byte(script), fs.PermExec,
); writeErr != nil {
- return ctxErr.FileWrite(outputFile, writeErr)
+ return errFs.FileWrite(outputFile, writeErr)
}
loop.InfoGenerated(
diff --git a/internal/cli/loop/cmd/root/script.go b/internal/cli/loop/cmd/root/script.go
index a5c87d67c..1a13eab93 100644
--- a/internal/cli/loop/cmd/root/script.go
+++ b/internal/cli/loop/cmd/root/script.go
@@ -13,7 +13,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/assets/tpl"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
- loopCfg "github.com/ActiveMemory/ctx/internal/config/loop"
+ cfgLoop "github.com/ActiveMemory/ctx/internal/config/loop"
)
// GenerateLoopScript creates a bash script for running a Ralph loop.
@@ -37,11 +37,11 @@ func GenerateLoopScript(
var aiCommand string
switch tool {
- case loopCfg.DefaultTool:
+ case cfgLoop.DefaultTool:
aiCommand = fmt.Sprintf(`claude --print "$(cat %s)"`, absPrompt)
- case loopCfg.ToolAider:
+ case cfgLoop.ToolAider:
aiCommand = fmt.Sprintf(`aider --message-file %s`, absPrompt)
- case loopCfg.ToolGeneric:
+ case cfgLoop.ToolGeneric:
aiCommand = fmt.Sprintf(`# Replace with your AI CLI command
cat %s | your-ai-cli`, absPrompt)
}
diff --git a/internal/cli/loop/loop.go b/internal/cli/loop/loop.go
index eb5669f5d..70ec6ed5c 100644
--- a/internal/cli/loop/loop.go
+++ b/internal/cli/loop/loop.go
@@ -9,10 +9,10 @@ package loop
import (
"github.com/spf13/cobra"
- looproot "github.com/ActiveMemory/ctx/internal/cli/loop/cmd/root"
+ loopRoot "github.com/ActiveMemory/ctx/internal/cli/loop/cmd/root"
)
// Cmd returns the "ctx loop" command for generating Ralph loop scripts.
func Cmd() *cobra.Command {
- return looproot.Cmd()
+ return loopRoot.Cmd()
}
diff --git a/internal/cli/mcp/cmd/root/doc.go b/internal/cli/mcp/cmd/root/doc.go
new file mode 100644
index 000000000..ff8028673
--- /dev/null
+++ b/internal/cli/mcp/cmd/root/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package root provides the run function for the ctx MCP server command.
+package root
diff --git a/internal/cli/memory/cmd/diff/run.go b/internal/cli/memory/cmd/diff/run.go
index fda14375d..46a6586ce 100644
--- a/internal/cli/memory/cmd/diff/run.go
+++ b/internal/cli/memory/cmd/diff/run.go
@@ -11,7 +11,7 @@ import (
"github.com/spf13/cobra"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/memory"
+ errMemory "github.com/ActiveMemory/ctx/internal/err/memory"
mem "github.com/ActiveMemory/ctx/internal/memory"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/write/memory"
@@ -31,12 +31,12 @@ func Run(cmd *cobra.Command) error {
sourcePath, discoverErr := mem.DiscoverMemoryPath(projectRoot)
if discoverErr != nil {
- return ctxErr.DiscoverFailed(discoverErr)
+ return errMemory.DiscoverFailed(discoverErr)
}
diff, diffErr := mem.Diff(contextDir, sourcePath)
if diffErr != nil {
- return ctxErr.DiffFailed(diffErr)
+ return errMemory.DiffFailed(diffErr)
}
if diff == "" {
diff --git a/internal/cli/memory/cmd/publish/run.go b/internal/cli/memory/cmd/publish/run.go
index 5ee5ed48c..cb2a0a508 100644
--- a/internal/cli/memory/cmd/publish/run.go
+++ b/internal/cli/memory/cmd/publish/run.go
@@ -11,7 +11,7 @@ import (
"github.com/spf13/cobra"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/memory"
+ errMemory "github.com/ActiveMemory/ctx/internal/err/memory"
mem "github.com/ActiveMemory/ctx/internal/memory"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/write/publish"
@@ -35,12 +35,12 @@ func Run(cmd *cobra.Command, budget int, dryRun bool) error {
memoryPath, discoverErr := mem.DiscoverMemoryPath(projectRoot)
if discoverErr != nil {
sync.ErrAutoMemoryNotActive(cmd, discoverErr)
- return ctxErr.NotFound()
+ return errMemory.NotFound()
}
result, selectErr := mem.SelectContent(contextDir, budget)
if selectErr != nil {
- return ctxErr.SelectContentFailed(selectErr)
+ return errMemory.SelectContentFailed(selectErr)
}
publish.PublishPlan(cmd, budget,
@@ -57,7 +57,7 @@ func Run(cmd *cobra.Command, budget int, dryRun bool) error {
if _, publishErr := mem.Publish(
contextDir, memoryPath, budget,
); publishErr != nil {
- return ctxErr.PublishFailed(publishErr)
+ return errMemory.PublishFailed(publishErr)
}
publish.PublishDone(cmd)
diff --git a/internal/cli/memory/cmd/sync/run.go b/internal/cli/memory/cmd/sync/run.go
index 276c45fd5..3bf133dea 100644
--- a/internal/cli/memory/cmd/sync/run.go
+++ b/internal/cli/memory/cmd/sync/run.go
@@ -13,7 +13,7 @@ import (
cfgMem "github.com/ActiveMemory/ctx/internal/config/memory"
errMem "github.com/ActiveMemory/ctx/internal/err/memory"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/state"
+ errState "github.com/ActiveMemory/ctx/internal/err/state"
"github.com/ActiveMemory/ctx/internal/memory"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/write/sync"
@@ -59,11 +59,11 @@ func Run(cmd *cobra.Command, dryRun bool) error {
// Update sync state
state, loadErr := memory.LoadState(contextDir)
if loadErr != nil {
- return ctxErr.Load(loadErr)
+ return errState.Load(loadErr)
}
state.MarkSynced()
if saveErr := memory.SaveState(contextDir, state); saveErr != nil {
- return ctxErr.Save(saveErr)
+ return errState.Save(saveErr)
}
return nil
diff --git a/internal/cli/notify/cmd/setup/run.go b/internal/cli/notify/cmd/setup/run.go
index ca7c60497..55a035ad5 100644
--- a/internal/cli/notify/cmd/setup/run.go
+++ b/internal/cli/notify/cmd/setup/run.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/crypto"
"github.com/ActiveMemory/ctx/internal/err/fs"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/notify"
+ errNotify "github.com/ActiveMemory/ctx/internal/err/notify"
iNotify "github.com/ActiveMemory/ctx/internal/notify"
"github.com/ActiveMemory/ctx/internal/write/notify"
)
@@ -39,11 +39,11 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
}
url := strings.TrimSpace(scanner.Text())
if url == "" {
- return ctxErr.WebhookEmpty()
+ return errNotify.WebhookEmpty()
}
if saveErr := iNotify.SaveWebhook(url); saveErr != nil {
- return ctxErr.SaveWebhook(saveErr)
+ return errNotify.SaveWebhook(saveErr)
}
notify.SetupDone(cmd, iNotify.MaskURL(url), crypto.NotifyEnc)
diff --git a/internal/cli/notify/cmd/test/run.go b/internal/cli/notify/cmd/test/run.go
index ed414fb40..27b94f3e8 100644
--- a/internal/cli/notify/cmd/test/run.go
+++ b/internal/cli/notify/cmd/test/run.go
@@ -15,10 +15,10 @@ import (
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/config/crypto"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/notify"
+ errNotify "github.com/ActiveMemory/ctx/internal/err/notify"
"github.com/ActiveMemory/ctx/internal/notify"
"github.com/ActiveMemory/ctx/internal/rc"
- iNotify "github.com/ActiveMemory/ctx/internal/write/notify"
+ writeNotify "github.com/ActiveMemory/ctx/internal/write/notify"
)
// runTest sends a test notification to the configured webhook.
@@ -31,10 +31,10 @@ import (
func runTest(cmd *cobra.Command) error {
url, loadErr := notify.LoadWebhook()
if loadErr != nil {
- return ctxErr.LoadWebhook(loadErr)
+ return errNotify.LoadWebhook(loadErr)
}
if url == "" {
- iNotify.TestNoWebhook(cmd)
+ writeNotify.TestNoWebhook(cmd)
return nil
}
@@ -52,20 +52,20 @@ func runTest(cmd *cobra.Command) error {
body, marshalErr := json.Marshal(payload)
if marshalErr != nil {
- return ctxErr.MarshalPayload(marshalErr)
+ return errNotify.MarshalPayload(marshalErr)
}
if !notify.EventAllowed("test", rc.NotifyEvents()) {
- iNotify.TestFiltered(cmd)
+ writeNotify.TestFiltered(cmd)
}
resp, postErr := notify.PostJSON(url, body)
if postErr != nil {
- return ctxErr.SendNotification(postErr)
+ return errNotify.SendNotification(postErr)
}
defer func() { _ = resp.Body.Close() }()
- iNotify.TestResult(cmd, resp.StatusCode, crypto.NotifyEnc)
+ writeNotify.TestResult(cmd, resp.StatusCode, crypto.NotifyEnc)
return nil
}
diff --git a/internal/cli/notify/notify_test.go b/internal/cli/notify/notify_test.go
index 92ef8058e..90a39c95e 100644
--- a/internal/cli/notify/notify_test.go
+++ b/internal/cli/notify/notify_test.go
@@ -17,7 +17,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/notify/cmd/setup"
"github.com/ActiveMemory/ctx/internal/config/ctx"
- notifylib "github.com/ActiveMemory/ctx/internal/notify"
+ libNotify "github.com/ActiveMemory/ctx/internal/notify"
"github.com/ActiveMemory/ctx/internal/rc"
)
@@ -139,9 +139,9 @@ func TestMaskURL(t *testing.T) {
}
for _, tc := range tests {
- got := notifylib.MaskURL(tc.input)
+ got := libNotify.MaskURL(tc.input)
if got != tc.want {
- t.Errorf("notifylib.MaskURL(%q) = %q, want %q", tc.input, got, tc.want)
+ t.Errorf("libNotify.MaskURL(%q) = %q, want %q", tc.input, got, tc.want)
}
}
}
@@ -206,7 +206,7 @@ func TestTest_WebhookSuccess(t *testing.T) {
defer server.Close()
// Save the test server URL as the webhook.
- if saveErr := notifylib.SaveWebhook(server.URL); saveErr != nil {
+ if saveErr := libNotify.SaveWebhook(server.URL); saveErr != nil {
t.Fatalf("SaveWebhook() error = %v", saveErr)
}
@@ -240,7 +240,7 @@ func TestTest_WebhookServerError(t *testing.T) {
}))
defer server.Close()
- if saveErr := notifylib.SaveWebhook(server.URL); saveErr != nil {
+ if saveErr := libNotify.SaveWebhook(server.URL); saveErr != nil {
t.Fatalf("SaveWebhook() error = %v", saveErr)
}
diff --git a/internal/cli/pad/cmd/add/run.go b/internal/cli/pad/cmd/add/run.go
index d6a24d95b..367c23d35 100644
--- a/internal/cli/pad/cmd/add/run.go
+++ b/internal/cli/pad/cmd/add/run.go
@@ -13,7 +13,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/pad"
"github.com/ActiveMemory/ctx/internal/err/fs"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/pad"
+ errPad "github.com/ActiveMemory/ctx/internal/err/pad"
"github.com/ActiveMemory/ctx/internal/io"
writePad "github.com/ActiveMemory/ctx/internal/write/pad"
)
@@ -58,7 +58,7 @@ func runAddBlob(cmd *cobra.Command, label, filePath string) error {
}
if len(data) > pad.MaxBlobSize {
- return ctxErr.FileTooLarge(len(data), pad.MaxBlobSize)
+ return errPad.FileTooLarge(len(data), pad.MaxBlobSize)
}
entries, readErr := store.ReadEntries()
diff --git a/internal/cli/pad/cmd/edit/cmd.go b/internal/cli/pad/cmd/edit/cmd.go
index eabf01247..986aaf45f 100644
--- a/internal/cli/pad/cmd/edit/cmd.go
+++ b/internal/cli/pad/cmd/edit/cmd.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/embed/cmd"
"github.com/ActiveMemory/ctx/internal/config/embed/flag"
cFlag "github.com/ActiveMemory/ctx/internal/config/flag"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/pad"
+ errPad "github.com/ActiveMemory/ctx/internal/err/pad"
)
// Cmd returns the pad edit subcommand.
@@ -48,7 +48,7 @@ func Cmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
n, err := strconv.Atoi(args[0])
if err != nil {
- return ctxErr.InvalidIndex(args[0])
+ return errPad.InvalidIndex(args[0])
}
hasPositional := len(args) == 2
@@ -59,7 +59,7 @@ func Cmd() *cobra.Command {
// --file/--label conflict with positional/--append/--prepend.
if (hasFile || hasLabel) && (hasPositional || hasAppend || hasPrepend) {
- return ctxErr.EditBlobTextConflict()
+ return errPad.EditBlobTextConflict()
}
// Blob edit mode.
@@ -80,10 +80,10 @@ func Cmd() *cobra.Command {
}
if flagCount == 0 {
- return ctxErr.EditNoMode()
+ return errPad.EditNoMode()
}
if flagCount > 1 {
- return ctxErr.EditTextConflict()
+ return errPad.EditTextConflict()
}
switch {
diff --git a/internal/cli/pad/cmd/edit/run.go b/internal/cli/pad/cmd/edit/run.go
index 957814ea2..370c938b0 100644
--- a/internal/cli/pad/cmd/edit/run.go
+++ b/internal/cli/pad/cmd/edit/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/pad"
"github.com/ActiveMemory/ctx/internal/err/fs"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/pad"
+ errPad "github.com/ActiveMemory/ctx/internal/err/pad"
"github.com/ActiveMemory/ctx/internal/io"
writePad "github.com/ActiveMemory/ctx/internal/write/pad"
)
@@ -68,7 +68,7 @@ func runEditAppend(cmd *cobra.Command, n int, text string) error {
}
if blob.ContainsBlob(entries[n-1]) {
- return ctxErr.BlobAppendNotAllowed()
+ return errPad.BlobAppendNotAllowed()
}
entries[n-1] = entries[n-1] + " " + text
@@ -101,7 +101,7 @@ func runEditPrepend(cmd *cobra.Command, n int, text string) error {
}
if blob.ContainsBlob(entries[n-1]) {
- return ctxErr.BlobPrependNotAllowed()
+ return errPad.BlobPrependNotAllowed()
}
entries[n-1] = text + " " + entries[n-1]
@@ -136,7 +136,7 @@ func runEditBlob(cmd *cobra.Command, n int, filePath, labelText string) error {
oldLabel, oldData, ok := blob.SplitBlob(entries[n-1])
if !ok {
- return ctxErr.NotBlobEntry(n)
+ return errPad.NotBlobEntry(n)
}
newLabel := oldLabel
@@ -152,7 +152,7 @@ func runEditBlob(cmd *cobra.Command, n int, filePath, labelText string) error {
return fs.ReadFile(readErr)
}
if len(data) > pad.MaxBlobSize {
- return ctxErr.FileTooLarge(len(data), pad.MaxBlobSize)
+ return errPad.FileTooLarge(len(data), pad.MaxBlobSize)
}
newData = data
}
diff --git a/internal/cli/pad/cmd/export/run.go b/internal/cli/pad/cmd/export/run.go
index d56aa3045..b07113e30 100644
--- a/internal/cli/pad/cmd/export/run.go
+++ b/internal/cli/pad/cmd/export/run.go
@@ -18,7 +18,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/config/token"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
"github.com/ActiveMemory/ctx/internal/write/export"
"github.com/ActiveMemory/ctx/internal/write/pad"
)
@@ -53,7 +53,7 @@ func runExport(cmd *cobra.Command, dir string, force, dryRun bool) error {
if !dryRun {
if mkErr := os.MkdirAll(dir, fs.PermExec); mkErr != nil {
- return ctxErr.Mkdir(dir, mkErr)
+ return errFs.Mkdir(dir, mkErr)
}
}
diff --git a/internal/cli/pad/cmd/imp/run.go b/internal/cli/pad/cmd/imp/run.go
index 0ac62861b..9bf391e99 100644
--- a/internal/cli/pad/cmd/imp/run.go
+++ b/internal/cli/pad/cmd/imp/run.go
@@ -18,7 +18,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/cli"
"github.com/ActiveMemory/ctx/internal/config/pad"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
internalIo "github.com/ActiveMemory/ctx/internal/io"
writePad "github.com/ActiveMemory/ctx/internal/write/pad"
)
@@ -38,7 +38,7 @@ func runImport(cmd *cobra.Command, file string) error {
} else {
f, err := internalIo.SafeOpenUserFile(file)
if err != nil {
- return ctxErr.OpenFile(file, err)
+ return errFs.OpenFile(file, err)
}
defer func() {
if cErr := f.Close(); cErr != nil {
@@ -64,7 +64,7 @@ func runImport(cmd *cobra.Command, file string) error {
count++
}
if scanErr := scanner.Err(); scanErr != nil {
- return ctxErr.ReadInput(scanErr)
+ return errFs.ReadInput(scanErr)
}
if count == 0 {
@@ -92,15 +92,15 @@ func runImport(cmd *cobra.Command, file string) error {
func runImportBlobs(cmd *cobra.Command, path string) error {
info, statErr := os.Stat(path)
if statErr != nil {
- return ctxErr.StatPath(path, statErr)
+ return errFs.StatPath(path, statErr)
}
if !info.IsDir() {
- return ctxErr.NotDirectory(path)
+ return errFs.NotDirectory(path)
}
dirEntries, readErr := os.ReadDir(path)
if readErr != nil {
- return ctxErr.ReadDirectory(path, readErr)
+ return errFs.ReadDirectory(path, readErr)
}
entries, loadErr := store.ReadEntries()
diff --git a/internal/cli/pad/cmd/merge/run.go b/internal/cli/pad/cmd/merge/run.go
index 2e2127637..6d756e5ba 100644
--- a/internal/cli/pad/cmd/merge/run.go
+++ b/internal/cli/pad/cmd/merge/run.go
@@ -12,7 +12,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/pad/core/store"
"github.com/spf13/cobra"
- ctEerr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
"github.com/ActiveMemory/ctx/internal/write/pad"
)
@@ -53,7 +53,7 @@ func Run(
for _, file := range files {
entries, fileErr := merge.ReadFileEntries(file, key)
if fileErr != nil {
- return ctEerr.OpenFile(file, fileErr)
+ return errFs.OpenFile(file, fileErr)
}
if merge.HasBinaryEntries(entries) {
diff --git a/internal/cli/pad/cmd/resolve/run.go b/internal/cli/pad/cmd/resolve/run.go
index 6d9a572a2..a5b9a81ad 100644
--- a/internal/cli/pad/cmd/resolve/run.go
+++ b/internal/cli/pad/cmd/resolve/run.go
@@ -7,14 +7,14 @@
package resolve
import (
- crypto2 "github.com/ActiveMemory/ctx/internal/cli/pad/core/crypto"
+ padCrypto "github.com/ActiveMemory/ctx/internal/cli/pad/core/crypto"
"github.com/ActiveMemory/ctx/internal/cli/pad/core/store"
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/config/pad"
"github.com/ActiveMemory/ctx/internal/crypto"
errCrypto "github.com/ActiveMemory/ctx/internal/err/crypto"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/pad"
+ errPad "github.com/ActiveMemory/ctx/internal/err/pad"
"github.com/ActiveMemory/ctx/internal/rc"
writePad "github.com/ActiveMemory/ctx/internal/write/pad"
)
@@ -28,7 +28,7 @@ import (
// - error: Non-nil if no conflict files found or decryption fails
func Run(cmd *cobra.Command) error {
if !rc.ScratchpadEncrypt() {
- return ctxErr.ResolveNotEncrypted()
+ return errPad.ResolveNotEncrypted()
}
kp := store.KeyPath()
@@ -39,11 +39,11 @@ func Run(cmd *cobra.Command) error {
dir := rc.ContextDir()
- ours, errOurs := crypto2.DecryptFile(key, dir, pad.EncOurs)
- theirs, errTheirs := crypto2.DecryptFile(key, dir, pad.EncTheirs)
+ ours, errOurs := padCrypto.DecryptFile(key, dir, pad.EncOurs)
+ theirs, errTheirs := padCrypto.DecryptFile(key, dir, pad.EncTheirs)
if errOurs != nil && errTheirs != nil {
- return ctxErr.NoConflictFiles(pad.Enc)
+ return errPad.NoConflictFiles(pad.Enc)
}
if errOurs == nil {
diff --git a/internal/cli/pad/cmd/show/cmd.go b/internal/cli/pad/cmd/show/cmd.go
index f34db02b9..589af9b57 100644
--- a/internal/cli/pad/cmd/show/cmd.go
+++ b/internal/cli/pad/cmd/show/cmd.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/embed/cmd"
"github.com/ActiveMemory/ctx/internal/config/embed/flag"
cFlag "github.com/ActiveMemory/ctx/internal/config/flag"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/pad"
+ errPad "github.com/ActiveMemory/ctx/internal/err/pad"
)
// Cmd returns the pad show subcommand.
@@ -39,7 +39,7 @@ func Cmd() *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
n, err := strconv.Atoi(args[0])
if err != nil {
- return ctxErr.InvalidIndex(args[0])
+ return errPad.InvalidIndex(args[0])
}
return Run(cmd, n, outPath)
},
diff --git a/internal/cli/pad/core/blob/doc.go b/internal/cli/pad/core/blob/doc.go
new file mode 100644
index 000000000..6d0d979fb
--- /dev/null
+++ b/internal/cli/pad/core/blob/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package blob handles binary blob encoding and decoding within scratchpad entries.
+package blob
diff --git a/internal/cli/pad/core/crypto/crypto.go b/internal/cli/pad/core/crypto/crypto.go
index 00c086e53..d58df0515 100644
--- a/internal/cli/pad/core/crypto/crypto.go
+++ b/internal/cli/pad/core/crypto/crypto.go
@@ -9,7 +9,7 @@ package crypto
import (
"github.com/ActiveMemory/ctx/internal/cli/pad/core/parse"
"github.com/ActiveMemory/ctx/internal/crypto"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/crypto"
+ errCrypto "github.com/ActiveMemory/ctx/internal/err/crypto"
"github.com/ActiveMemory/ctx/internal/io"
)
@@ -31,7 +31,7 @@ func DecryptFile(key []byte, baseDir, filename string) ([]string, error) {
plaintext, decErr := crypto.Decrypt(key, data)
if decErr != nil {
- return nil, ctxErr.DecryptFailed()
+ return nil, errCrypto.DecryptFailed()
}
return parse.ParseEntries(plaintext), nil
diff --git a/internal/cli/pad/core/crypto/doc.go b/internal/cli/pad/core/crypto/doc.go
new file mode 100644
index 000000000..82960565a
--- /dev/null
+++ b/internal/cli/pad/core/crypto/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package crypto reads and decrypts encrypted scratchpad files.
+package crypto
diff --git a/internal/cli/pad/core/merge/doc.go b/internal/cli/pad/core/merge/doc.go
new file mode 100644
index 000000000..99ec238fb
--- /dev/null
+++ b/internal/cli/pad/core/merge/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package merge reads scratchpad files with automatic decryption fallback and merges entries.
+package merge
diff --git a/internal/cli/pad/core/parse/doc.go b/internal/cli/pad/core/parse/doc.go
new file mode 100644
index 000000000..05e46615b
--- /dev/null
+++ b/internal/cli/pad/core/parse/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package parse splits raw scratchpad content into individual entry lines.
+package parse
diff --git a/internal/cli/pad/core/store/doc.go b/internal/cli/pad/core/store/doc.go
new file mode 100644
index 000000000..db368d1fa
--- /dev/null
+++ b/internal/cli/pad/core/store/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package store manages scratchpad file persistence including encryption and path resolution.
+package store
diff --git a/internal/cli/pad/core/validate/doc.go b/internal/cli/pad/core/validate/doc.go
new file mode 100644
index 000000000..292fbfe66
--- /dev/null
+++ b/internal/cli/pad/core/validate/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package validate checks that scratchpad entry indexes are within valid bounds.
+package validate
diff --git a/internal/cli/pad/core/validate/validate.go b/internal/cli/pad/core/validate/validate.go
index 55ef7f5c9..eafec942f 100644
--- a/internal/cli/pad/core/validate/validate.go
+++ b/internal/cli/pad/core/validate/validate.go
@@ -7,7 +7,7 @@
package validate
import (
- padErr "github.com/ActiveMemory/ctx/internal/err/pad"
+ errPad "github.com/ActiveMemory/ctx/internal/err/pad"
)
// ValidateIndex checks that n is a valid 1-based index into entries.
@@ -20,7 +20,7 @@ import (
// - error: Non-nil if n is out of range
func ValidateIndex(n int, entries []string) error {
if n < 1 || n > len(entries) {
- return padErr.EntryRange(n, len(entries))
+ return errPad.EntryRange(n, len(entries))
}
return nil
}
diff --git a/internal/cli/pad/pad_test.go b/internal/cli/pad/pad_test.go
index 31e41c7ca..76fca73d9 100644
--- a/internal/cli/pad/pad_test.go
+++ b/internal/cli/pad/pad_test.go
@@ -16,13 +16,13 @@ import (
"testing"
"github.com/ActiveMemory/ctx/internal/cli/pad/core/blob"
- crypto2 "github.com/ActiveMemory/ctx/internal/cli/pad/core/crypto"
+ padCrypto "github.com/ActiveMemory/ctx/internal/cli/pad/core/crypto"
"github.com/ActiveMemory/ctx/internal/cli/pad/core/parse"
"github.com/ActiveMemory/ctx/internal/cli/pad/core/store"
"github.com/ActiveMemory/ctx/internal/cli/pad/core/validate"
"github.com/ActiveMemory/ctx/internal/config/dir"
"github.com/ActiveMemory/ctx/internal/config/pad"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/pad"
+ errPad "github.com/ActiveMemory/ctx/internal/err/pad"
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/config/fs"
@@ -632,7 +632,7 @@ func TestValidateIndex_EmptySlice(t *testing.T) {
}
func TestErrEntryRange(t *testing.T) {
- err := ctxerr.EntryRange(5, 3)
+ err := errPad.EntryRange(5, 3)
msg := err.Error()
if !strings.Contains(msg, "5") || !strings.Contains(msg, "3") {
t.Errorf("EntryRange = %q, want indices 5 and 3 mentioned", msg)
@@ -1123,7 +1123,7 @@ func TestDecryptFile_BadData(t *testing.T) {
t.Fatal(writeErr)
}
- _, err := crypto2.DecryptFile(key, tmpDir, "bad.enc")
+ _, err := padCrypto.DecryptFile(key, tmpDir, "bad.enc")
if err == nil {
t.Fatal("expected decryption error for bad data")
}
@@ -1136,7 +1136,7 @@ func TestDecryptFile_MissingFile(t *testing.T) {
key, _ := crypto.GenerateKey()
tmpDir := t.TempDir()
- _, err := crypto2.DecryptFile(key, tmpDir, "nonexistent.enc")
+ _, err := padCrypto.DecryptFile(key, tmpDir, "nonexistent.enc")
if err == nil {
t.Fatal("expected error for missing file")
}
@@ -1155,7 +1155,7 @@ func TestDecryptFile_ValidData(t *testing.T) {
t.Fatal(writeErr)
}
- entries, err := crypto2.DecryptFile(key, tmpDir, "good.enc")
+ entries, err := padCrypto.DecryptFile(key, tmpDir, "good.enc")
if err != nil {
t.Fatalf("decryptFile error: %v", err)
}
diff --git a/internal/cli/pause/cmd/root/run.go b/internal/cli/pause/cmd/root/run.go
index 58ba077b0..4f327b31e 100644
--- a/internal/cli/pause/cmd/root/run.go
+++ b/internal/cli/pause/cmd/root/run.go
@@ -10,7 +10,7 @@ import (
"os"
"github.com/ActiveMemory/ctx/internal/cli/system/core/nudge"
- session2 "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
+ coreSession "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/write/session"
@@ -26,7 +26,7 @@ import (
// - error: Always nil
func Run(cmd *cobra.Command, sessionID string) error {
if sessionID == "" {
- sessionID = session2.ReadSessionID(os.Stdin)
+ sessionID = coreSession.ReadSessionID(os.Stdin)
}
nudge.Pause(sessionID)
session.SessionPaused(cmd, sessionID)
diff --git a/internal/cli/permission/cmd/restore/run.go b/internal/cli/permission/cmd/restore/run.go
index af50d3562..075aa38de 100644
--- a/internal/cli/permission/cmd/restore/run.go
+++ b/internal/cli/permission/cmd/restore/run.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/claude"
"github.com/ActiveMemory/ctx/internal/cli/permission/core"
- configClaude "github.com/ActiveMemory/ctx/internal/config/claude"
+ cfgClaude "github.com/ActiveMemory/ctx/internal/config/claude"
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/err/config"
errFs "github.com/ActiveMemory/ctx/internal/err/fs"
@@ -31,26 +31,26 @@ import (
// Returns:
// - error: Non-nil on read/write/parse failure or missing golden file
func Run(cmd *cobra.Command) error {
- goldenBytes, goldenReadErr := os.ReadFile(configClaude.SettingsGolden)
+ goldenBytes, goldenReadErr := os.ReadFile(cfgClaude.SettingsGolden)
if goldenReadErr != nil {
if os.IsNotExist(goldenReadErr) {
return config.GoldenNotFound()
}
- return errFs.FileRead(configClaude.SettingsGolden, goldenReadErr)
+ return errFs.FileRead(cfgClaude.SettingsGolden, goldenReadErr)
}
- localBytes, localReadErr := os.ReadFile(configClaude.Settings)
+ localBytes, localReadErr := os.ReadFile(cfgClaude.Settings)
if localReadErr != nil {
if os.IsNotExist(localReadErr) {
if writeErr := os.WriteFile(
- configClaude.Settings, goldenBytes, fs.PermFile,
+ cfgClaude.Settings, goldenBytes, fs.PermFile,
); writeErr != nil {
- return errFs.FileWrite(configClaude.Settings, writeErr)
+ return errFs.FileWrite(cfgClaude.Settings, writeErr)
}
restore.RestoreNoLocal(cmd)
return nil
}
- return errFs.FileRead(configClaude.Settings, localReadErr)
+ return errFs.FileRead(cfgClaude.Settings, localReadErr)
}
if bytes.Equal(goldenBytes, localBytes) {
@@ -60,10 +60,10 @@ func Run(cmd *cobra.Command) error {
var golden, local claude.Settings
if goldenParseErr := json.Unmarshal(goldenBytes, &golden); goldenParseErr != nil {
- return errParser.ParseFile(configClaude.SettingsGolden, goldenParseErr)
+ return errParser.ParseFile(cfgClaude.SettingsGolden, goldenParseErr)
}
if localParseErr := json.Unmarshal(localBytes, &local); localParseErr != nil {
- return errParser.ParseFile(configClaude.Settings, localParseErr)
+ return errParser.ParseFile(cfgClaude.Settings, localParseErr)
}
restored, dropped := core.DiffStringSlices(
@@ -76,9 +76,9 @@ func Run(cmd *cobra.Command) error {
restore.RestoreDiff(cmd, dropped, restored, denyDropped, denyRestored)
if writeErr := os.WriteFile(
- configClaude.Settings, goldenBytes, fs.PermFile,
+ cfgClaude.Settings, goldenBytes, fs.PermFile,
); writeErr != nil {
- return errFs.FileWrite(configClaude.Settings, writeErr)
+ return errFs.FileWrite(cfgClaude.Settings, writeErr)
}
restore.RestoreDone(cmd)
diff --git a/internal/cli/permission/cmd/snapshot/run.go b/internal/cli/permission/cmd/snapshot/run.go
index d338a44f5..5d221a0d7 100644
--- a/internal/cli/permission/cmd/snapshot/run.go
+++ b/internal/cli/permission/cmd/snapshot/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/claude"
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/err/config"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
"github.com/ActiveMemory/ctx/internal/write/restore"
)
@@ -31,7 +31,7 @@ func Run(cmd *cobra.Command) error {
if os.IsNotExist(readErr) {
return config.SettingsNotFound()
}
- return ctxErr.FileRead(claude.Settings, readErr)
+ return errFs.FileRead(claude.Settings, readErr)
}
updated := false
@@ -42,7 +42,7 @@ func Run(cmd *cobra.Command) error {
if writeErr := os.WriteFile(
claude.SettingsGolden, content, fs.PermFile,
); writeErr != nil {
- return ctxErr.FileWrite(claude.SettingsGolden, writeErr)
+ return errFs.FileWrite(claude.SettingsGolden, writeErr)
}
restore.SnapshotDone(cmd, updated, claude.SettingsGolden)
diff --git a/internal/cli/prompt/cmd/add/run.go b/internal/cli/prompt/cmd/add/run.go
index 6a4860c51..4bd06d582 100644
--- a/internal/cli/prompt/cmd/add/run.go
+++ b/internal/cli/prompt/cmd/add/run.go
@@ -18,7 +18,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/file"
"github.com/ActiveMemory/ctx/internal/config/fs"
errFs "github.com/ActiveMemory/ctx/internal/err/fs"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/prompt"
+ errPrompt "github.com/ActiveMemory/ctx/internal/err/prompt"
"github.com/ActiveMemory/ctx/internal/write/prompt"
)
@@ -41,7 +41,7 @@ func runAdd(cmd *cobra.Command, name string, fromStdin bool) error {
// Check if the file already exists.
if _, statErr := os.Stat(path); statErr == nil {
- return ctxErr.Exists(name)
+ return errPrompt.Exists(name)
}
var content []byte
@@ -57,7 +57,7 @@ func runAdd(cmd *cobra.Command, name string, fromStdin bool) error {
var templateErr error
content, templateErr = readPrompt.Template(name + file.ExtMarkdown)
if templateErr != nil {
- return ctxErr.NoPromptTemplate(name)
+ return errPrompt.NoPromptTemplate(name)
}
}
diff --git a/internal/cli/prompt/cmd/list/run.go b/internal/cli/prompt/cmd/list/run.go
index c0fabfefb..221393b80 100644
--- a/internal/cli/prompt/cmd/list/run.go
+++ b/internal/cli/prompt/cmd/list/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/prompt/core"
"github.com/ActiveMemory/ctx/internal/config/file"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
"github.com/ActiveMemory/ctx/internal/write/prompt"
)
@@ -34,7 +34,7 @@ func Run(cmd *cobra.Command) error {
prompt.PromptNone(cmd)
return nil
}
- return ctxErr.ReadDirectory(dir, readErr)
+ return errFs.ReadDirectory(dir, readErr)
}
var found bool
diff --git a/internal/cli/prompt/cmd/rm/run.go b/internal/cli/prompt/cmd/rm/run.go
index 25c05c55f..8448e7add 100644
--- a/internal/cli/prompt/cmd/rm/run.go
+++ b/internal/cli/prompt/cmd/rm/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/prompt/core"
"github.com/ActiveMemory/ctx/internal/config/file"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/prompt"
+ errPrompt "github.com/ActiveMemory/ctx/internal/err/prompt"
"github.com/ActiveMemory/ctx/internal/write/prompt"
)
@@ -30,11 +30,11 @@ func Run(cmd *cobra.Command, name string) error {
path := filepath.Join(core.PromptsDir(), name+file.ExtMarkdown)
if _, statErr := os.Stat(path); os.IsNotExist(statErr) {
- return ctxErr.NotFound(name)
+ return errPrompt.NotFound(name)
}
if removeErr := os.Remove(path); removeErr != nil {
- return ctxErr.Remove(removeErr)
+ return errPrompt.Remove(removeErr)
}
prompt.Removed(cmd, name)
diff --git a/internal/cli/prompt/cmd/show/run.go b/internal/cli/prompt/cmd/show/run.go
index 8c9bfa545..463667ca3 100644
--- a/internal/cli/prompt/cmd/show/run.go
+++ b/internal/cli/prompt/cmd/show/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/prompt/core"
"github.com/ActiveMemory/ctx/internal/config/file"
"github.com/ActiveMemory/ctx/internal/err/fs"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/prompt"
+ errPrompt "github.com/ActiveMemory/ctx/internal/err/prompt"
"github.com/ActiveMemory/ctx/internal/io"
writePrompt "github.com/ActiveMemory/ctx/internal/write/prompt"
)
@@ -33,7 +33,7 @@ func Run(cmd *cobra.Command, name string) error {
)
if readErr != nil {
if os.IsNotExist(readErr) {
- return ctxErr.NotFound(name)
+ return errPrompt.NotFound(name)
}
return fs.ReadFile(readErr)
}
diff --git a/internal/cli/recall/cmd/export/run.go b/internal/cli/recall/cmd/export/run.go
index 512dc562c..5a7b53289 100644
--- a/internal/cli/recall/cmd/export/run.go
+++ b/internal/cli/recall/cmd/export/run.go
@@ -27,7 +27,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/journal"
errFs "github.com/ActiveMemory/ctx/internal/err/fs"
errJournal "github.com/ActiveMemory/ctx/internal/err/journal"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/session"
+ errSession "github.com/ActiveMemory/ctx/internal/err/session"
"github.com/ActiveMemory/ctx/internal/journal/state"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/write/err"
@@ -64,7 +64,7 @@ func Run(cmd *cobra.Command, args []string, opts entity.ExportOpts) error {
// 3. Resolve sessions.
sessions, scanErr := query.FindSessions(opts.AllProjects)
if scanErr != nil {
- return ctxErr.Find(scanErr)
+ return errSession.Find(scanErr)
}
if len(sessions) == 0 {
@@ -85,12 +85,12 @@ func Run(cmd *cobra.Command, args []string, opts entity.ExportOpts) error {
}
}
if len(toExport) == 0 {
- return ctxErr.NotFound(args[0])
+ return errSession.NotFound(args[0])
}
if len(toExport) > 1 {
lines := format.SessionMatchLines(toExport)
recall.AmbiguousSessionMatch(cmd, args[0], lines)
- return ctxErr.AmbiguousQuery()
+ return errSession.AmbiguousQuery()
}
singleSession = true
}
diff --git a/internal/cli/recall/cmd/list/run.go b/internal/cli/recall/cmd/list/run.go
index 9919a0d09..c439ce1db 100644
--- a/internal/cli/recall/cmd/list/run.go
+++ b/internal/cli/recall/cmd/list/run.go
@@ -22,7 +22,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/entity"
"github.com/ActiveMemory/ctx/internal/err/date"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/session"
+ errSession "github.com/ActiveMemory/ctx/internal/err/session"
"github.com/ActiveMemory/ctx/internal/parse"
"github.com/ActiveMemory/ctx/internal/write/recall"
)
@@ -64,7 +64,7 @@ func Run(
sessions, scanErr := query.FindSessions(allProjects)
if scanErr != nil {
- return ctxErr.Find(scanErr)
+ return errSession.Find(scanErr)
}
if len(sessions) == 0 {
diff --git a/internal/cli/recall/cmd/show/run.go b/internal/cli/recall/cmd/show/run.go
index 82b6a3176..247c18b05 100644
--- a/internal/cli/recall/cmd/show/run.go
+++ b/internal/cli/recall/cmd/show/run.go
@@ -19,7 +19,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/entity"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/session"
+ errSession "github.com/ActiveMemory/ctx/internal/err/session"
"github.com/ActiveMemory/ctx/internal/write/recall"
)
@@ -42,14 +42,14 @@ func Run(
) error {
sessions, scanErr := query.FindSessions(allProjects)
if scanErr != nil {
- return ctxErr.Find(scanErr)
+ return errSession.Find(scanErr)
}
if len(sessions) == 0 {
if allProjects {
- return ctxErr.NoneFound("")
+ return errSession.NoneFound("")
}
- return ctxErr.NoneFound(desc.Text(text.DescKeyLabelHintUseAllProjects))
+ return errSession.NoneFound(desc.Text(text.DescKeyLabelHintUseAllProjects))
}
var session *entity.Session
@@ -58,7 +58,7 @@ func Run(
case latest:
session = sessions[0]
case len(args) == 0:
- return ctxErr.IDRequired()
+ return errSession.IDRequired()
default:
query := strings.ToLower(args[0])
var matches []*entity.Session
@@ -69,14 +69,14 @@ func Run(
}
}
if len(matches) == 0 {
- return ctxErr.NotFound(args[0])
+ return errSession.NotFound(args[0])
}
if len(matches) > 1 {
lines := format.SessionMatchLines(matches)
recall.AmbiguousSessionMatchWithHint(
cmd, args[0], lines, matches[0].ID[:journal.SessionIDHintLen],
)
- return ctxErr.AmbiguousQuery()
+ return errSession.AmbiguousQuery()
}
session = matches[0]
}
diff --git a/internal/cli/recall/cmd/sync/run.go b/internal/cli/recall/cmd/sync/run.go
index 805c22be1..00f0604f9 100644
--- a/internal/cli/recall/cmd/sync/run.go
+++ b/internal/cli/recall/cmd/sync/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/dir"
"github.com/ActiveMemory/ctx/internal/config/journal"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/journal"
+ errJournal "github.com/ActiveMemory/ctx/internal/err/journal"
"github.com/ActiveMemory/ctx/internal/journal/state"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/write/recall"
@@ -33,7 +33,7 @@ func Run(cmd *cobra.Command) error {
jstate, loadErr := state.Load(journalDir)
if loadErr != nil {
- return ctxErr.LoadState(loadErr)
+ return errJournal.LoadState(loadErr)
}
files, matchErr := lock.MatchJournalFiles(journalDir, nil, true)
@@ -65,7 +65,7 @@ func Run(cmd *cobra.Command) error {
}
if saveErr := jstate.Save(journalDir); saveErr != nil {
- return ctxErr.SaveState(saveErr)
+ return errJournal.SaveState(saveErr)
}
recall.JournalSyncSummary(cmd, locked, unlocked)
diff --git a/internal/cli/recall/core/confirm/doc.go b/internal/cli/recall/core/confirm/doc.go
new file mode 100644
index 000000000..608d83999
--- /dev/null
+++ b/internal/cli/recall/core/confirm/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package confirm prompts the user to confirm an export plan before execution.
+package confirm
diff --git a/internal/cli/recall/core/execute/doc.go b/internal/cli/recall/core/execute/doc.go
new file mode 100644
index 000000000..2041f71f4
--- /dev/null
+++ b/internal/cli/recall/core/execute/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package execute writes journal files according to an export plan.
+package execute
diff --git a/internal/cli/recall/core/extract/doc.go b/internal/cli/recall/core/extract/doc.go
new file mode 100644
index 000000000..b4e04786d
--- /dev/null
+++ b/internal/cli/recall/core/extract/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package extract extracts YAML frontmatter blocks from Markdown content.
+package extract
diff --git a/internal/cli/recall/core/format/doc.go b/internal/cli/recall/core/format/doc.go
new file mode 100644
index 000000000..fa077e84d
--- /dev/null
+++ b/internal/cli/recall/core/format/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package format transforms session data into formatted journal Markdown with frontmatter and turn headers.
+package format
diff --git a/internal/cli/recall/core/frontmatter/doc.go b/internal/cli/recall/core/frontmatter/doc.go
new file mode 100644
index 000000000..388a2d953
--- /dev/null
+++ b/internal/cli/recall/core/frontmatter/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package frontmatter resolves headings and builds YAML frontmatter for journal entries.
+package frontmatter
diff --git a/internal/cli/recall/core/index/doc.go b/internal/cli/recall/core/index/doc.go
new file mode 100644
index 000000000..ef34d39dd
--- /dev/null
+++ b/internal/cli/recall/core/index/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package index builds a session-ID-to-filename index from existing journal files.
+package index
diff --git a/internal/cli/recall/core/lock/doc.go b/internal/cli/recall/core/lock/doc.go
new file mode 100644
index 000000000..887ee5e0a
--- /dev/null
+++ b/internal/cli/recall/core/lock/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package lock marks journal entries as locked to prevent re-export and manages multipart files.
+package lock
diff --git a/internal/cli/recall/core/lock/lock.go b/internal/cli/recall/core/lock/lock.go
index ab3dfa931..2ce46d763 100644
--- a/internal/cli/recall/core/lock/lock.go
+++ b/internal/cli/recall/core/lock/lock.go
@@ -21,7 +21,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/session"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/err/journal"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/session"
+ errSession "github.com/ActiveMemory/ctx/internal/err/session"
"github.com/ActiveMemory/ctx/internal/io"
"github.com/ActiveMemory/ctx/internal/journal/state"
"github.com/ActiveMemory/ctx/internal/rc"
@@ -244,7 +244,7 @@ func RunLockUnlock(
return cmd.Help()
}
if len(args) > 0 && all {
- return ctxErr.AllWithPattern()
+ return errSession.AllWithPattern()
}
journalDir := filepath.Join(rc.ContextDir(), dir.Journal)
diff --git a/internal/cli/recall/core/plan/doc.go b/internal/cli/recall/core/plan/doc.go
new file mode 100644
index 000000000..c4c0d0967
--- /dev/null
+++ b/internal/cli/recall/core/plan/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package plan builds an export plan that maps sessions to journal output files.
+package plan
diff --git a/internal/cli/recall/core/query/doc.go b/internal/cli/recall/core/query/doc.go
new file mode 100644
index 000000000..17ca9a780
--- /dev/null
+++ b/internal/cli/recall/core/query/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package query finds sessions for the current or all projects.
+package query
diff --git a/internal/cli/recall/core/query/query.go b/internal/cli/recall/core/query/query.go
index efbe7cd57..c57ea8705 100644
--- a/internal/cli/recall/core/query/query.go
+++ b/internal/cli/recall/core/query/query.go
@@ -10,7 +10,7 @@ import (
"os"
"github.com/ActiveMemory/ctx/internal/entity"
- errfs "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
"github.com/ActiveMemory/ctx/internal/recall/parser"
)
@@ -29,7 +29,7 @@ func FindSessions(allProjects bool) ([]*entity.Session, error) {
}
cwd, cwdErr := os.Getwd()
if cwdErr != nil {
- return nil, errfs.WorkingDirectory(cwdErr)
+ return nil, errFs.WorkingDirectory(cwdErr)
}
return parser.FindSessionsForCWD(cwd)
}
diff --git a/internal/cli/recall/core/slug/doc.go b/internal/cli/recall/core/slug/doc.go
new file mode 100644
index 000000000..70e3ef884
--- /dev/null
+++ b/internal/cli/recall/core/slug/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package slug converts human-readable titles into URL-friendly slugs for journal filenames.
+package slug
diff --git a/internal/cli/recall/core/validate/doc.go b/internal/cli/recall/core/validate/doc.go
new file mode 100644
index 000000000..c8f65330e
--- /dev/null
+++ b/internal/cli/recall/core/validate/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package validate checks messages and flag combinations for recall export validity.
+package validate
diff --git a/internal/cli/recall/core/validate/validate.go b/internal/cli/recall/core/validate/validate.go
index 0454f68a2..994020f27 100644
--- a/internal/cli/recall/core/validate/validate.go
+++ b/internal/cli/recall/core/validate/validate.go
@@ -9,7 +9,7 @@ package validate
import (
"github.com/ActiveMemory/ctx/internal/entity"
"github.com/ActiveMemory/ctx/internal/err/journal"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/session"
+ errSession "github.com/ActiveMemory/ctx/internal/err/session"
)
// EmptyMessage reports whether a message has no meaningful content
@@ -34,7 +34,7 @@ func EmptyMessage(msg entity.Message) bool {
// - error: non-nil if flags conflict.
func ExportFlags(args []string, opts entity.ExportOpts) error {
if len(args) > 0 && opts.All {
- return ctxErr.AllWithID()
+ return errSession.AllWithID()
}
if opts.Regenerate && !opts.All {
return journal.RegenerateRequiresAll()
diff --git a/internal/cli/remind/cmd/dismiss/cmd.go b/internal/cli/remind/cmd/dismiss/cmd.go
index 009ce105a..2a11b20df 100644
--- a/internal/cli/remind/cmd/dismiss/cmd.go
+++ b/internal/cli/remind/cmd/dismiss/cmd.go
@@ -13,7 +13,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/embed/cmd"
"github.com/ActiveMemory/ctx/internal/config/embed/flag"
cFlag "github.com/ActiveMemory/ctx/internal/config/flag"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/reminder"
+ errReminder "github.com/ActiveMemory/ctx/internal/err/reminder"
)
// Cmd returns the remind dismiss subcommand.
@@ -34,7 +34,7 @@ func Cmd() *cobra.Command {
return RunDismissAll(cmd)
}
if len(args) == 0 {
- return ctxErr.IDRequired()
+ return errReminder.IDRequired()
}
return RunDismiss(cmd, args[0])
},
diff --git a/internal/cli/remind/cmd/dismiss/run.go b/internal/cli/remind/cmd/dismiss/run.go
index 9074cee90..0ee4bb190 100644
--- a/internal/cli/remind/cmd/dismiss/run.go
+++ b/internal/cli/remind/cmd/dismiss/run.go
@@ -12,7 +12,7 @@ import (
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/cli/remind/core"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/reminder"
+ errReminder "github.com/ActiveMemory/ctx/internal/err/reminder"
"github.com/ActiveMemory/ctx/internal/write/remind"
)
@@ -27,7 +27,7 @@ import (
func RunDismiss(cmd *cobra.Command, idStr string) error {
id, parseErr := strconv.Atoi(idStr)
if parseErr != nil {
- return ctxErr.InvalidID(idStr)
+ return errReminder.InvalidID(idStr)
}
reminders, readErr := core.ReadReminders()
@@ -44,7 +44,7 @@ func RunDismiss(cmd *cobra.Command, idStr string) error {
}
if found < 0 {
- return ctxErr.NotFound(id)
+ return errReminder.NotFound(id)
}
remind.ReminderDismissed(cmd, reminders[found].ID, reminders[found].Message)
diff --git a/internal/cli/remind/core/store.go b/internal/cli/remind/core/store.go
index fd86a96af..41e19b7e7 100644
--- a/internal/cli/remind/core/store.go
+++ b/internal/cli/remind/core/store.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/config/reminder"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/reminder"
+ errReminder "github.com/ActiveMemory/ctx/internal/err/reminder"
"github.com/ActiveMemory/ctx/internal/io"
"github.com/ActiveMemory/ctx/internal/rc"
)
@@ -30,11 +30,11 @@ func ReadReminders() ([]Reminder, error) {
if errors.Is(readErr, os.ErrNotExist) {
return nil, nil
}
- return nil, ctxErr.Read(readErr)
+ return nil, errReminder.Read(readErr)
}
var reminders []Reminder
if parseErr := json.Unmarshal(data, &reminders); parseErr != nil {
- return nil, ctxErr.Parse(parseErr)
+ return nil, errReminder.Parse(parseErr)
}
return reminders, nil
}
diff --git a/internal/cli/resume/cmd/root/run.go b/internal/cli/resume/cmd/root/run.go
index 15162038c..bd311477d 100644
--- a/internal/cli/resume/cmd/root/run.go
+++ b/internal/cli/resume/cmd/root/run.go
@@ -10,7 +10,7 @@ import (
"os"
"github.com/ActiveMemory/ctx/internal/cli/system/core/nudge"
- session2 "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
+ coreSession "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/write/session"
@@ -26,7 +26,7 @@ import (
// - error: Always nil
func Run(cmd *cobra.Command, sessionID string) error {
if sessionID == "" {
- sessionID = session2.ReadSessionID(os.Stdin)
+ sessionID = coreSession.ReadSessionID(os.Stdin)
}
nudge.Resume(sessionID)
session.SessionResumed(cmd, sessionID)
diff --git a/internal/cli/serve/cmd/root/run.go b/internal/cli/serve/cmd/root/run.go
index 217687028..a3e68ac8c 100644
--- a/internal/cli/serve/cmd/root/run.go
+++ b/internal/cli/serve/cmd/root/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/dir"
"github.com/ActiveMemory/ctx/internal/config/zensical"
"github.com/ActiveMemory/ctx/internal/err/fs"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/site"
+ errSite "github.com/ActiveMemory/ctx/internal/err/site"
zensicalBin "github.com/ActiveMemory/ctx/internal/exec/zensical"
"github.com/ActiveMemory/ctx/internal/rc"
)
@@ -48,13 +48,13 @@ func Run(args []string) error {
// Check zensical.toml exists
tomlPath := filepath.Join(d, zensical.Toml)
if _, statErr = os.Stat(tomlPath); os.IsNotExist(statErr) {
- return ctxErr.NoConfig(d)
+ return errSite.NoConfig(d)
}
// Check if zensical is available
_, lookErr := exec.LookPath(zensical.Bin)
if lookErr != nil {
- return ctxErr.ZensicalNotFound()
+ return errSite.ZensicalNotFound()
}
return zensicalBin.Run(d)
diff --git a/internal/cli/serve/serve_test.go b/internal/cli/serve/serve_test.go
index 8ddb76030..db081a84a 100644
--- a/internal/cli/serve/serve_test.go
+++ b/internal/cli/serve/serve_test.go
@@ -12,10 +12,10 @@ import (
"strings"
"testing"
- serveroot "github.com/ActiveMemory/ctx/internal/cli/serve/cmd/root"
+ serveRoot "github.com/ActiveMemory/ctx/internal/cli/serve/cmd/root"
"github.com/ActiveMemory/ctx/internal/config/zensical"
"github.com/ActiveMemory/ctx/internal/err/fs"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/site"
+ errSite "github.com/ActiveMemory/ctx/internal/err/site"
)
func TestCmd(t *testing.T) {
@@ -60,7 +60,7 @@ func TestCmd_AcceptsArgs(t *testing.T) {
}
func TestRunServe_DirNotFound(t *testing.T) {
- err := serveroot.Run([]string{"/tmp/nonexistent-dir-ctx-test-xyz"})
+ err := serveRoot.Run([]string{"/tmp/nonexistent-dir-ctx-test-xyz"})
if err == nil {
t.Fatal("expected error for nonexistent directory")
}
@@ -77,7 +77,7 @@ func TestRunServe_NotADirectory(t *testing.T) {
defer func() { _ = os.Remove(tmpFile.Name()) }()
_ = tmpFile.Close()
- serveErr := serveroot.Run([]string{tmpFile.Name()})
+ serveErr := serveRoot.Run([]string{tmpFile.Name()})
if serveErr == nil {
t.Fatal("expected error for non-directory path")
}
@@ -93,7 +93,7 @@ func TestRunServe_NoSiteConfig(t *testing.T) {
}
defer func() { _ = os.RemoveAll(tmpDir) }()
- serveErr := serveroot.Run([]string{tmpDir})
+ serveErr := serveRoot.Run([]string{tmpDir})
if serveErr == nil {
t.Fatal("expected error for missing zensical.toml")
}
@@ -118,7 +118,7 @@ func TestRunServe_ZensicalNotFound(t *testing.T) {
// Ensure zensical is not in PATH
t.Setenv("PATH", "")
- serveErr := serveroot.Run([]string{tmpDir})
+ serveErr := serveRoot.Run([]string{tmpDir})
if serveErr == nil {
t.Fatal("expected error for missing zensical binary")
}
@@ -128,9 +128,9 @@ func TestRunServe_ZensicalNotFound(t *testing.T) {
}
func TestRunServe_DefaultDir(t *testing.T) {
- // When no args are given, serveroot.Run uses the default journal-site directory
+ // When no args are given, serveRoot.Run uses the default journal-site directory
// which won't exist in test, so we expect directory not found
- err := serveroot.Run([]string{})
+ err := serveRoot.Run([]string{})
if err == nil {
t.Fatal("expected error when default dir doesn't exist")
}
@@ -168,7 +168,7 @@ func TestErrNotDir(t *testing.T) {
}
func TestErrNoSiteConfig(t *testing.T) {
- err := ctxerr.NoConfig("/some/dir")
+ err := errSite.NoConfig("/some/dir")
if err == nil {
t.Fatal("expected non-nil error")
}
@@ -210,14 +210,14 @@ func TestRunServe_WithMockZensical(t *testing.T) {
origPath := os.Getenv("PATH")
t.Setenv("PATH", binDir+":"+origPath)
- serveErr := serveroot.Run([]string{tmpDir})
+ serveErr := serveRoot.Run([]string{tmpDir})
if serveErr != nil {
t.Errorf("unexpected error: %v", serveErr)
}
}
func TestCmd_RunE(t *testing.T) {
- // Test that Cmd().RunE actually invokes serveroot.Run via the command
+ // Test that Cmd().RunE actually invokes serveRoot.Run via the command
cmd := Cmd()
// Set args to a nonexistent dir so we get a predictable error
cmd.SetArgs([]string{"/tmp/nonexistent-ctx-test-xyz"})
@@ -234,7 +234,7 @@ func TestCmd_RunE(t *testing.T) {
}
func TestErrZensicalNotFound(t *testing.T) {
- err := ctxerr.ZensicalNotFound()
+ err := errSite.ZensicalNotFound()
if err == nil {
t.Fatal("expected non-nil error")
}
diff --git a/internal/cli/site/core/rss/doc.go b/internal/cli/site/core/rss/doc.go
new file mode 100644
index 000000000..abdf67d02
--- /dev/null
+++ b/internal/cli/site/core/rss/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package rss generates Atom XML feeds from blog post metadata.
+package rss
diff --git a/internal/cli/site/core/scan/doc.go b/internal/cli/site/core/scan/doc.go
new file mode 100644
index 000000000..3666a3e0f
--- /dev/null
+++ b/internal/cli/site/core/scan/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package scan reads blog post directories, parses frontmatter, and returns sorted post metadata.
+package scan
diff --git a/internal/cli/status/cmd/root/run.go b/internal/cli/status/cmd/root/run.go
index f283cff2a..b22e87f46 100644
--- a/internal/cli/status/cmd/root/run.go
+++ b/internal/cli/status/cmd/root/run.go
@@ -11,10 +11,10 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/status/core/out"
"github.com/ActiveMemory/ctx/internal/context/load"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
"github.com/spf13/cobra"
- errctx "github.com/ActiveMemory/ctx/internal/err/context"
+ errCtx "github.com/ActiveMemory/ctx/internal/err/context"
)
// Run executes the status command logic.
@@ -29,9 +29,9 @@ import (
func Run(cmd *cobra.Command, jsonOutput, verbose bool) error {
ctx, err := load.Do("")
if err != nil {
- var notFoundError *errctx.NotFoundError
+ var notFoundError *errCtx.NotFoundError
if errors.As(err, ¬FoundError) {
- return ctxerr.ContextNotInitialized()
+ return errInit.ContextNotInitialized()
}
return err
}
diff --git a/internal/cli/status/core/out/doc.go b/internal/cli/status/core/out/doc.go
new file mode 100644
index 000000000..4dd83c49d
--- /dev/null
+++ b/internal/cli/status/core/out/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package out writes context status as JSON or formatted text to command output.
+package out
diff --git a/internal/cli/status/core/preview/doc.go b/internal/cli/status/core/preview/doc.go
new file mode 100644
index 000000000..23f7dec66
--- /dev/null
+++ b/internal/cli/status/core/preview/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package preview extracts short content previews from context files for status display.
+package preview
diff --git a/internal/cli/status/core/sort/doc.go b/internal/cli/status/core/sort/doc.go
new file mode 100644
index 000000000..f7ae350f1
--- /dev/null
+++ b/internal/cli/status/core/sort/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package sort orders context files by priority and recency for status output.
+package sort
diff --git a/internal/cli/status/status.go b/internal/cli/status/status.go
index 6a832faa3..e889a9104 100644
--- a/internal/cli/status/status.go
+++ b/internal/cli/status/status.go
@@ -9,10 +9,10 @@ package status
import (
"github.com/spf13/cobra"
- statuRroot "github.com/ActiveMemory/ctx/internal/cli/status/cmd/root"
+ statusRoot "github.com/ActiveMemory/ctx/internal/cli/status/cmd/root"
)
// Cmd returns the status command.
func Cmd() *cobra.Command {
- return statuRroot.Cmd()
+ return statusRoot.Cmd()
}
diff --git a/internal/cli/sync/cmd/root/run.go b/internal/cli/sync/cmd/root/run.go
index 885c0214a..9ba94bf0d 100644
--- a/internal/cli/sync/cmd/root/run.go
+++ b/internal/cli/sync/cmd/root/run.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/context/load"
errCtx "github.com/ActiveMemory/ctx/internal/err/context"
- initErr "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
"github.com/ActiveMemory/ctx/internal/write/sync"
)
@@ -35,7 +35,7 @@ func Run(cmd *cobra.Command, dryRun bool) error {
if err != nil {
var notFoundError *errCtx.NotFoundError
if errors.As(err, ¬FoundError) {
- return initErr.ContextNotInitialized()
+ return errInit.ContextNotInitialized()
}
return err
}
diff --git a/internal/cli/sync/core/action/doc.go b/internal/cli/sync/core/action/doc.go
new file mode 100644
index 000000000..1190dfc24
--- /dev/null
+++ b/internal/cli/sync/core/action/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package action detects discrepancies between the codebase and context documentation.
+package action
diff --git a/internal/cli/sync/core/validate/doc.go b/internal/cli/sync/core/validate/doc.go
new file mode 100644
index 000000000..d16827b2f
--- /dev/null
+++ b/internal/cli/sync/core/validate/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package validate checks for undocumented package managers, config files, and directories.
+package validate
diff --git a/internal/cli/sync/core/validate/validate.go b/internal/cli/sync/core/validate/validate.go
index 148ba23bf..e27d1f157 100644
--- a/internal/cli/sync/core/validate/validate.go
+++ b/internal/cli/sync/core/validate/validate.go
@@ -15,10 +15,10 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/assets/read/lookup"
"github.com/ActiveMemory/ctx/internal/cli/sync/core"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/dep"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
- syncCfg "github.com/ActiveMemory/ctx/internal/config/sync"
+ cfgSync "github.com/ActiveMemory/ctx/internal/config/sync"
"github.com/ActiveMemory/ctx/internal/entity"
)
@@ -41,12 +41,12 @@ func CheckPackageFiles(ctx *entity.Context) []core.Action {
if _, err := os.Stat(f); err == nil {
// File exists, check if we have DEPENDENCIES.md or similar
hasDepsDoc := false
- if f := ctx.File(ctxCfg.Dependency); f != nil {
+ if f := ctx.File(cfgCtx.Dependency); f != nil {
hasDepsDoc = true
} else {
for _, f := range ctx.Files {
if strings.Contains(strings.ToLower(string(f.Content)),
- syncCfg.KeywordDependencies,
+ cfgSync.KeywordDependencies,
) {
hasDepsDoc = true
break
@@ -56,15 +56,15 @@ func CheckPackageFiles(ctx *entity.Context) []core.Action {
if !hasDepsDoc {
actions = append(actions, core.Action{
- Type: syncCfg.ActionDeps,
- File: ctxCfg.Architecture,
+ Type: cfgSync.ActionDeps,
+ File: cfgCtx.Architecture,
Description: fmt.Sprintf(
lookup.TextDesc(text.DescKeySyncDepsDescription),
f, d,
),
Suggestion: fmt.Sprintf(
lookup.TextDesc(text.DescKeySyncDepsSuggestion),
- ctxCfg.Architecture, ctxCfg.Dependency,
+ cfgCtx.Architecture, cfgCtx.Dependency,
),
})
}
@@ -93,7 +93,7 @@ func CheckConfigFiles(ctx *entity.Context) []core.Action {
if len(matches) > 0 {
// Check if CONVENTIONS.md mentions this
var convContent string
- if f := ctx.File(ctxCfg.Convention); f != nil {
+ if f := ctx.File(cfgCtx.Convention); f != nil {
convContent = strings.ToLower(string(f.Content))
}
@@ -101,15 +101,15 @@ func CheckConfigFiles(ctx *entity.Context) []core.Action {
keyword = strings.TrimSuffix(keyword, "*")
if convContent == "" || !strings.Contains(convContent, keyword) {
actions = append(actions, core.Action{
- Type: syncCfg.ActionConfig,
- File: ctxCfg.Convention,
+ Type: cfgSync.ActionConfig,
+ File: cfgCtx.Convention,
Description: fmt.Sprintf(
desc.Text(text.DescKeySyncConfigDescription),
matches[0], cfg.Topic,
),
Suggestion: fmt.Sprintf(
desc.Text(text.DescKeySyncConfigSuggestion),
- cfg.Topic, ctxCfg.Convention,
+ cfg.Topic, cfgCtx.Convention,
),
})
}
@@ -136,7 +136,7 @@ func CheckNewDirectories(ctx *entity.Context) []core.Action {
// Get ARCHITECTURE.md content
var archContent string
- if f := ctx.File(ctxCfg.Architecture); f != nil {
+ if f := ctx.File(cfgCtx.Architecture); f != nil {
archContent = strings.ToLower(string(f.Content))
}
@@ -151,21 +151,21 @@ func CheckNewDirectories(ctx *entity.Context) []core.Action {
continue
}
name := entry.Name()
- if strings.HasPrefix(name, ".") || syncCfg.SkipDirs[name] {
+ if strings.HasPrefix(name, ".") || cfgSync.SkipDirs[name] {
continue
}
- if syncCfg.ImportantDirs[name] && !strings.Contains(archContent, name) {
+ if cfgSync.ImportantDirs[name] && !strings.Contains(archContent, name) {
actions = append(actions, core.Action{
- Type: syncCfg.ActionNewDir,
- File: ctxCfg.Architecture,
+ Type: cfgSync.ActionNewDir,
+ File: cfgCtx.Architecture,
Description: fmt.Sprintf(
desc.Text(text.DescKeySyncDirDescription),
name,
),
Suggestion: fmt.Sprintf(
desc.Text(text.DescKeySyncDirSuggestion),
- name, ctxCfg.Architecture,
+ name, cfgCtx.Architecture,
),
})
}
diff --git a/internal/cli/system/cmd/backup/run.go b/internal/cli/system/cmd/backup/run.go
index 3e21ed8b6..808c5a7c7 100644
--- a/internal/cli/system/cmd/backup/run.go
+++ b/internal/cli/system/cmd/backup/run.go
@@ -11,7 +11,7 @@ import (
"os"
"time"
- archive2 "github.com/ActiveMemory/ctx/internal/cli/system/core/archive"
+ coreArchive "github.com/ActiveMemory/ctx/internal/cli/system/core/archive"
"github.com/ActiveMemory/ctx/internal/entity"
"github.com/spf13/cobra"
@@ -19,7 +19,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/env"
cFlag "github.com/ActiveMemory/ctx/internal/config/flag"
errBackup "github.com/ActiveMemory/ctx/internal/err/backup"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/initialize"
+ errInit "github.com/ActiveMemory/ctx/internal/err/initialize"
"github.com/ActiveMemory/ctx/internal/write/backup"
)
@@ -46,15 +46,15 @@ func Run(cmd *cobra.Command) error {
home, homeErr := os.UserHomeDir()
if homeErr != nil {
- return ctxErr.HomeDir(homeErr)
+ return errInit.HomeDir(homeErr)
}
smbURL := os.Getenv(env.BackupSMBURL)
smbSubdir := os.Getenv(env.BackupSMBSubdir)
- var smb *archive2.SMBConfig
+ var smb *coreArchive.SMBConfig
if smbURL != "" {
var smbErr error
- smb, smbErr = archive2.ParseSMBConfig(smbURL, smbSubdir)
+ smb, smbErr = coreArchive.ParseSMBConfig(smbURL, smbSubdir)
if smbErr != nil {
return errBackup.SMBConfig(smbErr)
}
@@ -64,7 +64,7 @@ func Run(cmd *cobra.Command) error {
var results []entity.BackupResult
if scope == archive.BackupScopeProject || scope == archive.BackupScopeAll {
- result, projErr := archive2.BackupProject(cmd.ErrOrStderr(), home, timestamp, smb)
+ result, projErr := coreArchive.BackupProject(cmd.ErrOrStderr(), home, timestamp, smb)
if projErr != nil {
return errBackup.Project(projErr)
}
@@ -72,7 +72,7 @@ func Run(cmd *cobra.Command) error {
}
if scope == archive.BackupScopeGlobal || scope == archive.BackupScopeAll {
- result, globalErr := archive2.BackupGlobal(cmd.ErrOrStderr(), home, timestamp, smb)
+ result, globalErr := coreArchive.BackupGlobal(cmd.ErrOrStderr(), home, timestamp, smb)
if globalErr != nil {
return errBackup.Global(globalErr)
}
diff --git a/internal/cli/system/cmd/bootstrap/run.go b/internal/cli/system/cmd/bootstrap/run.go
index 108db15b6..4f1cfbde3 100644
--- a/internal/cli/system/cmd/bootstrap/run.go
+++ b/internal/cli/system/cmd/bootstrap/run.go
@@ -9,7 +9,7 @@ package bootstrap
import (
"os"
- bootstrap2 "github.com/ActiveMemory/ctx/internal/cli/system/core/bootstrap"
+ coreBootstrap "github.com/ActiveMemory/ctx/internal/cli/system/core/bootstrap"
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
@@ -43,14 +43,14 @@ func Run(cmd *cobra.Command) error {
return nil
}
- files := bootstrap2.ListContextFiles(dir)
- rules := bootstrap2.ParseNumberedLines(
+ files := coreBootstrap.ListContextFiles(dir)
+ rules := coreBootstrap.ParseNumberedLines(
desc.Text(text.DescKeyBootstrapRules),
)
- nextSteps := bootstrap2.ParseNumberedLines(
+ nextSteps := coreBootstrap.ParseNumberedLines(
desc.Text(text.DescKeyBootstrapNextSteps),
)
- warning := bootstrap2.PluginWarning()
+ warning := coreBootstrap.PluginWarning()
jsonFlag, _ := cmd.Flags().GetBool(cFlag.JSON)
if jsonFlag {
@@ -58,7 +58,7 @@ func Run(cmd *cobra.Command) error {
return nil
}
- fileList := bootstrap2.WrapFileList(
+ fileList := coreBootstrap.WrapFileList(
files,
cfgBootstrap.BootstrapFileListWidth,
cfgBootstrap.BootstrapFileListIndent,
diff --git a/internal/cli/system/cmd/check_journal/run.go b/internal/cli/system/cmd/check_journal/run.go
index bf224db8e..442d97b24 100644
--- a/internal/cli/system/cmd/check_journal/run.go
+++ b/internal/cli/system/cmd/check_journal/run.go
@@ -11,8 +11,8 @@ import (
"os"
"path/filepath"
- hook2 "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
- journal2 "github.com/ActiveMemory/ctx/internal/cli/system/core/journal"
+ coreCheck "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
+ coreJournal "github.com/ActiveMemory/ctx/internal/cli/system/core/journal"
"github.com/ActiveMemory/ctx/internal/cli/system/core/message"
"github.com/ActiveMemory/ctx/internal/cli/system/core/nudge"
"github.com/ActiveMemory/ctx/internal/cli/system/core/state"
@@ -46,7 +46,7 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
if !state.Initialized() {
return nil
}
- input, _, paused := hook2.Preamble(stdin)
+ input, _, paused := coreCheck.Preamble(stdin)
if paused {
return nil
}
@@ -58,7 +58,7 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
)
// Only remind once per day
- if hook2.DailyThrottled(remindedFile) {
+ if coreCheck.DailyThrottled(remindedFile) {
return nil
}
@@ -72,13 +72,13 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
}
// Stage 1: Unexported sessions
- newestJournal := journal2.NewestMtime(jDir, file.ExtMarkdown)
- unexported := journal2.CountNewerFiles(
+ newestJournal := coreJournal.NewestMtime(jDir, file.ExtMarkdown)
+ unexported := coreJournal.CountNewerFiles(
claudeProjectsDir, file.ExtJSONL, newestJournal,
)
// Stage 2: Unenriched entries
- unenriched := journal2.CountUnenriched(jDir)
+ unenriched := coreJournal.CountUnenriched(jDir)
if unexported == 0 && unenriched == 0 {
return nil
diff --git a/internal/cli/system/cmd/check_knowledge/run.go b/internal/cli/system/cmd/check_knowledge/run.go
index 5a46570c5..b6475ea6c 100644
--- a/internal/cli/system/cmd/check_knowledge/run.go
+++ b/internal/cli/system/cmd/check_knowledge/run.go
@@ -11,7 +11,7 @@ import (
"path/filepath"
"github.com/ActiveMemory/ctx/internal/cli/system/core/check"
- knowledge2 "github.com/ActiveMemory/ctx/internal/cli/system/core/knowledge"
+ coreKnowledge "github.com/ActiveMemory/ctx/internal/cli/system/core/knowledge"
"github.com/ActiveMemory/ctx/internal/cli/system/core/state"
"github.com/spf13/cobra"
@@ -48,7 +48,7 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
return nil
}
- if box, warned := knowledge2.CheckKnowledgeHealth(sessionID); warned {
+ if box, warned := coreKnowledge.CheckKnowledgeHealth(sessionID); warned {
writeHook.Nudge(cmd, box)
internalIo.TouchFile(markerPath)
}
diff --git a/internal/cli/system/cmd/check_memory_drift/run.go b/internal/cli/system/cmd/check_memory_drift/run.go
index d456643e6..b248c4400 100644
--- a/internal/cli/system/cmd/check_memory_drift/run.go
+++ b/internal/cli/system/cmd/check_memory_drift/run.go
@@ -10,7 +10,7 @@ import (
"os"
"path/filepath"
- hook2 "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
+ coreCheck "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
"github.com/ActiveMemory/ctx/internal/cli/system/core/nudge"
"github.com/ActiveMemory/ctx/internal/cli/system/core/state"
"github.com/spf13/cobra"
@@ -28,7 +28,7 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
return nil
}
- input, sessionID, paused := hook2.Preamble(stdin)
+ input, sessionID, paused := coreCheck.Preamble(stdin)
if paused {
return nil
}
diff --git a/internal/cli/system/cmd/check_reminder/run.go b/internal/cli/system/cmd/check_reminder/run.go
index 10529a38b..ef50a5df0 100644
--- a/internal/cli/system/cmd/check_reminder/run.go
+++ b/internal/cli/system/cmd/check_reminder/run.go
@@ -11,7 +11,7 @@ import (
"os"
"time"
- hook2 "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
+ coreCheck "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
"github.com/ActiveMemory/ctx/internal/cli/system/core/message"
"github.com/ActiveMemory/ctx/internal/cli/system/core/nudge"
"github.com/ActiveMemory/ctx/internal/cli/system/core/state"
@@ -45,7 +45,7 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
return nil
}
- input, _, paused := hook2.Preamble(stdin)
+ input, _, paused := coreCheck.Preamble(stdin)
if paused {
return nil
}
diff --git a/internal/cli/system/cmd/events/run.go b/internal/cli/system/cmd/events/run.go
index 12beb1a1d..879575355 100644
--- a/internal/cli/system/cmd/events/run.go
+++ b/internal/cli/system/cmd/events/run.go
@@ -11,7 +11,7 @@ import (
coreEvent "github.com/ActiveMemory/ctx/internal/cli/system/core/event"
cFlag "github.com/ActiveMemory/ctx/internal/config/flag"
- errRcall "github.com/ActiveMemory/ctx/internal/err/recall"
+ errRecall "github.com/ActiveMemory/ctx/internal/err/recall"
"github.com/ActiveMemory/ctx/internal/log"
writeEvents "github.com/ActiveMemory/ctx/internal/write/events"
)
@@ -42,7 +42,7 @@ func Run(cmd *cobra.Command) error {
evts, queryErr := log.Query(opts)
if queryErr != nil {
- return errRcall.EventLogRead(queryErr)
+ return errRecall.EventLogRead(queryErr)
}
if len(evts) == 0 {
diff --git a/internal/cli/system/cmd/heartbeat/run.go b/internal/cli/system/cmd/heartbeat/run.go
index a5e2312d3..95e52e479 100644
--- a/internal/cli/system/cmd/heartbeat/run.go
+++ b/internal/cli/system/cmd/heartbeat/run.go
@@ -11,10 +11,10 @@ import (
"os"
"path/filepath"
- hook2 "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
+ coreCheck "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
"github.com/ActiveMemory/ctx/internal/cli/system/core/counter"
- heartbeat2 "github.com/ActiveMemory/ctx/internal/cli/system/core/heartbeat"
- log2 "github.com/ActiveMemory/ctx/internal/cli/system/core/log"
+ coreHeartbeat "github.com/ActiveMemory/ctx/internal/cli/system/core/heartbeat"
+ coreLog "github.com/ActiveMemory/ctx/internal/cli/system/core/log"
"github.com/ActiveMemory/ctx/internal/cli/system/core/session"
"github.com/ActiveMemory/ctx/internal/cli/system/core/state"
"github.com/ActiveMemory/ctx/internal/cli/system/core/time"
@@ -48,7 +48,7 @@ func Run(_ *cobra.Command, stdin *os.File) error {
if !state.Initialized() {
return nil
}
- _, sessionID, paused := hook2.Preamble(stdin)
+ _, sessionID, paused := coreCheck.Preamble(stdin)
if paused {
return nil
}
@@ -69,9 +69,9 @@ func Run(_ *cobra.Command, stdin *os.File) error {
// Detect context modification since the last heartbeat.
currentMtime := time.GetLatestContextMtime(contextDir)
- lastMtime := heartbeat2.ReadMtime(mtimeFile)
+ lastMtime := coreHeartbeat.ReadMtime(mtimeFile)
contextModified := currentMtime > lastMtime
- heartbeat2.WriteMtime(mtimeFile, currentMtime)
+ coreHeartbeat.WriteMtime(mtimeFile, currentMtime)
// Read token usage for this session.
info, _ := session.ReadSessionTokenInfo(sessionID)
@@ -113,7 +113,7 @@ func Run(_ *cobra.Command, stdin *os.File) error {
logLine = fmt.Sprintf(desc.Text(text.DescKeyHeartbeatLogPlain),
count, contextModified)
}
- log2.Message(logFile, sessionID, logLine)
+ coreLog.Message(logFile, sessionID, logLine)
// No stdout - agent never sees this hook.
return nil
diff --git a/internal/cli/system/cmd/message/cmd/edit/doc.go b/internal/cli/system/cmd/message/cmd/edit/doc.go
new file mode 100644
index 000000000..e95fbeb28
--- /dev/null
+++ b/internal/cli/system/cmd/message/cmd/edit/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package edit provides the ctx system message edit subcommand for customizing hook templates.
+package edit
diff --git a/internal/cli/system/cmd/message/cmd/edit/run.go b/internal/cli/system/cmd/message/cmd/edit/run.go
index 0ba8d2aba..dc5171233 100644
--- a/internal/cli/system/cmd/message/cmd/edit/run.go
+++ b/internal/cli/system/cmd/message/cmd/edit/run.go
@@ -17,7 +17,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/hook"
"github.com/ActiveMemory/ctx/internal/config/file"
"github.com/ActiveMemory/ctx/internal/err/fs"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/hook"
+ errHook "github.com/ActiveMemory/ctx/internal/err/hook"
writeMessage "github.com/ActiveMemory/ctx/internal/write/message"
)
@@ -34,13 +34,13 @@ import (
func Run(cmd *cobra.Command, hk, variant string) error {
info := messages.Lookup(hk, variant)
if info == nil {
- return ctxErr.Validate(messages.Variants(hk) != nil, hk, variant)
+ return errHook.Validate(messages.Variants(hk) != nil, hk, variant)
}
oPath := message.OverridePath(hk, variant)
if _, statErr := os.Stat(oPath); statErr == nil {
- return ctxErr.OverrideExists(oPath, hk, variant)
+ return errHook.OverrideExists(oPath, hk, variant)
}
if info.Category == messages.CategoryCtxSpecific {
@@ -49,7 +49,7 @@ func Run(cmd *cobra.Command, hk, variant string) error {
data, readErr := hook.Message(hk, variant+file.ExtTxt)
if readErr != nil {
- return ctxErr.EmbeddedTemplateNotFound(hk, variant)
+ return errHook.EmbeddedTemplateNotFound(hk, variant)
}
dir := filepath.Dir(oPath)
@@ -58,7 +58,7 @@ func Run(cmd *cobra.Command, hk, variant string) error {
}
if writeErr := os.WriteFile(oPath, data, 0o600); writeErr != nil {
- return ctxErr.WriteOverride(oPath, writeErr)
+ return errHook.WriteOverride(oPath, writeErr)
}
writeMessage.OverrideCreated(cmd, oPath)
diff --git a/internal/cli/system/cmd/message/cmd/list/doc.go b/internal/cli/system/cmd/message/cmd/list/doc.go
new file mode 100644
index 000000000..df0ada529
--- /dev/null
+++ b/internal/cli/system/cmd/message/cmd/list/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package list provides the ctx system message list subcommand for displaying available hook messages.
+package list
diff --git a/internal/cli/system/cmd/message/cmd/reset/doc.go b/internal/cli/system/cmd/message/cmd/reset/doc.go
new file mode 100644
index 000000000..b461cdba0
--- /dev/null
+++ b/internal/cli/system/cmd/message/cmd/reset/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package reset provides the ctx system message reset subcommand for restoring default hook templates.
+package reset
diff --git a/internal/cli/system/cmd/message/cmd/reset/run.go b/internal/cli/system/cmd/message/cmd/reset/run.go
index d43b912a6..8604adbbc 100644
--- a/internal/cli/system/cmd/message/cmd/reset/run.go
+++ b/internal/cli/system/cmd/message/cmd/reset/run.go
@@ -14,7 +14,7 @@ import (
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/assets/hooks/messages"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/hook"
+ errHook "github.com/ActiveMemory/ctx/internal/err/hook"
writeMessage "github.com/ActiveMemory/ctx/internal/write/message"
)
@@ -30,7 +30,7 @@ import (
func Run(cmd *cobra.Command, hk, variant string) error {
info := messages.Lookup(hk, variant)
if info == nil {
- return ctxErr.Validate(messages.Variants(hk) != nil, hk, variant)
+ return errHook.Validate(messages.Variants(hk) != nil, hk, variant)
}
oPath := message.OverridePath(hk, variant)
@@ -40,7 +40,7 @@ func Run(cmd *cobra.Command, hk, variant string) error {
writeMessage.NoOverride(cmd, hk, variant)
return nil
}
- return ctxErr.RemoveOverride(oPath, removeErr)
+ return errHook.RemoveOverride(oPath, removeErr)
}
hookDir := filepath.Dir(oPath)
diff --git a/internal/cli/system/cmd/message/cmd/show/doc.go b/internal/cli/system/cmd/message/cmd/show/doc.go
new file mode 100644
index 000000000..63130135e
--- /dev/null
+++ b/internal/cli/system/cmd/message/cmd/show/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package show provides the ctx system message show subcommand for viewing hook template content.
+package show
diff --git a/internal/cli/system/cmd/message/cmd/show/run.go b/internal/cli/system/cmd/message/cmd/show/run.go
index 341781f56..42747144c 100644
--- a/internal/cli/system/cmd/message/cmd/show/run.go
+++ b/internal/cli/system/cmd/message/cmd/show/run.go
@@ -11,9 +11,9 @@ import (
"github.com/spf13/cobra"
"github.com/ActiveMemory/ctx/internal/assets/hooks/messages"
- hook "github.com/ActiveMemory/ctx/internal/assets/read/hook"
+ readHook "github.com/ActiveMemory/ctx/internal/assets/read/hook"
"github.com/ActiveMemory/ctx/internal/config/file"
- ctxErr "github.com/ActiveMemory/ctx/internal/err/hook"
+ errHook "github.com/ActiveMemory/ctx/internal/err/hook"
"github.com/ActiveMemory/ctx/internal/io"
writeMessage "github.com/ActiveMemory/ctx/internal/write/message"
)
@@ -30,7 +30,7 @@ import (
func Run(cmd *cobra.Command, hk, variant string) error {
info := messages.Lookup(hk, variant)
if info == nil {
- return ctxErr.Validate(messages.Variants(hk) != nil, hk, variant)
+ return errHook.Validate(messages.Variants(hk) != nil, hk, variant)
}
oPath := message.OverridePath(hk, variant)
@@ -41,9 +41,9 @@ func Run(cmd *cobra.Command, hk, variant string) error {
return nil
}
- data, readErr := hook.Message(hk, variant+file.ExtTxt)
+ data, readErr := readHook.Message(hk, variant+file.ExtTxt)
if readErr != nil {
- return ctxErr.EmbeddedTemplateNotFound(hk, variant)
+ return errHook.EmbeddedTemplateNotFound(hk, variant)
}
writeMessage.SourceDefault(cmd)
diff --git a/internal/cli/system/cmd/message/doc.go b/internal/cli/system/cmd/message/doc.go
new file mode 100644
index 000000000..274f26cf8
--- /dev/null
+++ b/internal/cli/system/cmd/message/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package message provides the parent command for ctx system message.
+package message
diff --git a/internal/cli/system/cmd/message/message.go b/internal/cli/system/cmd/message/message.go
index 813758dda..2b625f9b5 100644
--- a/internal/cli/system/cmd/message/message.go
+++ b/internal/cli/system/cmd/message/message.go
@@ -4,7 +4,6 @@
// \ Copyright 2026-present Context contributors.
// SPDX-License-Identifier: Apache-2.0
-// Package message provides the parent command for ctx system message.
package message
import (
diff --git a/internal/cli/system/cmd/post_commit/run.go b/internal/cli/system/cmd/post_commit/run.go
index d83c473c4..540e44e67 100644
--- a/internal/cli/system/cmd/post_commit/run.go
+++ b/internal/cli/system/cmd/post_commit/run.go
@@ -11,7 +11,7 @@ import (
"os"
"regexp"
- hook2 "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
+ coreCheck "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
"github.com/ActiveMemory/ctx/internal/cli/system/core/drift"
"github.com/ActiveMemory/ctx/internal/cli/system/core/message"
"github.com/ActiveMemory/ctx/internal/cli/system/core/nudge"
@@ -48,7 +48,7 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
if !state.Initialized() {
return nil
}
- input, sessionID, paused := hook2.Preamble(stdin)
+ input, sessionID, paused := coreCheck.Preamble(stdin)
if paused {
return nil
}
diff --git a/internal/cli/system/cmd/prune/run.go b/internal/cli/system/cmd/prune/run.go
index c526b1858..f5eaf2e58 100644
--- a/internal/cli/system/cmd/prune/run.go
+++ b/internal/cli/system/cmd/prune/run.go
@@ -13,8 +13,8 @@ import (
"github.com/ActiveMemory/ctx/internal/cli/system/core/health"
"github.com/ActiveMemory/ctx/internal/cli/system/core/state"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/state"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
+ errState "github.com/ActiveMemory/ctx/internal/err/state"
"github.com/ActiveMemory/ctx/internal/write/prune"
"github.com/spf13/cobra"
)
@@ -37,10 +37,10 @@ func Run(cmd *cobra.Command, days int, dryRun bool) error {
entries, readErr := os.ReadDir(dir)
if readErr != nil {
- return ctxerr.ReadingDir(readErr)
+ return errState.ReadingDir(readErr)
}
- cutoff := time.Now().Add(-time.Duration(days) * time2.HoursPerDay * time.Hour)
+ cutoff := time.Now().Add(-time.Duration(days) * cfgTime.HoursPerDay * time.Hour)
var pruned, skipped, preserved int
for _, entry := range entries {
diff --git a/internal/cli/system/cmd/qa_reminder/run.go b/internal/cli/system/cmd/qa_reminder/run.go
index e739db6de..5e6eabf30 100644
--- a/internal/cli/system/cmd/qa_reminder/run.go
+++ b/internal/cli/system/cmd/qa_reminder/run.go
@@ -11,7 +11,7 @@ import (
"os"
"strings"
- hook2 "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
+ coreCheck "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
"github.com/ActiveMemory/ctx/internal/cli/system/core/message"
"github.com/ActiveMemory/ctx/internal/cli/system/core/nudge"
coreSession "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
@@ -41,7 +41,7 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
if !state.Initialized() {
return nil
}
- input, _, paused := hook2.Preamble(stdin)
+ input, _, paused := coreCheck.Preamble(stdin)
if paused {
return nil
}
diff --git a/internal/cli/system/cmd/resume/run.go b/internal/cli/system/cmd/resume/run.go
index 045bd6d27..8a1553909 100644
--- a/internal/cli/system/cmd/resume/run.go
+++ b/internal/cli/system/cmd/resume/run.go
@@ -15,7 +15,7 @@ import (
coreSession "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
cFlag "github.com/ActiveMemory/ctx/internal/config/flag"
"github.com/ActiveMemory/ctx/internal/config/session"
- session2 "github.com/ActiveMemory/ctx/internal/write/session"
+ writeSession "github.com/ActiveMemory/ctx/internal/write/session"
)
// Run executes the resume logic.
@@ -41,6 +41,6 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
path := nudge.PauseMarkerPath(sessionID)
_ = os.Remove(path)
- session2.SessionResumed(cmd, sessionID)
+ writeSession.SessionResumed(cmd, sessionID)
return nil
}
diff --git a/internal/cli/system/cmd/specs_nudge/run.go b/internal/cli/system/cmd/specs_nudge/run.go
index 93a900418..877c7245e 100644
--- a/internal/cli/system/cmd/specs_nudge/run.go
+++ b/internal/cli/system/cmd/specs_nudge/run.go
@@ -10,7 +10,7 @@ import (
"fmt"
"os"
- hook2 "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
+ coreCheck "github.com/ActiveMemory/ctx/internal/cli/system/core/check"
"github.com/ActiveMemory/ctx/internal/cli/system/core/message"
"github.com/ActiveMemory/ctx/internal/cli/system/core/nudge"
coreSession "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
@@ -41,7 +41,7 @@ func Run(cmd *cobra.Command, stdin *os.File) error {
if !state.Initialized() {
return nil
}
- input, _, paused := hook2.Preamble(stdin)
+ input, _, paused := coreCheck.Preamble(stdin)
if paused {
return nil
}
diff --git a/internal/cli/system/core/archive/backup.go b/internal/cli/system/core/archive/backup.go
index 7533c2b39..a33cb3bdc 100644
--- a/internal/cli/system/core/archive/backup.go
+++ b/internal/cli/system/core/archive/backup.go
@@ -19,7 +19,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/archive"
"github.com/ActiveMemory/ctx/internal/config/dir"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
- configFs "github.com/ActiveMemory/ctx/internal/config/fs"
+ cfgFs "github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/entity"
errBackup "github.com/ActiveMemory/ctx/internal/err/backup"
internalIo "github.com/ActiveMemory/ctx/internal/io"
@@ -37,7 +37,7 @@ import (
func CreateArchive(
archivePath string, entries []entity.ArchiveEntry, w io.Writer,
) error {
- outFile, createErr := internalIo.SafeCreateFile(archivePath, configFs.PermFile)
+ outFile, createErr := internalIo.SafeCreateFile(archivePath, cfgFs.PermFile)
if createErr != nil {
return errBackup.CreateArchive(createErr)
}
@@ -95,7 +95,7 @@ func BackupProject(
// Touch marker file for check-backup-age hook.
markerDir := filepath.Join(home, archive.BackupMarkerDir)
- _ = os.MkdirAll(markerDir, configFs.PermExec)
+ _ = os.MkdirAll(markerDir, cfgFs.PermExec)
markerPath := filepath.Join(markerDir, archive.BackupMarkerFile)
internalIo.TouchFile(markerPath)
diff --git a/internal/cli/system/core/archive/doc.go b/internal/cli/system/core/archive/doc.go
new file mode 100644
index 000000000..53011e648
--- /dev/null
+++ b/internal/cli/system/core/archive/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package archive creates compressed tar archives of context data with optional SMB backup.
+package archive
diff --git a/internal/cli/system/core/archive/smb.go b/internal/cli/system/core/archive/smb.go
index 90474cde5..a2b0a8fa7 100644
--- a/internal/cli/system/core/archive/smb.go
+++ b/internal/cli/system/core/archive/smb.go
@@ -17,8 +17,8 @@ import (
"github.com/ActiveMemory/ctx/internal/config/archive"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/fs"
- backupErr "github.com/ActiveMemory/ctx/internal/err/backup"
- fserr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errBackup "github.com/ActiveMemory/ctx/internal/err/backup"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
"github.com/ActiveMemory/ctx/internal/io"
)
@@ -44,7 +44,7 @@ type SMBConfig struct {
func ParseSMBConfig(smbURL, subdir string) (*SMBConfig, error) {
u, parseErr := url.Parse(smbURL)
if parseErr != nil || u.Host == "" {
- return nil, backupErr.InvalidSMBURL(smbURL)
+ return nil, errBackup.InvalidSMBURL(smbURL)
}
host := u.Host
@@ -53,7 +53,7 @@ func ParseSMBConfig(smbURL, subdir string) (*SMBConfig, error) {
share = share[1:]
}
if share == "" {
- return nil, backupErr.SMBMissingShare(smbURL)
+ return nil, errBackup.SMBMissingShare(smbURL)
}
if subdir == "" {
@@ -87,7 +87,7 @@ func EnsureSMBMount(cfg *SMBConfig) error {
//nolint:gosec // G204: smbURL is from user env, not untrusted input
mountCmd := exec.Command("gio", "mount", cfg.SourceURL)
if mountErr := mountCmd.Run(); mountErr != nil {
- return backupErr.MountFailed(cfg.SourceURL, mountErr)
+ return errBackup.MountFailed(cfg.SourceURL, mountErr)
}
return nil
@@ -104,17 +104,17 @@ func EnsureSMBMount(cfg *SMBConfig) error {
func CopyToSMB(cfg *SMBConfig, localPath string) error {
dest := filepath.Join(cfg.GVFSPath, cfg.Subdir)
if mkdirErr := os.MkdirAll(dest, fs.PermExec); mkdirErr != nil {
- return fserr.CreateDir(dest, mkdirErr)
+ return errFs.CreateDir(dest, mkdirErr)
}
data, readErr := io.SafeReadUserFile(localPath)
if readErr != nil {
- return fserr.ReadFile(readErr)
+ return errFs.ReadFile(readErr)
}
destFile := filepath.Join(dest, filepath.Base(localPath))
if writeErr := os.WriteFile(destFile, data, fs.PermFile); writeErr != nil {
- return backupErr.WriteSMB(writeErr)
+ return errBackup.WriteSMB(writeErr)
}
return nil
diff --git a/internal/cli/system/core/bootstrap/doc.go b/internal/cli/system/core/bootstrap/doc.go
new file mode 100644
index 000000000..db043ed92
--- /dev/null
+++ b/internal/cli/system/core/bootstrap/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package bootstrap checks plugin installation status and emits warnings during context bootstrap.
+package bootstrap
diff --git a/internal/cli/system/core/ceremony/doc.go b/internal/cli/system/core/ceremony/doc.go
new file mode 100644
index 000000000..74beec55e
--- /dev/null
+++ b/internal/cli/system/core/ceremony/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package ceremony assembles hook ceremony output from recent journal files and message templates.
+package ceremony
diff --git a/internal/cli/system/core/check/doc.go b/internal/cli/system/core/check/doc.go
new file mode 100644
index 000000000..0bf896fcb
--- /dev/null
+++ b/internal/cli/system/core/check/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package check provides shared preamble logic and throttling for hook input processing.
+package check
diff --git a/internal/cli/system/core/counter/doc.go b/internal/cli/system/core/counter/doc.go
new file mode 100644
index 000000000..bfd09f2a4
--- /dev/null
+++ b/internal/cli/system/core/counter/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package counter reads and writes integer counters persisted in state files.
+package counter
diff --git a/internal/cli/system/core/drift/doc.go b/internal/cli/system/core/drift/doc.go
new file mode 100644
index 000000000..84988cb6d
--- /dev/null
+++ b/internal/cli/system/core/drift/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package drift detects version drift across VERSION, plugin, and marketplace files and reports stale context entries.
+package drift
diff --git a/internal/cli/system/core/event/doc.go b/internal/cli/system/core/event/doc.go
new file mode 100644
index 000000000..6212effea
--- /dev/null
+++ b/internal/cli/system/core/event/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package event formats and renders event log timestamps and entries.
+package event
diff --git a/internal/cli/system/core/health/doc.go b/internal/cli/system/core/health/doc.go
new file mode 100644
index 000000000..426b104bb
--- /dev/null
+++ b/internal/cli/system/core/health/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package health monitors context health by detecting stale architecture maps and pruning expired state files.
+package health
diff --git a/internal/cli/system/core/health/prune.go b/internal/cli/system/core/health/prune.go
index a911aa059..52c2493c8 100644
--- a/internal/cli/system/core/health/prune.go
+++ b/internal/cli/system/core/health/prune.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/cli/system/core/state"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
)
// UUIDPattern matches a UUID (v4) anywhere in a filename.
@@ -42,7 +42,7 @@ func AutoPrune(days int) int {
return 0
}
- cutoff := time.Now().Add(-time.Duration(days) * time2.HoursPerDay * time.Hour)
+ cutoff := time.Now().Add(-time.Duration(days) * cfgTime.HoursPerDay * time.Hour)
var pruned int
for _, entry := range entries {
diff --git a/internal/cli/system/core/heartbeat/doc.go b/internal/cli/system/core/heartbeat/doc.go
new file mode 100644
index 000000000..3011ba674
--- /dev/null
+++ b/internal/cli/system/core/heartbeat/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package heartbeat reads and writes mtime values for session heartbeat tracking.
+package heartbeat
diff --git a/internal/cli/system/core/journal/doc.go b/internal/cli/system/core/journal/doc.go
new file mode 100644
index 000000000..5ebeeefc9
--- /dev/null
+++ b/internal/cli/system/core/journal/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package journal scans journal directories for the newest file modification time and manages journal state.
+package journal
diff --git a/internal/cli/system/core/knowledge/doc.go b/internal/cli/system/core/knowledge/doc.go
new file mode 100644
index 000000000..6650199b9
--- /dev/null
+++ b/internal/cli/system/core/knowledge/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package knowledge scans knowledge files against configured thresholds and emits warnings when limits are exceeded.
+package knowledge
diff --git a/internal/cli/system/core/load/doc.go b/internal/cli/system/core/load/doc.go
new file mode 100644
index 000000000..2da08df09
--- /dev/null
+++ b/internal/cli/system/core/load/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package load extracts index content from context files and handles load-gate logic during session startup.
+package load
diff --git a/internal/cli/system/core/log/doc.go b/internal/cli/system/core/log/doc.go
new file mode 100644
index 000000000..962db87b5
--- /dev/null
+++ b/internal/cli/system/core/log/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package log appends timestamped messages to hook log files with automatic rotation.
+package log
diff --git a/internal/cli/system/core/message/doc.go b/internal/cli/system/core/message/doc.go
new file mode 100644
index 000000000..e260fd37d
--- /dev/null
+++ b/internal/cli/system/core/message/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package message loads and renders hook message templates with priority resolution between user overrides and defaults.
+package message
diff --git a/internal/cli/system/core/nudge/context_size.go b/internal/cli/system/core/nudge/context_size.go
index 40b1d3a76..a362cc069 100644
--- a/internal/cli/system/core/nudge/context_size.go
+++ b/internal/cli/system/core/nudge/context_size.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/cli/system/core/log"
"github.com/ActiveMemory/ctx/internal/cli/system/core/message"
- hook2 "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
+ coreSession "github.com/ActiveMemory/ctx/internal/cli/system/core/session"
"github.com/ActiveMemory/ctx/internal/cli/system/core/state"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/hook"
@@ -78,12 +78,12 @@ func EmitCheckpoint(logFile, sessionID string, count, tokens, pct, windowSize in
func EmitWindowWarning(logFile, sessionID string, count, tokens, pct int) string {
fallback := fmt.Sprintf(
desc.Text(text.DescKeyCheckContextSizeWindowFallback),
- pct, hook2.FormatTokenCount(tokens),
+ pct, coreSession.FormatTokenCount(tokens),
)
content := message.LoadMessage(hook.CheckContextSize, hook.VariantWindow,
map[string]any{
stats.VarPercentage: pct,
- stats.VarTokenCount: hook2.FormatTokenCount(tokens),
+ stats.VarTokenCount: coreSession.FormatTokenCount(tokens),
}, fallback)
if content == "" {
log.Message(
@@ -107,7 +107,7 @@ func EmitWindowWarning(logFile, sessionID string, count, tokens, pct int) string
ref := notify.NewTemplateRef(hook.CheckContextSize, hook.VariantWindow,
map[string]any{
stats.VarPercentage: pct,
- stats.VarTokenCount: hook2.FormatTokenCount(tokens),
+ stats.VarTokenCount: coreSession.FormatTokenCount(tokens),
},
)
windowMsg := fmt.Sprintf(desc.Text(text.DescKeyRelayPrefixFormat),
@@ -139,11 +139,11 @@ func EmitBillingWarning(logFile, sessionID string, count, tokens, threshold int)
}
fallback := fmt.Sprintf(desc.Text(text.DescKeyCheckContextSizeBillingFallback),
- hook2.FormatTokenCount(tokens), hook2.FormatTokenCount(threshold))
+ coreSession.FormatTokenCount(tokens), coreSession.FormatTokenCount(threshold))
content := message.LoadMessage(hook.CheckContextSize, hook.VariantBilling,
map[string]any{
- stats.VarTokenCount: hook2.FormatTokenCount(tokens),
- stats.VarThreshold: hook2.FormatTokenCount(threshold),
+ stats.VarTokenCount: coreSession.FormatTokenCount(tokens),
+ stats.VarThreshold: coreSession.FormatTokenCount(threshold),
}, fallback)
if content == "" {
log.Message(
@@ -171,15 +171,15 @@ func EmitBillingWarning(logFile, sessionID string, count, tokens, threshold int)
ref := notify.NewTemplateRef(
hook.CheckContextSize, hook.VariantBilling,
map[string]any{
- stats.VarTokenCount: hook2.FormatTokenCount(tokens),
- stats.VarThreshold: hook2.FormatTokenCount(threshold),
+ stats.VarTokenCount: coreSession.FormatTokenCount(tokens),
+ stats.VarThreshold: coreSession.FormatTokenCount(threshold),
},
)
billingMsg := fmt.Sprintf(desc.Text(text.DescKeyRelayPrefixFormat),
hook.CheckContextSize,
fmt.Sprintf(
desc.Text(text.DescKeyCheckContextSizeBillingRelayFormat),
- hook2.FormatTokenCount(tokens), hook2.FormatTokenCount(threshold),
+ coreSession.FormatTokenCount(tokens), coreSession.FormatTokenCount(threshold),
),
)
NudgeAndRelay(billingMsg, sessionID, ref)
diff --git a/internal/cli/system/core/nudge/doc.go b/internal/cli/system/core/nudge/doc.go
new file mode 100644
index 000000000..c589e0046
--- /dev/null
+++ b/internal/cli/system/core/nudge/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package nudge emits checkpoint, pause, and oversize nudge notifications during sessions.
+package nudge
diff --git a/internal/cli/system/core/persistence/doc.go b/internal/cli/system/core/persistence/doc.go
new file mode 100644
index 000000000..6aad217af
--- /dev/null
+++ b/internal/cli/system/core/persistence/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package persistence tracks edit counter state for persistence nudge timing.
+package persistence
diff --git a/internal/cli/system/core/session/doc.go b/internal/cli/system/core/session/doc.go
new file mode 100644
index 000000000..e2bc2aa45
--- /dev/null
+++ b/internal/cli/system/core/session/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package session builds hook JSON responses and manages session token tracking.
+package session
diff --git a/internal/cli/system/core/session/session_token.go b/internal/cli/system/core/session/session_token.go
index eaca55397..ec391ec53 100644
--- a/internal/cli/system/core/session/session_token.go
+++ b/internal/cli/system/core/session/session_token.go
@@ -27,7 +27,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/stats"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/entity"
- io2 "github.com/ActiveMemory/ctx/internal/io"
+ internalIo "github.com/ActiveMemory/ctx/internal/io"
"github.com/ActiveMemory/ctx/internal/rc"
)
@@ -74,7 +74,7 @@ func ReadSessionTokenInfo(sessionID string) (entity.TokenInfo, error) {
func FindJSONLPath(sessionID string) (string, error) {
// Check cache first
cacheFile := filepath.Join(state.StateDir(), stats.JsonlPathCachePrefix+sessionID)
- if data, readErr := io2.SafeReadUserFile(cacheFile); readErr == nil {
+ if data, readErr := internalIo.SafeReadUserFile(cacheFile); readErr == nil {
cached := strings.TrimSpace(string(data))
if cached != "" {
if _, statErr := os.Stat(cached); statErr == nil {
@@ -113,7 +113,7 @@ func FindJSONLPath(sessionID string) (string, error) {
// - SessionTokenInfo: Token count and model, or zero value if not found
// - error: Non-nil only on I/O errors
func ParseLastUsageAndModel(path string) (entity.TokenInfo, error) {
- f, openErr := io2.SafeOpenUserFile(path)
+ f, openErr := internalIo.SafeOpenUserFile(path)
if openErr != nil {
return entity.TokenInfo{}, openErr
}
@@ -243,7 +243,7 @@ func ClaudeSettingsHas1M() bool {
if homeErr != nil {
return false
}
- data, readErr := io2.SafeReadUserFile(filepath.Join(home, dir.Claude, claude.GlobalSettings))
+ data, readErr := internalIo.SafeReadUserFile(filepath.Join(home, dir.Claude, claude.GlobalSettings))
if readErr != nil {
return false
}
diff --git a/internal/cli/system/core/state/doc.go b/internal/cli/system/core/state/doc.go
new file mode 100644
index 000000000..84e428d5b
--- /dev/null
+++ b/internal/cli/system/core/state/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package state provides the project-scoped runtime state directory and initialization checks.
+package state
diff --git a/internal/cli/system/core/stats/stats.go b/internal/cli/system/core/stats/stats.go
index ee415fe2e..6ad760870 100644
--- a/internal/cli/system/core/stats/stats.go
+++ b/internal/cli/system/core/stats/stats.go
@@ -22,11 +22,11 @@ import (
"github.com/ActiveMemory/ctx/internal/config/file"
"github.com/ActiveMemory/ctx/internal/config/journal"
"github.com/ActiveMemory/ctx/internal/config/stats"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/entity"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/recall"
- io2 "github.com/ActiveMemory/ctx/internal/io"
+ errRecall "github.com/ActiveMemory/ctx/internal/err/recall"
+ internalIo "github.com/ActiveMemory/ctx/internal/io"
)
// ReadStatsDir reads all stats JSONL files, optionally filtered by session prefix.
@@ -42,7 +42,7 @@ func ReadStatsDir(dir, sessionFilter string) ([]Entry, error) {
pattern := filepath.Join(dir, stats.FilePrefix+"*"+file.ExtJSONL)
matches, globErr := filepath.Glob(pattern)
if globErr != nil {
- return nil, ctxerr.StatsGlob(globErr)
+ return nil, errRecall.StatsGlob(globErr)
}
var entries []Entry
@@ -93,7 +93,7 @@ func ExtractStatsSessionID(basename string) string {
// - []Entry: parsed entries
// - error: non-nil on read failure
func ParseStatsFile(path, sid string) ([]Entry, error) {
- data, readErr := io2.SafeReadUserFile(path)
+ data, readErr := internalIo.SafeReadUserFile(path)
if readErr != nil {
return nil, readErr
}
@@ -212,7 +212,7 @@ func FormatStatsTimestamp(ts string) string {
if parseErr != nil {
return ts
}
- return t.Local().Format(time2.DateTimePreciseFormat)
+ return t.Local().Format(cfgTime.DateTimePreciseFormat)
}
// ReadNewLines reads bytes from offset to end and parses JSONL lines.
@@ -225,7 +225,7 @@ func FormatStatsTimestamp(ts string) string {
// Returns:
// - []Entry: newly parsed entries
func ReadNewLines(path string, offset int64, sid string) []Entry {
- f, openErr := io2.SafeOpenUserFile(path)
+ f, openErr := internalIo.SafeOpenUserFile(path)
if openErr != nil {
return nil
}
diff --git a/internal/cli/system/core/time/doc.go b/internal/cli/system/core/time/doc.go
new file mode 100644
index 000000000..c541f3abb
--- /dev/null
+++ b/internal/cli/system/core/time/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package time returns the most recent modification time across context files.
+package time
diff --git a/internal/cli/system/core/version/doc.go b/internal/cli/system/core/version/doc.go
new file mode 100644
index 000000000..4ca4a5a1a
--- /dev/null
+++ b/internal/cli/system/core/version/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package version parses semantic versions and checks for update availability.
+package version
diff --git a/internal/cli/task/cmd/complete/run.go b/internal/cli/task/cmd/complete/run.go
index ba440a1e4..6f87e854f 100644
--- a/internal/cli/task/cmd/complete/run.go
+++ b/internal/cli/task/cmd/complete/run.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/config/regex"
"github.com/ActiveMemory/ctx/internal/config/token"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/task"
+ errTask "github.com/ActiveMemory/ctx/internal/err/task"
"github.com/ActiveMemory/ctx/internal/write/complete"
"github.com/spf13/cobra"
@@ -44,13 +44,13 @@ func CompleteTask(query, contextDir string) (string, error) {
// Check if the file exists
if _, statErr := os.Stat(filePath); os.IsNotExist(statErr) {
- return "", ctxerr.FileNotFound()
+ return "", errTask.FileNotFound()
}
// Read existing content
content, readErr := os.ReadFile(filepath.Clean(filePath))
if readErr != nil {
- return "", ctxerr.FileRead(readErr)
+ return "", errTask.FileRead(readErr)
}
// Parse tasks and find matching one
@@ -85,7 +85,7 @@ func CompleteTask(query, contextDir string) (string, error) {
strings.ToLower(taskText), strings.ToLower(query),
) {
if matchedLine != -1 {
- return "", ctxerr.MultipleMatches(query)
+ return "", errTask.MultipleMatches(query)
}
matchedLine = i
matchedTask = taskText
@@ -94,7 +94,7 @@ func CompleteTask(query, contextDir string) (string, error) {
}
if matchedLine == -1 {
- return "", ctxerr.NotFound(query)
+ return "", errTask.NotFound(query)
}
// Mark the task as complete
@@ -105,7 +105,7 @@ func CompleteTask(query, contextDir string) (string, error) {
// Write back
newContent := strings.Join(lines, token.NewlineLF)
if writeErr := os.WriteFile(filePath, []byte(newContent), fs.PermFile); writeErr != nil {
- return "", ctxerr.FileWrite(writeErr)
+ return "", errTask.FileWrite(writeErr)
}
return matchedTask, nil
diff --git a/internal/cli/task/core/count/doc.go b/internal/cli/task/core/count/doc.go
new file mode 100644
index 000000000..83a071fb8
--- /dev/null
+++ b/internal/cli/task/core/count/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package count counts pending top-level tasks in TASKS.md.
+package count
diff --git a/internal/cli/task/core/path/doc.go b/internal/cli/task/core/path/doc.go
new file mode 100644
index 000000000..eb060cfaf
--- /dev/null
+++ b/internal/cli/task/core/path/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package path resolves file paths for TASKS.md and the task archive directory.
+package path
diff --git a/internal/cli/watch/core/apply/doc.go b/internal/cli/watch/core/apply/doc.go
new file mode 100644
index 000000000..f59ed6137
--- /dev/null
+++ b/internal/cli/watch/core/apply/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package apply routes context updates to the appropriate handler for writing entries or completing tasks.
+package apply
diff --git a/internal/cli/watch/core/stream/doc.go b/internal/cli/watch/core/stream/doc.go
new file mode 100644
index 000000000..1c8aa58b5
--- /dev/null
+++ b/internal/cli/watch/core/stream/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package stream parses stdin for XML context-update tags and dispatches them for processing.
+package stream
diff --git a/internal/cli/watch/core/stream/stream.go b/internal/cli/watch/core/stream/stream.go
index d7feae53f..cd50146c3 100644
--- a/internal/cli/watch/core/stream/stream.go
+++ b/internal/cli/watch/core/stream/stream.go
@@ -17,8 +17,8 @@ import (
"github.com/ActiveMemory/ctx/internal/config/cli"
"github.com/ActiveMemory/ctx/internal/config/regex"
"github.com/ActiveMemory/ctx/internal/config/watch"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/fs"
- watch2 "github.com/ActiveMemory/ctx/internal/write/watch"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
+ writeWatch "github.com/ActiveMemory/ctx/internal/write/watch"
"github.com/spf13/cobra"
)
@@ -79,13 +79,13 @@ func ProcessStream(cmd *cobra.Command, reader io.Reader, dryRun bool) error {
}
if dryRun {
- watch2.DryRunPreview(cmd, update.Type, update.Content)
+ writeWatch.DryRunPreview(cmd, update.Type, update.Content)
} else {
err := apply.ApplyUpdate(update)
if err != nil {
- watch2.ApplyFailed(cmd, update.Type, err)
+ writeWatch.ApplyFailed(cmd, update.Type, err)
} else {
- watch2.ApplySuccess(cmd, update.Type, update.Content)
+ writeWatch.ApplySuccess(cmd, update.Type, update.Content)
updateCount++
}
}
@@ -94,7 +94,7 @@ func ProcessStream(cmd *cobra.Command, reader io.Reader, dryRun bool) error {
}
if err := scanner.Err(); err != nil {
- return ctxerr.ReadInputStream(err)
+ return errFs.ReadInputStream(err)
}
return nil
diff --git a/internal/cli/watch/watch.go b/internal/cli/watch/watch.go
index 0b861ed8a..5562d5220 100644
--- a/internal/cli/watch/watch.go
+++ b/internal/cli/watch/watch.go
@@ -9,10 +9,10 @@ package watch
import (
"github.com/spf13/cobra"
- watchroot "github.com/ActiveMemory/ctx/internal/cli/watch/cmd/root"
+ watchRoot "github.com/ActiveMemory/ctx/internal/cli/watch/cmd/root"
)
// Cmd returns the watch command.
func Cmd() *cobra.Command {
- return watchroot.Cmd()
+ return watchRoot.Cmd()
}
diff --git a/internal/cli/why/cmd/root/run.go b/internal/cli/why/cmd/root/run.go
index 730a17466..d287d70bb 100644
--- a/internal/cli/why/cmd/root/run.go
+++ b/internal/cli/why/cmd/root/run.go
@@ -16,8 +16,8 @@ import (
"github.com/ActiveMemory/ctx/internal/write/why"
"github.com/spf13/cobra"
- errcli "github.com/ActiveMemory/ctx/internal/err/cli"
- fserr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errCli "github.com/ActiveMemory/ctx/internal/err/cli"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
)
// Run dispatches to the interactive menu or direct document display.
@@ -47,13 +47,13 @@ func showMenu(cmd *cobra.Command) error {
reader := bufio.NewReader(os.Stdin)
input, readErr := reader.ReadString('\n')
if readErr != nil {
- return fserr.ReadInput(readErr)
+ return errFs.ReadInput(readErr)
}
input = strings.TrimSpace(input)
choice, parseErr := strconv.Atoi(input)
if parseErr != nil || choice < 1 || choice > len(DocOrder) {
- return errcli.InvalidSelection(input, len(DocOrder))
+ return errCli.InvalidSelection(input, len(DocOrder))
}
why.Separator(cmd)
@@ -71,12 +71,12 @@ func showMenu(cmd *cobra.Command) error {
func ShowDoc(cmd *cobra.Command, alias string) error {
name, ok := DocAliases[alias]
if !ok {
- return errcli.UnknownDocument(alias)
+ return errCli.UnknownDocument(alias)
}
content, loadErr := philosophy.WhyDoc(name)
if loadErr != nil {
- return fserr.FileRead(name, loadErr)
+ return errFs.FileRead(name, loadErr)
}
cleaned := StripMkDocs(string(content))
diff --git a/internal/cli/why/strip_test.go b/internal/cli/why/strip_test.go
index 143046d65..8d59f5fa9 100644
--- a/internal/cli/why/strip_test.go
+++ b/internal/cli/why/strip_test.go
@@ -11,12 +11,12 @@ import (
"testing"
"github.com/ActiveMemory/ctx/internal/assets/read/philosophy"
- whyroot "github.com/ActiveMemory/ctx/internal/cli/why/cmd/root"
+ whyRoot "github.com/ActiveMemory/ctx/internal/cli/why/cmd/root"
)
func TestStripMkDocs_Frontmatter(t *testing.T) {
input := "---\ntitle: Test\nicon: lucide/flame\n---\n\n# Hello\n"
- got := whyroot.StripMkDocs(input)
+ got := whyRoot.StripMkDocs(input)
if strings.Contains(got, "title: Test") {
t.Error("frontmatter was not stripped")
}
@@ -27,7 +27,7 @@ func TestStripMkDocs_Frontmatter(t *testing.T) {
func TestStripMkDocs_Images(t *testing.T) {
input := "# Title\n\n\n\nSome text.\n"
- got := whyroot.StripMkDocs(input)
+ got := whyRoot.StripMkDocs(input)
if strings.Contains(got, "![banner]") {
t.Error("image line was not stripped")
}
@@ -43,7 +43,7 @@ func TestStripMkDocs_Admonitions(t *testing.T) {
Normal text.
`
- got := whyroot.StripMkDocs(input)
+ got := whyRoot.StripMkDocs(input)
if !strings.Contains(got, `> **Important Note**`) {
t.Errorf("admonition title not converted, got:\n%s", got)
}
@@ -60,7 +60,7 @@ Normal text.
func TestStripMkDocs_AdmonitionNoTitle(t *testing.T) {
input := "!!! warning\n Body here.\n"
- got := whyroot.StripMkDocs(input)
+ got := whyRoot.StripMkDocs(input)
if strings.Contains(got, "!!!") {
t.Error("admonition marker was not stripped")
}
@@ -81,7 +81,7 @@ func TestStripMkDocs_Tabs(t *testing.T) {
Normal text.
`
- got := whyroot.StripMkDocs(input)
+ got := whyRoot.StripMkDocs(input)
if !strings.Contains(got, "**Without ctx**") {
t.Errorf("tab title not converted, got:\n%s", got)
}
@@ -101,7 +101,7 @@ Normal text.
func TestStripMkDocs_RelativeLinks(t *testing.T) {
input := "[Getting Started](getting-started.md) and [About](../home/about.md)\n"
- got := whyroot.StripMkDocs(input)
+ got := whyRoot.StripMkDocs(input)
if strings.Contains(got, ".md") {
t.Errorf("relative .md link not stripped, got:\n%s", got)
}
@@ -115,7 +115,7 @@ func TestStripMkDocs_RelativeLinks(t *testing.T) {
func TestStripMkDocs_PreservesExternalLinks(t *testing.T) {
input := "[ctx site](https://ctx.ist) stays.\n"
- got := whyroot.StripMkDocs(input)
+ got := whyRoot.StripMkDocs(input)
if !strings.Contains(got, "[ctx site](https://ctx.ist)") {
t.Errorf("external link was modified, got:\n%s", got)
}
@@ -123,7 +123,7 @@ func TestStripMkDocs_PreservesExternalLinks(t *testing.T) {
func TestStripMkDocs_PreservesCodeBlocks(t *testing.T) {
input := "```text\n{} --> what\nctx --> why\n```\n"
- got := whyroot.StripMkDocs(input)
+ got := whyRoot.StripMkDocs(input)
if !strings.Contains(got, "ctx --> why") {
t.Errorf("code block content was lost, got:\n%s", got)
}
@@ -135,7 +135,7 @@ func TestStripMkDocs_EmbeddedManifesto(t *testing.T) {
t.Fatalf("failed to load embedded manifesto: %v", loadErr)
}
- got := whyroot.StripMkDocs(string(content))
+ got := whyRoot.StripMkDocs(string(content))
// Should not contain MkDocs artifacts.
if strings.Contains(got, "---\ntitle:") {
diff --git a/internal/cli/why/why.go b/internal/cli/why/why.go
index c7f5a1f6d..6ca9af703 100644
--- a/internal/cli/why/why.go
+++ b/internal/cli/why/why.go
@@ -9,10 +9,10 @@ package why
import (
"github.com/spf13/cobra"
- whyroot "github.com/ActiveMemory/ctx/internal/cli/why/cmd/root"
+ whyRoot "github.com/ActiveMemory/ctx/internal/cli/why/cmd/root"
)
// Cmd returns the "ctx why" cobra command.
func Cmd() *cobra.Command {
- return whyroot.Cmd()
+ return whyRoot.Cmd()
}
diff --git a/internal/cli/why/why_test.go b/internal/cli/why/why_test.go
index 5db92df1d..fa3a33e89 100644
--- a/internal/cli/why/why_test.go
+++ b/internal/cli/why/why_test.go
@@ -11,7 +11,7 @@ import (
"strings"
"testing"
- whyroot "github.com/ActiveMemory/ctx/internal/cli/why/cmd/root"
+ whyRoot "github.com/ActiveMemory/ctx/internal/cli/why/cmd/root"
"github.com/spf13/cobra"
)
@@ -27,7 +27,7 @@ func newTestCmd() (*cobra.Command, *bytes.Buffer) {
func TestShowDoc_Manifesto(t *testing.T) {
cmd, buf := newTestCmd()
- showErr := whyroot.ShowDoc(cmd, "manifesto")
+ showErr := whyRoot.ShowDoc(cmd, "manifesto")
if showErr != nil {
t.Fatalf("ShowDoc(manifesto) error = %v", showErr)
}
@@ -44,7 +44,7 @@ func TestShowDoc_Manifesto(t *testing.T) {
func TestShowDoc_UnknownAlias(t *testing.T) {
cmd, _ := newTestCmd()
- showErr := whyroot.ShowDoc(cmd, "nonexistent")
+ showErr := whyRoot.ShowDoc(cmd, "nonexistent")
if showErr == nil {
t.Fatal("expected error for unknown document alias")
}
@@ -106,7 +106,7 @@ func TestExtractAdmonitionTitle(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- got := whyroot.ExtractAdmonitionTitle(tc.input)
+ got := whyRoot.ExtractAdmonitionTitle(tc.input)
if got != tc.want {
t.Errorf("ExtractAdmonitionTitle(%q) = %q, want %q", tc.input, got, tc.want)
}
@@ -139,7 +139,7 @@ func TestExtractTabTitle(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
- got := whyroot.ExtractTabTitle(tc.input)
+ got := whyRoot.ExtractTabTitle(tc.input)
if got != tc.want {
t.Errorf("ExtractTabTitle(%q) = %q, want %q", tc.input, got, tc.want)
}
diff --git a/internal/context/load/loader.go b/internal/context/load/loader.go
index 640e9daf8..4b856bf61 100644
--- a/internal/context/load/loader.go
+++ b/internal/context/load/loader.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/context/summary"
"github.com/ActiveMemory/ctx/internal/context/token"
"github.com/ActiveMemory/ctx/internal/entity"
- errctx "github.com/ActiveMemory/ctx/internal/err/context"
+ errCtx "github.com/ActiveMemory/ctx/internal/err/context"
"github.com/ActiveMemory/ctx/internal/rc"
"github.com/ActiveMemory/ctx/internal/validation"
)
@@ -40,12 +40,12 @@ func Do(dir string) (*entity.Context, error) {
info, statErr := os.Stat(dir)
if statErr != nil {
if os.IsNotExist(statErr) {
- return nil, errctx.NotFound(dir)
+ return nil, errCtx.NotFound(dir)
}
return nil, statErr
}
if !info.IsDir() {
- return nil, errctx.NotFound(dir)
+ return nil, errCtx.NotFound(dir)
}
// Reject context directories that contain symlinks (M-2 defense).
diff --git a/internal/context/load/loader_test.go b/internal/context/load/loader_test.go
index 6a07f912c..41fcc3f00 100644
--- a/internal/context/load/loader_test.go
+++ b/internal/context/load/loader_test.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/context/sanitize"
"github.com/ActiveMemory/ctx/internal/context/validate"
- errctx "github.com/ActiveMemory/ctx/internal/err/context"
+ errCtx "github.com/ActiveMemory/ctx/internal/err/context"
)
func TestExists(t *testing.T) {
@@ -137,7 +137,7 @@ func TestLoadNonExistent(t *testing.T) {
t.Error("Do() should return error for non-existent directory")
}
- var notFoundError *errctx.NotFoundError
+ var notFoundError *errCtx.NotFoundError
ok := errors.As(err, ¬FoundError)
if !ok {
t.Errorf("error should be *NotFoundError, got %T", err)
@@ -188,7 +188,7 @@ func TestIsEffectivelyEmpty(t *testing.T) {
}
func TestNotFoundError(t *testing.T) {
- err := errctx.NotFound("/test/path")
+ err := errCtx.NotFound("/test/path")
expected := "context directory not found: /test/path"
if err.Error() != expected {
t.Errorf("NotFoundError.Error() = %q, want %q", err.Error(), expected)
diff --git a/internal/crypto/crypto.go b/internal/crypto/crypto.go
index 6fa68e410..e7fdd329e 100644
--- a/internal/crypto/crypto.go
+++ b/internal/crypto/crypto.go
@@ -20,8 +20,8 @@ import (
"github.com/ActiveMemory/ctx/internal/config/crypto"
"github.com/ActiveMemory/ctx/internal/config/fs"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/crypto"
- io2 "github.com/ActiveMemory/ctx/internal/io"
+ errCrypto "github.com/ActiveMemory/ctx/internal/err/crypto"
+ internalIo "github.com/ActiveMemory/ctx/internal/io"
)
// GenerateKey returns a new 256-bit random key.
@@ -32,7 +32,7 @@ import (
func GenerateKey() ([]byte, error) {
key := make([]byte, crypto.KeySize)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
- return nil, ctxerr.GenerateKey(err)
+ return nil, errCrypto.GenerateKey(err)
}
return key, nil
}
@@ -53,17 +53,17 @@ func GenerateKey() ([]byte, error) {
func Encrypt(key, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
- return nil, ctxerr.CreateCipher(err)
+ return nil, errCrypto.CreateCipher(err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
- return nil, ctxerr.CreateGCM(err)
+ return nil, errCrypto.CreateGCM(err)
}
nonce := make([]byte, crypto.NonceSize)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
- return nil, ctxerr.GenerateNonce(err)
+ return nil, errCrypto.GenerateNonce(err)
}
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
@@ -82,17 +82,17 @@ func Encrypt(key, plaintext []byte) ([]byte, error) {
// authentication fails
func Decrypt(key, ciphertext []byte) ([]byte, error) {
if len(ciphertext) < crypto.NonceSize {
- return nil, ctxerr.CiphertextTooShort()
+ return nil, errCrypto.CiphertextTooShort()
}
block, err := aes.NewCipher(key)
if err != nil {
- return nil, ctxerr.CreateCipher(err)
+ return nil, errCrypto.CreateCipher(err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
- return nil, ctxerr.CreateGCM(err)
+ return nil, errCrypto.CreateGCM(err)
}
nonce := ciphertext[:crypto.NonceSize]
@@ -100,7 +100,7 @@ func Decrypt(key, ciphertext []byte) ([]byte, error) {
plaintext, err := gcm.Open(nil, nonce, data, nil)
if err != nil {
- return nil, ctxerr.Decrypt(err)
+ return nil, errCrypto.Decrypt(err)
}
return plaintext, nil
@@ -115,12 +115,12 @@ func Decrypt(key, ciphertext []byte) ([]byte, error) {
// - []byte: The 32-byte key
// - error: Non-nil if the file cannot be read or is not exactly 32 bytes
func LoadKey(path string) ([]byte, error) {
- key, err := io2.SafeReadUserFile(path)
+ key, err := internalIo.SafeReadUserFile(path)
if err != nil {
- return nil, ctxerr.ReadKey(err)
+ return nil, errCrypto.ReadKey(err)
}
if len(key) != crypto.KeySize {
- return nil, ctxerr.InvalidKeySize(len(key), crypto.KeySize)
+ return nil, errCrypto.InvalidKeySize(len(key), crypto.KeySize)
}
return key, nil
}
@@ -135,7 +135,7 @@ func LoadKey(path string) ([]byte, error) {
// - error: Non-nil if the file cannot be written
func SaveKey(path string, key []byte) error {
if err := os.WriteFile(path, key, fs.PermSecret); err != nil {
- return ctxerr.WriteKey(err)
+ return errCrypto.WriteKey(err)
}
return nil
}
diff --git a/internal/crypto/keypath.go b/internal/crypto/keypath.go
index 28c70ae28..942f1234c 100644
--- a/internal/crypto/keypath.go
+++ b/internal/crypto/keypath.go
@@ -11,7 +11,7 @@ import (
"path/filepath"
"strings"
- cryptocfg "github.com/ActiveMemory/ctx/internal/config/crypto"
+ cfgCrypto "github.com/ActiveMemory/ctx/internal/config/crypto"
"github.com/ActiveMemory/ctx/internal/config/dir"
)
@@ -24,7 +24,7 @@ func GlobalKeyPath() string {
if err != nil {
return ""
}
- return filepath.Join(home, dir.CtxData, cryptocfg.ContextKey)
+ return filepath.Join(home, dir.CtxData, cfgCrypto.ContextKey)
}
// ExpandHome expands a leading ~/ prefix to the user's home directory.
@@ -69,7 +69,7 @@ func ResolveKeyPath(contextDir, overridePath string) string {
}
// Tier 2: project-local key.
- local := filepath.Join(contextDir, cryptocfg.ContextKey)
+ local := filepath.Join(contextDir, cfgCrypto.ContextKey)
if _, err := os.Stat(local); err == nil {
return local
}
diff --git a/internal/crypto/keypath_test.go b/internal/crypto/keypath_test.go
index bfa24a94f..f2e91cc1d 100644
--- a/internal/crypto/keypath_test.go
+++ b/internal/crypto/keypath_test.go
@@ -11,7 +11,7 @@ import (
"path/filepath"
"testing"
- cryptocfg "github.com/ActiveMemory/ctx/internal/config/crypto"
+ cfgCrypto "github.com/ActiveMemory/ctx/internal/config/crypto"
"github.com/ActiveMemory/ctx/internal/config/fs"
)
@@ -20,7 +20,7 @@ func TestGlobalKeyPath(t *testing.T) {
t.Setenv("HOME", dir)
got := GlobalKeyPath()
- want := filepath.Join(dir, ".ctx", cryptocfg.ContextKey)
+ want := filepath.Join(dir, ".ctx", cfgCrypto.ContextKey)
if got != want {
t.Errorf("GlobalKeyPath() = %q, want %q", got, want)
}
@@ -71,7 +71,7 @@ func TestResolveKeyPath_ProjectLocalBeforeGlobal(t *testing.T) {
if err := os.MkdirAll(contextDir, 0750); err != nil {
t.Fatal(err)
}
- localKey := filepath.Join(contextDir, cryptocfg.ContextKey)
+ localKey := filepath.Join(contextDir, cfgCrypto.ContextKey)
if err := os.WriteFile(localKey, []byte("local-key"), fs.PermSecret); err != nil {
t.Fatal(err)
}
@@ -80,7 +80,7 @@ func TestResolveKeyPath_ProjectLocalBeforeGlobal(t *testing.T) {
if err := os.MkdirAll(globalDir, fs.PermKeyDir); err != nil {
t.Fatal(err)
}
- globalKey := filepath.Join(globalDir, cryptocfg.ContextKey)
+ globalKey := filepath.Join(globalDir, cfgCrypto.ContextKey)
if err := os.WriteFile(globalKey, []byte("global-key"), fs.PermSecret); err != nil {
t.Fatal(err)
}
@@ -100,7 +100,7 @@ func TestResolveKeyPath_FallbackToGlobal(t *testing.T) {
if err := os.MkdirAll(globalDir, fs.PermKeyDir); err != nil {
t.Fatal(err)
}
- globalKey := filepath.Join(globalDir, cryptocfg.ContextKey)
+ globalKey := filepath.Join(globalDir, cfgCrypto.ContextKey)
if err := os.WriteFile(globalKey, []byte("global-key"), fs.PermSecret); err != nil {
t.Fatal(err)
}
diff --git a/internal/index/index.go b/internal/index/index.go
index ce2943fba..c9f4232b4 100644
--- a/internal/index/index.go
+++ b/internal/index/index.go
@@ -19,8 +19,8 @@ import (
"github.com/ActiveMemory/ctx/internal/config/marker"
"github.com/ActiveMemory/ctx/internal/config/regex"
"github.com/ActiveMemory/ctx/internal/config/token"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/recall"
- io2 "github.com/ActiveMemory/ctx/internal/io"
+ errRecall "github.com/ActiveMemory/ctx/internal/err/recall"
+ internalIo "github.com/ActiveMemory/ctx/internal/io"
)
// ParseHeaders extracts all entries from file content.
@@ -216,18 +216,18 @@ func ReindexFile(
entryType string,
) error {
if _, err := os.Stat(filePath); os.IsNotExist(err) {
- return ctxerr.ReindexFileNotFound(fileName)
+ return errRecall.ReindexFileNotFound(fileName)
}
- content, err := io2.SafeReadUserFile(filePath)
+ content, err := internalIo.SafeReadUserFile(filePath)
if err != nil {
- return ctxerr.ReindexFileRead(filePath, err)
+ return errRecall.ReindexFileRead(filePath, err)
}
updated := updateFunc(string(content))
if err := os.WriteFile(filePath, []byte(updated), fs.PermFile); err != nil {
- return ctxerr.ReindexFileWrite(filePath, err)
+ return errRecall.ReindexFileWrite(filePath, err)
}
entries := ParseHeaders(string(content))
diff --git a/internal/io/security.go b/internal/io/security.go
index 7e3e154f7..b18de52e2 100644
--- a/internal/io/security.go
+++ b/internal/io/security.go
@@ -15,9 +15,9 @@ import (
"strings"
"time"
- configFs "github.com/ActiveMemory/ctx/internal/config/fs"
- fserr "github.com/ActiveMemory/ctx/internal/err/fs"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/http"
+ cfgFs "github.com/ActiveMemory/ctx/internal/config/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
+ errHTTP "github.com/ActiveMemory/ctx/internal/err/http"
)
// SafeReadFile resolves filename within baseDir, verifies the result
@@ -37,13 +37,13 @@ import (
func SafeReadFile(baseDir, filename string) ([]byte, error) {
absBase, absErr := filepath.Abs(baseDir)
if absErr != nil {
- return nil, fserr.ResolveBase(absErr)
+ return nil, errFs.ResolveBase(absErr)
}
safe := filepath.Join(absBase, filepath.Base(filename))
if !strings.HasPrefix(safe, absBase+string(os.PathSeparator)) {
- return nil, fserr.PathEscapesBase(filename)
+ return nil, errFs.PathEscapesBase(filename)
}
data, readErr := os.ReadFile(safe) //nolint:gosec
@@ -154,7 +154,7 @@ func SafeWriteFile(path string, data []byte, perm os.FileMode) error {
// Parameters:
// - path: absolute file path to touch
func TouchFile(path string) {
- _ = os.WriteFile(path, nil, configFs.PermSecret) //nolint:gosec // state marker, path from internal code
+ _ = os.WriteFile(path, nil, cfgFs.PermSecret) //nolint:gosec // state marker, path from internal code
}
// maxRedirects caps the number of HTTP redirects the client will follow.
@@ -205,7 +205,7 @@ func SafePost(
Timeout: timeout,
CheckRedirect: func(_ *http.Request, via []*http.Request) error {
if len(via) >= maxRedirects {
- return ctxerr.TooManyRedirects()
+ return errHTTP.TooManyRedirects()
}
return nil
},
@@ -221,11 +221,11 @@ func SafePost(
func validateHTTPScheme(rawURL string) error {
parsed, parseErr := url.Parse(rawURL)
if parseErr != nil {
- return ctxerr.ParseURL(parseErr)
+ return errHTTP.ParseURL(parseErr)
}
scheme := strings.ToLower(parsed.Scheme)
if scheme != "http" && scheme != "https" {
- return ctxerr.UnsafeURLScheme(parsed.Scheme)
+ return errHTTP.UnsafeURLScheme(parsed.Scheme)
}
return nil
}
diff --git a/internal/io/validate.go b/internal/io/validate.go
index fcab7728d..fa97c3b37 100644
--- a/internal/io/validate.go
+++ b/internal/io/validate.go
@@ -10,18 +10,18 @@ import (
"path/filepath"
"strings"
- fserr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
)
// rejectDangerousPath returns an error if the resolved absolute path
// falls under a system directory that ctx should never touch.
func rejectDangerousPath(absPath string) error {
if absPath == "/" {
- return fserr.RefuseSystemPathRoot()
+ return errFs.RefuseSystemPathRoot()
}
for _, prefix := range dangerousPrefixes {
if strings.HasPrefix(absPath, prefix) {
- return fserr.RefuseSystemPath(absPath)
+ return errFs.RefuseSystemPath(absPath)
}
}
return nil
@@ -33,7 +33,7 @@ func cleanAndValidate(path string) (string, error) {
clean := filepath.Clean(path)
abs, absErr := filepath.Abs(clean)
if absErr != nil {
- return "", fserr.ResolvePath(absErr)
+ return "", errFs.ResolvePath(absErr)
}
if checkErr := rejectDangerousPath(abs); checkErr != nil {
return "", checkErr
diff --git a/internal/mcp/handler/task/doc.go b/internal/mcp/handler/task/doc.go
new file mode 100644
index 000000000..45da8d231
--- /dev/null
+++ b/internal/mcp/handler/task/doc.go
@@ -0,0 +1,8 @@
+// / ctx: https://ctx.ist
+// ,'`./ do you remember?
+// `.,'\
+// \ Copyright 2026-present Context contributors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package task iterates pending tasks from TASKS.md for MCP tool responses.
+package task
diff --git a/internal/mcp/handler/tool.go b/internal/mcp/handler/tool.go
index 47bd2a462..774581f9f 100644
--- a/internal/mcp/handler/tool.go
+++ b/internal/mcp/handler/tool.go
@@ -13,22 +13,22 @@ import (
"time"
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
- remindcore "github.com/ActiveMemory/ctx/internal/cli/remind/core"
- taskcomplete "github.com/ActiveMemory/ctx/internal/cli/task/cmd/complete"
- archiveCfg "github.com/ActiveMemory/ctx/internal/config/archive"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ remindCore "github.com/ActiveMemory/ctx/internal/cli/remind/core"
+ taskComplete "github.com/ActiveMemory/ctx/internal/cli/task/cmd/complete"
+ cfgArchive "github.com/ActiveMemory/ctx/internal/config/archive"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
- entryCfg "github.com/ActiveMemory/ctx/internal/config/entry"
- configfs "github.com/ActiveMemory/ctx/internal/config/fs"
+ cfgEntry "github.com/ActiveMemory/ctx/internal/config/entry"
+ cfgFs "github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/config/mcp/event"
"github.com/ActiveMemory/ctx/internal/config/mcp/field"
- timeCfg "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/context/load"
"github.com/ActiveMemory/ctx/internal/drift"
"github.com/ActiveMemory/ctx/internal/entity"
"github.com/ActiveMemory/ctx/internal/entry"
- mcpErr "github.com/ActiveMemory/ctx/internal/err/mcp"
+ errMcp "github.com/ActiveMemory/ctx/internal/err/mcp"
"github.com/ActiveMemory/ctx/internal/mcp/handler/task"
"github.com/ActiveMemory/ctx/internal/mcp/server/stat"
"github.com/ActiveMemory/ctx/internal/mcp/session"
@@ -131,7 +131,7 @@ func (h *Handler) Complete(query string) (string, error) {
return "", boundaryErr
}
- completedTask, completeErr := taskcomplete.CompleteTask(
+ completedTask, completeErr := taskComplete.CompleteTask(
query, h.ContextDir,
)
if completeErr != nil {
@@ -245,7 +245,7 @@ func (h *Handler) Recall(limit int, since time.Time) (string, error) {
_, _ = fmt.Fprintf(
&sb,
desc.Text(text.DescKeyMCPRecallItemFormat),
- i+1, sess.StartTime.Format(timeCfg.DateTimeFormat),
+ i+1, sess.StartTime.Format(cfgTime.DateTimeFormat),
)
if sess.Project != "" {
_, _ = fmt.Fprintf(
@@ -289,8 +289,8 @@ func (h *Handler) WatchUpdate(
}
// Handle the "complete" type as a special case.
- if entryType == entryCfg.Complete {
- completedTask, completeErr := taskcomplete.CompleteTask(
+ if entryType == cfgEntry.Complete {
+ completedTask, completeErr := taskComplete.CompleteTask(
content, h.ContextDir)
if completeErr != nil {
return "", completeErr
@@ -366,7 +366,7 @@ func (h *Handler) Compact(archive bool) (string, error) {
if writeErr := os.WriteFile(
result.TasksFileUpdate.Path,
result.TasksFileUpdate.Content,
- configfs.PermFile,
+ cfgFs.PermFile,
); writeErr != nil {
return "", writeErr
}
@@ -375,7 +375,7 @@ func (h *Handler) Compact(archive bool) (string, error) {
// Write section-cleaned files.
for _, fu := range result.SectionFileUpdates {
if writeErr := os.WriteFile(
- fu.Path, fu.Content, configfs.PermFile,
+ fu.Path, fu.Content, cfgFs.PermFile,
); writeErr != nil {
return "", writeErr
}
@@ -390,7 +390,7 @@ func (h *Handler) Compact(archive bool) (string, error) {
token.NewlineLF + token.NewlineLF
}
if _, archiveErr := tidy.WriteArchive(
- archiveCfg.ArchiveScopeTasks,
+ cfgArchive.ArchiveScopeTasks,
desc.Text(text.DescKeyHeadingArchivedTasks),
archiveContent,
); archiveErr != nil {
@@ -445,7 +445,7 @@ func (h *Handler) Next() (string, error) {
return "", loadErr
}
- tasksFile := ctx.File(ctxCfg.Task)
+ tasksFile := ctx.File(cfgCtx.Task)
if tasksFile == nil {
return desc.Text(text.DescKeyMCPNoTasks), nil
}
@@ -483,7 +483,7 @@ func (h *Handler) CheckTaskCompletion(recentAction string) (string, error) {
return "", loadErr
}
- tasksFile := ctx.File(ctxCfg.Task)
+ tasksFile := ctx.File(cfgCtx.Task)
if tasksFile == nil {
return "", nil
}
@@ -572,7 +572,7 @@ func (h *Handler) SessionEvent(
return sb.String(), nil
default:
- return "", mcpErr.UnknownEventType(eventType)
+ return "", errMcp.UnknownEventType(eventType)
}
}
@@ -582,7 +582,7 @@ func (h *Handler) SessionEvent(
// - string: formatted reminder list or no-reminders message
// - error: reminder read error
func (h *Handler) Remind() (string, error) {
- reminders, readErr := remindcore.ReadReminders()
+ reminders, readErr := remindCore.ReadReminders()
if readErr != nil {
return "", readErr
}
@@ -591,7 +591,7 @@ func (h *Handler) Remind() (string, error) {
return desc.Text(text.DescKeyMCPNoReminders), nil
}
- today := time.Now().Format(timeCfg.DateFormat)
+ today := time.Now().Format(cfgTime.DateFormat)
var sb strings.Builder
_, _ = fmt.Fprintf(
&sb,
diff --git a/internal/mcp/server/catalog/data.go b/internal/mcp/server/catalog/data.go
index 8068cec2a..40ae8f39f 100644
--- a/internal/mcp/server/catalog/data.go
+++ b/internal/mcp/server/catalog/data.go
@@ -8,7 +8,7 @@ package catalog
import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/mcp/resource"
)
@@ -23,14 +23,14 @@ type mapping struct {
// table defines all individual context file resources.
var table = []mapping{
- {ctxCfg.Constitution, resource.Constitution, desc.Text(text.DescKeyMCPResConstitution)},
- {ctxCfg.Task, resource.Tasks, desc.Text(text.DescKeyMCPResTasks)},
- {ctxCfg.Convention, resource.Conventions, desc.Text(text.DescKeyMCPResConventions)},
- {ctxCfg.Architecture, resource.Architecture, desc.Text(text.DescKeyMCPResArchitecture)},
- {ctxCfg.Decision, resource.Decisions, desc.Text(text.DescKeyMCPResDecisions)},
- {ctxCfg.Learning, resource.Learnings, desc.Text(text.DescKeyMCPResLearnings)},
- {ctxCfg.Glossary, resource.Glossary, desc.Text(text.DescKeyMCPResGlossary)},
- {ctxCfg.AgentPlaybook, resource.Playbook, desc.Text(text.DescKeyMCPResPlaybook)},
+ {cfgCtx.Constitution, resource.Constitution, desc.Text(text.DescKeyMCPResConstitution)},
+ {cfgCtx.Task, resource.Tasks, desc.Text(text.DescKeyMCPResTasks)},
+ {cfgCtx.Convention, resource.Conventions, desc.Text(text.DescKeyMCPResConventions)},
+ {cfgCtx.Architecture, resource.Architecture, desc.Text(text.DescKeyMCPResArchitecture)},
+ {cfgCtx.Decision, resource.Decisions, desc.Text(text.DescKeyMCPResDecisions)},
+ {cfgCtx.Learning, resource.Learnings, desc.Text(text.DescKeyMCPResLearnings)},
+ {cfgCtx.Glossary, resource.Glossary, desc.Text(text.DescKeyMCPResGlossary)},
+ {cfgCtx.AgentPlaybook, resource.Playbook, desc.Text(text.DescKeyMCPResPlaybook)},
}
// uriLookup maps full resource URIs to context file names. Populated
diff --git a/internal/mcp/server/def/prompt/prompt.go b/internal/mcp/server/def/prompt/prompt.go
index 8efd0103b..3c071e503 100644
--- a/internal/mcp/server/def/prompt/prompt.go
+++ b/internal/mcp/server/def/prompt/prompt.go
@@ -11,19 +11,19 @@ import (
"github.com/ActiveMemory/ctx/internal/config/cli"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/mcp/field"
- promptCfg "github.com/ActiveMemory/ctx/internal/config/mcp/prompt"
+ cfgMcpPrompt "github.com/ActiveMemory/ctx/internal/config/mcp/prompt"
"github.com/ActiveMemory/ctx/internal/mcp/proto"
)
// Defs defines all available MCP prompts.
var Defs = []proto.Prompt{
{
- Name: promptCfg.SessionStart,
+ Name: cfgMcpPrompt.SessionStart,
Description: desc.Text(
text.DescKeyMCPPromptSessionStartDesc),
},
{
- Name: promptCfg.AddDecision,
+ Name: cfgMcpPrompt.AddDecision,
Description: desc.Text(
text.DescKeyMCPPromptAddDecisionDesc),
Arguments: []proto.PromptArgument{
@@ -50,7 +50,7 @@ var Defs = []proto.Prompt{
},
},
{
- Name: promptCfg.AddLearning,
+ Name: cfgMcpPrompt.AddLearning,
Description: desc.Text(
text.DescKeyMCPPromptAddLearningDesc),
Arguments: []proto.PromptArgument{
@@ -77,12 +77,12 @@ var Defs = []proto.Prompt{
},
},
{
- Name: promptCfg.Reflect,
+ Name: cfgMcpPrompt.Reflect,
Description: desc.Text(
text.DescKeyMCPPromptReflectDesc),
},
{
- Name: promptCfg.Checkpoint,
+ Name: cfgMcpPrompt.Checkpoint,
Description: desc.Text(
text.DescKeyMCPPromptCheckpointDesc),
},
diff --git a/internal/mcp/server/def/tool/tool.go b/internal/mcp/server/def/tool/tool.go
index dd4c25665..8713f6e2d 100644
--- a/internal/mcp/server/def/tool/tool.go
+++ b/internal/mcp/server/def/tool/tool.go
@@ -13,21 +13,21 @@ import (
"github.com/ActiveMemory/ctx/internal/config/entry"
"github.com/ActiveMemory/ctx/internal/config/mcp/field"
"github.com/ActiveMemory/ctx/internal/config/mcp/schema"
- toolCfg "github.com/ActiveMemory/ctx/internal/config/mcp/tool"
+ cfgMcpTool "github.com/ActiveMemory/ctx/internal/config/mcp/tool"
"github.com/ActiveMemory/ctx/internal/mcp/proto"
)
// Defs defines all available MCP tools.
var Defs = []proto.Tool{
{
- Name: toolCfg.Status,
+ Name: cfgMcpTool.Status,
Description: desc.Text(
text.DescKeyMCPToolStatusDesc),
InputSchema: proto.InputSchema{Type: schema.Object},
Annotations: &proto.ToolAnnotations{ReadOnlyHint: true},
},
{
- Name: toolCfg.Add,
+ Name: cfgMcpTool.Add,
Description: desc.Text(
text.DescKeyMCPToolAddDesc),
InputSchema: proto.InputSchema{
@@ -60,7 +60,7 @@ var Defs = []proto.Tool{
Annotations: &proto.ToolAnnotations{},
},
{
- Name: toolCfg.Complete,
+ Name: cfgMcpTool.Complete,
Description: desc.Text(
text.DescKeyMCPToolCompleteDesc),
InputSchema: proto.InputSchema{
@@ -77,14 +77,14 @@ var Defs = []proto.Tool{
Annotations: &proto.ToolAnnotations{IdempotentHint: true},
},
{
- Name: toolCfg.Drift,
+ Name: cfgMcpTool.Drift,
Description: desc.Text(
text.DescKeyMCPToolDriftDesc),
InputSchema: proto.InputSchema{Type: schema.Object},
Annotations: &proto.ToolAnnotations{ReadOnlyHint: true},
},
{
- Name: toolCfg.Recall,
+ Name: cfgMcpTool.Recall,
Description: desc.Text(
text.DescKeyMCPToolRecallDesc),
InputSchema: proto.InputSchema{
@@ -105,7 +105,7 @@ var Defs = []proto.Tool{
Annotations: &proto.ToolAnnotations{ReadOnlyHint: true},
},
{
- Name: toolCfg.WatchUpdate,
+ Name: cfgMcpTool.WatchUpdate,
Description: desc.Text(
text.DescKeyMCPToolWatchUpdateDesc),
InputSchema: proto.InputSchema{
@@ -128,7 +128,7 @@ var Defs = []proto.Tool{
Annotations: &proto.ToolAnnotations{},
},
{
- Name: toolCfg.Compact,
+ Name: cfgMcpTool.Compact,
Description: desc.Text(
text.DescKeyMCPToolCompactDesc),
InputSchema: proto.InputSchema{
@@ -144,14 +144,14 @@ var Defs = []proto.Tool{
Annotations: &proto.ToolAnnotations{},
},
{
- Name: toolCfg.Next,
+ Name: cfgMcpTool.Next,
Description: desc.Text(
text.DescKeyMCPToolNextDesc),
InputSchema: proto.InputSchema{Type: schema.Object},
Annotations: &proto.ToolAnnotations{ReadOnlyHint: true},
},
{
- Name: toolCfg.CheckTaskCompletion,
+ Name: cfgMcpTool.CheckTaskCompletion,
Description: desc.Text(
text.DescKeyMCPToolCheckTaskDesc),
InputSchema: proto.InputSchema{
@@ -167,7 +167,7 @@ var Defs = []proto.Tool{
Annotations: &proto.ToolAnnotations{ReadOnlyHint: true},
},
{
- Name: toolCfg.SessionEvent,
+ Name: cfgMcpTool.SessionEvent,
Description: desc.Text(
text.DescKeyMCPToolSessionDesc),
InputSchema: proto.InputSchema{
@@ -189,7 +189,7 @@ var Defs = []proto.Tool{
Annotations: &proto.ToolAnnotations{},
},
{
- Name: toolCfg.Remind,
+ Name: cfgMcpTool.Remind,
Description: desc.Text(
text.DescKeyMCPToolRemindDesc),
InputSchema: proto.InputSchema{Type: schema.Object},
diff --git a/internal/mcp/server/extract/extract.go b/internal/mcp/server/extract/extract.go
index 16ac7e6ad..441503b0f 100644
--- a/internal/mcp/server/extract/extract.go
+++ b/internal/mcp/server/extract/extract.go
@@ -9,7 +9,7 @@ package extract
import (
"github.com/ActiveMemory/ctx/internal/config/cli"
"github.com/ActiveMemory/ctx/internal/config/mcp/field"
- mcperr "github.com/ActiveMemory/ctx/internal/err/mcp"
+ errMcp "github.com/ActiveMemory/ctx/internal/err/mcp"
"github.com/ActiveMemory/ctx/internal/mcp/handler"
)
@@ -29,7 +29,7 @@ func EntryArgs(
content, _ := args[field.Content].(string)
if entryType == "" || content == "" {
- return "", "", mcperr.TypeContentRequired()
+ return "", "", errMcp.TypeContentRequired()
}
return entryType, content, nil
diff --git a/internal/mcp/server/resource/resource.go b/internal/mcp/server/resource/resource.go
index 96f3d4ed1..b6673c4d1 100644
--- a/internal/mcp/server/resource/resource.go
+++ b/internal/mcp/server/resource/resource.go
@@ -12,7 +12,7 @@ import (
"strings"
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/mcp/mime"
"github.com/ActiveMemory/ctx/internal/config/token"
@@ -78,7 +78,7 @@ func readAgentPacket(
tokensUsed := ctxToken.EstimateTokensString(header)
var skipped []string
- for _, fileName := range ctxCfg.ReadOrder {
+ for _, fileName := range cfgCtx.ReadOrder {
f := ctx.File(fileName)
if f == nil || f.IsEmpty {
continue
diff --git a/internal/mcp/server/route/prompt/dispatch.go b/internal/mcp/server/route/prompt/dispatch.go
index 11c169260..dcce69264 100644
--- a/internal/mcp/server/route/prompt/dispatch.go
+++ b/internal/mcp/server/route/prompt/dispatch.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/mcp/prompt"
"github.com/ActiveMemory/ctx/internal/mcp/handler"
"github.com/ActiveMemory/ctx/internal/mcp/proto"
- promptdef "github.com/ActiveMemory/ctx/internal/mcp/server/def/prompt"
+ defPrompt "github.com/ActiveMemory/ctx/internal/mcp/server/def/prompt"
"github.com/ActiveMemory/ctx/internal/mcp/server/out"
)
@@ -28,7 +28,7 @@ import (
// - *proto.Response: prompt list response
func DispatchList(req proto.Request) *proto.Response {
return out.OkResponse(
- req.ID, proto.PromptListResult{Prompts: promptdef.Defs},
+ req.ID, proto.PromptListResult{Prompts: defPrompt.Defs},
)
}
diff --git a/internal/mcp/server/route/prompt/entry.go b/internal/mcp/server/route/prompt/entry.go
index fabd290f0..891e6a4e1 100644
--- a/internal/mcp/server/route/prompt/entry.go
+++ b/internal/mcp/server/route/prompt/entry.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/mcp/prompt"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/mcp/proto"
- promptdef "github.com/ActiveMemory/ctx/internal/mcp/server/def/prompt"
+ defPrompt "github.com/ActiveMemory/ctx/internal/mcp/server/def/prompt"
"github.com/ActiveMemory/ctx/internal/mcp/server/out"
)
@@ -30,7 +30,7 @@ import (
// Returns:
// - *proto.Response: formatted entry prompt
func buildEntry(
- id json.RawMessage, spec promptdef.EntryPromptSpec,
+ id json.RawMessage, spec defPrompt.EntryPromptSpec,
) *proto.Response {
fieldFmt := desc.Text(spec.FieldFmtK)
diff --git a/internal/mcp/server/route/prompt/prompt.go b/internal/mcp/server/route/prompt/prompt.go
index a906612a8..65e545cb3 100644
--- a/internal/mcp/server/route/prompt/prompt.go
+++ b/internal/mcp/server/route/prompt/prompt.go
@@ -13,7 +13,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/config/cli"
- ctxCfg "github.com/ActiveMemory/ctx/internal/config/ctx"
+ cfgCtx "github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/mcp/field"
"github.com/ActiveMemory/ctx/internal/config/mcp/mime"
@@ -21,7 +21,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/context/load"
"github.com/ActiveMemory/ctx/internal/mcp/proto"
- promptdef "github.com/ActiveMemory/ctx/internal/mcp/server/def/prompt"
+ defPrompt "github.com/ActiveMemory/ctx/internal/mcp/server/def/prompt"
"github.com/ActiveMemory/ctx/internal/mcp/server/out"
"github.com/ActiveMemory/ctx/internal/mcp/server/stat"
)
@@ -51,7 +51,7 @@ func sessionStart(
sb.WriteString(token.NewlineLF)
sb.WriteString(token.NewlineLF)
- for _, fileName := range ctxCfg.ReadOrder {
+ for _, fileName := range cfgCtx.ReadOrder {
f := ctx.File(fileName)
if f == nil || f.IsEmpty {
continue
@@ -146,12 +146,12 @@ func checkpoint(
func addDecision(
id json.RawMessage, args map[string]string,
) *proto.Response {
- return buildEntry(id, promptdef.EntryPromptSpec{
+ return buildEntry(id, defPrompt.EntryPromptSpec{
KeyHeader: text.DescKeyMCPPromptAddDecisionHeader,
KeyFooter: text.DescKeyMCPPromptAddDecisionFooter,
FieldFmtK: text.DescKeyMCPPromptAddDecisionFieldFmt,
KeyResultD: text.DescKeyMCPPromptAddDecisionResultD,
- Fields: []promptdef.EntryField{
+ Fields: []defPrompt.EntryField{
{KeyLabel: text.DescKeyMCPPromptLabelDecision,
Value: args[field.Content]},
{KeyLabel: text.DescKeyMCPPromptLabelContext,
@@ -176,12 +176,12 @@ func addDecision(
func addLearning(
id json.RawMessage, args map[string]string,
) *proto.Response {
- return buildEntry(id, promptdef.EntryPromptSpec{
+ return buildEntry(id, defPrompt.EntryPromptSpec{
KeyHeader: text.DescKeyMCPPromptAddLearningHeader,
KeyFooter: text.DescKeyMCPPromptAddLearningFooter,
FieldFmtK: text.DescKeyMCPPromptAddLearningFieldFmt,
KeyResultD: text.DescKeyMCPPromptAddLearningResultD,
- Fields: []promptdef.EntryField{
+ Fields: []defPrompt.EntryField{
{KeyLabel: text.DescKeyMCPPromptLabelLearning,
Value: args[field.Content]},
{KeyLabel: text.DescKeyMCPPromptLabelContext,
diff --git a/internal/mcp/server/route/tool/dispatch.go b/internal/mcp/server/route/tool/dispatch.go
index 76fb5ea32..46bd0f774 100644
--- a/internal/mcp/server/route/tool/dispatch.go
+++ b/internal/mcp/server/route/tool/dispatch.go
@@ -15,7 +15,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/mcp/tool"
"github.com/ActiveMemory/ctx/internal/mcp/handler"
"github.com/ActiveMemory/ctx/internal/mcp/proto"
- tooldef "github.com/ActiveMemory/ctx/internal/mcp/server/def/tool"
+ defTool "github.com/ActiveMemory/ctx/internal/mcp/server/def/tool"
"github.com/ActiveMemory/ctx/internal/mcp/server/out"
)
@@ -27,7 +27,7 @@ import (
// Returns:
// - *proto.Response: tool list response
func DispatchList(req proto.Request) *proto.Response {
- return out.OkResponse(req.ID, proto.ToolListResult{Tools: tooldef.Defs})
+ return out.OkResponse(req.ID, proto.ToolListResult{Tools: defTool.Defs})
}
// DispatchCall unmarshals tool call params and dispatches to the
diff --git a/internal/mcp/server/route/tool/tool.go b/internal/mcp/server/route/tool/tool.go
index 12dfe2d7a..e5aa22f52 100644
--- a/internal/mcp/server/route/tool/tool.go
+++ b/internal/mcp/server/route/tool/tool.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/mcp/cfg"
"github.com/ActiveMemory/ctx/internal/config/mcp/field"
- timeCfg "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/mcp/handler"
"github.com/ActiveMemory/ctx/internal/mcp/proto"
"github.com/ActiveMemory/ctx/internal/mcp/server/extract"
@@ -88,7 +88,7 @@ func recall(
var since time.Time
if sinceStr, _ := args[field.Since].(string); sinceStr != "" {
var parseErr error
- since, parseErr = time.Parse(timeCfg.DateFormat, sinceStr)
+ since, parseErr = time.Parse(cfgTime.DateFormat, sinceStr)
if parseErr != nil {
return out.ToolError(
id, fmt.Sprintf(
diff --git a/internal/memory/discover.go b/internal/memory/discover.go
index 4960abc15..ac96e2bc9 100644
--- a/internal/memory/discover.go
+++ b/internal/memory/discover.go
@@ -13,7 +13,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/dir"
"github.com/ActiveMemory/ctx/internal/config/memory"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/memory"
+ errMemory "github.com/ActiveMemory/ctx/internal/err/memory"
)
// DiscoverMemoryPath locates Claude Code's auto memory file for the
@@ -26,19 +26,19 @@ import (
func DiscoverMemoryPath(projectRoot string) (string, error) {
abs, absErr := filepath.Abs(projectRoot)
if absErr != nil {
- return "", ctxerr.DiscoverResolveRoot(absErr)
+ return "", errMemory.DiscoverResolveRoot(absErr)
}
home, homeErr := os.UserHomeDir()
if homeErr != nil {
- return "", ctxerr.DiscoverResolveHome(homeErr)
+ return "", errMemory.DiscoverResolveHome(homeErr)
}
slug := ProjectSlug(abs)
memPath := filepath.Join(home, dir.Claude, dir.Projects, slug, dir.Memory, memory.MemorySource)
if _, statErr := os.Stat(memPath); statErr != nil {
- return "", ctxerr.DiscoverNoMemory(memPath)
+ return "", errMemory.DiscoverNoMemory(memPath)
}
return memPath, nil
}
diff --git a/internal/memory/mirror.go b/internal/memory/mirror.go
index 6ac03d3d5..c59d59b05 100644
--- a/internal/memory/mirror.go
+++ b/internal/memory/mirror.go
@@ -20,9 +20,9 @@ import (
"github.com/ActiveMemory/ctx/internal/config/file"
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/config/memory"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/config/token"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/memory"
+ errMemory "github.com/ActiveMemory/ctx/internal/err/memory"
"github.com/ActiveMemory/ctx/internal/io"
)
@@ -34,7 +34,7 @@ func Sync(contextDir, sourcePath string) (SyncResult, error) {
sourceData, readErr := io.SafeReadUserFile(sourcePath)
if readErr != nil {
- return SyncResult{}, ctxerr.ReadSource(readErr)
+ return SyncResult{}, errMemory.ReadSource(readErr)
}
result := SyncResult{
@@ -48,17 +48,17 @@ func Sync(contextDir, sourcePath string) (SyncResult, error) {
result.MirrorLines = countLines(existingData)
archivePath, archiveErr := Archive(contextDir)
if archiveErr != nil {
- return SyncResult{}, ctxerr.ArchivePrevious(archiveErr)
+ return SyncResult{}, errMemory.ArchivePrevious(archiveErr)
}
result.ArchivedTo = archivePath
}
if mkErr := os.MkdirAll(mirrorDir, fs.PermExec); mkErr != nil {
- return SyncResult{}, ctxerr.CreateDir(mkErr)
+ return SyncResult{}, errMemory.CreateDir(mkErr)
}
if writeErr := os.WriteFile(mirrorPath, sourceData, fs.PermFile); writeErr != nil {
- return SyncResult{}, ctxerr.WriteMirror(writeErr)
+ return SyncResult{}, errMemory.WriteMirror(writeErr)
}
return result, nil
@@ -72,18 +72,18 @@ func Archive(contextDir string) (string, error) {
data, readErr := io.SafeReadUserFile(mirrorPath)
if readErr != nil {
- return "", ctxerr.ReadMirrorArchive(readErr)
+ return "", errMemory.ReadMirrorArchive(readErr)
}
if mkErr := os.MkdirAll(archiveDir, fs.PermExec); mkErr != nil {
- return "", ctxerr.CreateArchiveDir(mkErr)
+ return "", errMemory.CreateArchiveDir(mkErr)
}
- ts := time.Now().Format(time2.TimestampCompact)
+ ts := time.Now().Format(cfgTime.TimestampCompact)
archivePath := filepath.Join(archiveDir, memory.PrefixMirror+ts+file.ExtMarkdown)
if writeErr := os.WriteFile(archivePath, data, fs.PermFile); writeErr != nil {
- return "", ctxerr.WriteArchive(writeErr)
+ return "", errMemory.WriteArchive(writeErr)
}
return archivePath, nil
@@ -96,12 +96,12 @@ func Diff(contextDir, sourcePath string) (string, error) {
mirrorData, mirrorErr := io.SafeReadUserFile(mirrorPath)
if mirrorErr != nil {
- return "", ctxerr.ReadMirror(mirrorErr)
+ return "", errMemory.ReadMirror(mirrorErr)
}
sourceData, sourceErr := io.SafeReadUserFile(sourcePath)
if sourceErr != nil {
- return "", ctxerr.ReadDiffSource(sourceErr)
+ return "", errMemory.ReadDiffSource(sourceErr)
}
if bytes.Equal(mirrorData, sourceData) {
diff --git a/internal/memory/promote.go b/internal/memory/promote.go
index dc1a6b1b5..58addf5b8 100644
--- a/internal/memory/promote.go
+++ b/internal/memory/promote.go
@@ -10,7 +10,7 @@ import (
"github.com/ActiveMemory/ctx/internal/assets/read/desc"
"github.com/ActiveMemory/ctx/internal/config/embed/text"
"github.com/ActiveMemory/ctx/internal/config/entry"
- ctxentry "github.com/ActiveMemory/ctx/internal/entry"
+ ctxEntry "github.com/ActiveMemory/ctx/internal/entry"
)
// Promote writes a classified entry to the appropriate .context/ file.
@@ -28,7 +28,7 @@ func Promote(e Entry, classification Classification) error {
// (first line, trimmed of Markdown markers)
title := extractTitle(e.Text)
- params := ctxentry.Params{
+ params := ctxEntry.Params{
Type: classification.Target,
Content: title,
}
@@ -51,5 +51,5 @@ func Promote(e Entry, classification Classification) error {
// Conventions just need content: FormatConvention handles the rest
}
- return ctxentry.Write(params)
+ return ctxEntry.Write(params)
}
diff --git a/internal/memory/publish.go b/internal/memory/publish.go
index b25e10901..a75449b78 100644
--- a/internal/memory/publish.go
+++ b/internal/memory/publish.go
@@ -17,7 +17,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/marker"
"github.com/ActiveMemory/ctx/internal/config/memory"
"github.com/ActiveMemory/ctx/internal/config/token"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/memory"
+ errMemory "github.com/ActiveMemory/ctx/internal/err/memory"
"github.com/ActiveMemory/ctx/internal/io"
)
@@ -157,7 +157,7 @@ func RemovePublished(content string) (string, bool) {
func Publish(contextDir, memoryPath string, budget int) (PublishResult, error) {
result, selectErr := SelectContent(contextDir, budget)
if selectErr != nil {
- return PublishResult{}, ctxerr.SelectContent(selectErr)
+ return PublishResult{}, errMemory.SelectContent(selectErr)
}
formatted := result.Format()
@@ -173,7 +173,7 @@ func Publish(contextDir, memoryPath string, budget int) (PublishResult, error) {
if writeErr := os.WriteFile(
memoryPath, []byte(merged), fs.PermFile,
); writeErr != nil {
- return PublishResult{}, ctxerr.WriteMemory(writeErr)
+ return PublishResult{}, errMemory.WriteMemory(writeErr)
}
return result, nil
diff --git a/internal/memory/publish_test.go b/internal/memory/publish_test.go
index 8f5cda105..d0071ff25 100644
--- a/internal/memory/publish_test.go
+++ b/internal/memory/publish_test.go
@@ -17,8 +17,8 @@ import (
"github.com/ActiveMemory/ctx/internal/config/ctx"
"github.com/ActiveMemory/ctx/internal/config/dir"
"github.com/ActiveMemory/ctx/internal/config/marker"
- cfgmem "github.com/ActiveMemory/ctx/internal/config/memory"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgMem "github.com/ActiveMemory/ctx/internal/config/memory"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/rc"
)
@@ -151,7 +151,7 @@ func TestSelectContent(t *testing.T) {
}
// Create DECISIONS.md with a recent entry
- ts := time.Now().Format(time2.TimestampCompact)
+ ts := time.Now().Format(cfgTime.TimestampCompact)
decisions := fmt.Sprintf("# Decisions\n\n## [%s] Use SQLite\n\nContext: testing\n", ts)
if writeErr := os.WriteFile(filepath.Join(contextDir, ctx.Decision), []byte(decisions), 0o644); writeErr != nil {
t.Fatal(writeErr)
@@ -168,7 +168,7 @@ func TestSelectContent(t *testing.T) {
t.Fatal(writeErr)
}
- result, selectErr := SelectContent(contextDir, cfgmem.DefaultPublishBudget)
+ result, selectErr := SelectContent(contextDir, cfgMem.DefaultPublishBudget)
if selectErr != nil {
t.Fatalf("SelectContent: %v", selectErr)
}
diff --git a/internal/memory/state.go b/internal/memory/state.go
index 44acaed0d..fcd7a0cf8 100644
--- a/internal/memory/state.go
+++ b/internal/memory/state.go
@@ -20,7 +20,7 @@ import (
cfgFmt "github.com/ActiveMemory/ctx/internal/config/format"
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/config/memory"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/io"
)
@@ -90,7 +90,7 @@ func (s *State) Imported(hash string) bool {
// MarkImported records an entry hash with its target and date.
func (s *State) MarkImported(hash, target string) {
- date := time.Now().Format(time2.DateFormat)
+ date := time.Now().Format(cfgTime.DateFormat)
entry := strings.Join([]string{hash, target, date}, token.Colon)
s.ImportedHashes = append(s.ImportedHashes, entry)
}
diff --git a/internal/notify/notify.go b/internal/notify/notify.go
index 69b43094a..faf1b950c 100644
--- a/internal/notify/notify.go
+++ b/internal/notify/notify.go
@@ -18,7 +18,7 @@ import (
"path/filepath"
"time"
- cryptoCfg "github.com/ActiveMemory/ctx/internal/config/crypto"
+ cfgCrypto "github.com/ActiveMemory/ctx/internal/config/crypto"
"github.com/ActiveMemory/ctx/internal/config/fs"
"github.com/ActiveMemory/ctx/internal/crypto"
"github.com/ActiveMemory/ctx/internal/io"
@@ -35,7 +35,7 @@ import (
// - error: non-nil only if decryption fails (missing files are silent)
func LoadWebhook() (string, error) {
kp := rc.KeyPath()
- encPath := filepath.Join(rc.ContextDir(), cryptoCfg.NotifyEnc)
+ encPath := filepath.Join(rc.ContextDir(), cfgCrypto.NotifyEnc)
key, err := crypto.LoadKey(kp)
if err != nil {
@@ -72,7 +72,7 @@ func LoadWebhook() (string, error) {
// - error: non-nil if key generation, encryption, or file write fails
func SaveWebhook(url string) error {
kp := rc.KeyPath()
- encPath := filepath.Join(rc.ContextDir(), cryptoCfg.NotifyEnc)
+ encPath := filepath.Join(rc.ContextDir(), cfgCrypto.NotifyEnc)
key, err := crypto.LoadKey(kp)
if err != nil {
diff --git a/internal/parse/date.go b/internal/parse/date.go
index b12ff67ec..b90a3733b 100644
--- a/internal/parse/date.go
+++ b/internal/parse/date.go
@@ -9,7 +9,7 @@ package parse
import (
"time"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
)
// Date parses a YYYY-MM-DD string into a time.Time at midnight UTC.
@@ -25,5 +25,5 @@ func Date(s string) (time.Time, error) {
if s == "" {
return time.Time{}, nil
}
- return time.Parse(time2.DateFormat, s)
+ return time.Parse(cfgTime.DateFormat, s)
}
diff --git a/internal/recall/parser/claude.go b/internal/recall/parser/claude.go
index 2d8475516..8aa06ee5b 100644
--- a/internal/recall/parser/claude.go
+++ b/internal/recall/parser/claude.go
@@ -19,7 +19,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/parser"
"github.com/ActiveMemory/ctx/internal/config/session"
"github.com/ActiveMemory/ctx/internal/entity"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/parser"
+ errParser "github.com/ActiveMemory/ctx/internal/err/parser"
)
// ClaudeCodeParser parses Claude Code JSONL session files.
@@ -108,7 +108,7 @@ func (p *ClaudeCodeParser) Matches(path string) bool {
func (p *ClaudeCodeParser) ParseFile(path string) ([]*entity.Session, error) {
f, openErr := os.Open(filepath.Clean(path))
if openErr != nil {
- return nil, ctxerr.OpenFile(openErr)
+ return nil, errParser.OpenFile(openErr)
}
defer func() { _ = f.Close() }()
@@ -147,7 +147,7 @@ func (p *ClaudeCodeParser) ParseFile(path string) ([]*entity.Session, error) {
}
if scanErr := scanner.Err(); scanErr != nil {
- return nil, ctxerr.ScanFile(scanErr)
+ return nil, errParser.ScanFile(scanErr)
}
// Convert to sessions
@@ -187,7 +187,7 @@ func (p *ClaudeCodeParser) ParseLine(line []byte) (*entity.Message, string, erro
var raw claudeRawMessage
if unmarshalErr := json.Unmarshal(line, &raw); unmarshalErr != nil {
- return nil, "", ctxerr.Unmarshal(unmarshalErr)
+ return nil, "", errParser.Unmarshal(unmarshalErr)
}
// Skip non-message lines
diff --git a/internal/recall/parser/markdown.go b/internal/recall/parser/markdown.go
index fbb55806c..68a5d3e86 100644
--- a/internal/recall/parser/markdown.go
+++ b/internal/recall/parser/markdown.go
@@ -16,7 +16,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/parser"
"github.com/ActiveMemory/ctx/internal/config/session"
"github.com/ActiveMemory/ctx/internal/entity"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/parser"
+ errParser "github.com/ActiveMemory/ctx/internal/err/parser"
)
// MarkdownSessionParser parses Markdown session files written by AI agents.
@@ -110,7 +110,7 @@ func (p *MarkdownSessionParser) Matches(path string) bool {
func (p *MarkdownSessionParser) ParseFile(path string) ([]*entity.Session, error) {
content, err := os.ReadFile(filepath.Clean(path))
if err != nil {
- return nil, ctxerr.ReadFile(err)
+ return nil, errParser.ReadFile(err)
}
s := p.parseMarkdownSession(string(content), path)
diff --git a/internal/recall/parser/parser.go b/internal/recall/parser/parser.go
index 2c1015e1a..e9db58672 100644
--- a/internal/recall/parser/parser.go
+++ b/internal/recall/parser/parser.go
@@ -14,7 +14,7 @@ import (
"github.com/ActiveMemory/ctx/internal/config/parser"
"github.com/ActiveMemory/ctx/internal/entity"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/parser"
+ errParser "github.com/ActiveMemory/ctx/internal/err/parser"
)
// registeredParsers holds all available session parsers.
@@ -40,7 +40,7 @@ func ParseFile(path string) ([]*entity.Session, error) {
return parser.ParseFile(path)
}
}
- return nil, ctxerr.NoMatch(path)
+ return nil, errParser.NoMatch(path)
}
// ScanDirectory recursively scans a directory for session files.
@@ -103,7 +103,7 @@ func ScanDirectoryWithErrors(dir string) ([]*entity.Session, []error, error) {
if parser.Matches(path) {
sessions, err := parser.ParseFile(path)
if err != nil {
- parseErrors = append(parseErrors, ctxerr.FileError(path, err))
+ parseErrors = append(parseErrors, errParser.FileError(path, err))
break
}
allSessions = append(allSessions, sessions...)
@@ -115,7 +115,7 @@ func ScanDirectoryWithErrors(dir string) ([]*entity.Session, []error, error) {
})
if err != nil {
- return nil, nil, ctxerr.WalkDir(err)
+ return nil, nil, errParser.WalkDir(err)
}
// Sort by start time (newest first)
diff --git a/internal/tidy/archive.go b/internal/tidy/archive.go
index 6fd64cd55..3f2421897 100644
--- a/internal/tidy/archive.go
+++ b/internal/tidy/archive.go
@@ -15,9 +15,9 @@ import (
"github.com/ActiveMemory/ctx/internal/config/archive"
"github.com/ActiveMemory/ctx/internal/config/dir"
"github.com/ActiveMemory/ctx/internal/config/fs"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/config/token"
- ctxerr "github.com/ActiveMemory/ctx/internal/err/backup"
+ errBackup "github.com/ActiveMemory/ctx/internal/err/backup"
"github.com/ActiveMemory/ctx/internal/rc"
)
@@ -35,11 +35,11 @@ import (
func WriteArchive(prefix, heading, content string) (string, error) {
archiveDir := filepath.Join(rc.ContextDir(), dir.Archive)
if mkErr := os.MkdirAll(archiveDir, fs.PermExec); mkErr != nil {
- return "", ctxerr.CreateArchiveDir(mkErr)
+ return "", errBackup.CreateArchiveDir(mkErr)
}
now := time.Now()
- dateStr := now.Format(time2.DateFormat)
+ dateStr := now.Format(cfgTime.DateFormat)
archiveFile := filepath.Join(
archiveDir,
fmt.Sprintf(archive.TplArchiveFilename, prefix, dateStr),
@@ -55,7 +55,7 @@ func WriteArchive(prefix, heading, content string) (string, error) {
}
if writeErr := os.WriteFile(archiveFile, []byte(finalContent), fs.PermFile); writeErr != nil {
- return "", ctxerr.WriteArchive(writeErr)
+ return "", errBackup.WriteArchive(writeErr)
}
return archiveFile, nil
diff --git a/internal/tidy/parse.go b/internal/tidy/parse.go
index e0f8cc48e..82346a265 100644
--- a/internal/tidy/parse.go
+++ b/internal/tidy/parse.go
@@ -11,7 +11,7 @@ import (
"time"
"github.com/ActiveMemory/ctx/internal/config/regex"
- time2 "github.com/ActiveMemory/ctx/internal/config/time"
+ cfgTime "github.com/ActiveMemory/ctx/internal/config/time"
"github.com/ActiveMemory/ctx/internal/config/token"
"github.com/ActiveMemory/ctx/internal/entity"
"github.com/ActiveMemory/ctx/internal/task"
@@ -111,7 +111,7 @@ func parseDoneTimestamp(line string) *time.Time {
}
// Parse YYYY-MM-DD-HHMMSS format
- t, err := time.Parse(time2.TimestampCompact, match[1])
+ t, err := time.Parse(cfgTime.TimestampCompact, match[1])
if err != nil {
return nil
}
diff --git a/internal/validation/path.go b/internal/validation/path.go
index bb9f12e9c..a507b0476 100644
--- a/internal/validation/path.go
+++ b/internal/validation/path.go
@@ -11,8 +11,8 @@ import (
"path/filepath"
"strings"
- errctx "github.com/ActiveMemory/ctx/internal/err/context"
- fserr "github.com/ActiveMemory/ctx/internal/err/fs"
+ errCtx "github.com/ActiveMemory/ctx/internal/err/context"
+ errFs "github.com/ActiveMemory/ctx/internal/err/fs"
)
// ValidateBoundary checks that dir resolves to a path within the current
@@ -21,19 +21,19 @@ import (
func ValidateBoundary(dir string) error {
cwd, err := os.Getwd()
if err != nil {
- return fserr.BoundaryViolation(err)
+ return errFs.BoundaryViolation(err)
}
absDir, err := filepath.Abs(dir)
if err != nil {
- return fserr.BoundaryViolation(err)
+ return errFs.BoundaryViolation(err)
}
// Resolve symlinks in both paths so traversal via symlinked parents
// is caught.
resolvedCwd, err := filepath.EvalSymlinks(cwd)
if err != nil {
- return fserr.BoundaryViolation(err)
+ return errFs.BoundaryViolation(err)
}
resolvedDir, err := filepath.EvalSymlinks(absDir)
@@ -47,7 +47,7 @@ func ValidateBoundary(dir string) error {
// Append os.PathSeparator to avoid "/foo/bar" matching "/foo/b".
root := resolvedCwd + string(os.PathSeparator)
if resolvedDir != resolvedCwd && !strings.HasPrefix(resolvedDir, root) {
- return errctx.OutsideRoot(dir, resolvedCwd)
+ return errCtx.OutsideRoot(dir, resolvedCwd)
}
return nil
@@ -63,7 +63,7 @@ func CheckSymlinks(dir string) error {
return nil
}
if info.Mode()&os.ModeSymlink != 0 {
- return errctx.DirSymlink(dir)
+ return errCtx.DirSymlink(dir)
}
// Check immediate children.
@@ -79,7 +79,7 @@ func CheckSymlinks(dir string) error {
continue
}
if ci.Mode()&os.ModeSymlink != 0 {
- return errctx.FileSymlink(child)
+ return errCtx.FileSymlink(child)
}
}
diff --git a/site/404.html b/site/404.html
index 8fa94ad68..585d61db2 100644
--- a/site/404.html
+++ b/site/404.html
@@ -3084,6 +3084,8 @@
+
+
@@ -3239,6 +3241,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-01-27-building-ctx-using-ctx/index.html b/site/blog/2026-01-27-building-ctx-using-ctx/index.html
index 444c3ab3f..044d6015b 100644
--- a/site/blog/2026-01-27-building-ctx-using-ctx/index.html
+++ b/site/blog/2026-01-27-building-ctx-using-ctx/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-01-ctx-v0.2.0-the-archaeology-release/index.html b/site/blog/2026-02-01-ctx-v0.2.0-the-archaeology-release/index.html
index c7af0af92..831153e98 100644
--- a/site/blog/2026-02-01-ctx-v0.2.0-the-archaeology-release/index.html
+++ b/site/blog/2026-02-01-ctx-v0.2.0-the-archaeology-release/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-01-refactoring-with-intent/index.html b/site/blog/2026-02-01-refactoring-with-intent/index.html
index 0958ad429..01588ec56 100644
--- a/site/blog/2026-02-01-refactoring-with-intent/index.html
+++ b/site/blog/2026-02-01-refactoring-with-intent/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-03-the-attention-budget/index.html b/site/blog/2026-02-03-the-attention-budget/index.html
index a4db41a5e..65c9d243b 100644
--- a/site/blog/2026-02-03-the-attention-budget/index.html
+++ b/site/blog/2026-02-03-the-attention-budget/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-04-skills-that-fight-the-platform/index.html b/site/blog/2026-02-04-skills-that-fight-the-platform/index.html
index 40c8325c9..1d0b1ead1 100644
--- a/site/blog/2026-02-04-skills-that-fight-the-platform/index.html
+++ b/site/blog/2026-02-04-skills-that-fight-the-platform/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-05-you-cant-import-expertise/index.html b/site/blog/2026-02-05-you-cant-import-expertise/index.html
index 3b6c5584e..2f3673fda 100644
--- a/site/blog/2026-02-05-you-cant-import-expertise/index.html
+++ b/site/blog/2026-02-05-you-cant-import-expertise/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-07-the-anatomy-of-a-skill-that-works/index.html b/site/blog/2026-02-07-the-anatomy-of-a-skill-that-works/index.html
index e0a28e451..ed389faf2 100644
--- a/site/blog/2026-02-07-the-anatomy-of-a-skill-that-works/index.html
+++ b/site/blog/2026-02-07-the-anatomy-of-a-skill-that-works/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-08-not-everything-is-a-skill/index.html b/site/blog/2026-02-08-not-everything-is-a-skill/index.html
index e9150d940..79fc36818 100644
--- a/site/blog/2026-02-08-not-everything-is-a-skill/index.html
+++ b/site/blog/2026-02-08-not-everything-is-a-skill/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-09-defense-in-depth-securing-ai-agents/index.html b/site/blog/2026-02-09-defense-in-depth-securing-ai-agents/index.html
index 207dc93e1..22af96cd8 100644
--- a/site/blog/2026-02-09-defense-in-depth-securing-ai-agents/index.html
+++ b/site/blog/2026-02-09-defense-in-depth-securing-ai-agents/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-12-how-deep-is-too-deep/index.html b/site/blog/2026-02-12-how-deep-is-too-deep/index.html
index 311b8099e..3cdb2e365 100644
--- a/site/blog/2026-02-12-how-deep-is-too-deep/index.html
+++ b/site/blog/2026-02-12-how-deep-is-too-deep/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-14-irc-as-context/index.html b/site/blog/2026-02-14-irc-as-context/index.html
index b2d5387e8..bbc1e2c63 100644
--- a/site/blog/2026-02-14-irc-as-context/index.html
+++ b/site/blog/2026-02-14-irc-as-context/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-14-parallel-agents-with-worktrees/index.html b/site/blog/2026-02-14-parallel-agents-with-worktrees/index.html
index d6b015eb5..c0d87a49d 100644
--- a/site/blog/2026-02-14-parallel-agents-with-worktrees/index.html
+++ b/site/blog/2026-02-14-parallel-agents-with-worktrees/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-15-ctx-v0.3.0-the-discipline-release/index.html b/site/blog/2026-02-15-ctx-v0.3.0-the-discipline-release/index.html
index 342db2c17..715c1c4d5 100644
--- a/site/blog/2026-02-15-ctx-v0.3.0-the-discipline-release/index.html
+++ b/site/blog/2026-02-15-ctx-v0.3.0-the-discipline-release/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-15-eight-ways-a-hook-can-talk/index.html b/site/blog/2026-02-15-eight-ways-a-hook-can-talk/index.html
index 082d2985a..76959dcf1 100644
--- a/site/blog/2026-02-15-eight-ways-a-hook-can-talk/index.html
+++ b/site/blog/2026-02-15-eight-ways-a-hook-can-talk/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-15-why-zensical/index.html b/site/blog/2026-02-15-why-zensical/index.html
index 6bd8aab0c..4fd51636f 100644
--- a/site/blog/2026-02-15-why-zensical/index.html
+++ b/site/blog/2026-02-15-why-zensical/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-16-ctx-v0.6.0-the-integration-release/index.html b/site/blog/2026-02-16-ctx-v0.6.0-the-integration-release/index.html
index e483c7dd9..4e477df63 100644
--- a/site/blog/2026-02-16-ctx-v0.6.0-the-integration-release/index.html
+++ b/site/blog/2026-02-16-ctx-v0.6.0-the-integration-release/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-17-code-is-cheap-judgment-is-not/index.html b/site/blog/2026-02-17-code-is-cheap-judgment-is-not/index.html
index 008ed859c..42fdbb2f5 100644
--- a/site/blog/2026-02-17-code-is-cheap-judgment-is-not/index.html
+++ b/site/blog/2026-02-17-code-is-cheap-judgment-is-not/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-17-context-as-infrastructure/index.html b/site/blog/2026-02-17-context-as-infrastructure/index.html
index 84f8aa358..f134d4693 100644
--- a/site/blog/2026-02-17-context-as-infrastructure/index.html
+++ b/site/blog/2026-02-17-context-as-infrastructure/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-17-parallel-agents-merge-debt-and-the-myth-of-overnight-progress/index.html b/site/blog/2026-02-17-parallel-agents-merge-debt-and-the-myth-of-overnight-progress/index.html
index 387742805..b690a6008 100644
--- a/site/blog/2026-02-17-parallel-agents-merge-debt-and-the-myth-of-overnight-progress/index.html
+++ b/site/blog/2026-02-17-parallel-agents-merge-debt-and-the-myth-of-overnight-progress/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-17-the-3-1-ratio/index.html b/site/blog/2026-02-17-the-3-1-ratio/index.html
index e7fa36892..2e786d4ca 100644
--- a/site/blog/2026-02-17-the-3-1-ratio/index.html
+++ b/site/blog/2026-02-17-the-3-1-ratio/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-17-when-a-system-starts-explaining-itself/index.html b/site/blog/2026-02-17-when-a-system-starts-explaining-itself/index.html
index a5e05476f..680d96b7a 100644
--- a/site/blog/2026-02-17-when-a-system-starts-explaining-itself/index.html
+++ b/site/blog/2026-02-17-when-a-system-starts-explaining-itself/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-25-the-homework-problem/index.html b/site/blog/2026-02-25-the-homework-problem/index.html
index aea96d921..0dc93778d 100644
--- a/site/blog/2026-02-25-the-homework-problem/index.html
+++ b/site/blog/2026-02-25-the-homework-problem/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-02-28-the-last-question/index.html b/site/blog/2026-02-28-the-last-question/index.html
index 341831016..95e927378 100644
--- a/site/blog/2026-02-28-the-last-question/index.html
+++ b/site/blog/2026-02-28-the-last-question/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/2026-03-04-agent-memory-is-infrastructure/index.html b/site/blog/2026-03-04-agent-memory-is-infrastructure/index.html
index 3ed01c9ef..99923a24e 100644
--- a/site/blog/2026-03-04-agent-memory-is-infrastructure/index.html
+++ b/site/blog/2026-03-04-agent-memory-is-infrastructure/index.html
@@ -3091,6 +3091,8 @@
+
+
@@ -3246,6 +3248,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/blog/index.html b/site/blog/index.html
index c66e2cd36..794c033f6 100644
--- a/site/blog/index.html
+++ b/site/blog/index.html
@@ -3102,6 +3102,8 @@
+
+
@@ -3257,6 +3259,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/config/index.html b/site/cli/config/index.html
index 97fda08fa..fa3af6416 100644
--- a/site/cli/config/index.html
+++ b/site/cli/config/index.html
@@ -3187,6 +3187,8 @@
+
+
@@ -3342,6 +3344,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/context/index.html b/site/cli/context/index.html
index 03d303e9f..ab3d85702 100644
--- a/site/cli/context/index.html
+++ b/site/cli/context/index.html
@@ -3348,6 +3348,8 @@
+
+
@@ -3503,6 +3505,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/doctor/index.html b/site/cli/doctor/index.html
index c4bc72f3b..6a9ddd28e 100644
--- a/site/cli/doctor/index.html
+++ b/site/cli/doctor/index.html
@@ -3231,6 +3231,8 @@
+
+
@@ -3386,6 +3388,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/index.html b/site/cli/index.html
index 03c036e52..d98063422 100644
--- a/site/cli/index.html
+++ b/site/cli/index.html
@@ -3102,6 +3102,8 @@
+
+
@@ -3257,6 +3259,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/init-status/index.html b/site/cli/init-status/index.html
index 3d04f8369..aabb2e692 100644
--- a/site/cli/init-status/index.html
+++ b/site/cli/init-status/index.html
@@ -3192,6 +3192,8 @@
+
+
@@ -3347,6 +3349,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/mcp/index.html b/site/cli/mcp/index.html
index 5b202b6e9..193218e55 100644
--- a/site/cli/mcp/index.html
+++ b/site/cli/mcp/index.html
@@ -3464,6 +3464,8 @@
+
+
@@ -3619,6 +3621,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/recall/index.html b/site/cli/recall/index.html
index 1b034edd3..4729c45de 100644
--- a/site/cli/recall/index.html
+++ b/site/cli/recall/index.html
@@ -3281,6 +3281,8 @@
+
+
@@ -3436,6 +3438,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/system/index.html b/site/cli/system/index.html
index e60fc959d..8f5cdd40b 100644
--- a/site/cli/system/index.html
+++ b/site/cli/system/index.html
@@ -3264,6 +3264,8 @@
+
+
@@ -3419,6 +3421,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/cli/tools/index.html b/site/cli/tools/index.html
index 3ff4195b1..93ca89a50 100644
--- a/site/cli/tools/index.html
+++ b/site/cli/tools/index.html
@@ -3663,6 +3663,8 @@
+
+
@@ -3818,6 +3820,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -5538,7 +5570,7 @@ ]ctx prompt rm
+See also: Prompt Templates
ctx remind
Session-scoped reminders that surface at session start. Reminders are
diff --git a/site/home/about/index.html b/site/home/about/index.html
index 0f4186fa4..3d26d7dca 100644
--- a/site/home/about/index.html
+++ b/site/home/about/index.html
@@ -3198,6 +3198,8 @@
+
+
@@ -3353,6 +3355,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -3796,7 +3828,7 @@ How ctx Solves This: ask questions, share workflows, and help shape what comes next
-Read the Blog →: real-world patterns, ponderings, and lessons learned from building ctx using ctx
+Read the Blog →: real-world patterns, ponderings, and lessons learned from building ctx using ctx
Ready to Get Started?
diff --git a/site/home/common-workflows/index.html b/site/home/common-workflows/index.html
index 2a2ce1f09..03157f591 100644
--- a/site/home/common-workflows/index.html
+++ b/site/home/common-workflows/index.html
@@ -3336,6 +3336,8 @@
+
+
@@ -3491,6 +3493,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/home/community/index.html b/site/home/community/index.html
index e53149158..09f4c148d 100644
--- a/site/home/community/index.html
+++ b/site/home/community/index.html
@@ -3192,6 +3192,8 @@
+
+
@@ -3347,6 +3349,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/home/configuration/index.html b/site/home/configuration/index.html
index 3ba5ffb03..5efd694b4 100644
--- a/site/home/configuration/index.html
+++ b/site/home/configuration/index.html
@@ -3392,6 +3392,8 @@
+
+
@@ -3547,6 +3549,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/home/context-files/index.html b/site/home/context-files/index.html
index ba0a3aa0c..2016fe092 100644
--- a/site/home/context-files/index.html
+++ b/site/home/context-files/index.html
@@ -3587,6 +3587,8 @@
+
+
@@ -3742,6 +3744,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/site/home/contributing/index.html b/site/home/contributing/index.html
index 1c003abcb..d0ba4ba14 100644
--- a/site/home/contributing/index.html
+++ b/site/home/contributing/index.html
@@ -3520,6 +3520,8 @@
+
+
@@ -3675,6 +3677,36 @@
+
+
+
+
+
+
+
+
+
+
+ Cutting a Release
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4410,7 +4442,7 @@