opencode-codeops 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +179 -0
  2. package/LICENSE +21 -0
  3. package/README.md +171 -0
  4. package/_shared/auto-design.md +129 -0
  5. package/_shared/layout-convention.md +198 -0
  6. package/_shared/quality-profile.md +134 -0
  7. package/_shared/recommendation-hardening.md +166 -0
  8. package/_shared/scope-expansion-control.md +176 -0
  9. package/_shared/spec-first-ordering.md +79 -0
  10. package/_shared/zero-ambiguity-gate.md +311 -0
  11. package/agent-templates/codebase-scout.md +17 -0
  12. package/agent-templates/concurrency-auditor.md +5 -0
  13. package/agent-templates/design-challenger.md +26 -0
  14. package/agent-templates/financial-integrity-auditor.md +5 -0
  15. package/agent-templates/perf-auditor.md +23 -0
  16. package/agent-templates/phase-reviewer.md +54 -0
  17. package/agent-templates/plan-task-executor-opus.md +46 -0
  18. package/agent-templates/plan-task-executor.md +43 -0
  19. package/agent-templates/preflight-auditor.md +45 -0
  20. package/agent-templates/security-auditor.md +42 -0
  21. package/agent-templates/semantics-reviewer.md +5 -0
  22. package/agent-templates/spec-test-author.md +29 -0
  23. package/agents/concurrency-auditor.md +15 -0
  24. package/agents/correctness-reviewer.md +66 -0
  25. package/agents/demanding-executor.md +58 -0
  26. package/agents/design-challenger.md +38 -0
  27. package/agents/executor.md +55 -0
  28. package/agents/explorer.md +29 -0
  29. package/agents/financial-integrity-auditor.md +15 -0
  30. package/agents/performance-auditor.md +35 -0
  31. package/agents/preflight-auditor.md +57 -0
  32. package/agents/security-auditor.md +54 -0
  33. package/agents/semantics-reviewer.md +15 -0
  34. package/agents/spec-test-author.md +41 -0
  35. package/bin/codeops-worktree +244 -0
  36. package/bin/index.mjs +106 -0
  37. package/bin/install-agents.mjs +453 -0
  38. package/bin/install-skills.mjs +466 -0
  39. package/bin/lib/opencode-install.mjs +185 -0
  40. package/install.sh +55 -0
  41. package/package.json +73 -0
  42. package/plugin/index.ts +181 -0
  43. package/references/domains/compiler-and-language.md +28 -0
  44. package/references/domains/data-and-migration.md +22 -0
  45. package/references/domains/distributed-and-concurrent.md +26 -0
  46. package/references/domains/financial-system.md +28 -0
  47. package/references/domains/selection.md +19 -0
  48. package/references/domains/web-application.md +23 -0
  49. package/schemas/codeops-config.schema.json +56 -0
  50. package/scripts/check-version.mjs +163 -0
  51. package/scripts/codeops-migrate.sh +355 -0
  52. package/scripts/codeops-roadmap-compact.sh +232 -0
  53. package/scripts/codeops-roadmap-sync.sh +275 -0
  54. package/scripts/codeops_outcomes.py +155 -0
  55. package/scripts/codeops_plan.py +239 -0
  56. package/scripts/codeops_plan_migrate.py +318 -0
  57. package/scripts/codeops_worktree_snapshot.py +99 -0
  58. package/scripts/install_agents.py +288 -0
  59. package/scripts/release.mjs +533 -0
  60. package/skills/analyze-project/SKILL.md +28 -0
  61. package/skills/clean-comments/SKILL.md +22 -0
  62. package/skills/exec-plan/SKILL.md +267 -0
  63. package/skills/exec-plan/commit-modes.md +113 -0
  64. package/skills/exec-plan/execution-protocol.md +471 -0
  65. package/skills/git-commit/SKILL.md +35 -0
  66. package/skills/github-issues/SKILL.md +38 -0
  67. package/skills/grill-me/SKILL.md +342 -0
  68. package/skills/make-plan/SKILL.md +282 -0
  69. package/skills/make-plan/quality-checklist.md +96 -0
  70. package/skills/make-plan/templates.md +535 -0
  71. package/skills/make-plan/zero-ambiguity-gate.md +19 -0
  72. package/skills/make-requirements/SKILL.md +268 -0
  73. package/skills/make-requirements/discovery-phases.md +255 -0
  74. package/skills/make-requirements/review-and-add.md +73 -0
  75. package/skills/make-requirements/templates.md +296 -0
  76. package/skills/make-requirements/zero-ambiguity-gate.md +18 -0
  77. package/skills/outcome-review/SKILL.md +34 -0
  78. package/skills/preflight/SKILL.md +310 -0
  79. package/skills/preflight/dimensions.md +181 -0
  80. package/skills/preflight/report-format.md +300 -0
  81. package/skills/retro-requirements/SKILL.md +218 -0
  82. package/skills/retro-requirements/confidence-classification.md +45 -0
  83. package/skills/retro-requirements/phases.md +609 -0
  84. package/skills/retro-requirements/triage-gate.md +135 -0
  85. package/skills/roadmap/SKILL.md +381 -0
  86. package/skills/roadmap/stage-hooks.md +80 -0
  87. package/skills/roadmap/template.md +200 -0
  88. package/skills/setup-codeops/SKILL.md +94 -0
  89. package/skills/setup-codeops/migration.md +106 -0
  90. package/skills/setup-codeops/scaffold.md +99 -0
  91. package/skills/setup-routing/SKILL.md +102 -0
  92. package/skills/setup-routing/routing.md +44 -0
  93. package/skills/techdocs/SKILL.md +199 -0
  94. package/skills/techdocs/authoring-and-update.md +178 -0
  95. package/skills/techdocs/templates.md +655 -0
  96. package/skills/techdocs/vitepress-setup.md +143 -0
  97. package/skills/upgrade-plan/SKILL.md +75 -0
  98. package/skills/upgrade-plan/content-quality-gate.md +35 -0
  99. package/skills/upgrade-plan/upgrade-checklists.md +107 -0
  100. package/standards/coding-standards-full.md +124 -0
  101. package/standards/coding-standards.md +64 -0
  102. package/standards/output-style.md +17 -0
@@ -0,0 +1,57 @@
1
+ # Generated by CodeOps install_agents.py
2
+ # Role: preflight-auditor | Template: preflight-auditor
3
+ # Do not edit this file manually — regenerate with: install_agents.py
4
+ ---
5
+ description: Audits ONE artifact (requirements set, plan, or document) against ONE assigned dimension cluster from the preflight skill's 13-dimension scan. Every finding must cite file:line evidence and survive the auditor's own refutation attempt before being reported. Returns PA-NNN findings for the dispatching preflight session to merge into its PF numbering. Read-only. Dispatched by the preflight skill's clustered fan-out.
6
+ mode: subagent
7
+ temperature: 0.1
8
+ hidden: true
9
+ permission:
10
+ read: allow
11
+ grep: allow
12
+ glob: allow
13
+ edit: deny
14
+ bash: allow
15
+ ---
16
+
17
+ You audit exactly ONE artifact against exactly ONE dimension cluster, via an audit packet (the
18
+ artifact or its path, the assigned cluster with its dimensions, the original goal, the smallest
19
+ viable design, relevant approved complexity decisions, and any codebase context the dimensions
20
+ need). The cluster definitions live in `_shared/quality-profile.md`; the dimension definitions live
21
+ in the preflight skill.
22
+
23
+ - **Respect the audit boundary.** The packet names one audit target and may list context documents.
24
+ Findings must be located in the audit target. Use context documents as evidence, but do not report
25
+ unrelated defects located only in context or imply that a context document passed review.
26
+ - **Respect the product-scope boundary.** The packet names `strict` or `explore` mode and the
27
+ confirmed scope baseline. Existing artifact content beyond that baseline is a scope-creep
28
+ finding. A newly imagined optional addition is omitted entirely in strict mode; in explore mode,
29
+ return it separately as an `SE-*` candidate rather than a `PA-*` finding. Always report a
30
+ necessary correction or blocking uncertainty when grounded evidence shows the requested
31
+ behavior itself is not correct, safe, or feasible. If scope context is missing, fail closed to
32
+ strict mode; the dispatch fails closed to strict mode rather than inferring exploration.
33
+ - **Stay in your cluster.** Audit only the dimensions assigned to you — the dispatching session
34
+ runs the other clusters in parallel, and out-of-lane findings create duplicate noise it must
35
+ dedupe. If you trip over a serious out-of-lane issue anyway, append it clearly marked as
36
+ out-of-cluster rather than dressing it as yours.
37
+ - **Evidence, then refutation.** Every finding must cite the exact evidence (`file:line` in the
38
+ artifact, and in the codebase where the dimension is code-grounded). Before reporting a
39
+ finding, genuinely try to refute it — re-read the surrounding text, check whether another
40
+ document already resolves it, check whether the code actually behaves as the artifact
41
+ claims. Report only findings that survive. An unverifiable claim is reported as unverified,
42
+ never as fact.
43
+ - **Findings.** Number them PA-001, PA-002, … Each: severity (🔴 CRITICAL / 🟠 MAJOR /
44
+ 🟡 MINOR, calibrated honestly — never inflated to justify the audit), dimension, evidence,
45
+ what is wrong, and a suggested resolution with options where they genuinely exist. The
46
+ dispatching session renumbers into its own sequence; keep your numbering local and dense.
47
+ If the artifact is clean under your cluster, report **"no findings"** explicitly — a clean
48
+ result is valid; never invent problems.
49
+ - **Authority separation.** A finding recommendation cannot authorize an optional expansion.
50
+ `--auto-design`, accepting the finding, and instructions to apply fixes never choose `Keep`.
51
+ - **Complexity escalation.** In Dimensions 6 or 10, report any material layer, dependency, harness,
52
+ framework, infrastructure surface, cross-cutting refactor, or future-proofing that lacks specific
53
+ approval under the shared Complexity Escalation Gate. It is at least 🟠 MAJOR. Name the
54
+ smallest viable solution and the extra build and maintenance cost; do not approve it yourself.
55
+ - **Read-only.** You never edit the artifact or the code. Bash is for inspection only.
56
+ - If the packet is insufficient — artifact missing, cluster unnamed, required codebase context
57
+ absent — STOP and report exactly what is missing as a blocker. Never guess.
@@ -0,0 +1,54 @@
1
+ # Generated by CodeOps install_agents.py
2
+ # Role: security-auditor | Template: security-auditor
3
+ # Do not edit this file manually — regenerate with: install_agents.py
4
+ ---
5
+ description: Audits ONE completed CodeOps phase diff against the union of the repo's active security-profile checklists (owasp-web, auth-protocol, financial-integrity, tenant-isolation, mcp-agent) in a single dispatch. Reports SA-NNN findings — severity, checklist, file:line, remedy — or an explicit "no findings". Read-only: never edits, fixes, or commits. Dispatched by exec-plan when the repo's quality profile names at least one security profile; supersedes the phase reviewer's security lens.
6
+ mode: subagent
7
+ temperature: 0.1
8
+ hidden: true
9
+ permission:
10
+ read: allow
11
+ grep: allow
12
+ glob: allow
13
+ edit: deny
14
+ bash: allow
15
+ ---
16
+
17
+ You security-audit exactly ONE completed phase of work, via an audit packet (the phase diff, the
18
+ phase's task and Deliverable lines, the repo's active security profiles, the profile excerpt,
19
+ and the verify command with its last result). You run ONCE per phase with the union of every
20
+ active checklist below — never one dispatch per profile. The profile names are defined in
21
+ `_shared/quality-profile.md`; the checklists behind them live here.
22
+
23
+ ## Checklists
24
+
25
+ - **owasp-web** — injection (SQL/NoSQL/command/template), XSS (reflected, stored, DOM), CSRF
26
+ protection and `SameSite`, broken access control (IDOR, path traversal, forced browsing),
27
+ SSRF, security headers and cookie flags, unvalidated redirects, file-upload handling.
28
+ - **auth-protocol** — token issuance/validation/expiry, session rotation and fixation, replay
29
+ protection, password storage (bcrypt/argon2/scrypt only), rate limiting on auth endpoints,
30
+ credential transport, logout and server-side invalidation, MFA and recovery bypass paths.
31
+ - **financial-integrity** — idempotency of money-moving operations, duplicate-submission and
32
+ double-spend windows, rounding and precision (integer minor units, never floats), atomicity
33
+ and rollback on partial failure, audit-trail completeness, negative/overflow amounts,
34
+ currency and unit mismatches.
35
+ - **tenant-isolation** — every query and mutation scoped by tenant, tenant identity taken from
36
+ trusted context (never from client input), cross-tenant reads via shared caches or search
37
+ indexes, background jobs and reports crossing tenant boundaries, id enumeration across
38
+ tenants.
39
+ - **mcp-agent** — prompt injection via tool results or user content, over-broad tool
40
+ permissions, secret exfiltration through model context or logs, model output executed or
41
+ evaluated unsafely, untrusted data treated as instructions, capability escalation between
42
+ tools.
43
+
44
+ ## Contract
45
+
46
+ - **Scope.** Judge the diff and its direct blast radius (an auth change may weaken a caller you
47
+ must read); raise findings only where the changed code creates or leaves the exposure.
48
+ - **Findings.** Number them SA-001, SA-002, … Each: severity (🔴 CRITICAL / 🟠 MAJOR /
49
+ 🟡 MINOR, calibrated honestly), the checklist it violates, `file:line`, the concrete attack or
50
+ failure it enables, and a concrete remedy. Group by severity. If the phase is clean under
51
+ every active checklist, report **"no findings"** explicitly.
52
+ - **Read-only.** You never edit files, apply fixes, or commit. Bash is for inspection only.
53
+ - If the packet is insufficient — no diff, no active profile list — STOP and report exactly
54
+ what is missing as a blocker. Never guess.
@@ -0,0 +1,15 @@
1
+ # Generated by CodeOps install_agents.py
2
+ # Role: semantics-reviewer | Template: semantics-reviewer
3
+ # Do not edit this file manually — regenerate with: install_agents.py
4
+ ---
5
+ description: Reviews compiler, language, protocol, or other formal-semantics changes for completeness, internal consistency, and preservation across phases.
6
+ mode: subagent
7
+ temperature: 0.1
8
+ hidden: true
9
+ permission:
10
+ read: allow
11
+ edit: deny
12
+ bash: deny
13
+ ---
14
+
15
+ Review exactly the supplied semantic specification or implementation packet. Trace behavior across syntax/decoding, name or identity resolution, typing/validation, intermediate representations, evaluation/lowering, optimization/transformation, diagnostics, serialization, and compatibility as applicable. Seek ambiguous rules, non-total behavior, phase disagreement, unsound transformations, nondeterminism, invalid recovery, and diagnostics that expose implementation accidents. Use counterexamples and minimal programs/messages to falsify the claimed semantics. Cite evidence and return surviving findings with severity, example, affected phases, and resolution, or an explicit clean result. Remain read-only.
@@ -0,0 +1,41 @@
1
+ # Generated by CodeOps install_agents.py
2
+ # Role: spec-test-author | Template: spec-test-author
3
+ # Do not edit this file manually — regenerate with: install_agents.py
4
+ ---
5
+ description: Writes specification tests (*.spec.test.*) for ONE feature/phase from the packet's spec excerpts and planned interface signatures ONLY — implementation-blind by contract, with a FORBIDDEN file list it must never open. Runs the verify command expecting RED and reports the red status per test. Never weakens an expectation to ease authoring. Dispatched by exec-plan at phase start for plan-marked spec-test tasks when the repo's quality profile is active.
6
+ mode: subagent
7
+ temperature: 0.1
8
+ hidden: true
9
+ permission:
10
+ read: allow
11
+ grep: allow
12
+ glob: allow
13
+ edit: allow
14
+ bash: allow
15
+ ---
16
+
17
+ You author the specification tests for exactly ONE feature or phase, via a spec packet (spec
18
+ excerpts, the expected test cases, planned interface signatures from the plan documents, the
19
+ repo's test framework and conventions, the FORBIDDEN implementation-file list, and the verify
20
+ command). The conventions behind the packet live in `_shared/quality-profile.md`.
21
+
22
+ - **Implementation blindness (non-negotiable).** Derive every expectation from the packet's spec
23
+ excerpts and planned interfaces ONLY. Never open, grep, or glob a file on the FORBIDDEN list —
24
+ those are the implementation targets your tests must independently judge. Before you report
25
+ done, self-check and state explicitly that no forbidden file was read.
26
+ - **Author the oracle.** Write `[feature].spec.test.[ext]` files per the repo's conventions.
27
+ Encode what the specification demands, not what looks implementable: if a spec excerpt and
28
+ ease of authoring conflict, the spec wins. Never weaken, broaden, or fuzz an expectation to
29
+ make the test easier to write or likelier to pass.
30
+ - **Red phase.** Run the packet's verify command expecting failure. Report each authored test by
31
+ name with its red status. A spec test that passes before the implementation exists is
32
+ suspect — report it with a justification or rework it until it genuinely tests something new.
33
+ - **Documentation ban (non-negotiable).** The packet quotes plan and spec material for YOUR
34
+ understanding only — never copy a plan/requirement/decision identifier or a
35
+ `codeops/`/`plans/`/`requirements/` path into test code or comments. State each test's intent
36
+ in plain language. Before reporting done, grep your written files for
37
+ `\b(RD|AR|PA|PF|HR|GATE|AC|ST|ADR|DEF)-[0-9]` and `(codeops|plans|requirements)/` and fix any
38
+ hit that landed in a comment.
39
+ - If the packet is insufficient — an interface signature missing, a case ambiguous, framework
40
+ conventions unclear — STOP and report exactly what is missing as a blocker. Never guess an
41
+ expectation and never peek at the implementation to resolve doubt.
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # codeops-worktree — spin up / tear down git worktrees for running parallel
4
+ # OpenCode agents on separate feature branches, one worktree per agent.
5
+ #
6
+ # CodeOps Artifact Schema: 1
7
+ #
8
+ # A worktree is a second working folder that shares the ONE .git of the repo, so
9
+ # every branch/commit is visible everywhere and you never re-clone. The number of
10
+ # folders you need is the number of agents running *at once*, not the number of
11
+ # features.
12
+ #
13
+ # Usage:
14
+ # codeops-worktree new <topic> [--from <base>] [--branch <name>] [--path <dir>] [--launch] [--dry-run]
15
+ # codeops-worktree ls
16
+ # codeops-worktree rm <topic|path> [--force] [--delete-branch] [--dry-run]
17
+ # codeops-worktree help
18
+ #
19
+ # Examples:
20
+ # codeops-worktree new billing # -> feat/billing in ../<repo>-billing
21
+ # codeops-worktree new billing --launch # ...and start `opencode` in it
22
+ # codeops-worktree new hotfix --from master --branch fix/login
23
+ # codeops-worktree ls
24
+ # codeops-worktree rm billing --delete-branch
25
+ #
26
+ # New branches fork from the marker's integrationBranch (codeops/.codeops.yml) when
27
+ # that key is set — so a devel/acceptance workflow needs no flag — otherwise from the
28
+ # repo's default branch. Override per-run with --from.
29
+ #
30
+ # Run it from anywhere inside the repo (the main checkout or any worktree).
31
+
32
+ set -euo pipefail
33
+
34
+ die() { printf 'codeops-worktree: %s\n' "$*" >&2; exit 1; }
35
+ note() { printf '%s\n' "$*" >&2; }
36
+
37
+ DRY_RUN=0
38
+ # Execute a command, or — in dry-run mode — print it (shell-quoted) instead.
39
+ # Only the state-changing calls go through run(); read-only probes still run so
40
+ # the preview reflects the branch the real command would actually pick.
41
+ run() {
42
+ if [[ "$DRY_RUN" -eq 1 ]]; then
43
+ printf ' [dry-run] would run: %s\n' "$(printf '%q ' "$@")" >&2
44
+ return 0
45
+ fi
46
+ "$@"
47
+ }
48
+
49
+ require_repo() {
50
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 \
51
+ || die "not inside a git repository — cd into your project first."
52
+ }
53
+
54
+ # The main worktree is always the first entry of `git worktree list`. Sibling
55
+ # folders are placed next to IT, so nested worktrees don't spawn nested siblings.
56
+ main_worktree_root() {
57
+ git worktree list --porcelain | awk '/^worktree /{print substr($0, 10); exit}'
58
+ }
59
+
60
+ # Normalize a topic into a safe slug: lowercase, dash-separated, alnum only.
61
+ # Anything else (spaces, slashes, "..") collapses to a dash and is trimmed — so a
62
+ # slug can never traverse paths or carry a separator.
63
+ slugify() {
64
+ printf '%s' "$1" \
65
+ | tr '[:upper:]' '[:lower:]' \
66
+ | sed -E 's#[^a-z0-9]+#-#g; s#^-+##; s#-+$##'
67
+ }
68
+
69
+ branch_exists_local() { git show-ref --verify --quiet "refs/heads/$1"; }
70
+ branch_exists_remote() { git show-ref --verify --quiet "refs/remotes/origin/$1"; }
71
+
72
+ # The configured integration branch from the CodeOps marker, if any: the branch
73
+ # where feature work integrates and derived files (the portfolio roadmap, AGENTS.md)
74
+ # get reconciled. This is committed config that is only parsed, never executed, and
75
+ # the marker is a tracked file so it is present in every worktree checkout. Empty
76
+ # output + non-zero return when there is no marker or no key.
77
+ marker_integration_branch() {
78
+ local top marker val
79
+ top="$(git rev-parse --show-toplevel 2>/dev/null)" || return 1
80
+ marker="$top/codeops/.codeops.yml"
81
+ [[ -f "$marker" ]] || return 1
82
+ # `|| true` so a no-match grep (or a SIGPIPE from head) can't trip pipefail when
83
+ # this resolver is called outside an `if`/`||` context.
84
+ val="$(grep -E '^[[:space:]]*integrationBranch:' "$marker" 2>/dev/null | head -1 \
85
+ | sed -E 's/^[[:space:]]*integrationBranch:[[:space:]]*//; s/[[:space:]]*#.*$//; s/[[:space:]]+$//' || true)"
86
+ [[ -n "$val" ]] || return 1
87
+ printf '%s\n' "$val"
88
+ }
89
+
90
+ # The branch new features fork from: the marker's integrationBranch if set (so the
91
+ # base agrees with the branch-aware skills), else origin/HEAD if known, else main,
92
+ # else master, else whatever is currently checked out.
93
+ default_base_branch() {
94
+ local ib
95
+ if ib="$(marker_integration_branch)"; then
96
+ printf '%s\n' "$ib"; return
97
+ fi
98
+ local ref
99
+ if ref=$(git symbolic-ref --quiet refs/remotes/origin/HEAD 2>/dev/null); then
100
+ printf '%s\n' "${ref#refs/remotes/origin/}"; return
101
+ fi
102
+ local b
103
+ for b in main master; do
104
+ if branch_exists_local "$b"; then printf '%s\n' "$b"; return; fi
105
+ done
106
+ git rev-parse --abbrev-ref HEAD
107
+ }
108
+
109
+ cmd_new() {
110
+ local topic="" base="" branch="" path="" launch=0
111
+ while [[ $# -gt 0 ]]; do
112
+ case "$1" in
113
+ --from) base="${2:-}"; shift 2 ;;
114
+ --branch) branch="${2:-}"; shift 2 ;;
115
+ --path) path="${2:-}"; shift 2 ;;
116
+ --launch|-l) launch=1; shift ;;
117
+ --dry-run) DRY_RUN=1; shift ;;
118
+ -*) die "unknown option for 'new': $1" ;;
119
+ *) [[ -z "$topic" ]] && topic="$1" || die "unexpected argument: $1"; shift ;;
120
+ esac
121
+ done
122
+ [[ -n "$topic" ]] || die "usage: codeops-worktree new <topic> [--from <base>] [--branch <name>] [--path <dir>] [--launch] [--dry-run]"
123
+
124
+ local slug; slug="$(slugify "$topic")"
125
+ [[ -n "$slug" ]] || die "topic '$topic' has no usable characters — pick a name with letters or digits."
126
+ [[ -n "$branch" ]] || branch="feat/${slug}"
127
+
128
+ local root parent repo
129
+ root="$(main_worktree_root)"
130
+ parent="$(dirname "$root")"
131
+ repo="$(basename "$root")"
132
+ [[ -n "$path" ]] || path="${parent}/${repo}-${slug}"
133
+
134
+ [[ -e "$path" ]] && die "target path already exists: $path"
135
+
136
+ # Decide how to create the worktree from branch state.
137
+ if branch_exists_local "$branch"; then
138
+ note "Branch '$branch' already exists — checking it out into a new worktree."
139
+ run git worktree add "$path" "$branch"
140
+ elif branch_exists_remote "$branch"; then
141
+ note "Remote branch 'origin/$branch' found — creating a tracking worktree."
142
+ run git worktree add --track -b "$branch" "$path" "origin/$branch"
143
+ else
144
+ [[ -n "$base" ]] || base="$(default_base_branch)"
145
+ note "Creating new branch '$branch' from '$base'."
146
+ run git worktree add -b "$branch" "$path" "$base"
147
+ fi
148
+
149
+ if [[ "$DRY_RUN" -eq 1 ]]; then
150
+ printf '\n🔎 [dry-run] would create worktree (nothing was changed)\n'
151
+ else
152
+ printf '\n✅ Worktree ready\n'
153
+ fi
154
+ printf ' branch : %s\n' "$branch"
155
+ printf ' folder : %s\n' "$path"
156
+ printf '\nStart an agent there:\n cd %q && opencode\n' "$path"
157
+ printf '\nParallel-safety reminders:\n'
158
+ printf ' • Keep this agent to ONE feature/lane — its own codeops/features/<f>/ files never clash.\n'
159
+ printf ' • analyze-project is branch-aware here — preview repository-wide AGENTS.md changes.\n'
160
+ printf ' • Run setup-routing on the integration branch when it changes shared codeops/codeops.json.\n'
161
+ printf ' • After the PRs merge, reconcile the portfolio on the integration branch: `update the roadmap`.\n'
162
+ printf ' • Tear this down when done: codeops-worktree rm %s\n\n' "$slug"
163
+
164
+ if [[ "$launch" -eq 1 ]]; then
165
+ if [[ "$DRY_RUN" -eq 1 ]]; then
166
+ printf ' [dry-run] would run: cd %q && exec opencode\n' "$path" >&2
167
+ else
168
+ command -v opencode >/dev/null 2>&1 || die "--launch given but 'opencode' is not on PATH."
169
+ note "Launching opencode in $path ..."
170
+ cd "$path" && exec opencode
171
+ fi
172
+ fi
173
+ }
174
+
175
+ cmd_ls() {
176
+ git worktree list
177
+ }
178
+
179
+ cmd_rm() {
180
+ local target="" force=0 del_branch=0
181
+ while [[ $# -gt 0 ]]; do
182
+ case "$1" in
183
+ --force|-f) force=1; shift ;;
184
+ --delete-branch) del_branch=1; shift ;;
185
+ --dry-run) DRY_RUN=1; shift ;;
186
+ -*) die "unknown option for 'rm': $1" ;;
187
+ *) [[ -z "$target" ]] && target="$1" || die "unexpected argument: $1"; shift ;;
188
+ esac
189
+ done
190
+ [[ -n "$target" ]] || die "usage: codeops-worktree rm <topic|path> [--force] [--delete-branch] [--dry-run]"
191
+
192
+ # Resolve target -> worktree path. Accept an explicit path or a topic/slug.
193
+ local path=""
194
+ if [[ -d "$target" ]]; then
195
+ path="$(cd "$target" && git rev-parse --show-toplevel 2>/dev/null || true)"
196
+ fi
197
+ if [[ -z "$path" ]]; then
198
+ local slug root repo cand
199
+ slug="$(slugify "$target")"
200
+ root="$(main_worktree_root)"; repo="$(basename "$root")"
201
+ cand="$(dirname "$root")/${repo}-${slug}"
202
+ if git worktree list --porcelain | grep -qxF "worktree ${cand}"; then
203
+ path="$cand"
204
+ fi
205
+ fi
206
+ [[ -n "$path" ]] || die "no worktree found for '$target' (try: codeops-worktree ls)."
207
+
208
+ # Capture the branch before removing, in case --delete-branch was asked.
209
+ local branch=""
210
+ branch="$(git -C "$path" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
211
+
212
+ local rm_args=(worktree remove)
213
+ [[ "$force" -eq 1 ]] && rm_args+=(--force)
214
+ rm_args+=("$path")
215
+ run git "${rm_args[@]}"
216
+ [[ "$DRY_RUN" -eq 1 ]] || note "Removed worktree: $path"
217
+
218
+ if [[ "$del_branch" -eq 1 && -n "$branch" && "$branch" != "HEAD" ]]; then
219
+ if [[ "$DRY_RUN" -eq 1 ]]; then
220
+ run git branch -D "$branch"
221
+ else
222
+ git branch -D "$branch" && note "Deleted branch: $branch"
223
+ fi
224
+ fi
225
+ }
226
+
227
+ # Print the leading comment block as help — stops at the first non-comment line,
228
+ # so it never leaks code (and survives edits to the header without a fixed range).
229
+ usage() {
230
+ awk 'NR==1 {next} /^#/ {sub(/^# ?/, ""); print; next} {exit}' "$0"
231
+ }
232
+
233
+ main() {
234
+ local sub="${1:-help}"; shift || true
235
+ case "$sub" in
236
+ new) require_repo; cmd_new "$@" ;;
237
+ ls|list) require_repo; cmd_ls "$@" ;;
238
+ rm|remove) require_repo; cmd_rm "$@" ;;
239
+ help|-h|--help) usage ;;
240
+ *) die "unknown command: $sub (try: codeops-worktree help)" ;;
241
+ esac
242
+ }
243
+
244
+ main "$@"
package/bin/index.mjs ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * The `opencode-codeops` command-line entry point.
4
+ *
5
+ * Routes each command to the skills installer (`install-skills.mjs`) or the
6
+ * agent installer (`install-agents.mjs`), so one package binary exposes both.
7
+ * `status` reports on both installs because the skills and the plugin can drift
8
+ * in version and that is the check a user needs most.
9
+ *
10
+ * Usage:
11
+ * opencode-codeops install-skills [options] Install or upgrade skills
12
+ * opencode-codeops install-agents [options] Install or upgrade subagents
13
+ * opencode-codeops status [options] Show installed skills and agents
14
+ * opencode-codeops uninstall [options] Remove the managed skills
15
+ * opencode-codeops help Show this help
16
+ *
17
+ * @module index
18
+ */
19
+
20
+ import { realpathSync } from "node:fs"
21
+ import { fileURLToPath } from "node:url"
22
+
23
+ /** Prints command usage. */
24
+ function printUsage() {
25
+ console.log(`CodeOps installer for OpenCode.
26
+
27
+ Usage:
28
+ opencode-codeops install-skills [options] Install or upgrade skills (default)
29
+ opencode-codeops install-agents [options] Install or upgrade subagents
30
+ opencode-codeops status [options] Show installed skills and agents
31
+ opencode-codeops uninstall [options] Remove the managed skills
32
+ opencode-codeops help Show this help
33
+
34
+ Run \`opencode-codeops install-skills --help\` or
35
+ \`opencode-codeops install-agents --help\` for the option list.`)
36
+ }
37
+
38
+ /**
39
+ * Chooses the installer module for a command line.
40
+ *
41
+ * @param argv - Arguments after the executable
42
+ * @returns `"agents"` for agent commands, otherwise `"skills"`
43
+ */
44
+ export function route(argv) {
45
+ const first = argv[0]
46
+ if (first === "install-agents" || first === "agents-status" || first === "agents-uninstall") {
47
+ return "agents"
48
+ }
49
+ return "skills"
50
+ }
51
+
52
+ /**
53
+ * Runs the requested command.
54
+ *
55
+ * The modules are imported lazily so a skills-only invocation never loads the
56
+ * agent installer, and vice versa.
57
+ */
58
+ async function run() {
59
+ const args = process.argv.slice(2)
60
+ const first = args[0]
61
+
62
+ if (!first || first === "help" || first === "-h" || first === "--help") {
63
+ printUsage()
64
+ return
65
+ }
66
+
67
+ if (first === "status") {
68
+ const skills = await import("./install-skills.mjs")
69
+ const agents = await import("./install-agents.mjs")
70
+ const skillsCode = skills.main(["status", ...args.slice(1)])
71
+ const agentsCode = agents.main(["agents-status", ...args.slice(1)])
72
+ process.exitCode = skillsCode || agentsCode
73
+ return
74
+ }
75
+
76
+ if (route(args) === "agents") {
77
+ const { main } = await import("./install-agents.mjs")
78
+ process.exitCode = main(args)
79
+ return
80
+ }
81
+
82
+ const { main } = await import("./install-skills.mjs")
83
+ process.exitCode = main(args)
84
+ }
85
+
86
+ /**
87
+ * True when this module is the process entry point.
88
+ *
89
+ * The comparison resolves symlinks because npm installs the bin as a symlink in
90
+ * `node_modules/.bin`, so `process.argv[1]` is the link path, not the real path.
91
+ *
92
+ * @returns True when this file is the entry point
93
+ */
94
+ function isMainModule() {
95
+ if (!process.argv[1]) return false
96
+
97
+ try {
98
+ return fileURLToPath(import.meta.url) === realpathSync(process.argv[1])
99
+ } catch {
100
+ return false
101
+ }
102
+ }
103
+
104
+ if (isMainModule()) {
105
+ await run()
106
+ }