triad-plus 1.9.0 → 1.11.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 (34) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +39 -0
  3. package/adapters/antigravity/README.md +1 -1
  4. package/adapters/antigravity/install.sh +1 -1
  5. package/adapters/claude-code/README.md +1 -1
  6. package/adapters/claude-code/install.sh +1 -1
  7. package/adapters/codex/README.md +1 -1
  8. package/adapters/codex/install.sh +3 -3
  9. package/adapters/hermes/install.sh +1 -1
  10. package/adapters/opencode/.opencode/agents/triad-developer.md +14 -0
  11. package/adapters/opencode/.opencode/agents/triad-orchestrator.md +1 -0
  12. package/adapters/opencode/.opencode/agents/triad-reviewer.md +15 -0
  13. package/adapters/opencode/README.md +15 -6
  14. package/adapters/opencode/install.sh +1 -1
  15. package/adapters/registry.mjs +22 -2
  16. package/bin/triad-plus.js +767 -56
  17. package/docs/configuration.md +57 -6
  18. package/docs/npx-installation.md +77 -4
  19. package/docs/opencode-replication.md +8 -0
  20. package/docs/runtimes.md +1 -1
  21. package/docs/troubleshooting.md +41 -0
  22. package/package.json +2 -2
  23. package/runtime/lib/assignment-packet.mjs +38 -1
  24. package/runtime/lib/installation-manifest.mjs +269 -0
  25. package/runtime/lib/model-config.mjs +165 -0
  26. package/runtime/lib/repository-context.mjs +192 -0
  27. package/runtime/lib/terminal.mjs +86 -0
  28. package/runtime/triad-runtime-context.mjs +88 -0
  29. package/runtime/triad-verify.mjs +26 -18
  30. package/skills/triad-loop-developer/SKILL.md +14 -0
  31. package/skills/triad-loop-orchestrator/SKILL.md +13 -0
  32. package/skills/triad-loop-reviewer/SKILL.md +16 -0
  33. package/skills/triad-model-configuration/SKILL.md +98 -0
  34. package/skills/triad-model-configuration/agents/openai.yaml +4 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.11.0 — 2026-09-17
4
+
5
+ - Add an installation manifest that records Triad-owned assets, materialized
6
+ version, adapter scopes, hashes, and deterministic ownership fingerprint.
7
+ - Add CLI/workspace version visibility, legacy manifest migration through
8
+ `upgrade --apply`, and conservative dry-run/apply uninstall for all adapters.
9
+ - Preserve modified or unknown assets and all project-control state during
10
+ uninstall; report version skew and manifest integrity through `doctor`.
11
+ - Preserve shared global assets by default, remove only exact managed
12
+ `AGENTS.md` blocks, and leave generic host directories untouched.
13
+
14
+ ## 1.10.0 — 2026-09-16
15
+
16
+ - Improve TUI and doctor output with terminal-native summaries, sober color
17
+ handling, `NO_COLOR`, and non-TTY support.
18
+ - Add the agent-first model-configuration skill with `.triad-plus/team.json`
19
+ as the role/model source of truth and explicit host-native bindings,
20
+ including OpenCode `variant` materialization.
21
+ - Add shared-skill diagnostics and repair during managed upgrades, plus
22
+ deterministic repository/runtime context verification and nested-repository
23
+ hardening.
24
+ - Fail closed on incorrect working directories, mixed Git roots, symlink
25
+ escapes, missing or mutated skills, and implicit fallback to unrelated skill
26
+ copies.
27
+
3
28
  ## 1.9.0 — 2026-09-16
4
29
 
5
30
  - Add native BMAD `epics.md` intake while preserving the boundary that BMAD
package/README.md CHANGED
@@ -107,6 +107,38 @@ The Orchestrator first shows the feature cards, then delegates the bounded work.
107
107
  If a tutorial step is unclear, see the [OpenCode guide](docs/runtimes.md#opencode)
108
108
  and [troubleshooting](docs/troubleshooting.md).
109
109
 
110
+ ## Installation version and safe uninstall
111
+
112
+ The CLI version and the version materialized in a control workspace are
113
+ reported independently:
114
+
115
+ ```bash
116
+ npx triad-plus --version
117
+ npx triad-plus version --control /absolute/path/to/triad-control
118
+ ```
119
+
120
+ Successful `init` and `upgrade --apply` operations record exact Triad-owned
121
+ files in `.triad-plus/installation.json`. This manifest does not own
122
+ `.triad-plus/team.json`, `.loop/`, product files, or evidence.
123
+ `init` is the first-install path; `upgrade --apply` is also the managed
124
+ update/repair/restore path for a registered workspace, including one left
125
+ `uninstalled` or `partial` by safe uninstall.
126
+
127
+ Uninstall is conservative and dry-run by default:
128
+
129
+ ```bash
130
+ npx triad-plus uninstall --host opencode --control /absolute/path/to/triad-control
131
+ npx triad-plus uninstall --host opencode --control /absolute/path/to/triad-control --apply
132
+ ```
133
+
134
+ Only unchanged files recorded by the manifest are removed. Modified or
135
+ unknown files are preserved and reported; user state remains available for a
136
+ future installation or review. Generic host directories are never pruned. A
137
+ managed `AGENTS.md` role-run block is removed only when its markers and hash
138
+ are exact; surrounding user content is preserved. Global assets are shown but
139
+ preserved by default with `--global` because another control workspace may
140
+ share them.
141
+
110
142
  ## Optional immutable quality target
111
143
 
112
144
  An initialized project may bind an immutable JSON Quality Baseline through
@@ -184,6 +216,13 @@ workspace with that adapter. Triad+ does not dispatch different roles across
184
216
  different hosts in one run. Read [configuration](docs/configuration.md) and
185
217
  [runtime details](docs/runtimes.md).
186
218
 
219
+ The installed `triad-model-configuration` Agent Skill lets the host agent
220
+ inspect or change requested role models while preserving `team.json` as the
221
+ source of truth and reporting host-specific binding limits.
222
+
223
+ On OpenCode, `team.json`'s `reasoning_effort` is materialized as the native
224
+ per-agent `variant`; the OpenCode session default is a separate host setting.
225
+
187
226
  `triad-verify` validates assignment/candidate binding, runs declared
188
227
  control-plane gates, detects mutation, and writes evidence. It supports the
189
228
  loop; it never replaces the Orchestrator. See [verification](docs/verification.md).
@@ -1,7 +1,7 @@
1
1
  # Triad+ for Antigravity
2
2
 
3
3
  The Antigravity adapter uses native workspace paths: `.agents/skills/` for the
4
- five shared Triad+ skills and `/triad` workflow, plus `.agents/agents/` for the
4
+ six shared Triad+ skills and `/triad` workflow, plus `.agents/agents/` for the
5
5
  four technical agents. Start Antigravity from the project-control workspace and
6
6
  invoke:
7
7
 
@@ -30,7 +30,7 @@ fi
30
30
  adapter_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
31
31
  repository_root="$(cd -- "$adapter_root/../.." && pwd)"
32
32
  agent_names=(triad-orchestrator triad-developer triad-evaluator triad-reviewer)
33
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
33
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
34
34
  collisions=()
35
35
 
36
36
  for agent_name in "${agent_names[@]}"; do
@@ -1,6 +1,6 @@
1
1
  # Triad+ Engineering Loop for Claude Code
2
2
 
3
- This adapter installs the shared five Triad skills, three isolated Claude Code
3
+ This adapter installs the shared six Triad skills, three isolated Claude Code
4
4
  subagents, the `/triad` command, and—on a project installation—the Node control
5
5
  plane. It does not modify global Claude settings or automatically activate a
6
6
  hook.
@@ -30,7 +30,7 @@ fi
30
30
  adapter_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
31
31
  repository_root="$(cd -- "$adapter_root/../.." && pwd)"
32
32
  agent_names=(triad-developer triad-evaluator triad-reviewer)
33
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
33
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
34
34
  collisions=()
35
35
 
36
36
  for agent_name in "${agent_names[@]}"; do
@@ -18,7 +18,7 @@ Then install the global entry point and the skills once for the Codex user:
18
18
  ```
19
19
 
20
20
  The installer refuses any overwrite. It installs the project runtime under
21
- `.triad-runtime` and the five shared skills under `.agents/skills`; the global
21
+ `.triad-runtime` and the six shared skills under `.agents/skills`; the global
22
22
  installation also makes the skills available to Codex and writes
23
23
  `$CODEX_HOME/prompts/triad.md` (`$HOME/.codex` by default).
24
24
 
@@ -8,10 +8,10 @@ Usage:
8
8
  ./adapters/codex/install.sh --global
9
9
 
10
10
  Install the Triad Codex adapter without overwriting existing skills, prompt, or
11
- project runtime. --project installs the five skills under
11
+ project runtime. --project installs the six skills under
12
12
  <control-repository-path>/.agents/skills and installs .triad-runtime. It does
13
13
  not install the user-level Codex prompt. --global installs the /prompts:triad
14
- entry point and the five skills under $CODEX_HOME (default: $HOME/.codex).
14
+ entry point and the six skills under $CODEX_HOME (default: $HOME/.codex).
15
15
 
16
16
  For normal use run both commands once: --project for each control repository,
17
17
  then --global for the Codex entry point. Existing paths are never overwritten.
@@ -19,7 +19,7 @@ EOF
19
19
  }
20
20
 
21
21
  codex_home="${CODEX_HOME:-$HOME/.codex}"
22
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
22
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
23
23
 
24
24
  if [[ $# -eq 2 && "$1" == "--project" ]]; then
25
25
  project_root="$2"
@@ -18,7 +18,7 @@ fi
18
18
 
19
19
  adapter_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
20
20
  repository_root="$(cd -- "$adapter_root/../.." && pwd)"
21
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
21
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
22
22
 
23
23
  if [[ "$global_install" == true ]]; then
24
24
  hermes_root="${HERMES_HOME:-$HOME/.hermes}"
@@ -28,6 +28,20 @@ At activation, read `.triad-plus/team.json`. Your first report to the
28
28
  Orchestrator identifies the configured `roles.developer.displayName` as the
29
29
  Triad+ Developer and names the assigned card.
30
30
 
31
+ Before editing, run the exact runtime-context proof from this activation's
32
+ actual cwd:
33
+
34
+ ```bash
35
+ node /absolute/path/to/control-workspace/.triad-runtime/triad-runtime-context.mjs \
36
+ --project /absolute/path/to/control-workspace \
37
+ --assignment /absolute/path/to/control-workspace/.loop/runtime/assignments/<assignment-file>.json
38
+ ```
39
+
40
+ Record packet cwd, process cwd, shell `pwd`, Git top-level, and every resolved
41
+ repository-skill path/hash. A non-zero result or any mismatch is invalid
42
+ runtime context: stop and report it, and never substitute a control-workspace
43
+ or global skill copy.
44
+
31
45
  Verify the worktree and branch. Do not expand scope, silently change project
32
46
  policy, add dependencies without authorization, or make delivery decisions. Run
33
47
  the required gates, measure the declared metrics, and report exact commands and
@@ -17,6 +17,7 @@ permission:
17
17
  triad-reviewer: allow
18
18
  skill:
19
19
  "triad-loop-*": allow
20
+ "triad-model-configuration": allow
20
21
  ---
21
22
 
22
23
  You are the Triad+ Engineering Loop orchestrator. Govern the workflow and the
@@ -30,6 +30,21 @@ At activation, read `.triad-plus/team.json`. Your first report to the
30
30
  Orchestrator identifies the configured `roles.reviewer.displayName` as the
31
31
  Triad+ Reviewer and names the feature and attempt under review.
32
32
 
33
+ Before inspecting the candidate, run this exact runtime-context proof from the
34
+ actual Reviewer cwd:
35
+
36
+ ```bash
37
+ node /absolute/path/to/control-workspace/.triad-runtime/triad-runtime-context.mjs \
38
+ --project /absolute/path/to/control-workspace \
39
+ --assignment /absolute/path/to/control-workspace/.loop/runtime/assignments/<assignment-file>.json
40
+ ```
41
+
42
+ Record packet cwd, process cwd, shell `pwd`, Git top-level, and each resolved
43
+ repository-skill path/hash. On non-zero status, `NotFound`, hash mismatch,
44
+ mixed repository roots, or cwd inconsistency, return `blocked` / invalid
45
+ runtime context. Do not use a same-named skill from the control workspace or
46
+ global skills as a fallback.
47
+
33
48
  Return one evidence-based recommendation: `approved`, `rework`, or `blocked`.
34
49
  List severity-ranked findings before gate and metric evidence, residual risks,
35
50
  and the recommendation. A blocked recommendation must identify the exact owner
@@ -1,6 +1,6 @@
1
1
  # Triad+ Engineering Loop for OpenCode
2
2
 
3
- This adapter makes the repository's existing five standard Agent Skills
3
+ This adapter makes the repository's existing six standard Agent Skills
4
4
  operational in OpenCode. It installs four specialized agents and the `/triad`
5
5
  command; it does not duplicate the method or alter `opencode.json`.
6
6
 
@@ -26,6 +26,7 @@ skills/triad-loop-orchestrator/
26
26
  skills/triad-loop-developer/
27
27
  skills/triad-loop-evaluator/
28
28
  skills/triad-loop-reviewer/
29
+ skills/triad-model-configuration/
29
30
  .triad-runtime/
30
31
  triad-verify.mjs
31
32
  triad-runtime-capabilities.mjs
@@ -55,12 +56,12 @@ remain in a declared project workspace.
55
56
 
56
57
  `.triad-plus/team.json` is the source of truth for role-level model
57
58
  configuration. When a team file supplies values, Triad+ materializes the
58
- OpenCode-native `model` and `reasoningEffort` frontmatter fields in each
59
- project agent definition during `init` and `upgrade --apply`:
59
+ OpenCode-native `model` and `variant` frontmatter fields in each project agent
60
+ definition during `init` and `upgrade --apply`:
60
61
 
61
62
  ```text
62
63
  team.json model → OpenCode model
63
- team.json reasoning_effort → OpenCode reasoningEffort
64
+ team.json reasoning_effort → OpenCode variant
64
65
  ```
65
66
 
66
67
  Do not treat generated `.opencode/agents/*.md` files as an independent
@@ -70,8 +71,9 @@ absent so OpenCode uses its host/session default.
70
71
 
71
72
  The adapter only writes fields supported by this OpenCode materialization. Host
72
73
  and provider capabilities may still limit which model or reasoning levels are
73
- available. Inspect identifiers with `opencode models` and keep role-specific
74
- routing explicit in the team file:
74
+ available. Inspect identifiers and variants with `opencode models` and keep
75
+ role-specific routing explicit in the team file. OpenCode's role `variant` is
76
+ not the same thing as the default model/session selected by the TUI:
75
77
 
76
78
  | Agent | Recommended profile |
77
79
  | --- | --- |
@@ -84,6 +86,13 @@ Use `opencode models` to inspect the provider/model identifiers available in
84
86
  the local installation. Keep the developer, evaluator, and reviewer separately
85
87
  configured when independent model routing is required.
86
88
 
89
+ The adapter targets the installed OpenCode agent contract: `variant` is the
90
+ host-native per-agent option, while `team.json` remains the canonical desired
91
+ configuration. A null or blank model/effort removes the managed field and lets
92
+ OpenCode use its host or session default. `upgrade --apply` refreshes managed
93
+ agents and then reapplies this same binding; it does not rewrite `team.json`.
94
+ OpenCode 1.18.x exposes this contract through the `AgentConfig.variant` field.
95
+
87
96
  ## Start the loop
88
97
 
89
98
  Launch OpenCode from the project-control repository, then enter:
@@ -31,7 +31,7 @@ adapter_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
31
31
  repository_root="$(cd -- "$adapter_root/../.." && pwd)"
32
32
 
33
33
  agent_names=(triad-orchestrator triad-developer triad-evaluator triad-reviewer)
34
- skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer)
34
+ skill_names=(triad-loop-bootstrap triad-loop-orchestrator triad-loop-developer triad-loop-evaluator triad-loop-reviewer triad-model-configuration)
35
35
  command_name="triad"
36
36
  collisions=()
37
37
 
@@ -7,7 +7,8 @@ export const sharedSkillNames = [
7
7
  'triad-loop-orchestrator',
8
8
  'triad-loop-developer',
9
9
  'triad-loop-evaluator',
10
- 'triad-loop-reviewer'
10
+ 'triad-loop-reviewer',
11
+ 'triad-model-configuration'
11
12
  ];
12
13
 
13
14
  export const roleDefinitions = [
@@ -87,7 +88,13 @@ const definitions = [
87
88
  entry: '/triad',
88
89
  lifecycle: null,
89
90
  modelBinding: 'project-frontmatter',
90
- modelFields: ['model', 'reasoningEffort'],
91
+ // OpenCode's agent frontmatter has a first-class `variant` field. Keep
92
+ // Triad's canonical `reasoning_effort` name in team.json and map it to
93
+ // that host-native field rather than emitting provider options such as
94
+ // `reasoningEffort` into the agent profile.
95
+ modelFields: ['model', 'variant'],
96
+ modelFieldSources: { model: 'model', variant: 'reasoning_effort' },
97
+ modelCleanupFields: ['reasoningEffort'],
91
98
  modelRoles: roleDefinitions.map((role) => role.id),
92
99
  projectAssets: [
93
100
  { source: 'adapters/opencode/.opencode/agents', destination: '.opencode/agents' },
@@ -114,6 +121,10 @@ const definitions = [
114
121
  },
115
122
  roleModelPaths(controlRoot) {
116
123
  return roleDefinitions.map((role) => join(controlRoot, '.opencode', 'agents', `triad-${role.id}.md`));
124
+ },
125
+ globalRoleModelPaths() {
126
+ const root = hostHome('.config', 'opencode');
127
+ return rolePaths(root, roleDefinitions.map((role) => `triad-${role.id}`));
117
128
  }
118
129
  },
119
130
  {
@@ -157,6 +168,10 @@ const definitions = [
157
168
  },
158
169
  roleModelPaths(controlRoot) {
159
170
  return ['developer', 'reviewer', 'evaluator'].map((role) => join(controlRoot, '.claude', 'agents', `triad-${role}.md`));
171
+ },
172
+ globalRoleModelPaths() {
173
+ const root = hostHome('.claude');
174
+ return ['developer', 'reviewer', 'evaluator'].map((role) => join(root, 'agents', `triad-${role}.md`));
160
175
  }
161
176
  },
162
177
  {
@@ -222,6 +237,7 @@ const definitions = [
222
237
  lifecycle: null,
223
238
  modelBinding: 'project-frontmatter',
224
239
  modelFields: ['model', 'reasoningEffort'],
240
+ modelFieldSources: { model: 'model', reasoningEffort: 'reasoning_effort' },
225
241
  modelRoles: roleDefinitions.map((role) => role.id),
226
242
  projectAssets: [
227
243
  { source: 'adapters/copilot/.github/agents', destination: '.github/agents' },
@@ -253,6 +269,10 @@ const definitions = [
253
269
  },
254
270
  roleModelPaths(controlRoot) {
255
271
  return roleDefinitions.map((role) => join(controlRoot, '.github', 'agents', `triad-${role.id}.agent.md`));
272
+ },
273
+ globalRoleModelPaths() {
274
+ const root = hostHome('.copilot');
275
+ return roleDefinitions.map((role) => join(root, 'agents', `triad-${role.id}.agent.md`));
256
276
  }
257
277
  }
258
278
  ];