6 phases, 13 plans, 21 requirements. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
28 KiB
Phase 29: Default Provider & End-to-End - Research
Researched: 2026-04-01 Domain: Onboarding UX, agent template configuration, GSD workflow compatibility Confidence: HIGH
Summary
Phase 29 closes the last gap in the v1.4 milestone: a fresh Nexus install that has Hermes + Ollama but no cloud provider (Claude Code, Anthropic API key, etc.) currently hits a wall at every entry point. The Nexus-owned onboarding wizard (NexusOnboardingWizard.tsx) hardcodes adapterType: "claude_local" for all three agents it creates. The NewAgentDialog.tsx template shortcuts also hardcode adapterType: "claude_local". Neither component has any logic to detect which adapters are locally available.
The Hermes adapter is well-integrated at the runtime level: it has supportsLocalAgentJwt: true (so PAPERCLIP_API_KEY is injected automatically), handles session persistence via --resume, and the heartbeat service already has a Hermes-specific stateJson merge path. However, there is one confirmed gap: syncHermesSkills is a no-op (it returns the skill snapshot but writes nothing to disk), and the Hermes execute.ts does not inject Nexus-managed skill content into the prompt. This means the PM/Engineer HEARTBEAT.md/TOOLS.md bundles are recorded in the DB but are not actually available to the Hermes agent at runtime.
The agent template files (HEARTBEAT.md, TOOLS.md, AGENTS.md) are curl-based and entirely adapter-neutral. The onboarding-assets work for any adapter that has API access — the only barrier is getting skill content into the Hermes context.
Phase 29 involves five targeted areas: (1) provider detection at onboarding; (2) adapter default in the Nexus wizard; (3) AGENT_TEMPLATES fix in NewAgentDialog; (4) skill injection for Hermes agents; and (5) end-to-end smoke test.
Primary recommendation: The Nexus onboarding wizard should probe hermes availability via a board-authenticated route, then silently pre-select hermes_local when hermes is found and no cloud provider is detected. For skill injection, the simplest fix is to include the Nexus skill bundle content inline in the Hermes prompt template via ctx.context.skills (the heartbeat service already populates this; the adapter just needs to use it).
<user_constraints>
User Constraints (from CONTEXT.md)
Locked Decisions
All implementation choices are at Claude's discretion — discuss phase was skipped per user setting. Use ROADMAP phase goal, success criteria, and codebase conventions to guide decisions.
Claude's Discretion
All implementation choices are at Claude's discretion.
Deferred Ideas (OUT OF SCOPE)
None — discuss phase skipped. </user_constraints>
<phase_requirements>
Phase Requirements
| ID | Description | Research Support |
|---|---|---|
| DFLT-01 | If no cloud provider (Claude Code, etc.) is detected, Hermes + Ollama is offered as default during onboarding | NexusOnboardingWizard.tsx hardcodes claude_local; needs provider detection + conditional adapter pre-selection |
| DFLT-02 | Default agent templates (PM, Engineer, Generalist) work correctly with Hermes runtime | AGENT_TEMPLATES in NewAgentDialog.tsx hardcodes claude_local; onboarding-assets adapter-agnostic but skill injection gap must be closed |
| DFLT-03 | GSD workflow functions correctly with Hermes as the agent runtime | Hermes has supportsLocalAgentJwt: true; skill sync is a no-op; need to inject Nexus skill content via prompt context |
| DFLT-04 | Fresh install with only Hermes + Ollama works end-to-end (no paid subscription or API key required) | All individual pieces exist; needs integration smoke test and skill injection gap closed |
| </phase_requirements> |
Standard Stack
Core
| Library | Version | Purpose | Why Standard |
|---|---|---|---|
| hermes-paperclip-adapter | 0.2.1 | Hermes adapter module | Already installed; provides testEnvironment, execute, detectModel |
| @tanstack/react-query | (project version) | Server state in onboarding wizard | All other queries in the onboarding wizard use this |
| React | (project version) | UI component model | Project standard |
Supporting
| Library | Version | Purpose | When to Use |
|---|---|---|---|
| agentsApi | (internal) | Frontend API client for adapter queries | testEnvironment, detectModel, adapterModels |
Installation: No new packages needed. hermes-paperclip-adapter@0.2.1 is already in the monorepo.
Architecture Patterns
Recommended Project Structure
Changes span these files:
ui/src/components/
├── NexusOnboardingWizard.tsx # Add provider detection + conditional hermes default
├── NewAgentDialog.tsx # Make AGENT_TEMPLATES adapter-neutral
server/src/routes/
└── agents.ts # Add board-auth probe route (no companyId)
server/src/
└── hermes-skill-inject.ts # (optional) skill injection helper
server/src/__tests__/
├── 29-default-provider.test.ts # Probe route + agent creation tests
└── 29-hermes-skill-inject.test.ts # Skill injection unit tests
Pattern 1: Provider Availability Detection at Onboarding
What: Before the Nexus onboarding wizard renders its form, fire a background probe to check if hermes is installed. If yes (and no cloud provider credential exists), pre-populate adapterType to hermes_local.
When to use: In NexusOnboardingWizard.tsx.
Implementation notes:
- The existing
testEnvironmentroute requires acompanyId(it lives under/companies/:companyId/adapters/:type/test-environment). At wizard load time there is no company yet. - Approach: Add a board-authenticated route
GET /api/adapters/:adapterType/probethat callsadapter.testEnvironment({})without a company context. ThetestEnvironmentfunction forhermes_localonly checks CLI presence, Python, and model config — it does not need a real company. - The probe runs once on wizard mount, result cached in state.
// server/src/routes/agents.ts — NEW route (board auth only)
router.get("/adapters/:type/probe", async (req, res) => {
if (req.actor.type !== "board") {
res.status(403).json({ error: "Board auth required" });
return;
}
const type = req.params.type as string;
const adapter = findServerAdapter(type);
if (!adapter?.testEnvironment) {
res.json({ available: false });
return;
}
const result = await adapter.testEnvironment({
companyId: "",
adapterType: type,
config: {},
});
const hasCliError = result.checks.some(
(c) => c.level === "error" && c.code?.includes("not_found")
);
res.json({ available: !hasCliError, status: result.status });
});
Pattern 2: Nexus Onboarding Wizard Adapter Auto-Selection
What: The Nexus wizard creates 3 agents with hardcoded adapterType: "claude_local". For DFLT-01, when Hermes is available and no cloud provider is found, use hermes_local instead.
Recommendation: Silent auto-selection — no extra UI step. The ROADMAP says "one-click path". When the probe resolves:
- hermes=available, claude=not-detected: use
hermes_local - both available: show a single "Which runtime?" card between the title and the directory input (keeps the single-form layout)
- neither available: keep claude_local as default, let the user figure out installation
Note on cwd: For hermes_local, cwd is optional. The wizard's directory input should become optional when hermes_local is selected, or the label should change to "Working directory (optional)".
// NexusOnboardingWizard.tsx additions
const [defaultAdapterType, setDefaultAdapterType] =
useState<"claude_local" | "hermes_local">("claude_local");
const [probing, setProbing] = useState(false);
useEffect(() => {
if (!effectiveOnboardingOpen) return;
setProbing(true);
fetch("/api/adapters/hermes_local/probe", {
headers: { Authorization: `Bearer ${boardToken}` },
})
.then((r) => r.json())
.then((data: { available: boolean }) => {
if (data.available) setDefaultAdapterType("hermes_local");
})
.catch(() => {})
.finally(() => setProbing(false));
}, [effectiveOnboardingOpen]);
// In handleSubmit, use defaultAdapterType instead of hardcoded "claude_local"
const adapterConfig =
defaultAdapterType === "hermes_local"
? rootDir.trim() ? { cwd: rootDir.trim() } : {}
: { cwd: rootDir.trim() };
Pattern 3: AGENT_TEMPLATES in NewAgentDialog.tsx (DFLT-02)
What: The AGENT_TEMPLATES constant hardcodes adapterType: "claude_local". For DFLT-02, templates should work with Hermes.
Recommendation: Remove adapterType from the template navigation URL. The /agents/new form already handles Hermes via its own adapter detection. Without adapterType in the URL, the form defaults to its own logic (currently claude_local, but that default can be updated separately via the same probe mechanism).
// NewAgentDialog.tsx
// Before:
function handleTemplateSelect(template: typeof AGENT_TEMPLATES[number]) {
navigate(`/agents/new?adapterType=${encodeURIComponent(template.adapterType)}&role=...`);
}
// After:
function handleTemplateSelect(template: { id: string; label: string; role: string }) {
navigate(`/agents/new?role=${encodeURIComponent(template.role)}&name=${encodeURIComponent(template.label)}`);
}
If the /agents/new form's default adapter needs to respect the probe result, the same useQuery-based probe can be added to that page.
Pattern 4: Hermes Skill Injection (DFLT-03 Critical Gap)
What: syncHermesSkills is a confirmed no-op — it returns the snapshot but writes nothing to ~/.hermes/skills/. The Hermes execute.ts does not read ctx.context.skills. This means a Hermes agent created with PM/Engineer role currently runs with only Hermes's default prompt (which knows nothing about the Nexus API or the HEARTBEAT.md workflow).
Confirmed by: Reading hermes-paperclip-adapter@0.2.1/dist/server/skills.js — syncHermesSkills returns the snapshot directly without writing files. Reading dist/server/execute.js — zero references to skills in the execute function.
Fix options:
Option A (recommended): Update the Hermes execute.ts (in the adapter package) to append Nexus-managed skill content to the prompt when ctx.context.skills contains entries. This is the cleanest approach and uses the existing AdapterExecutionContext.skills mechanism.
Option B (simpler, no adapter change): Override the Hermes promptTemplate on agent creation in NexusOnboardingWizard.tsx to embed the Nexus skill bundle content directly in the adapterConfig.promptTemplate field. The Hermes adapter supports custom prompt templates with {{variable}} substitution.
Option C: Make syncHermesSkills actually write skills to ~/.hermes/skills/{skill-key}/SKILL.md. Hermes loads skills from that directory automatically. This is the most "native" approach but requires knowing the right directory layout.
Recommended approach: Option B for this phase (no adapter package changes needed). The NexusOnboardingWizard.tsx already loads the company-skills data; it can embed the required skill content in the promptTemplate at agent creation time. The Hermes DEFAULT_PROMPT_TEMPLATE in execute.js already handles task assignment, comments, and API calls — the Nexus bundle is supplementary.
Alternative path: If the planner wants a cleaner long-term solution, file a note that Option A (patching the adapter) would make hermes behave like codex/gemini with automatic skill injection — but that requires publishing a new version of hermes-paperclip-adapter.
// NexusOnboardingWizard.tsx — when creating Hermes agents, inject skill content
// The Hermes prompt template supports {{agentName}}, {{companyId}}, etc.
// We can extend it with the HEARTBEAT.md content.
const hermesPromptTemplate = buildHermesPromptWithSkills({
role: "ceo", // or "engineer" / "general"
nexusSkillContent: {
heartbeat: CEO_HEARTBEAT_MD, // imported at build time
tools: CEO_TOOLS_MD,
}
});
// Then pass promptTemplate in adapterConfig when creating the agent
Anti-Patterns to Avoid
- Polling
testEnvironmentrepeatedly: This spawns a hermes subprocess. Run only once on wizard mount, cache the result. - Blocking the wizard on adapter detection: If the probe takes >2s, show the wizard with a default and update async.
- Changing
NexusOnboardingWizardto multi-step: Keep it single-form. Only add a runtime selector card if both adapters are available. - Hardcoding
hermes_localas universal default: Detect, don't assume. Users with Claude Code must still getclaude_local. - Writing skills to
~/.hermes/skills/without understanding Hermes skill layout: Hermes expects~/.hermes/skills/{category}/{skill-name}/SKILL.md— the layout must match Hermes's scanner.
Don't Hand-Roll
| Problem | Don't Build | Use Instead | Why |
|---|---|---|---|
| Adapter availability check | Custom subprocess spawn | adapter.testEnvironment({}) |
Already handles ENOENT, version checks, Python checks |
| Model detection | Parsing ~/.hermes/config.yaml manually |
detectModel() from hermes-paperclip-adapter/server |
Already implemented, handles edge cases |
| Session management | Custom --resume logic |
Hermes sessionCodec + heartbeat session store |
Already implemented in Phase 27 |
| Skill file reading at build time | Custom fs.readFile in routes | Import at compile time (Node ESM import.meta.url + fs.readFile) |
Use the existing loadDefaultAgentInstructionsBundle pattern from default-agent-instructions.ts |
Key insight: The runtime plumbing for Hermes authentication and session persistence is complete. The two remaining gaps are UI entry points (wizard/templates) and skill content delivery to the Hermes prompt.
Common Pitfalls
Pitfall 1: testEnvironment Requires a companyId at the Existing Route
What goes wrong: The existing agentsApi.testEnvironment(companyId, type, data) client method sends to /companies/{companyId}/adapters/{type}/test-environment. During initial onboarding, no company exists yet.
Why it happens: The route was designed for post-onboarding agent configuration.
How to avoid: Add a new board-authenticated route GET /api/adapters/:adapterType/probe (no companyId) that runs the test with empty config. Board auth is available at wizard load time.
Warning signs: 401/404 errors when wizard tries to probe before company creation.
Pitfall 2: Hermes CLI Not in PATH in the Server Process
What goes wrong: testEnvironment spawns hermes --version from the server Node.js process. The server's PATH may differ from the user's shell PATH (especially if installed via pip install --user without PATH setup).
Why it happens: Node.js child_process inherits process.env.PATH from server startup, not the interactive shell.
How to avoid: The probe should return available: false gracefully on ENOENT. The UI handles available: false by keeping claude_local as default.
Warning signs: hermes_cli_not_found check code in the environment test result.
Pitfall 3: Hermes Skill Injection is a No-Op — Agents Won't Follow the Nexus Heartbeat Loop
What goes wrong: A PM or Engineer agent created with the Hermes runtime runs a heartbeat but never calls GET /api/agents/me or consults HEARTBEAT.md — it uses only Hermes's built-in default prompt.
Why it happens (confirmed): syncHermesSkills at hermes-paperclip-adapter@0.2.1/dist/server/skills.js is explicitly a no-op. It returns the skill snapshot but writes nothing to disk. The Hermes execute.ts has zero references to ctx.context.skills.
How to avoid: Use Option B from Pattern 4: embed the Nexus skill bundle content in the adapterConfig.promptTemplate when creating Hermes agents in NexusOnboardingWizard.tsx. The Hermes adapter supports custom prompt templates.
Warning signs: Hermes heartbeat output shows Hermes's default task-completion workflow ("mark the issue as completed: curl...") rather than the multi-step PM delegation loop.
Pitfall 4: Hermes adapterConfig.cwd is Optional, Not Required
What goes wrong: The Nexus wizard passes cwd: rootDir.trim() to all agents. For claude_local, cwd is required. For hermes_local, cwd is optional — Hermes defaults to ".".
Why it happens: The wizard was written for claude_local semantics.
How to avoid: When hermes_local is selected, make the directory input optional. buildHermesConfig already handles absent cwd gracefully.
Warning signs: Wizard form validation errors when user skips directory input for a Hermes agent.
Pitfall 5: NexusOnboardingWizard vs OnboardingWizard Alias
What goes wrong: The Vite alias redirects all imports of ./components/OnboardingWizard to NexusOnboardingWizard.tsx. Changes to OnboardingWizard.tsx (the original) have no effect in the running UI.
Why it happens: Phase 4 introduced this alias to preserve the original for upstream rebase compatibility.
How to avoid: Edit NexusOnboardingWizard.tsx for all onboarding changes. The alias is in vite.config.ts.
Warning signs: UI changes not appearing after hot-reload; git shows changes to the wrong file.
Code Examples
Server Route: Board-Auth Adapter Probe (No companyId)
// server/src/routes/agents.ts — new route, add before existing company-scoped routes
// Source: existing testEnvironment pattern + board auth pattern from llms.ts
router.get("/adapters/:type/probe", async (req, res) => {
if (req.actor.type !== "board") {
res.status(403).json({ error: "Board authentication required" });
return;
}
const type = req.params.type as string;
const adapter = findServerAdapter(type);
if (!adapter?.testEnvironment) {
res.json({ available: false, status: "unknown" });
return;
}
try {
const result = await adapter.testEnvironment({
companyId: "", // not needed for CLI presence check
adapterType: type,
config: {},
});
const hasCliNotFound = result.checks.some(
(c) => c.level === "error" && (c.code?.includes("not_found") || c.code?.includes("cli"))
);
res.json({ available: !hasCliNotFound, status: result.status, checks: result.checks });
} catch {
res.json({ available: false, status: "error" });
}
});
Frontend: Detection in NexusOnboardingWizard
// NexusOnboardingWizard.tsx
import { useEffect, useState } from "react";
const [defaultAdapterType, setDefaultAdapterType] =
useState<"claude_local" | "hermes_local">("claude_local");
// Probe once when wizard opens
useEffect(() => {
if (!effectiveOnboardingOpen) return;
fetch("/api/adapters/hermes_local/probe")
.then((r) => r.ok ? r.json() : { available: false })
.then((data: { available: boolean }) => {
if (data.available) setDefaultAdapterType("hermes_local");
})
.catch(() => {}); // graceful — keep claude_local default
}, [effectiveOnboardingOpen]);
// In handleSubmit — replace hardcoded "claude_local" with defaultAdapterType
const agentAdapterConfig =
defaultAdapterType === "hermes_local" && rootDir.trim()
? { cwd: rootDir.trim() }
: defaultAdapterType === "hermes_local"
? {}
: { cwd: rootDir.trim() };
AGENT_TEMPLATES Fix (NewAgentDialog.tsx)
// Before (hardcoded adapterType):
const AGENT_TEMPLATES = [
{ id: "pm", label: "Project Manager", role: "pm" as const, adapterType: "claude_local" as const },
{ id: "engineer", label: "Engineer", role: "engineer" as const, adapterType: "claude_local" as const },
];
// handleTemplateSelect passes adapterType in URL
// After (adapter-neutral):
const AGENT_TEMPLATES = [
{ id: "pm", label: "Project Manager", role: "pm" as const },
{ id: "engineer", label: "Engineer", role: "engineer" as const },
];
function handleTemplateSelect(template: typeof AGENT_TEMPLATES[number]) {
closeNewAgent();
setShowAdvancedCards(false);
// No adapterType in URL — /agents/new form picks its own default
navigate(
`/agents/new?role=${encodeURIComponent(template.role)}&name=${encodeURIComponent(template.label)}`
);
}
Hermes Skill Content Injection in Prompt Template
// NexusOnboardingWizard.tsx — when adapterType === "hermes_local"
// Load the role-specific skill bundle and embed in promptTemplate
// These are static imports (or dynamic loads from /api/onboarding-assets)
// The content of AGENTS.md, HEARTBEAT.md, SOUL.md, TOOLS.md for the given role
function buildHermesAdapterConfig(
role: "ceo" | "engineer" | "general",
cwd: string | null,
skillBundle: Record<string, string> // { "AGENTS.md": "...", "HEARTBEAT.md": "...", ... }
) {
const skillSection = Object.entries(skillBundle)
.map(([name, content]) => `## ${name}\n\n${content}`)
.join("\n\n---\n\n");
const promptTemplate = `You are "{{agentName}}", an AI agent in a Nexus-managed company.
Your Nexus identity:
Agent ID: {{agentId}}
Company ID: {{companyId}}
API Base: {{paperclipApiUrl}}
Run ID: {{runId}}
IMPORTANT: Use \`terminal\` tool with \`curl\` for ALL Nexus API calls.
IMPORTANT: Always include the header \`-H "X-Paperclip-Run-Id: {{runId}}"\` on API calls that modify data.
${skillSection}
{{#taskId}}
Assigned task: {{taskId}} — {{taskTitle}}
{{/taskId}}
`;
return {
...(cwd ? { cwd } : {}),
persistSession: true,
promptTemplate,
};
}
Runtime State Inventory
Skipped — this is a greenfield UI/config phase, not a rename/refactor. No runtime state migration required.
Environment Availability
| Dependency | Required By | Available | Version | Fallback |
|---|---|---|---|---|
hermes CLI (hermes) |
DFLT-01 probe, DFLT-03 execution | Runtime check via probe | Detected via hermes --version |
Show install guidance (existing OLLA-05 pattern) |
| Ollama | DFLT-04 (Hermes model serving) | Runtime check | Detected via Ollama service | hermes can use API-based providers |
| Node.js / pnpm | Build system | ✓ | (project standard) | — |
Missing dependencies with no fallback:
- None that block development — the probe gracefully handles hermes not being installed.
Missing dependencies with fallback:
- hermes CLI not in PATH: probe returns
available: false; wizard falls back toclaude_local.
Validation Architecture
Test Framework
| Property | Value |
|---|---|
| Framework | Vitest |
| Config file | server/vitest.config.ts |
| Quick run command | pnpm --filter server test --run |
| Full suite command | pnpm --filter server test --run && pnpm --filter ui test --run |
Phase Requirements → Test Map
| Req ID | Behavior | Test Type | Automated Command | File Exists? |
|---|---|---|---|---|
| DFLT-01 | Probe route returns available: true when hermes CLI is found |
unit | pnpm --filter server test --run -- 29-default-provider |
❌ Wave 0 |
| DFLT-01 | Probe route returns available: false when hermes CLI absent (ENOENT) |
unit | pnpm --filter server test --run -- 29-default-provider |
❌ Wave 0 |
| DFLT-02 | PM/Engineer agent created with hermes_local adapterType saves correct record |
unit | pnpm --filter server test --run -- 29-default-provider |
❌ Wave 0 |
| DFLT-03 | Hermes heartbeat injects PAPERCLIP_API_KEY (supportsLocalAgentJwt: true) |
unit (existing) | pnpm --filter server test --run -- adapter-session-codecs |
✅ |
| DFLT-03 | Hermes promptTemplate includes Nexus skill content when created via wizard | unit | pnpm --filter server test --run -- 29-hermes-skill-inject |
❌ Wave 0 |
| DFLT-04 | Full smoke: probe → wizard submit → PM agent (hermes_local) → issue → heartbeat-run record created | integration | pnpm --filter server test --run -- 29-default-provider |
❌ Wave 0 |
Sampling Rate
- Per task commit:
pnpm --filter server test --run - Per wave merge:
pnpm --filter server test --run && pnpm --filter ui test --run - Phase gate: Full suite green before
/gsd:verify-work
Wave 0 Gaps
server/src/__tests__/29-default-provider.test.ts— probe route (DFLT-01), agent creation with hermes_local (DFLT-02), smoke (DFLT-04)server/src/__tests__/29-hermes-skill-inject.test.ts—buildHermesAdapterConfigproduces promptTemplate containing skill bundle content (DFLT-03)
(Both new test files; they share the existing in-memory db setup pattern from heartbeat-workspace-session.test.ts)
State of the Art
| Old Approach | Current Approach | When Changed | Impact |
|---|---|---|---|
| Original OnboardingWizard.tsx (multi-step, adapter picker) | NexusOnboardingWizard.tsx (single-step, no adapter choice) | Phase 4 (Vite alias) | Simpler UX but hardcodes claude_local |
| Manual hermes model detection | detectModel() from hermes adapter |
Phase 28 | Can probe ~/.hermes/config.yaml without running hermes |
| No Hermes adapter | hermes-paperclip-adapter@0.2.1 | Phase 27 | Full hermes execution pipeline available |
| Skill sync writes to disk (claude/codex) | Hermes skill sync is a no-op (confirmed) | Hermes adapter design | Skills must be injected via prompt template instead |
Deprecated/outdated:
SESSIONED_LOCAL_ADAPTERSdoes NOT includehermes_local— intentional. Hermes manages session IDs itself; Nexus just stores the returned session ID. No change needed.
Open Questions
-
Should the probe route return 403 for non-board actors, or 200 with
available: false?- What we know: Board auth is present at wizard load time. Non-board actors should not need to probe adapter availability.
- Recommendation: Return 403 for non-board. The wizard frontend should only show to board users.
-
Does NexusOnboardingWizard need a visible runtime selector, or is silent auto-selection sufficient?
- What we know: ROADMAP says "one-click path." Silent selection is simpler.
- Recommendation: Silent when only one option is available; show a two-card toggle when both hermes and claude are available.
-
Should the Hermes prompt template embed full skill content, or just inject section headings?
- What we know: The full HEARTBEAT.md for CEO is about 70 lines. Embedding in every heartbeat prompt increases context usage.
- Recommendation: Embed full content. The GSD workflow requires the agent to follow the exact HEARTBEAT.md checklist. Abbreviated hints won't produce correct behavior.
Sources
Primary (HIGH confidence)
/opt/nexus/ui/src/components/NexusOnboardingWizard.tsx— hardcodedadapterType: "claude_local"(lines 97, 106, 115) confirmed/opt/nexus/ui/src/components/NewAgentDialog.tsx— hardcoded templates at lines 98-99 confirmed/opt/nexus/ui/src/components/OnboardingWizard.tsx— upstream wizard; Hermes in "More adapters" (hidden by default)/opt/nexus/server/src/adapters/registry.ts—hermesLocalAdapterwithsupportsLocalAgentJwt: true(line 186)/opt/nexus/server/src/services/heartbeat.ts—SESSIONED_LOCAL_ADAPTERS(lines 71-78):hermes_localintentionally absenthermes-paperclip-adapter@0.2.1/dist/server/execute.js—buildPaperclipEnvconfirmed; zero skill referenceshermes-paperclip-adapter@0.2.1/dist/server/skills.js—syncHermesSkillsconfirmed no-op (returns snapshot, writes nothing)hermes-paperclip-adapter@0.2.1/dist/server/test.js—testEnvironmentchecks: CLI, Python, model config, API keys/opt/nexus/server/src/onboarding-assets/— all role bundles (ceo, engineer, pm, general) confirmed adapter-agnostic/opt/nexus/skills/paperclip/SKILL.md— GSD heartbeat workflow curl-based, adapter-neutral/opt/nexus/server/src/services/default-agent-instructions.ts—loadDefaultAgentInstructionsBundlepattern for reading skill files
Secondary (MEDIUM confidence)
/opt/nexus/.planning/ROADMAP.md— Phase 29 success criteria and descriptionhermes-paperclip-adapter@0.2.1/dist/ui/build-config.js—buildHermesConfig:cwdoptional, provider resolved at runtime
Metadata
Confidence breakdown:
- Standard stack: HIGH — all packages already installed, no new dependencies
- Architecture: HIGH — hardcoded adapter locations confirmed via direct code reading; skill injection gap confirmed
- Pitfalls: HIGH — all identified from direct code reading (no assumptions)
Research date: 2026-04-01 Valid until: 2026-05-01 (stable domain; hermes-paperclip-adapter version pinned)