codemini-cli 0.1.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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/OPERATIONS.md +202 -0
  3. package/README.md +138 -0
  4. package/bin/coder.js +7 -0
  5. package/deployment.md +205 -0
  6. package/package.json +54 -0
  7. package/skills/brainstorming-lite/SKILL.md +37 -0
  8. package/skills/executing-plan-lite/SKILL.md +41 -0
  9. package/skills/superpowers-lite/SKILL.md +44 -0
  10. package/souls/anime.md +3 -0
  11. package/souls/default.md +3 -0
  12. package/souls/playful.md +3 -0
  13. package/souls/professional.md +3 -0
  14. package/src/cli.js +62 -0
  15. package/src/commands/chat.js +106 -0
  16. package/src/commands/config.js +61 -0
  17. package/src/commands/doctor.js +87 -0
  18. package/src/commands/run.js +64 -0
  19. package/src/commands/skill.js +264 -0
  20. package/src/core/agent-loop.js +281 -0
  21. package/src/core/chat-runtime.js +2075 -0
  22. package/src/core/checkpoint-store.js +66 -0
  23. package/src/core/command-loader.js +201 -0
  24. package/src/core/command-policy.js +71 -0
  25. package/src/core/config-store.js +196 -0
  26. package/src/core/context-compact.js +90 -0
  27. package/src/core/default-system-prompt.js +5 -0
  28. package/src/core/fs-utils.js +16 -0
  29. package/src/core/input-history-store.js +48 -0
  30. package/src/core/input-parser.js +15 -0
  31. package/src/core/paths.js +109 -0
  32. package/src/core/provider/openai-compatible.js +228 -0
  33. package/src/core/session-store.js +178 -0
  34. package/src/core/shell-profile.js +122 -0
  35. package/src/core/shell.js +71 -0
  36. package/src/core/skill-registry.js +55 -0
  37. package/src/core/soul.js +55 -0
  38. package/src/core/task-store.js +116 -0
  39. package/src/core/tools.js +237 -0
  40. package/src/tui/chat-app.js +2007 -0
  41. package/src/tui/input-escape.js +21 -0
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: superpowers-lite
3
+ description: Concise workflow skill tuned for 30B-class models: search first, keep context tight, use sub-agents for narrow tasks, and verify before claiming success.
4
+ version: 0.1.0
5
+ ---
6
+
7
+ Use this skill as the default lightweight operating style for coding work.
8
+
9
+ Core rules:
10
+
11
+ 1. Search first.
12
+ Use `rg` for repo search before broad file reads. Prefer local context commands and read only the smallest useful slice.
13
+
14
+ 2. Keep context tight.
15
+ Do not carry full conversation history into every step. Summarize, narrow scope, and work from the most recent relevant evidence.
16
+
17
+ 3. Prefer narrow sub-agents.
18
+ When a task can be split cleanly, use sub-agents for bounded subtasks so the main thread keeps global focus. Give each sub-agent:
19
+ - one clear objective
20
+ - a tiny context summary
21
+ - a tiny file evidence packet
22
+ - a concrete expected output
23
+
24
+ 4. Read and write with intent.
25
+ Use `read_file` only when shell output is not enough. Use `write_file` for edits. Avoid unnecessary tool calls and avoid rereading the same file without a reason.
26
+
27
+ 5. Verify before claiming success.
28
+ Run the relevant test, check, or command before saying work is fixed or complete.
29
+
30
+ Default workflow:
31
+ - Search with `rg`
32
+ - Inspect local context
33
+ - Plan the next smallest step
34
+ - Delegate if the work is independent
35
+ - Edit
36
+ - Verify
37
+ - Summarize briefly
38
+
39
+ Sub-agent guidance:
40
+ - `planner`: break work into steps, risks, and checks
41
+ - `coder`: implement one bounded change
42
+ - `reviewer`: look for bugs, regressions, and missing verification
43
+
44
+ If the task is simple, stay lightweight. Do not expand into a large ceremony unless the problem actually needs it.
package/souls/anime.md ADDED
@@ -0,0 +1,3 @@
1
+ Respond with a light anime-inspired tone.
2
+ Keep the content useful and technically accurate, but add a playful, upbeat style.
3
+ Do not overdo catchphrases, and do not let the style reduce clarity.
@@ -0,0 +1,3 @@
1
+ Respond in a clear, calm, helpful tone.
2
+ Be concise, friendly, and practical.
3
+ Avoid roleplay, slang overload, or exaggerated personality.
@@ -0,0 +1,3 @@
1
+ Respond with a witty and lively tone.
2
+ Keep the answer readable and practical first, but allow a bit more personality and humor.
3
+ Do not let humor obscure instructions or technical accuracy.
@@ -0,0 +1,3 @@
1
+ Respond in a polished, professional tone.
2
+ Keep phrasing precise, confident, and concise.
3
+ Prefer structured explanations over playful expression.
package/src/cli.js ADDED
@@ -0,0 +1,62 @@
1
+ import { handleChat } from './commands/chat.js';
2
+ import { handleRun } from './commands/run.js';
3
+ import { handleConfig } from './commands/config.js';
4
+ import { handleDoctor } from './commands/doctor.js';
5
+ import { handleSkill } from './commands/skill.js';
6
+
7
+ const VERSION = '0.1.0';
8
+
9
+ function printHelp() {
10
+ console.log(`codemini ${VERSION}
11
+ Usage:
12
+ codemini [prompt] [--plain]
13
+ codemini chat [prompt] [--plain]
14
+ codemini run <task> [--max-steps N]
15
+ codemini config set|get|list <key> [value]
16
+ codemini doctor
17
+ codemini skill list|install|enable|disable|inspect|reindex
18
+ codemini --version
19
+ codemini --help`);
20
+ }
21
+
22
+ export async function runCli(args) {
23
+ const [command, ...rest] = args;
24
+ const knownCommands = new Set(['chat', 'run', 'config', 'doctor', 'skill']);
25
+
26
+ if (!command || command === '--help' || command === '-h') {
27
+ if (!command) {
28
+ await handleChat([]);
29
+ return;
30
+ }
31
+ printHelp();
32
+ return;
33
+ }
34
+
35
+ if (command === '--version' || command === '-v' || command === 'version') {
36
+ console.log(VERSION);
37
+ return;
38
+ }
39
+
40
+ if (!knownCommands.has(command)) {
41
+ await handleChat(args);
42
+ return;
43
+ }
44
+
45
+ switch (command) {
46
+ case 'chat':
47
+ await handleChat(rest);
48
+ return;
49
+ case 'run':
50
+ await handleRun(rest);
51
+ return;
52
+ case 'config':
53
+ await handleConfig(rest);
54
+ return;
55
+ case 'doctor':
56
+ await handleDoctor();
57
+ return;
58
+ case 'skill':
59
+ await handleSkill(rest);
60
+ return;
61
+ }
62
+ }
@@ -0,0 +1,106 @@
1
+ import readline from 'node:readline/promises';
2
+ import { stdin as input, stdout as output } from 'node:process';
3
+ import { loadConfig } from '../core/config-store.js';
4
+ import { createChatRuntime } from '../core/chat-runtime.js';
5
+ import { buildDefaultSystemPrompt } from '../core/default-system-prompt.js';
6
+ import { resolveSession } from '../core/session-store.js';
7
+ import pkg from '../../package.json' with { type: 'json' };
8
+
9
+ function parseChatArgs(args) {
10
+ const parsed = {
11
+ prompt: '',
12
+ sessionId: undefined,
13
+ model: undefined,
14
+ system: undefined,
15
+ plain: false
16
+ };
17
+
18
+ for (let i = 0; i < args.length; i += 1) {
19
+ const arg = args[i];
20
+ if (arg === '--session') {
21
+ parsed.sessionId = args[i + 1];
22
+ i += 1;
23
+ continue;
24
+ }
25
+ if (arg === '--model') {
26
+ parsed.model = args[i + 1];
27
+ i += 1;
28
+ continue;
29
+ }
30
+ if (arg === '--system') {
31
+ parsed.system = args[i + 1];
32
+ i += 1;
33
+ continue;
34
+ }
35
+ if (arg === '--plain') {
36
+ parsed.plain = true;
37
+ continue;
38
+ }
39
+ parsed.prompt += `${parsed.prompt ? ' ' : ''}${arg}`;
40
+ }
41
+
42
+ return parsed;
43
+ }
44
+
45
+ async function runPlainLoop(runtime) {
46
+ console.log('CodeMini CLI plain mode. Use /help and /exit.');
47
+ const rl = readline.createInterface({ input, output });
48
+ try {
49
+ while (true) {
50
+ let line;
51
+ try {
52
+ line = await rl.question('codemini> ');
53
+ } catch {
54
+ break;
55
+ }
56
+ const result = await runtime.submit(line);
57
+ if (result.type === 'exit') break;
58
+ if (result.type === 'noop') continue;
59
+ if (result.text) console.log(result.text);
60
+ }
61
+ } finally {
62
+ rl.close();
63
+ }
64
+ }
65
+
66
+ export async function handleChat(args) {
67
+ const parsed = parseChatArgs(args);
68
+ const config = await loadConfig();
69
+ const session = await resolveSession(parsed.sessionId);
70
+ const systemPrompt =
71
+ parsed.system ||
72
+ buildDefaultSystemPrompt(config);
73
+
74
+ const runtime = await createChatRuntime({
75
+ session,
76
+ config,
77
+ model: parsed.model,
78
+ systemPrompt
79
+ });
80
+
81
+ if (parsed.prompt) {
82
+ const result = await runtime.submit(parsed.prompt);
83
+ if (result.text) console.log(result.text);
84
+ return;
85
+ }
86
+
87
+ if (parsed.plain || !process.stdout.isTTY) {
88
+ await runPlainLoop(runtime);
89
+ return;
90
+ }
91
+
92
+ const React = (await import('react')).default;
93
+ const { render } = await import('ink');
94
+ const { ChatApp } = await import('../tui/chat-app.js');
95
+ const instance = render(
96
+ React.createElement(ChatApp, {
97
+ runtime,
98
+ sessionId: session.id,
99
+ model: parsed.model || config.model.name,
100
+ language: config.ui?.language || 'zh',
101
+ shellName: config.shell?.default || 'powershell',
102
+ version: pkg.version
103
+ })
104
+ );
105
+ await instance.waitUntilExit();
106
+ }
@@ -0,0 +1,61 @@
1
+ import { getConfigValue, loadConfig, resetConfig, setConfigValue } from '../core/config-store.js';
2
+
3
+ function usage() {
4
+ console.log(`Usage:
5
+ codemini config set <key> <value>
6
+ codemini config get <key>
7
+ codemini config list
8
+ codemini config reset`);
9
+ }
10
+
11
+ export async function handleConfig(args) {
12
+ const [sub, ...rest] = args;
13
+
14
+ if (!sub) {
15
+ usage();
16
+ return;
17
+ }
18
+
19
+ if (sub === 'set') {
20
+ const [key, ...valueParts] = rest;
21
+ if (!key || valueParts.length === 0) {
22
+ throw new Error('config set requires <key> <value>');
23
+ }
24
+ const value = valueParts.join(' ');
25
+ await setConfigValue(key, value);
26
+ console.log(`Set ${key}=${value}`);
27
+ return;
28
+ }
29
+
30
+ if (sub === 'get') {
31
+ const [key] = rest;
32
+ if (!key) {
33
+ throw new Error('config get requires <key>');
34
+ }
35
+ const value = await getConfigValue(key);
36
+ if (value === undefined) {
37
+ console.log('undefined');
38
+ return;
39
+ }
40
+ if (typeof value === 'object') {
41
+ console.log(JSON.stringify(value, null, 2));
42
+ return;
43
+ }
44
+ console.log(String(value));
45
+ return;
46
+ }
47
+
48
+ if (sub === 'list') {
49
+ const config = await loadConfig();
50
+ console.log(JSON.stringify(config, null, 2));
51
+ return;
52
+ }
53
+
54
+ if (sub === 'reset') {
55
+ await resetConfig();
56
+ console.log('Config reset complete');
57
+ return;
58
+ }
59
+
60
+ usage();
61
+ }
@@ -0,0 +1,87 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { getConfigFilePath, getSessionsDir, getSkillsDir } from '../core/paths.js';
4
+ import { loadConfig } from '../core/config-store.js';
5
+
6
+ async function checkPathWritable(targetPath) {
7
+ try {
8
+ await fs.mkdir(targetPath, { recursive: true });
9
+ const tmp = `${targetPath}/.doctor-write-test`;
10
+ await fs.writeFile(tmp, 'ok', 'utf8');
11
+ await fs.unlink(tmp);
12
+ return true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ async function checkGateway(config) {
19
+ if (!config.gateway.base_url || !config.gateway.api_key) {
20
+ return { ok: false, reason: 'gateway.base_url or gateway.api_key missing' };
21
+ }
22
+ const url = `${config.gateway.base_url.replace(/\/$/, '')}/models`;
23
+ try {
24
+ const res = await fetch(url, {
25
+ method: 'GET',
26
+ headers: { Authorization: `Bearer ${config.gateway.api_key}` }
27
+ });
28
+ if (!res.ok) {
29
+ return { ok: false, reason: `HTTP ${res.status}` };
30
+ }
31
+ return { ok: true, reason: 'reachable' };
32
+ } catch (err) {
33
+ return { ok: false, reason: err.message };
34
+ }
35
+ }
36
+
37
+ export async function handleDoctor() {
38
+ const config = await loadConfig();
39
+ const checks = [];
40
+
41
+ checks.push({
42
+ name: 'Node.js version',
43
+ ok: Number(process.versions.node.split('.')[0]) >= 20,
44
+ detail: process.versions.node
45
+ });
46
+
47
+ checks.push({
48
+ name: 'Platform',
49
+ ok: process.platform === 'win32',
50
+ detail: process.platform === 'win32' ? 'win32' : `non-win32 (${process.platform})`
51
+ });
52
+
53
+ checks.push({
54
+ name: 'Config file writable',
55
+ ok: await checkPathWritable(path.dirname(getConfigFilePath())),
56
+ detail: getConfigFilePath()
57
+ });
58
+
59
+ checks.push({
60
+ name: 'Sessions dir writable',
61
+ ok: await checkPathWritable(getSessionsDir()),
62
+ detail: getSessionsDir()
63
+ });
64
+
65
+ checks.push({
66
+ name: 'Skills dir writable',
67
+ ok: await checkPathWritable(getSkillsDir()),
68
+ detail: getSkillsDir()
69
+ });
70
+
71
+ const gateway = await checkGateway(config);
72
+ checks.push({
73
+ name: 'Gateway connectivity',
74
+ ok: gateway.ok,
75
+ detail: gateway.reason
76
+ });
77
+
78
+ for (const check of checks) {
79
+ const mark = check.ok ? 'OK' : 'FAIL';
80
+ console.log(`[${mark}] ${check.name}: ${check.detail}`);
81
+ }
82
+
83
+ const failed = checks.filter((c) => !c.ok).length;
84
+ if (failed > 0) {
85
+ process.exitCode = 1;
86
+ }
87
+ }
@@ -0,0 +1,64 @@
1
+ import { loadConfig } from '../core/config-store.js';
2
+ import { buildDefaultSystemPrompt } from '../core/default-system-prompt.js';
3
+ import { runAgentLoop } from '../core/agent-loop.js';
4
+ import { createChatCompletion } from '../core/provider/openai-compatible.js';
5
+ import { buildSystemPromptWithSoul } from '../core/soul.js';
6
+ import { getBuiltinTools } from '../core/tools.js';
7
+
8
+ function parseRunArgs(args) {
9
+ const parsed = {
10
+ task: '',
11
+ model: undefined,
12
+ maxSteps: 8
13
+ };
14
+ for (let i = 0; i < args.length; i += 1) {
15
+ const arg = args[i];
16
+ if (arg === '--model') {
17
+ parsed.model = args[i + 1];
18
+ i += 1;
19
+ continue;
20
+ }
21
+ if (arg === '--max-steps') {
22
+ parsed.maxSteps = Number(args[i + 1] || 8);
23
+ i += 1;
24
+ continue;
25
+ }
26
+ parsed.task += `${parsed.task ? ' ' : ''}${arg}`;
27
+ }
28
+ return parsed;
29
+ }
30
+
31
+ export async function handleRun(args) {
32
+ const parsed = parseRunArgs(args);
33
+ if (!parsed.task) {
34
+ throw new Error('run requires <task>');
35
+ }
36
+
37
+ const config = await loadConfig();
38
+ const { definitions, handlers } = getBuiltinTools({
39
+ workspaceRoot: process.cwd(),
40
+ config
41
+ });
42
+ const systemPrompt = await buildSystemPromptWithSoul(buildDefaultSystemPrompt(config), config);
43
+
44
+ const result = await runAgentLoop({
45
+ systemPrompt,
46
+ userPrompt: parsed.task,
47
+ model: parsed.model || config.model.name,
48
+ toolDefinitions: definitions,
49
+ toolHandlers: handlers,
50
+ maxSteps: parsed.maxSteps,
51
+ requestCompletion: async ({ messages, tools, model }) =>
52
+ createChatCompletion({
53
+ baseUrl: config.gateway.base_url,
54
+ apiKey: config.gateway.api_key,
55
+ model,
56
+ messages,
57
+ tools,
58
+ timeoutMs: config.gateway.timeout_ms || 90000,
59
+ maxRetries: config.gateway.max_retries ?? 2
60
+ })
61
+ });
62
+
63
+ console.log(result.text);
64
+ }