Keep SoloMap conversations available after each reply.

<img src="https://raw.githubusercontent.com/jobssteve164dev/solopreneur-roadmap/main/resources/logo_with_text.png" width="320" alt="SoloMap Logo" />
<h1 align="center">SoloMap - AI Coding Agent Roadmap</h1>
<strong>English</strong> | <a href="https://github.com/jobssteve164dev/solopreneur-roadmap/blob/main/README.zh-CN.md">简体中文</a>
<strong>Stop losing momentum in scattered AI chats. Turn your local folder into a visual cockpit for autonomous agents.</strong>
<a href="https://solomap.app"><img src="https://img.shields.io/badge/Website-solomap.app-c92f38?style=flat-square" alt="SoloMap website" /></a> <a href="https://marketplace.visualstudio.com/items?itemName=SZLK.solopreneur-roadmap"><img src="https://img.shields.io/badge/VS%20Code-Marketplace-blue?style=flat-square&logo=visual-studio-code" alt="Marketplace" /></a> <a href="https://github.com/jobssteve164dev/solopreneur-roadmap/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-emerald?style=flat-square" alt="License" /></a> <a href="https://github.com/jobssteve164dev/solopreneur-roadmap"><img src="https://img.shields.io/badge/Git-Friendly-purple?style=flat-square&logo=git" alt="Git Friendly" /></a>
SoloMap is a local-first, Git-friendly, highly-polished execution control panel built directly inside VS Code. It does not replace your favorite Agent CLIs (like Claude Code, agy, or Codex) — it orchestrates them.
.solopreneur/roadmap.csv. Every status update or step adjustment is a plain text diff. Zero database lock-in.agy, codex, claude-code, copilot-cli, grok) directly from the active roadmap step..solopreneur/step-memory/ package before each execution so agents know the exact scope and completion criteria.agy for building and claude for read-only review) to get automatic code quality gates.Get started in less than 60 seconds. All you need is a local folder and your favorite Agent CLI.
Cmd/Ctrl + Shift + P) and run SoloMap: Show AI Roadmap..solopreneur/roadmap.csv.SoloMap runs your local CLI agents via VS Code's integrated terminal for maximum transparency. We auto-detect:
agy)codex-cli)claude-code)copilot-cli)opencode)grok)SoloMap creates a .solopreneur/ folder inside your project root to keep it completely self-contained:
.solopreneur/
├── roadmap.csv <- The single source of truth for your roadmap (Git-friendly)
├── step-memory/ <- Context & completion criteria for each roadmap node
└── agent-runs/ <- Minimal live task recovery and report files
.solomap-global/
└── solomap.db <- Shared conversations, run indexes, artifacts, growth data, and memory
For solo founders looking to build a highly reproducible delivery machine, explore our core blueprints:
We are building SoloMap in public for solo developers. Your feedback directly shapes the product!
hooks/register.js 76 lines1async function send($, action, event) {
2 const node = await $.env.get('SOLOMAP_NODE_EXECUTABLE');
3 const checkpoint = await $.env.get('SOLOMAP_TASK_COMMAND');
4 if (!node || !checkpoint) return;
5 for (;;) {
6 try {
7 const result = await $.process.run([node, checkpoint, action], { stdin: JSON.stringify(event), timeoutMs: 600000 });
8 if (result.exitCode === 0) return result;
9 if (result.exitCode === 3) return;
10 } catch {}
11 await $.clock.sleep(250);
12 }
13}
14
15export function register(on) {
16 let pendingUser = '';
17 let active;
18 let sequence = 0;
19 let ready = false;
20 on('session.start', async ($, e, next) => {
21 const result = await next(e);
22 const receipt = await send($, 'native-ready', { schemaVersion: 1, sessionId: await $.session.id() });
23 if (receipt) {
24 ready = true;
25 try {
26 const saved = JSON.parse(receipt.stdout);
27 sequence = Number(saved.nextSequence) - 1;
28 active = saved.resume?.active;
29 pendingUser = saved.resume?.pendingUser || '';
30 } catch { sequence = 0; }
31 }
32 return result;
33 });
34 on('session.append', async ($, e, next) => {
35 if (!e.agentId && e.uuid) {
36 const message = e.message;
37 if (e.door === 'prompt' && message.type === 'user' && !message.isMeta) {
38 await send($, 'native-append', { schemaVersion: 1, kind: 'prompt', sessionId: await $.session.id(), uuid: e.uuid });
39 pendingUser = e.uuid;
40 }
41 if (e.door === 'response' && active && message.type === 'assistant' && message.role === 'assistant') {
42 const turn = active;
43 await send($, 'native-append', { schemaVersion: 1, kind: 'assistant', sessionId: await $.session.id(), uuid: e.uuid, userUuid: turn.userUuid, providerTurnId: turn.providerTurnId });
44 if (!turn.assistantUuids.includes(e.uuid)) turn.assistantUuids.push(e.uuid);
45 }
46 }
47 return next(e);
48 });
49 on('turn.start', async ($, e, next) => {
50 const result = await next(e);
51 if (!e.agentId && pendingUser) {
52 if (!ready) {
53 const receipt = await send($, 'native-ready', { schemaVersion: 1, sessionId: await $.session.id() });
54 if (receipt) {
55 ready = true;
56 try { sequence = Number(JSON.parse(receipt.stdout).nextSequence) - 1; } catch { sequence = 0; }
57 }
58 }
59 active = { providerTurnId: e.turnId, userUuid: pendingUser, assistantUuids: [], startedAt: new Date(await $.clock.now()).toISOString() };
60 pendingUser = '';
61 await send($, 'native-event', { schemaVersion: 1, phase: 'start', sequence: ++sequence, sessionId: await $.session.id(), ...active, completedAt: active.startedAt, reason: 'answer' });
62 }
63 return result;
64 });
65 on('turn.complete', async ($, e, next) => {
66 const result = await next(e);
67 if (!e.agentId && active?.providerTurnId === e.turnId) {
68 const event = { schemaVersion: 1, phase: 'complete', sequence: ++sequence, sessionId: await $.session.id(), ...active,
69 completedAt: new Date(await $.clock.now()).toISOString(), reason: e.isAborted ? 'aborted' : e.reason };
70 active = undefined;
71 await send($, 'native-event', event);
72 }
73 return result;
74 });
75}
76