glitool 1.0.0 → 1.0.1

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.md CHANGED
@@ -40,6 +40,9 @@ glitool config --show
40
40
  | /exit | Save and exit |
41
41
 
42
42
  ## Requirements
43
+ - Node.js 22 or higher
44
+ - npm install -g glitool
43
45
 
44
- - Node.js 18+
45
- - OpenAI API key
46
+
47
+ curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
48
+ sudo apt-get install -y nodejs
package/dist/agent.js CHANGED
@@ -9,7 +9,8 @@ import { loadProjectMemory } from "./projectMemory.js";
9
9
  import { config as loadEnv } from 'dotenv';
10
10
  import { fileURLToPath } from 'url';
11
11
  import { dirname, join } from 'path';
12
- import { detectComplexity } from './llm/router.js';
12
+ import { route } from './llm/router.js';
13
+ import { logRouting } from './llm/telemetry.js';
13
14
  import { runAgentGraph } from "./agents/graph.js";
14
15
  import os from 'os';
15
16
  const __filename = fileURLToPath(import.meta.url);
@@ -61,9 +62,10 @@ const complexAgent = createReactAgent({
61
62
  stateModifier: new SystemMessage(buildSystemPrompt())
62
63
  });
63
64
  export async function chat(userInput, onToolCall, onStatus, onToken) {
64
- const complexity = detectComplexity(userInput);
65
+ const decision = route(userInput);
66
+ logRouting(userInput, decision);
65
67
  sessionMessages.push(new HumanMessage(userInput));
66
- if (complexity === 'complex') {
68
+ if (decision.domain === 'coding' || decision.tier === 'complex') {
67
69
  const result = await runAgentGraph(userInput, buildSystemPrompt(), onToolCall, onStatus ?? (() => { }));
68
70
  if (result !== null && result !== undefined) {
69
71
  sessionMessages.push(new AIMessage(result));
@@ -6,7 +6,7 @@ import { listFilesTool, readFileTool, searchCodeTool, editFileTool, writeFileToo
6
6
  import { scoreRisk, getRiskMessage } from "../trust/riskScorer.js";
7
7
  import { requestConfirm } from "../confirmHandler.js";
8
8
  const coderLlm = new ChatOpenAI({
9
- model: 'gpt-4o-mini',
9
+ model: 'gpt-5.4-mini',
10
10
  apiKey: process.env.OPENAI_API_KEY
11
11
  });
12
12
  const coderAgent = createReactAgent({
@@ -1,7 +1,7 @@
1
1
  import { ChatOpenAI } from "@langchain/openai";
2
2
  import { SystemMessage, HumanMessage } from "@langchain/core/messages";
3
3
  const plannerLlm = new ChatOpenAI({
4
- model: 'gpt-4o-mini',
4
+ model: 'gpt-5.4',
5
5
  apiKey: process.env.OPENAI_API_KEY
6
6
  });
7
7
  export async function runPlanner(userMessage, context) {
@@ -11,7 +11,9 @@ Rules:
11
11
  - Be specific about which files to read, edit, or create
12
12
  - Do NOT write any code — only plan the steps
13
13
  - Keep it to 3-6 steps maximum
14
- - If the task is a simple question or explanation (not a coding task), output exactly: SIMPLE`),
14
+ - If the task is ONLY a question or explanation with NO file creation or code execution needed, output exactly: SIMPLE
15
+ - Any task that requires creating, editing, or writing files must NEVER be SIMPLE
16
+ `),
15
17
  new HumanMessage(`Context:\n${context}\n\nUser request: ${userMessage}`)
16
18
  ]);
17
19
  return response.content;
@@ -1,7 +1,7 @@
1
1
  import { ChatOpenAI } from "@langchain/openai";
2
2
  import { SystemMessage, HumanMessage } from "@langchain/core/messages";
3
3
  const reviewerLlm = new ChatOpenAI({
4
- model: 'gpt-4o-mini',
4
+ model: 'gpt-5.4-mini',
5
5
  apiKey: process.env.OPENAI_API_KEY
6
6
  });
7
7
  export async function runReviewer(plan, coderOutput, userMessage) {
@@ -1,25 +1,77 @@
1
- const SIMPLE_PATTERNS = [
2
- /^(what|who|when|where|how|why)\s.{0,60}\?$/i,
3
- /^(explain|define|what is|what are|tell me about)\s/i,
4
- /^(hi|hello|hey|thanks|thank you)/i,
1
+ const MODEL_BY_TIER = {
2
+ quick: 'gpt-4o-mini',
3
+ standard: 'gpt-4o-mini',
4
+ complex: 'gpt-4o-mini'
5
+ };
6
+ const CHAT_PATTERNS = [
7
+ /^(hi|hello|hey|thanks|thank you|ok|sure|yes|no|great|awesome)\b/i,
5
8
  /^\/\w+/,
6
9
  ];
7
- const COMPLEX_PATTERNS = [
10
+ const EXPLANATION_PATTERNS = [
11
+ /^(explain|describe|what is|what are|tell me about|how does|why does|what does)\s/i,
12
+ /^(what|who|when|where|how|why)\s.{0,60}\?$/i,
13
+ ];
14
+ const CODING_PATTERNS = [
8
15
  /refactor|rewrite|redesign/i,
9
16
  /implement|build|create|add|generate/i,
10
17
  /fix|debug|solve|resolve/i,
11
18
  /optimize|improve|upgrade/i,
12
19
  /migrate|convert|transform/i,
20
+ /function|class|component|api|endpoint|hook|module/i,
21
+ ];
22
+ const PLANNING_PATTERNS = [
23
+ /architecture|design|system|scalab/i,
24
+ /plan|roadmap|strategy|approach/i,
25
+ /how should (i|we)|what.s the best way/i,
13
26
  ];
27
+ function detectDomain(prompt) {
28
+ if (CHAT_PATTERNS.some(p => p.test(prompt)))
29
+ return 'chat';
30
+ if (EXPLANATION_PATTERNS.some(p => p.test(prompt)))
31
+ return 'explanation';
32
+ if (CODING_PATTERNS.some(p => p.test(prompt)))
33
+ return 'coding';
34
+ if (PLANNING_PATTERNS.some(p => p.test(prompt)))
35
+ return 'planning';
36
+ return 'chat';
37
+ }
38
+ function scoreComplexity(prompt) {
39
+ let score = 0;
40
+ if (prompt.length > 300)
41
+ score += 1;
42
+ if (prompt.length > 800)
43
+ score += 1;
44
+ if (/```/.test(prompt))
45
+ score += 1;
46
+ if ((prompt.match(/\n/g)?.length ?? 0) > 5)
47
+ score += 1;
48
+ if (PLANNING_PATTERNS.some(p => p.test(prompt)))
49
+ score += 2;
50
+ return score;
51
+ }
52
+ function selectTier(domain, score) {
53
+ if (domain === 'chat')
54
+ return 'quick';
55
+ if (domain === 'explanation' && score <= 1)
56
+ return 'quick';
57
+ if (score >= 3 || domain === 'planning')
58
+ return 'complex';
59
+ return 'standard';
60
+ }
61
+ export function route(prompt) {
62
+ const trimmed = prompt.trim();
63
+ const domain = detectDomain(trimmed);
64
+ const complexityScore = scoreComplexity(trimmed);
65
+ const tier = selectTier(domain, complexityScore);
66
+ return {
67
+ tier,
68
+ domain,
69
+ complexityScore,
70
+ recommendedModel: MODEL_BY_TIER[tier],
71
+ reason: `domain=${domain} score=${complexityScore} tier=${tier}`
72
+ };
73
+ }
14
74
  export function detectComplexity(message) {
15
- const trimmed = message.trim();
16
- for (const pattern of SIMPLE_PATTERNS) {
17
- if (pattern.test(trimmed))
18
- return 'simple';
19
- }
20
- for (const pattern of COMPLEX_PATTERNS) {
21
- if (pattern.test(trimmed))
22
- return 'complex';
23
- }
24
- return trimmed.length > 120 ? 'complex' : 'simple';
75
+ const { domain, tier } = route(message);
76
+ return (domain === 'coding' || tier === 'complex') ? 'complex' : 'simple';
25
77
  }
@@ -0,0 +1,18 @@
1
+ import { appendFileSync, mkdirSync } from "fs";
2
+ import { join } from 'path';
3
+ import os from 'os';
4
+ export function logRouting(prompt, decision) {
5
+ try {
6
+ const logDir = join(os.homedir(), '.glitool');
7
+ mkdirSync(logDir, { recursive: true });
8
+ const event = {
9
+ timestamp: new Date().toISOString(),
10
+ prompt_length: prompt.length,
11
+ ...decision,
12
+ };
13
+ appendFileSync(join(logDir, 'routing.log.jsonl'), JSON.stringify(event) + '\n', 'utf-8');
14
+ }
15
+ catch {
16
+ // never crash the tool because of telemetry
17
+ }
18
+ }
@@ -1,5 +1,4 @@
1
1
  import { tool } from "@langchain/core/tools";
2
- import { confirm } from "@inquirer/prompts";
3
2
  import fs from 'fs';
4
3
  import path from 'path';
5
4
  import { z } from 'zod';
package/package.json CHANGED
@@ -1,51 +1,56 @@
1
1
  {
2
2
  "name": "glitool",
3
- "version": "1.0.0",
4
-
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1",
8
- "build": "tsc",
9
- "dev": "tsx src/index.ts",
10
- "prepublishOnly": "npm run build"
11
- },
12
- "repository": {
13
- "type": "git",
14
- "url": "https://github.com/deep9038/glitool"
15
- },
16
- "bin": {
17
- "glitool": "./dist/index.js"
18
- },
19
- "keywords": ["ai", "cli", "coding-assistant", "openai", "terminal"],
20
- "author": "Deep Sarkar <deep22sarkar@gmail.com>",
21
- "license": "MIT",
3
+ "version": "1.0.1",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1",
7
+ "build": "tsc",
8
+ "dev": "tsx src/index.ts",
9
+ "prepublishOnly": "npm run build"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/deep9038/glitool"
14
+ },
15
+ "bin": {
16
+ "glitool": "./dist/index.js"
17
+ },
18
+ "keywords": [
19
+ "ai",
20
+ "cli",
21
+ "coding-assistant",
22
+ "openai",
23
+ "terminal"
24
+ ],
25
+ "author": "Deep Sarkar <deep22sarkar@gmail.com>",
26
+ "license": "MIT",
22
27
  "description": "AI coding assistant for your terminal",
23
- "homepage":"https://github.com/deep9038/glitool",
24
- "dependencies": {
25
- "@inquirer/prompts": "^8.4.1",
26
- "@langchain/core": "^1.1.40",
27
- "@langchain/langgraph": "^1.2.9",
28
- "@langchain/ollama": "^1.2.6",
29
- "@langchain/openai": "^1.4.4",
30
- "chalk": "^5.6.2",
31
- "commander": "^14.0.3",
32
- "dotenv": "^17.4.2",
33
- "ink": "^7.0.1",
34
- "ink-text-input": "^6.0.0",
35
- "ora": "^9.3.0",
36
- "react": "^19.2.5"
37
- },
38
- "devDependencies": {
39
- "@types/node": "^25.6.0",
40
- "@types/react": "^19.2.14",
41
- "tsx": "^4.21.0",
42
- "typescript": "^6.0.2"
43
- },
44
- "type": "module",
45
- "engines": {
46
- "node": ">=18.0.0"
47
- },
48
- "files": [
49
- "dist/"
50
- ]
28
+ "homepage": "https://github.com/deep9038/glitool",
29
+ "dependencies": {
30
+ "@inquirer/prompts": "^8.4.1",
31
+ "@langchain/core": "^1.1.40",
32
+ "@langchain/langgraph": "^1.2.9",
33
+ "@langchain/ollama": "^1.2.6",
34
+ "@langchain/openai": "^1.4.4",
35
+ "chalk": "^5.6.2",
36
+ "commander": "^14.0.3",
37
+ "dotenv": "^17.4.2",
38
+ "ink": "^7.0.1",
39
+ "ink-text-input": "^6.0.0",
40
+ "ora": "^9.3.0",
41
+ "react": "^19.2.5"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^25.6.0",
45
+ "@types/react": "^19.2.14",
46
+ "tsx": "^4.21.0",
47
+ "typescript": "^6.0.2"
48
+ },
49
+ "type": "module",
50
+ "engines": {
51
+ "node": ">=22.0.0"
52
+ },
53
+ "files": [
54
+ "dist/"
55
+ ]
51
56
  }