@launchchair/agent 0.4.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/AGENTS.md +248 -0
- package/LICENSE +8 -0
- package/README.md +188 -0
- package/chatgpt-runner.mjs +239 -0
- package/claude-code-runner.mjs +141 -0
- package/claude-runner.mjs +218 -0
- package/codex-runner.mjs +123 -0
- package/launchchair-cli.mjs +1013 -0
- package/local-bridge.mjs +540 -0
- package/mcp-bridge-server.mjs +1101 -0
- package/package.json +43 -0
- package/runner-command.mjs +96 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# LaunchChair Agent Bootstrap
|
|
2
|
+
|
|
3
|
+
This file is for fresh agent chats that need to use the LaunchChair Agent API or MCP bridge without wasting time searching an empty repo.
|
|
4
|
+
|
|
5
|
+
## Core Rule
|
|
6
|
+
|
|
7
|
+
Use LaunchChair first for product, research, validation, positioning, pricing, MVP blueprint, and phase-loop work. Do not inspect the local repo first unless the user is in Stack Setup, Build MVP, Design and Landing Page implementation, or another code-changing phase.
|
|
8
|
+
|
|
9
|
+
Fresh LaunchChair projects often do not have a local repo yet. Before Stack Setup, the configured runner folder may be empty on purpose.
|
|
10
|
+
|
|
11
|
+
## Public Docs To Read
|
|
12
|
+
|
|
13
|
+
- Agent API docs: https://www.launchchair.io/docs/agent-api
|
|
14
|
+
- MCP setup docs: https://www.launchchair.io/docs/mcp
|
|
15
|
+
- Optional CLI docs: https://www.launchchair.io/docs/cli
|
|
16
|
+
- Public machine-readable catalog: https://www.launchchair.io/api/agent/v1/docs
|
|
17
|
+
- Bridge file catalog: https://www.launchchair.io/api/agent/v1/bridge
|
|
18
|
+
|
|
19
|
+
## Security Rules
|
|
20
|
+
|
|
21
|
+
- Never print a LaunchChair token.
|
|
22
|
+
- Never paste a LaunchChair token into public docs, issue comments, screenshots, or chat summaries.
|
|
23
|
+
- Real tokens come from LaunchChair Settings -> API Access or from an approved `launchchair login` browser flow.
|
|
24
|
+
- Public docs and artifacts contain placeholders only.
|
|
25
|
+
- If you inspect env files, confirm whether token variables exist without showing their values.
|
|
26
|
+
- If the user asks you to create or continue a LaunchChair project and the configured token permits it, do not ask for extra confirmation before using LaunchChair MCP/API tools. The token scope is the authorization boundary.
|
|
27
|
+
- Ask for confirmation only for external account authorization, production SQL execution, destructive local file actions, or shell commands that the client sandbox requires the user to approve.
|
|
28
|
+
|
|
29
|
+
## Bridge And Runner Files
|
|
30
|
+
|
|
31
|
+
Download these into a stable local folder such as `~/launchchair-agent` or `~/.hermes/launchchair-mcp`:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
mkdir -p "$HOME/launchchair-agent"
|
|
35
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/local-bridge.mjs" -o "$HOME/launchchair-agent/local-bridge.mjs"
|
|
36
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/launchchair-cli.mjs" -o "$HOME/launchchair-agent/launchchair-cli.mjs"
|
|
37
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/mcp-bridge-server.mjs" -o "$HOME/launchchair-agent/mcp-bridge-server.mjs"
|
|
38
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/runner-command.mjs" -o "$HOME/launchchair-agent/runner-command.mjs"
|
|
39
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/codex-runner.mjs" -o "$HOME/launchchair-agent/codex-runner.mjs"
|
|
40
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/claude-code-runner.mjs" -o "$HOME/launchchair-agent/claude-code-runner.mjs"
|
|
41
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/chatgpt-runner.mjs" -o "$HOME/launchchair-agent/chatgpt-runner.mjs"
|
|
42
|
+
curl -fsSL "https://www.launchchair.io/api/agent/v1/bridge/claude-runner.mjs" -o "$HOME/launchchair-agent/claude-runner.mjs"
|
|
43
|
+
curl -fsSL "https://www.launchchair.io/agent-artifacts/AGENTS.md" -o "$HOME/launchchair-agent/AGENTS.md"
|
|
44
|
+
chmod +x "$HOME/launchchair-agent"/*.mjs
|
|
45
|
+
LAUNCHCHAIR_AGENT_WORKDIR="${LC_AGENT_CODEX_CWD:-${LC_AGENT_CLAUDE_CODE_CWD:-$PWD}}"
|
|
46
|
+
if [ -d "$LAUNCHCHAIR_AGENT_WORKDIR" ] && [ ! -f "$LAUNCHCHAIR_AGENT_WORKDIR/AGENTS.md" ]; then
|
|
47
|
+
printf '%s\n' \
|
|
48
|
+
'# LaunchChair Agent Bootstrap Pointer' \
|
|
49
|
+
'' \
|
|
50
|
+
'Before searching this repo, read ~/launchchair-agent/AGENTS.md.' \
|
|
51
|
+
'Use LaunchChair MCP/API first for product phases, project discovery, project creation, and phase loops.' \
|
|
52
|
+
'Do not ask for extra confirmation before using LaunchChair MCP/API when the user already requested the action and the token permits it.' \
|
|
53
|
+
'Only inspect this repo for Stack Setup, Build MVP, Design/Landing Page, SEO implementation, smoke testing, security review, or code remediation.' \
|
|
54
|
+
> "$LAUNCHCHAIR_AGENT_WORKDIR/AGENTS.md"
|
|
55
|
+
fi
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The final block creates a tiny project-local `AGENTS.md` pointer only when the target folder does not already have one. It helps fresh Codex chats auto-load the LaunchChair instructions without overwriting a real project instruction file.
|
|
59
|
+
|
|
60
|
+
## Optional CLI
|
|
61
|
+
|
|
62
|
+
MCP-only setup is still supported. Use `mcp-bridge-server.mjs` when the desktop agent exposes LaunchChair MCP tools. Use `launchchair-cli.mjs` when you need quick diagnostics, project discovery, retry-safe project creation, MCP config output, or a simple way to start the local bridge.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
node "$HOME/launchchair-agent/launchchair-cli.mjs" doctor
|
|
66
|
+
node "$HOME/launchchair-agent/launchchair-cli.mjs" login
|
|
67
|
+
node "$HOME/launchchair-agent/launchchair-cli.mjs" projects --query LeafLore
|
|
68
|
+
node "$HOME/launchchair-agent/launchchair-cli.mjs" create --name SignalLift --one-liner "Distribution workspace for technical founders" --project-type b2b --idempotency-key signallift-v1
|
|
69
|
+
node "$HOME/launchchair-agent/launchchair-cli.mjs" run-once
|
|
70
|
+
node "$HOME/launchchair-agent/launchchair-cli.mjs" bridge
|
|
71
|
+
node "$HOME/launchchair-agent/launchchair-cli.mjs" mcp-config codex
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Required Environment Variables
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
LAUNCHCHAIR_BASE_URL="https://www.launchchair.io"
|
|
78
|
+
LAUNCHCHAIR_AGENT_TOKEN="<full lc_at token from LaunchChair Settings API Access>"
|
|
79
|
+
LAUNCHCHAIR_PROJECT_ID="optional project UUID; omit or leave blank if creating or discovering a project"
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For Codex Desktop local runs:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
LC_AGENT_ROUTER_GPT_TARGET="codex"
|
|
86
|
+
LC_AGENT_ROUTER_CODEX_TARGET="codex"
|
|
87
|
+
LC_AGENT_CODEX_CWD="/absolute/path/to/the-folder-codex-should-build-into"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
For Claude Desktop plus Claude Code local runs:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
LC_AGENT_ROUTER_GPT_TARGET="claude_code"
|
|
94
|
+
LC_AGENT_ROUTER_CODEX_TARGET="claude_code"
|
|
95
|
+
LC_AGENT_CLAUDE_CODE_CWD="/absolute/path/to/the-folder-claude-code-should-build-into"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The build folder can be an empty scratch folder until Stack Setup creates or records the GitHub repository. After Stack Setup, the user should clone the generated repo locally or update the runner CWD to the cloned repo path.
|
|
99
|
+
|
|
100
|
+
## MCP Server Config Example
|
|
101
|
+
|
|
102
|
+
```toml
|
|
103
|
+
[mcp_servers.launchchair]
|
|
104
|
+
command = "node"
|
|
105
|
+
args = ["/Users/you/launchchair-agent/mcp-bridge-server.mjs"]
|
|
106
|
+
|
|
107
|
+
[mcp_servers.launchchair.env]
|
|
108
|
+
LAUNCHCHAIR_BASE_URL = "https://www.launchchair.io"
|
|
109
|
+
LAUNCHCHAIR_AGENT_TOKEN = "<full lc_at token from Settings API Access>"
|
|
110
|
+
LAUNCHCHAIR_PROJECT_ID = ""
|
|
111
|
+
LC_AGENT_ROUTER_GPT_TARGET = "codex"
|
|
112
|
+
LC_AGENT_ROUTER_CODEX_TARGET = "codex"
|
|
113
|
+
LC_AGENT_CODEX_CWD = "/absolute/path/to/the-folder-codex-should-build-into"
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## What To Do First In A Fresh Chat
|
|
117
|
+
|
|
118
|
+
1. If LaunchChair MCP tools are available, call `launchchair_heartbeat`.
|
|
119
|
+
2. Call `launchchair_status` to confirm token, bridge, runner, and project context.
|
|
120
|
+
3. If `LAUNCHCHAIR_PROJECT_ID` is blank and the user named a project, call `launchchair_list_projects` and select the matching project.
|
|
121
|
+
4. If the user asks to create a new LaunchChair project, call `launchchair_create_project`.
|
|
122
|
+
5. If the user clicked Run with agent in LaunchChair, call `launchchair_run_once`.
|
|
123
|
+
6. If the user asks to advance the product workflow, call `launchchair_continue_project`.
|
|
124
|
+
7. If LaunchChair asks for a user-choice bucket such as wedge, pricing, or feature direction, call `launchchair_select_choice`. Do not use generic SPEC_UPDATE apply for user-choice buckets.
|
|
125
|
+
|
|
126
|
+
## Consent Behavior
|
|
127
|
+
|
|
128
|
+
Do not ask the user for repeated permission to perform normal LaunchChair project-loop work when the user already requested it and the token allows it.
|
|
129
|
+
|
|
130
|
+
Safe LaunchChair MCP/API actions that should not need another user confirmation:
|
|
131
|
+
|
|
132
|
+
- creating a LaunchChair project with `launchchair_create_project`
|
|
133
|
+
- listing accessible LaunchChair projects with `launchchair_list_projects`
|
|
134
|
+
- continuing a project loop with `launchchair_continue_project`
|
|
135
|
+
- selecting a generated wedge, pricing, or feature direction with `launchchair_select_choice`
|
|
136
|
+
- running/rerunning Product Ideation, Market Validation, Positioning, Pricing, and MVP Blueprint buckets
|
|
137
|
+
- returning phase synthesis links and selected choices
|
|
138
|
+
|
|
139
|
+
Stop and ask only when:
|
|
140
|
+
|
|
141
|
+
- the local client sandbox asks the user to approve a shell command
|
|
142
|
+
- the user must authorize GitHub, Vercel, Supabase, Stripe, or another external account
|
|
143
|
+
- production SQL must be executed
|
|
144
|
+
- Product-track manual QA requires a human when LaunchChair explicitly returns that checkpoint; Feature-track QA should be executed by the agent with matching evidence whenever the local runtime is available
|
|
145
|
+
- a destructive local filesystem action is required
|
|
146
|
+
- the token is missing a required scope
|
|
147
|
+
|
|
148
|
+
If Codex asks for permission to run the local bridge fallback command, tell the user they can choose "Yes, and don't ask again for commands that start with ..." for the LaunchChair bridge command prefix. Prefer exposed MCP tools when available because MCP tool calls avoid shell-command permission prompts.
|
|
149
|
+
|
|
150
|
+
## When To Avoid Repo Inspection
|
|
151
|
+
|
|
152
|
+
Do not start with `ls`, `rg`, dependency inspection, or nearby repo searches when the user asks for:
|
|
153
|
+
|
|
154
|
+
- product ideation
|
|
155
|
+
- market validation
|
|
156
|
+
- ICP research
|
|
157
|
+
- wedge selection
|
|
158
|
+
- positioning and pricing
|
|
159
|
+
- MVP blueprint
|
|
160
|
+
- LaunchChair project creation
|
|
161
|
+
- LaunchChair phase loops
|
|
162
|
+
- score-based reruns such as ICP Pain Score, Market Saturation Score, or Wedge Opportunity Score
|
|
163
|
+
|
|
164
|
+
Those are LaunchChair project-state tasks, not local repo tasks.
|
|
165
|
+
|
|
166
|
+
## When Repo Inspection Is Correct
|
|
167
|
+
|
|
168
|
+
Inspect the repo when the current LaunchChair work is:
|
|
169
|
+
|
|
170
|
+
- Stack Setup local readiness
|
|
171
|
+
- Build MVP cards
|
|
172
|
+
- Design and Landing Page implementation
|
|
173
|
+
- SEO, GEO, AEO implementation against an existing app
|
|
174
|
+
- smoke testing, security review, or code remediation
|
|
175
|
+
|
|
176
|
+
For Build MVP work, read the repo `AGENTS.md` first if it exists, then inspect the files named in the LaunchChair build prompt.
|
|
177
|
+
|
|
178
|
+
## Human Checkpoints
|
|
179
|
+
|
|
180
|
+
Stop and give the user a direct LaunchChair link when LaunchChair needs:
|
|
181
|
+
|
|
182
|
+
- GitHub authorization or repo selection
|
|
183
|
+
- Vercel account, team, project, or domain selection
|
|
184
|
+
- Supabase project selection or SQL execution
|
|
185
|
+
- Stripe account authorization
|
|
186
|
+
- Product-track manual QA, or Feature QA that is genuinely blocked by unavailable credentials, external authorization, or an inaccessible runtime
|
|
187
|
+
- a human product choice that the token cannot save
|
|
188
|
+
|
|
189
|
+
Do not claim that SQL migrations, production deploys, billing setup, or QA are complete unless you have verified evidence. For Feature projects, run the available QA yourself and return matching `executionEvidence.agentQaChecks` instead of handing runnable checks back to the user.
|
|
190
|
+
|
|
191
|
+
## Progress Reporting
|
|
192
|
+
|
|
193
|
+
Keep chat updates short. Report:
|
|
194
|
+
|
|
195
|
+
- the LaunchChair project name and phase
|
|
196
|
+
- the bucket or card being worked
|
|
197
|
+
- choices selected, especially wedge, pricing, and feature direction
|
|
198
|
+
- phase synthesis links returned by LaunchChair
|
|
199
|
+
- exact user checkpoints with links to the relevant LaunchChair page or build-card tab
|
|
200
|
+
|
|
201
|
+
Avoid long explanations of local file searching when LaunchChair MCP tools are available.
|
|
202
|
+
|
|
203
|
+
## Common Commands
|
|
204
|
+
|
|
205
|
+
Run a full project loop:
|
|
206
|
+
|
|
207
|
+
```text
|
|
208
|
+
Call launchchair_continue_project with projectId, maxSteps, and optional stopPhaseKey.
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Create a project from a new idea:
|
|
212
|
+
|
|
213
|
+
```text
|
|
214
|
+
Call launchchair_create_project with projectName, oneLiner, projectType, and ideaSnapshot.
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Find a project by name:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
Call launchchair_list_projects with query set to the user-provided project name.
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Run one queued UI job:
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
Call launchchair_run_once.
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Run one queued UI job from CLI:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
node "$HOME/launchchair-agent/launchchair-cli.mjs" run-once
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Select a generated wedge:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
Call launchchair_select_choice with domain="wedge" and optionTitle or optionId.
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Do Not Do This
|
|
242
|
+
|
|
243
|
+
- Do not brute-force project state by writing arbitrary JSON Patch to `projects.spec_json`.
|
|
244
|
+
- Do not use generic SPEC_UPDATE for user-choice buckets.
|
|
245
|
+
- Do not invent missing Supabase, Vercel, GitHub, or Stripe access.
|
|
246
|
+
- Do not run production SQL without explicit user authorization and a known project ref.
|
|
247
|
+
- Do not mark QA complete without matching evidence. For Feature projects, produce that evidence by running the repository-native or browser checks whenever possible.
|
|
248
|
+
- Do not search the whole filesystem for LaunchChair source when the public docs and MCP tools are available.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Copyright (c) LaunchChair.
|
|
2
|
+
|
|
3
|
+
All rights reserved.
|
|
4
|
+
|
|
5
|
+
This package is provided for use with LaunchChair services. You may install,
|
|
6
|
+
execute, and configure this package to connect authorized local agent clients to
|
|
7
|
+
LaunchChair. No permission is granted to copy, modify, sublicense, sell, or
|
|
8
|
+
redistribute this package except as expressly permitted by LaunchChair.
|
package/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# LaunchChair Agent
|
|
2
|
+
|
|
3
|
+
LaunchChair Agent packages the LaunchChair CLI, MCP bridge, local bridge, and runner scripts used by Codex, Claude Code, ChatGPT, Claude, and Hermes workflows.
|
|
4
|
+
|
|
5
|
+
Use it when you want an agent to create or discover LaunchChair projects, run product phases, continue project loops, pick up `Run with agent` jobs from LaunchChair, or expose LaunchChair tools through MCP.
|
|
6
|
+
|
|
7
|
+
## Install Locally For Testing
|
|
8
|
+
|
|
9
|
+
From this package directory:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm pack
|
|
13
|
+
npm install -g ./launchchair-agent-*.tgz
|
|
14
|
+
launchchair help
|
|
15
|
+
launchchair doctor
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
After publishing:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g @launchchair/agent
|
|
22
|
+
launchchair help
|
|
23
|
+
launchchair doctor
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Login
|
|
27
|
+
|
|
28
|
+
Approve the CLI once in your browser. LaunchChair creates a scoped Agent API token after approval and the CLI stores it locally:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
launchchair login
|
|
32
|
+
launchchair doctor
|
|
33
|
+
launchchair whoami
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Manual fallback when you already have a token from LaunchChair Settings -> API Access:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
launchchair login --token "<full lc_at token from LaunchChair Settings API Access>"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The CLI stores local config at:
|
|
43
|
+
|
|
44
|
+
```text
|
|
45
|
+
~/.launchchair/config.json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Tokens are masked in CLI output. Do not paste real tokens into public docs, screenshots, issue comments, or chat summaries.
|
|
49
|
+
|
|
50
|
+
## Guided Setup
|
|
51
|
+
|
|
52
|
+
Use `setup` as the easiest path. It saves the build folder, writes a small `AGENTS.md` pointer into that folder, starts browser login when no token is saved yet, prints the MCP config for your desktop agent, and tells you exactly what to do next.
|
|
53
|
+
|
|
54
|
+
Codex Desktop:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
launchchair setup codex --cwd /absolute/path/to/repo-or-empty-folder
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Claude Desktop + Claude Code:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
launchchair setup claude --cwd /absolute/path/to/repo-or-empty-folder
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Terminal / CLI agents:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
launchchair setup terminal --cwd /absolute/path/to/repo-or-empty-folder
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
If you do not want browser login during setup, add `--skip-login`. You can also save an existing token during setup:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
launchchair setup codex --cwd . --skip-login
|
|
76
|
+
launchchair setup codex --cwd . --token "<full lc_at token from LaunchChair Settings API Access>"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For a fresh project before a repo exists, point `--cwd` at an empty folder. LaunchChair can run ideation, validation, positioning, pricing, and MVP blueprint before code exists; Stack Setup and Build MVP can later use that folder as the build target.
|
|
80
|
+
|
|
81
|
+
Use `setup terminal` when the agent runs inside a normal shell instead of Codex Desktop or Claude Desktop MCP. It writes the same project-local `AGENTS.md` pointer, saves the build folder, and prints the CLI commands the agent should use: `launchchair projects`, `launchchair create`, `launchchair continue`, `launchchair run-once`, and `launchchair bridge`.
|
|
82
|
+
|
|
83
|
+
## Common Commands
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
launchchair setup codex --cwd /absolute/path/to/repo-or-empty-folder
|
|
87
|
+
launchchair setup claude --cwd /absolute/path/to/repo-or-empty-folder
|
|
88
|
+
launchchair setup terminal --cwd /absolute/path/to/repo-or-empty-folder
|
|
89
|
+
launchchair projects
|
|
90
|
+
launchchair projects --query LeafLore
|
|
91
|
+
launchchair create --name "SignalLift" --one-liner "Distribution workspace for technical founders" --project-type b2b --save-project
|
|
92
|
+
launchchair continue --stop-phase-key market_validation
|
|
93
|
+
launchchair continue --completion-target "rerun Product Ideation until ICP Pain Score is above 85"
|
|
94
|
+
launchchair run-once
|
|
95
|
+
launchchair bridge
|
|
96
|
+
launchchair mcp-config codex
|
|
97
|
+
launchchair mcp-config claude
|
|
98
|
+
launchchair install-agent-md --cwd /absolute/path/to/repo-or-empty-folder
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Feature beta projects can be created and planned from the CLI when the token owner has access:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
launchchair create \
|
|
105
|
+
--name "Retention Engine" \
|
|
106
|
+
--project-kind feature \
|
|
107
|
+
--feature-description "Add account-level lifecycle nudges to an existing SaaS dashboard" \
|
|
108
|
+
--problem "Activated users forget the next valuable action after onboarding" \
|
|
109
|
+
--product-description "Existing B2B SaaS dashboard with auth, billing, and workspace teams" \
|
|
110
|
+
--plan-to-build-board \
|
|
111
|
+
--save-project
|
|
112
|
+
|
|
113
|
+
# Optional: continue from feature planning into Build MVP
|
|
114
|
+
launchchair create \
|
|
115
|
+
--name "Retention Engine" \
|
|
116
|
+
--project-kind feature \
|
|
117
|
+
--feature-description "Add account-level lifecycle nudges to an existing SaaS dashboard" \
|
|
118
|
+
--problem "Activated users forget the next valuable action after onboarding" \
|
|
119
|
+
--full-loop \
|
|
120
|
+
--max-steps 12 \
|
|
121
|
+
--build-max-steps 25
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## MCP Config
|
|
125
|
+
|
|
126
|
+
Print a Codex MCP config example:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
launchchair mcp-config codex
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Print a Claude Desktop MCP config example:
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
launchchair mcp-config claude
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The printed config intentionally uses a token placeholder. Paste your real `lc_at_` token into your MCP client config when the desktop client launches MCP directly, or use `launchchair login` for CLI-started bridge sessions.
|
|
139
|
+
|
|
140
|
+
Desktop MCP clients do not automatically inherit the token saved by `launchchair login` unless they launch through a CLI-managed bridge process. For direct MCP config, put the token in that client config's `env` block.
|
|
141
|
+
|
|
142
|
+
## Local Bridge
|
|
143
|
+
|
|
144
|
+
Use the local bridge when the LaunchChair web app's `Run with agent` button should queue work and your local agent should pick it up:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
launchchair bridge
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
For a one-shot test:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
launchchair run-once
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Runner Targets
|
|
157
|
+
|
|
158
|
+
Codex:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
launchchair config set codex-cwd /absolute/path/to/repo-or-empty-folder
|
|
162
|
+
launchchair bridge
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Claude Code:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
launchchair config set claude-code-cwd /absolute/path/to/repo-or-empty-folder
|
|
169
|
+
LC_AGENT_ROUTER_GPT_TARGET=claude_code LC_AGENT_ROUTER_CODEX_TARGET=claude_code launchchair bridge
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
ChatGPT and Claude API runner fallbacks require provider API keys:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
OPENAI_API_KEY=... launchchair bridge
|
|
176
|
+
ANTHROPIC_API_KEY=... launchchair bridge
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Fresh Agent Chats
|
|
180
|
+
|
|
181
|
+
This package includes `AGENTS.md`. Fresh Codex, Claude, and Hermes chats should read that file before searching an empty repo. Product ideation, market validation, positioning, pricing, MVP blueprint, and project loops are LaunchChair API/MCP tasks. Local repo inspection starts later, usually at Stack Setup or Build MVP.
|
|
182
|
+
|
|
183
|
+
## Docs
|
|
184
|
+
|
|
185
|
+
- CLI docs: https://www.launchchair.io/docs/cli
|
|
186
|
+
- MCP docs: https://www.launchchair.io/docs/mcp
|
|
187
|
+
- Agent API docs: https://www.launchchair.io/docs/agent-api
|
|
188
|
+
- Machine-readable catalog: https://www.launchchair.io/api/agent/v1/docs
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
|
|
5
|
+
function env(name, fallback = "") {
|
|
6
|
+
const value = process.env[name];
|
|
7
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : fallback;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function toInt(value, fallback) {
|
|
11
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
12
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function toFloat(value, fallback) {
|
|
16
|
+
const parsed = Number.parseFloat(String(value ?? ""));
|
|
17
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function stripCodeFence(text) {
|
|
21
|
+
const trimmed = String(text ?? "").trim();
|
|
22
|
+
if (!trimmed.startsWith("```")) {
|
|
23
|
+
return trimmed;
|
|
24
|
+
}
|
|
25
|
+
const lines = trimmed.split("\n");
|
|
26
|
+
if (lines.length < 3) {
|
|
27
|
+
return trimmed;
|
|
28
|
+
}
|
|
29
|
+
const first = lines[0].trim();
|
|
30
|
+
const last = lines[lines.length - 1].trim();
|
|
31
|
+
if (!first.startsWith("```") || last !== "```") {
|
|
32
|
+
return trimmed;
|
|
33
|
+
}
|
|
34
|
+
return lines.slice(1, -1).join("\n").trim();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function tryJsonParse(candidate) {
|
|
38
|
+
try {
|
|
39
|
+
return { ok: true, value: JSON.parse(candidate) };
|
|
40
|
+
} catch {
|
|
41
|
+
return { ok: false, value: null };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function extractFirstJsonValue(text) {
|
|
46
|
+
const raw = String(text ?? "");
|
|
47
|
+
const direct = tryJsonParse(raw.trim());
|
|
48
|
+
if (direct.ok) {
|
|
49
|
+
return direct.value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const unfenced = stripCodeFence(raw);
|
|
53
|
+
const fromFence = tryJsonParse(unfenced);
|
|
54
|
+
if (fromFence.ok) {
|
|
55
|
+
return fromFence.value;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (let start = 0; start < unfenced.length; start += 1) {
|
|
59
|
+
const open = unfenced[start];
|
|
60
|
+
if (open !== "{" && open !== "[") {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const close = open === "{" ? "}" : "]";
|
|
64
|
+
let depth = 0;
|
|
65
|
+
let inString = false;
|
|
66
|
+
let escaped = false;
|
|
67
|
+
|
|
68
|
+
for (let i = start; i < unfenced.length; i += 1) {
|
|
69
|
+
const char = unfenced[i];
|
|
70
|
+
|
|
71
|
+
if (inString) {
|
|
72
|
+
if (escaped) {
|
|
73
|
+
escaped = false;
|
|
74
|
+
} else if (char === "\\") {
|
|
75
|
+
escaped = true;
|
|
76
|
+
} else if (char === "\"") {
|
|
77
|
+
inString = false;
|
|
78
|
+
}
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (char === "\"") {
|
|
83
|
+
inString = true;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (char === open) {
|
|
87
|
+
depth += 1;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (char === close) {
|
|
91
|
+
depth -= 1;
|
|
92
|
+
if (depth === 0) {
|
|
93
|
+
const candidate = unfenced.slice(start, i + 1).trim();
|
|
94
|
+
const parsed = tryJsonParse(candidate);
|
|
95
|
+
if (parsed.ok) {
|
|
96
|
+
return parsed.value;
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function readStdin() {
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
let buffer = "";
|
|
110
|
+
process.stdin.setEncoding("utf8");
|
|
111
|
+
process.stdin.on("data", (chunk) => {
|
|
112
|
+
buffer += chunk;
|
|
113
|
+
});
|
|
114
|
+
process.stdin.on("end", () => resolve(buffer));
|
|
115
|
+
process.stdin.on("error", (error) => reject(error));
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function extractCompletionText(payload) {
|
|
120
|
+
const choices = Array.isArray(payload?.choices) ? payload.choices : [];
|
|
121
|
+
const choice = choices[0] ?? null;
|
|
122
|
+
const content = choice?.message?.content;
|
|
123
|
+
if (typeof content === "string") {
|
|
124
|
+
return content;
|
|
125
|
+
}
|
|
126
|
+
if (Array.isArray(content)) {
|
|
127
|
+
const merged = content
|
|
128
|
+
.map((part) => {
|
|
129
|
+
if (typeof part?.text === "string") {
|
|
130
|
+
return part.text;
|
|
131
|
+
}
|
|
132
|
+
if (typeof part?.content === "string") {
|
|
133
|
+
return part.content;
|
|
134
|
+
}
|
|
135
|
+
return "";
|
|
136
|
+
})
|
|
137
|
+
.join("\n")
|
|
138
|
+
.trim();
|
|
139
|
+
if (merged) {
|
|
140
|
+
return merged;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (typeof payload?.output_text === "string") {
|
|
144
|
+
return payload.output_text;
|
|
145
|
+
}
|
|
146
|
+
throw new Error("OpenAI response did not include assistant text.");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function callOpenAi(input, config) {
|
|
150
|
+
const controller = new AbortController();
|
|
151
|
+
const timeout = setTimeout(() => controller.abort(), config.timeoutMs);
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const response = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
155
|
+
method: "POST",
|
|
156
|
+
headers: {
|
|
157
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
158
|
+
"Content-Type": "application/json",
|
|
159
|
+
},
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
model: config.model,
|
|
162
|
+
temperature: config.temperature,
|
|
163
|
+
max_tokens: config.maxTokens,
|
|
164
|
+
messages: [
|
|
165
|
+
{
|
|
166
|
+
role: "system",
|
|
167
|
+
content:
|
|
168
|
+
"Return exactly one valid JSON value (object or array) and nothing else. No markdown, no prose.",
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
role: "user",
|
|
172
|
+
content: input,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
}),
|
|
176
|
+
signal: controller.signal,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const payload = await response.json().catch(() => ({}));
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
const detail = String(payload?.error?.message ?? payload?.error ?? response.statusText ?? "Request failed");
|
|
182
|
+
throw new Error(`OpenAI request failed (${response.status}): ${detail}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return extractCompletionText(payload);
|
|
186
|
+
} finally {
|
|
187
|
+
clearTimeout(timeout);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function main() {
|
|
192
|
+
const config = {
|
|
193
|
+
apiKey: env("OPENAI_API_KEY"),
|
|
194
|
+
baseUrl: env("OPENAI_BASE_URL", "https://api.openai.com/v1").replace(/\/+$/, ""),
|
|
195
|
+
model: env("LC_AGENT_OPENAI_MODEL", env("OPENAI_MODEL", "gpt-5-mini")),
|
|
196
|
+
temperature: toFloat(process.env.LC_AGENT_OPENAI_TEMPERATURE, 0),
|
|
197
|
+
maxTokens: toInt(process.env.LC_AGENT_OPENAI_MAX_TOKENS, 12000),
|
|
198
|
+
timeoutMs: toInt(process.env.LC_AGENT_OPENAI_TIMEOUT_MS, 180000),
|
|
199
|
+
maxAttempts: toInt(process.env.LC_AGENT_JSON_MAX_ATTEMPTS, 3),
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
if (!config.apiKey) {
|
|
203
|
+
throw new Error("Missing OPENAI_API_KEY for chatgpt-runner.");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const prompt = String(await readStdin()).trim();
|
|
207
|
+
if (!prompt) {
|
|
208
|
+
throw new Error("Runner received an empty prompt.");
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let lastRaw = "";
|
|
212
|
+
for (let attempt = 1; attempt <= config.maxAttempts; attempt += 1) {
|
|
213
|
+
const input =
|
|
214
|
+
attempt === 1
|
|
215
|
+
? prompt
|
|
216
|
+
: [
|
|
217
|
+
"Repair this to strict valid JSON only.",
|
|
218
|
+
"Do not add explanations, markdown, or code fences.",
|
|
219
|
+
"Return exactly one JSON value.",
|
|
220
|
+
"",
|
|
221
|
+
lastRaw,
|
|
222
|
+
].join("\n");
|
|
223
|
+
|
|
224
|
+
lastRaw = await callOpenAi(input, config);
|
|
225
|
+
const parsed = extractFirstJsonValue(lastRaw);
|
|
226
|
+
if (parsed !== null) {
|
|
227
|
+
process.stdout.write(`${JSON.stringify(parsed, null, 2)}\n`);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
throw new Error("Model did not return valid JSON after retry attempts.");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
main().catch((error) => {
|
|
236
|
+
const message = error instanceof Error ? error.message : String(error ?? "chatgpt-runner failed");
|
|
237
|
+
process.stderr.write(`${message}\n`);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
});
|