jinzd-ai-cli 0.4.207 → 0.4.208
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/{batch-6PORMJWT.js → batch-3T44NFLG.js} +2 -2
- package/dist/{chunk-QQYSZMET.js → chunk-2OVMMSGQ.js} +3 -3
- package/dist/{chunk-2WV6AGHM.js → chunk-J22B3OSQ.js} +1 -1
- package/dist/{chunk-QNUVZO5X.js → chunk-KV4GYNEA.js} +1 -1
- package/dist/{chunk-KJHMRAJU.js → chunk-P7JY3SWA.js} +1 -1
- package/dist/{chunk-BQIV4ZQT.js → chunk-QDY72ABW.js} +1 -1
- package/dist/{chunk-KOU4KX7J.js → chunk-TD7JEHCR.js} +2 -2
- package/dist/{chunk-TW47X5AO.js → chunk-TM4MS63K.js} +1 -1
- package/dist/{chunk-SFCNFX42.js → chunk-VWYUB22Y.js} +1 -1
- package/dist/{ci-AYMV66P3.js → ci-6QWBVRJX.js} +2 -2
- package/dist/{constants-NHGTSHKT.js → constants-SZTQNN7K.js} +1 -1
- package/dist/{doctor-cli-WKH7T4AW.js → doctor-cli-I2Y2YR4V.js} +4 -4
- package/dist/electron-server.js +2 -2
- package/dist/{hub-6R3M3NTC.js → hub-XVPFBODB.js} +1 -1
- package/dist/index.js +16 -16
- package/dist/{run-tests-5F2OBWIK.js → run-tests-D5SNUBYX.js} +2 -2
- package/dist/{run-tests-NBCA4KEG.js → run-tests-HZ2CRCTZ.js} +1 -1
- package/dist/{server-L2MXWYF7.js → server-4EFTRFTN.js} +7 -7
- package/dist/{server-37EXC4EB.js → server-PVKLPRUP.js} +4 -4
- package/dist/{task-orchestrator-2DTDVCMR.js → task-orchestrator-2A5VCFLS.js} +4 -4
- package/dist/{usage-SZWZFX3T.js → usage-Q24E5636.js} +2 -2
- package/dist/web/client/actions.js +78 -78
- package/dist/web/client/app.js +5 -5
- package/dist/web/client/index.html +1 -1
- package/dist/web/client/templates.js +188 -188
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConfigManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TM4MS63K.js";
|
|
5
5
|
import "./chunk-TZQHYZKT.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-KV4GYNEA.js";
|
|
7
7
|
import {
|
|
8
8
|
atomicWriteFileSync
|
|
9
9
|
} from "./chunk-IW3Q7AE5.js";
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
} from "./chunk-T2NL5ZIA.js";
|
|
6
6
|
import {
|
|
7
7
|
runTestsTool
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-VWYUB22Y.js";
|
|
9
9
|
import {
|
|
10
10
|
runTool
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-J22B3OSQ.js";
|
|
12
12
|
import {
|
|
13
13
|
getDangerLevel,
|
|
14
14
|
isFileWriteTool,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
SUBAGENT_ALLOWED_TOOLS,
|
|
27
27
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
28
28
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-KV4GYNEA.js";
|
|
30
30
|
import {
|
|
31
31
|
fileCheckpoints
|
|
32
32
|
} from "./chunk-4BKXL7SM.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
truncateForPersist
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2OVMMSGQ.js";
|
|
5
5
|
import {
|
|
6
6
|
APP_NAME,
|
|
7
7
|
CONFIG_DIR_NAME,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
MCP_PROTOCOL_VERSION,
|
|
12
12
|
MCP_TOOL_PREFIX,
|
|
13
13
|
VERSION
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-KV4GYNEA.js";
|
|
15
15
|
|
|
16
16
|
// src/mcp/client.ts
|
|
17
17
|
import { spawn } from "child_process";
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
import "./chunk-XPBEJB27.js";
|
|
11
11
|
import {
|
|
12
12
|
ConfigManager
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TM4MS63K.js";
|
|
14
14
|
import "./chunk-TZQHYZKT.js";
|
|
15
15
|
import {
|
|
16
16
|
VERSION
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-KV4GYNEA.js";
|
|
18
18
|
|
|
19
19
|
// src/cli/ci.ts
|
|
20
20
|
import { execFileSync } from "child_process";
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getConfigDirUsage,
|
|
4
4
|
listRecentCrashes
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-P7JY3SWA.js";
|
|
6
6
|
import {
|
|
7
7
|
ProviderRegistry
|
|
8
8
|
} from "./chunk-QMXC327F.js";
|
|
@@ -11,17 +11,17 @@ import {
|
|
|
11
11
|
getTopFailingTools,
|
|
12
12
|
getTopUsedTools,
|
|
13
13
|
resetStats
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-J22B3OSQ.js";
|
|
15
15
|
import "./chunk-XPBEJB27.js";
|
|
16
16
|
import {
|
|
17
17
|
ConfigManager
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-TM4MS63K.js";
|
|
19
19
|
import "./chunk-TZQHYZKT.js";
|
|
20
20
|
import {
|
|
21
21
|
DEV_STATE_FILE_NAME,
|
|
22
22
|
MEMORY_FILE_NAME,
|
|
23
23
|
VERSION
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-KV4GYNEA.js";
|
|
25
25
|
import "./chunk-IW3Q7AE5.js";
|
|
26
26
|
|
|
27
27
|
// src/diagnostics/doctor-cli.ts
|
package/dist/electron-server.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
VERSION,
|
|
37
37
|
buildUserIdentityPrompt,
|
|
38
38
|
runTestsTool
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-QDY72ABW.js";
|
|
40
40
|
import {
|
|
41
41
|
hasSemanticIndex,
|
|
42
42
|
semanticSearch
|
|
@@ -14373,7 +14373,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
14373
14373
|
case "test": {
|
|
14374
14374
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
14375
14375
|
try {
|
|
14376
|
-
const { executeTests } = await import("./run-tests-
|
|
14376
|
+
const { executeTests } = await import("./run-tests-HZ2CRCTZ.js");
|
|
14377
14377
|
const argStr = args.join(" ").trim();
|
|
14378
14378
|
let testArgs = {};
|
|
14379
14379
|
if (argStr) {
|
|
@@ -154,7 +154,7 @@ ${content}`);
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
157
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
157
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-2A5VCFLS.js");
|
|
158
158
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
159
159
|
let interrupted = false;
|
|
160
160
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
saveDevState,
|
|
16
16
|
sessionHasMeaningfulContent,
|
|
17
17
|
setupProxy
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-TD7JEHCR.js";
|
|
19
19
|
import {
|
|
20
20
|
ToolExecutor,
|
|
21
21
|
ToolRegistry,
|
|
@@ -35,10 +35,10 @@ import {
|
|
|
35
35
|
spawnAgentContext,
|
|
36
36
|
theme,
|
|
37
37
|
undoStack
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-2OVMMSGQ.js";
|
|
39
39
|
import "./chunk-T2NL5ZIA.js";
|
|
40
40
|
import "./chunk-BXP6YZ2P.js";
|
|
41
|
-
import "./chunk-
|
|
41
|
+
import "./chunk-VWYUB22Y.js";
|
|
42
42
|
import {
|
|
43
43
|
SessionManager,
|
|
44
44
|
getContentText
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
getConfigDirUsage,
|
|
56
56
|
listRecentCrashes,
|
|
57
57
|
writeCrashLog
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-P7JY3SWA.js";
|
|
59
59
|
import {
|
|
60
60
|
ProviderRegistry
|
|
61
61
|
} from "./chunk-QMXC327F.js";
|
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
getTopFailingTools,
|
|
65
65
|
getTopUsedTools,
|
|
66
66
|
installFlushOnExit
|
|
67
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-J22B3OSQ.js";
|
|
68
68
|
import {
|
|
69
69
|
CONTENT_ONLY_STREAM_REMINDER,
|
|
70
70
|
TEE_FINAL_USER_NUDGE,
|
|
@@ -86,7 +86,7 @@ import {
|
|
|
86
86
|
} from "./chunk-XPBEJB27.js";
|
|
87
87
|
import {
|
|
88
88
|
ConfigManager
|
|
89
|
-
} from "./chunk-
|
|
89
|
+
} from "./chunk-TM4MS63K.js";
|
|
90
90
|
import {
|
|
91
91
|
AuthError,
|
|
92
92
|
ProviderError,
|
|
@@ -113,7 +113,7 @@ import {
|
|
|
113
113
|
SKILLS_DIR_NAME,
|
|
114
114
|
VERSION,
|
|
115
115
|
buildUserIdentityPrompt
|
|
116
|
-
} from "./chunk-
|
|
116
|
+
} from "./chunk-KV4GYNEA.js";
|
|
117
117
|
import {
|
|
118
118
|
formatGitContextForPrompt,
|
|
119
119
|
getGitContext,
|
|
@@ -1832,7 +1832,7 @@ No tools match "${filter}".
|
|
|
1832
1832
|
const { join: join6 } = await import("path");
|
|
1833
1833
|
const { existsSync: existsSync6 } = await import("fs");
|
|
1834
1834
|
const { getGitRoot: getGitRoot2 } = await import("./git-context-EXOEHQSF.js");
|
|
1835
|
-
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-
|
|
1835
|
+
const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-SZTQNN7K.js");
|
|
1836
1836
|
const { approveProject, hashMcpFile } = await import("./project-trust-NKYHL3VZ.js");
|
|
1837
1837
|
const cwd = process.cwd();
|
|
1838
1838
|
const projectRoot = getGitRoot2(cwd) ?? cwd;
|
|
@@ -2893,7 +2893,7 @@ ${hint}` : "")
|
|
|
2893
2893
|
usage: "/test [command|filter]",
|
|
2894
2894
|
async execute(args, ctx) {
|
|
2895
2895
|
try {
|
|
2896
|
-
const { executeTests } = await import("./run-tests-
|
|
2896
|
+
const { executeTests } = await import("./run-tests-D5SNUBYX.js");
|
|
2897
2897
|
const argStr = args.join(" ").trim();
|
|
2898
2898
|
let testArgs = {};
|
|
2899
2899
|
if (argStr) {
|
|
@@ -7271,7 +7271,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
7271
7271
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
7272
7272
|
process.exit(1);
|
|
7273
7273
|
}
|
|
7274
|
-
const { startWebServer } = await import("./server-
|
|
7274
|
+
const { startWebServer } = await import("./server-4EFTRFTN.js");
|
|
7275
7275
|
await startWebServer({ port, host: options.host });
|
|
7276
7276
|
});
|
|
7277
7277
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -7438,16 +7438,16 @@ program.command("sessions").description("List recent conversation sessions").opt
|
|
|
7438
7438
|
console.log(footer + "\n");
|
|
7439
7439
|
});
|
|
7440
7440
|
program.command("usage").description("Show token + cost usage grouped by provider/model (cross-session)").option("--days <n>", "Only the last N days (inclusive of today)").option("--month <ym>", "Only a specific month, format YYYY-MM (e.g. 2026-06)").option("--json", "Output as JSON (for scripting)").action(async (options) => {
|
|
7441
|
-
const { runUsageCli } = await import("./usage-
|
|
7441
|
+
const { runUsageCli } = await import("./usage-Q24E5636.js");
|
|
7442
7442
|
await runUsageCli(options);
|
|
7443
7443
|
});
|
|
7444
7444
|
program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
|
|
7445
|
-
const { runDoctorCli } = await import("./doctor-cli-
|
|
7445
|
+
const { runDoctorCli } = await import("./doctor-cli-I2Y2YR4V.js");
|
|
7446
7446
|
await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
|
|
7447
7447
|
});
|
|
7448
7448
|
program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
|
|
7449
7449
|
try {
|
|
7450
|
-
const batch = await import("./batch-
|
|
7450
|
+
const batch = await import("./batch-3T44NFLG.js");
|
|
7451
7451
|
switch (action) {
|
|
7452
7452
|
case "submit":
|
|
7453
7453
|
if (!arg) {
|
|
@@ -7490,7 +7490,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
|
|
|
7490
7490
|
}
|
|
7491
7491
|
});
|
|
7492
7492
|
program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
|
|
7493
|
-
const { startMcpServer } = await import("./server-
|
|
7493
|
+
const { startMcpServer } = await import("./server-PVKLPRUP.js");
|
|
7494
7494
|
await startMcpServer({
|
|
7495
7495
|
allowDestructive: !!options.allowDestructive,
|
|
7496
7496
|
allowOutsideCwd: !!options.allowOutsideCwd,
|
|
@@ -7499,7 +7499,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
|
|
|
7499
7499
|
});
|
|
7500
7500
|
});
|
|
7501
7501
|
program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
|
|
7502
|
-
const { runCi } = await import("./ci-
|
|
7502
|
+
const { runCi } = await import("./ci-6QWBVRJX.js");
|
|
7503
7503
|
const result = await runCi({
|
|
7504
7504
|
pr: options.pr,
|
|
7505
7505
|
base: options.base,
|
|
@@ -7645,7 +7645,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
7645
7645
|
}),
|
|
7646
7646
|
config.get("customProviders")
|
|
7647
7647
|
);
|
|
7648
|
-
const { startHub } = await import("./hub-
|
|
7648
|
+
const { startHub } = await import("./hub-XVPFBODB.js");
|
|
7649
7649
|
await startHub(
|
|
7650
7650
|
{
|
|
7651
7651
|
topic: topic ?? "",
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
loadDevState,
|
|
20
20
|
persistToolRound,
|
|
21
21
|
setupProxy
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-TD7JEHCR.js";
|
|
23
23
|
import {
|
|
24
24
|
ToolExecutor,
|
|
25
25
|
ToolRegistry,
|
|
@@ -38,10 +38,10 @@ import {
|
|
|
38
38
|
spawnAgentContext,
|
|
39
39
|
truncateOutput,
|
|
40
40
|
undoStack
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-2OVMMSGQ.js";
|
|
42
42
|
import "./chunk-T2NL5ZIA.js";
|
|
43
43
|
import "./chunk-BXP6YZ2P.js";
|
|
44
|
-
import "./chunk-
|
|
44
|
+
import "./chunk-VWYUB22Y.js";
|
|
45
45
|
import {
|
|
46
46
|
SessionManager,
|
|
47
47
|
getContentText
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
} from "./chunk-QMXC327F.js";
|
|
56
56
|
import {
|
|
57
57
|
runTool
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-J22B3OSQ.js";
|
|
59
59
|
import {
|
|
60
60
|
CONTENT_ONLY_STREAM_REMINDER,
|
|
61
61
|
TEE_FINAL_USER_NUDGE,
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
} from "./chunk-XPBEJB27.js";
|
|
75
75
|
import {
|
|
76
76
|
ConfigManager
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-TM4MS63K.js";
|
|
78
78
|
import "./chunk-TZQHYZKT.js";
|
|
79
79
|
import {
|
|
80
80
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -94,7 +94,7 @@ import {
|
|
|
94
94
|
SKILLS_DIR_NAME,
|
|
95
95
|
VERSION,
|
|
96
96
|
buildUserIdentityPrompt
|
|
97
|
-
} from "./chunk-
|
|
97
|
+
} from "./chunk-KV4GYNEA.js";
|
|
98
98
|
import {
|
|
99
99
|
formatGitContextForPrompt,
|
|
100
100
|
getGitContext,
|
|
@@ -2458,7 +2458,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2458
2458
|
case "test": {
|
|
2459
2459
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2460
2460
|
try {
|
|
2461
|
-
const { executeTests } = await import("./run-tests-
|
|
2461
|
+
const { executeTests } = await import("./run-tests-D5SNUBYX.js");
|
|
2462
2462
|
const argStr = args.join(" ").trim();
|
|
2463
2463
|
let testArgs = {};
|
|
2464
2464
|
if (argStr) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ToolRegistry
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-2OVMMSGQ.js";
|
|
5
5
|
import "./chunk-T2NL5ZIA.js";
|
|
6
6
|
import "./chunk-BXP6YZ2P.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-VWYUB22Y.js";
|
|
8
8
|
import {
|
|
9
9
|
runTool
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-J22B3OSQ.js";
|
|
11
11
|
import {
|
|
12
12
|
getDangerLevel,
|
|
13
13
|
schemaToJsonSchema
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import "./chunk-TZQHYZKT.js";
|
|
16
16
|
import {
|
|
17
17
|
VERSION
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-KV4GYNEA.js";
|
|
19
19
|
import "./chunk-4BKXL7SM.js";
|
|
20
20
|
import "./chunk-TB4W4Y4T.js";
|
|
21
21
|
import "./chunk-KHYD3WXE.js";
|
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
ToolRegistry,
|
|
4
4
|
googleSearchContext,
|
|
5
5
|
truncateOutput
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2OVMMSGQ.js";
|
|
7
7
|
import "./chunk-T2NL5ZIA.js";
|
|
8
8
|
import "./chunk-BXP6YZ2P.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-VWYUB22Y.js";
|
|
10
10
|
import {
|
|
11
11
|
runTool
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-J22B3OSQ.js";
|
|
13
13
|
import {
|
|
14
14
|
getDangerLevel,
|
|
15
15
|
runLeanAgentLoop
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import "./chunk-TZQHYZKT.js";
|
|
18
18
|
import {
|
|
19
19
|
SUBAGENT_ALLOWED_TOOLS
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-KV4GYNEA.js";
|
|
21
21
|
import "./chunk-4BKXL7SM.js";
|
|
22
22
|
import "./chunk-TB4W4Y4T.js";
|
|
23
23
|
import "./chunk-KHYD3WXE.js";
|
|
@@ -8,9 +8,9 @@ import {
|
|
|
8
8
|
} from "./chunk-E44DTERW.js";
|
|
9
9
|
import {
|
|
10
10
|
ConfigManager
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-TM4MS63K.js";
|
|
12
12
|
import "./chunk-TZQHYZKT.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-KV4GYNEA.js";
|
|
14
14
|
import "./chunk-IW3Q7AE5.js";
|
|
15
15
|
|
|
16
16
|
// src/cli/usage.ts
|
|
@@ -3,82 +3,82 @@
|
|
|
3
3
|
* Kept separate from app.js so static and dynamic UI markup can avoid inline handlers.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
function handleDeclarativeAction(action, el) {
|
|
7
|
-
switch (action) {
|
|
8
|
-
case 'toggle-auth-mode': toggleAuthMode(); break;
|
|
9
|
-
case 'show-enable-auth': showEnableAuthDialog(); break;
|
|
10
|
-
case 'logout': handleLogout(); break;
|
|
11
|
-
case 'toggle-batch-select': toggleBatchSelect(); break;
|
|
12
|
-
case 'select-all-sessions': selectAllSessions(); break;
|
|
13
|
-
case 'batch-delete-selected': batchDeleteSelected(); break;
|
|
14
|
-
case 'start-hub': window.startHub(); break;
|
|
15
|
-
case 'abort-hub': window.abortHub(); break;
|
|
16
|
-
case 'close-sidebar': closeSidebar(); break;
|
|
17
|
-
case 'import-templates': importTemplates(); break;
|
|
18
|
-
case 'export-templates': exportTemplates(); break;
|
|
19
|
-
case 'show-add-template': showAddTemplate(); break;
|
|
20
|
-
case 'cancel-template-form': cancelTemplateForm(); break;
|
|
21
|
-
case 'save-template-form': saveTemplateForm(); break;
|
|
22
|
-
case 'confirm-response': window.respondConfirm(el.dataset.requestId, el.dataset.approved === 'true'); break;
|
|
23
|
-
case 'batch-confirm-response': window.respondBatchConfirm(el.dataset.requestId, el); break;
|
|
24
|
-
case 'batch-deny-response': window.respondBatchDeny(el.dataset.requestId, el); break;
|
|
25
|
-
case 'ask-user-response': window.respondAskUser(el.dataset.requestId); break;
|
|
26
|
-
case 'auto-pause-response': window.respondAutoPause(el.dataset.requestId, el.dataset.pauseAction); break;
|
|
27
|
-
case 'use-template': useTemplate(el.closest('[data-tpl-id]')?.dataset.tplId); break;
|
|
28
|
-
case 'edit-template': editTemplate(el.closest('[data-tpl-id]')?.dataset.tplId); break;
|
|
29
|
-
case 'delete-template': deleteTemplate(el.closest('[data-tpl-id]')?.dataset.tplId); break;
|
|
30
|
-
case 'insert-file-ref': insertFileRef(el.dataset.filePath || ''); break;
|
|
31
|
-
case 'show-full-preview':
|
|
32
|
-
if (el.previousElementSibling) el.previousElementSibling.style.maxHeight = 'none';
|
|
33
|
-
el.remove();
|
|
34
|
-
break;
|
|
35
|
-
case 'load-session': send({ type: 'command', name: 'session', args: ['load', el.dataset.sessionId] }); break;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
document.addEventListener('click', (event) => {
|
|
40
|
-
const themeEl = event.target.closest('[data-theme-choice]');
|
|
41
|
-
if (themeEl) {
|
|
42
|
-
event.preventDefault();
|
|
43
|
-
window.setTheme(themeEl.dataset.themeChoice);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const actionEl = event.target.closest('[data-action]');
|
|
48
|
-
if (!actionEl) return;
|
|
49
|
-
event.preventDefault();
|
|
50
|
-
handleDeclarativeAction(actionEl.dataset.action, actionEl);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
document.addEventListener('keydown', (event) => {
|
|
54
|
-
if (event.key !== 'Enter') return;
|
|
55
|
-
const actionEl = event.target.closest('[data-enter-action]');
|
|
56
|
-
if (!actionEl) return;
|
|
57
|
-
event.preventDefault();
|
|
58
|
-
handleDeclarativeAction(actionEl.dataset.enterAction, actionEl);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
document.getElementById('auth-form')?.addEventListener('submit', handleAuth);
|
|
62
|
-
|
|
63
|
-
function registerServiceWorker() {
|
|
64
|
-
// Self-updating service worker (v0.4.159). A new SW postMessages
|
|
65
|
-
// 'sw-updated' (and triggers controllerchange) when it takes over; reload
|
|
66
|
-
// once so users do not stay on stale index.html/app.js after an upgrade.
|
|
67
|
-
if (!('serviceWorker' in navigator)) return;
|
|
68
|
-
let swReloaded = false;
|
|
69
|
-
const reloadOnce = () => {
|
|
70
|
-
if (!swReloaded) {
|
|
71
|
-
swReloaded = true;
|
|
72
|
-
location.reload();
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
navigator.serviceWorker.addEventListener('message', (e) => {
|
|
76
|
-
if (e.data && e.data.type === 'sw-updated') reloadOnce();
|
|
77
|
-
});
|
|
78
|
-
navigator.serviceWorker.addEventListener('controllerchange', reloadOnce);
|
|
79
|
-
navigator.serviceWorker.register('sw.js').then((reg) => {
|
|
80
|
-
reg.update();
|
|
81
|
-
}).catch(() => {});
|
|
82
|
-
}
|
|
83
|
-
|
|
6
|
+
function handleDeclarativeAction(action, el) {
|
|
7
|
+
switch (action) {
|
|
8
|
+
case 'toggle-auth-mode': toggleAuthMode(); break;
|
|
9
|
+
case 'show-enable-auth': showEnableAuthDialog(); break;
|
|
10
|
+
case 'logout': handleLogout(); break;
|
|
11
|
+
case 'toggle-batch-select': toggleBatchSelect(); break;
|
|
12
|
+
case 'select-all-sessions': selectAllSessions(); break;
|
|
13
|
+
case 'batch-delete-selected': batchDeleteSelected(); break;
|
|
14
|
+
case 'start-hub': window.startHub(); break;
|
|
15
|
+
case 'abort-hub': window.abortHub(); break;
|
|
16
|
+
case 'close-sidebar': closeSidebar(); break;
|
|
17
|
+
case 'import-templates': importTemplates(); break;
|
|
18
|
+
case 'export-templates': exportTemplates(); break;
|
|
19
|
+
case 'show-add-template': showAddTemplate(); break;
|
|
20
|
+
case 'cancel-template-form': cancelTemplateForm(); break;
|
|
21
|
+
case 'save-template-form': saveTemplateForm(); break;
|
|
22
|
+
case 'confirm-response': window.respondConfirm(el.dataset.requestId, el.dataset.approved === 'true'); break;
|
|
23
|
+
case 'batch-confirm-response': window.respondBatchConfirm(el.dataset.requestId, el); break;
|
|
24
|
+
case 'batch-deny-response': window.respondBatchDeny(el.dataset.requestId, el); break;
|
|
25
|
+
case 'ask-user-response': window.respondAskUser(el.dataset.requestId); break;
|
|
26
|
+
case 'auto-pause-response': window.respondAutoPause(el.dataset.requestId, el.dataset.pauseAction); break;
|
|
27
|
+
case 'use-template': useTemplate(el.closest('[data-tpl-id]')?.dataset.tplId); break;
|
|
28
|
+
case 'edit-template': editTemplate(el.closest('[data-tpl-id]')?.dataset.tplId); break;
|
|
29
|
+
case 'delete-template': deleteTemplate(el.closest('[data-tpl-id]')?.dataset.tplId); break;
|
|
30
|
+
case 'insert-file-ref': insertFileRef(el.dataset.filePath || ''); break;
|
|
31
|
+
case 'show-full-preview':
|
|
32
|
+
if (el.previousElementSibling) el.previousElementSibling.style.maxHeight = 'none';
|
|
33
|
+
el.remove();
|
|
34
|
+
break;
|
|
35
|
+
case 'load-session': send({ type: 'command', name: 'session', args: ['load', el.dataset.sessionId] }); break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
document.addEventListener('click', (event) => {
|
|
40
|
+
const themeEl = event.target.closest('[data-theme-choice]');
|
|
41
|
+
if (themeEl) {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
window.setTheme(themeEl.dataset.themeChoice);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const actionEl = event.target.closest('[data-action]');
|
|
48
|
+
if (!actionEl) return;
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
handleDeclarativeAction(actionEl.dataset.action, actionEl);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
document.addEventListener('keydown', (event) => {
|
|
54
|
+
if (event.key !== 'Enter') return;
|
|
55
|
+
const actionEl = event.target.closest('[data-enter-action]');
|
|
56
|
+
if (!actionEl) return;
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
handleDeclarativeAction(actionEl.dataset.enterAction, actionEl);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
document.getElementById('auth-form')?.addEventListener('submit', handleAuth);
|
|
62
|
+
|
|
63
|
+
function registerServiceWorker() {
|
|
64
|
+
// Self-updating service worker (v0.4.159). A new SW postMessages
|
|
65
|
+
// 'sw-updated' (and triggers controllerchange) when it takes over; reload
|
|
66
|
+
// once so users do not stay on stale index.html/app.js after an upgrade.
|
|
67
|
+
if (!('serviceWorker' in navigator)) return;
|
|
68
|
+
let swReloaded = false;
|
|
69
|
+
const reloadOnce = () => {
|
|
70
|
+
if (!swReloaded) {
|
|
71
|
+
swReloaded = true;
|
|
72
|
+
location.reload();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
navigator.serviceWorker.addEventListener('message', (e) => {
|
|
76
|
+
if (e.data && e.data.type === 'sw-updated') reloadOnce();
|
|
77
|
+
});
|
|
78
|
+
navigator.serviceWorker.addEventListener('controllerchange', reloadOnce);
|
|
79
|
+
navigator.serviceWorker.register('sw.js').then((reg) => {
|
|
80
|
+
reg.update();
|
|
81
|
+
}).catch(() => {});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
84
|
registerServiceWorker();
|
package/dist/web/client/app.js
CHANGED
|
@@ -666,7 +666,7 @@ window.setTheme = function(theme) {
|
|
|
666
666
|
|
|
667
667
|
// ── UI helpers ─────────────────────────────────────────────────────
|
|
668
668
|
|
|
669
|
-
|
|
669
|
+
|
|
670
670
|
function createAssistantMessage() {
|
|
671
671
|
const el = document.createElement('div');
|
|
672
672
|
el.className = 'msg-assistant';
|
|
@@ -715,10 +715,10 @@ function renderMarkdown(el, text) {
|
|
|
715
715
|
const btn = document.createElement('button');
|
|
716
716
|
btn.className = 'copy-code-btn btn btn-ghost btn-xs';
|
|
717
717
|
btn.textContent = '📋 Copy';
|
|
718
|
-
btn.addEventListener('click', () => {
|
|
719
|
-
navigator.clipboard.writeText(block.textContent || '');
|
|
720
|
-
btn.textContent = 'Copied';
|
|
721
|
-
setTimeout(() => btn.textContent = 'Copy', 1500);
|
|
718
|
+
btn.addEventListener('click', () => {
|
|
719
|
+
navigator.clipboard.writeText(block.textContent || '');
|
|
720
|
+
btn.textContent = 'Copied';
|
|
721
|
+
setTimeout(() => btn.textContent = 'Copy', 1500);
|
|
722
722
|
});
|
|
723
723
|
pre.style.position = 'relative';
|
|
724
724
|
pre.appendChild(btn);
|
|
@@ -338,7 +338,7 @@
|
|
|
338
338
|
<form method="dialog" class="modal-backdrop"><button>close</button></form>
|
|
339
339
|
</dialog>
|
|
340
340
|
|
|
341
|
-
<script src="app.js"></script>
|
|
341
|
+
<script src="app.js"></script>
|
|
342
342
|
<script src="templates.js"></script>
|
|
343
343
|
<script src="actions.js"></script>
|
|
344
344
|
</body>
|
|
@@ -1,188 +1,188 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Prompt template modal and import/export helpers.
|
|
3
|
-
* Loaded after app.js so it can use shared UI helpers.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const TEMPLATES_KEY = 'aicli-templates';
|
|
7
|
-
|
|
8
|
-
function loadTemplates() {
|
|
9
|
-
try {
|
|
10
|
-
return JSON.parse(localStorage.getItem(TEMPLATES_KEY) || '[]');
|
|
11
|
-
} catch {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function saveTemplatesToStorage(templates) {
|
|
17
|
-
localStorage.setItem(TEMPLATES_KEY, JSON.stringify(templates));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function openTemplatesModal() {
|
|
21
|
-
const modal = document.getElementById('templates-modal');
|
|
22
|
-
if (!modal) return;
|
|
23
|
-
cancelTemplateForm();
|
|
24
|
-
renderTemplateList();
|
|
25
|
-
document.getElementById('template-search').value = '';
|
|
26
|
-
modal.showModal();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function renderTemplateList(filter) {
|
|
30
|
-
const listEl = document.getElementById('template-list');
|
|
31
|
-
if (!listEl) return;
|
|
32
|
-
const templates = loadTemplates();
|
|
33
|
-
const q = (filter || '').toLowerCase();
|
|
34
|
-
const filtered = q
|
|
35
|
-
? templates.filter(t => t.name.toLowerCase().includes(q) || (t.tags || []).some(tag => tag.toLowerCase().includes(q)) || t.content.toLowerCase().includes(q))
|
|
36
|
-
: templates;
|
|
37
|
-
|
|
38
|
-
if (filtered.length === 0) {
|
|
39
|
-
listEl.innerHTML = `<div class="template-empty">${q ? 'No matching templates' : 'No templates yet. Click + New to create one.'}</div>`;
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
listEl.innerHTML = filtered.map(t => {
|
|
44
|
-
const preview = t.content.length > 80 ? t.content.slice(0, 80) + '...' : t.content;
|
|
45
|
-
const tagsHtml = (t.tags || []).map(tag => `<span class="template-item-tag">${escapeHtml(tag)}</span>`).join('');
|
|
46
|
-
return `
|
|
47
|
-
<div class="template-item" data-tpl-id="${t.id}" data-action="use-template">
|
|
48
|
-
<div class="template-item-body">
|
|
49
|
-
<div class="template-item-name">${escapeHtml(t.name)}</div>
|
|
50
|
-
<div class="template-item-preview">${escapeHtml(preview)}</div>
|
|
51
|
-
${tagsHtml ? `<div class="template-item-tags">${tagsHtml}</div>` : ''}
|
|
52
|
-
</div>
|
|
53
|
-
<div class="template-item-actions">
|
|
54
|
-
<button class="btn btn-xs btn-ghost" data-action="edit-template" title="Edit">Edit</button>
|
|
55
|
-
<button class="btn btn-xs btn-ghost" data-action="delete-template" title="Delete">Delete</button>
|
|
56
|
-
</div>
|
|
57
|
-
</div>`;
|
|
58
|
-
}).join('');
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function useTemplate(id) {
|
|
62
|
-
const templates = loadTemplates();
|
|
63
|
-
const tpl = templates.find(t => t.id === id);
|
|
64
|
-
if (!tpl) return;
|
|
65
|
-
const input = document.getElementById('user-input');
|
|
66
|
-
if (!input) return;
|
|
67
|
-
input.value = tpl.content;
|
|
68
|
-
input.focus();
|
|
69
|
-
input.style.height = 'auto';
|
|
70
|
-
input.style.height = Math.min(input.scrollHeight, 200) + 'px';
|
|
71
|
-
document.getElementById('templates-modal')?.close();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function showAddTemplate() {
|
|
75
|
-
const form = document.getElementById('template-form');
|
|
76
|
-
form.classList.remove('hidden');
|
|
77
|
-
document.getElementById('tpl-edit-id').value = '';
|
|
78
|
-
document.getElementById('tpl-name').value = '';
|
|
79
|
-
document.getElementById('tpl-content').value = '';
|
|
80
|
-
document.getElementById('tpl-tags').value = '';
|
|
81
|
-
document.getElementById('tpl-name').focus();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function editTemplate(id) {
|
|
85
|
-
const templates = loadTemplates();
|
|
86
|
-
const tpl = templates.find(t => t.id === id);
|
|
87
|
-
if (!tpl) return;
|
|
88
|
-
const form = document.getElementById('template-form');
|
|
89
|
-
form.classList.remove('hidden');
|
|
90
|
-
document.getElementById('tpl-edit-id').value = tpl.id;
|
|
91
|
-
document.getElementById('tpl-name').value = tpl.name;
|
|
92
|
-
document.getElementById('tpl-content').value = tpl.content;
|
|
93
|
-
document.getElementById('tpl-tags').value = (tpl.tags || []).join(', ');
|
|
94
|
-
document.getElementById('tpl-name').focus();
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function cancelTemplateForm() {
|
|
98
|
-
document.getElementById('template-form')?.classList.add('hidden');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function saveTemplateForm() {
|
|
102
|
-
const name = document.getElementById('tpl-name').value.trim();
|
|
103
|
-
const content = document.getElementById('tpl-content').value.trim();
|
|
104
|
-
const tagsRaw = document.getElementById('tpl-tags').value.trim();
|
|
105
|
-
const editId = document.getElementById('tpl-edit-id').value;
|
|
106
|
-
|
|
107
|
-
if (!name || !content) return;
|
|
108
|
-
|
|
109
|
-
const tags = tagsRaw ? tagsRaw.split(',').map(s => s.trim()).filter(Boolean) : [];
|
|
110
|
-
const templates = loadTemplates();
|
|
111
|
-
|
|
112
|
-
if (editId) {
|
|
113
|
-
const idx = templates.findIndex(t => t.id === editId);
|
|
114
|
-
if (idx >= 0) {
|
|
115
|
-
templates[idx].name = name;
|
|
116
|
-
templates[idx].content = content;
|
|
117
|
-
templates[idx].tags = tags;
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
templates.unshift({
|
|
121
|
-
id: 'tpl-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
122
|
-
name,
|
|
123
|
-
content,
|
|
124
|
-
tags,
|
|
125
|
-
createdAt: new Date().toISOString(),
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
saveTemplatesToStorage(templates);
|
|
130
|
-
cancelTemplateForm();
|
|
131
|
-
renderTemplateList();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function deleteTemplate(id) {
|
|
135
|
-
const templates = loadTemplates().filter(t => t.id !== id);
|
|
136
|
-
saveTemplatesToStorage(templates);
|
|
137
|
-
renderTemplateList(document.getElementById('template-search')?.value);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function exportTemplates() {
|
|
141
|
-
const templates = loadTemplates();
|
|
142
|
-
if (templates.length === 0) return;
|
|
143
|
-
const blob = new Blob([JSON.stringify(templates, null, 2)], { type: 'application/json' });
|
|
144
|
-
const url = URL.createObjectURL(blob);
|
|
145
|
-
const a = document.createElement('a');
|
|
146
|
-
a.href = url;
|
|
147
|
-
a.download = `aicli-templates-${new Date().toISOString().slice(0, 10)}.json`;
|
|
148
|
-
a.click();
|
|
149
|
-
URL.revokeObjectURL(url);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function importTemplates() {
|
|
153
|
-
const input = document.createElement('input');
|
|
154
|
-
input.type = 'file';
|
|
155
|
-
input.accept = '.json';
|
|
156
|
-
input.addEventListener('change', () => {
|
|
157
|
-
const file = input.files[0];
|
|
158
|
-
if (!file) return;
|
|
159
|
-
const reader = new FileReader();
|
|
160
|
-
reader.addEventListener('load', () => {
|
|
161
|
-
try {
|
|
162
|
-
const imported = JSON.parse(reader.result);
|
|
163
|
-
if (!Array.isArray(imported)) throw new Error('Not an array');
|
|
164
|
-
const existing = loadTemplates();
|
|
165
|
-
const existingIds = new Set(existing.map(t => t.id));
|
|
166
|
-
let added = 0;
|
|
167
|
-
for (const t of imported) {
|
|
168
|
-
if (t.id && t.name && t.content && !existingIds.has(t.id)) {
|
|
169
|
-
existing.push(t);
|
|
170
|
-
added++;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
saveTemplatesToStorage(existing);
|
|
174
|
-
renderTemplateList();
|
|
175
|
-
addInfoMessage(`Imported ${added} template(s).`);
|
|
176
|
-
} catch {
|
|
177
|
-
addErrorMessage('Failed to import templates: invalid JSON format.');
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
reader.readAsText(file);
|
|
181
|
-
});
|
|
182
|
-
input.click();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
document.getElementById('btn-templates')?.addEventListener('click', openTemplatesModal);
|
|
186
|
-
document.getElementById('template-search')?.addEventListener('input', (e) => {
|
|
187
|
-
renderTemplateList(e.target.value);
|
|
188
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Prompt template modal and import/export helpers.
|
|
3
|
+
* Loaded after app.js so it can use shared UI helpers.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const TEMPLATES_KEY = 'aicli-templates';
|
|
7
|
+
|
|
8
|
+
function loadTemplates() {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(localStorage.getItem(TEMPLATES_KEY) || '[]');
|
|
11
|
+
} catch {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function saveTemplatesToStorage(templates) {
|
|
17
|
+
localStorage.setItem(TEMPLATES_KEY, JSON.stringify(templates));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function openTemplatesModal() {
|
|
21
|
+
const modal = document.getElementById('templates-modal');
|
|
22
|
+
if (!modal) return;
|
|
23
|
+
cancelTemplateForm();
|
|
24
|
+
renderTemplateList();
|
|
25
|
+
document.getElementById('template-search').value = '';
|
|
26
|
+
modal.showModal();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function renderTemplateList(filter) {
|
|
30
|
+
const listEl = document.getElementById('template-list');
|
|
31
|
+
if (!listEl) return;
|
|
32
|
+
const templates = loadTemplates();
|
|
33
|
+
const q = (filter || '').toLowerCase();
|
|
34
|
+
const filtered = q
|
|
35
|
+
? templates.filter(t => t.name.toLowerCase().includes(q) || (t.tags || []).some(tag => tag.toLowerCase().includes(q)) || t.content.toLowerCase().includes(q))
|
|
36
|
+
: templates;
|
|
37
|
+
|
|
38
|
+
if (filtered.length === 0) {
|
|
39
|
+
listEl.innerHTML = `<div class="template-empty">${q ? 'No matching templates' : 'No templates yet. Click + New to create one.'}</div>`;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
listEl.innerHTML = filtered.map(t => {
|
|
44
|
+
const preview = t.content.length > 80 ? t.content.slice(0, 80) + '...' : t.content;
|
|
45
|
+
const tagsHtml = (t.tags || []).map(tag => `<span class="template-item-tag">${escapeHtml(tag)}</span>`).join('');
|
|
46
|
+
return `
|
|
47
|
+
<div class="template-item" data-tpl-id="${t.id}" data-action="use-template">
|
|
48
|
+
<div class="template-item-body">
|
|
49
|
+
<div class="template-item-name">${escapeHtml(t.name)}</div>
|
|
50
|
+
<div class="template-item-preview">${escapeHtml(preview)}</div>
|
|
51
|
+
${tagsHtml ? `<div class="template-item-tags">${tagsHtml}</div>` : ''}
|
|
52
|
+
</div>
|
|
53
|
+
<div class="template-item-actions">
|
|
54
|
+
<button class="btn btn-xs btn-ghost" data-action="edit-template" title="Edit">Edit</button>
|
|
55
|
+
<button class="btn btn-xs btn-ghost" data-action="delete-template" title="Delete">Delete</button>
|
|
56
|
+
</div>
|
|
57
|
+
</div>`;
|
|
58
|
+
}).join('');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function useTemplate(id) {
|
|
62
|
+
const templates = loadTemplates();
|
|
63
|
+
const tpl = templates.find(t => t.id === id);
|
|
64
|
+
if (!tpl) return;
|
|
65
|
+
const input = document.getElementById('user-input');
|
|
66
|
+
if (!input) return;
|
|
67
|
+
input.value = tpl.content;
|
|
68
|
+
input.focus();
|
|
69
|
+
input.style.height = 'auto';
|
|
70
|
+
input.style.height = Math.min(input.scrollHeight, 200) + 'px';
|
|
71
|
+
document.getElementById('templates-modal')?.close();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function showAddTemplate() {
|
|
75
|
+
const form = document.getElementById('template-form');
|
|
76
|
+
form.classList.remove('hidden');
|
|
77
|
+
document.getElementById('tpl-edit-id').value = '';
|
|
78
|
+
document.getElementById('tpl-name').value = '';
|
|
79
|
+
document.getElementById('tpl-content').value = '';
|
|
80
|
+
document.getElementById('tpl-tags').value = '';
|
|
81
|
+
document.getElementById('tpl-name').focus();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function editTemplate(id) {
|
|
85
|
+
const templates = loadTemplates();
|
|
86
|
+
const tpl = templates.find(t => t.id === id);
|
|
87
|
+
if (!tpl) return;
|
|
88
|
+
const form = document.getElementById('template-form');
|
|
89
|
+
form.classList.remove('hidden');
|
|
90
|
+
document.getElementById('tpl-edit-id').value = tpl.id;
|
|
91
|
+
document.getElementById('tpl-name').value = tpl.name;
|
|
92
|
+
document.getElementById('tpl-content').value = tpl.content;
|
|
93
|
+
document.getElementById('tpl-tags').value = (tpl.tags || []).join(', ');
|
|
94
|
+
document.getElementById('tpl-name').focus();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function cancelTemplateForm() {
|
|
98
|
+
document.getElementById('template-form')?.classList.add('hidden');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function saveTemplateForm() {
|
|
102
|
+
const name = document.getElementById('tpl-name').value.trim();
|
|
103
|
+
const content = document.getElementById('tpl-content').value.trim();
|
|
104
|
+
const tagsRaw = document.getElementById('tpl-tags').value.trim();
|
|
105
|
+
const editId = document.getElementById('tpl-edit-id').value;
|
|
106
|
+
|
|
107
|
+
if (!name || !content) return;
|
|
108
|
+
|
|
109
|
+
const tags = tagsRaw ? tagsRaw.split(',').map(s => s.trim()).filter(Boolean) : [];
|
|
110
|
+
const templates = loadTemplates();
|
|
111
|
+
|
|
112
|
+
if (editId) {
|
|
113
|
+
const idx = templates.findIndex(t => t.id === editId);
|
|
114
|
+
if (idx >= 0) {
|
|
115
|
+
templates[idx].name = name;
|
|
116
|
+
templates[idx].content = content;
|
|
117
|
+
templates[idx].tags = tags;
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
templates.unshift({
|
|
121
|
+
id: 'tpl-' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6),
|
|
122
|
+
name,
|
|
123
|
+
content,
|
|
124
|
+
tags,
|
|
125
|
+
createdAt: new Date().toISOString(),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
saveTemplatesToStorage(templates);
|
|
130
|
+
cancelTemplateForm();
|
|
131
|
+
renderTemplateList();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function deleteTemplate(id) {
|
|
135
|
+
const templates = loadTemplates().filter(t => t.id !== id);
|
|
136
|
+
saveTemplatesToStorage(templates);
|
|
137
|
+
renderTemplateList(document.getElementById('template-search')?.value);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function exportTemplates() {
|
|
141
|
+
const templates = loadTemplates();
|
|
142
|
+
if (templates.length === 0) return;
|
|
143
|
+
const blob = new Blob([JSON.stringify(templates, null, 2)], { type: 'application/json' });
|
|
144
|
+
const url = URL.createObjectURL(blob);
|
|
145
|
+
const a = document.createElement('a');
|
|
146
|
+
a.href = url;
|
|
147
|
+
a.download = `aicli-templates-${new Date().toISOString().slice(0, 10)}.json`;
|
|
148
|
+
a.click();
|
|
149
|
+
URL.revokeObjectURL(url);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function importTemplates() {
|
|
153
|
+
const input = document.createElement('input');
|
|
154
|
+
input.type = 'file';
|
|
155
|
+
input.accept = '.json';
|
|
156
|
+
input.addEventListener('change', () => {
|
|
157
|
+
const file = input.files[0];
|
|
158
|
+
if (!file) return;
|
|
159
|
+
const reader = new FileReader();
|
|
160
|
+
reader.addEventListener('load', () => {
|
|
161
|
+
try {
|
|
162
|
+
const imported = JSON.parse(reader.result);
|
|
163
|
+
if (!Array.isArray(imported)) throw new Error('Not an array');
|
|
164
|
+
const existing = loadTemplates();
|
|
165
|
+
const existingIds = new Set(existing.map(t => t.id));
|
|
166
|
+
let added = 0;
|
|
167
|
+
for (const t of imported) {
|
|
168
|
+
if (t.id && t.name && t.content && !existingIds.has(t.id)) {
|
|
169
|
+
existing.push(t);
|
|
170
|
+
added++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
saveTemplatesToStorage(existing);
|
|
174
|
+
renderTemplateList();
|
|
175
|
+
addInfoMessage(`Imported ${added} template(s).`);
|
|
176
|
+
} catch {
|
|
177
|
+
addErrorMessage('Failed to import templates: invalid JSON format.');
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
reader.readAsText(file);
|
|
181
|
+
});
|
|
182
|
+
input.click();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
document.getElementById('btn-templates')?.addEventListener('click', openTemplatesModal);
|
|
186
|
+
document.getElementById('template-search')?.addEventListener('input', (e) => {
|
|
187
|
+
renderTemplateList(e.target.value);
|
|
188
|
+
});
|