jeo-code 0.1.0 → 0.4.4
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/README.ja.md +160 -0
- package/README.ko.md +160 -0
- package/README.md +115 -297
- package/README.zh.md +160 -0
- package/package.json +11 -6
- package/scripts/install.sh +28 -28
- package/scripts/uninstall.sh +17 -15
- package/src/AGENTS.md +50 -0
- package/src/agent/AGENTS.md +49 -0
- package/src/agent/bash-fixups.ts +103 -0
- package/src/agent/compaction.ts +410 -19
- package/src/agent/config-schema.ts +119 -5
- package/src/agent/context-files.ts +314 -17
- package/src/agent/dev/AGENTS.md +36 -0
- package/src/agent/dev/advanced-analyzer.ts +12 -0
- package/src/agent/dev/evolution-bridge.ts +82 -0
- package/src/agent/dev/evolution-logger.ts +41 -0
- package/src/agent/dev/self-analysis.ts +64 -0
- package/src/agent/dev/self-improve.ts +24 -0
- package/src/agent/dev/spec-automation.ts +49 -0
- package/src/agent/engine.ts +804 -54
- package/src/agent/hooks.ts +273 -0
- package/src/agent/loop.ts +21 -1
- package/src/agent/memory.ts +201 -0
- package/src/agent/model-recency.ts +32 -0
- package/src/agent/output-minimizer.ts +108 -0
- package/src/agent/output-util.ts +64 -0
- package/src/agent/plan.ts +187 -0
- package/src/agent/seed.ts +52 -0
- package/src/agent/session.ts +235 -21
- package/src/agent/state.ts +286 -39
- package/src/agent/step-budget.ts +232 -0
- package/src/agent/subagents.ts +223 -26
- package/src/agent/task-tool.ts +272 -0
- package/src/agent/todo-tool.ts +87 -0
- package/src/agent/tokenizer.ts +117 -0
- package/src/agent/tool-registry.ts +54 -0
- package/src/agent/tools.ts +562 -103
- package/src/agent/web-search.ts +538 -0
- package/src/ai/AGENTS.md +44 -0
- package/src/ai/index.ts +1 -0
- package/src/ai/model-catalog-compat.ts +3 -1
- package/src/ai/model-catalog.ts +74 -9
- package/src/ai/model-discovery.ts +215 -17
- package/src/ai/model-manager.ts +346 -32
- package/src/ai/model-picker.ts +1 -1
- package/src/ai/model-registry.ts +4 -2
- package/src/ai/pricing.ts +84 -0
- package/src/ai/provider-registry.ts +23 -0
- package/src/ai/provider-status.ts +60 -16
- package/src/ai/providers/AGENTS.md +42 -0
- package/src/ai/providers/anthropic.ts +250 -31
- package/src/ai/providers/antigravity.ts +219 -0
- package/src/ai/providers/errors.ts +15 -1
- package/src/ai/providers/gemini.ts +196 -13
- package/src/ai/providers/ollama.ts +37 -7
- package/src/ai/providers/openai-responses.ts +173 -0
- package/src/ai/providers/openai.ts +64 -12
- package/src/ai/sse.ts +4 -1
- package/src/ai/types.ts +18 -1
- package/src/auth/AGENTS.md +41 -0
- package/src/auth/callback-server.ts +6 -1
- package/src/auth/flows/AGENTS.md +32 -0
- package/src/auth/flows/antigravity.ts +151 -0
- package/src/auth/flows/google-project.ts +190 -0
- package/src/auth/flows/google.ts +39 -18
- package/src/auth/flows/index.ts +15 -5
- package/src/auth/flows/openai.ts +2 -2
- package/src/auth/oauth.ts +8 -0
- package/src/auth/refresh.ts +44 -27
- package/src/auth/storage.ts +149 -26
- package/src/auth/types.ts +1 -1
- package/src/autopilot.ts +362 -0
- package/src/bun-imports.d.ts +4 -0
- package/src/cli/AGENTS.md +39 -0
- package/src/cli/runner.ts +148 -14
- package/src/cli.ts +13 -4
- package/src/commands/AGENTS.md +40 -0
- package/src/commands/approve.ts +62 -3
- package/src/commands/auth.ts +167 -25
- package/src/commands/chat.ts +37 -8
- package/src/commands/deep-interview.ts +633 -175
- package/src/commands/doctor.ts +84 -37
- package/src/commands/evolve-core.ts +18 -0
- package/src/commands/evolve.ts +2 -1
- package/src/commands/export.ts +176 -0
- package/src/commands/gjc.ts +52 -0
- package/src/commands/launch.ts +3549 -240
- package/src/commands/mcp.ts +3 -3
- package/src/commands/ooo-seed.ts +19 -0
- package/src/commands/ralplan.ts +253 -35
- package/src/commands/resume.ts +1 -1
- package/src/commands/session.ts +183 -0
- package/src/commands/setup-helpers.ts +10 -3
- package/src/commands/setup.ts +57 -16
- package/src/commands/skills.ts +78 -18
- package/src/commands/state.ts +198 -0
- package/src/commands/status.ts +84 -0
- package/src/commands/team.ts +340 -212
- package/src/commands/ultragoal.ts +122 -61
- package/src/commands/update.ts +244 -0
- package/src/ledger.ts +270 -0
- package/src/mcp/AGENTS.md +38 -0
- package/src/mcp/server.ts +115 -14
- package/src/mcp/tools.ts +42 -22
- package/src/md-modules.d.ts +4 -0
- package/src/prompts/AGENTS.md +41 -0
- package/src/prompts/agents/AGENTS.md +35 -0
- package/src/prompts/agents/architect.md +35 -0
- package/src/prompts/agents/critic.md +37 -0
- package/src/prompts/agents/executor.md +36 -0
- package/src/prompts/agents/planner.md +37 -0
- package/src/prompts/skills/AGENTS.md +36 -0
- package/src/prompts/skills/deep-dive/AGENTS.md +31 -0
- package/src/prompts/skills/deep-dive/SKILL.md +13 -0
- package/src/prompts/skills/deep-interview/AGENTS.md +31 -0
- package/src/prompts/skills/deep-interview/SKILL.md +12 -0
- package/src/prompts/skills/gjc/AGENTS.md +31 -0
- package/src/prompts/skills/gjc/SKILL.md +15 -0
- package/src/prompts/skills/ralplan/AGENTS.md +31 -0
- package/src/prompts/skills/ralplan/SKILL.md +11 -0
- package/src/prompts/skills/team/AGENTS.md +31 -0
- package/src/prompts/skills/team/SKILL.md +11 -0
- package/src/prompts/skills/ultragoal/AGENTS.md +31 -0
- package/src/prompts/skills/ultragoal/SKILL.md +11 -0
- package/src/skills/AGENTS.md +38 -0
- package/src/skills/catalog.ts +565 -31
- package/src/tui/AGENTS.md +43 -0
- package/src/tui/app.ts +1181 -92
- package/src/tui/components/AGENTS.md +42 -0
- package/src/tui/components/ascii-art.ts +257 -15
- package/src/tui/components/autocomplete.ts +98 -16
- package/src/tui/components/autopilot-status.ts +65 -0
- package/src/tui/components/category-index.ts +49 -0
- package/src/tui/components/code-view.ts +54 -11
- package/src/tui/components/color.ts +171 -2
- package/src/tui/components/config-panel.ts +82 -15
- package/src/tui/components/duration.ts +38 -0
- package/src/tui/components/evolution.ts +3 -3
- package/src/tui/components/footer.ts +91 -42
- package/src/tui/components/forge.ts +426 -31
- package/src/tui/components/hints.ts +54 -0
- package/src/tui/components/hud.ts +73 -0
- package/src/tui/components/index.ts +4 -0
- package/src/tui/components/input-box.ts +150 -0
- package/src/tui/components/layout.ts +11 -3
- package/src/tui/components/live-model-picker.ts +108 -0
- package/src/tui/components/markdown-table.ts +140 -0
- package/src/tui/components/markdown-text.ts +97 -0
- package/src/tui/components/meter.ts +4 -1
- package/src/tui/components/model-picker.ts +3 -2
- package/src/tui/components/provider-picker.ts +3 -2
- package/src/tui/components/section.ts +70 -0
- package/src/tui/components/select-list.ts +40 -10
- package/src/tui/components/skill-picker.ts +25 -0
- package/src/tui/components/slash.ts +244 -21
- package/src/tui/components/status.ts +272 -11
- package/src/tui/components/step-timeline.ts +218 -0
- package/src/tui/components/stream.ts +26 -9
- package/src/tui/components/themes.ts +212 -6
- package/src/tui/components/todo-card.ts +47 -0
- package/src/tui/components/tool-list.ts +58 -12
- package/src/tui/components/transcript.ts +120 -0
- package/src/tui/components/update-box.ts +31 -0
- package/src/tui/components/welcome.ts +162 -0
- package/src/tui/components/width.ts +163 -0
- package/src/tui/monitoring/AGENTS.md +31 -0
- package/src/tui/monitoring/hud-view.ts +55 -0
- package/src/tui/renderer.ts +112 -3
- package/src/tui/terminal.ts +40 -33
- package/src/util/AGENTS.md +39 -0
- package/src/util/clipboard-image.ts +118 -0
- package/src/util/env.ts +12 -0
- package/src/util/provider-error.ts +78 -0
- package/src/util/retry.ts +91 -6
- package/src/util/update-check.ts +64 -0
- package/src/commands/models.ts +0 -104
package/src/commands/doctor.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { readGlobalConfig } from "../agent/state";
|
|
2
2
|
import { resolveCredential, snapshotProvider, type AuthProvider, type Credential } from "../auth";
|
|
3
3
|
import { resolveProvider } from "../ai";
|
|
4
|
+
import { effectiveCredentialForProvider } from "../ai/model-manager";
|
|
4
5
|
import { resolveModelId } from "../ai/model-registry";
|
|
5
6
|
import { meter } from "../tui/components/meter";
|
|
6
7
|
import { size } from "../tui/terminal";
|
|
7
8
|
import chalk from "chalk";
|
|
9
|
+
import { extractChatgptAccountId, CODEX_RESPONSES_URL } from "../ai/providers/openai-responses";
|
|
8
10
|
|
|
9
11
|
interface ProbeResult {
|
|
10
12
|
status: "ok" | "fail" | "skipped";
|
|
@@ -12,7 +14,7 @@ interface ProbeResult {
|
|
|
12
14
|
latencyMs?: number;
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
const APP_NAME = "
|
|
17
|
+
const APP_NAME = "jeo";
|
|
16
18
|
const PROBE_TIMEOUT_MS = 4000;
|
|
17
19
|
|
|
18
20
|
async function timedFetch(url: string, init: RequestInit): Promise<{ res: Response; latencyMs: number }> {
|
|
@@ -27,9 +29,40 @@ async function timedFetch(url: string, init: RequestInit): Promise<{ res: Respon
|
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
|
|
32
|
+
const NO_OPENAI_CREDENTIAL_DETAIL = "no credential (run 'jeo setup' or 'jeo auth login openai')";
|
|
33
|
+
|
|
30
34
|
async function probeOpenAi(credential: Credential, baseUrl: string | undefined): Promise<ProbeResult> {
|
|
35
|
+
if (credential.kind === "none" && !baseUrl) {
|
|
36
|
+
return { status: "skipped", detail: NO_OPENAI_CREDENTIAL_DETAIL };
|
|
37
|
+
}
|
|
38
|
+
// ChatGPT/Codex OAuth can't use api.openai.com — verify the Codex backend instead.
|
|
39
|
+
// A deliberately-unsupported model returns 400 *after* auth but *before* any generation,
|
|
40
|
+
// so it confirms connectivity + credentials without burning subscription credit.
|
|
41
|
+
if (credential.kind === "oauth") {
|
|
42
|
+
const accountId = extractChatgptAccountId(credential.token);
|
|
43
|
+
const headers: Record<string, string> = {
|
|
44
|
+
"content-type": "application/json",
|
|
45
|
+
authorization: `Bearer ${credential.token}`,
|
|
46
|
+
"OpenAI-Beta": "responses=experimental",
|
|
47
|
+
originator: "codex_cli_rs",
|
|
48
|
+
accept: "text/event-stream",
|
|
49
|
+
};
|
|
50
|
+
if (accountId) headers["chatgpt-account-id"] = accountId;
|
|
51
|
+
try {
|
|
52
|
+
const { res, latencyMs } = await timedFetch(CODEX_RESPONSES_URL, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers,
|
|
55
|
+
body: JSON.stringify({ model: "jeo-doctor-probe", input: [{ role: "user", content: [{ type: "input_text", text: "ping" }] }], stream: true, store: false }),
|
|
56
|
+
});
|
|
57
|
+
if (res.ok || res.status === 400) return { status: "ok", detail: "POST codex/responses (Codex backend reachable)", latencyMs };
|
|
58
|
+
if (res.status === 401 || res.status === 403) return { status: "fail", detail: `Codex auth rejected (${res.status})`, latencyMs };
|
|
59
|
+
return { status: "fail", detail: `POST codex/responses ${res.status}`, latencyMs };
|
|
60
|
+
} catch (err) {
|
|
61
|
+
return { status: "fail", detail: `network error: ${(err as Error).message}` };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
31
64
|
const base = (baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
32
|
-
const token = credential.kind === "
|
|
65
|
+
const token = credential.kind === "api_key" ? credential.token : "no-key";
|
|
33
66
|
try {
|
|
34
67
|
const { res, latencyMs } = await timedFetch(`${base}/models`, {
|
|
35
68
|
headers: { Authorization: `Bearer ${token}` },
|
|
@@ -43,18 +76,31 @@ async function probeOpenAi(credential: Credential, baseUrl: string | undefined):
|
|
|
43
76
|
|
|
44
77
|
async function probeGemini(credential: Credential): Promise<ProbeResult> {
|
|
45
78
|
if (credential.kind === "none") {
|
|
46
|
-
return { status: "skipped", detail: "no credential (run '
|
|
79
|
+
return { status: "skipped", detail: "no credential (run 'jeo setup' or 'jeo auth login gemini')" };
|
|
47
80
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
81
|
+
// OAuth tokens are served via Cloud Code Assist (the REAL call path) — probe
|
|
82
|
+
// loadCodeAssist there; the public generativelanguage list rejects them.
|
|
83
|
+
if (credential.kind === "oauth") {
|
|
84
|
+
const { getGeminiCliHeaders } = await import("../ai/providers/gemini");
|
|
85
|
+
try {
|
|
86
|
+
const { res, latencyMs } = await timedFetch("https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist", {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: {
|
|
89
|
+
authorization: `Bearer ${credential.token}`,
|
|
90
|
+
"content-type": "application/json",
|
|
91
|
+
...getGeminiCliHeaders(),
|
|
92
|
+
},
|
|
93
|
+
body: JSON.stringify({ metadata: { ideType: "IDE_UNSPECIFIED", platform: "PLATFORM_UNSPECIFIED", pluginType: "GEMINI" } }),
|
|
94
|
+
});
|
|
95
|
+
if (res.ok) return { status: "ok", detail: "POST cloudcode-pa /v1internal:loadCodeAssist 200 (Cloud Code Assist)", latencyMs };
|
|
96
|
+
return { status: "fail", detail: `POST cloudcode-pa /v1internal:loadCodeAssist ${res.status}`, latencyMs };
|
|
97
|
+
} catch (err) {
|
|
98
|
+
return { status: "fail", detail: `network error: ${(err as Error).message}` };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models?key=${credential.token}`;
|
|
52
102
|
try {
|
|
53
|
-
const { res, latencyMs } = await timedFetch(url, {
|
|
54
|
-
headers: credential.kind === "oauth"
|
|
55
|
-
? { authorization: `Bearer ${credential.token}` }
|
|
56
|
-
: {},
|
|
57
|
-
});
|
|
103
|
+
const { res, latencyMs } = await timedFetch(url, { headers: {} });
|
|
58
104
|
if (res.ok) return { status: "ok", detail: "GET /v1beta/models 200", latencyMs };
|
|
59
105
|
return { status: "fail", detail: `GET /v1beta/models ${res.status}`, latencyMs };
|
|
60
106
|
} catch (err) {
|
|
@@ -64,14 +110,12 @@ async function probeGemini(credential: Credential): Promise<ProbeResult> {
|
|
|
64
110
|
|
|
65
111
|
async function probeAnthropic(credential: Credential): Promise<ProbeResult> {
|
|
66
112
|
if (credential.kind === "none") {
|
|
67
|
-
return { status: "skipped", detail: "no credential (run '
|
|
68
|
-
}
|
|
69
|
-
//
|
|
70
|
-
// 1-token
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
"anthropic-version": "2023-06-01",
|
|
74
|
-
};
|
|
113
|
+
return { status: "skipped", detail: "no credential (run 'jeo setup' or 'jeo auth login anthropic')" };
|
|
114
|
+
}
|
|
115
|
+
// GET /v1/models verifies auth without burning credit and without depending on a
|
|
116
|
+
// (possibly retired) model id — the old 1-token POST 404'd whenever the probe model
|
|
117
|
+
// was deprecated.
|
|
118
|
+
const headers: Record<string, string> = { "anthropic-version": "2023-06-01" };
|
|
75
119
|
if (credential.kind === "oauth") {
|
|
76
120
|
headers.authorization = `Bearer ${credential.token}`;
|
|
77
121
|
headers["anthropic-beta"] = "oauth-2025-04-20";
|
|
@@ -79,20 +123,12 @@ async function probeAnthropic(credential: Credential): Promise<ProbeResult> {
|
|
|
79
123
|
headers["x-api-key"] = credential.token;
|
|
80
124
|
}
|
|
81
125
|
try {
|
|
82
|
-
const { res, latencyMs } = await timedFetch("https://api.anthropic.com/v1/
|
|
83
|
-
|
|
84
|
-
headers,
|
|
85
|
-
body: JSON.stringify({
|
|
86
|
-
model: "claude-3-5-sonnet-20241022",
|
|
87
|
-
max_tokens: 1,
|
|
88
|
-
messages: [{ role: "user", content: "ping" }],
|
|
89
|
-
}),
|
|
90
|
-
});
|
|
91
|
-
if (res.ok) return { status: "ok", detail: "POST /v1/messages 200 (1-token probe)", latencyMs };
|
|
126
|
+
const { res, latencyMs } = await timedFetch("https://api.anthropic.com/v1/models?limit=1", { headers });
|
|
127
|
+
if (res.ok) return { status: "ok", detail: "GET /v1/models 200", latencyMs };
|
|
92
128
|
if (res.status === 401 || res.status === 403) {
|
|
93
129
|
return { status: "fail", detail: `auth rejected (${res.status})`, latencyMs };
|
|
94
130
|
}
|
|
95
|
-
return { status: "fail", detail: `
|
|
131
|
+
return { status: "fail", detail: `GET /v1/models ${res.status}`, latencyMs };
|
|
96
132
|
} catch (err) {
|
|
97
133
|
return { status: "fail", detail: `network error: ${(err as Error).message}` };
|
|
98
134
|
}
|
|
@@ -139,11 +175,22 @@ export async function runDoctorCommand(args: string[] = []): Promise<void> {
|
|
|
139
175
|
const cloud = ["anthropic", "openai", "gemini"] as AuthProvider[];
|
|
140
176
|
const cloudProbes = await Promise.all(
|
|
141
177
|
cloud.map(async provider => {
|
|
142
|
-
const
|
|
178
|
+
const rawCredential = await resolveCredential(provider);
|
|
179
|
+
let credential = rawCredential;
|
|
143
180
|
let result: ProbeResult;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
181
|
+
try {
|
|
182
|
+
credential = effectiveCredentialForProvider(
|
|
183
|
+
provider,
|
|
184
|
+
rawCredential,
|
|
185
|
+
config,
|
|
186
|
+
provider === defaultProvider ? config.defaultModel : provider,
|
|
187
|
+
);
|
|
188
|
+
if (provider === "openai") result = await probeOpenAi(credential, config.openaiBaseUrl);
|
|
189
|
+
else if (provider === "gemini") result = await probeGemini(credential);
|
|
190
|
+
else result = await probeAnthropic(credential);
|
|
191
|
+
} catch (err) {
|
|
192
|
+
result = { status: "fail", detail: (err as Error).message };
|
|
193
|
+
}
|
|
147
194
|
return { name: provider, credKind: credential.kind, result };
|
|
148
195
|
})
|
|
149
196
|
);
|
|
@@ -200,7 +247,7 @@ export async function runDoctorCommand(args: string[] = []): Promise<void> {
|
|
|
200
247
|
console.log("");
|
|
201
248
|
console.log(`Bun runtime: v${Bun.version}`);
|
|
202
249
|
console.log(`Default model: ${config.defaultModel}${resolvedModel !== config.defaultModel ? ` → ${resolvedModel}` : ""} → ${defaultProvider}`);
|
|
203
|
-
console.log(`Config: ${process.env.HOME}/.
|
|
250
|
+
console.log(`Config: ${process.env.HOME}/.jeo/config.json`);
|
|
204
251
|
if (config.openaiBaseUrl) console.log(`OpenAI base: ${config.openaiBaseUrl}`);
|
|
205
252
|
console.log(`Ollama base: ${ollamaBase}`);
|
|
206
253
|
|
|
@@ -236,7 +283,7 @@ export async function runDoctorCommand(args: string[] = []): Promise<void> {
|
|
|
236
283
|
} else if (defaultProbe?.result.status === "skipped") {
|
|
237
284
|
console.log(
|
|
238
285
|
`${chalk.red("[NOT READY]")} Default model '${config.defaultModel}' resolves to '${defaultProvider}', ` +
|
|
239
|
-
`but no credential is configured. Run '
|
|
286
|
+
`but no credential is configured. Run 'jeo setup' or 'jeo auth login ${defaultProvider}'.`
|
|
240
287
|
);
|
|
241
288
|
} else {
|
|
242
289
|
console.log(
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { consultGjcForEvolution } from "../agent/dev/evolution-bridge";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* jeo evolve-core: Triggers a self-evolution turn using gjc as a guide.
|
|
6
|
+
*/
|
|
7
|
+
export async function runEvolveCoreCommand(args: string[]): Promise<void> {
|
|
8
|
+
const cwd = process.cwd();
|
|
9
|
+
console.log("\n=== " + chalk.bold.magenta("jeo") + " Autonomous Core Evolution ===");
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
await consultGjcForEvolution(cwd);
|
|
13
|
+
console.log("\n" + chalk.green("✔") + " Evolution turn completed.");
|
|
14
|
+
} catch (err: any) {
|
|
15
|
+
console.error("\n" + chalk.red("✘") + " Evolution turn failed: " + err.message);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/commands/evolve.ts
CHANGED
|
@@ -114,7 +114,7 @@ function stageModelJson() {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
|
-
* Preview the
|
|
117
|
+
* Preview the jeo "evolution" TUI identity: render the five stages (or one stage
|
|
118
118
|
* for a given step) with art, evolution track, and a stage meter. Supports
|
|
119
119
|
* theming, truecolor gradients, ASCII-only fallback, terminal-width fitting,
|
|
120
120
|
* frame-loop animation, and machine-readable `--json` / `--list` output.
|
|
@@ -162,6 +162,7 @@ export async function runEvolveCommand(args: string[], opts: EvolveOptions = {})
|
|
|
162
162
|
color: useColor,
|
|
163
163
|
width: artWidth,
|
|
164
164
|
height: stageHeight(),
|
|
165
|
+
unicode,
|
|
165
166
|
...(gradientOn ? { gradient: themeGradient(theme, index), colorLevel } : {}),
|
|
166
167
|
};
|
|
167
168
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { exportSession, latestSessionId, loadSession } from "../agent/session";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
|
|
5
|
+
export function renderSessionHtml(
|
|
6
|
+
meta: { id: string; timestamp?: string; [key: string]: any },
|
|
7
|
+
messages: { role: string; content: string }[]
|
|
8
|
+
): string {
|
|
9
|
+
const escapeHtml = (text: string): string => {
|
|
10
|
+
return text
|
|
11
|
+
.replace(/&/g, "&")
|
|
12
|
+
.replace(/</g, "<")
|
|
13
|
+
.replace(/>/g, ">")
|
|
14
|
+
.replace(/"/g, """)
|
|
15
|
+
.replace(/'/g, "'");
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const messageHtml = messages
|
|
19
|
+
.map(m => {
|
|
20
|
+
const roleClass = (m.role === "system" || m.role === "user" || m.role === "assistant") ? m.role : "other";
|
|
21
|
+
return ` <div class="message ${roleClass}">
|
|
22
|
+
<div class="role-label">${escapeHtml(m.role)}</div>
|
|
23
|
+
<pre>${escapeHtml(m.content)}</pre>
|
|
24
|
+
</div>`;
|
|
25
|
+
})
|
|
26
|
+
.join("\n");
|
|
27
|
+
|
|
28
|
+
return `<!DOCTYPE html>
|
|
29
|
+
<html lang="en">
|
|
30
|
+
<head>
|
|
31
|
+
<meta charset="UTF-8">
|
|
32
|
+
<title>Session ${escapeHtml(meta.id)}</title>
|
|
33
|
+
<style>
|
|
34
|
+
body {
|
|
35
|
+
background-color: #121212;
|
|
36
|
+
color: #e0e0e0;
|
|
37
|
+
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
|
38
|
+
padding: 24px;
|
|
39
|
+
margin: 0;
|
|
40
|
+
line-height: 1.5;
|
|
41
|
+
}
|
|
42
|
+
.header {
|
|
43
|
+
margin-bottom: 24px;
|
|
44
|
+
border-bottom: 1px solid #333;
|
|
45
|
+
padding-bottom: 16px;
|
|
46
|
+
}
|
|
47
|
+
.header h1 {
|
|
48
|
+
margin: 0 0 8px 0;
|
|
49
|
+
font-size: 24px;
|
|
50
|
+
color: #ffffff;
|
|
51
|
+
}
|
|
52
|
+
.header p {
|
|
53
|
+
margin: 0;
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
color: #888888;
|
|
56
|
+
}
|
|
57
|
+
.message {
|
|
58
|
+
margin: 16px 0;
|
|
59
|
+
padding: 16px;
|
|
60
|
+
border-radius: 6px;
|
|
61
|
+
border-left: 4px solid #555;
|
|
62
|
+
}
|
|
63
|
+
.message.system {
|
|
64
|
+
background-color: #1c1c1c;
|
|
65
|
+
border-left-color: #888888;
|
|
66
|
+
}
|
|
67
|
+
.message.user {
|
|
68
|
+
background-color: #172a3a;
|
|
69
|
+
border-left-color: #3b82f6;
|
|
70
|
+
}
|
|
71
|
+
.message.assistant {
|
|
72
|
+
background-color: #143224;
|
|
73
|
+
border-left-color: #10b981;
|
|
74
|
+
}
|
|
75
|
+
.role-label {
|
|
76
|
+
font-weight: bold;
|
|
77
|
+
text-transform: uppercase;
|
|
78
|
+
font-size: 12px;
|
|
79
|
+
margin-bottom: 8px;
|
|
80
|
+
letter-spacing: 0.5px;
|
|
81
|
+
}
|
|
82
|
+
.message.system .role-label {
|
|
83
|
+
color: #aaaaaa;
|
|
84
|
+
}
|
|
85
|
+
.message.user .role-label {
|
|
86
|
+
color: #60a5fa;
|
|
87
|
+
}
|
|
88
|
+
.message.assistant .role-label {
|
|
89
|
+
color: #34d399;
|
|
90
|
+
}
|
|
91
|
+
pre {
|
|
92
|
+
margin: 0;
|
|
93
|
+
white-space: pre-wrap;
|
|
94
|
+
word-break: break-all;
|
|
95
|
+
font-family: inherit;
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
98
|
+
</head>
|
|
99
|
+
<body>
|
|
100
|
+
<div class="header">
|
|
101
|
+
<h1>Session: ${escapeHtml(meta.id)}</h1>
|
|
102
|
+
${meta.timestamp ? `<p>Timestamp: ${escapeHtml(meta.timestamp)}</p>` : ""}
|
|
103
|
+
</div>
|
|
104
|
+
${messageHtml}
|
|
105
|
+
</body>
|
|
106
|
+
</html>`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* `jeo export [id] [--json] [--system] [--html] [--out <path>]` — print a saved session transcript
|
|
111
|
+
* (Markdown by default; `--json` for structured; `--system` to include system
|
|
112
|
+
* messages). Defaults to the latest session when no id is given.
|
|
113
|
+
*/
|
|
114
|
+
export async function runExportCommand(args: string[] = []): Promise<void> {
|
|
115
|
+
const htmlMode = args.includes("--html");
|
|
116
|
+
const jsonMode = args.includes("--json");
|
|
117
|
+
const includeSystem = args.includes("--system");
|
|
118
|
+
|
|
119
|
+
if (htmlMode && jsonMode) {
|
|
120
|
+
console.error("Error: --html and --json options are mutually exclusive.");
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Parse --out <path>
|
|
126
|
+
let outPath: string | undefined;
|
|
127
|
+
const outIdx = args.indexOf("--out");
|
|
128
|
+
if (outIdx !== -1 && outIdx + 1 < args.length) {
|
|
129
|
+
outPath = args[outIdx + 1];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Find the session ID: first non-flag argument that is not the value after --out
|
|
133
|
+
let id: string | undefined;
|
|
134
|
+
for (let i = 0; i < args.length; i++) {
|
|
135
|
+
const arg = args[i];
|
|
136
|
+
if (arg.startsWith("--")) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (outIdx !== -1 && i === outIdx + 1) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
id = arg;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!id) {
|
|
147
|
+
id = await latestSessionId();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!id) {
|
|
151
|
+
console.log("No session to export. Pass a session id or run a session first.");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (htmlMode) {
|
|
156
|
+
try {
|
|
157
|
+
const { header, messages } = await loadSession(id, process.cwd());
|
|
158
|
+
const picked = includeSystem ? messages : messages.filter(m => m.role !== "system");
|
|
159
|
+
const html = renderSessionHtml(header, picked);
|
|
160
|
+
const resolvedOutPath = outPath
|
|
161
|
+
? path.resolve(process.cwd(), outPath)
|
|
162
|
+
: path.join(process.cwd(), `jeo-session-${id}.html`);
|
|
163
|
+
await fs.writeFile(resolvedOutPath, html, "utf8");
|
|
164
|
+
console.log(resolvedOutPath);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
console.log(`Could not export session ${id}: ${(err as Error).message}`);
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
const format: "markdown" | "json" = jsonMode ? "json" : "markdown";
|
|
170
|
+
try {
|
|
171
|
+
console.log(await exportSession(id, format, process.cwd(), { includeSystem }));
|
|
172
|
+
} catch (err) {
|
|
173
|
+
console.log(`Could not export session ${id}: ${(err as Error).message}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { runAgentLoop, executorSystemPrompt, DEFAULT_TOOLS } from "../agent/engine";
|
|
2
|
+
import { loadSkills, buildSkillTask, getSkillFrom } from "../skills/catalog";
|
|
3
|
+
import { readGlobalConfig, isDevMode } from "../agent/state";
|
|
4
|
+
import { runPostImplementationHooks } from "../agent/hooks";
|
|
5
|
+
|
|
6
|
+
export async function runGjcCommand(args: string[]): Promise<void> {
|
|
7
|
+
const intent = args.join(" ").trim();
|
|
8
|
+
const config = await readGlobalConfig();
|
|
9
|
+
|
|
10
|
+
if (intent.toLowerCase().includes("self-improve") && !isDevMode()) {
|
|
11
|
+
console.error("Error: Self-improvement tasks are only allowed in JEO_DEV_MODE=1.");
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const model = config.defaultModel || "fast";
|
|
16
|
+
const skills = await loadSkills();
|
|
17
|
+
const gjcSkill = getSkillFrom(skills, "gjc");
|
|
18
|
+
|
|
19
|
+
if (!gjcSkill) {
|
|
20
|
+
console.error("Error: gjc skill not found.");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Correct signature: executorSystemPrompt(role?, protocol?, verificationDirective?)
|
|
25
|
+
const systemPrompt = executorSystemPrompt();
|
|
26
|
+
const task = buildSkillTask(gjcSkill, intent);
|
|
27
|
+
|
|
28
|
+
await runAgentLoop([{ role: "user", content: task }], {
|
|
29
|
+
cwd: process.cwd(),
|
|
30
|
+
systemPrompt,
|
|
31
|
+
model,
|
|
32
|
+
tools: DEFAULT_TOOLS,
|
|
33
|
+
maxSteps: 50,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
console.log("\n[jeo] Verifying implementation...");
|
|
37
|
+
const verify = await runPostImplementationHooks(process.cwd(), intent);
|
|
38
|
+
|
|
39
|
+
if (!verify.success) {
|
|
40
|
+
console.error("\n[jeo] Verification FAILED. Auto-repairing...");
|
|
41
|
+
const repairTask = `Previous implementation failed verification.\nErrors:\n${verify.output}\n\nPlease fix.`;
|
|
42
|
+
await runAgentLoop([{ role: "user", content: repairTask }], {
|
|
43
|
+
cwd: process.cwd(),
|
|
44
|
+
systemPrompt,
|
|
45
|
+
model,
|
|
46
|
+
tools: DEFAULT_TOOLS,
|
|
47
|
+
maxSteps: 30,
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
console.log("\n[jeo] Verification SUCCESSFUL.");
|
|
51
|
+
}
|
|
52
|
+
}
|