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.
- package/CHANGELOG.md +179 -0
- package/LICENSE +21 -0
- package/README.md +171 -0
- package/_shared/auto-design.md +129 -0
- package/_shared/layout-convention.md +198 -0
- package/_shared/quality-profile.md +134 -0
- package/_shared/recommendation-hardening.md +166 -0
- package/_shared/scope-expansion-control.md +176 -0
- package/_shared/spec-first-ordering.md +79 -0
- package/_shared/zero-ambiguity-gate.md +311 -0
- package/agent-templates/codebase-scout.md +17 -0
- package/agent-templates/concurrency-auditor.md +5 -0
- package/agent-templates/design-challenger.md +26 -0
- package/agent-templates/financial-integrity-auditor.md +5 -0
- package/agent-templates/perf-auditor.md +23 -0
- package/agent-templates/phase-reviewer.md +54 -0
- package/agent-templates/plan-task-executor-opus.md +46 -0
- package/agent-templates/plan-task-executor.md +43 -0
- package/agent-templates/preflight-auditor.md +45 -0
- package/agent-templates/security-auditor.md +42 -0
- package/agent-templates/semantics-reviewer.md +5 -0
- package/agent-templates/spec-test-author.md +29 -0
- package/agents/concurrency-auditor.md +15 -0
- package/agents/correctness-reviewer.md +66 -0
- package/agents/demanding-executor.md +58 -0
- package/agents/design-challenger.md +38 -0
- package/agents/executor.md +55 -0
- package/agents/explorer.md +29 -0
- package/agents/financial-integrity-auditor.md +15 -0
- package/agents/performance-auditor.md +35 -0
- package/agents/preflight-auditor.md +57 -0
- package/agents/security-auditor.md +54 -0
- package/agents/semantics-reviewer.md +15 -0
- package/agents/spec-test-author.md +41 -0
- package/bin/codeops-worktree +244 -0
- package/bin/index.mjs +106 -0
- package/bin/install-agents.mjs +453 -0
- package/bin/install-skills.mjs +466 -0
- package/bin/lib/opencode-install.mjs +185 -0
- package/install.sh +55 -0
- package/package.json +73 -0
- package/plugin/index.ts +181 -0
- package/references/domains/compiler-and-language.md +28 -0
- package/references/domains/data-and-migration.md +22 -0
- package/references/domains/distributed-and-concurrent.md +26 -0
- package/references/domains/financial-system.md +28 -0
- package/references/domains/selection.md +19 -0
- package/references/domains/web-application.md +23 -0
- package/schemas/codeops-config.schema.json +56 -0
- package/scripts/check-version.mjs +163 -0
- package/scripts/codeops-migrate.sh +355 -0
- package/scripts/codeops-roadmap-compact.sh +232 -0
- package/scripts/codeops-roadmap-sync.sh +275 -0
- package/scripts/codeops_outcomes.py +155 -0
- package/scripts/codeops_plan.py +239 -0
- package/scripts/codeops_plan_migrate.py +318 -0
- package/scripts/codeops_worktree_snapshot.py +99 -0
- package/scripts/install_agents.py +288 -0
- package/scripts/release.mjs +533 -0
- package/skills/analyze-project/SKILL.md +28 -0
- package/skills/clean-comments/SKILL.md +22 -0
- package/skills/exec-plan/SKILL.md +267 -0
- package/skills/exec-plan/commit-modes.md +113 -0
- package/skills/exec-plan/execution-protocol.md +471 -0
- package/skills/git-commit/SKILL.md +35 -0
- package/skills/github-issues/SKILL.md +38 -0
- package/skills/grill-me/SKILL.md +342 -0
- package/skills/make-plan/SKILL.md +282 -0
- package/skills/make-plan/quality-checklist.md +96 -0
- package/skills/make-plan/templates.md +535 -0
- package/skills/make-plan/zero-ambiguity-gate.md +19 -0
- package/skills/make-requirements/SKILL.md +268 -0
- package/skills/make-requirements/discovery-phases.md +255 -0
- package/skills/make-requirements/review-and-add.md +73 -0
- package/skills/make-requirements/templates.md +296 -0
- package/skills/make-requirements/zero-ambiguity-gate.md +18 -0
- package/skills/outcome-review/SKILL.md +34 -0
- package/skills/preflight/SKILL.md +310 -0
- package/skills/preflight/dimensions.md +181 -0
- package/skills/preflight/report-format.md +300 -0
- package/skills/retro-requirements/SKILL.md +218 -0
- package/skills/retro-requirements/confidence-classification.md +45 -0
- package/skills/retro-requirements/phases.md +609 -0
- package/skills/retro-requirements/triage-gate.md +135 -0
- package/skills/roadmap/SKILL.md +381 -0
- package/skills/roadmap/stage-hooks.md +80 -0
- package/skills/roadmap/template.md +200 -0
- package/skills/setup-codeops/SKILL.md +94 -0
- package/skills/setup-codeops/migration.md +106 -0
- package/skills/setup-codeops/scaffold.md +99 -0
- package/skills/setup-routing/SKILL.md +102 -0
- package/skills/setup-routing/routing.md +44 -0
- package/skills/techdocs/SKILL.md +199 -0
- package/skills/techdocs/authoring-and-update.md +178 -0
- package/skills/techdocs/templates.md +655 -0
- package/skills/techdocs/vitepress-setup.md +143 -0
- package/skills/upgrade-plan/SKILL.md +75 -0
- package/skills/upgrade-plan/content-quality-gate.md +35 -0
- package/skills/upgrade-plan/upgrade-checklists.md +107 -0
- package/standards/coding-standards-full.md +124 -0
- package/standards/coding-standards.md +64 -0
- 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
|
+
}
|