Skip to content
This repository was archived by the owner on Jul 24, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 182 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,190 @@
# codectx
# Codectx

`codectx` is a Rust CLI/runtime experiment for bounded AI coding sessions.
**A deterministic repository context compiler for humans and coding tools.**

It models a development session as a small inspectable system:
Codectx observes a Git repository, combines explicit task focus with project commands and imported external signals, and emits a focused context packet for implementation, review, or handoff.

- task goal
- allowed file scope
- current Git/worktree state
- runtime session metadata
- required evidence before exit
- out-of-scope changes and explained net moves
Git remains repository authority. Codectx does not resolve external obligations, run autonomous coding loops, or silently modify implementation files.

The goal is not to replace coding agents. The goal is to give agents and humans a lightweight context surface for understanding what changed, what is allowed, and what proof is still missing.
## Current capabilities

## Status
- human HUD through `codectx`;
- deterministic JSON through `codectx --json`;
- Git root, branch, HEAD, dirty-file, and baseline-relative observation;
- explicit focus from `.codectx/focus.toml` or `--focus PATH`;
- expected, supporting, unexpected, and unclassified path handling;
- discovery of public `justfile` recipes and definition hashes;
- expanded review output through `codectx review`;
- managed handoff output through `codectx handoff`;
- imported Runseal records retained as external claims with unknown freshness;
- the earlier session runtime retained as `codectx-legacy`.

Early public foundation. Interfaces may change.
Codectx does not currently provide closure evaluation, proof freshness, orchestration, repository indexing, a daemon, or a hosted service.

## Build
## Install

```sh
cargo fmt --all
cargo test --workspace
```bash
cargo install --path crates/codectx-cli --locked --force
```

This installs both `codectx` and `codectx-legacy`.

For repository-local development:

```bash
just codectx --help
just codectx-bins
```

## Product walkthrough

Run the committed fixture demonstration:

```bash
just demo
```

The script creates a temporary Git repository from `fixtures/context-demo/repository`, declares task focus, commits a baseline, and applies:

- one expected source change;
- one supporting documentation change;
- one unexpected operations file.

It renders and verifies the HUD, review, deterministic JSON, discovered commands, attention signal, and managed handoff.

### Human output

```text
CODECTX · context

Location
demo · <fixture-head>

Focus
DEMO-001 · Add request tracing
Keep request tracing changes focused while preserving project commands.

Changes
expected 1 · supporting 1 · unexpected 1 · unclassified 0

Attention
- unexpected_changes: 1 changed path(s) do not match the declared focus.

Next
Inspect unexpected changes
Changed paths fall outside the declared expected and supporting paths.
run: git diff --stat
```

### Review output

```text
Changed paths
- README.md [Supporting]
- ops/emergency.toml [Unexpected]
- src/lib.rs [Expected]

Constraints
- keep the public API stable

Available commands
- just check
- just format
```

### JSON excerpt

```json
{
"schema_version": 1,
"repository": {
"root": "<fixture-repository>",
"branch": "demo",
"head": "<fixture-head>"
},
"focus": {
"id": "DEMO-001",
"title": "Add request tracing"
},
"changes": {
"changed_files": [
{ "path": "README.md", "relevance": "supporting" },
{ "path": "ops/emergency.toml", "relevance": "unexpected" },
{ "path": "src/lib.rs", "relevance": "expected" }
]
},
"runnables": [
{ "id": "check", "command": "just check", "visibility": "public" },
{ "id": "format", "command": "just format", "visibility": "public" }
],
"attention": [
{ "code": "unexpected_changes", "level": "warning" }
]
}
```

The handoff command writes the same packet to:

```text
.codectx/handoffs/latest.json
```

## Use Codectx in a repository

Create `.codectx/focus.toml`:

```toml
id = "CX-0001"
title = "Add deterministic context packet output"
summary = "Expose focused repository state to implementers and reviewers."
expected_paths = ["crates/codectx-core/**", "crates/codectx-cli/**"]
supporting_paths = ["README.md", "docs/**", ".github/**"]
constraints = [
"preserve Git as repository authority",
"retain source authority labels",
"derive human and JSON output from one packet",
]
```

Primary surfaces:

```bash
codectx
codectx --json
codectx review
codectx handoff
```

## Packet contract

The packet contains repository, focus, changes, runnables, external signals, attention items, actions, and source references. Human and review outputs are projections of the same packet used for JSON and handoff.

See [`docs/context-packet-v1.md`](docs/context-packet-v1.md) for the schema and authority rules.

## Runseal boundary

Runseal owns persistent obligations, evidence semantics, resolution, and regression. Codectx may display a Runseal record:

```bash
codectx --runseal-closure ../runseal/closures/RS-0001.toml
```

Imported records remain external claims with unknown freshness. Codectx reports the record; it does not independently validate its current truth.

## Legacy compatibility

The previous bounded-session implementation remains available:

```bash
codectx-legacy --help
```

## Proof

Run the accepted repository proof:

```bash
just check
```

It covers formatting, Clippy with denied warnings, workspace tests, binary help, and the fixture-backed product walkthrough.
4 changes: 4 additions & 0 deletions crates/codectx-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ repository.workspace = true

[[bin]]
name = "codectx"
path = "src/context_main.rs"

[[bin]]
name = "codectx-legacy"
path = "src/main.rs"

[dependencies]
Expand Down
152 changes: 152 additions & 0 deletions crates/codectx-cli/src/context_main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use anyhow::{anyhow, bail, Result};
use camino::Utf8PathBuf;
use codectx_core::{render_context_hud, render_review, write_context_packet, ContextPacketBuilder};
use std::{env, path::PathBuf};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Mode {
Context,
Review,
Handoff,
}

#[derive(Debug)]
struct ParsedArgs {
mode: Mode,
json: bool,
focus: Option<Utf8PathBuf>,
baseline: Option<String>,
runseal_closures: Vec<Utf8PathBuf>,
}

fn main() -> Result<()> {
let args: Vec<String> = env::args().skip(1).collect();
if args.iter().any(|arg| arg == "--help" || arg == "-h") {
print_help();
return Ok(());
}

let parsed = parse_args(args)?;
let mut builder = ContextPacketBuilder::new();
if let Some(baseline) = parsed.baseline {
builder = builder.baseline(baseline);
}
if let Some(focus) = parsed.focus {
builder = builder.focus_path(focus);
}
for closure in parsed.runseal_closures {
builder = builder.runseal_closure(closure);
}

let packet = builder.build_current(env::current_dir()?)?;
match parsed.mode {
Mode::Context => {
if parsed.json {
print!("{}", packet.to_pretty_json()?);
} else {
print!("{}", render_context_hud(&packet));
}
}
Mode::Review => {
if parsed.json {
print!("{}", packet.to_pretty_json()?);
} else {
print!("{}", render_review(&packet));
}
}
Mode::Handoff => {
let output = packet.repository.root.join(".codectx/handoffs/latest.json");
write_context_packet(&output, &packet)?;
if parsed.json {
print!("{}", packet.to_pretty_json()?);
} else {
println!("handoff written: {output}");
print!("{}", render_context_hud(&packet));
}
}
}

Ok(())
}

fn parse_args(args: Vec<String>) -> Result<ParsedArgs> {
let mut mode = Mode::Context;
let mut mode_was_set = false;
let mut json = false;
let mut focus = None;
let mut baseline = None;
let mut runseal_closures = Vec::new();
let mut index = 0usize;

while index < args.len() {
match args[index].as_str() {
"packet" => set_mode(&mut mode, &mut mode_was_set, Mode::Context)?,
"review" => set_mode(&mut mode, &mut mode_was_set, Mode::Review)?,
"handoff" => set_mode(&mut mode, &mut mode_was_set, Mode::Handoff)?,
"--json" => json = true,
"--focus" => {
index += 1;
focus = Some(next_path(&args, index, "--focus")?);
}
"--baseline" => {
index += 1;
baseline = Some(next_value(&args, index, "--baseline")?);
}
"--runseal-closure" => {
index += 1;
runseal_closures.push(next_path(&args, index, "--runseal-closure")?);
}
unknown => bail!("unknown Codectx context option: {unknown}"),
}
index += 1;
}

Ok(ParsedArgs {
mode,
json,
focus,
baseline,
runseal_closures,
})
}

fn set_mode(mode: &mut Mode, was_set: &mut bool, next: Mode) -> Result<()> {
if *was_set && *mode != next {
bail!("choose only one of `packet`, `review`, or `handoff`");
}
*mode = next;
*was_set = true;
Ok(())
}

fn next_value(args: &[String], index: usize, option: &str) -> Result<String> {
args.get(index)
.cloned()
.ok_or_else(|| anyhow!("{option} requires a value"))
}

fn next_path(args: &[String], index: usize, option: &str) -> Result<Utf8PathBuf> {
let value = next_value(args, index, option)?;
Utf8PathBuf::from_path_buf(PathBuf::from(value))
.map_err(|path| anyhow!("{option} path is not valid UTF-8: {}", path.display()))
}

fn print_help() {
println!(
"codectx - compile repository signals into focused context\n\
\nUSAGE:\n\
codectx [--json] [--focus PATH] [--baseline REV] [--runseal-closure PATH]...\n\
codectx review [OPTIONS]\n\
codectx handoff [OPTIONS]\n\
\nCOMMANDS:\n\
packet Render the default context packet\n\
review Render paths, constraints, commands, and attention items\n\
handoff Write .codectx/handoffs/latest.json\n\
\nOPTIONS:\n\
--json Emit deterministic JSON\n\
--focus PATH Load a focus TOML file\n\
--baseline REV Compare changes with a Git revision\n\
--runseal-closure PATH Import a Runseal record as an external claim\n\
\nThe earlier session runtime remains available as codectx-legacy."
);
}
2 changes: 1 addition & 1 deletion crates/codectx-cli/tests/cli_command_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ where
S: AsRef<std::ffi::OsStr>,
{
CommandOutput {
output: Command::new(env!("CARGO_BIN_EXE_codectx"))
output: Command::new(env!("CARGO_BIN_EXE_codectx-legacy"))
.args(args)
.current_dir(repo.path())
.output()
Expand Down
Loading