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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +373 -0
  3. package/dist/composer/bootstrap.js +493 -0
  4. package/dist/composer/builtin-assets.js +139 -0
  5. package/dist/composer/capabilities.js +20 -0
  6. package/dist/composer/compose.js +824 -0
  7. package/dist/composer/defaults.js +10 -0
  8. package/dist/composer/home-transfer.js +288 -0
  9. package/dist/composer/install-home.js +5 -0
  10. package/dist/composer/library/README.md +93 -0
  11. package/dist/composer/library/bundles/auto.json +18 -0
  12. package/dist/composer/library/bundles/build.json +17 -0
  13. package/dist/composer/library/bundles/hr-adapter.json +26 -0
  14. package/dist/composer/library/bundles/hr-cto.json +24 -0
  15. package/dist/composer/library/bundles/hr-evaluator.json +26 -0
  16. package/dist/composer/library/bundles/hr-planner.json +26 -0
  17. package/dist/composer/library/bundles/hr-sourcer.json +24 -0
  18. package/dist/composer/library/bundles/hr-verifier.json +26 -0
  19. package/dist/composer/library/bundles/hr.json +35 -0
  20. package/dist/composer/library/bundles/plan.json +19 -0
  21. package/dist/composer/library/instructions/hr-boundaries.md +38 -0
  22. package/dist/composer/library/instructions/hr-protocol.md +102 -0
  23. package/dist/composer/library/profiles/auto.json +9 -0
  24. package/dist/composer/library/profiles/hr.json +9 -0
  25. package/dist/composer/library/souls/auto.md +29 -0
  26. package/dist/composer/library/souls/build.md +21 -0
  27. package/dist/composer/library/souls/hr-adapter.md +64 -0
  28. package/dist/composer/library/souls/hr-cto.md +57 -0
  29. package/dist/composer/library/souls/hr-evaluator.md +64 -0
  30. package/dist/composer/library/souls/hr-planner.md +48 -0
  31. package/dist/composer/library/souls/hr-sourcer.md +70 -0
  32. package/dist/composer/library/souls/hr-verifier.md +62 -0
  33. package/dist/composer/library/souls/hr.md +186 -0
  34. package/dist/composer/library/souls/plan.md +23 -0
  35. package/dist/composer/library/workflow/auto-mode.json +139 -0
  36. package/dist/composer/model-utils.js +39 -0
  37. package/dist/composer/opencode-profile.js +2299 -0
  38. package/dist/composer/package-manager.js +75 -0
  39. package/dist/composer/package-version.js +20 -0
  40. package/dist/composer/platform.js +48 -0
  41. package/dist/composer/query.js +133 -0
  42. package/dist/composer/settings.js +400 -0
  43. package/dist/plugins/opencode-agenthub.js +310 -0
  44. package/dist/plugins/opencode-question.js +223 -0
  45. package/dist/plugins/plan-guidance.js +263 -0
  46. package/dist/plugins/runtime-config.js +57 -0
  47. package/dist/skills/agenthub-doctor/SKILL.md +238 -0
  48. package/dist/skills/agenthub-doctor/diagnose.js +213 -0
  49. package/dist/skills/agenthub-doctor/fix.js +293 -0
  50. package/dist/skills/agenthub-doctor/index.js +30 -0
  51. package/dist/skills/agenthub-doctor/interactive.js +756 -0
  52. package/dist/skills/hr-assembly/SKILL.md +121 -0
  53. package/dist/skills/hr-final-check/SKILL.md +98 -0
  54. package/dist/skills/hr-review/SKILL.md +100 -0
  55. package/dist/skills/hr-staffing/SKILL.md +85 -0
  56. package/dist/skills/hr-support/bin/sync_sources.py +560 -0
  57. package/dist/skills/hr-support/bin/validate_staged_package.py +290 -0
  58. package/dist/skills/hr-support/bin/vendor_stage_mcps.py +234 -0
  59. package/dist/skills/hr-support/bin/vendor_stage_skills.py +104 -0
  60. package/dist/types.js +11 -0
  61. 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