Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 0 additions & 141 deletions PROMPT-build.md

This file was deleted.

13 changes: 9 additions & 4 deletions cmd/muster/e2e_link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/mlaccetti/muster/internal/testutil"
)

func TestE2E_linkProjectNoRealHarness(t *testing.T) {
func TestE2E_linkHarnessWideNoRealHarness(t *testing.T) {
home := t.TempDir()
project := t.TempDir()
bin := t.TempDir()
Expand All @@ -31,16 +31,21 @@ func TestE2E_linkProjectNoRealHarness(t *testing.T) {
t.Fatalf("install: %s", out.String())
}
if cli.RunForTest([]string{
"link", "--harness", "claude", "--project-dir", project, "--global=false",
"link", "--harness", "claude",
}, &out, &out) != 0 {
t.Fatalf("link: %s", out.String())
}
raw, err := os.ReadFile(filepath.Join(project, ".claude", "settings.json"))
raw, err := os.ReadFile(filepath.Join(home, ".claude", "settings.json"))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(raw), "muster-orchestrator") {
t.Fatal(".claude/settings.json not linked")
t.Fatal("harness-wide .claude/settings.json not linked")
}
for _, path := range []string{"AGENTS.md", ".muster", ".claude", ".codex", ".cursor", ".agents"} {
if _, err := os.Lstat(filepath.Join(project, path)); !os.IsNotExist(err) {
t.Fatalf("link wrote project path %s (err=%v)", path, err)
}
}
if cli.RunForTest([]string{"doctor"}, &out, &out) != 0 {
t.Fatalf("doctor: %s", out.String())
Expand Down
60 changes: 24 additions & 36 deletions docs/linking.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,40 @@
# Linking harnesses

`muster install` lays down the versioned **kit** (prose policies and `link/` harness entrypoints). **`muster link`** symlinks those entrypoints into each harness and sets the **default orchestrator** selector. Assignments and handoffs are ordinary harness conversation; Muster requires no fixed message format.
`muster install` lays down the versioned **kit** (policy and harness entrypoints). **`muster link`** connects those entrypoints to harness-wide user configuration and selects Muster as the default orchestrator where the harness supports that setting. Link and unlink never write into the current repository.

## Behavior

| Mode | Default | What it does |
|------|---------|----------------|
| Auto-detect | yes (`muster link`) | Links only harnesses whose CLIs are on `PATH` (`claude`, `codex`, `cursor-agent`, `agy`) |
| `--harness` | optional | Link specific IDs even if not on `PATH` |
| `--all` | off | Link all four supported harnesses |
| `--global` | on | User-level harness config |
| `--project` | on | Repo-level harness config + `.muster/` pointers |
| `--project-dir` | cwd | Repo root to link |
| `--unlink` | off | Remove symlinks, config patches, and shims |

State is recorded in `$MUSTER_HOME/link-manifest.json`. `muster doctor` reports link status and runs **`link.VerifyReports`** on each linked harness.

## Per harness (global)

| ID | Native activation | Linked definition |
|----|----------------------|---------------------------------------------|
| `claude` | `~/.claude/settings.json` → `"agent": "muster-orchestrator"` | `~/.claude/agents/muster-orchestrator.md`, `muster-worker.md` |
| `codex` | `~/.codex/AGENTS.md` (every Codex run) | `~/.codex/agents/muster-worker.toml` |
| `cursor` | Project-scoped `.cursor/rules/muster-orchestrator.mdc` with `alwaysApply: true`; global User Rules must be set in Cursor Settings | `.cursor/rules/muster-orchestrator.mdc` |
| `antigravity` (`agy`) | `~/.gemini/antigravity-cli/settings.json` → `"agent": "muster-orchestrator"` | `~/.gemini/config/agents/muster-orchestrator/agent.md`, `muster-worker/agent.md` |
| Command or option | Behavior |
|-------------------|----------|
| `muster link` | Auto-detect installed harness CLIs and link their harness-wide configuration |
| `--harness <id>` | Link a specific harness, whether or not its CLI is on `PATH` |
| `--all` | Attempt all supported harnesses |
| `--unlink` | Remove Muster's harness-wide configuration |

Codex also receives the orchestrator instructions in the linked project's root `AGENTS.md`. Its native worker agent uses `gpt-6-luna` with `medium` reasoning effort. Cursor CLI automatically loads the project rule; global Cursor User Rules must be configured in Cursor Settings.
State for successfully linked harnesses is recorded in `$MUSTER_HOME/link-manifest.json`. `muster doctor` checks each recorded link.

Project linking adds `.claude/`, `.codex/`, `.cursor/rules/`, or `.agents/agents/` equivalents under the repo.
## Harness support

## Optional shims
| ID | Harness-wide activation | Linked definition |
|----|------------------------|-------------------|
| `claude` | `~/.claude/settings.json` → `"agent": "muster-orchestrator"` | `~/.claude/agents/muster-orchestrator.md` |
| `codex` | `~/.codex/AGENTS.md` (loaded for every Codex run) | `~/.codex/agents/muster-worker.toml` |
| `cursor` | Add Muster policy to User Rules in Cursor Settings | Manual setup; the documented interface is Settings, with no supported CLI or file interface for configuring global User Rules |
| `antigravity` (`agy`) | `~/.gemini/antigravity-cli/settings.json` → `"agent": "muster-orchestrator"` | `~/.gemini/config/agents/muster-orchestrator/agent.md`, `muster-worker/agent.md` |

After link, `$MUSTER_HOME/bin/` contains:
Codex's worker agent uses `gpt-6-luna` with `medium` reasoning effort. Muster reports Cursor as skipped because it cannot configure Cursor's global User Rules through a supported interface. To use Muster in Cursor, copy the contents of `muster kit path` + `/policy/orchestrator.md` into Cursor Settings → Rules → User Rules. Cursor's worker and event behavior depends on the capabilities exposed by the active session.

- **`agy-muster`**: `agy --agent muster-orchestrator "$@"`
## Optional shims

Add `$MUSTER_HOME/bin` to `PATH` if you want those names instead of the stock CLIs.
After link, `$MUSTER_HOME/bin/` contains `agy-muster`, which runs `agy --agent muster-orchestrator "$@"`. Add that directory to `PATH` to use the shim.

## Validation

```bash
muster install
muster link --all
muster doctor # link.VerifyReports on manifest harnesses
muster doctor --probe # harness binaries on PATH
muster doctor # validate recorded harness-wide links
muster doctor --probe # probe harness binaries on PATH
```

**Claude**
Expand All @@ -58,15 +49,12 @@ readlink ~/.claude/agents/muster-orchestrator.md
```bash
rg 'MUSTER_LINK_START|default orchestrator' ~/.codex/AGENTS.md
readlink ~/.codex/agents/muster-worker.toml
codex debug prompt-input "Summarize the active Muster instructions" # inspect loaded prompt inputs
codex debug prompt-input "Summarize the active Muster instructions"
```

**cursor-agent**
**Cursor**

```bash
test -f .cursor/rules/muster-orchestrator.mdc
cursor-agent -p --output-format text "Summarize the active Muster project rule"
```
Confirm Muster's orchestrator policy appears under Cursor Settings → Rules → User Rules.

**agy**

Expand All @@ -80,4 +68,4 @@ Re-run **`muster link`** after `muster install` upgrades the kit so symlinks tar

## Testing without real harnesses

CI uses `muster link --harness claude --project <dir> --global=false`. Package tests cover Codex global and project `AGENTS.md` instruction wiring, native worker agent files, Cursor project rules, and Antigravity global linking with temporary homes.
Package and CLI tests use temporary homes to validate harness-wide configuration, worker definitions, preservation of existing settings, unlink behavior, and the Cursor Settings limitation. Tests never write into the caller's project directory.
4 changes: 2 additions & 2 deletions docs/projections/codex.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Codex projection

Prefer **`muster link`**: it adds the orchestrator policy to native global and project `AGENTS.md` instructions and installs a native worker agent.
Use **`muster link`** to add the orchestrator policy to Codex's native global `AGENTS.md` instructions and install a native worker agent.

Codex has no separate root-agent selector in this setup: the active Codex session is the orchestrator because native instruction discovery loads the managed global and project guidance. The `muster-worker` custom agent is available for delegation.
Codex has no separate root-agent selector in this setup: the active Codex session is the orchestrator because native instruction discovery loads the managed global guidance. The `muster-worker` custom agent is available for delegation.

```bash
rg 'MUSTER_LINK_START|default orchestrator' ~/.codex/AGENTS.md
Expand Down
10 changes: 2 additions & 8 deletions docs/projections/cursor.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
# Cursor Agent projection

Prefer **`muster link --project`**: it installs `.cursor/rules/muster-orchestrator.mdc` with `alwaysApply: true`. Cursor CLI loads project rules. Global User Rules are managed in Cursor Settings, so Muster cannot set them through a supported file path.

```bash
test -f .cursor/rules/muster-orchestrator.mdc
cat .muster/orchestrator.path
```

Binary resolved as `cursor-agent`. The linked always-applied project rule makes the current session follow the Muster orchestrator policy. Cursor CLI documentation currently describes a single CLI agent and does not document a native subagent dispatch or lifecycle callback interface. The rule therefore tells the agent to use only worker and event tools actually exposed by the session; Cursor Background Agents are a separate capability.
Muster cannot configure Cursor's global User Rules through a supported CLI or file interface. Add the contents of Muster's `policy/orchestrator.md` to Cursor Settings → Rules → User Rules to make Muster the default orchestrator for Cursor.

Cursor Agent resolves as `cursor-agent`. Worker delegation and event-driven waiting depend on capabilities exposed by the active Cursor session; the projection policy asks the orchestrator to use only capabilities actually available.
2 changes: 1 addition & 1 deletion docs/upgrades.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ muster link # refresh harness snippets if you use link
The CLI checks GitHub **at most once per day** for a newer release and prints a hint to stderr:

```text
muster v0.2.0 is available (you have v0.1.0). Run: brew update && brew upgrade muster
muster v0.3.0 is available (you have v0.2.0). Run: brew update && brew upgrade muster
```

Skip the check in CI with `MUSTER_SKIP_UPDATE_CHECK=1`. `dev` builds never notify.
Expand Down
34 changes: 12 additions & 22 deletions internal/cli/link.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,30 @@ import (

func newLinkCmd() *cobra.Command {
var (
projectDir string
harnesses []string
all bool
global bool
project bool
dryRun bool
unlink bool
harnesses []string
all bool
dryRun bool
unlink bool
)
cmd := &cobra.Command{
Use: "link",
Short: "Wire muster orchestrator and worker policies into harness configs",
Long: `Link installs managed snippets that point every harness at the same kit policies.

By default, muster auto-detects harnesses whose CLIs are on PATH and links both global
(user) and project (current repo) entrypoints. Use --harness to pin specific IDs, or
--all to link every supported harness even when not installed.`,
By default, muster auto-detects harnesses whose CLIs are on PATH and links their
harness-wide (user) configuration. Use --harness to pin specific IDs, or --all to
link every supported harness even when not installed.`,
Example: ` muster install
muster link
muster link --harness claude --harness codex --project-dir .
muster link --harness claude --harness codex
muster link --all --dry-run
muster link --unlink`,
RunE: func(cmd *cobra.Command, args []string) error {
res, err := link.Run(link.Options{
ProjectDir: projectDir,
Harnesses: harnesses,
All: all,
Global: global,
Project: project,
DryRun: dryRun,
Unlink: unlink,
Harnesses: harnesses,
All: all,
DryRun: dryRun,
Unlink: unlink,
})
if err != nil {
return err
Expand All @@ -48,7 +42,6 @@ By default, muster auto-detects harnesses whose CLIs are on PATH and links both
return nil
}
writef(cmd.OutOrStdout(), "kit: %s\n", res.KitDir)
writef(cmd.OutOrStdout(), "project: %s\n", res.Project)
for _, h := range res.Harnesses {
writef(cmd.OutOrStdout(), "linked %s:\n", h.ID)
for _, t := range h.Targets {
Expand All @@ -64,11 +57,8 @@ By default, muster auto-detects harnesses whose CLIs are on PATH and links both
return nil
},
}
cmd.Flags().StringVar(&projectDir, "project-dir", "", "Project directory (default: cwd)")
cmd.Flags().StringArrayVar(&harnesses, "harness", nil, "Harness id (repeatable); default: auto-detect on PATH")
cmd.Flags().BoolVar(&all, "all", false, "Link all supported harnesses (do not require CLI on PATH)")
cmd.Flags().BoolVar(&global, "global", true, "Link user-level harness config")
cmd.Flags().BoolVar(&project, "project", true, "Link project-level config (.muster, .claude/, .cursor/rules, etc.)")
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Print actions without writing files")
cmd.Flags().BoolVar(&unlink, "unlink", false, "Remove managed muster snippets")
return cmd
Expand Down
Loading
Loading