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 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
- export const llm = createLlm('meta-llama/Llama-3.3-70B-Instruct-Turbo');
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, llm);
58
+ generateAndSaveSummary(rawSession, getDefaultLlm());
52
59
  summary = loadSummary();
53
60
  }
54
61
  }
55
- let prompt = `You are an expert coding assistant. Be concise and code-focused.
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
- CRITICAL file operations:
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
- IMPORTANT: If any tool returns USER_CANCELLED, immediately stop all tool calls and tell the user the operation was cancelled. Never retry a cancelled operation.`;
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]'
@@ -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 = '';
@@ -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. Give what an AI coding assistant just did, explain it in 2-4 simple sentences a biginner would underStand.
9
- Focus on:
10
- - What changed and why
11
- - What concept was used
12
- - What to learn next
13
- Keep it friendly and short.`),
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;
@@ -23,7 +23,7 @@ IMPORTANT — how the coder works:
23
23
  Return JSON only:
24
24
  {
25
25
  "verdict": "ok" or "fail",
26
- "failure_point": "plan" | "workflow" | "executor" | "final_output" | null,
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
- - "executor": a specific step failed — use failure_step_id
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` +
@@ -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
- - If the task is ONLY a question or explanation with NO file creation or code execution needed, output exactly: SIMPLE
10
- - Otherwise output a JSON array of steps with this shape:
11
- [{ "id": 1, "action": "read|edit|create|run|search", "target": "file/path or command", "depends_on": [], "why": "reason" }]
12
- - Be specific about which files to read, edit, or create
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('deepseek-ai/DeepSeek-V3');
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 ? `...existing logic...` : `You are a planning assistant. Create a clear structured plan based on user's request.
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 markdown report in EXACTLY this shape:
30
+ 5. Return a plain-text report (terminal-friendly, NO markdown):
31
31
 
32
- ## Summary
32
+ SUMMARY
33
33
  <one or two sentences — overall health>
34
34
 
35
- ## Issues
36
-
37
- ### CRITICAL
35
+ CRITICAL
38
36
  - file.ts:LINE — what is wrong and why it matters
39
37
 
40
- ### WARNING
38
+ WARNING
41
39
  - file.ts:LINE — what is wrong and how to think about it
42
40
 
43
- ### SUGGESTION
41
+ SUGGESTION
44
42
  - file.ts:LINE — small improvement
45
43
 
46
- ## What's good
44
+ GOOD
47
45
  - one or two things the code does well
48
46
 
49
- If a section has no items, write "none" under its heading. Do not omit sections.
50
-
51
- TERMINAL OUTPUT RULES these override everything else about formatting:
52
- - No markdown headers (no ##, no ###). Use plain uppercase labels: SUMMARY, CRITICAL, WARNING, SUGGESTION, GOOD.
53
- - No **bold** or *italic*. Plain text only.
54
- - No bullet dashes longer than needed — one short line per issue.
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 { llm, sessionMessages } from "./agent.js";
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
- await generateAndSaveSummary(sessionMessages, llm);
74
- await extractAndSaveProjectMemory(sessionMessages, llm);
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
@@ -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
- Return JSON: {"domain":"<domain>","confidence":"high" or "low", "reason":"<one sentence>"}`),
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('Summarize this coding session in 2-3 sentences. Focus on: what was built, what decisions ware made, and what the next step is.'),
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);
@@ -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. return valid JSON only:
33
- {
34
- "techStack": ["languages, frameworks, libraries mentioned"],
35
- "architectureDecisions": ["key structural decisions made"],
36
- "todos": ["next steps or TODOs mentioned"],
37
- "lastUpdated": "${new Date().toISOString()}"
38
- }
39
-
40
- ${existing ? `Merge with existing memory: ${JSON.stringify(existing)}` : ''}`),
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, llm, sessionMessages } from '../agent.js';
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
- await generateAndSaveSummary(sessionMessages, llm);
133
- await extractAndSaveProjectMemory(sessionMessages, llm);
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
- ' → https://glitool.dev/activate',
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: err?.message ?? 'Something went wrong.'
445
+ content: friendly
426
446
  }]);
427
447
  }
428
448
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glitool",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "AI coding assistant for your terminal",
5
5
  "main": "index.js",
6
6
  "type": "module",