Skip to content

chore(repo): merge opensop-cli into cli/ (subtree, history preserved)#67

Merged
Chosen9115 merged 23 commits into
mainfrom
feature/merge-cli-subtree
Jun 11, 2026
Merged

chore(repo): merge opensop-cli into cli/ (subtree, history preserved)#67
Chosen9115 merged 23 commits into
mainfrom
feature/merge-cli-subtree

Conversation

@Chosen9115

Copy link
Copy Markdown
Owner

Summary

Consolidates the CLI into this repo at cli/ as part of the local-first pivot ("Process as Infrastructure"). Done as a history-preserving subtree merge (merge -s ours + read-tree --prefix=cli/), so opensop-cli's full commit lineage is a merge parent.

  • cli/bin/opensop (v0.8.0, local-first), cli/test/, cli/docs/, cli/CLAUDE.md etc. now live here.
  • The standalone opensop-cli repo will be archived with a redirect README; its tags + GitHub releases (v0.1.0..v0.8.0) remain there for provenance.

⚠️ Merge strategy

Merge with a merge commit, NOT squash — squashing drops the second parent and loses the imported history.

Self-rating

  • correctness 9 · simplicity 9 · test coverage 9 · naming 9 · performance risk 10 · security risk 9 · process-model fidelity 9
  • Lowest: this PR is mechanical (no behavior change); CLI tests pass unchanged from cli/. Follow-up (Phase 4): reframe README/SPEC around manifesto + local-first and update the now-internal CLI links.

Test plan

  • cd cli && bash -n bin/opensop && bash test/test.sh289 PASS from the new location.

🤖 Generated with Claude Code

Carlos and others added 23 commits May 8, 2026 13:46
Thin client over the /sop/* HTTP API. 9 subcommands (list, schema, run,
status, steps, submit, cancel, instances, config), tiny local cache for
id→name resolution, TTY-aware pretty/JSON output. ~640 lines of bash;
deps are curl + jq.

Verified end-to-end against demo.opensop.ai:
- list / schema / run / status / cache populate all green
- submit / cancel / instances share the same api_call plumbing

README + docs/CLAUDE-INTEGRATION.md included for agent onboarding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the highest-impact items from the Darwin team's adoption
wishlist: ranked text search, inverse-retrieval suggest, tag
filtering on list. Plus README updates with a worked example,
sample list output, and the local-cache note lifted to a visible
section.

The framing change: discovery latency is the moat. These three
commands turn `opensop list` from "remember the name" to "describe
the intent" and are the load-bearing addition for agents to reach
for OpenSOP first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…alidate, --local

Adds the rest of the Darwin agent-adoption wishlist:

Tier 3 (inspection + planning):
  diff      — compare two instances of the same process
  compass   — top processes by run-count, recency, failure-rate
  history   — discoverable alias for `instances --process X`
  dry-run   — client-side preview: validates inputs against schema,
              walks steps and describes what each WOULD do, no execution

Tier 4 (process authoring):
  register         — POST a .sop.yaml to /sop/processes/register
  schema validate  — fully client-side YAML lint (yq or python3)
  --local          — global flag: override OPENSOP_URL to localhost:3000

The split with v0.2.0 was: search/suggest/list --tag = "find what
already exists" (consumer); v0.3.0 = "see what's there, see what
would happen, ship something new" (consumer + author). Together,
the wishlist's full Tier 1-4 surface is in.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cy, by_failure_rate}

v0.3.0 documented the compass --json output as
  {by_runs: [...], by_recency: [...], by_failure_rate: [...]}
but emitted a flat array. Brings the implementation in line with the
spec so agents and scripts can consume the three named dimensions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes the Darwin wishlist's Tier 2 #8 ("structured error responses").
Today, server errors round-trip cleanly as {error, message, ...} via
api_call. CLI-side errors (config missing, file not found, jq parse,
etc.) printed prose to stderr regardless of --json mode — agents
couldn't reliably parse them.

This release: when --json is set, die()/err() emit
  {error, message, hint?}
to stderr from the same JSON envelope. Prose-mode default (TTY)
unchanged — backward-compatible.

Adds CHANGELOG.md so fresh agents and contributors can see at a
glance what's been shipped without grepping git log. Five entries:
0.1.0, 0.2.0, 0.3.0, 0.3.1, 0.4.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… corpus widening

Addresses four issues from Darwin's real-use testing on coba.opensop.ai
(see proposals/opensop-agent-adoption-wishlist.md "Real-use findings"):

A. cmd_history and cmd_instances now populate the local id→name cache
   from each response row, so subsequent `status <id>` doesn't have
   to scan /sop/instances.
B. lookup_name's cache-miss fallback now paginates up to 1000 rows
   (5 pages of 200) instead of stopping at the first 200.
C. search/suggest now tokenize queries on whitespace and score each
   token independently. Hyphenated process names also tokenized on
   - and _ so `search "morning briefing"` matches `darwin-morning-briefing`.
D. The search/suggest corpus now also indexes inputs_summary and
   outputs_summary (already in the /sop/ discovery response, just
   unused). ~30% recall lift expected for "what produces X?" intents.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a second backend to the one CLI: `opensop run X.sop.json --local` runs processes on-machine (bash + jq, no server/curl/network), threading a JSON context between steps and writing append-only receipts. New commands: runs, show, list --local. Includes review hardening of the failure path (set -e), curl gating, run lifecycle, and failure-path regression tests.

BREAKING: --local now means local execution (no longer aliases OPENSOP_URL to localhost:3000).
Promote the Unreleased --local work to 0.5.0: bump OPENSOP_CLI_VERSION
and the CHANGELOG section, including the review-hardening fixes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add CLAUDE.md: single-file architecture, the two-backend (remote vs
--local) split, the cross-cutting output/error contract, set -e/set -u
bash invariants, and the add-a-command / release loops.

Fix the README Configuration section, which still described --local as
aliasing OPENSOP_URL to localhost:3000 — stale since v0.5.0, where
--local means local execution. Point at OPENSOP_URL=... for a dev server.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: cell primitive (v0.6) — opensop init + opensop scope

Introduces the cell primitive that grounds the v0.6 substrate work: a
directory marked by .opensop/manifest.yaml. Cells nest; init creates
one in cwd; scope walks up from cwd and prints the active cell + its
ancestor chain.

* opensop init [--name N] [--parent PATH]
    Creates .opensop/manifest.yaml + .opensop/{runs,archive}/ + an empty
    .opensop/lineage.json. Defaults: --name to cwd basename; --parent
    auto-detected by walking up for an ancestor cell (null if none).
    Refuses to clobber an existing .opensop/.

* opensop scope
    Prints active cell + ancestor chain, nearest-first. JSON or pretty.
    Exits non-zero when cwd is not inside any cell.

Pure-additive. No existing command behavior changes — verified by the
full existing test suite passing untouched. Foundation for the rest of
v0.6 (lineage, per-cell receipts, name resolution across cells, fork
mechanic, executor field), which will land in follow-up PRs.

manifest.yaml uses a tiny grammar (name + parent on their own lines)
parsed by a 6-line grep+sed helper to avoid adding a yaml dependency.
lineage.json (not .yaml) is initialized empty for the same reason; the
forthcoming lineage commands will read/write JSON consistent with the
existing .sop.json convention.

Tests:
* init creates the .opensop/ tree as root cell (parent: null)
* init auto-detects ancestor cell as parent ("../")
* init refuses to clobber an existing .opensop/
* scope walks ancestor chain (child + parent = 2 entries, correct order)
* scope from root cell shows only itself
* scope outside any cell exits non-zero
* explicit --name / --parent flags honored

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(help): mention parent auto-detection + scope error condition

Two small clarifications to the CELLS help block, surfaced by a
docs-only readability test:

* init: note that parent is auto-detected when cwd is inside an ancestor
  cell. Previously only the README mentioned this — a first-time user
  reading only --help might pass --parent ../ defensively.
* scope: note that it errors when cwd is not inside any cell. Previously
  the failure-mode was only discoverable by running it outside a cell.

No behavior change. Help text only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
)

* feat: lineage primitives (v0.6) — opensop annotate + opensop lineage

Substrate-level event log per skill, stored in .opensop/lineage.json
per cell. Sits on top of the cell primitive from PR #6.

* opensop annotate <skill> <event-type> <json>
    Append a policy event to the skill's lineage history in the active
    cell. Creates the lineage entry with default fields if it doesn't
    exist. Event type is open-string; data is whatever JSON the policy
    needs. Records ISO 8601 UTC timestamp.

* opensop lineage <skill>
    Print the skill's lineage entry (logical_name, forked_from, status,
    metadata, history) in the active cell. Returns the empty default
    entry if no events have been recorded for that skill yet.

Policy-neutral: the substrate stores `status` (open string), `metadata`
(open object), and `history[].type` (open string) — it does NOT
interpret any of them. Evolution policies (mineralization, maturity
grades, formal-verification gates, tag-based capability, etc.) sit on
top to record promotions, demotions, locks, archives, blesses, etc.

Lineage entry schema (per skill, keyed by logical_name):
  { logical_name, forked_from, history: [{at, type, data}], status, metadata }

Atomic writes via temp + mv. Corruption guard: invalid JSON in
lineage.json triggers an invalid_json error rather than silent
acceptance.

Tests: 9 new cases
  * annotate creates lineage entry + appends first event
  * annotate appends to existing entry, preserves order + prior events
  * lineage returns full entry with correct shape (json round-trip)
  * lineage returns empty default for never-annotated skill
  * annotate rejects invalid JSON data (invalid_json)
  * annotate rejects missing args (usage_error)
  * annotate errors when not inside a cell (usage_error)
  * lineage errors when not inside a cell (usage_error)
  * lineage refuses to read a corrupt lineage.json (invalid_json)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: cell + lineage commands now respect TTY auto-mode

The four v0.6 commands (init, scope, annotate, lineage) were calling
_resolve_output_mode via command substitution: $(_resolve_output_mode).
Inside that subshell, stdout is the capture pipe — not the terminal —
so is_tty (which checks [[ -t 1 ]]) returned false even when the user
was in a real TTY. Result: those four commands always emitted JSON in
the terminal, ignoring the documented "pretty in TTY, JSON when piped"
contract.

Fix: inline the check using the same pattern the rest of the codebase
uses (cmd_list, cmd_status, cmd_steps, etc.):

  if is_tty && [[ "$OUTPUT_MODE" != "json" ]]; then  # pretty
  else                                                # json
  fi

is_tty is now evaluated in the parent shell where stdout actually IS
the terminal, so the auto-mode resolves correctly.

Tests: added 4 regression cases — one per command — that verify
--pretty explicit override produces prose (not JSON) even when called
from a non-TTY context (the test harness). These would have caught the
bug had they existed at PR #6 ship time.

Reported by a real-terminal test by Carlos; confirmed with
[[ -t 1 ]] && echo "tty-detected".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: cell + lineage output-mode check now honors --pretty in non-TTY

Follow-up to 8e48114 which only fixed half the bug. The simple inline
pattern `is_tty && [[ "$OUTPUT_MODE" != "json" ]]` works in auto-mode
inside a TTY, but ignores `--pretty` when called from a non-TTY (the
4 --pretty regression tests added in 8e48114 were failing on this).

Replacing with three-state logic that matches what _resolve_output_mode
does (but without the subshell capture bug that broke the original):

  if [[ "$OUTPUT_MODE" == "pretty" ]] \
     || { [[ "$OUTPUT_MODE" == "auto" ]] && is_tty; }; then
    # pretty
  else
    # JSON
  fi

Resolves all three documented states:
  --json explicit  → json
  --pretty explicit → pretty
  no flag + TTY    → pretty
  no flag + pipe   → json

All 24 tests now pass (20 prior + 4 --pretty regression).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: cell-aware default for OPENSOP_LOCAL_HOME (v0.6)

When cwd is inside an OpenSOP cell AND the user has not explicitly set
OPENSOP_LOCAL_HOME, local-mode receipts now default to the active
cell's .opensop/ instead of the global ~/.opensop-local. Receipts
land alongside the processes that produced them; each cell has its
own receipt history.

This is the first v0.6 PR that changes existing behavior, but it's
backwards-compatible: the cell-aware default only kicks in when a
.opensop/ marker exists in cwd's path, and no pre-v0.6 user has one.
Explicit OPENSOP_LOCAL_HOME=… always wins.

Resolution order for OPENSOP_LOCAL_HOME:
  1. Explicit env var (if user set it)        → wins
  2. Active cell's .opensop/ (v0.6, new)      → if cwd inside a cell
  3. ~/.opensop-local (existing default)      → otherwise

The resolution happens in main() after helpers are loaded (so
_find_scope_root is available). Constants section now tracks
_OPENSOP_LOCAL_HOME_EXPLICIT so the late resolution knows whether
to honor the user's explicit choice.

Tests: 3 new cases
  * runs — inside cell, default OPENSOP_LOCAL_HOME → receipt in cell
  * runs — explicit OPENSOP_LOCAL_HOME wins over cell-aware default
  * runs — `opensop runs` inside cell reads cell receipts only

Docs:
  * --help ENVIRONMENT now lists OPENSOP_LOCAL_HOME with new default
  * README's "Local execution" intro explains the cell-aware default
  * CHANGELOG [Unreleased] gets a Changed section

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test: add coverage for corrupt cell + nested-cell routing

Folding in the two coverage gaps surfaced during PR-test scenarios:

* Corrupt cell: a `.opensop/` directory without `manifest.yaml` should
  NOT be treated as a cell (walk-up requires both). The smoke check
  confirmed the behavior; this test pins it.
* Nested cells: when an inner cell exists below an outer cell, runs
  invoked from the inner cell must land in the inner cell's
  `.opensop/runs/`, NOT bubble up to the outer.

Full suite is now 29 cases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two changes to the local engine when a cell is active:

* opensop run <name> --local
    Accepts a bare logical name in addition to a file path. The name
    resolves to processes/<name>.sop.json in the active cell, then in
    each ancestor cell — nearest wins. Explicit file paths still work
    (detected when the arg ends in .sop.json or contains '/'), so
    backwards-compatible with every existing usage.

* opensop list --local (no dir arg)
    When invoked from inside a cell, walks the active cell + ancestors,
    tagging each entry with [cell-name]. Passing an explicit dir
    preserves the original find-based behavior with no cell awareness.

New helper: _find_skill_in_cells <name> — walks the cell chain looking
for processes/<name>.sop.json, returning the first (nearest) match or
empty if none.

No dedup yet — when the same logical name exists in multiple cells,
list shows all rows. A future --conflicts flag will distinguish
"nearest" from "shadowed."

Tests: 7 new cases
  * list inside cell walks active + ancestor processes/ with [cell-name] tags
  * list with explicit dir arg uses original find behavior (no tags)
  * run by name resolves to ancestor cell's processes/<name>.sop.json
  * nearest-wins resolution (child cell shadows parent's same-named skill)
  * explicit path still works (backwards compat)
  * run with non-existent name errors cleanly
  * run by bare name outside any cell errors (no chain to search)

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Materializes a copy of an ancestor cell's skill into the active cell
and records a lineage entry with forked_from = { cell, forked_at,
snapshot } where snapshot captures the parent's status + metadata
opaquely.

The substrate stores; evolution policies decide what to do with the
snapshot. Typical policy use: inherit parent's state + mark unverified
until first run in the child cell (the "knife metaphor" from the v0.6
spec — the shape carries its sharpness, the cell verifies fit on first
cut).

Behavior:
* Default: auto-detect source via walk-up (nearest ancestor with the
  skill).
* --from <cell>: override; resolves relative to the active cell or
  accepts absolute paths. Validated to be an actual cell (.opensop/ +
  manifest.yaml).
* Refuses to overwrite an existing skill in the active cell (forces
  the user to make removal explicit).
* Child's live status/metadata/history start empty. Substrate stays
  policy-neutral.
* After fork, name resolution from PR #9 finds the child's copy first
  (nearest-wins).

Tests: 9 new cases
  * fork copies file from ancestor's processes/ into active cell
  * forked_from captures parent's status + metadata as snapshot
  * child's live status/metadata/history start empty
  * parent's lineage is untouched
  * refuses to overwrite existing skill in active cell
  * errors when skill not found in any ancestor
  * errors when not inside any cell
  * --from override copies from a non-ancestor cell
  * forked skill is nearest-wins for subsequent name resolution

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Steps in a .sop.json may now declare `executor: internal|external`:
* "external" → work happens in an outside process (script, webhook);
  the OpenSOP runtime orchestrates and receives the typed receipt
* "internal" → the OpenSOP runtime handles the step directly

Field is optional. Per-type defaults apply when absent:
* automated, shell, webhook → external
* noop, form, approval, notification, wait, judgment → internal

Invalid values fail with parse_error at process load time — BEFORE any
step runs and before a run directory is even created. Pre-validation
pass walks all steps and refuses to start if any has an invalid value.

The effective executor (explicit or defaulted) is recorded in each
step's audit.jsonl entry, so receipts document where the work happened.

This formalizes the B-mode-vs-A-mode distinction from the v0.6 spec
and matches existing production patterns (deterministic external
scripts producing typed receipts).

Tests: 6 new cases
  * shell step defaults to external in receipts
  * noop step defaults to internal in receipts
  * explicit "internal" honored
  * explicit "external" honored
  * invalid executor errors with parse_error before any step runs
  * pre-validates ALL steps before creating a run dir (no partial runs)

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bump OPENSOP_CLI_VERSION → 0.6.0. Consolidate CHANGELOG [Unreleased]
into ## [0.6.0] — 2026-06-08 with one Added section + one Changed
section (the prior accumulation had a duplicate ### Added from
incremental PR landings).

v0.6 shipped across six PRs (#6, #7, #8, #9, #10, #11):
* Cell primitive — fractal addressing via .opensop/manifest.yaml
* Lineage primitives — substrate-level event log per skill
* Cell-aware OPENSOP_LOCAL_HOME — receipts live with the cell
* Name resolution across the cell chain — nearest-wins
* Fork mechanic — explicit inheritance with snapshot
* Step executor field — internal|external, formalizes B-mode/A-mode

Backwards-compatible: every pre-v0.6 usage continues to work; v0.6
features only activate inside a cell.

Test suite: 51 cases, all pass.

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When inside a cell, --conflicts marks the first occurrence of each
filename (nearest cell that has it) as "← active" and subsequent
occurrences in ancestor cells as "← shadowed by [cell-name]".

Resolves the deferred follow-up flagged in PR #9's CHANGELOG entry:
"No dedup yet; same logical name in multiple cells shows all rows.
A future --conflicts flag will distinguish 'nearest' from 'shadowed.'"

* Default behavior unchanged (no markers, backwards-compatible).
* --conflicts only annotates; it doesn't dedup or hide rows.
* Tracking implemented with a space-separated string scan rather than
  an associative array (declare -A is bash 4+, but the existing repo
  works fine on bash 3.2 macOS).
* Explicit-dir mode (`list --local /some/dir`) ignores --conflicts —
  no cell chain to compare against.

Tests: 3 new cases
  * default list (no flag) preserves backwards-compatible output
  * --conflicts marks active + shadowed entries correctly
  * --conflicts with explicit dir arg is benign

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A step-by-step install guide written for AI coding agents (Claude Code,
Codex, Cursor, etc.) that a user has asked to set up OpenSOP CLI. The
file uses [ASK] markers to mark the points where an agent should
pause for user input; everything else is mechanical and should proceed
without prompting.

Covers:
* Prerequisites check (bash 4+, jq, curl)
* Install via the canonical curl one-liner + no-sudo fallback +
  from-source fallback for hermetic envs
* Verify install (version + help-text inspection)
* Two configuration paths:
  - A: point at a server (URL + token)
  - B: local mode + v0.6 cell setup
* Walkthrough of one working end-to-end example
* Handoff confirmation + suggested next steps
* Trust boundary callout for local execution
* Common issues table mapping symptom → cause → fix

Companions the Rails runtime's INSTALL_FOR_AGENTS.md referenced from
the opensop.ai hero prompt; this one is for users who want the CLI
specifically (lighter setup, no Docker, --local mode + v0.6 cells).

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…bprocess/wait + pause/resume (#15)

The CLI's --local mode now executes the full step-type set, not just
automated/shell/noop. Adds a pause/resume keystone so form/approval/
wait-until/webhook-callback/subprocess can pause a local run and resume
via `opensop submit <run_id> <step-id> --local`.

- New local step types: form, approval, llm, webhook, subprocess, wait
- Pause/resume: manifest state machine (running/waiting/completed/failed),
  cursor.next_index, live context.json checkpoint, submit --local
- llm matches the runtime's Anthropic contract (OSL_LLM_STUB test seam)
- webhook: ${...} templating, sync + callback modes, CRLF header guard,
  body sends only declared step inputs (no context over-share)
- subprocess: recursive local run + context merge + depth guard
- Security: CRLF guards on header keys+values, fork name validation
- 164 tests pass (up from 81). Declares opensop: "0.6".

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- response_mode is now required for webhook steps (no silent sync default);
  enforced in schema validate and the local engine
- callback mode fires the outbound request, then pauses (runtime parity)
- fallback body resolves declared inputs via from: (InputResolver parity),
  no longer over-shares the full context to the endpoint
- ${process.inputs.X} supports nested dot-paths
- 172 tests pass (up from 164)

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The default backend is now LOCAL. Every command runs against local
.sop.json files with no server/curl. Remote is opt-in via --remote
(configured server) or --server <url>. --local is a deprecated no-op
kept for script compatibility.

- Phase 1: full local parity — search/suggest/dry-run/status/steps/
  instances/compass/history/diff/cancel now have local implementations
  mirroring the runtime serializer shapes
- Phase 2: inverted dispatch (REMOTE_MODE, default false); register and
  schema <name> gated remote-only; curl/config only in remote mode;
  run/instance vocabulary unified to "run"
- 289 tests pass (from 172). Docs/help/README reframed local-first.

BREAKING CHANGE: scripts expecting default-remote must add --remote or
--server <url>. See CHANGELOG [0.8.0] migration note.

Co-authored-by: Carlos <carlos>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidates the CLI into the opensop repo as part of the local-first
pivot. opensop-cli's full commit history is preserved as a merge parent
(git log --follow cli/bin/opensop traces across the merge). The
standalone opensop-cli repo will be archived with a redirect; its tags
and GitHub releases (v0.1.0..v0.8.0) remain there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Chosen9115 Chosen9115 merged commit 1d6affb into main Jun 11, 2026
@Chosen9115 Chosen9115 deleted the feature/merge-cli-subtree branch June 11, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants