diff --git a/.changeset/bootstrap-ux-fixes.md b/.changeset/bootstrap-ux-fixes.md new file mode 100644 index 0000000..16d0104 --- /dev/null +++ b/.changeset/bootstrap-ux-fixes.md @@ -0,0 +1,10 @@ +--- +"@united-workforce/cli": patch +--- + +fix: preset provider base-url auto-fill, bootstrap ACP docs, friendlier name mismatch error + +- `uwf setup --provider dashscope` now auto-fills `--base-url` from preset list (#106) +- Bootstrap guide documents uwf-hermes ACP dependency (`pip install hermes-agent[acp]`) (#107) +- Bootstrap verify step uses inline workflow instead of missing `examples/eval-simple.yaml` (#107) +- Workflow filename mismatch error now suggests how to fix it (#108) diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 17e775d..0c343ad 100755 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -11,7 +11,7 @@ import { cmdPromptUsage, cmdPromptWorkflowAuthoring, } from "./commands/prompt.js"; -import { cmdSetup, cmdSetupInteractive } from "./commands/setup.js"; +import { cmdSetup, cmdSetupInteractive, resolvePresetBaseUrl } from "./commands/setup.js"; import { cmdStepFork, cmdStepList, cmdStepRead, cmdStepShow } from "./commands/step.js"; import { cmdThreadCancel, @@ -558,10 +558,14 @@ program }) => { const storageRoot = resolveStorageRoot(); runAction(async () => { - if (opts.provider && opts.baseUrl && opts.apiKey && opts.model) { + // Resolve preset base-url when provider is known but --base-url is omitted + const resolvedBaseUrl = + opts.baseUrl ?? + (opts.provider !== undefined ? resolvePresetBaseUrl(opts.provider) : null); + if (opts.provider && resolvedBaseUrl && opts.apiKey && opts.model) { const result = await cmdSetup({ provider: opts.provider, - baseUrl: opts.baseUrl, + baseUrl: resolvedBaseUrl, apiKey: opts.apiKey, model: opts.model, agent: opts.agent ?? undefined, @@ -572,7 +576,7 @@ program await cmdSetupInteractive(storageRoot); } else { throw new Error( - "Non-interactive setup requires all of: --provider, --base-url, --api-key, --model", + "Non-interactive setup requires: --provider, --api-key, --model (--base-url is optional for preset providers)", ); } }); diff --git a/packages/cli/src/commands/prompt.ts b/packages/cli/src/commands/prompt.ts index 60c5a97..3f33fef 100644 --- a/packages/cli/src/commands/prompt.ts +++ b/packages/cli/src/commands/prompt.ts @@ -43,6 +43,11 @@ Install an agent adapter (at least one is required): | uwf-claude-code | \`npm install -g @united-workforce/agent-claude-code\` | When using Claude Code CLI directly | | uwf-builtin | \`npm install -g @united-workforce/agent-builtin\` | Lightweight built-in agent (no external dependency) | +**uwf-hermes** also requires the Hermes ACP plugin. After installing \`hermes-agent\`, run: +\`\`\`bash +pip install hermes-agent[acp] # or: pip install -e .[acp] if installed from source +\`\`\` + Verify the adapter is installed: \`uwf-hermes --version\` (or whichever you chose). ### Step 2 — Configure provider and model @@ -81,20 +86,43 @@ Verify skills are installed by listing them (e.g. \`skills_list()\`) and confirm ### Step 4 — Verify end-to-end -Run a quick smoke test with the built-in eval workflow: +Create a minimal workflow file to test your setup: \`\`\`bash -# Start a thread with the example workflow -uwf thread start examples/eval-simple.yaml -p "Hello, test run" +cat > /tmp/hello.yaml << 'YAML' +name: hello +description: Minimal smoke test +roles: + greeter: + description: "Greet the user" + goal: "Respond with a friendly greeting" + capabilities: [] + procedure: "Write a short greeting based on the prompt." + output: "A greeting message." + frontmatter: + type: object + properties: + $status: { enum: [done] } + message: { type: string } + required: [$status, message] +graph: + $START: + new: { role: greeter, prompt: "Say hello to the user." } + resume: { role: greeter, prompt: "Greet the user again." } + greeter: + done: { role: "$END", prompt: "Done." } +YAML +\`\`\` -# Execute one step +Then run: + +\`\`\`bash +uwf thread start /tmp/hello.yaml -p "Hello, world!" uwf thread exec - -# Check result uwf thread show \`\`\` -If the thread reaches \`$END\` or produces output, the setup is working. +If the thread reaches \`$END\` with status \`completed\`, the setup is working. ## Scenario B: Upgrade from Previous Version diff --git a/packages/cli/src/commands/setup.ts b/packages/cli/src/commands/setup.ts index 5c8b04b..2dd4a83 100644 --- a/packages/cli/src/commands/setup.ts +++ b/packages/cli/src/commands/setup.ts @@ -72,6 +72,12 @@ const PRESET_PROVIDERS = [ { name: "ollama", label: "Ollama (local)", baseUrl: "http://localhost:11434/v1" }, ] as const; +/** Look up the base URL for a preset provider name. Returns null if not a preset. */ +export function resolvePresetBaseUrl(providerName: string): string | null { + const preset = PRESET_PROVIDERS.find((p) => p.name === providerName); + return preset !== undefined ? preset.baseUrl : null; +} + type SetupArgs = { provider: string; baseUrl: string; diff --git a/packages/cli/src/validate.ts b/packages/cli/src/validate.ts index 5824454..437eb3b 100644 --- a/packages/cli/src/validate.ts +++ b/packages/cli/src/validate.ts @@ -99,7 +99,7 @@ export function checkWorkflowFilenameConsistency( ): string | null { const expected = workflowNameFromPath(filePath); if (payload.name !== expected) { - return `workflow name mismatch: file "${basename(filePath)}" implies name "${expected}" but YAML declares name "${payload.name}"`; + return `workflow name mismatch: file "${basename(filePath)}" implies name "${expected}" but YAML declares name "${payload.name}". Either rename the file to "${payload.name}.yaml" or change the YAML \`name\` field to "${expected}"`; } return null; }