opencode-agenthub 0.1.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/LICENSE +21 -0
- package/README.md +373 -0
- package/dist/composer/bootstrap.js +493 -0
- package/dist/composer/builtin-assets.js +139 -0
- package/dist/composer/capabilities.js +20 -0
- package/dist/composer/compose.js +824 -0
- package/dist/composer/defaults.js +10 -0
- package/dist/composer/home-transfer.js +288 -0
- package/dist/composer/install-home.js +5 -0
- package/dist/composer/library/README.md +93 -0
- package/dist/composer/library/bundles/auto.json +18 -0
- package/dist/composer/library/bundles/build.json +17 -0
- package/dist/composer/library/bundles/hr-adapter.json +26 -0
- package/dist/composer/library/bundles/hr-cto.json +24 -0
- package/dist/composer/library/bundles/hr-evaluator.json +26 -0
- package/dist/composer/library/bundles/hr-planner.json +26 -0
- package/dist/composer/library/bundles/hr-sourcer.json +24 -0
- package/dist/composer/library/bundles/hr-verifier.json +26 -0
- package/dist/composer/library/bundles/hr.json +35 -0
- package/dist/composer/library/bundles/plan.json +19 -0
- package/dist/composer/library/instructions/hr-boundaries.md +38 -0
- package/dist/composer/library/instructions/hr-protocol.md +102 -0
- package/dist/composer/library/profiles/auto.json +9 -0
- package/dist/composer/library/profiles/hr.json +9 -0
- package/dist/composer/library/souls/auto.md +29 -0
- package/dist/composer/library/souls/build.md +21 -0
- package/dist/composer/library/souls/hr-adapter.md +64 -0
- package/dist/composer/library/souls/hr-cto.md +57 -0
- package/dist/composer/library/souls/hr-evaluator.md +64 -0
- package/dist/composer/library/souls/hr-planner.md +48 -0
- package/dist/composer/library/souls/hr-sourcer.md +70 -0
- package/dist/composer/library/souls/hr-verifier.md +62 -0
- package/dist/composer/library/souls/hr.md +186 -0
- package/dist/composer/library/souls/plan.md +23 -0
- package/dist/composer/library/workflow/auto-mode.json +139 -0
- package/dist/composer/model-utils.js +39 -0
- package/dist/composer/opencode-profile.js +2299 -0
- package/dist/composer/package-manager.js +75 -0
- package/dist/composer/package-version.js +20 -0
- package/dist/composer/platform.js +48 -0
- package/dist/composer/query.js +133 -0
- package/dist/composer/settings.js +400 -0
- package/dist/plugins/opencode-agenthub.js +310 -0
- package/dist/plugins/opencode-question.js +223 -0
- package/dist/plugins/plan-guidance.js +263 -0
- package/dist/plugins/runtime-config.js +57 -0
- package/dist/skills/agenthub-doctor/SKILL.md +238 -0
- package/dist/skills/agenthub-doctor/diagnose.js +213 -0
- package/dist/skills/agenthub-doctor/fix.js +293 -0
- package/dist/skills/agenthub-doctor/index.js +30 -0
- package/dist/skills/agenthub-doctor/interactive.js +756 -0
- package/dist/skills/hr-assembly/SKILL.md +121 -0
- package/dist/skills/hr-final-check/SKILL.md +98 -0
- package/dist/skills/hr-review/SKILL.md +100 -0
- package/dist/skills/hr-staffing/SKILL.md +85 -0
- package/dist/skills/hr-support/bin/sync_sources.py +560 -0
- package/dist/skills/hr-support/bin/validate_staged_package.py +290 -0
- package/dist/skills/hr-support/bin/vendor_stage_mcps.py +234 -0
- package/dist/skills/hr-support/bin/vendor_stage_skills.py +104 -0
- package/dist/types.js +11 -0
- package/package.json +54 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
const defaultPlanReminderLines = (confidence = "high") => [
|
|
2
|
+
"PLAN_INJECTION_TEST_ACTIVE",
|
|
3
|
+
"Keep the user's current task, constraints, and repo context as the source of truth.",
|
|
4
|
+
"Do not restart the task or replace the answer format.",
|
|
5
|
+
"Include exactly one short line: workflow-received",
|
|
6
|
+
...confidence === "medium" ? ["- If this was not intended as a planning turn, ignore this reminder."] : []
|
|
7
|
+
];
|
|
8
|
+
const DEFAULT_SCAN_LINE_LIMIT = 5;
|
|
9
|
+
const DEFAULT_SCAN_CHAR_LIMIT = 800;
|
|
10
|
+
const CLASSIFICATION_PLAN_RE = /(?:^|\n)\s*(?:#{1,6}\s*)?(?:\*\*)?Classification(?:\s*:|:)(?:\*\*)?\s*(?:\[\s*Plan\s*\]|Plan)(?=[\s:;,.!?-]|$)/mi;
|
|
11
|
+
const I_DETECT_PLAN_RE = /(?:^|\n)\s*I\s+detect\s+(?:\[\s*Plan\s*\]|Plan)(?=[\s:;,.!?-]|$)/i;
|
|
12
|
+
const buildScanWindow = (text, lineLimit = DEFAULT_SCAN_LINE_LIMIT, charLimit = DEFAULT_SCAN_CHAR_LIMIT) => {
|
|
13
|
+
const lines = text.split("\n");
|
|
14
|
+
const selected = [];
|
|
15
|
+
let inFence = false;
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
const trimmed = line.trim();
|
|
18
|
+
if (trimmed.startsWith("```")) {
|
|
19
|
+
inFence = !inFence;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (inFence) continue;
|
|
23
|
+
selected.push(line);
|
|
24
|
+
if (selected.length >= lineLimit) break;
|
|
25
|
+
}
|
|
26
|
+
return selected.join("\n").slice(0, charLimit);
|
|
27
|
+
};
|
|
28
|
+
const confidenceRank = {
|
|
29
|
+
none: 0,
|
|
30
|
+
medium: 1,
|
|
31
|
+
high: 2
|
|
32
|
+
};
|
|
33
|
+
const getTriggerConfidence = (trigger) => trigger.confidence ?? "high";
|
|
34
|
+
const matchTrigger = (scanWindow, trigger) => {
|
|
35
|
+
if (!trigger.value) return -1;
|
|
36
|
+
if (trigger.type === "regex") {
|
|
37
|
+
try {
|
|
38
|
+
const flags = trigger.caseSensitive ? "m" : "im";
|
|
39
|
+
const regex = new RegExp(trigger.value, flags);
|
|
40
|
+
const match = regex.exec(scanWindow);
|
|
41
|
+
return match?.index ?? -1;
|
|
42
|
+
} catch {
|
|
43
|
+
return -1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const haystack = trigger.caseSensitive ? scanWindow : scanWindow.toLowerCase();
|
|
47
|
+
const needle = trigger.caseSensitive ? trigger.value : trigger.value.toLowerCase();
|
|
48
|
+
return haystack.indexOf(needle);
|
|
49
|
+
};
|
|
50
|
+
const findBestRuleMatch = (scanWindow, rule) => {
|
|
51
|
+
const activeTriggers = rule.triggers.filter(
|
|
52
|
+
(trigger) => typeof trigger.value === "string" && trigger.value.trim().length > 0
|
|
53
|
+
);
|
|
54
|
+
if (activeTriggers.length === 0) {
|
|
55
|
+
return { detected: false, confidence: "none", ruleId: null, matchOffset: -1 };
|
|
56
|
+
}
|
|
57
|
+
const strategy = rule.match ?? "any";
|
|
58
|
+
const matches = activeTriggers.map((trigger) => ({
|
|
59
|
+
trigger,
|
|
60
|
+
index: matchTrigger(scanWindow, trigger)
|
|
61
|
+
}));
|
|
62
|
+
if (strategy === "all" && matches.some((match) => match.index === -1)) {
|
|
63
|
+
return { detected: false, confidence: "none", ruleId: null, matchOffset: -1 };
|
|
64
|
+
}
|
|
65
|
+
const matched = matches.filter((match) => match.index !== -1);
|
|
66
|
+
if (matched.length === 0) {
|
|
67
|
+
return { detected: false, confidence: "none", ruleId: null, matchOffset: -1 };
|
|
68
|
+
}
|
|
69
|
+
const strongest = matched.reduce((best, current) => {
|
|
70
|
+
const currentConfidence = getTriggerConfidence(current.trigger);
|
|
71
|
+
const bestConfidence = getTriggerConfidence(best.trigger);
|
|
72
|
+
if (confidenceRank[currentConfidence] > confidenceRank[bestConfidence]) {
|
|
73
|
+
return current;
|
|
74
|
+
}
|
|
75
|
+
if (confidenceRank[currentConfidence] === confidenceRank[bestConfidence] && current.index < best.index) {
|
|
76
|
+
return current;
|
|
77
|
+
}
|
|
78
|
+
return best;
|
|
79
|
+
});
|
|
80
|
+
const firstOffset = matched.reduce(
|
|
81
|
+
(offset, current) => Math.min(offset, current.index),
|
|
82
|
+
matched[0]?.index ?? -1
|
|
83
|
+
);
|
|
84
|
+
return {
|
|
85
|
+
detected: true,
|
|
86
|
+
confidence: getTriggerConfidence(strongest.trigger),
|
|
87
|
+
ruleId: rule.id,
|
|
88
|
+
matchOffset: firstOffset
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
const workflowInjectionFromPlanDetection = (config) => {
|
|
92
|
+
if (!config?.enabled) return void 0;
|
|
93
|
+
return {
|
|
94
|
+
enabled: true,
|
|
95
|
+
debugLog: config.debugLog,
|
|
96
|
+
queueVisibleReminder: config.queueVisibleReminder ?? config.userVisibleTrace ?? false,
|
|
97
|
+
queueVisibleReminderTemplate: config.queueVisibleReminderTemplate ?? config.userVisibleTraceTemplate,
|
|
98
|
+
scanLineLimit: config.scanLineLimit,
|
|
99
|
+
scanCharLimit: config.scanCharLimit,
|
|
100
|
+
maxInjectionsPerSession: config.maxInjectionsPerSession,
|
|
101
|
+
rules: [
|
|
102
|
+
{
|
|
103
|
+
id: "plan",
|
|
104
|
+
match: "any",
|
|
105
|
+
triggers: [
|
|
106
|
+
{
|
|
107
|
+
type: "regex",
|
|
108
|
+
value: "(?:^|\\n)\\s*(?:#{1,6}\\s*)?(?:\\*\\*)?Classification(?:\\s*:|:)(?:\\*\\*)?\\s*(?:\\[\\s*Plan\\s*\\]|Plan)(?=[\\s:;,.!?-]|$)",
|
|
109
|
+
confidence: "high"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
type: "regex",
|
|
113
|
+
value: "(?:^|\\n)\\s*I\\s+detect\\s+(?:\\[\\s*Plan\\s*\\]|Plan)(?=[\\s:;,.!?-]|$)",
|
|
114
|
+
confidence: "medium"
|
|
115
|
+
}
|
|
116
|
+
],
|
|
117
|
+
reminderTemplate: config.reminderTemplate?.trim() || [
|
|
118
|
+
"PLAN_INJECTION_TEST_ACTIVE",
|
|
119
|
+
"Keep the user's current task, constraints, and repo context as the source of truth.",
|
|
120
|
+
"Do not restart the task or replace the answer format.",
|
|
121
|
+
"Include exactly one short line: workflow-received"
|
|
122
|
+
].join("\n"),
|
|
123
|
+
queueVisibleReminderTemplate: config.queueVisibleReminderTemplate ?? config.userVisibleTraceTemplate
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
const getRule = (config, ruleId) => config?.rules.find((rule) => rule.id === ruleId);
|
|
129
|
+
const extractReminderLine = (reminderTemplate, prefix) => {
|
|
130
|
+
if (!reminderTemplate?.trim()) return null;
|
|
131
|
+
for (const line of reminderTemplate.split("\n")) {
|
|
132
|
+
const trimmed = line.trim();
|
|
133
|
+
if (prefix.test(trimmed)) return trimmed;
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
};
|
|
137
|
+
const detectWorkflowIntent = (text, config) => {
|
|
138
|
+
if (!config?.enabled || !text || text.length < 10) {
|
|
139
|
+
return { detected: false, confidence: "none", ruleId: null, matchOffset: -1 };
|
|
140
|
+
}
|
|
141
|
+
const scanWindow = buildScanWindow(
|
|
142
|
+
text,
|
|
143
|
+
config.scanLineLimit,
|
|
144
|
+
config.scanCharLimit
|
|
145
|
+
);
|
|
146
|
+
let best = {
|
|
147
|
+
detected: false,
|
|
148
|
+
confidence: "none",
|
|
149
|
+
ruleId: null,
|
|
150
|
+
matchOffset: -1
|
|
151
|
+
};
|
|
152
|
+
for (const rule of config.rules) {
|
|
153
|
+
if (rule.enabled === false) continue;
|
|
154
|
+
const signal = findBestRuleMatch(scanWindow, rule);
|
|
155
|
+
if (!signal.detected) continue;
|
|
156
|
+
if (confidenceRank[signal.confidence] > confidenceRank[best.confidence]) {
|
|
157
|
+
best = signal;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (confidenceRank[signal.confidence] === confidenceRank[best.confidence] && (best.matchOffset === -1 || signal.matchOffset < best.matchOffset)) {
|
|
161
|
+
best = signal;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return best;
|
|
165
|
+
};
|
|
166
|
+
const shouldInjectWorkflowGuidance = (signal, config) => signal.detected && !!config?.enabled;
|
|
167
|
+
const buildWorkflowReminder = (signal, config) => {
|
|
168
|
+
const rule = getRule(config, signal.ruleId);
|
|
169
|
+
const reminderBody = rule?.reminderTemplate?.trim() ? rule.reminderTemplate.trim() : [
|
|
170
|
+
"WORKFLOW_INJECTION_TEST_ACTIVE",
|
|
171
|
+
"Keep the user's current task, constraints, and repo context as the source of truth.",
|
|
172
|
+
"Do not restart the task or replace the answer format.",
|
|
173
|
+
"Include exactly one short line: workflow-received"
|
|
174
|
+
].join("\n");
|
|
175
|
+
return `<system-reminder>
|
|
176
|
+
${reminderBody}
|
|
177
|
+
</system-reminder>`;
|
|
178
|
+
};
|
|
179
|
+
const buildWorkflowTraceNotice = (signal, config) => {
|
|
180
|
+
const rule = getRule(config, signal.ruleId);
|
|
181
|
+
const traceBody = rule?.queueVisibleReminderTemplate?.trim() || rule?.reminderTemplate?.trim() || config?.queueVisibleReminderTemplate?.trim() || "[agenthub] Workflow reminder injected.";
|
|
182
|
+
const reportLine = extractReminderLine(rule?.reminderTemplate, /^0\.\s*Report:/i);
|
|
183
|
+
const firstStep = extractReminderLine(rule?.reminderTemplate, /^1\./i);
|
|
184
|
+
return [traceBody, reportLine, firstStep].filter((value) => !!value).join("\n");
|
|
185
|
+
};
|
|
186
|
+
const INTERNAL_INITIATOR_MARKER = "<!-- OMO_INTERNAL_INITIATOR -->";
|
|
187
|
+
const buildQueuedWorkflowNotice = (signal, config) => {
|
|
188
|
+
return `${buildWorkflowReminder(signal, config)}
|
|
189
|
+
${INTERNAL_INITIATOR_MARKER}`;
|
|
190
|
+
};
|
|
191
|
+
const detectPlanIntent = (text, config) => {
|
|
192
|
+
if (!text || text.length < 10) {
|
|
193
|
+
return { detected: false, confidence: "none", marker: null, matchOffset: -1 };
|
|
194
|
+
}
|
|
195
|
+
const scanWindow = buildScanWindow(
|
|
196
|
+
text,
|
|
197
|
+
config?.scanLineLimit,
|
|
198
|
+
config?.scanCharLimit
|
|
199
|
+
);
|
|
200
|
+
const classificationMatch = CLASSIFICATION_PLAN_RE.exec(scanWindow);
|
|
201
|
+
if (classificationMatch) {
|
|
202
|
+
return {
|
|
203
|
+
detected: true,
|
|
204
|
+
confidence: "high",
|
|
205
|
+
marker: "classification",
|
|
206
|
+
matchOffset: classificationMatch.index
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const detectMatch = I_DETECT_PLAN_RE.exec(scanWindow);
|
|
210
|
+
if (detectMatch) {
|
|
211
|
+
return {
|
|
212
|
+
detected: true,
|
|
213
|
+
confidence: "medium",
|
|
214
|
+
marker: "i-detect",
|
|
215
|
+
matchOffset: detectMatch.index
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return { detected: false, confidence: "none", marker: null, matchOffset: -1 };
|
|
219
|
+
};
|
|
220
|
+
const shouldInjectPlanGuidance = (signal, config) => {
|
|
221
|
+
if (!signal.detected) return false;
|
|
222
|
+
const threshold = config?.threshold ?? "medium";
|
|
223
|
+
if (threshold === "high") return signal.confidence === "high";
|
|
224
|
+
return signal.confidence === "high" || signal.confidence === "medium";
|
|
225
|
+
};
|
|
226
|
+
const buildPlanReminder = (signal, config) => {
|
|
227
|
+
const reminderBody = config?.reminderTemplate?.trim() ? config.reminderTemplate.trim() : defaultPlanReminderLines(signal.confidence).join("\n");
|
|
228
|
+
return `<system-reminder>
|
|
229
|
+
${reminderBody}
|
|
230
|
+
</system-reminder>`;
|
|
231
|
+
};
|
|
232
|
+
const buildPlanTraceNotice = (config) => {
|
|
233
|
+
const traceTemplate = config?.queueVisibleReminderTemplate ?? config?.userVisibleTraceTemplate;
|
|
234
|
+
const traceBody = traceTemplate?.trim() ? traceTemplate.trim() : config?.reminderTemplate?.trim() ? config.reminderTemplate.trim() : "[agenthub] Plan reminder injected.";
|
|
235
|
+
const reportLine = extractReminderLine(config?.reminderTemplate, /^0\.\s*Report:/i);
|
|
236
|
+
const firstStep = extractReminderLine(config?.reminderTemplate, /^1\./i);
|
|
237
|
+
return [
|
|
238
|
+
traceBody,
|
|
239
|
+
reportLine,
|
|
240
|
+
firstStep ?? "Follow a structured plan for the current task before continuing."
|
|
241
|
+
].filter((value) => !!value).join("\n");
|
|
242
|
+
};
|
|
243
|
+
const buildQueuedPlanNotice = (config, signal) => {
|
|
244
|
+
return `${buildPlanReminder(
|
|
245
|
+
signal ?? { detected: true, confidence: "high", marker: "classification", matchOffset: 0 },
|
|
246
|
+
config
|
|
247
|
+
)}
|
|
248
|
+
${INTERNAL_INITIATOR_MARKER}`;
|
|
249
|
+
};
|
|
250
|
+
export {
|
|
251
|
+
INTERNAL_INITIATOR_MARKER,
|
|
252
|
+
buildPlanReminder,
|
|
253
|
+
buildPlanTraceNotice,
|
|
254
|
+
buildQueuedPlanNotice,
|
|
255
|
+
buildQueuedWorkflowNotice,
|
|
256
|
+
buildWorkflowReminder,
|
|
257
|
+
buildWorkflowTraceNotice,
|
|
258
|
+
detectPlanIntent,
|
|
259
|
+
detectWorkflowIntent,
|
|
260
|
+
shouldInjectPlanGuidance,
|
|
261
|
+
shouldInjectWorkflowGuidance,
|
|
262
|
+
workflowInjectionFromPlanDetection
|
|
263
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const resolvePluginConfigRoot = (explicitRoot) => {
|
|
4
|
+
if (explicitRoot?.trim()) return path.resolve(explicitRoot);
|
|
5
|
+
const fromEnv = typeof process !== "undefined" ? process.env.OPENCODE_CONFIG_DIR : void 0;
|
|
6
|
+
if (fromEnv?.trim()) return path.resolve(fromEnv);
|
|
7
|
+
return path.resolve(".opencode");
|
|
8
|
+
};
|
|
9
|
+
const runtimeConfigPathForRoot = (configRoot) => path.join(configRoot, "agenthub-runtime.json");
|
|
10
|
+
const parseRuntimeJson = (content) => {
|
|
11
|
+
const jsonContent = content.split("\n").filter((line) => !line.trim().startsWith("//")).join("\n");
|
|
12
|
+
return JSON.parse(jsonContent);
|
|
13
|
+
};
|
|
14
|
+
const inspectRuntimeConfig = async (configRoot = resolvePluginConfigRoot()) => {
|
|
15
|
+
const runtimeConfigPath = runtimeConfigPathForRoot(configRoot);
|
|
16
|
+
try {
|
|
17
|
+
const content = await readFile(runtimeConfigPath, "utf-8");
|
|
18
|
+
return {
|
|
19
|
+
ok: true,
|
|
20
|
+
configRoot,
|
|
21
|
+
runtimeConfigPath,
|
|
22
|
+
config: parseRuntimeJson(content)
|
|
23
|
+
};
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
configRoot,
|
|
28
|
+
runtimeConfigPath,
|
|
29
|
+
error
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const summarizeRuntimeFeatureState = (config) => {
|
|
34
|
+
const allBlockedTools = /* @__PURE__ */ new Set();
|
|
35
|
+
if (config.globalBlockedTools) {
|
|
36
|
+
for (const tool of config.globalBlockedTools) {
|
|
37
|
+
allBlockedTools.add(tool);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const agentInfo of Object.values(config.agents)) {
|
|
41
|
+
for (const tool of agentInfo.blockedTools) {
|
|
42
|
+
allBlockedTools.add(tool);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
blockedTools: allBlockedTools,
|
|
47
|
+
planDetection: config.planDetection,
|
|
48
|
+
workflowInjection: config.workflowInjection
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
export {
|
|
52
|
+
inspectRuntimeConfig,
|
|
53
|
+
parseRuntimeJson,
|
|
54
|
+
resolvePluginConfigRoot,
|
|
55
|
+
runtimeConfigPathForRoot,
|
|
56
|
+
summarizeRuntimeFeatureState
|
|
57
|
+
};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Agent Hub Doctor Skill
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
You are an Agent Hub diagnostic and assembly agent. Your job is to:
|
|
6
|
+
1. **Diagnose** Agent Hub setup issues
|
|
7
|
+
2. **Explain** problems to users clearly
|
|
8
|
+
3. **Assemble** bundles and profiles interactively
|
|
9
|
+
|
|
10
|
+
## Interactive Assembly Flow
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
┌─────────────────────────────────────────┐
|
|
14
|
+
│ setup completes │
|
|
15
|
+
│ OR user runs: opencode-agenthub doctor │
|
|
16
|
+
└──────────────────┬──────────────────────┘
|
|
17
|
+
│
|
|
18
|
+
▼
|
|
19
|
+
┌────────────────────┐
|
|
20
|
+
│ Run diagnostics │
|
|
21
|
+
└────────┬───────────┘
|
|
22
|
+
│
|
|
23
|
+
▼
|
|
24
|
+
┌───────────────────────┐
|
|
25
|
+
│ Found orphaned souls? │
|
|
26
|
+
└─────┬──────────┬──────┘
|
|
27
|
+
│ No │ Yes
|
|
28
|
+
│ ▼
|
|
29
|
+
│ ┌─────────────────────────────────┐
|
|
30
|
+
│ │ "Found N souls without bundles: │
|
|
31
|
+
│ │ - soul-1, soul-2 │
|
|
32
|
+
│ │ │
|
|
33
|
+
│ │ Create bundles? [Y/n]" │
|
|
34
|
+
│ └────────┬────────────────────────┘
|
|
35
|
+
│ │ Yes
|
|
36
|
+
│ ▼
|
|
37
|
+
│ ┌─────────────────────────────────┐
|
|
38
|
+
│ │ FOR EACH SOUL: │
|
|
39
|
+
│ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━│
|
|
40
|
+
│ │ │
|
|
41
|
+
│ │ 1️⃣ Select skill sets (multi): │
|
|
42
|
+
│ │ □ core_reading │
|
|
43
|
+
│ │ □ core_editing │
|
|
44
|
+
│ │ ☑ full_dev (default) │
|
|
45
|
+
│ │ │
|
|
46
|
+
│ │ 2️⃣ Add dedicated skills: │
|
|
47
|
+
│ │ > my-skill, custom-skill │
|
|
48
|
+
│ │ (comma-separated or skip) │
|
|
49
|
+
│ │ │
|
|
50
|
+
│ │ 3️⃣ Select MCP servers: │
|
|
51
|
+
│ │ (Only if custom MCPs exist) │
|
|
52
|
+
│ │ □ my-mcp │
|
|
53
|
+
│ │ ☑ custom-mcp │
|
|
54
|
+
│ │ │
|
|
55
|
+
│ │ 4️⃣ Apply guards: │
|
|
56
|
+
│ │ □ read_only │
|
|
57
|
+
│ │ □ no_task │
|
|
58
|
+
│ │ ☑ none (default) │
|
|
59
|
+
│ │ │
|
|
60
|
+
│ │ ✓ Bundle created! │
|
|
61
|
+
│ └────────┬────────────────────────┘
|
|
62
|
+
│ │
|
|
63
|
+
│ ▼
|
|
64
|
+
│ ┌─────────────────────────────────┐
|
|
65
|
+
│ │ Create profile? [Y/n] │
|
|
66
|
+
│ │ │
|
|
67
|
+
│ │ Profile name: [imported] │
|
|
68
|
+
│ │ Bundles: soul-1, soul-2 │
|
|
69
|
+
│ │ Default agent: [soul-1] │
|
|
70
|
+
│ │ │
|
|
71
|
+
│ │ ✓ Profile created! │
|
|
72
|
+
│ └────────┬────────────────────────┘
|
|
73
|
+
│ │
|
|
74
|
+
├─────────────┘
|
|
75
|
+
▼
|
|
76
|
+
┌─────────────────────────┐
|
|
77
|
+
│ Missing guards? [Y/n] │
|
|
78
|
+
│ ✓ Guards added │
|
|
79
|
+
└────────┬────────────────┘
|
|
80
|
+
│
|
|
81
|
+
▼
|
|
82
|
+
┌─────────────────────────┐
|
|
83
|
+
│ ✅ Assembly complete! │
|
|
84
|
+
│ │
|
|
85
|
+
│ Next steps: │
|
|
86
|
+
│ cd /your/project │
|
|
87
|
+
│ opencode-agenthub run │
|
|
88
|
+
└─────────────────────────┘
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Key Principles
|
|
92
|
+
|
|
93
|
+
1. **No waste** - Only ask relevant questions
|
|
94
|
+
2. **Smart defaults** - full_dev skill set, no guards
|
|
95
|
+
3. **Smooth flow** - No interruptions, comma-separated inputs
|
|
96
|
+
4. **Clear feedback** - Show what was created
|
|
97
|
+
|
|
98
|
+
## Capabilities
|
|
99
|
+
|
|
100
|
+
### Diagnostic Functions
|
|
101
|
+
|
|
102
|
+
- `diagnoseGuards()` - Check if required guards exist in settings.json
|
|
103
|
+
- `diagnoseOrphanedAssets()` - Find souls/skills not referenced by any bundle
|
|
104
|
+
- `diagnoseProfiles()` - Check if profiles exist and are valid
|
|
105
|
+
- `diagnoseBundles()` - Validate bundle configurations
|
|
106
|
+
|
|
107
|
+
### Fix Functions
|
|
108
|
+
|
|
109
|
+
- `fixMissingGuards()` - Add default guards to settings.json
|
|
110
|
+
- `createBundleForSoul()` - Generate bundle for an orphaned soul
|
|
111
|
+
- `createProfile()` - Generate profile referencing bundles
|
|
112
|
+
- `validateAndFix()` - Comprehensive validation and repair
|
|
113
|
+
|
|
114
|
+
## Workflow
|
|
115
|
+
|
|
116
|
+
### 1. Initial Diagnosis
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const issues = await runDiagnostics(targetRoot);
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Report findings to user in this format:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
🔍 Agent Hub Diagnostics Report
|
|
126
|
+
|
|
127
|
+
✅ Healthy:
|
|
128
|
+
- Settings file exists
|
|
129
|
+
- 3 souls found
|
|
130
|
+
- 2 skills found
|
|
131
|
+
|
|
132
|
+
⚠️ Issues Found:
|
|
133
|
+
1. Missing guards: no_task, read_only
|
|
134
|
+
2. Orphaned souls: custom-agent, my-helper
|
|
135
|
+
3. No profiles found
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 2. Ask User for Consent
|
|
139
|
+
|
|
140
|
+
Before applying fixes, ask:
|
|
141
|
+
- "Fix missing guards?" (Recommended: Yes)
|
|
142
|
+
- "Create bundles for orphaned souls?" (List which souls)
|
|
143
|
+
- "Create a default profile?" (Explain what it includes)
|
|
144
|
+
|
|
145
|
+
### 3. Apply Fixes
|
|
146
|
+
|
|
147
|
+
Apply fixes one by one, reporting progress:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
✓ Added default guards to settings.json
|
|
151
|
+
✓ Created bundle for 'custom-agent'
|
|
152
|
+
✓ Created bundle for 'my-helper'
|
|
153
|
+
✓ Created profile 'imported' with 2 bundles
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 4. Verify
|
|
157
|
+
|
|
158
|
+
Run diagnostics again to confirm all issues resolved:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
✅ All issues resolved! Agent Hub is ready to use.
|
|
162
|
+
|
|
163
|
+
Next steps:
|
|
164
|
+
cd /your/project
|
|
165
|
+
opencode-agenthub run imported
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Guard Definitions
|
|
169
|
+
|
|
170
|
+
Default guards that should exist:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"read_only": {
|
|
175
|
+
"description": "Read-only access - no file modifications",
|
|
176
|
+
"permission": {
|
|
177
|
+
"edit": "deny",
|
|
178
|
+
"write": "deny",
|
|
179
|
+
"bash": "deny"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"no_task": {
|
|
183
|
+
"description": "Block task tool",
|
|
184
|
+
"blockedTools": ["task"],
|
|
185
|
+
"permission": {
|
|
186
|
+
"task": { "*": "deny" }
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Bundle Template
|
|
193
|
+
|
|
194
|
+
When creating bundles for orphaned souls:
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"name": "<soul-name>",
|
|
199
|
+
"runtime": "native",
|
|
200
|
+
"soul": "<soul-name>",
|
|
201
|
+
"skills": [],
|
|
202
|
+
"mcp": [],
|
|
203
|
+
"agent": {
|
|
204
|
+
"name": "<soul-name>",
|
|
205
|
+
"mode": "primary",
|
|
206
|
+
"model": "github-copilot/claude-sonnet-4.5",
|
|
207
|
+
"description": "Auto-generated bundle for imported soul"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Profile Template
|
|
213
|
+
|
|
214
|
+
When creating profiles:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"name": "imported",
|
|
219
|
+
"description": "Auto-generated profile for imported assets",
|
|
220
|
+
"bundles": ["<list-of-bundle-names>"],
|
|
221
|
+
"defaultAgent": "<first-bundle-agent-name>",
|
|
222
|
+
"plugins": ["opencode-agenthub"]
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Error Handling
|
|
227
|
+
|
|
228
|
+
- If settings.json doesn't exist, create it with defaults
|
|
229
|
+
- If a soul file is invalid, skip it and warn user
|
|
230
|
+
- If bundle/profile creation fails, roll back and explain why
|
|
231
|
+
- Never modify files without user consent
|
|
232
|
+
|
|
233
|
+
## Communication Style
|
|
234
|
+
|
|
235
|
+
- Be clear and concise
|
|
236
|
+
- Use emojis for visual clarity (✓, ⚠️, ✅, ❌)
|
|
237
|
+
- Explain technical terms in plain language
|
|
238
|
+
- Provide actionable next steps
|