Compare commits

..

1 Commits

Author SHA1 Message Date
Yeachan-Heo
ac9005bc95 Refresh docs to match ultraworkers/claw-code source of truth
Replace the stale Python-first README narrative, old community links, and leftover branded metadata with the current Rust-first repo guidance. Also align funding handles and asset naming so the public docs point at the canonical ultraworkers/claw-code surface.\n\nConstraint: Scope limited to docs/metadata and branding residue; no runtime behavior changes\nRejected: Add a new CI lint in this pass | outside the requested docs-and-config cleanup scope\nConfidence: medium\nScope-risk: narrow\nReversibility: clean\nDirective: Keep README, funding metadata, and community links aligned with ultraworkers/claw-code and the current UltraWorkers Discord invite\nTested: stale-branding grep across markdown/.github; root doc-link existence checks; cargo fmt --all --check; cargo check --workspace; cargo test --workspace\nNot-tested: cargo clippy --workspace --all-targets -- -D warnings | fails on pre-existing runtime lint debt unrelated to these doc changes
2026-04-05 17:26:41 +00:00
8 changed files with 122 additions and 302 deletions

4
.github/FUNDING.yml vendored
View File

@@ -1 +1,3 @@
github: instructkr
github:
- ultraworkers
- Yeachan-Heo

196
README.md
View File

@@ -1,7 +1,17 @@
# Rewriting Project Claw Code
# Claw Code
<p align="center">
<strong>⭐ The fastest repo in history to surpass 50K stars, reaching the milestone in just 2 hours after publication ⭐</strong>
<a href="https://github.com/ultraworkers/claw-code">ultraworkers/claw-code</a>
·
<a href="./USAGE.md">Usage</a>
·
<a href="./rust/README.md">Rust workspace</a>
·
<a href="./PARITY.md">Parity</a>
·
<a href="./ROADMAP.md">Roadmap</a>
·
<a href="https://discord.gg/5TUQKqFWd">UltraWorkers Discord</a>
</p>
<p align="center">
@@ -9,177 +19,75 @@
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=ultraworkers/claw-code&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=ultraworkers/claw-code&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=ultraworkers/claw-code&type=Date" width="600" />
<img alt="Star history for ultraworkers/claw-code" src="https://api.star-history.com/svg?repos=ultraworkers/claw-code&type=Date" width="600" />
</picture>
</a>
</p>
<p align="center">
<img src="assets/clawd-hero.jpeg" alt="Claw" width="300" />
<img src="assets/claw-hero.jpeg" alt="Claw Code" width="300" />
</p>
<p align="center">
<strong>Autonomously maintained by lobsters/claws — not by human hands</strong>
</p>
<p align="center">
<a href="https://github.com/Yeachan-Heo/clawhip">clawhip</a> ·
<a href="https://github.com/code-yeongyu/oh-my-openagent">oh-my-openagent</a> ·
<a href="https://github.com/Yeachan-Heo/oh-my-claudecode">oh-my-claudecode</a> ·
<a href="https://github.com/Yeachan-Heo/oh-my-codex">oh-my-codex</a> ·
<a href="https://discord.gg/6ztZB9jvWq">UltraWorkers Discord</a>
</p>
Claw Code is the public Rust implementation of the `claw` CLI agent harness.
The canonical implementation lives in [`rust/`](./rust), and the current source of truth for this repository is **ultraworkers/claw-code**.
> [!IMPORTANT]
> The active Rust workspace now lives in [`rust/`](./rust). Start with [`USAGE.md`](./USAGE.md) for build, auth, CLI, session, and parity-harness workflows, then use [`rust/README.md`](./rust/README.md) for crate-level details.
> Start with [`USAGE.md`](./USAGE.md) for build, auth, CLI, session, and parity-harness workflows. Use [`rust/README.md`](./rust/README.md) for crate-level details and [`PARITY.md`](./PARITY.md) for the current Rust-port checkpoint.
> Want the bigger idea behind this repo? Read [`PHILOSOPHY.md`](./PHILOSOPHY.md) and Sigrid Jin's public explanation: https://x.com/realsigridjin/status/2039472968624185713
## Current repository shape
> Shout-out to the UltraWorkers ecosystem powering this repo: [clawhip](https://github.com/Yeachan-Heo/clawhip), [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent), [oh-my-claudecode](https://github.com/Yeachan-Heo/oh-my-claudecode), [oh-my-codex](https://github.com/Yeachan-Heo/oh-my-codex), and the [UltraWorkers Discord](https://discord.gg/6ztZB9jvWq).
- **`rust/`** — canonical Rust workspace and the `claw` CLI binary
- **`USAGE.md`** — task-oriented usage guide for the current product surface
- **`PARITY.md`** — Rust-port parity status and migration notes
- **`ROADMAP.md`** — active roadmap and cleanup backlog
- **`PHILOSOPHY.md`** — project intent and system-design framing
- **`src/` + `tests/`** — companion Python/reference workspace and audit helpers; not the primary runtime surface
---
## Backstory
This repo is maintained by **lobsters/claws**, not by a conventional human-only dev team.
The people behind the system are [Bellman / Yeachan Heo](https://github.com/Yeachan-Heo) and friends like [Yeongyu](https://github.com/code-yeongyu), but the repo itself is being pushed forward by autonomous claw workflows: parallel coding sessions, event-driven orchestration, recovery loops, and machine-readable lane state.
In practice, that means this project is not just *about* coding agents — it is being **actively built by them**. Features, tests, telemetry, docs, and workflow hardening are landed through claw-driven loops using [clawhip](https://github.com/Yeachan-Heo/clawhip), [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent), [oh-my-claudecode](https://github.com/Yeachan-Heo/oh-my-claudecode), and [oh-my-codex](https://github.com/Yeachan-Heo/oh-my-codex).
This repository exists to prove that an open coding harness can be built **autonomously, in public, and at high velocity** — with humans setting direction and claws doing the grinding.
See the public build story here:
https://x.com/realsigridjin/status/2039472968624185713
![Tweet screenshot](assets/tweet-screenshot.png)
---
## Porting Status
The main source tree is now Python-first.
- `src/` contains the active Python porting workspace
- `tests/` verifies the current Python workspace
- the exposed snapshot is no longer part of the tracked repository state
The current Python workspace is not yet a complete one-to-one replacement for the original system, but the primary implementation surface is now Python.
## Why this rewrite exists
I originally studied the exposed codebase to understand its harness, tool wiring, and agent workflow. After spending more time with the legal and ethical questions—and after reading the essay linked below—I did not want the exposed snapshot itself to remain the main tracked source tree.
This repository now focuses on Python porting work instead.
## Repository Layout
```text
.
├── src/ # Python porting workspace
│ ├── __init__.py
│ ├── commands.py
│ ├── main.py
│ ├── models.py
│ ├── port_manifest.py
│ ├── query_engine.py
│ ├── task.py
│ └── tools.py
├── tests/ # Python verification
├── assets/omx/ # OmX workflow screenshots
├── 2026-03-09-is-legal-the-same-as-legitimate-ai-reimplementation-and-the-erosion-of-copyleft.md
└── README.md
```
## Python Workspace Overview
The new Python `src/` tree currently provides:
- **`port_manifest.py`** — summarizes the current Python workspace structure
- **`models.py`** — dataclasses for subsystems, modules, and backlog state
- **`commands.py`** — Python-side command port metadata
- **`tools.py`** — Python-side tool port metadata
- **`query_engine.py`** — renders a Python porting summary from the active workspace
- **`main.py`** — a CLI entrypoint for manifest and summary output
## Quickstart
Render the Python porting summary:
## Quick start
```bash
python3 -m src.main summary
cd rust
cargo build --workspace
./target/debug/claw --help
./target/debug/claw prompt "summarize this repository"
```
Print the current Python workspace manifest:
Authenticate with either an API key or the built-in OAuth flow:
```bash
python3 -m src.main manifest
export ANTHROPIC_API_KEY="sk-ant-..."
# or
cd rust
./target/debug/claw login
```
List the current Python modules:
Run the workspace test suite:
```bash
python3 -m src.main subsystems --limit 16
cd rust
cargo test --workspace
```
Run verification:
## Documentation map
```bash
python3 -m unittest discover -s tests -v
```
- [`USAGE.md`](./USAGE.md) — quick commands, auth, sessions, config, parity harness
- [`rust/README.md`](./rust/README.md) — crate map, CLI surface, features, workspace layout
- [`PARITY.md`](./PARITY.md) — parity status for the Rust port
- [`rust/MOCK_PARITY_HARNESS.md`](./rust/MOCK_PARITY_HARNESS.md) — deterministic mock-service harness details
- [`ROADMAP.md`](./ROADMAP.md) — active roadmap and open cleanup work
- [`PHILOSOPHY.md`](./PHILOSOPHY.md) — why the project exists and how it is operated
Run the parity audit against the local ignored archive (when present):
## Ecosystem
```bash
python3 -m src.main parity-audit
```
Claw Code is built in the open alongside the broader UltraWorkers toolchain:
Inspect mirrored command/tool inventories:
- [clawhip](https://github.com/Yeachan-Heo/clawhip)
- [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent)
- [oh-my-claudecode](https://github.com/Yeachan-Heo/oh-my-claudecode)
- [oh-my-codex](https://github.com/Yeachan-Heo/oh-my-codex)
- [UltraWorkers Discord](https://discord.gg/5TUQKqFWd)
```bash
python3 -m src.main commands --limit 10
python3 -m src.main tools --limit 10
```
## Current Parity Checkpoint
The port now mirrors the archived root-entry file surface, top-level subsystem names, and command/tool inventories much more closely than before. However, it is **not yet** a full runtime-equivalent replacement for the original TypeScript system; the Python tree still contains fewer executable runtime slices than the archived source.
## Built with `oh-my-codex`
The restructuring and documentation work on this repository was AI-assisted and orchestrated with Yeachan Heo's [oh-my-codex (OmX)](https://github.com/Yeachan-Heo/oh-my-codex), layered on top of Codex.
- **`$team` mode:** used for coordinated parallel review and architectural feedback
- **`$ralph` mode:** used for persistent execution, verification, and completion discipline
- **Codex-driven workflow:** used to turn the main `src/` tree into a Python-first porting workspace
### OmX workflow screenshots
![OmX workflow screenshot 1](assets/omx/omx-readme-review-1.png)
*Ralph/team orchestration view while the README and essay context were being reviewed in terminal panes.*
![OmX workflow screenshot 2](assets/omx/omx-readme-review-2.png)
*Split-pane review and verification flow during the final README wording pass.*
## Community
<p align="center">
<a href="https://discord.gg/6ztZB9jvWq"><img src="https://img.shields.io/badge/UltraWorkers-Discord-5865F2?logo=discord&style=for-the-badge" alt="UltraWorkers Discord" /></a>
</p>
Join the [**UltraWorkers Discord**](https://discord.gg/6ztZB9jvWq) — the community around clawhip, oh-my-openagent, oh-my-claudecode, oh-my-codex, and claw-code. Come chat about LLMs, harness engineering, agent workflows, and autonomous software development.
[![Discord](https://img.shields.io/badge/Join%20Discord-UltraWorkers-5865F2?logo=discord&style=for-the-badge)](https://discord.gg/6ztZB9jvWq)
## Star History
See the chart at the top of this README.
## Ownership / Affiliation Disclaimer
## Ownership / affiliation disclaimer
- This repository does **not** claim ownership of the original Claude Code source material.
- This repository is **not affiliated with, endorsed by, or maintained by Anthropic**.

View File

@@ -276,14 +276,13 @@ Priority order: P0 = blocks CI/green state, P1 = blocks integration wiring, P2 =
3. Add release-grade binary workflow — repo has a Rust CLI and release intent, but no GitHub Actions path that builds tagged artifacts / checks release packaging before a publish step
4. Add container-first test/run docs — runtime detects Docker/Podman/container state, but docs do not show a canonical container workflow for `cargo test --workspace`, binary execution, or bind-mounted repo usage
5. Surface `doctor` / preflight diagnostics in onboarding docs and help — the CLI already has setup-diagnosis commands and branch preflight machinery, but they are not prominent enough in README/USAGE, so new users still ask manual setup questions instead of running a built-in health check first
6. Add branding/source-of-truth residue checks for docs — after repo migration, old org names can survive in badges, star-history URLs, and copied snippets; docs need a consistency pass or CI lint to catch stale branding automatically
7. Reconcile README product narrative with current repo reality — top-level docs now say the active workspace is Rust, but later sections still describe the repo as Python-first; users should not have to infer which implementation is canonical
8. Eliminate warning spam from first-run help/build path — `cargo run -p rusty-claude-cli -- --help` currently prints a wall of compile warnings before the actual help text, which pollutes the first-touch UX and hides the product surface behind unrelated noise
9. Promote `doctor` from slash-only to top-level CLI entrypoint — users naturally try `claw doctor`, but today it errors and tells them to enter a REPL or resume path first; healthcheck flows should be callable directly from the shell
10. Make machine-readable status commands actually machine-readable — `status` and `sandbox` accept the global `--output-format json` flag path, but currently still render prose tables, which breaks shell automation and agent-friendly health polling
11. Unify legacy config/skill namespaces in user-facing output — `skills` currently surfaces mixed project roots like `.codex` and `.claude`, which leaks historical layers into the current product and makes it unclear which config namespace is canonical
12. Honor JSON output on inventory commands like `skills` and `mcp` — these are exactly the commands agents and shell scripts want to inspect programmatically, but `--output-format json` still yields prose, forcing text scraping where structured inventory should exist
13. Audit `--output-format` contract across the whole CLI surface — current behavior is inconsistent by subcommand, so agents cannot trust the global flag without command-by-command probing; the format contract itself needs to become deterministic
6. Automate branding/source-of-truth residue checks in CI — the manual doc pass is done, but badges, Discord invites, copied repo URLs, and org-name drift can regress unless a cheap lint/check keeps README/docs aligned with `ultraworkers/claw-code`
7. Eliminate warning spam from first-run help/build path — `cargo run -p rusty-claude-cli -- --help` currently prints a wall of compile warnings before the actual help text, which pollutes the first-touch UX and hides the product surface behind unrelated noise
8. Promote `doctor` from slash-only to top-level CLI entrypoint — users naturally try `claw doctor`, but today it errors and tells them to enter a REPL or resume path first; healthcheck flows should be callable directly from the shell
9. Make machine-readable status commands actually machine-readable — `status` and `sandbox` accept the global `--output-format json` flag path, but currently still render prose tables, which breaks shell automation and agent-friendly health polling
10. Unify legacy config/skill namespaces in user-facing output — `skills` currently surfaces mixed project roots like `.codex` and `.claude`, which leaks historical layers into the current product and makes it unclear which config namespace is canonical
11. Honor JSON output on inventory commands like `skills` and `mcp` — these are exactly the commands agents and shell scripts want to inspect programmatically, but `--output-format json` still yields prose, forcing text scraping where structured inventory should exist
12. Audit `--output-format` contract across the whole CLI surface — current behavior is inconsistent by subcommand, so agents cannot trust the global flag without command-by-command probing; the format contract itself needs to become deterministic
**P1 — Next (integration wiring, unblocks verification)**
2. Add cross-module integration tests — **done**: 12 integration tests covering worker→recovery→policy, stale_branch→policy, green_contract→policy, reconciliation flows

View File

Before

Width:  |  Height:  |  Size: 233 KiB

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -1954,46 +1954,22 @@ pub struct PluginsCommandResult {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum DefinitionSource {
ProjectClaw,
ProjectCodex,
ProjectClaude,
UserClawConfigHome,
UserCodexHome,
UserClaw,
UserCodex,
UserClaude,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
enum DefinitionScope {
Project,
UserConfigHome,
UserHome,
}
impl DefinitionScope {
fn label(self) -> &'static str {
match self {
Self::Project => "Project (.claw)",
Self::UserConfigHome => "User ($CLAW_CONFIG_HOME)",
Self::UserHome => "User (~/.claw)",
}
}
}
impl DefinitionSource {
fn report_scope(self) -> DefinitionScope {
match self {
Self::ProjectClaw | Self::ProjectCodex | Self::ProjectClaude => {
DefinitionScope::Project
}
Self::UserClawConfigHome | Self::UserCodexHome => DefinitionScope::UserConfigHome,
Self::UserClaw | Self::UserCodex | Self::UserClaude => DefinitionScope::UserHome,
}
}
fn label(self) -> &'static str {
self.report_scope().label()
match self {
Self::ProjectCodex => "Project (.codex)",
Self::ProjectClaude => "Project (.claude)",
Self::UserCodexHome => "User ($CODEX_HOME)",
Self::UserCodex => "User (~/.codex)",
Self::UserClaude => "User (~/.claude)",
}
}
}
@@ -2326,11 +2302,6 @@ fn discover_definition_roots(cwd: &Path, leaf: &str) -> Vec<(DefinitionSource, P
let mut roots = Vec::new();
for ancestor in cwd.ancestors() {
push_unique_root(
&mut roots,
DefinitionSource::ProjectClaw,
ancestor.join(".claw").join(leaf),
);
push_unique_root(
&mut roots,
DefinitionSource::ProjectCodex,
@@ -2343,14 +2314,6 @@ fn discover_definition_roots(cwd: &Path, leaf: &str) -> Vec<(DefinitionSource, P
);
}
if let Ok(claw_config_home) = env::var("CLAW_CONFIG_HOME") {
push_unique_root(
&mut roots,
DefinitionSource::UserClawConfigHome,
PathBuf::from(claw_config_home).join(leaf),
);
}
if let Ok(codex_home) = env::var("CODEX_HOME") {
push_unique_root(
&mut roots,
@@ -2361,11 +2324,6 @@ fn discover_definition_roots(cwd: &Path, leaf: &str) -> Vec<(DefinitionSource, P
if let Some(home) = env::var_os("HOME") {
let home = PathBuf::from(home);
push_unique_root(
&mut roots,
DefinitionSource::UserClaw,
home.join(".claw").join(leaf),
);
push_unique_root(
&mut roots,
DefinitionSource::UserCodex,
@@ -2385,12 +2343,6 @@ fn discover_skill_roots(cwd: &Path) -> Vec<SkillRoot> {
let mut roots = Vec::new();
for ancestor in cwd.ancestors() {
push_unique_skill_root(
&mut roots,
DefinitionSource::ProjectClaw,
ancestor.join(".claw").join("skills"),
SkillOrigin::SkillsDir,
);
push_unique_skill_root(
&mut roots,
DefinitionSource::ProjectCodex,
@@ -2403,12 +2355,6 @@ fn discover_skill_roots(cwd: &Path) -> Vec<SkillRoot> {
ancestor.join(".claude").join("skills"),
SkillOrigin::SkillsDir,
);
push_unique_skill_root(
&mut roots,
DefinitionSource::ProjectClaw,
ancestor.join(".claw").join("commands"),
SkillOrigin::LegacyCommandsDir,
);
push_unique_skill_root(
&mut roots,
DefinitionSource::ProjectCodex,
@@ -2423,22 +2369,6 @@ fn discover_skill_roots(cwd: &Path) -> Vec<SkillRoot> {
);
}
if let Ok(claw_config_home) = env::var("CLAW_CONFIG_HOME") {
let claw_config_home = PathBuf::from(claw_config_home);
push_unique_skill_root(
&mut roots,
DefinitionSource::UserClawConfigHome,
claw_config_home.join("skills"),
SkillOrigin::SkillsDir,
);
push_unique_skill_root(
&mut roots,
DefinitionSource::UserClawConfigHome,
claw_config_home.join("commands"),
SkillOrigin::LegacyCommandsDir,
);
}
if let Ok(codex_home) = env::var("CODEX_HOME") {
let codex_home = PathBuf::from(codex_home);
push_unique_skill_root(
@@ -2457,18 +2387,6 @@ fn discover_skill_roots(cwd: &Path) -> Vec<SkillRoot> {
if let Some(home) = env::var_os("HOME") {
let home = PathBuf::from(home);
push_unique_skill_root(
&mut roots,
DefinitionSource::UserClaw,
home.join(".claw").join("skills"),
SkillOrigin::SkillsDir,
);
push_unique_skill_root(
&mut roots,
DefinitionSource::UserClaw,
home.join(".claw").join("commands"),
SkillOrigin::LegacyCommandsDir,
);
push_unique_skill_root(
&mut roots,
DefinitionSource::UserCodex,
@@ -2549,18 +2467,15 @@ fn install_skill_into(
}
fn default_skill_install_root() -> std::io::Result<PathBuf> {
if let Ok(claw_config_home) = env::var("CLAW_CONFIG_HOME") {
return Ok(PathBuf::from(claw_config_home).join("skills"));
}
if let Ok(codex_home) = env::var("CODEX_HOME") {
return Ok(PathBuf::from(codex_home).join("skills"));
}
if let Some(home) = env::var_os("HOME") {
return Ok(PathBuf::from(home).join(".claw").join("skills"));
return Ok(PathBuf::from(home).join(".codex").join("skills"));
}
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"unable to resolve a skills install root; set CLAW_CONFIG_HOME or HOME",
"unable to resolve a skills install root; set CODEX_HOME or HOME",
))
}
@@ -2926,20 +2841,22 @@ fn render_agents_report(agents: &[AgentSummary]) -> String {
String::new(),
];
for scope in [
DefinitionScope::Project,
DefinitionScope::UserConfigHome,
DefinitionScope::UserHome,
for source in [
DefinitionSource::ProjectCodex,
DefinitionSource::ProjectClaude,
DefinitionSource::UserCodexHome,
DefinitionSource::UserCodex,
DefinitionSource::UserClaude,
] {
let group = agents
.iter()
.filter(|agent| agent.source.report_scope() == scope)
.filter(|agent| agent.source == source)
.collect::<Vec<_>>();
if group.is_empty() {
continue;
}
lines.push(format!("{}:", scope.label()));
lines.push(format!("{}:", source.label()));
for agent in group {
let detail = agent_detail(agent);
match agent.shadowed_by {
@@ -2982,20 +2899,22 @@ fn render_skills_report(skills: &[SkillSummary]) -> String {
String::new(),
];
for scope in [
DefinitionScope::Project,
DefinitionScope::UserConfigHome,
DefinitionScope::UserHome,
for source in [
DefinitionSource::ProjectCodex,
DefinitionSource::ProjectClaude,
DefinitionSource::UserCodexHome,
DefinitionSource::UserCodex,
DefinitionSource::UserClaude,
] {
let group = skills
.iter()
.filter(|skill| skill.source.report_scope() == scope)
.filter(|skill| skill.source == source)
.collect::<Vec<_>>();
if group.is_empty() {
continue;
}
lines.push(format!("{}:", scope.label()));
lines.push(format!("{}:", source.label()));
for skill in group {
let mut parts = vec![skill.name.clone()];
if let Some(description) = &skill.description {
@@ -3161,7 +3080,7 @@ fn render_agents_usage(unexpected: Option<&str>) -> String {
"Agents".to_string(),
" Usage /agents [list|help]".to_string(),
" Direct CLI claw agents".to_string(),
" Sources .claw/agents, ~/.claw/agents, $CLAW_CONFIG_HOME/agents".to_string(),
" Sources .codex/agents, .claude/agents, $CODEX_HOME/agents".to_string(),
];
if let Some(args) = unexpected {
lines.push(format!(" Unexpected {args}"));
@@ -3174,8 +3093,8 @@ fn render_skills_usage(unexpected: Option<&str>) -> String {
"Skills".to_string(),
" Usage /skills [list|install <path>|help]".to_string(),
" Direct CLI claw skills [list|install <path>|help]".to_string(),
" Install root $CLAW_CONFIG_HOME/skills or ~/.claw/skills".to_string(),
" Sources .claw/skills, ~/.claw/skills, legacy /commands".to_string(),
" Install root $CODEX_HOME/skills or ~/.codex/skills".to_string(),
" Sources .codex/skills, .claude/skills, legacy /commands".to_string(),
];
if let Some(args) = unexpected {
lines.push(format!(" Unexpected {args}"));
@@ -4014,7 +3933,7 @@ mod tests {
let workspace = temp_dir("agents-workspace");
let project_agents = workspace.join(".codex").join("agents");
let user_home = temp_dir("agents-home");
let user_agents = user_home.join(".claude").join("agents");
let user_agents = user_home.join(".codex").join("agents");
write_agent(
&project_agents,
@@ -4047,10 +3966,10 @@ mod tests {
assert!(report.contains("Agents"));
assert!(report.contains("2 active agents"));
assert!(report.contains("Project (.claw):"));
assert!(report.contains("Project (.codex):"));
assert!(report.contains("planner · Project planner · gpt-5.4 · medium"));
assert!(report.contains("User (~/.claw):"));
assert!(report.contains("(shadowed by Project (.claw)) planner · User planner"));
assert!(report.contains("User (~/.codex):"));
assert!(report.contains("(shadowed by Project (.codex)) planner · User planner"));
assert!(report.contains("verifier · Verification agent · gpt-5.4-mini · high"));
let _ = fs::remove_dir_all(workspace);
@@ -4092,11 +4011,12 @@ mod tests {
assert!(report.contains("Skills"));
assert!(report.contains("3 available skills"));
assert!(report.contains("Project (.claw):"));
assert!(report.contains("Project (.codex):"));
assert!(report.contains("plan · Project planning guidance"));
assert!(report.contains("Project (.claude):"));
assert!(report.contains("deploy · Legacy deployment guidance · legacy /commands"));
assert!(report.contains("User (~/.claw):"));
assert!(report.contains("(shadowed by Project (.claw)) plan · User planning guidance"));
assert!(report.contains("User (~/.codex):"));
assert!(report.contains("(shadowed by Project (.codex)) plan · User planning guidance"));
assert!(report.contains("help · Help guidance"));
let _ = fs::remove_dir_all(workspace);
@@ -4111,8 +4031,6 @@ mod tests {
super::handle_agents_slash_command(Some("help"), &cwd).expect("agents help");
assert!(agents_help.contains("Usage /agents [list|help]"));
assert!(agents_help.contains("Direct CLI claw agents"));
assert!(agents_help
.contains("Sources .claw/agents, ~/.claw/agents, $CLAW_CONFIG_HOME/agents"));
let agents_unexpected =
super::handle_agents_slash_command(Some("show planner"), &cwd).expect("agents usage");
@@ -4121,7 +4039,7 @@ mod tests {
let skills_help =
super::handle_skills_slash_command(Some("--help"), &cwd).expect("skills help");
assert!(skills_help.contains("Usage /skills [list|install <path>|help]"));
assert!(skills_help.contains("Install root $CLAW_CONFIG_HOME/skills or ~/.claw/skills"));
assert!(skills_help.contains("Install root $CODEX_HOME/skills or ~/.codex/skills"));
assert!(skills_help.contains("legacy /commands"));
let skills_unexpected =
@@ -4295,7 +4213,7 @@ mod tests {
let listed = render_skills_report(
&load_skills_from_roots(&roots).expect("installed skills should load"),
);
assert!(listed.contains("User ($CLAW_CONFIG_HOME):"));
assert!(listed.contains("User ($CODEX_HOME):"));
assert!(listed.contains("help · Helpful skill"));
let _ = fs::remove_dir_all(workspace);

View File

@@ -1,7 +1,7 @@
use std::fs;
use std::path::{Path, PathBuf};
const STARTER_CLAW_JSON: &str = concat!(
const STARTER_CLAUDE_JSON: &str = concat!(
"{\n",
" \"permissions\": {\n",
" \"defaultMode\": \"dontAsk\"\n",
@@ -9,7 +9,7 @@ const STARTER_CLAW_JSON: &str = concat!(
"}\n",
);
const GITIGNORE_COMMENT: &str = "# Claw Code local artifacts";
const GITIGNORE_ENTRIES: [&str; 2] = [".claw/settings.local.json", ".claw/sessions/"];
const GITIGNORE_ENTRIES: [&str; 2] = [".claude/settings.local.json", ".claude/sessions/"];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum InitStatus {
@@ -80,16 +80,16 @@ struct RepoDetection {
pub(crate) fn initialize_repo(cwd: &Path) -> Result<InitReport, Box<dyn std::error::Error>> {
let mut artifacts = Vec::new();
let claw_dir = cwd.join(".claw");
let claude_dir = cwd.join(".claude");
artifacts.push(InitArtifact {
name: ".claw/",
status: ensure_dir(&claw_dir)?,
name: ".claude/",
status: ensure_dir(&claude_dir)?,
});
let claw_json = cwd.join(".claw.json");
let claude_json = cwd.join(".claude.json");
artifacts.push(InitArtifact {
name: ".claw.json",
status: write_file_if_missing(&claw_json, STARTER_CLAW_JSON)?,
name: ".claude.json",
status: write_file_if_missing(&claude_json, STARTER_CLAUDE_JSON)?,
});
let gitignore = cwd.join(".gitignore");
@@ -209,7 +209,7 @@ pub(crate) fn render_init_claude_md(cwd: &Path) -> String {
lines.push("## Working agreement".to_string());
lines.push("- Prefer small, reviewable changes and keep generated bootstrap files aligned with actual repo workflows.".to_string());
lines.push("- Keep shared defaults in `.claw.json`; reserve `.claw/settings.local.json` for machine-local overrides.".to_string());
lines.push("- Keep shared defaults in `.claude.json`; reserve `.claude/settings.local.json` for machine-local overrides.".to_string());
lines.push("- Do not overwrite existing `CLAUDE.md` content automatically; update it intentionally when repo workflows change.".to_string());
lines.push(String::new());
@@ -354,16 +354,15 @@ mod tests {
let report = initialize_repo(&root).expect("init should succeed");
let rendered = report.render();
assert!(rendered.contains(".claw/"));
assert!(rendered.contains(".claw.json"));
assert!(rendered.contains("created"));
assert!(rendered.contains(".claude/ created"));
assert!(rendered.contains(".claude.json created"));
assert!(rendered.contains(".gitignore created"));
assert!(rendered.contains("CLAUDE.md created"));
assert!(root.join(".claw").is_dir());
assert!(root.join(".claw.json").is_file());
assert!(root.join(".claude").is_dir());
assert!(root.join(".claude.json").is_file());
assert!(root.join("CLAUDE.md").is_file());
assert_eq!(
fs::read_to_string(root.join(".claw.json")).expect("read claw json"),
fs::read_to_string(root.join(".claude.json")).expect("read claude json"),
concat!(
"{\n",
" \"permissions\": {\n",
@@ -373,8 +372,8 @@ mod tests {
)
);
let gitignore = fs::read_to_string(root.join(".gitignore")).expect("read gitignore");
assert!(gitignore.contains(".claw/settings.local.json"));
assert!(gitignore.contains(".claw/sessions/"));
assert!(gitignore.contains(".claude/settings.local.json"));
assert!(gitignore.contains(".claude/sessions/"));
let claude_md = fs::read_to_string(root.join("CLAUDE.md")).expect("read claude md");
assert!(claude_md.contains("Languages: Rust."));
assert!(claude_md.contains("cargo clippy --workspace --all-targets -- -D warnings"));
@@ -387,7 +386,8 @@ mod tests {
let root = temp_dir();
fs::create_dir_all(&root).expect("create root");
fs::write(root.join("CLAUDE.md"), "custom guidance\n").expect("write existing claude md");
fs::write(root.join(".gitignore"), ".claw/settings.local.json\n").expect("write gitignore");
fs::write(root.join(".gitignore"), ".claude/settings.local.json\n")
.expect("write gitignore");
let first = initialize_repo(&root).expect("first init should succeed");
assert!(first
@@ -395,9 +395,8 @@ mod tests {
.contains("CLAUDE.md skipped (already exists)"));
let second = initialize_repo(&root).expect("second init should succeed");
let second_rendered = second.render();
assert!(second_rendered.contains(".claw/"));
assert!(second_rendered.contains(".claw.json"));
assert!(second_rendered.contains("skipped (already exists)"));
assert!(second_rendered.contains(".claude/ skipped (already exists)"));
assert!(second_rendered.contains(".claude.json skipped (already exists)"));
assert!(second_rendered.contains(".gitignore skipped (already exists)"));
assert!(second_rendered.contains("CLAUDE.md skipped (already exists)"));
assert_eq!(
@@ -405,8 +404,8 @@ mod tests {
"custom guidance\n"
);
let gitignore = fs::read_to_string(root.join(".gitignore")).expect("read gitignore");
assert_eq!(gitignore.matches(".claw/settings.local.json").count(), 1);
assert_eq!(gitignore.matches(".claw/sessions/").count(), 1);
assert_eq!(gitignore.matches(".claude/settings.local.json").count(), 1);
assert_eq!(gitignore.matches(".claude/sessions/").count(), 1);
fs::remove_dir_all(root).expect("cleanup temp dir");
}

View File

@@ -2976,21 +2976,15 @@ fn resolve_skill_path(skill: &str) -> Result<std::path::PathBuf, String> {
}
let mut candidates = Vec::new();
if let Ok(claw_config_home) = std::env::var("CLAW_CONFIG_HOME") {
candidates.push(std::path::PathBuf::from(claw_config_home).join("skills"));
}
if let Ok(codex_home) = std::env::var("CODEX_HOME") {
candidates.push(std::path::PathBuf::from(codex_home).join("skills"));
}
if let Ok(home) = std::env::var("HOME") {
let home = std::path::PathBuf::from(home);
candidates.push(home.join(".claw").join("skills"));
candidates.push(home.join(".agents").join("skills"));
candidates.push(home.join(".config").join("opencode").join("skills"));
candidates.push(home.join(".codex").join("skills"));
candidates.push(home.join(".claude").join("skills"));
}
candidates.push(std::path::PathBuf::from("/home/bellman/.claw/skills"));
candidates.push(std::path::PathBuf::from("/home/bellman/.codex/skills"));
for root in candidates {