jinzd-ai-cli 0.1.93 → 0.1.95
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/{chunk-BZVXXYAB.js → chunk-KKNZPNLC.js} +1 -1
- package/dist/{chunk-HLMYGNCU.js → chunk-R2NPYF4C.js} +1 -1
- package/dist/index.js +4 -4
- package/dist/{run-tests-X5ZA6FTL.js → run-tests-LYE22U6B.js} +1 -1
- package/dist/{server-G4DGAXKU.js → server-YI4KLT7T.js} +58 -4
- package/dist/web/client/icon-192.png +0 -0
- package/dist/web/client/icon-512.png +0 -0
- package/dist/web/client/icon.svg +22 -0
- package/dist/web/client/index.html +16 -3
- package/dist/web/client/manifest.json +15 -0
- package/dist/web/client/style.css +1 -1
- package/dist/web/client/sw.js +83 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
theme,
|
|
36
36
|
truncateOutput,
|
|
37
37
|
undoStack
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-R2NPYF4C.js";
|
|
39
39
|
import {
|
|
40
40
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
41
41
|
AUTHOR,
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
REPO_URL,
|
|
56
56
|
SKILLS_DIR_NAME,
|
|
57
57
|
VERSION
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-KKNZPNLC.js";
|
|
59
59
|
|
|
60
60
|
// src/index.ts
|
|
61
61
|
import { program } from "commander";
|
|
@@ -1904,7 +1904,7 @@ ${hint}` : "")
|
|
|
1904
1904
|
description: "Run project tests and show structured report",
|
|
1905
1905
|
usage: "/test [command|filter]",
|
|
1906
1906
|
async execute(args, _ctx) {
|
|
1907
|
-
const { executeTests } = await import("./run-tests-
|
|
1907
|
+
const { executeTests } = await import("./run-tests-LYE22U6B.js");
|
|
1908
1908
|
const argStr = args.join(" ").trim();
|
|
1909
1909
|
let testArgs = {};
|
|
1910
1910
|
if (argStr) {
|
|
@@ -5292,7 +5292,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
5292
5292
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
5293
5293
|
process.exit(1);
|
|
5294
5294
|
}
|
|
5295
|
-
const { startWebServer } = await import("./server-
|
|
5295
|
+
const { startWebServer } = await import("./server-YI4KLT7T.js");
|
|
5296
5296
|
await startWebServer({ port, host: options.host });
|
|
5297
5297
|
});
|
|
5298
5298
|
program.command("sessions").description("List recent conversation sessions").action(async () => {
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
setupProxy,
|
|
24
24
|
spawnAgentContext,
|
|
25
25
|
truncateOutput
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-R2NPYF4C.js";
|
|
27
27
|
import {
|
|
28
28
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
29
29
|
CONTEXT_FILE_CANDIDATES,
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
PLAN_MODE_SYSTEM_ADDON,
|
|
36
36
|
SKILLS_DIR_NAME,
|
|
37
37
|
VERSION
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-KKNZPNLC.js";
|
|
39
39
|
|
|
40
40
|
// src/web/server.ts
|
|
41
41
|
import express from "express";
|
|
@@ -524,6 +524,10 @@ var SessionHandler = class {
|
|
|
524
524
|
if (this.abortController) this.abortController.abort();
|
|
525
525
|
for (const resolve3 of this.pendingAskUser.values()) resolve3(null);
|
|
526
526
|
this.pendingAskUser.clear();
|
|
527
|
+
this.saveIfNeeded();
|
|
528
|
+
}
|
|
529
|
+
/** Save session only if it exists and has messages (never persist empty "Untitled" sessions). */
|
|
530
|
+
saveIfNeeded() {
|
|
527
531
|
if (this.sessions.current && this.sessions.current.messages.length > 0) {
|
|
528
532
|
this.sessions.save();
|
|
529
533
|
}
|
|
@@ -580,7 +584,9 @@ var SessionHandler = class {
|
|
|
580
584
|
this.send({ type: "error", message });
|
|
581
585
|
} finally {
|
|
582
586
|
this.processing = false;
|
|
587
|
+
this.saveIfNeeded();
|
|
583
588
|
this.sendStatus();
|
|
589
|
+
this.sendSessionList();
|
|
584
590
|
}
|
|
585
591
|
}
|
|
586
592
|
async handleChatSimple(provider, messages) {
|
|
@@ -873,10 +879,12 @@ ${summaryResult.content}`,
|
|
|
873
879
|
break;
|
|
874
880
|
}
|
|
875
881
|
case "clear":
|
|
882
|
+
this.saveIfNeeded();
|
|
876
883
|
this.sessions.createSession(this.currentProvider, this.currentModel);
|
|
877
884
|
this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
|
|
878
885
|
this.send({ type: "info", message: "Conversation cleared." });
|
|
879
886
|
this.sendStatus();
|
|
887
|
+
this.sendSessionList();
|
|
880
888
|
break;
|
|
881
889
|
case "compact":
|
|
882
890
|
await this.compactSession(args.join(" ") || void 0);
|
|
@@ -919,7 +927,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
919
927
|
case "session": {
|
|
920
928
|
const sub = args[0];
|
|
921
929
|
if (sub === "new") {
|
|
922
|
-
this.
|
|
930
|
+
this.saveIfNeeded();
|
|
923
931
|
this.sessions.createSession(this.currentProvider, this.currentModel);
|
|
924
932
|
this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0 };
|
|
925
933
|
this.send({ type: "info", message: "New session created." });
|
|
@@ -927,7 +935,7 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
927
935
|
this.sendSessionList();
|
|
928
936
|
} else if (sub === "load" && args[1]) {
|
|
929
937
|
const targetId = args[1];
|
|
930
|
-
this.
|
|
938
|
+
this.saveIfNeeded();
|
|
931
939
|
const list = this.sessions.listSessions();
|
|
932
940
|
const found = list.find((s) => s.id.startsWith(targetId));
|
|
933
941
|
if (found) {
|
|
@@ -997,6 +1005,9 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
997
1005
|
" /cost \u2014 Show cumulative token usage",
|
|
998
1006
|
" /tools \u2014 Show tools, MCP servers & skills in sidebar",
|
|
999
1007
|
" /export [md|json] \u2014 Export conversation as Markdown or JSON",
|
|
1008
|
+
" /skill \u2014 List available skills",
|
|
1009
|
+
" /skill <name> \u2014 Activate a skill",
|
|
1010
|
+
" /skill off \u2014 Deactivate current skill",
|
|
1000
1011
|
" /memory \u2014 Show persistent memory contents",
|
|
1001
1012
|
" /memory add <text> \u2014 Add entry to persistent memory",
|
|
1002
1013
|
" /memory clear \u2014 Clear persistent memory",
|
|
@@ -1045,6 +1056,49 @@ Tokens: in=${this.sessionTokenUsage.inputTokens} out=${this.sessionTokenUsage.ou
|
|
|
1045
1056
|
}
|
|
1046
1057
|
break;
|
|
1047
1058
|
}
|
|
1059
|
+
case "skill": {
|
|
1060
|
+
const sub = args[0];
|
|
1061
|
+
if (!this.skillManager) {
|
|
1062
|
+
this.send({ type: "error", message: "Skill system not available." });
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
if (!sub || sub === "list") {
|
|
1066
|
+
const skills = this.skillManager.listSkills();
|
|
1067
|
+
const active = this.skillManager.getActive();
|
|
1068
|
+
if (skills.length === 0) {
|
|
1069
|
+
this.send({ type: "info", message: "No skills available. Add .md files to ~/.aicli/skills/" });
|
|
1070
|
+
} else {
|
|
1071
|
+
const lines = skills.map((s) => {
|
|
1072
|
+
const isActive = active?.meta.name === s.meta.name;
|
|
1073
|
+
const marker = isActive ? " \u2705 (active)" : "";
|
|
1074
|
+
return ` ${s.meta.name}${marker} \u2014 ${s.meta.description || "(no description)"}`;
|
|
1075
|
+
});
|
|
1076
|
+
this.send({ type: "info", message: `\u{1F4DA} Skills:
|
|
1077
|
+
${lines.join("\n")}` });
|
|
1078
|
+
}
|
|
1079
|
+
} else if (sub === "off") {
|
|
1080
|
+
this.skillManager.deactivate();
|
|
1081
|
+
this.send({ type: "info", message: "\u{1F50C} Skill deactivated." });
|
|
1082
|
+
this.sendToolsList();
|
|
1083
|
+
this.sendStatus();
|
|
1084
|
+
} else if (sub === "reload") {
|
|
1085
|
+
this.skillManager.loadSkills();
|
|
1086
|
+
this.send({ type: "info", message: `\u{1F504} Reloaded ${this.skillManager.listSkills().length} skill(s).` });
|
|
1087
|
+
this.sendToolsList();
|
|
1088
|
+
} else {
|
|
1089
|
+
const activated = this.skillManager.activate(sub);
|
|
1090
|
+
if (activated) {
|
|
1091
|
+
this.send({ type: "info", message: `\u2705 Skill activated: ${activated.meta.name}
|
|
1092
|
+
${activated.meta.description || ""}` });
|
|
1093
|
+
this.sendToolsList();
|
|
1094
|
+
this.sendStatus();
|
|
1095
|
+
} else {
|
|
1096
|
+
const available = this.skillManager.listSkills().map((s) => s.meta.name).join(", ");
|
|
1097
|
+
this.send({ type: "error", message: `Skill "${sub}" not found. Available: ${available}` });
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
break;
|
|
1101
|
+
}
|
|
1048
1102
|
default:
|
|
1049
1103
|
this.send({ type: "error", message: `Unknown command: /${name}. Type /help for available commands.` });
|
|
1050
1104
|
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
2
|
+
<rect width="512" height="512" rx="96" fill="#1d232a"/>
|
|
3
|
+
<g transform="translate(256,240)">
|
|
4
|
+
<!-- Head -->
|
|
5
|
+
<rect x="-120" y="-100" width="240" height="180" rx="32" fill="#6419e6"/>
|
|
6
|
+
<!-- Eyes -->
|
|
7
|
+
<circle cx="-50" cy="-20" r="28" fill="#fff"/>
|
|
8
|
+
<circle cx="50" cy="-20" r="28" fill="#fff"/>
|
|
9
|
+
<circle cx="-50" cy="-20" r="14" fill="#1d232a"/>
|
|
10
|
+
<circle cx="50" cy="-20" r="14" fill="#1d232a"/>
|
|
11
|
+
<!-- Mouth -->
|
|
12
|
+
<rect x="-40" y="30" width="80" height="12" rx="6" fill="#fff" opacity="0.8"/>
|
|
13
|
+
<!-- Antenna -->
|
|
14
|
+
<line x1="0" y1="-100" x2="0" y2="-140" stroke="#6419e6" stroke-width="8" stroke-linecap="round"/>
|
|
15
|
+
<circle cx="0" cy="-148" r="12" fill="#a855f7"/>
|
|
16
|
+
<!-- Ears -->
|
|
17
|
+
<rect x="-140" y="-60" width="24" height="60" rx="12" fill="#a855f7"/>
|
|
18
|
+
<rect x="116" y="-60" width="24" height="60" rx="12" fill="#a855f7"/>
|
|
19
|
+
</g>
|
|
20
|
+
<!-- Text -->
|
|
21
|
+
<text x="256" y="420" text-anchor="middle" fill="#fff" font-family="system-ui,sans-serif" font-weight="700" font-size="72">ai-cli</text>
|
|
22
|
+
</svg>
|
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>ai-cli Web UI</title>
|
|
7
|
+
<!-- PWA -->
|
|
8
|
+
<link rel="manifest" href="manifest.json">
|
|
9
|
+
<meta name="theme-color" content="#6419e6">
|
|
10
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
11
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
12
|
+
<link rel="apple-touch-icon" href="icon-192.png">
|
|
13
|
+
<link rel="icon" type="image/svg+xml" href="icon.svg">
|
|
14
|
+
<link rel="icon" type="image/png" sizes="192x192" href="icon-192.png">
|
|
7
15
|
<!-- Tailwind CSS + DaisyUI (CDN) -->
|
|
8
16
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.23/dist/full.min.css" rel="stylesheet">
|
|
9
17
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
@@ -49,7 +57,7 @@
|
|
|
49
57
|
<div class="flex flex-1 overflow-hidden">
|
|
50
58
|
|
|
51
59
|
<!-- Sidebar -->
|
|
52
|
-
<aside id="sidebar" class="sidebar bg-base-200 border-r border-base-content/10 flex flex-col w-
|
|
60
|
+
<aside id="sidebar" class="sidebar bg-base-200 border-r border-base-content/10 flex flex-col w-72 flex-shrink-0 overflow-hidden transition-all duration-200">
|
|
53
61
|
<!-- Sidebar tabs -->
|
|
54
62
|
<div class="flex border-b border-base-content/10 flex-shrink-0">
|
|
55
63
|
<button class="sidebar-tab active flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="sessions">📋 Sessions</button>
|
|
@@ -59,9 +67,9 @@
|
|
|
59
67
|
<!-- Sessions tab -->
|
|
60
68
|
<div id="tab-sessions" class="sidebar-tab-content flex flex-col flex-1 overflow-hidden">
|
|
61
69
|
<div class="p-2 border-b border-base-content/10 flex items-center justify-between gap-1">
|
|
62
|
-
<input id="session-search" type="text" class="input input-xs input-bordered flex-1" placeholder="Search
|
|
70
|
+
<input id="session-search" type="text" class="input input-xs input-bordered flex-1 min-w-0" placeholder="Search...">
|
|
63
71
|
<button id="btn-batch-select" class="btn btn-xs btn-ghost flex-shrink-0" title="Select multiple" onclick="toggleBatchSelect()">☑</button>
|
|
64
|
-
<button id="btn-new-session" class="btn btn-xs btn-primary btn-outline flex-shrink-0" title="New session">+ New</button>
|
|
72
|
+
<button id="btn-new-session" class="btn btn-xs btn-primary btn-outline flex-shrink-0 whitespace-nowrap" title="New session">+ New</button>
|
|
65
73
|
</div>
|
|
66
74
|
<!-- Batch action bar (hidden by default) -->
|
|
67
75
|
<div id="batch-bar" class="hidden px-2 py-1 border-b border-base-content/10 flex items-center gap-1 bg-base-300 text-xs">
|
|
@@ -187,5 +195,10 @@
|
|
|
187
195
|
</dialog>
|
|
188
196
|
|
|
189
197
|
<script src="app.js"></script>
|
|
198
|
+
<script>
|
|
199
|
+
if ('serviceWorker' in navigator) {
|
|
200
|
+
navigator.serviceWorker.register('sw.js').catch(() => {});
|
|
201
|
+
}
|
|
202
|
+
</script>
|
|
190
203
|
</body>
|
|
191
204
|
</html>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-cli Web UI",
|
|
3
|
+
"short_name": "ai-cli",
|
|
4
|
+
"description": "Cross-platform AI chat assistant with agentic tool calling",
|
|
5
|
+
"start_url": "/",
|
|
6
|
+
"display": "standalone",
|
|
7
|
+
"background_color": "#1d232a",
|
|
8
|
+
"theme_color": "#6419e6",
|
|
9
|
+
"orientation": "any",
|
|
10
|
+
"icons": [
|
|
11
|
+
{ "src": "icon-192.png", "sizes": "192x192", "type": "image/png" },
|
|
12
|
+
{ "src": "icon-512.png", "sizes": "512x512", "type": "image/png" },
|
|
13
|
+
{ "src": "icon.svg", "sizes": "any", "type": "image/svg+xml" }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -562,7 +562,7 @@
|
|
|
562
562
|
/* ── Responsive ─────────────────────────────────────── */
|
|
563
563
|
@media (max-width: 768px) {
|
|
564
564
|
.sidebar { width: 0; padding: 0; border: none; }
|
|
565
|
-
.sidebar.sidebar-open { width:
|
|
565
|
+
.sidebar.sidebar-open { width: 18rem; position: absolute; z-index: 20; height: calc(100vh - 3.5rem); top: 3.5rem; }
|
|
566
566
|
}
|
|
567
567
|
@media (max-width: 640px) {
|
|
568
568
|
.navbar-start .select { width: 6rem; font-size: 0.75rem; }
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-cli PWA Service Worker
|
|
3
|
+
* Caches app shell (HTML/CSS/JS/icons) for fast startup.
|
|
4
|
+
* API calls and WebSocket are always network-first.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const CACHE_NAME = 'aicli-v1';
|
|
8
|
+
|
|
9
|
+
// App shell — files needed for offline-capable startup
|
|
10
|
+
const APP_SHELL = [
|
|
11
|
+
'/',
|
|
12
|
+
'/index.html',
|
|
13
|
+
'/style.css',
|
|
14
|
+
'/app.js',
|
|
15
|
+
'/manifest.json',
|
|
16
|
+
'/icon.svg',
|
|
17
|
+
'/icon-192.png',
|
|
18
|
+
'/icon-512.png',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// CDN resources to cache on first use
|
|
22
|
+
const CDN_CACHE = [
|
|
23
|
+
'cdn.jsdelivr.net',
|
|
24
|
+
'cdn.tailwindcss.com',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
self.addEventListener('install', (event) => {
|
|
28
|
+
event.waitUntil(
|
|
29
|
+
caches.open(CACHE_NAME)
|
|
30
|
+
.then(cache => cache.addAll(APP_SHELL))
|
|
31
|
+
.then(() => self.skipWaiting())
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
self.addEventListener('activate', (event) => {
|
|
36
|
+
event.waitUntil(
|
|
37
|
+
caches.keys().then(keys =>
|
|
38
|
+
Promise.all(keys
|
|
39
|
+
.filter(k => k !== CACHE_NAME)
|
|
40
|
+
.map(k => caches.delete(k))
|
|
41
|
+
)
|
|
42
|
+
).then(() => self.clients.claim())
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
self.addEventListener('fetch', (event) => {
|
|
47
|
+
const url = new URL(event.request.url);
|
|
48
|
+
|
|
49
|
+
// Never cache API calls or WebSocket upgrades
|
|
50
|
+
if (url.pathname.startsWith('/api/') || event.request.headers.get('upgrade') === 'websocket') {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// CDN resources: cache-first (they're versioned by URL)
|
|
55
|
+
if (CDN_CACHE.some(host => url.hostname.includes(host))) {
|
|
56
|
+
event.respondWith(
|
|
57
|
+
caches.match(event.request).then(cached => {
|
|
58
|
+
if (cached) return cached;
|
|
59
|
+
return fetch(event.request).then(response => {
|
|
60
|
+
if (response.ok) {
|
|
61
|
+
const clone = response.clone();
|
|
62
|
+
caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
|
|
63
|
+
}
|
|
64
|
+
return response;
|
|
65
|
+
});
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// App shell: network-first with cache fallback (always get latest from server)
|
|
72
|
+
event.respondWith(
|
|
73
|
+
fetch(event.request)
|
|
74
|
+
.then(response => {
|
|
75
|
+
if (response.ok) {
|
|
76
|
+
const clone = response.clone();
|
|
77
|
+
caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
|
|
78
|
+
}
|
|
79
|
+
return response;
|
|
80
|
+
})
|
|
81
|
+
.catch(() => caches.match(event.request))
|
|
82
|
+
);
|
|
83
|
+
});
|