@nerviq/cli 0.9.2 → 0.9.3
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/package.json +1 -1
- package/src/aider/techniques.js +82 -11
- package/src/copilot/techniques.js +122 -11
- package/src/cursor/techniques.js +90 -10
- package/src/gemini/techniques.js +174 -23
- package/src/opencode/techniques.js +70 -99
- package/src/windsurf/techniques.js +211 -138
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenCode Techniques —
|
|
2
|
+
* OpenCode Techniques — 73 checks (OC-A01 through OC-P03)
|
|
3
3
|
*
|
|
4
4
|
* Categories:
|
|
5
5
|
* A. Instructions (7 checks)
|
|
@@ -62,18 +62,14 @@ const VALID_PLUGIN_EVENTS = new Set([
|
|
|
62
62
|
'error', 'warning',
|
|
63
63
|
]);
|
|
64
64
|
|
|
65
|
-
const DEPRECATED_CONFIG_KEYS = [
|
|
66
|
-
{ key: 'mode', replacement: 'agent', note: 'Use `agent` field instead of deprecated `mode`.' },
|
|
67
|
-
];
|
|
68
|
-
|
|
69
65
|
// --- Helpers ---
|
|
70
66
|
|
|
71
67
|
function agentsPath(ctx) {
|
|
72
|
-
return ctx.
|
|
68
|
+
return ctx.fileContent('AGENTS.md') ? 'AGENTS.md' : null;
|
|
73
69
|
}
|
|
74
70
|
|
|
75
71
|
function agentsContent(ctx) {
|
|
76
|
-
return ctx.
|
|
72
|
+
return ctx.fileContent('AGENTS.md') || '';
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
function configFileName(ctx) {
|
|
@@ -262,17 +258,16 @@ const OPENCODE_TECHNIQUES = {
|
|
|
262
258
|
|
|
263
259
|
opencodeNoCoexistenceConflict: {
|
|
264
260
|
id: 'OC-A05',
|
|
265
|
-
name: '
|
|
261
|
+
name: 'Mixed AGENTS.md + CLAUDE.md repos keep OpenCode guidance in AGENTS.md',
|
|
266
262
|
check: (ctx) => {
|
|
267
263
|
if (!ctx.hasAgentsMdAndClaudeMd || !ctx.hasAgentsMdAndClaudeMd()) return true;
|
|
268
|
-
// Both exist: check that AGENTS.md is primary, warn about potential confusion
|
|
269
264
|
const agentsMd = ctx.fileContent('AGENTS.md') || '';
|
|
270
265
|
return agentsMd.length > 0;
|
|
271
266
|
},
|
|
272
267
|
impact: 'high',
|
|
273
268
|
rating: 4,
|
|
274
269
|
category: 'instructions',
|
|
275
|
-
fix: '
|
|
270
|
+
fix: 'Keep OpenCode instructions in `AGENTS.md` when both files exist. Current runtime evidence did not validate a clean `CLAUDE.md` fallback, so do not rely on `CLAUDE.md` as the primary OpenCode instruction surface.',
|
|
276
271
|
template: 'opencode-agents-md',
|
|
277
272
|
file: () => 'AGENTS.md',
|
|
278
273
|
line: () => null,
|
|
@@ -291,7 +286,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
291
286
|
category: 'instructions',
|
|
292
287
|
fix: 'Remove generic filler ("Be helpful", "Write clean code") and replace with specific, actionable project instructions.',
|
|
293
288
|
template: 'opencode-agents-md',
|
|
294
|
-
file: () => agentsPath,
|
|
289
|
+
file: (ctx) => agentsPath(ctx),
|
|
295
290
|
line: (ctx) => findFillerLine(agentsContent(ctx)),
|
|
296
291
|
},
|
|
297
292
|
|
|
@@ -571,7 +566,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
571
566
|
|
|
572
567
|
opencodeAllToolsCovered: {
|
|
573
568
|
id: 'OC-C08',
|
|
574
|
-
name: '
|
|
569
|
+
name: 'Critical tool permissions are explicit',
|
|
575
570
|
check: (ctx) => {
|
|
576
571
|
const perms = ctx.toolPermissions();
|
|
577
572
|
if (!perms || Object.keys(perms).length === 0) return null;
|
|
@@ -582,7 +577,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
582
577
|
impact: 'high',
|
|
583
578
|
rating: 4,
|
|
584
579
|
category: 'permissions',
|
|
585
|
-
fix: 'Set explicit permissions for at least the critical tools: bash, edit, read, task.',
|
|
580
|
+
fix: 'Set explicit permissions for at least the critical tools: bash, edit, read, and task. The old fixed "15 tools" framing no longer matches current CLI/runtime surfaces.',
|
|
586
581
|
template: 'opencode-permissions',
|
|
587
582
|
file: (ctx) => configFileName(ctx),
|
|
588
583
|
line: () => null,
|
|
@@ -670,21 +665,17 @@ const OPENCODE_TECHNIQUES = {
|
|
|
670
665
|
|
|
671
666
|
opencodePluginHookGapAware: {
|
|
672
667
|
id: 'OC-D05',
|
|
673
|
-
name: '
|
|
668
|
+
name: 'Plugin docs do not rely on stale hook-gap claims',
|
|
674
669
|
check: (ctx) => {
|
|
675
670
|
const pluginFiles = ctx.pluginFiles();
|
|
676
671
|
if (pluginFiles.length === 0) return null;
|
|
677
672
|
const docs = docsBundle(ctx);
|
|
678
|
-
|
|
679
|
-
if (/\btool\.execute\.before\b/i.test(docs) && !/\b(bypass|gap|limitation|bug|5894|2319)\b/i.test(docs)) {
|
|
680
|
-
return false;
|
|
681
|
-
}
|
|
682
|
-
return true;
|
|
673
|
+
return !/\btool\.execute\.before\b[\s\S]{0,80}\b(subagent|mcp)\b[\s\S]{0,80}\b(bypass|gap|broken|2319|5894)\b/i.test(docs);
|
|
683
674
|
},
|
|
684
675
|
impact: 'high',
|
|
685
676
|
rating: 4,
|
|
686
677
|
category: 'plugins',
|
|
687
|
-
fix: '
|
|
678
|
+
fix: 'Remove blanket claims that subagent or MCP calls bypass plugin visibility. On current runtime, hook coverage was observed for direct, subagent, and MCP paths, so any caveat should be version-specific and evidence-backed.',
|
|
688
679
|
template: 'opencode-agents-md',
|
|
689
680
|
file: () => 'AGENTS.md',
|
|
690
681
|
line: () => null,
|
|
@@ -713,20 +704,17 @@ const OPENCODE_TECHNIQUES = {
|
|
|
713
704
|
|
|
714
705
|
opencodeToolInterceptionGap: {
|
|
715
706
|
id: 'OC-E02',
|
|
716
|
-
name: '
|
|
707
|
+
name: 'Security docs do not overstate plugin hook bypass gaps',
|
|
717
708
|
check: (ctx) => {
|
|
718
709
|
const pluginFiles = ctx.pluginFiles();
|
|
719
710
|
if (pluginFiles.length === 0) return null;
|
|
720
|
-
// Check if the project uses plugins and has documented the gap
|
|
721
711
|
const docs = docsBundle(ctx);
|
|
722
|
-
|
|
723
|
-
if (!usesToolHooks) return true;
|
|
724
|
-
return /\b(bypass|gap|limitation|5894|2319)\b/i.test(docs);
|
|
712
|
+
return !/\b(subagent|mcp)\b[\s\S]{0,80}\b(bypass|gap|broken|2319|5894)\b/i.test(docs);
|
|
725
713
|
},
|
|
726
714
|
impact: 'high',
|
|
727
715
|
rating: 4,
|
|
728
716
|
category: 'security',
|
|
729
|
-
fix: '
|
|
717
|
+
fix: 'Do not treat historical hook-gap bug reports as a current security guarantee. If you mention plugin coverage limits, mark them as version-sensitive and pair them with fresh runtime evidence.',
|
|
730
718
|
template: 'opencode-agents-md',
|
|
731
719
|
file: () => 'AGENTS.md',
|
|
732
720
|
line: () => null,
|
|
@@ -734,19 +722,19 @@ const OPENCODE_TECHNIQUES = {
|
|
|
734
722
|
|
|
735
723
|
opencodeAgentDenyNotBypassable: {
|
|
736
724
|
id: 'OC-E03',
|
|
737
|
-
name: 'Agent
|
|
725
|
+
name: 'Agent-permission docs do not rely on stale SDK bypass claims',
|
|
738
726
|
check: (ctx) => {
|
|
739
727
|
const agents = ctx.customAgents();
|
|
740
728
|
if (!agents || Object.keys(agents).length === 0) return null;
|
|
741
729
|
const docs = docsBundle(ctx);
|
|
742
730
|
const usesAgentPerms = Object.values(agents).some(a => a && a.permissions);
|
|
743
731
|
if (!usesAgentPerms) return true;
|
|
744
|
-
return
|
|
732
|
+
return !/\b6396\b|\bagent\b[\s\S]{0,80}\b(bypass|gap|broken)\b/i.test(docs);
|
|
745
733
|
},
|
|
746
734
|
impact: 'high',
|
|
747
735
|
rating: 4,
|
|
748
736
|
category: 'security',
|
|
749
|
-
fix: '
|
|
737
|
+
fix: 'Remove blanket claims that agent deny permissions are bypassed via SDK unless you have fresh version-specific proof. The older `#6396` framing did not reproduce in the current CLI harness.',
|
|
750
738
|
template: 'opencode-agents-md',
|
|
751
739
|
file: () => 'AGENTS.md',
|
|
752
740
|
line: () => null,
|
|
@@ -781,7 +769,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
781
769
|
impact: 'critical',
|
|
782
770
|
rating: 5,
|
|
783
771
|
category: 'security',
|
|
784
|
-
fix: '
|
|
772
|
+
fix: 'Do not hardcode secrets in `opencode.json`, and do not assume `{env:VAR}` keeps values invisible. Current runtime exposed resolved env substitutions in `debug config`, so treat that surface as sensitive too.',
|
|
785
773
|
template: 'opencode-config',
|
|
786
774
|
file: (ctx) => configFileName(ctx),
|
|
787
775
|
line: (ctx) => {
|
|
@@ -821,9 +809,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
821
809
|
if (!mcp || Object.keys(mcp).length === 0) return null;
|
|
822
810
|
for (const [id, server] of Object.entries(mcp)) {
|
|
823
811
|
if (!server) continue;
|
|
824
|
-
|
|
825
|
-
if (server.command && !Array.isArray(server.command) && typeof server.command !== 'string') return false;
|
|
826
|
-
// env is the wrong key — should be environment
|
|
812
|
+
if (server.command && !Array.isArray(server.command)) return false;
|
|
827
813
|
if (server.env && !server.environment) return false;
|
|
828
814
|
}
|
|
829
815
|
return true;
|
|
@@ -831,7 +817,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
831
817
|
impact: 'critical',
|
|
832
818
|
rating: 5,
|
|
833
819
|
category: 'mcp',
|
|
834
|
-
fix: 'Fix MCP config schema: use
|
|
820
|
+
fix: 'Fix MCP config schema: use `command` as a string array and `environment` as the env-var object. Current runtime rejected string commands and the legacy `env` key.',
|
|
835
821
|
template: 'opencode-config',
|
|
836
822
|
file: (ctx) => configFileName(ctx),
|
|
837
823
|
line: () => null,
|
|
@@ -843,17 +829,13 @@ const OPENCODE_TECHNIQUES = {
|
|
|
843
829
|
check: (ctx) => {
|
|
844
830
|
const mcp = ctx.mcpServers();
|
|
845
831
|
if (!mcp || Object.keys(mcp).length === 0) return null;
|
|
846
|
-
|
|
847
|
-
const perms = ctx.toolPermissions();
|
|
848
|
-
if (!perms) return null;
|
|
849
|
-
// Look for MCP-related tool permission patterns
|
|
850
|
-
const hasMcpToolRestrictions = Object.keys(perms).some(key => key.includes('mcp') || key.includes('*'));
|
|
832
|
+
const hasMcpToolRestrictions = Object.values(mcp).some((server) => server && server.tools && Object.keys(server.tools).length > 0);
|
|
851
833
|
return hasMcpToolRestrictions || Object.keys(mcp).length <= 2;
|
|
852
834
|
},
|
|
853
835
|
impact: 'high',
|
|
854
836
|
rating: 4,
|
|
855
837
|
category: 'mcp',
|
|
856
|
-
fix: 'Add tool
|
|
838
|
+
fix: 'Add MCP tool restrictions with per-tool globs such as `{ "tools": { "my-mcp*": false } }`. This limits only those MCP tools; other available tools like `webfetch` may still satisfy the same intent unless you restrict them too.',
|
|
857
839
|
template: 'opencode-config',
|
|
858
840
|
file: (ctx) => configFileName(ctx),
|
|
859
841
|
line: () => null,
|
|
@@ -886,19 +868,19 @@ const OPENCODE_TECHNIQUES = {
|
|
|
886
868
|
|
|
887
869
|
opencodeMcpHookLimitation: {
|
|
888
870
|
id: 'OC-F04',
|
|
889
|
-
name: 'MCP
|
|
871
|
+
name: 'MCP hook caveats are treated as version-sensitive',
|
|
890
872
|
check: (ctx) => {
|
|
891
873
|
const mcp = ctx.mcpServers();
|
|
892
874
|
const pluginFiles = ctx.pluginFiles();
|
|
893
875
|
if (!mcp || Object.keys(mcp).length === 0) return null;
|
|
894
876
|
if (pluginFiles.length === 0) return null;
|
|
895
877
|
const docs = docsBundle(ctx);
|
|
896
|
-
return
|
|
878
|
+
return !/\bmcp\b[\s\S]{0,80}\b(hook|plugin)\b[\s\S]{0,80}\b(bypass|gap|broken|2319)\b/i.test(docs);
|
|
897
879
|
},
|
|
898
880
|
impact: 'medium',
|
|
899
881
|
rating: 3,
|
|
900
882
|
category: 'mcp',
|
|
901
|
-
fix: '
|
|
883
|
+
fix: 'Do not hard-code an MCP hook-bypass warning as if it were universal. Current runtime showed MCP plugin events firing, so keep any caveat version-sensitive and backed by fresh evidence.',
|
|
902
884
|
template: 'opencode-agents-md',
|
|
903
885
|
file: () => 'AGENTS.md',
|
|
904
886
|
line: () => null,
|
|
@@ -937,18 +919,17 @@ const OPENCODE_TECHNIQUES = {
|
|
|
937
919
|
|
|
938
920
|
opencodeCiPermissionsPreset: {
|
|
939
921
|
id: 'OC-G01',
|
|
940
|
-
name: 'opencode run usage pre-configures
|
|
922
|
+
name: 'opencode run usage pre-configures permissions to avoid silent auto-rejects',
|
|
941
923
|
check: (ctx) => {
|
|
942
924
|
const workflows = workflowArtifacts(ctx);
|
|
943
925
|
const hasOpencodeRun = workflows.some(w => /\bopencode\s+run\b/i.test(w.content));
|
|
944
926
|
if (!hasOpencodeRun) return null;
|
|
945
|
-
// Check that permissions are pre-configured
|
|
946
927
|
return workflows.some(w => /\bpermissions?\b.*\ballow\b|\b--yes\b|\b--no-prompt\b/i.test(w.content));
|
|
947
928
|
},
|
|
948
929
|
impact: 'critical',
|
|
949
930
|
rating: 5,
|
|
950
931
|
category: 'ci',
|
|
951
|
-
fix: 'Pre-configure
|
|
932
|
+
fix: 'Pre-configure permissions when using `opencode run` in CI. In the current harness, permission requests auto-rejected instead of hanging, which still breaks tasks that expected tool access.',
|
|
952
933
|
template: 'opencode-ci',
|
|
953
934
|
file: () => '.github/workflows/',
|
|
954
935
|
line: () => null,
|
|
@@ -984,7 +965,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
984
965
|
impact: 'medium',
|
|
985
966
|
rating: 3,
|
|
986
967
|
category: 'ci',
|
|
987
|
-
fix: 'Use `--format json` when running OpenCode in CI
|
|
968
|
+
fix: 'Use `--format json` when running OpenCode in CI, and parse it as JSONL/event frames rather than expecting one monolithic JSON document.',
|
|
988
969
|
template: 'opencode-ci',
|
|
989
970
|
file: () => '.github/workflows/',
|
|
990
971
|
line: () => null,
|
|
@@ -1036,19 +1017,16 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1036
1017
|
|
|
1037
1018
|
opencodeNoDeprecatedPatterns: {
|
|
1038
1019
|
id: 'OC-H02',
|
|
1039
|
-
name: '
|
|
1020
|
+
name: 'Repo docs do not push the stale mode -> agent migration claim',
|
|
1040
1021
|
check: (ctx) => {
|
|
1041
|
-
const
|
|
1042
|
-
if (!
|
|
1043
|
-
|
|
1044
|
-
if (config.data[key] !== undefined) return false;
|
|
1045
|
-
}
|
|
1046
|
-
return true;
|
|
1022
|
+
const docs = docsBundle(ctx);
|
|
1023
|
+
if (!docs.trim()) return null;
|
|
1024
|
+
return !/\bmode\b[\s\S]{0,60}\bdeprecated\b|\buse\b[\s\S]{0,40}\bagent\b[\s\S]{0,40}\binstead of\b[\s\S]{0,20}\bmode\b/i.test(docs);
|
|
1047
1025
|
},
|
|
1048
1026
|
impact: 'medium',
|
|
1049
1027
|
rating: 3,
|
|
1050
1028
|
category: 'quality-deep',
|
|
1051
|
-
fix: '
|
|
1029
|
+
fix: 'Do not tell users that `mode` has been replaced by `agent` across the board. Current runtime still validated `mode` for markdown custom agents, so any migration guidance should be explicitly version-scoped.',
|
|
1052
1030
|
template: 'opencode-config',
|
|
1053
1031
|
file: (ctx) => configFileName(ctx),
|
|
1054
1032
|
line: () => null,
|
|
@@ -1162,16 +1140,16 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1162
1140
|
|
|
1163
1141
|
opencodeSkillKebabCase: {
|
|
1164
1142
|
id: 'OC-I03',
|
|
1165
|
-
name: 'Skill names use kebab-case
|
|
1143
|
+
name: 'Skill names preferably use kebab-case',
|
|
1166
1144
|
check: (ctx) => {
|
|
1167
1145
|
const skillDirs = ctx.skillDirs();
|
|
1168
1146
|
if (skillDirs.length === 0) return null;
|
|
1169
1147
|
return skillDirs.every(name => /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name));
|
|
1170
1148
|
},
|
|
1171
|
-
impact: '
|
|
1172
|
-
rating:
|
|
1149
|
+
impact: 'medium',
|
|
1150
|
+
rating: 2,
|
|
1173
1151
|
category: 'skills',
|
|
1174
|
-
fix: '
|
|
1152
|
+
fix: 'Prefer kebab-case for skill names, but treat it as a style recommendation rather than a hard runtime requirement. Current runtime still discovered underscore-based names.',
|
|
1175
1153
|
template: 'opencode-skills',
|
|
1176
1154
|
file: () => '.opencode/commands/',
|
|
1177
1155
|
line: () => null,
|
|
@@ -1201,20 +1179,17 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1201
1179
|
|
|
1202
1180
|
opencodeSkillCompatPaths: {
|
|
1203
1181
|
id: 'OC-I05',
|
|
1204
|
-
name: '
|
|
1182
|
+
name: 'OpenCode skill discovery accepts either .opencode/commands or .claude/skills',
|
|
1205
1183
|
check: (ctx) => {
|
|
1206
1184
|
const hasClaudeSkills = ctx.hasDir('.claude/skills');
|
|
1207
|
-
if (!hasClaudeSkills) return null;
|
|
1208
1185
|
const hasOpencodeCommands = ctx.hasDir('.opencode/commands');
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
// Claude skills exist but no OpenCode commands — warn
|
|
1212
|
-
return false;
|
|
1186
|
+
if (!hasClaudeSkills && !hasOpencodeCommands) return null;
|
|
1187
|
+
return hasClaudeSkills || hasOpencodeCommands;
|
|
1213
1188
|
},
|
|
1214
1189
|
impact: 'medium',
|
|
1215
1190
|
rating: 3,
|
|
1216
1191
|
category: 'skills',
|
|
1217
|
-
fix: '
|
|
1192
|
+
fix: 'Use `.opencode/commands/` for native OpenCode skills when you need them, but do not require a duplicate tree just to mirror `.claude/skills/`. Current runtime discovered `.claude/skills/` compatibility successfully.',
|
|
1218
1193
|
template: 'opencode-skills',
|
|
1219
1194
|
file: () => '.opencode/commands/',
|
|
1220
1195
|
line: () => null,
|
|
@@ -1247,21 +1222,22 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1247
1222
|
|
|
1248
1223
|
opencodeAgentModeValid: {
|
|
1249
1224
|
id: 'OC-J02',
|
|
1250
|
-
name: '
|
|
1225
|
+
name: 'Custom agent mode is valid when declared',
|
|
1251
1226
|
check: (ctx) => {
|
|
1252
1227
|
const agents = ctx.customAgents();
|
|
1253
1228
|
if (!agents || Object.keys(agents).length === 0) return null;
|
|
1254
1229
|
const validModes = new Set(['primary', 'subagent', 'all']);
|
|
1255
1230
|
for (const [name, agent] of Object.entries(agents)) {
|
|
1256
1231
|
if (!agent) continue;
|
|
1257
|
-
|
|
1232
|
+
const mode = agent.mode || agent.agent;
|
|
1233
|
+
if (mode && !validModes.has(mode)) return false;
|
|
1258
1234
|
}
|
|
1259
1235
|
return true;
|
|
1260
1236
|
},
|
|
1261
1237
|
impact: 'medium',
|
|
1262
1238
|
rating: 3,
|
|
1263
1239
|
category: 'agents',
|
|
1264
|
-
fix: '
|
|
1240
|
+
fix: 'Use a valid mode value (`primary`, `subagent`, or `all`) when declaring custom agents. Current runtime still validated `mode` for markdown agents, so do not rename to `agent` solely because of stale docs.',
|
|
1265
1241
|
template: 'opencode-config',
|
|
1266
1242
|
file: (ctx) => configFileName(ctx),
|
|
1267
1243
|
line: () => null,
|
|
@@ -1269,20 +1245,20 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1269
1245
|
|
|
1270
1246
|
opencodeBuiltinAgentsProtected: {
|
|
1271
1247
|
id: 'OC-J03',
|
|
1272
|
-
name: 'Built-in
|
|
1248
|
+
name: 'Built-in agent overrides are intentional and documented',
|
|
1273
1249
|
check: (ctx) => {
|
|
1274
1250
|
const agents = ctx.customAgents();
|
|
1275
1251
|
if (!agents || Object.keys(agents).length === 0) return null;
|
|
1276
1252
|
const builtins = new Set(['build', 'plan', 'default']);
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
return
|
|
1253
|
+
const overriding = Object.keys(agents).filter((name) => builtins.has(name.toLowerCase()));
|
|
1254
|
+
if (overriding.length === 0) return true;
|
|
1255
|
+
const docs = docsBundle(ctx);
|
|
1256
|
+
return /override|intentional|customized|replace/i.test(docs);
|
|
1281
1257
|
},
|
|
1282
1258
|
impact: 'medium',
|
|
1283
1259
|
rating: 3,
|
|
1284
1260
|
category: 'agents',
|
|
1285
|
-
fix: '
|
|
1261
|
+
fix: 'Built-in agents appear overrideable in current runtime. If you intentionally override `build`, `plan`, or `default`, document why; otherwise rename the custom agent to avoid surprising behavior.',
|
|
1286
1262
|
template: 'opencode-config',
|
|
1287
1263
|
file: (ctx) => configFileName(ctx),
|
|
1288
1264
|
line: () => null,
|
|
@@ -1389,7 +1365,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1389
1365
|
impact: 'medium',
|
|
1390
1366
|
rating: 3,
|
|
1391
1367
|
category: 'tui',
|
|
1392
|
-
fix: 'Fix JSONC syntax in tui.json.
|
|
1368
|
+
fix: 'Fix JSONC syntax in `tui.json`, then validate the behavioral effect in the real TUI/UI. Headless CLI surfaces did not provide enough evidence for TUI behavior on their own.',
|
|
1393
1369
|
template: 'opencode-config',
|
|
1394
1370
|
file: () => 'tui.json',
|
|
1395
1371
|
line: () => 1,
|
|
@@ -1415,7 +1391,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1415
1391
|
impact: 'low',
|
|
1416
1392
|
rating: 2,
|
|
1417
1393
|
category: 'tui',
|
|
1418
|
-
fix: 'Fix JSON syntax errors in theme
|
|
1394
|
+
fix: 'Fix JSON syntax errors in `.opencode/themes/`, then verify the theme in an actual UI/TUI session. Headless `run` did not give reliable theme evidence.',
|
|
1419
1395
|
template: 'opencode-config',
|
|
1420
1396
|
file: () => '.opencode/themes/',
|
|
1421
1397
|
line: () => null,
|
|
@@ -1432,7 +1408,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1432
1408
|
impact: 'medium',
|
|
1433
1409
|
rating: 3,
|
|
1434
1410
|
category: 'tui',
|
|
1435
|
-
fix: 'Remove any sensitive data from tui.json.',
|
|
1411
|
+
fix: 'Remove any sensitive data from `tui.json`, and remember that `tui.json` was not meaningfully observable through headless `run` alone.',
|
|
1436
1412
|
template: 'opencode-config',
|
|
1437
1413
|
file: () => 'tui.json',
|
|
1438
1414
|
line: (ctx) => {
|
|
@@ -1520,17 +1496,16 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1520
1496
|
id: 'OC-N02',
|
|
1521
1497
|
name: 'Config references current OpenCode features (no removed or renamed keys)',
|
|
1522
1498
|
check: (ctx) => {
|
|
1523
|
-
const
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
return true;
|
|
1499
|
+
const docs = docsBundle(ctx);
|
|
1500
|
+
const config = ctx.configContent();
|
|
1501
|
+
if (!docs.trim() && !config) return null;
|
|
1502
|
+
const combined = `${docs}\n${config || ''}`;
|
|
1503
|
+
return !/\bconfig\.json\b|\.well-known\/opencode|mode\s*->\s*agent|CLAUDE\.md fallback/i.test(combined);
|
|
1529
1504
|
},
|
|
1530
1505
|
impact: 'medium',
|
|
1531
1506
|
rating: 3,
|
|
1532
1507
|
category: 'release-freshness',
|
|
1533
|
-
fix: 'Update
|
|
1508
|
+
fix: 'Update stale OpenCode references. Use `opencode.json`/`opencode.jsonc`, keep `mode` guidance version-scoped, and treat `.well-known/opencode` plus `CLAUDE.md` fallback claims as unvalidated until you have fresh runtime proof.',
|
|
1534
1509
|
template: 'opencode-config',
|
|
1535
1510
|
file: (ctx) => configFileName(ctx),
|
|
1536
1511
|
line: () => null,
|
|
@@ -1596,15 +1571,13 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1596
1571
|
|
|
1597
1572
|
opencodeInstructionsArrayResolvable: {
|
|
1598
1573
|
id: 'OC-O02',
|
|
1599
|
-
name: 'instructions array
|
|
1574
|
+
name: 'instructions array uses validated local file paths',
|
|
1600
1575
|
check: (ctx) => {
|
|
1601
1576
|
const instructions = ctx.instructionsArray();
|
|
1602
1577
|
if (!Array.isArray(instructions) || instructions.length === 0) return null;
|
|
1603
1578
|
for (const instruction of instructions) {
|
|
1604
1579
|
if (typeof instruction !== 'string') continue;
|
|
1605
|
-
|
|
1606
|
-
if (instruction.startsWith('http') || instruction.includes('*')) continue;
|
|
1607
|
-
// Check local file references
|
|
1580
|
+
if (instruction.startsWith('http') || instruction.includes('*')) return false;
|
|
1608
1581
|
if (!ctx.fileContent(instruction)) return false;
|
|
1609
1582
|
}
|
|
1610
1583
|
return true;
|
|
@@ -1612,7 +1585,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1612
1585
|
impact: 'high',
|
|
1613
1586
|
rating: 4,
|
|
1614
1587
|
category: 'mixed-agent',
|
|
1615
|
-
fix: '
|
|
1588
|
+
fix: 'Prefer direct local file paths in the `instructions` array. Current runtime clearly validated direct files, but glob and URL sources were not visibly applied in `run`, so treat them as experimental until reproduced.',
|
|
1616
1589
|
template: 'opencode-config',
|
|
1617
1590
|
file: (ctx) => configFileName(ctx),
|
|
1618
1591
|
line: () => null,
|
|
@@ -1620,18 +1593,16 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1620
1593
|
|
|
1621
1594
|
opencodeGlobalAgentsNoConflict: {
|
|
1622
1595
|
id: 'OC-O03',
|
|
1623
|
-
name: '
|
|
1596
|
+
name: 'Project docs do not depend on global AGENTS.md behavior',
|
|
1624
1597
|
check: (ctx) => {
|
|
1625
|
-
const
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
// Basic conflict check: same heading structure with different content
|
|
1629
|
-
return true; // Soft pass; deep-review does thorough analysis
|
|
1598
|
+
const docs = `${ctx.fileContent('AGENTS.md') || ''}\n${ctx.fileContent('README.md') || ''}`;
|
|
1599
|
+
if (!docs.trim()) return null;
|
|
1600
|
+
return !/~\/\.config\/opencode\/AGENTS\.md|global AGENTS/i.test(docs);
|
|
1630
1601
|
},
|
|
1631
1602
|
impact: 'medium',
|
|
1632
1603
|
rating: 3,
|
|
1633
1604
|
category: 'mixed-agent',
|
|
1634
|
-
fix: '
|
|
1605
|
+
fix: 'Do not rely on `~/.config/opencode/AGENTS.md` as a guaranteed project behavior. Current Windows runtime did not show global AGENTS loading in `run`, so keep project-critical guidance in repo files.',
|
|
1635
1606
|
template: 'opencode-agents-md',
|
|
1636
1607
|
file: () => 'AGENTS.md',
|
|
1637
1608
|
line: () => null,
|
|
@@ -1643,7 +1614,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1643
1614
|
|
|
1644
1615
|
opencodeConfigMergeConsistent: {
|
|
1645
1616
|
id: 'OC-P01',
|
|
1646
|
-
name: '
|
|
1617
|
+
name: 'Observed config merge hierarchy does not produce conflicting values',
|
|
1647
1618
|
check: (ctx) => {
|
|
1648
1619
|
const projectConfig = ctx.configJson();
|
|
1649
1620
|
const globalConfig = ctx.globalConfigJson();
|
|
@@ -1658,7 +1629,7 @@ const OPENCODE_TECHNIQUES = {
|
|
|
1658
1629
|
impact: 'high',
|
|
1659
1630
|
rating: 4,
|
|
1660
1631
|
category: 'propagation',
|
|
1661
|
-
fix: 'Review
|
|
1632
|
+
fix: 'Review the observed precedence chain: global `opencode.json` < `OPENCODE_CONFIG` < project `opencode.json` < `.opencode/opencode.json` < `OPENCODE_CONFIG_CONTENT`. Treat `.well-known/opencode` as remote-only until you have runtime proof.',
|
|
1662
1633
|
template: 'opencode-config',
|
|
1663
1634
|
file: (ctx) => configFileName(ctx),
|
|
1664
1635
|
line: () => null,
|