<% if notice.present? %>
<%= notice %>
diff --git a/engine/config/routes.rb b/engine/config/routes.rb index 18fb108..032bebb 100644 --- a/engine/config/routes.rb +++ b/engine/config/routes.rb @@ -47,5 +47,8 @@ end end + get "llms.txt", to: "llms#show", as: :llms_txt + get "agent-instructions", to: "agent_instructions#show", as: :agent_instructions + root "plans#index" end diff --git a/engine/lib/coplan/configuration.rb b/engine/lib/coplan/configuration.rb index 091c6ad..a52801a 100644 --- a/engine/lib/coplan/configuration.rb +++ b/engine/lib/coplan/configuration.rb @@ -4,6 +4,9 @@ class Configuration attr_accessor :ai_base_url, :ai_api_key, :ai_model attr_accessor :error_reporter attr_accessor :notification_handler + attr_accessor :onboarding_banner + attr_accessor :agent_auth_instructions + attr_accessor :agent_curl_prefix def initialize @authenticate = nil @@ -12,6 +15,30 @@ def initialize @ai_model = "gpt-4o" @error_reporter = ->(exception, context) { Rails.error.report(exception, context: context) } @notification_handler = nil + @onboarding_banner = 'Want to upload Agentic plans? Give your agent these instructions.' + @agent_curl_prefix = 'curl -s -H "Authorization: Bearer $TOKEN"' + @agent_auth_instructions = <<~MARKDOWN + ## Authentication + + Credentials are stored at `~/.config/coplan/credentials.json`: + + ```json + { + "base_url": "BASE_URL", + "token": "your-token-here" + } + ``` + + On first use: + + 1. Read `~/.config/coplan/credentials.json` to get `token` and `base_url`. + 2. If the file does not exist, tell the user: "Go to **Settings → API Tokens** in the CoPlan web UI to create a token." Ask for the token and base URL, then save to `~/.config/coplan/credentials.json` with `chmod 600`. + 3. If any API call returns 401, the token is invalid or revoked. Prompt the user to create a new token in Settings and update the credentials file. + + Use the values from the credentials file in all API calls below. + + All requests use `Authorization: Bearer $TOKEN` header. + MARKDOWN end def show_api_tokens? diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 2706b70..e873bbd 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -23,6 +23,8 @@ + + def sign_in_as(coplan_user) coplan_user.update!(email: "#{coplan_user.external_id}@test.example.com") unless coplan_user.email.present? post sign_in_path, params: { email: coplan_user.email } diff --git a/spec/requests/plans_spec.rb b/spec/requests/plans_spec.rb index 0b642dd..ae778bc 100644 --- a/spec/requests/plans_spec.rb +++ b/spec/requests/plans_spec.rb @@ -109,4 +109,36 @@ get plan_path(brainstorm_plan) expect(response).to have_http_status(:ok) end + + describe "onboarding banner" do + it "shows banner when user has no plans" do + sign_in_as(bob) + get plans_path + expect(response.body).to include("onboarding-banner") + end + + it "hides banner when user has created a plan" do + plan # alice has a plan + get plans_path + expect(response.body).not_to include("onboarding-banner") + end + + it "hides banner when onboarding_banner config is nil" do + sign_in_as(bob) + original = CoPlan.configuration.onboarding_banner + CoPlan.configuration.onboarding_banner = nil + get plans_path + expect(response.body).not_to include("onboarding-banner") + CoPlan.configuration.onboarding_banner = original + end + + it "displays custom banner text from configuration" do + sign_in_as(bob) + original = CoPlan.configuration.onboarding_banner + CoPlan.configuration.onboarding_banner = "Custom onboarding message" + get plans_path + expect(response.body).to include("Custom onboarding message") + CoPlan.configuration.onboarding_banner = original + end + end end From 33e863020b55fb642e5a5424f0b77fddaa3c20e6 Mon Sep 17 00:00:00 2001 From: Hampton Lintorn-Catlin Date: Fri, 3 Apr 2026 10:31:05 -0500 Subject: [PATCH 4/4] Use route helpers for agent-instructions paths Fixes mount-prefix issue: when engine is mounted at /coplan, the header and agent redirect now correctly resolve to /coplan/agent-instructions instead of /agent-instructions. Amp-Thread-ID: https://ampcode.com/threads/T-019d4ffd-9cbb-7626-aeba-9e03685aa4dc Co-authored-by: Amp --- engine/app/controllers/coplan/api/v1/base_controller.rb | 2 +- engine/app/controllers/coplan/application_controller.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/app/controllers/coplan/api/v1/base_controller.rb b/engine/app/controllers/coplan/api/v1/base_controller.rb index 924f6ad..4542c36 100644 --- a/engine/app/controllers/coplan/api/v1/base_controller.rb +++ b/engine/app/controllers/coplan/api/v1/base_controller.rb @@ -8,7 +8,7 @@ class BaseController < ActionController::API private def set_agent_instructions_header - response.headers["X-Agent-Instructions"] = "/agent-instructions" + response.headers["X-Agent-Instructions"] = CoPlan::Engine.routes.url_helpers.agent_instructions_path end def authenticate_api! diff --git a/engine/app/controllers/coplan/application_controller.rb b/engine/app/controllers/coplan/application_controller.rb index f3d37a5..c368c1b 100644 --- a/engine/app/controllers/coplan/application_controller.rb +++ b/engine/app/controllers/coplan/application_controller.rb @@ -87,7 +87,7 @@ def authorize!(record, action) end def set_agent_instructions_header - response.headers["X-Agent-Instructions"] = "/agent-instructions" + response.headers["X-Agent-Instructions"] = coplan.agent_instructions_path end def agent_request? @@ -104,7 +104,7 @@ def agent_redirect_instructions To interact with CoPlan programmatically, use the API. Full instructions are at: - #{base}/agent-instructions + #{base}#{coplan.agent_instructions_path} Read that document for authentication setup, endpoint reference, and usage examples. MARKDOWN