glitool 2.0.1 → 2.0.3
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/dist/agent.js +30 -8
- package/dist/agents/coder.js +8 -1
- package/dist/agents/explainer.js +11 -7
- package/dist/agents/judge.js +2 -2
- package/dist/agents/planner.js +23 -8
- package/dist/agents/planningAgent.js +19 -4
- package/dist/agents/reviewer-agent.js +12 -16
- package/dist/index.js +4 -3
- package/dist/llm/classifier.js +8 -1
- package/dist/memory.js +8 -1
- package/dist/projectMemory.js +24 -9
- package/dist/ui/App.js +25 -5
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -26,10 +26,17 @@ const __dirname = dirname(__filename);
|
|
|
26
26
|
loadEnv({ path: join(os.homedir(), '.glitool', '.env') });
|
|
27
27
|
const MAX_HISTORY_CHARS = 60_000;
|
|
28
28
|
// const simpleLlm = makeLlm('meta-llama/Llama-3.3-70B-Instruct-Turbo');
|
|
29
|
-
|
|
29
|
+
// const simpleLlm = makeLlm('meta-llama/Llama-3.3-70B-Instruct-Turbo');
|
|
30
30
|
function createLlm(model) {
|
|
31
31
|
return makeLlm(model);
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Lazy default LLM. Fresh instance each call so it picks up the
|
|
35
|
+
* current request_id set by startNewRequest() at the top of chat().
|
|
36
|
+
*/
|
|
37
|
+
export function getDefaultLlm() {
|
|
38
|
+
return createLlm('meta-llama/Llama-3.3-70B-Instruct-Turbo');
|
|
39
|
+
}
|
|
33
40
|
// const config = loadConfig();
|
|
34
41
|
const tools = [listFilesTool, readFileTool, searchCodeTool, writeFileTool, editFileTool, bashTool, readBackgroundOutputTool, webFetchTool];
|
|
35
42
|
process.on('exit', cleanupAll);
|
|
@@ -48,18 +55,33 @@ function buildSystemPrompt() {
|
|
|
48
55
|
if (!summary) {
|
|
49
56
|
const rawSession = loadSession();
|
|
50
57
|
if (rawSession.length > 4) {
|
|
51
|
-
generateAndSaveSummary(rawSession,
|
|
58
|
+
generateAndSaveSummary(rawSession, getDefaultLlm());
|
|
52
59
|
summary = loadSummary();
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
|
-
let prompt = `You are an
|
|
62
|
+
let prompt = `You are Glitool — an AI coding assistant running in the user's terminal.
|
|
63
|
+
|
|
64
|
+
You can:
|
|
65
|
+
- Have a normal conversation (just respond, no tools needed)
|
|
66
|
+
- Answer programming and general questions
|
|
67
|
+
- Read, write, edit, and search files in the user's project
|
|
68
|
+
- Run shell commands, fetch web pages
|
|
69
|
+
- Plan, review, refactor, debug code, and help with git
|
|
70
|
+
|
|
71
|
+
Be concise. Default to plain conversation. Only call tools when the request clearly needs them.
|
|
72
|
+
|
|
73
|
+
When the user asks to read, show, or display a specific file → call readFile.
|
|
74
|
+
For "read <name>" shorthand, pass the bare name; the tool searches the project automatically.
|
|
75
|
+
Don't claim a file is missing without verifying via listFiles or readFile first.
|
|
56
76
|
|
|
57
|
-
|
|
58
|
-
- When the user asks to read, show, view, or display a file, you MUST call the readFile tool. NEVER answer from memory or guess at file contents.
|
|
59
|
-
- When the user asks if a file exists, you MUST call listFiles or readFile to verify. NEVER claim a file is missing without checking.
|
|
60
|
-
- For "read <name>" prompts, call readFile with the bare name — the tool will search the project automatically.
|
|
77
|
+
If any tool returns USER_CANCELLED, stop immediately and tell the user. Never retry a cancelled operation.
|
|
61
78
|
|
|
62
|
-
|
|
79
|
+
Style:
|
|
80
|
+
- No preamble like "Sure!", "Of course!", "I'd be happy to..."
|
|
81
|
+
- Code blocks use language tags: \`\`\`ts, \`\`\`py, \`\`\`bash
|
|
82
|
+
- File references use backticks: \`src/auth.ts:42\`
|
|
83
|
+
- The user's current working directory IS the project they're working on — file paths are relative to it.
|
|
84
|
+
`;
|
|
63
85
|
if (summary) {
|
|
64
86
|
const capped = summary.length > MAX_SUMMARY_CHARS
|
|
65
87
|
? summary.slice(0, MAX_SUMMARY_CHARS) + '\n…[summary truncated]'
|
package/dist/agents/coder.js
CHANGED
|
@@ -22,7 +22,14 @@ GROUNDING RULES — these are not optional:
|
|
|
22
22
|
6. Maximum 5 file reads per task. If you need more, you're doing it wrong — use searchCode instead.
|
|
23
23
|
7. If you can't safely complete the task, STOP and return a failure message. Do not invent.
|
|
24
24
|
|
|
25
|
-
Be surgical, not exhaustive. Most tasks need 2-4 tool calls, not 15. The validator will catch broken output — you don't need to over-verify
|
|
25
|
+
Be surgical, not exhaustive. Most tasks need 2-4 tool calls, not 15. The validator will catch broken output — you don't need to over-verify.
|
|
26
|
+
|
|
27
|
+
Response style:
|
|
28
|
+
- Your final text should be 1-3 sentences summarizing what files you changed and why.
|
|
29
|
+
- Do NOT paste file contents in the response — the files are on disk; the user can read them.
|
|
30
|
+
- The validator runs tsc + ESLint after you finish — no need to verify those yourself.
|
|
31
|
+
- If a step is impossible (binary file, command blocked, etc.), say so explicitly and stop.
|
|
32
|
+
`)
|
|
26
33
|
});
|
|
27
34
|
const stream = await coderAgent.stream({ messages: [new HumanMessage(`Plan to execute:\n${plan}\n\nOriginal request: ${userMessage}`)] }, { recursionLimit: 60, streamMode: 'updates' });
|
|
28
35
|
let result = '';
|
package/dist/agents/explainer.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { makeLlm } from '../llm/factory.js';
|
|
2
2
|
import { SystemMessage, HumanMessage } from "@langchain/core/messages";
|
|
3
|
-
const explainerLlm = makeLlm('meta-llama/Llama-3.3-70B-Instruct-Turbo');
|
|
4
3
|
export async function explainResponse(response) {
|
|
5
4
|
if (!response || response.length < 50)
|
|
6
5
|
return '';
|
|
6
|
+
const explainerLlm = makeLlm('meta-llama/Llama-3.3-70B-Instruct-Turbo');
|
|
7
7
|
const result = await explainerLlm.invoke([
|
|
8
|
-
new SystemMessage(`You are a coding teacher.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
new SystemMessage(`You are a friendly coding teacher.
|
|
9
|
+
|
|
10
|
+
Given what an AI coding assistant just did (described below), explain it in 2-4 simple sentences that a beginner would understand.
|
|
11
|
+
|
|
12
|
+
Focus on:
|
|
13
|
+
- WHAT changed and WHY
|
|
14
|
+
- WHICH concept was used (e.g., "promise chaining", "dependency injection")
|
|
15
|
+
- ONE thing to learn next
|
|
16
|
+
|
|
17
|
+
Tone: encouraging, plain language, define any jargon used.`),
|
|
14
18
|
new HumanMessage(`What was just done:\n${response}`)
|
|
15
19
|
]);
|
|
16
20
|
return result.content;
|
package/dist/agents/judge.js
CHANGED
|
@@ -23,7 +23,7 @@ IMPORTANT — how the coder works:
|
|
|
23
23
|
Return JSON only:
|
|
24
24
|
{
|
|
25
25
|
"verdict": "ok" or "fail",
|
|
26
|
-
|
|
26
|
+
"failure_point": "plan" | "workflow" | "execution" | "final_output" | null,
|
|
27
27
|
"failure_step_id": number or null,
|
|
28
28
|
"reason": "short explanation",
|
|
29
29
|
"fix_hint": "specific instruction to fix the problem, empty if ok",
|
|
@@ -34,7 +34,7 @@ Return JSON only:
|
|
|
34
34
|
failure_point meanings:
|
|
35
35
|
- "plan": the plan itself was wrong or incomplete — needs replanning
|
|
36
36
|
- "workflow": wrong execution order caused the failure
|
|
37
|
-
- "
|
|
37
|
+
- "execution": a specific step failed — use failure_step_id
|
|
38
38
|
- "final_output": code runs but doesn't meet the requirement
|
|
39
39
|
- null: everything ok, verdict must be ok`),
|
|
40
40
|
new HumanMessage(`User request: ${input.userMessage}\n\n` +
|
package/dist/agents/planner.js
CHANGED
|
@@ -5,15 +5,30 @@ export async function runPlanner(userMessage, context, model) {
|
|
|
5
5
|
const response = await llm.invoke([
|
|
6
6
|
new SystemMessage(`You are a coding task planner. Output a structured JSON plan.
|
|
7
7
|
|
|
8
|
+
Output exactly one of:
|
|
9
|
+
|
|
10
|
+
A) The literal text: SIMPLE
|
|
11
|
+
(use when the task is just a question, explanation, or chat — no file changes or code execution needed)
|
|
12
|
+
|
|
13
|
+
B) A JSON array of 3-6 steps:
|
|
14
|
+
[
|
|
15
|
+
{ "id": 1, "action": "read", "target": "src/auth.ts", "depends_on": [], "why": "understand current login flow" },
|
|
16
|
+
{ "id": 2, "action": "edit", "target": "src/auth.ts", "depends_on": [1], "why": "add refresh-token check before validate" },
|
|
17
|
+
{ "id": 3, "action": "run", "target": "npx tsc --noEmit","depends_on": [2], "why": "verify it compiles" }
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
Action values:
|
|
21
|
+
- "read": open a file to understand it
|
|
22
|
+
- "edit": modify an existing file
|
|
23
|
+
- "create": make a new file
|
|
24
|
+
- "run": execute a shell command (tsc, npm, git, etc.)
|
|
25
|
+
- "search": grep the codebase
|
|
26
|
+
|
|
8
27
|
Rules:
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
- Be specific about
|
|
13
|
-
- Do NOT write any code — only plan the steps
|
|
14
|
-
- Keep it to 3-6 steps maximum
|
|
15
|
-
- Steps that can run independently should have no shared depends_on
|
|
16
|
-
- depends_on contains the ids of steps that must finish before this one`),
|
|
28
|
+
- Output ONLY the SIMPLE literal OR the JSON array. No prose, no markdown, no code fences.
|
|
29
|
+
- Steps that can run in parallel: leave depends_on empty.
|
|
30
|
+
- Sequential dependencies: list step ids in depends_on.
|
|
31
|
+
- Be specific about file paths and command names — vague targets cause failures.`),
|
|
17
32
|
new HumanMessage(`Context:\n${context}\n\nUser request: ${userMessage}`)
|
|
18
33
|
]);
|
|
19
34
|
const content = response.content.trim();
|
|
@@ -5,10 +5,26 @@ import { join } from "path";
|
|
|
5
5
|
const PLAN_FILE = "plan.md";
|
|
6
6
|
const BLOCKED_EXTENSIONS = ['.ts', '.js', '.tsx', '.jsx', '.py', '.go', '.rs'];
|
|
7
7
|
export async function runPlanningAgent(userMessage, onUsage) {
|
|
8
|
-
const llm = makeLlm('
|
|
8
|
+
const llm = makeLlm('openai/gpt-oss-120b');
|
|
9
9
|
const planPath = join(process.cwd(), PLAN_FILE);
|
|
10
10
|
const existingPlan = existsSync(planPath) ? readFileSync(planPath, 'utf-8') : null;
|
|
11
|
-
const systemPrompt = existingPlan
|
|
11
|
+
const systemPrompt = existingPlan
|
|
12
|
+
? `You are a planning assistant. The user has an existing plan.md and wants to update it.
|
|
13
|
+
|
|
14
|
+
You receive: (1) the current plan, (2) the user's change request.
|
|
15
|
+
|
|
16
|
+
OUTPUT:
|
|
17
|
+
- The FULL updated plan in Markdown — this overwrites plan.md.
|
|
18
|
+
- After the plan, write exactly "---" on its own line.
|
|
19
|
+
- Then 1-3 bullets summarising what changed.
|
|
20
|
+
|
|
21
|
+
RULES:
|
|
22
|
+
- Preserve sections the user didn't ask to change.
|
|
23
|
+
- Apply requested changes precisely; don't expand scope.
|
|
24
|
+
- If the request is ambiguous, ask ONE clarifying question instead of guessing.
|
|
25
|
+
- Never delete existing sections unless the user explicitly says to.
|
|
26
|
+
- Avoid writing implementation code; this is planning, not execution.`
|
|
27
|
+
: `You are a planning assistant. Create a clear structured plan based on user's request.
|
|
12
28
|
|
|
13
29
|
BEFORE writing a plan:
|
|
14
30
|
- Look at the user's request and identify the key feature names, file paths, or concepts mentioned.
|
|
@@ -21,8 +37,7 @@ Rules:
|
|
|
21
37
|
- Use Markdown with clear sections and numbered steps
|
|
22
38
|
- Be specific: name files, components, decisions, trade-offs
|
|
23
39
|
- If the request is vague, prefer asking 1-2 clarifying questions over guessing
|
|
24
|
-
- After the plan, write exactly "---" on its own line, then 1-3 bullet points summarising what you created
|
|
25
|
-
`;
|
|
40
|
+
- After the plan, write exactly "---" on its own line, then 1-3 bullet points summarising what you created`;
|
|
26
41
|
const userContent = existingPlan ? `Current plan:\n\n${existingPlan}\n\nUser request:${userMessage}` : userMessage;
|
|
27
42
|
const response = await llm.invoke([
|
|
28
43
|
new SystemMessage(systemPrompt),
|
|
@@ -27,33 +27,29 @@ Workflow:
|
|
|
27
27
|
2. Run \`npx tsc --noEmit\` via the bash tool and capture type errors.
|
|
28
28
|
3. Run \`npx eslint <changed-files>\` via the bash tool (skip if eslint isn't configured).
|
|
29
29
|
4. Read the files. Combine static-analysis output with your own reading.
|
|
30
|
-
5. Return a
|
|
30
|
+
5. Return a plain-text report (terminal-friendly, NO markdown):
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
SUMMARY
|
|
33
33
|
<one or two sentences — overall health>
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
### CRITICAL
|
|
35
|
+
CRITICAL
|
|
38
36
|
- file.ts:LINE — what is wrong and why it matters
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
WARNING
|
|
41
39
|
- file.ts:LINE — what is wrong and how to think about it
|
|
42
40
|
|
|
43
|
-
|
|
41
|
+
SUGGESTION
|
|
44
42
|
- file.ts:LINE — small improvement
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
GOOD
|
|
47
45
|
- one or two things the code does well
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
- Each issue: filename:LINE — one sentence max.
|
|
56
|
-
- Total response: 25 lines maximum. Cut low-value suggestions if needed to stay under.`;
|
|
47
|
+
FORMAT RULES:
|
|
48
|
+
- No ##, ###, **bold**, *italic*. Plain text only.
|
|
49
|
+
- Each issue: one line, exactly "filename:LINE — single sentence".
|
|
50
|
+
- If a section has no items, write "none" under the label.
|
|
51
|
+
- Total response: 25 lines maximum. Cut low-value suggestions to stay under.
|
|
52
|
+
`;
|
|
57
53
|
export async function runReviewer(userMessage, onToolCall, model) {
|
|
58
54
|
const llm = makeLlm(model);
|
|
59
55
|
const tools = [listFilesTool, readFileTool, searchCodeTool, bashTool];
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { render } from "ink";
|
|
|
9
9
|
import { App } from "./ui/App.js";
|
|
10
10
|
import { loadConfig, saveConfig } from "./config.js";
|
|
11
11
|
import { generateAndSaveSummary } from "./memory.js";
|
|
12
|
-
import {
|
|
12
|
+
import { getDefaultLlm, sessionMessages } from "./agent.js";
|
|
13
13
|
import { extractAndSaveProjectMemory } from "./projectMemory.js";
|
|
14
14
|
import os from 'os';
|
|
15
15
|
// import { mkdirSync, writeFileSync, existsSync } from 'fs';
|
|
@@ -70,8 +70,9 @@ async function ensureApiKey() {
|
|
|
70
70
|
}
|
|
71
71
|
const saveAndExit = async () => {
|
|
72
72
|
try {
|
|
73
|
-
|
|
74
|
-
await
|
|
73
|
+
const summaryLlm = getDefaultLlm();
|
|
74
|
+
await generateAndSaveSummary(sessionMessages, summaryLlm);
|
|
75
|
+
await extractAndSaveProjectMemory(sessionMessages, summaryLlm);
|
|
75
76
|
}
|
|
76
77
|
catch {
|
|
77
78
|
// exit cleanly even if LLM is unreachable
|
package/dist/llm/classifier.js
CHANGED
|
@@ -41,7 +41,14 @@ Tie-breakers:
|
|
|
41
41
|
- starts with the literal word "git" → git
|
|
42
42
|
- short greeting or opinion question → chat
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Confidence guide:
|
|
45
|
+
- "high": user's intent is unambiguous from this single message; pattern + verb both point one way.
|
|
46
|
+
- "low": message is short or uses pronouns ("this", "it", "that"), or could plausibly fit 2+ domains, or relies on context the history doesn't fully clarify.
|
|
47
|
+
|
|
48
|
+
Return JSON: {"domain":"<domain>","confidence":"high" or "low", "reason":"<one sentence>"}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
`),
|
|
45
52
|
new HumanMessage(history ? `Conversation so far:\n${history}\n\nNew message: ${prompt}` : `Message: ${prompt}`)
|
|
46
53
|
], { timeout: 3000 });
|
|
47
54
|
const raw = typeof response.content === 'string' ? response.content : '';
|
package/dist/memory.js
CHANGED
|
@@ -79,7 +79,14 @@ export async function generateAndSaveSummary(messages, llm) {
|
|
|
79
79
|
if (!readable.trim())
|
|
80
80
|
return;
|
|
81
81
|
const response = await llm.invoke([
|
|
82
|
-
new SystemMessage('
|
|
82
|
+
new SystemMessage(`You're writing a brief for the next coding session. The user will return to this project days or weeks later and need fast context recall.
|
|
83
|
+
|
|
84
|
+
In 2-3 sentences, summarize:
|
|
85
|
+
- What was built or changed (be specific: file paths, function names)
|
|
86
|
+
- Key decisions made (e.g., "chose JWT over sessions", "Postgres over Mongo")
|
|
87
|
+
- Likely next step
|
|
88
|
+
|
|
89
|
+
Avoid generic phrases like "we worked on the project". Cite specifics.`),
|
|
83
90
|
new HumanMessage(readable)
|
|
84
91
|
]);
|
|
85
92
|
const hash = crypto.createHash('md5').update(process.cwd()).digest('hex').slice(0, 8);
|
package/dist/projectMemory.js
CHANGED
|
@@ -29,15 +29,30 @@ export async function extractAndSaveProjectMemory(messages, llm) {
|
|
|
29
29
|
return;
|
|
30
30
|
const existing = loadProjectMemory();
|
|
31
31
|
const response = await llm.invoke([
|
|
32
|
-
new SystemMessage(`Extract structured project facts from this conversation.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
new SystemMessage(`Extract structured project facts from this conversation.
|
|
33
|
+
|
|
34
|
+
OUTPUT REQUIREMENTS:
|
|
35
|
+
- Valid JSON ONLY. No markdown, no code fences, no prose.
|
|
36
|
+
- Exactly this shape:
|
|
37
|
+
{
|
|
38
|
+
"techStack": ["TypeScript", "Express", "MongoDB"],
|
|
39
|
+
"architectureDecisions": ["use JWT not sessions", "Mongo over Postgres for prototyping"],
|
|
40
|
+
"todos": ["add password reset", "wire Stripe webhooks"],
|
|
41
|
+
"lastUpdated": "${new Date().toISOString()}"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
EXTRACTION RULES:
|
|
45
|
+
- techStack: only languages/frameworks/libraries that were USED or CHOSEN — not briefly mentioned and discarded.
|
|
46
|
+
- architectureDecisions: concrete choices ("use JWT for auth"), not vague phrases ("we'll handle auth").
|
|
47
|
+
- todos: actual next steps mentioned, not aspirational tangents.
|
|
48
|
+
|
|
49
|
+
${existing ? `MERGE WITH EXISTING:
|
|
50
|
+
- techStack: union (no duplicates).
|
|
51
|
+
- architectureDecisions: append new, keep old unless contradicted.
|
|
52
|
+
- todos: keep old unless explicitly completed in this conversation.
|
|
53
|
+
|
|
54
|
+
Existing memory:
|
|
55
|
+
${JSON.stringify(existing, null, 2)}` : ''}`),
|
|
41
56
|
new HumanMessage(readable)
|
|
42
57
|
]);
|
|
43
58
|
try {
|
package/dist/ui/App.js
CHANGED
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import React, { useState, useRef } from "react";
|
|
3
3
|
import { Box, Text, useApp, Static } from 'ink';
|
|
4
4
|
import TextInput from "ink-text-input";
|
|
5
|
-
import { chat, clearSession,
|
|
5
|
+
import { chat, clearSession, getDefaultLlm, sessionMessages } from '../agent.js';
|
|
6
6
|
import { clearSummary, generateAndSaveSummary } from "../memory.js";
|
|
7
7
|
import { clearProjectMemory, extractAndSaveProjectMemory } from "../projectMemory.js";
|
|
8
8
|
import { useInput } from 'ink';
|
|
@@ -129,8 +129,9 @@ export const App = ({ explainMode = false }) => {
|
|
|
129
129
|
setPaletteIndex(0);
|
|
130
130
|
};
|
|
131
131
|
const handleExit = async () => {
|
|
132
|
-
|
|
133
|
-
await
|
|
132
|
+
const summaryLlm = getDefaultLlm();
|
|
133
|
+
await generateAndSaveSummary(sessionMessages, summaryLlm);
|
|
134
|
+
await extractAndSaveProjectMemory(sessionMessages, summaryLlm);
|
|
134
135
|
exit();
|
|
135
136
|
};
|
|
136
137
|
useInput((inputKey, key) => {
|
|
@@ -335,7 +336,7 @@ export const App = ({ explainMode = false }) => {
|
|
|
335
336
|
"You've used your 5 free requests.",
|
|
336
337
|
'',
|
|
337
338
|
'Sign in with GitHub — free, 50 requests/month:',
|
|
338
|
-
'
|
|
339
|
+
' → https://glit.in/activate',
|
|
339
340
|
'',
|
|
340
341
|
'Type /signup to start the sign-in flow in your terminal.',
|
|
341
342
|
].join('\n'),
|
|
@@ -420,9 +421,28 @@ export const App = ({ explainMode = false }) => {
|
|
|
420
421
|
}
|
|
421
422
|
}
|
|
422
423
|
catch (err) {
|
|
424
|
+
const raw = err?.message ?? 'Something went wrong.';
|
|
425
|
+
let friendly = raw;
|
|
426
|
+
if (raw.includes('anon_limit') || raw.includes('"anon_limit"')) {
|
|
427
|
+
friendly =
|
|
428
|
+
"Your free trial (5 requests) is over.\n\n" +
|
|
429
|
+
"→ Type /signup to continue with 50 free requests/month.\n" +
|
|
430
|
+
" It takes one click — sign in with GitHub.";
|
|
431
|
+
}
|
|
432
|
+
else if (raw.includes('monthly_limit') || raw.includes('"monthly_limit"')) {
|
|
433
|
+
friendly =
|
|
434
|
+
"You've reached your monthly limit of 50 requests.\n\n" +
|
|
435
|
+
"→ Upgrade to Pro for unlimited at https://glit.in/upgrade\n" +
|
|
436
|
+
"Or wait until next month — your limit resets on the 1st.";
|
|
437
|
+
}
|
|
438
|
+
else if (raw.includes('Token expired') || raw.includes('Invalid token')) {
|
|
439
|
+
friendly =
|
|
440
|
+
"Your session expired.\n\n" +
|
|
441
|
+
"→ Type /signup to sign in again.";
|
|
442
|
+
}
|
|
423
443
|
setMessages(prev => [...prev, {
|
|
424
444
|
role: 'error',
|
|
425
|
-
content:
|
|
445
|
+
content: friendly
|
|
426
446
|
}]);
|
|
427
447
|
}
|
|
428
448
|
finally {
|