Skip to content
Conduit

skill

CLIC-Agentic-CLI-like-Code-Claude

CLIC (Agentic CLI like ClaudeCode) — Command Line Intelligence Companion

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/UdaySharmaGitHub/CLIC-Agentic-CLI-like-Code-Claude itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 25, 2026

v4.3.0 — An agentic CLI powered by any OpenAI-compatible API with streaming, function calling, user-controlled parallel tool execution, abort support, API retry with exponential backoff, cost estimation, context-window guard with auto-compact, Zod runtime input validation, named sessions, privacy/ephemeral mode, conversation export, workspace file watching, and a persistent Parallel Execution PTY terminal pool for stateful shell execution.

CLIC is a terminal-based Agentic CLI that can read/write files, run persistent shell sessions, search the web, manage named conversation sessions, and chain multiple steps automatically to complete complex tasks — all with human approval before every action. It ships a hardened execution harness: a node-pty PTY pool where each terminal retains its shell state (cwd, env, virtual environments) across calls, commands on the same terminal serialise via a per-entry promise queue, and different terminals run in true parallel — so the agent can drive dev servers, test runners, and file watchers concurrently without losing context.


Table of Contents


Features

CapabilityDescription
💬 Chat / Q&AAny topic — code, math, devops, science
⚙️ Run CommandsExecute safe shell commands with approval
📖 Read FilesRead and analyze file contents
✏️ Write FilesCreate or overwrite files
➕ Append FilesAdd content to existing files
🔧 Modify FilesFind-and-replace text in files (with backup)
📂 List DirsBrowse directory listings
🔍 Search FilesGlob-based file search
🌐 Web SearchLLM-powered knowledge lookup — routes query to the active model via a fresh API call; no external search key required
🐙 GitHubFetch any user's profile, activity streak, and public repos
📋 Model PickerLive model list fetched from API at startup; switch mid-session with /model
🔗 Agentic LoopAuto-chain multiple steps: plan → execute → verify
⚡ Parallel ToolsWhen the LLM issues multiple tool calls, user is prompted once: "parallel or sequential?" — parallel runs all via Promise.all
⛔ Abort SupportAbortSignal threading lets you cancel a running agent turn mid-stream with Ctrl+C
🔁 API RetryExponential backoff (up to 4 attempts) on HTTP 429 / 500 / 502 / 503 / 504 errors
💰 Cost Estimation/tokens shows estimated USD cost per session and all-time using live model pricing data
📚 Knowledge BaseLoad role/behavior/persona from a Markdown file
🧠 Persistent MemoryChat history saved to chat_history.json — agent remembers across sessions
🛡️ Safety LayerBlocked commands + protected paths + human approval
🪟 Context GuardMonitors context fill % after every turn; auto-compacts at 80% to prevent cutoff
🔍 Diff Previewwrite_file and modify_file render a full-width unified diff before confirmation
🗂️ Named SessionsMultiple independent chat histories via --session <name> or /session — create, switch, rename, delete sessions without losing context
🔒 Privacy / Ephemeral Mode--no-history starts an ephemeral session — nothing written to disk (chat history, token graph, session index all suppressed). Toggle mid-session with /privacy
📤 Conversation Export/export [markdown|json|html] serialises the active conversation to a self-contained file in exports/
👁️ Workspace File Watchingchokidar watches CWD for external edits; injects a Workspace File Activity block into the system prompt each turn and prepends an inline staleness note on read_file when a file changed since the agent last saw it; disable with --no-watch
🖥️ Persistent PTY Terminalsnode-pty terminal pool (src/terminal.ts) backs run_command and the terminal tool; shell state (cwd, env, venv) persists across calls; commands on different terminals run in parallel, same-terminal calls serialise; disable with --no-terminals to fall back to legacy one-shot execa

Tech Stack

PackageRole
openaiOpenAI-compatible API client with streaming + function calling
zodRuntime input validation — each tool declares a z.ZodObject schema; executeTool() runs safeParse before calling execute()
commanderCLI argument parsing (--model, --kb, --yolo, --full-history, etc.)
@clack/promptsInteractive setup wizard (API key, model picker, KB file)
execaSafe subprocess execution with timeout + error capture
fast-globGlob-based file search
diffUnified diff generation for write_file / modify_file preview
chokidarCross-platform filesystem watcher for workspace file activity tracking (pinned to v3)
node-ptyNative PTY bindings for persistent shell sessions — powers the terminal pool in src/terminal.ts
chalkColored terminal output
oraSpinner while waiting for LLM responses
dotenvLoad .env config (API keys)
tsxRun TypeScript directly during development
tsupBundle for production distribution

Project Structure

clic/
├── src/
│   ├── index.ts              ← CLI entry point + REPL loop + context-window guard (auto-compact)
│   ├── agent.ts              ← ReAct agentic loop (runAgentTurn) + parallel/sequential tool dispatch + KG recording
│   ├── openai.ts             ← OpenAI SDK wrapper (createClient / streamMessage / withRetry / TokenUsage)
│   ├── knowledgeGraph.ts     ← Token-tracking Knowledge Graph (persisted to token_graph.json)
│   ├── pricing.ts            ← Real-time model pricing via LiteLLM proxy (getCost / formatCost / isPricingLoaded)
│   ├── prompts.ts            ← System prompt builder (buildSystemPrompt — injects workspace activity + KB)
│   ├── memory.ts             ← Chat history management (load/save/push/pop/clear/trim + setHistoryFile)
│   ├── session.ts            ← Named-session lifecycle (create/switch/rename/delete + sessions.json index)
│   ├── watcher.ts            ← Singleton workspace file watcher (chokidar; staleness notes + ambient context)
│   ├── terminal.ts           ← TerminalManager singleton — node-pty PTY pool (persistent shell state, per-terminal serialisation, parallel across terminals)
│   ├── privacy.ts            ← Ephemeral-session flag (setEphemeral / isEphemeral; dependency-free singleton)
│   ├── safety.ts             ← Blocked commands + protected paths
│   ├── config.ts             ← Env loading, constants, context limits, session paths, KB loader
│   ├── ui.ts                 ← Banner, help, status, context bar, diff view, chalk formatters
│   ├── commands/
│   │   ├── index.ts          ← Command registry + router + tab completer
│   │   ├── types.ts          ← Shared types (SlashCommand, CommandContext, CommandAction)
│   │   ├── compact.ts        ← /compact — summarize + compress history
│   │   ├── model.ts          ← /model  — switch model mid-session
│   │   ├── role.ts           ← /role   — switch KB/persona mid-session
│   │   ├── undo.ts           ← /undo   — remove last exchange
│   │   ├── retry.ts          ← /retry  — regenerate last response
│   │   ├── tokens.ts         ← /tokens — token counts + cost estimate from Knowledge Graph
│   │   ├── status.ts         ← /status — show system info
│   │   ├── history.ts        ← /history — show conversation history
│   │   ├── clear.ts          ← /clear  — clear history
│   │   ├── raw.ts            ← /raw    — toggle debug output
│   │   ├── help.ts           ← /help   — show help menu
│   │   ├── session.ts        ← /session — create, switch, rename, delete named sessions
│   │   ├── privacy.ts        ← /privacy — toggle ephemeral mode mid-session
│   │   ├── export.ts         ← /export  — export conversation to markdown / json / html
│   │   └── exit.ts           ← /exit   — quit agent
│   └── tools/
│       ├── index.ts          ← Tool registry + router
│       ├── types.ts          ← Shared types (ConfirmFn, ToolResult, ToolDefinition)
│       ├── helpers.ts        ← Shared helpers (resolvePath, renderDiff — unified diff renderer)
│       ├── readFile.ts       ← read_file tool
│       ├── writeFile.ts      ← write_file tool (with diff preview)
│       ├── appendFile.ts     ← append_file tool
│       ├── modifyFile.ts     ← modify_file tool (with diff preview + .bak backup)
│       ├── listDir.ts        ← list_directory tool
│       ├── runCommand.ts     ← run_command tool
│       ├── searchFiles.ts    ← search_files tool
│       ├── webSearch.ts      ← web_search tool (LLM-powered; uses active model via CLIC_MODEL)
│       ├── githubExtractor.ts← github tool (profile, streak, repos)
│       ├── terminal.ts       ← terminal tool (PTY pool actions: create/list/read/write/start/kill/wait)
│       └── listModelfromOpenAI.ts ← fetchAvailableModelOptions() startup helper (not in tool registry)
├── roles based Workflow/     ← Built-in role/persona files (auto-discovered)
├── .env                      ← API keys (not committed)
├── .env.example              ← Template for .env
├── .gitignore
├── package.json
├── tsconfig.json
├── setup.sh                  ← Original bash version (v4.1)
├── sessions/                 ← Per-session history dirs (auto-generated, gitignored)
├── sessions.json             ← Named-session index (auto-generated, gitignored)
├── exports/                  ← Conversation exports from /export (auto-generated, gitignored)
├── chat_history.json         ← Legacy root history (migrated to sessions/ on first run, gitignored)
└── token_graph.json          ← Token usage Knowledge Graph (auto-generated, gitignored)

Architecture

High-Level Flow

flowchart TD
    User(["👤 User Input\nREPL / Single-turn"])
    index["index.ts\nCLI + REPL"]
    memory["memory.ts\nChat History"]
    agent["agent.ts\nReAct Loop"]
    llm["openai.ts\nOpenAI-compatible API"]
    kg["knowledgeGraph.ts\nToken KG"]
    cmdRegistry["commands/index.ts\nCommand Registry"]
    toolRegistry["tools/index.ts\nTool Registry"]

    compact["/compact\n/model · /role\n/undo · /retry\n/tokens · …"]
    readFile["read_file"]
    writeFile["write_file"]
    appendFile["append_file"]
    modifyFile["modify_file"]
    listDir["list_directory"]
    runCmd["run_command"]
    search["search_files"]
    webSearch["web_search"]
    github["github"]
    listModels["list_models"]

    User -->|"slash command"| index
    User -->|"natural language"| index
    index -->|"slash command"| cmdRegistry
    cmdRegistry --> compact
    compact -->|"update / retry / exit"| index
    index --> memory
    memory --> agent
    agent -->|"streamMessage()"| llm
    llm -->|"text + tool_calls + TokenUsage"| agent
    agent -->|"executeTool()"| toolRegistry
    toolRegistry --> readFile & writeFile & appendFile & modifyFile
    toolRegistry --> listDir & runCmd & search & webSearch
    toolRegistry --> github & listModels
    toolRegistry -->|"tool_result"| agent
    agent -->|"record turn"| kg
    agent -->|"no more tool_calls → end_turn"| index
    index --> User

    classDef user      fill:#7C3AED,stroke:#5B21B6,color:#fff,font-weight:bold
    classDef core      fill:#1D4ED8,stroke:#1E40AF,color:#fff
    classDef llmNode   fill:#0D9488,stroke:#0F766E,color:#fff,font-weight:bold
    classDef kgNode    fill:#D97706,stroke:#B45309,color:#fff,font-weight:bold
    classDef cmdNode   fill:#7E22CE,stroke:#6B21A8,color:#fff
    classDef registry  fill:#0369A1,stroke:#075985,color:#fff,font-weight:bold
    classDef toolItem  fill:#059669,stroke:#047857,color:#fff

    class User user
    class index,memory,agent core
    class llm llmNode
    class kg kgNode
    class cmdRegistry,compact cmdNode
    class toolRegistry registry
    class readFile,writeFile,appendFile,modifyFile,listDir,runCmd,search,webSearch,github,listModels toolItem

    linkStyle default stroke:#ffffff,stroke-width:1.5px

ReAct Agent Loop

The core pattern is a ReAct loop (Reason + Act). This runs in agent.ts:

flowchart TD
    Start(["💬 User sends message"])
    SlashCheck{{"Slash\ncommand?"}}
    CmdRun["⌘ Execute command\n(/compact · /model · /role\n/undo · /tokens · …)"]
    RetryPath["🔄 /retry — trim last\nassistant turn, re-enter loop"]
    CallAPI["⚙️ Call OpenAI-compatible API\n— streaming response —"]
    Decision{{"tool_calls?"}}
    EndTurn(["✅ end_turn\nRecord turn in KG\nReturn response to user"])
    ToolUse["🔧 Execute tool(s) in parallel\nPromise.all — with user approval"]
    SaveResult["📩 Push tool_result\nback into context"]
    StepCheck{{"Max steps\nreached?"}}
    Abort(["⛔ Abort\nMax steps exceeded"])

    Start --> SlashCheck
    SlashCheck -->|"yes"| CmdRun
    SlashCheck -->|"no"| CallAPI
    CmdRun -->|"retry action"| RetryPath
    CmdRun -->|"continue / update / exit"| Start
    RetryPath --> CallAPI
    CallAPI --> Decision
    Decision -->|"no"| EndTurn
    Decision -->|"yes"| ToolUse
    ToolUse --> SaveResult
    SaveResult --> StepCheck
    StepCheck -->|"No"| CallAPI
    StepCheck -->|"Yes"| Abort

    classDef startEnd  fill:#7C3AED,stroke:#5B21B6,color:#fff,font-weight:bold
    classDef decision  fill:#D97706,stroke:#B45309,color:#fff,font-weight:bold
    classDef command   fill:#7E22CE,stroke:#6B21A8,color:#fff
    classDef apiCall   fill:#0D9488,stroke:#0F766E,color:#fff,font-weight:bold
    classDef toolExec  fill:#059669,stroke:#047857,color:#fff
    classDef success   fill:#1D4ED8,stroke:#1E40AF,color:#fff,font-weight:bold
    classDef abort     fill:#DC2626,stroke:#B91C1C,color:#fff,font-weight:bold
    classDef neutral   fill:#374151,stroke:#1F2937,color:#fff

    class Start,EndTurn startEnd
    class SlashCheck,Decision,StepCheck decision
    class CmdRun,RetryPath command
    class CallAPI apiCall
    class ToolUse,SaveResult toolExec
    class EndTurn success
    class Abort abort

    linkStyle default stroke:#ffffff,stroke-width:1.5px

Key design: The openai SDK's native streaming + function calling handles structured tool calls — no manual JSON parsing or done flag needed. When the LLM emits multiple tool calls in a single response, the user is prompted once: "Run all N tools in parallel?" — answering y executes them concurrently via Promise.all; answering n runs them one-by-one with individual confirms. A single tool call skips the prompt and runs directly. All API calls are wrapped in withRetry() for automatic exponential-backoff retry on transient errors.

Parallel tool execution: Controlled by a single user confirmation per batch — "Run all N tools in parallel?" When parallel, individual per-tool confirms are auto-approved to avoid garbled output. When sequential, each tool goes through its own confirm. The LLM issues dependent calls across separate turns naturally.

Step limit: Max 15 steps per user turn (configurable via --max-steps).

Tool System

Every tool is a self-contained module that exports two things:

// src/tools/myTool.ts

const schema = z.object({ ... });   // Zod schema for runtime validation

export const definition: ToolDefinition = {
  name: 'my_tool',
  description: '...',
  parameters: { type: 'object', properties: { ... }, required: [] },
  schema,   // ← required: executeTool() calls schema.safeParse(input) before execute()
};

export async function execute(
  input: z.infer<typeof schema>,
  confirm: ConfirmFn,
): Promise<ToolResult> {
  // 1. Print header
  // 2. Safety check (if applicable)
  // 3. Ask for user confirmation
  // 4. Execute the action
  // 5. Return { output, isError }
}

The registry (tools/index.ts) auto-wires everything:

tools/index.ts
  ├── Imports all tool modules
  ├── Builds toolMap (name → module)
  ├── getToolDefinitions() → JSON schemas sent to the LLM
  ├── executeTool(name, input, confirm)
  │     ├── schema.safeParse(input) → validation gate (returns error result on failure)
  │     └── tool.execute(parsed.data, confirm) → routes to correct module
  └── getToolNames() → list of registered tool names

Registered tools:

ToolModuleDescription
read_filereadFile.tsRead file contents
write_filewriteFile.tsCreate or overwrite a file
append_fileappendFile.tsAppend to an existing file
modify_filemodifyFile.tsFind-and-replace in a file
list_directorylistDir.tsList directory contents
run_commandrunCommand.tsExecute a shell command
search_filessearchFiles.tsGlob-based file search
web_searchwebSearch.tsWeb search — routes query to the active LLM via a fresh OpenAI client call using CLIC_MODEL; no external search API required
githubgithubExtractor.tsGitHub profile, streak, and repos
terminalterminal.tsPersistent PTY terminal pool — create/list/read/write/start/kill/wait actions; shell state survives across calls

list_models (listModelfromOpenAI.ts) is a startup-only helper — it powers the interactive model picker but is not registered in the LLM tool registry.

Module Responsibilities

ModulePurpose
index.tsCLI parsing, setup wizard, live model picker, REPL loop, context-window guard (renders printContextBar + triggers runCompact at 80% fill). Passes extended CommandContext (with callLLM, systemPrompt, sessionId) to commands; handles retry and update actions (recreates OpenAI client on model swap)
agent.tsThe ReAct loop — calls the API via openai.ts, handles streaming. When >1 tool call arrives, shows a queue preview and asks "parallel or sequential?" — parallel uses Promise.all with auto-approved per-tool confirms; sequential runs each with its own confirm. After each turn records session/turn/model/tool/usage nodes in the Knowledge Graph
openai.tsThin wrapper around openai — createClient() + streamMessage() with tool-call chunk assembly. Wraps the API call in withRetry() (exponential backoff on 429/5xx, up to 4 attempts). Accepts an optional AbortSignal passed through to the SDK's create() call. Returns LLMResponse including TokenUsage (actual from stream_options.include_usage)
knowledgeGraph.tsIn-memory graph with addNode() / addEdge() / query helpers (getSessionTokenSummary, getGlobalTokenSummary, getSessionToolUsage, getSessionTokensByModel, getGlobalTokensByModel). Persisted to token_graph.json
pricing.tsReal-time pricing from LiteLLM proxy /model/info — loadPricing() (called at startup), getCost(), getPricing(), formatCost(), isPricingLoaded(). Used by /tokens to compute estimated USD spend
prompts.tsBuilds the system prompt with live system context (OS, user, CWD, date) + optional Workspace File Activity block (from getRecentlyModified()) + optional knowledge base
memory.tsManages ChatMessage[] in memory (OpenAI format) — setHistoryFile(), pushMessage(), popMessage(), getMessages(), clearMessages(), messageCount(), loadHistory(limit?), saveHistory(), trimToLastUserMessage()
session.tsNamed-session lifecycle — loadIndex(), saveIndex(), listSessions(), getActive(), setActive(), hasSession(), ensureSession(), createSession(), renameSession(), deleteSession(), migrateLegacy(), sessionNodeId(). Persists sessions.json + per-session sessions/<name>/ directories
watcher.tsSingleton chokidar watcher — startWatcher(cwd), stopWatcher(), markRead(filepath), getStalenessNote(filepath), getRecentlyModified(windowMs?). Also exports pure helpers for testing: formatAgo(), computeStalenessNote(), selectRecent()
terminal.tsTerminalManager singleton backed by node-pty — pool of up to 8 persistent PTY shells. Commands on the same terminal serialise via a per-terminal promise queue; different terminals run in parallel. Sentinel detection captures exit codes. Exports pure helpers: stripAnsi(), RingBuffer, assertValidTerminalName(), and singleton terminalManager
privacy.tsDependency-free singleton for ephemeral-session mode — setEphemeral(value), isEphemeral(). Imported by memory.ts, knowledgeGraph.ts, and session.ts to skip all disk writes when enabled
config.tsLoads .env via dotenv; exports DEFAULT_MODEL, DEFAULT_MAX_STEPS, HISTORY_FILE, TOKEN_GRAPH_FILE, SESSIONS_DIR, SESSIONS_INDEX_FILE, DEFAULT_SESSION, sessionHistoryPath(), TERMINAL_SHELL, TERMINAL_MAX, TERMINAL_CMD_TIMEOUT_MS, TERMINAL_BUFFER_LINES, MODEL_CONTEXT_LIMITS, DEFAULT_CONTEXT_LIMIT, CONTEXT_GUARD_THRESHOLD, HISTORY_LOAD_LIMIT, AppConfig interface, loadKnowledgeBase(), getContextLimit()
safety.tsisCommandSafe() checks against blocked patterns, isPathSafe() checks against protected paths
ui.tsprintBanner(), printHelp(), printStatus(), printStepHeader(), printSeparator(), promptPrintSeperator(), printToolHeader(), printToolSuccess(), printToolError(), printToolBlocked(), printRejected(), printDimOutput(), printContextBar(), actionLabel(), sessionNameBadge()
commands/types.tsShared types: SlashCommand, CommandContext (with callLLM + sessionId), CommandAction (continue/exit/retry/update)
commands/index.tsRegistry: imports all commands, supports args parsing (e.g. /model gpt-4o), exports executeCommand(), isSlashedCommand(), getSlashCommands(), slashCompleter()
tools/types.tsShared types: ConfirmFn, ToolResult, ToolDefinition (includes schema: z.ZodTypeAny for the Zod validation gate)
tools/helpers.tsShared utilities: resolvePath() (handles ~ expansion + path.resolve), renderDiff() (full-width unified diff renderer using diff package)
tools/index.tsRegistry: imports all tools, builds lookup map, exports getToolDefinitions(), executeTool(), getToolNames()

Getting Started

Prerequisites

  • Node.js >= 20
  • pnpm 10 or newer (recommended)

Install

git clone https://github.com/UdaySharmaGitHub/CLIC-Agentic-CLI-like-Code-Claude.git
cd CLIC-Agentic-CLI-like-Code-Claude
pnpm install

Configure

cp .env.example .env

Edit .env and add your API key:

API_KEY=sk-...

# Optional: point at any OpenAI-compatible endpoint
BASE_URL=https://api.openai.com/v1

If you don't set API_KEY in .env, the setup wizard will prompt you interactively.

Run

# Development (with hot reload via tsx)
pnpm dev

# Build for production
pnpm build

# Run production build
pnpm start

Usage

Interactive REPL

pnpm dev

This launches the setup wizard (API key + optional knowledge base), then drops you into the REPL:

  🧑 You:
  > create a hello.ts file, make it executable, and run it

The agent will chain multiple steps automatically:

  1. write_file → create hello.ts
  2. run_command → chmod +x hello.ts
  3. run_command → ./hello.ts
  4. respond → summarize what was done

Every action requires your approval before execution.

Single-Turn Mode

pnpm dev -- "list all TypeScript files in src/"

Runs the prompt, outputs the result, and exits.

CLI Flags

FlagDefaultDescription
--model <model>gpt-4oModel to use (see live picker at startup)
--kb <path>—Path to a knowledge base / role file
--max-steps <n>15Max agent steps per user turn
--yolofalseAuto-approve all actions (skip confirmations)
--full-historyfalseLoad entire chat history (default: last 10 messages)
--session <name>defaultLoad or create a named session; names may contain letters, digits, dashes, underscores
--no-historyhistory onStart an ephemeral session — nothing persisted to disk (chat history, token graph, and session index writes are all suppressed)
--no-watchwatch onDisable workspace file watcher (recommended for large repos, NFS mounts, or Docker)
--no-terminalsterminals onDisable persistent PTY terminal pool — fall back to legacy one-shot execa for run_command (use when node-pty prebuilds are unavailable)
-p, --pastefalseRead prompt from stdin until EOF (Ctrl+D) and run as single-turn; works with pipes: cat file.txt | pnpm dev --paste

Available Models

Models are fetched live from your configured API endpoint at startup and presented as an interactive picker. Pass --model <name> to bypass it. Use /model mid-session to switch without restarting.

Model IDProvider
anthropic--claude-4-sonnetAnthropic
anthropic--claude-4.5-haikuAnthropic
anthropic--claude-4.5-opusAnthropic
anthropic--claude-4.5-sonnetAnthropic
anthropic--claude-4.6-opusAnthropic
anthropic--claude-4.6-sonnetAnthropic
gemini-2.5-flashGoogle
gemini-2.5-flash-liteGoogle
gemini-2.5-proGoogle
gpt-4.1OpenAI
gpt-4.1-miniOpenAI
gpt-5OpenAI
gpt-5-miniOpenAI
sonarPerplexity
sonar-proPerplexity
# Example: use Claude Sonnet
pnpm dev -- --model anthropic--claude-4.6-sonnet

# Example: use GPT-5
pnpm dev -- --model gpt-5

# Example: use Gemini 2.5 Pro
pnpm dev -- --model gemini-2.5-pro

REPL Commands

CommandAliasAction
/compact—Summarize + compress history to free up context
/model [name]/mSwitch LLM model mid-session (shows picker if no name given)
/role—Switch knowledge base / persona without restarting
/session [new|switch|rename|delete] [name]/sManage named sessions — create, switch, rename, or delete
/privacy—Toggle ephemeral mode mid-session (enable: suppresses future disk writes; disable: re-enables writes and flushes full in-memory history on next save)
/export [markdown|json|html]—Export the active conversation to exports/clic-export-<date>.[md|json|html] (current window or full history)
/undo—Remove the last user + assistant exchange from history
/retry/rRegenerate the last response (re-runs last user message)
/tokens—Show actual token usage (from Knowledge Graph) + context size estimate
/status—Show system info (OS, model, history count, etc.)
/history—Show conversation history
/clear—Clear conversation history
/raw—Toggle raw JSON debug output
/help—Show capabilities and example prompts
/exit / /quit—Save history and exit

Adding a New Tool

The tool system is designed for easy extension. Two steps:

Step 1: Create the tool module

Create src/tools/myNewTool.ts:

import { z } from 'zod';
import type { ToolDefinition, ConfirmFn, ToolResult } from './types.js';

// 1. Zod schema — used by executeTool() for runtime input validation
const schema = z.object({
  param1: z.string().min(1),
  param2: z.number().optional(),
});

// 2. Define the JSON schema (sent to the LLM)
export const definition: ToolDefinition = {
  name: 'my_new_tool',
  description: 'What this tool does — the LLM reads this to decide when to use it.',
  parameters: {
    type: 'object',
    properties: {
      param1: { type: 'string', description: 'Description for the model' },
      param2: { type: 'number', description: 'Another param' },
    },
    required: ['param1'],
  },
  schema, // ← required: executeTool() calls schema.safeParse(input) before execute()
};

// 3. Implement the executor
export async function execute(
  input: z.infer<typeof schema>,
  confirm: ConfirmFn,
): Promise<ToolResult> {
  // Ask for approval
  if (!await confirm(`Run my_new_tool with '${input.param1}'?`)) {
    return { output: 'User rejected.', isError: true };
  }

  // Do the work
  const result = `Did something with ${input.param1}`;

  return { output: result, isError: false };
}

Step 2: Register it

In src/tools/index.ts, add two lines:

import * as myNewTool from './myNewTool.js';    // ← add import

const tools: ToolModule[] = [
  readFile,
  writeFile,
  // ... existing tools ...
  myNewTool,                                     // ← add to array
];

That's it. The registry auto-wires the definition (sent to the LLM) and the executor (called when the LLM uses it).


Adding a New Command

Slash commands are self-contained modules. Two steps:

Step 1: Create the command module

Create src/commands/myCmd.ts:

import chalk from 'chalk';
import type { SlashCommand } from './types.js';

export const command: SlashCommand = {
  name: '/mycmd',
  aliases: ['/mc'],            // optional
  description: 'What this command does',
  usage: '/mycmd [optional-arg]',
  execute: async (ctx, args) => {
    // ctx: { model, maxSteps, showRaw, kbFile, systemPrompt, yolo, callLLM }
    // args: everything after the command name (e.g. "gpt-4o" from "/mycmd gpt-4o")

    console.log(chalk.green(`  ✅ Running mycmd, current model: ${ctx.model}`));
    console.log();

    // Return one of:
    //   { type: 'continue' }                           — nothing changes
    //   { type: 'exit' }                               — quit the REPL
    //   { type: 'retry' }                              — re-run last user message
    //   { type: 'update', updates: { model: '...' } }  — mutate session state
    return { type: 'continue' };
  },
};

Step 2: Register it

In src/commands/index.ts, add two lines:

import { command as myCmdCmd } from './myCmd.js';  // ← add import

const commands: SlashCommand[] = [
  // ... existing commands ...
  myCmdCmd,                                         // ← add to array
];

Tab-completion, routing, and /help all pick it up automatically.


Knowledge Base

You can customize the agent's persona by loading a knowledge base file:

pnpm dev -- --kb "./roles based Workflow/devops-expert.md"

Or select a role during the setup wizard — CLIC auto-discovers any .md files in the roles based Workflow/ folder and presents them as a menu.

Built-in roles available:

FilePersona
AI_Engineer.mdAI Engineer
Forward_Deployed_Engineer.mdForward Deployed Engineer
Gen_AI_Engineer.mdGenerative AI Engineer
genz_workflow.mdGen-Z Communication Style
Legal_Software_Advocate.mdLegal Software Advocate
Machine_Learning_Engineer.mdMachine Learning Engineer
Multi_Language_Teacher.mdMulti-Language Teacher
Senior_Devops_Engineer.mdSenior DevOps Engineer
SRE_Engineer.mdSite Reliability Engineer
TestCase_Fixer_&_Error_Resolution_Specialist.mdTest Case Fixer & Error Resolution Specialist

The file contents are appended to the system prompt as a "ROLE & KNOWLEDGE BASE" section. The agent will adopt the role while retaining all tool capabilities.

Example KB file (roles based Workflow/devops-expert.md):

You are a senior DevOps engineer specializing in AWS, Kubernetes, and CI/CD.
Always suggest infrastructure-as-code approaches.
Prefer Terraform over CloudFormation.
When troubleshooting, check logs first, then configs.

Persistent Agent Memory

CLIC maintains two independent persistence stores — both built without any third-party memory or vector-store library.

Chat History (sessions/<name>/chat_history.json)

Every conversation turn (user message + assistant response + tool calls/results) is serialised and saved as a flat array of OpenAI-compatible messages. Each named session keeps its own independent history file under sessions/<name>/chat_history.json. On the next session start (or --session <name>), the agent loads that session's array and injects it into the context window.

A session index (sessions.json) tracks all named sessions and the currently active one. On first run, any existing root chat_history.json is automatically migrated into sessions/default/chat_history.json.

sessions/
  default/chat_history.json   ←  default session history
  work/chat_history.json       ←  "work" session history
  ...
sessions.json                  ←  { active: "work", sessions: [...] }

Token Knowledge Graph (token_graph.json)

After every agent turn, CLIC writes a structured graph of what happened — which model was used, which tools were called, and how many tokens were consumed. This powers the /tokens command with accurate per-session and all-time totals.

token_graph.json  ←  Knowledge Graph
Session -[HAS_TURN]->   Turn
Turn    -[USED_MODEL]-> Model
Turn    -[CALLED_TOOL]-> Tool
Turn    -[HAS_USAGE]->  TokenUsage  { promptTokens, completionTokens, source: "actual"|"estimated" }

Token counts come directly from stream_options: { include_usage: true } in the API response. If the API omits usage (some providers), CLIC estimates at ~4 chars/token and marks the record as estimated.

Key properties

PropertyDetail
Zero dependenciesPure Node.js fs + JSON — no LangChain, no vector DB, no external memory service
Survives restartsBoth files written to disk after every turn and on /exit
Full context replayEntire message array injected back into the context window on startup
Accurate token trackingActual API usage when available; estimated fallback otherwise
Selective clearUse /clear to wipe the current session's chat history (token graph and other sessions preserved); use /session delete <name> to remove a whole session
Configurable pathsAGENT_HISTORY_FILE and AGENT_TOKEN_GRAPH_FILE env vars

Result

The agent remembers previous tasks, code it wrote, commands it ran, and conclusions it reached — across any number of sessions — without you having to re-explain context each time.

Session 1:  "Create a FastAPI server in server.py"
            → agent writes server.py, saves memory + token graph

Session 2:  "Add authentication to the server"
            → agent already knows server.py exists and what's in it
            → picks up exactly where it left off

Safety

Blocked Commands

The following patterns are blocked and will never execute:

rm -rf /    rm -rf /*    mkfs          dd if=
:(){:|:&};: fork bomb    > /dev/sda    chmod -R 777 /
shutdown    reboot       halt          init 0 / init 6
kill -9 1   mv /*        curl | bash   poweroff

Protected Paths

File operations are denied on:

/etc/passwd   /etc/shadow   /etc/sudoers   /etc/hosts
/boot/        /dev/         /proc/         /sys/
/var/log/auth

Human Approval

Every tool action (read, write, command, search, etc.) requires explicit y/n confirmation before execution. Use --yolo to skip confirmations (use with caution).


Environment Variables

VariableRequiredDescription
API_KEYYes*Your OpenAI or compatible API key (prompted interactively if missing)
BASE_URLNoOpenAI-compatible endpoint base URL (default: https://api.openai.com/v1)
AGENT_HISTORY_FILENoCustom path for legacy root chat history (default: chat_history.json)
AGENT_TOKEN_GRAPH_FILENoCustom path for token Knowledge Graph (default: token_graph.json)
AGENT_SESSIONS_DIRNoCustom path for sessions directory (default: sessions)
AGENT_SESSIONS_INDEX_FILENoCustom path for sessions index file (default: sessions.json)
CLIC_MODELNoSet automatically at startup to the active model; read by web_search and other tools at call time

Contributing

Contributions are welcome. See CONTRIBUTING.md for development setup, coding standards, testing requirements, and pull request guidance. Please also review the Code of Conduct before participating.


Security

Please do not disclose API keys or other secrets in issues, pull requests, logs, or screenshots. See SECURITY.md for responsible vulnerability reporting instructions.


Roadmap

Future work will be prioritized through GitHub issues and discussions. Areas of interest include:

  • Improving provider and model configuration workflows.
  • Expanding test coverage for interactive terminal and tool execution paths.
  • Improving documentation, examples, and contributor tooling.
  • Evaluating additional integrations while preserving the provider-agnostic core.

See the open issues for current priorities and proposed work.


FAQ

Do I need an OpenAI API key?

No. CLIC works with any OpenAI-compatible API endpoint. Set API_KEY and optionally BASE_URL in .env for your provider.

Why does CLIC ask for approval before actions?

Human approval is enabled by default for safety. Use --yolo only in a trusted environment where automatic approval is appropriate.

Why does node-pty fail to install?

Install the platform build tools required by Node.js and run pnpm install again. You can also use --no-terminals to disable the persistent PTY terminal pool and use the fallback command runner.

Can I run CLIC without saving conversation history?

Yes. Start it with --no-history to use an ephemeral session that suppresses history, token graph, and session index writes.


Claude Code Integration

CLIC ships a set of Claude Code slash commands and skills under .claude/ that let Claude Code work on this repo more effectively — running the app, reviewing code, documenting features, and keeping configuration in sync.

Multi-Agent IDE Compatibility

CLIC is compatible with all major agentic AI IDEs and coding agents — not just Claude Code.

File / DirectoryRead by
AGENTS.mdAll agentic IDEs: Cursor, Windsurf, Cline, Antigravity, Copilot Workspace, Continue.dev, Aider, Amazon Q, Devin, Gemini Code Assist, OpenAI Codex
.agents/commands/Any agent with a file-reading tool — detailed workflow instructions per task
.agents/skills/run-clic.mdAny agent — end-to-end driver workflow for headless CLIC execution
CLAUDE.mdClaude Code only — imports AGENTS.md via @AGENTS.md + Claude Code-specific additions
.claude/commands/Claude Code only — slash command execution layer
.claude/skills/Claude Code only — skills runner

AGENTS.md is the single source of truth for shared project context. CLAUDE.md imports it and extends it with Claude Code-specific tooling.


Claude Commands

Slash commands are stored in .claude/commands/ and are invoked inside a Claude Code session with /command-name. Each command is a markdown prompt file that is injected as instructions when you run it.

CommandInvokeWhat it does
run/runRun CLIC with pnpm dev (or pnpm build && pnpm start for production). Documents all CLI flags and reports what was observed in the terminal output.
verify/verifyEnd-to-end smoke test after a code change — runs CLIC in single-turn mode, checks that tool calls complete, confirms token_graph.json is updated, and reports pass/fail.
code-review/code-reviewReviews the current git diff for correctness bugs and simplification opportunities across all key files (agent.ts, openai.ts, tools/, commands/, safety.ts, memory.ts).
security-review/security-reviewAudits pending changes for security issues — command injection in runCommand.ts, path traversal in file tools, API key exposure in logs, prompt injection via readFile, and safety bypass patterns in safety.ts.
feature-review-for-specific/feature-review-for-specific <feature>Deep review of a named feature — reads every relevant file, then scores it across correctness, types, loop integration, safety, edge cases, and output. Reports findings by severity with file:line references.
clic-features-doc/clic-features-doc <feature-name>Explores the codebase and writes (or updates) a complete feature doc to docs/features/<feature-name>.md — covering architecture, data flow, key types, a core code breakdown with annotated source, workflow, configuration, edge cases, example usage, and related features.
update-context-for-future/update-context-for-futureScans the codebase for drift since the last doc update and rewrites README.md + CLAUDE.md to match the current source — key files, registered tools/commands, exported functions, env vars, version number.
claude-api/claude-apiLooks up the Claude / Anthropic API reference for the current task — model IDs, tool-use schemas, streaming delta shapes, token counting fields, pricing.
update-config/update-configReads and merges changes into .claude/settings.json — adding allowed Bash patterns, hooks, or environment variables. Never overwrites the file wholesale.
fewer-permission-prompts/fewer-permission-promptsScans this project's common dev-loop commands and adds precise Bash(...) / Read(...) allow rules to .claude/settings.json to reduce repetitive permission prompts.

Usage examples

# In a Claude Code session:
/run
/verify
/code-review
/security-review
/feature-review-for-specific parallel-tool-execution
/clic-features-doc api-retry
/update-context-for-future
/claude-api streaming tool_calls delta format
/update-config add pnpm lint to allowed commands
/fewer-permission-prompts

All commands read from source before acting — they never guess or use stale assumptions. Pass $ARGUMENTS after the command name where shown to scope the output.


Claude Skills

Skills live in .claude/skills/ and provide reusable, stateful capabilities — unlike one-shot commands, a skill can launch a process, interact with it, and report back.

run-clic skill

The run-clic skill wraps CLIC in a tmux session via a Node.js driver script, solving the core problem that @clack/prompts requires a real TTY — you cannot run pnpm dev from a backgrounded shell command. The driver provides a programmable interface for Claude Code subagents to launch, interact with, and observe the running CLIC instance.

Prerequisite: brew install tmux

Driver commands (node .claude/skills/run-clic/driver.mjs <command>):

CommandWhat it does
single <prompt>Full single-turn run — launches CLIC, selects the first model, sends the prompt, waits for ✔ Task complete, prints full output, exits
launch [model]Start an interactive REPL session in the background tmux session clic-driver
send <text>Send text + Enter to the running REPL
slash <cmd>Send a slash command (e.g. /status, /tokens) and print the pane output
capturePrint current tmux pane contents
wait <marker>Poll until a marker string appears in the pane (30 s timeout)
quitSend /exit to save history cleanly, then kill the tmux session
killForce-kill the tmux session without saving

Example — verify a code change end-to-end:

# Single-turn smoke test (most common)
node .claude/skills/run-clic/driver.mjs single "list the TypeScript files in src/"

# Interactive session — send a slash command
node .claude/skills/run-clic/driver.mjs launch
node .claude/skills/run-clic/driver.mjs slash /tokens
node .claude/skills/run-clic/driver.mjs quit

Key gotchas the skill documents:

  • --model gpt-4o does not skip the model picker — gpt-4o is DEFAULT_MODEL, so src/index.ts treats it as unset and shows the picker anyway. Pass a different model name or let the driver press Enter on the highlighted default.
  • Paths with spaces in the project root are handled by the driver passing paths through tmux environment variables (CLIC_ROOT, CLIC_TSX) rather than embedding them in shell strings.
  • chat_history.json accumulates across driver runs — run echo "[]" > chat_history.json to reset context before a clean test.

License

MIT — see LICENSE for the full text.


Owner

Uday Sharma — github.com/UdaySharmaGitHub

↑↓ navigate openesc close