@pencil-agent/nano-pencil 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +349 -53
- package/core/slash-commands.ts +5 -0
- package/modes/interactive/components/index.ts +1 -0
- package/modes/interactive/components/memory-stats.ts +139 -0
- package/modes/interactive/components/pencil-loader.ts +85 -0
- package/modes/interactive/components/soul-stats.ts +193 -0
- package/modes/interactive/interactive-mode.ts +94 -38
- package/modes/interactive/theme/warm.json +81 -81
- package/package.json +1 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [INPUT]: Soul profile and memory manager
|
|
3
|
+
* [OUTPUT]: Formatted display of Soul stats and personality
|
|
4
|
+
* [POS]: Interactive mode component for /soul command
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { SoulManager } from "nanosoul";
|
|
8
|
+
|
|
9
|
+
interface DisplayOptions {
|
|
10
|
+
compact?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format Soul stats for display
|
|
15
|
+
*/
|
|
16
|
+
export function formatSoulStats(
|
|
17
|
+
soul: SoulManager,
|
|
18
|
+
options: DisplayOptions = {},
|
|
19
|
+
): string {
|
|
20
|
+
const profile = soul.getProfile();
|
|
21
|
+
const stats = soul.getStats();
|
|
22
|
+
|
|
23
|
+
if (options.compact) {
|
|
24
|
+
return formatCompactSoul(profile, stats);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return formatFullSoul(profile, stats);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Format compact Soul stats (single line)
|
|
32
|
+
*/
|
|
33
|
+
function formatCompactSoul(profile: any, stats: any): string {
|
|
34
|
+
const personality = profile.personality;
|
|
35
|
+
const expertise = stats.expertise.slice(0, 3);
|
|
36
|
+
|
|
37
|
+
const topTraits = Object.entries(personality)
|
|
38
|
+
.filter(
|
|
39
|
+
([_, value]) => typeof value === "number" && (value > 0.6 || value < 0.4),
|
|
40
|
+
)
|
|
41
|
+
.map(([key, value]) => {
|
|
42
|
+
const label = getTraitLabel(key);
|
|
43
|
+
const numValue = typeof value === "number" ? value : 0.5;
|
|
44
|
+
const status = numValue > 0.6 ? "↑" : numValue < 0.4 ? "↓" : "→";
|
|
45
|
+
return `${label}${status} ${(numValue * 100).toFixed(0)}%`;
|
|
46
|
+
})
|
|
47
|
+
.slice(0, 3)
|
|
48
|
+
.join(", ");
|
|
49
|
+
|
|
50
|
+
const topExpertise = expertise
|
|
51
|
+
.map((e: any) => `${e.domain}(${(e.confidence * 100).toFixed(0)}%)`)
|
|
52
|
+
.join(", ");
|
|
53
|
+
|
|
54
|
+
return `🧠 Soul: ${topTraits} | Expertise: ${topExpertise} | Interactions: ${stats.stats.totalInteractions}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Format full Soul stats (detailed view)
|
|
59
|
+
*/
|
|
60
|
+
function formatFullSoul(profile: any, stats: any): string {
|
|
61
|
+
const lines: string[] = [];
|
|
62
|
+
|
|
63
|
+
lines.push("╔═════════════════════════════════════════════════╗");
|
|
64
|
+
lines.push("║ 🧠 AI Soul - Personality & Stats ║");
|
|
65
|
+
lines.push("╠═════════════════════════════════════════════════╣");
|
|
66
|
+
lines.push("║");
|
|
67
|
+
|
|
68
|
+
// Personality Section
|
|
69
|
+
lines.push("║ 📊 Personality Traits");
|
|
70
|
+
lines.push("║ ────────────────────────────────────────────────");
|
|
71
|
+
|
|
72
|
+
const personality = profile.personality;
|
|
73
|
+
const traits = [
|
|
74
|
+
{ key: "openness", label: "开放性", emoji: "🎨" },
|
|
75
|
+
{ key: "conscientiousness", label: "尽责性", emoji: "📋" },
|
|
76
|
+
{ key: "codeVerbosity", label: "代码冗长", emoji: "📝" },
|
|
77
|
+
{ key: "abstractionLevel", label: "抽象层级", emoji: "🏗️" },
|
|
78
|
+
{ key: "safetyMargin", label: "安全边际", emoji: "🛡️" },
|
|
79
|
+
{ key: "explorationDrive", label: "探索欲望", emoji: "🔍" },
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
for (const trait of traits) {
|
|
83
|
+
const value = personality[trait.key];
|
|
84
|
+
const bar = createBar(value, 10);
|
|
85
|
+
const percent = (value * 100).toFixed(0).padStart(3);
|
|
86
|
+
lines.push(`║ ${trait.emoji} ${trait.label.padEnd(12)} ${bar} ${percent}%`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
lines.push("║");
|
|
90
|
+
lines.push("║ 🎯 Top Expertise Areas");
|
|
91
|
+
|
|
92
|
+
const expertise = stats.expertise.slice(0, 5);
|
|
93
|
+
if (expertise.length === 0) {
|
|
94
|
+
lines.push("║ (暂无专长数据,继续使用以积累)");
|
|
95
|
+
} else {
|
|
96
|
+
for (const exp of expertise) {
|
|
97
|
+
const confidence = (exp.confidence * 100).toFixed(0).padStart(3);
|
|
98
|
+
const examples = exp.examples.toString().padStart(3);
|
|
99
|
+
lines.push(
|
|
100
|
+
`║ • ${exp.domain.padEnd(20)} 信心: ${confidence}% 成功: ${examples} 次`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
lines.push("║");
|
|
106
|
+
lines.push("║ 💭 Current Mood");
|
|
107
|
+
|
|
108
|
+
const emotional = profile.emotionalState;
|
|
109
|
+
const mood = [
|
|
110
|
+
{ label: "信心", value: emotional.confidence, emoji: "😊" },
|
|
111
|
+
{ label: "好奇心", value: emotional.curiosity, emoji: "🤔" },
|
|
112
|
+
{ label: "挫败感", value: emotional.frustration, emoji: "😤" },
|
|
113
|
+
{ label: "心流", value: emotional.flow, emoji: "✨" },
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
for (const m of mood) {
|
|
117
|
+
const bar = createBar(m.value, 8);
|
|
118
|
+
const percent = (m.value * 100).toFixed(0).padStart(3);
|
|
119
|
+
lines.push(`║ ${m.emoji} ${m.label.padEnd(8)} ${bar} ${percent}%`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
lines.push("║");
|
|
123
|
+
lines.push("║ 📈 Development Stats");
|
|
124
|
+
lines.push("║ ────────────────────────────────────────────────");
|
|
125
|
+
lines.push(`║ 总交互次数: ${stats.stats.totalInteractions}`);
|
|
126
|
+
lines.push(`║ 成功率: ${(stats.stats.successRate * 100).toFixed(1)}%`);
|
|
127
|
+
lines.push(`║ Soul 版本: ${profile.version}`);
|
|
128
|
+
lines.push(
|
|
129
|
+
`║ Soul 年龄: ${Math.floor((Date.now() - profile.createdAt.getTime()) / (1000 * 60 * 60 * 24))} 天`,
|
|
130
|
+
);
|
|
131
|
+
lines.push(`║ 最后进化: ${formatTimeAgo(profile.lastEvolved)}`);
|
|
132
|
+
|
|
133
|
+
lines.push("║");
|
|
134
|
+
lines.push("║ 🧘 User Relationship");
|
|
135
|
+
lines.push("║ ────────────────────────────────────────────────");
|
|
136
|
+
const rel = profile.userRelationship;
|
|
137
|
+
lines.push(`║ 交互次数: ${rel.interactionCount}`);
|
|
138
|
+
lines.push(`║ 满意度: ${(rel.satisfactionScore * 100).toFixed(0)}%`);
|
|
139
|
+
lines.push(` 沟通风格: ${rel.communicationStyle}`);
|
|
140
|
+
|
|
141
|
+
if (rel.knownPreferences.length > 0) {
|
|
142
|
+
lines.push(`║ 已知偏好: ${rel.knownPreferences.slice(0, 3).join(", ")}`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
lines.push("║");
|
|
146
|
+
lines.push("╚═════════════════════════════════════════════════╝");
|
|
147
|
+
|
|
148
|
+
return lines.join("\n");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Create a visual bar for values
|
|
153
|
+
*/
|
|
154
|
+
function createBar(value: number, width: number): string {
|
|
155
|
+
const filled = Math.round(value * width);
|
|
156
|
+
const empty = width - filled;
|
|
157
|
+
return "█".repeat(filled) + "░".repeat(empty);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get trait label in Chinese
|
|
162
|
+
*/
|
|
163
|
+
function getTraitLabel(key: string): string {
|
|
164
|
+
const labels: Record<string, string> = {
|
|
165
|
+
openness: "开放性",
|
|
166
|
+
conscientiousness: "尽责性",
|
|
167
|
+
extraversion: "外向性",
|
|
168
|
+
agreeableness: "宜人性",
|
|
169
|
+
neuroticism: "神经质",
|
|
170
|
+
codeVerbosity: "代码冗长",
|
|
171
|
+
abstractionLevel: "抽象层级",
|
|
172
|
+
safetyMargin: "安全边际",
|
|
173
|
+
explorationDrive: "探索",
|
|
174
|
+
};
|
|
175
|
+
return labels[key] || key;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Format time ago in Chinese
|
|
180
|
+
*/
|
|
181
|
+
function formatTimeAgo(date: Date): string {
|
|
182
|
+
const now = Date.now();
|
|
183
|
+
const diff = now - date.getTime();
|
|
184
|
+
|
|
185
|
+
const minutes = Math.floor(diff / (1000 * 60));
|
|
186
|
+
const hours = Math.floor(diff / (1000 * 60 * 60));
|
|
187
|
+
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
188
|
+
|
|
189
|
+
if (minutes < 1) return "刚刚";
|
|
190
|
+
if (minutes < 60) return `${minutes} 分钟前`;
|
|
191
|
+
if (hours < 24) return `${hours} 小时前`;
|
|
192
|
+
return `${days} 天前`;
|
|
193
|
+
}
|
|
@@ -95,6 +95,7 @@ import { promptForApiKey } from "./components/apikey-input.js";
|
|
|
95
95
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
96
96
|
import { BorderedLoader } from "./components/bordered-loader.js";
|
|
97
97
|
import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
|
|
98
|
+
import { PencilLoader } from "./components/pencil-loader.js";
|
|
98
99
|
import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
|
|
99
100
|
import { CustomEditor } from "./components/custom-editor.js";
|
|
100
101
|
import { CustomMessageComponent } from "./components/custom-message.js";
|
|
@@ -193,7 +194,7 @@ export class InteractiveMode {
|
|
|
193
194
|
private version: string;
|
|
194
195
|
private isInitialized = false;
|
|
195
196
|
private onInputCallback?: (text: string) => void;
|
|
196
|
-
private loadingAnimation:
|
|
197
|
+
private loadingAnimation: Component | undefined = undefined;
|
|
197
198
|
private pendingWorkingMessage: string | undefined = undefined;
|
|
198
199
|
private readonly defaultWorkingMessage = "Working...";
|
|
199
200
|
|
|
@@ -233,11 +234,11 @@ export class InteractiveMode {
|
|
|
233
234
|
private pendingBashComponents: BashExecutionComponent[] = [];
|
|
234
235
|
|
|
235
236
|
// Auto-compaction state
|
|
236
|
-
private autoCompactionLoader:
|
|
237
|
+
private autoCompactionLoader: Component | undefined = undefined;
|
|
237
238
|
private autoCompactionEscapeHandler?: () => void;
|
|
238
239
|
|
|
239
240
|
// Auto-retry state
|
|
240
|
-
private retryLoader:
|
|
241
|
+
private retryLoader: Component | undefined = undefined;
|
|
241
242
|
private retryEscapeHandler?: () => void;
|
|
242
243
|
|
|
243
244
|
// Messages queued while compaction is running
|
|
@@ -1153,7 +1154,7 @@ export class InteractiveMode {
|
|
|
1153
1154
|
waitForIdle: () => this.session.agent.waitForIdle(),
|
|
1154
1155
|
newSession: async (options) => {
|
|
1155
1156
|
if (this.loadingAnimation) {
|
|
1156
|
-
this.loadingAnimation.stop();
|
|
1157
|
+
(this.loadingAnimation as PencilLoader).stop();
|
|
1157
1158
|
this.loadingAnimation = undefined;
|
|
1158
1159
|
}
|
|
1159
1160
|
this.statusContainer.clear();
|
|
@@ -1439,7 +1440,7 @@ export class InteractiveMode {
|
|
|
1439
1440
|
this.defaultEditor.onExtensionShortcut = undefined;
|
|
1440
1441
|
this.updateTerminalTitle();
|
|
1441
1442
|
if (this.loadingAnimation) {
|
|
1442
|
-
this.loadingAnimation.setMessage(
|
|
1443
|
+
(this.loadingAnimation as PencilLoader).setMessage(
|
|
1443
1444
|
`${this.defaultWorkingMessage} (${appKey(this.keybindings, "interrupt")} to interrupt)`,
|
|
1444
1445
|
);
|
|
1445
1446
|
}
|
|
@@ -1606,9 +1607,9 @@ export class InteractiveMode {
|
|
|
1606
1607
|
setWorkingMessage: (message) => {
|
|
1607
1608
|
if (this.loadingAnimation) {
|
|
1608
1609
|
if (message) {
|
|
1609
|
-
this.loadingAnimation.setMessage(message);
|
|
1610
|
+
(this.loadingAnimation as PencilLoader).setMessage(message);
|
|
1610
1611
|
} else {
|
|
1611
|
-
this.loadingAnimation.setMessage(
|
|
1612
|
+
(this.loadingAnimation as PencilLoader).setMessage(
|
|
1612
1613
|
`${this.defaultWorkingMessage} (${appKey(this.keybindings, "interrupt")} to interrupt)`,
|
|
1613
1614
|
);
|
|
1614
1615
|
}
|
|
@@ -2237,6 +2238,16 @@ export class InteractiveMode {
|
|
|
2237
2238
|
await this.handleReloadCommand();
|
|
2238
2239
|
return;
|
|
2239
2240
|
}
|
|
2241
|
+
if (text === "/soul") {
|
|
2242
|
+
this.handleSoulCommand();
|
|
2243
|
+
this.editor.setText("");
|
|
2244
|
+
return;
|
|
2245
|
+
}
|
|
2246
|
+
if (text === "/memory") {
|
|
2247
|
+
this.handleMemoryCommand();
|
|
2248
|
+
this.editor.setText("");
|
|
2249
|
+
return;
|
|
2250
|
+
}
|
|
2240
2251
|
if (text === "/debug") {
|
|
2241
2252
|
this.handleDebugCommand();
|
|
2242
2253
|
this.editor.setText("");
|
|
@@ -2349,24 +2360,25 @@ export class InteractiveMode {
|
|
|
2349
2360
|
this.retryEscapeHandler = undefined;
|
|
2350
2361
|
}
|
|
2351
2362
|
if (this.retryLoader) {
|
|
2352
|
-
this.retryLoader.stop();
|
|
2363
|
+
(this.retryLoader as PencilLoader).stop();
|
|
2353
2364
|
this.retryLoader = undefined;
|
|
2354
2365
|
}
|
|
2355
2366
|
if (this.loadingAnimation) {
|
|
2356
|
-
this.loadingAnimation.stop();
|
|
2367
|
+
(this.loadingAnimation as PencilLoader).stop();
|
|
2357
2368
|
}
|
|
2358
2369
|
this.statusContainer.clear();
|
|
2359
|
-
this.loadingAnimation = new
|
|
2370
|
+
this.loadingAnimation = new PencilLoader(
|
|
2360
2371
|
this.ui,
|
|
2361
|
-
|
|
2362
|
-
(text) => theme.fg("muted", text),
|
|
2372
|
+
theme,
|
|
2363
2373
|
this.defaultWorkingMessage,
|
|
2364
2374
|
);
|
|
2365
2375
|
this.statusContainer.addChild(this.loadingAnimation);
|
|
2366
2376
|
// Apply any pending working message queued before loader existed
|
|
2367
2377
|
if (this.pendingWorkingMessage !== undefined) {
|
|
2368
2378
|
if (this.pendingWorkingMessage) {
|
|
2369
|
-
this.loadingAnimation.setMessage(
|
|
2379
|
+
(this.loadingAnimation as PencilLoader).setMessage(
|
|
2380
|
+
this.pendingWorkingMessage,
|
|
2381
|
+
);
|
|
2370
2382
|
}
|
|
2371
2383
|
this.pendingWorkingMessage = undefined;
|
|
2372
2384
|
}
|
|
@@ -2512,7 +2524,7 @@ export class InteractiveMode {
|
|
|
2512
2524
|
|
|
2513
2525
|
case "agent_end":
|
|
2514
2526
|
if (this.loadingAnimation) {
|
|
2515
|
-
this.loadingAnimation.stop();
|
|
2527
|
+
(this.loadingAnimation as PencilLoader).stop();
|
|
2516
2528
|
this.loadingAnimation = undefined;
|
|
2517
2529
|
this.statusContainer.clear();
|
|
2518
2530
|
}
|
|
@@ -2539,10 +2551,9 @@ export class InteractiveMode {
|
|
|
2539
2551
|
this.statusContainer.clear();
|
|
2540
2552
|
const reasonText =
|
|
2541
2553
|
event.reason === "overflow" ? "Context overflow detected, " : "";
|
|
2542
|
-
this.autoCompactionLoader = new
|
|
2554
|
+
this.autoCompactionLoader = new PencilLoader(
|
|
2543
2555
|
this.ui,
|
|
2544
|
-
|
|
2545
|
-
(text) => theme.fg("muted", text),
|
|
2556
|
+
theme,
|
|
2546
2557
|
`${reasonText}Auto-compacting... (${appKey(this.keybindings, "interrupt")} to cancel)`,
|
|
2547
2558
|
);
|
|
2548
2559
|
this.statusContainer.addChild(this.autoCompactionLoader);
|
|
@@ -2558,7 +2569,7 @@ export class InteractiveMode {
|
|
|
2558
2569
|
}
|
|
2559
2570
|
// Stop loader
|
|
2560
2571
|
if (this.autoCompactionLoader) {
|
|
2561
|
-
this.autoCompactionLoader.stop();
|
|
2572
|
+
(this.autoCompactionLoader as PencilLoader).stop();
|
|
2562
2573
|
this.autoCompactionLoader = undefined;
|
|
2563
2574
|
this.statusContainer.clear();
|
|
2564
2575
|
}
|
|
@@ -2598,10 +2609,9 @@ export class InteractiveMode {
|
|
|
2598
2609
|
// Show retry indicator
|
|
2599
2610
|
this.statusContainer.clear();
|
|
2600
2611
|
const delaySeconds = Math.round(event.delayMs / 1000);
|
|
2601
|
-
this.retryLoader = new
|
|
2612
|
+
this.retryLoader = new PencilLoader(
|
|
2602
2613
|
this.ui,
|
|
2603
|
-
|
|
2604
|
-
(text) => theme.fg("muted", text),
|
|
2614
|
+
theme,
|
|
2605
2615
|
`Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s... (${appKey(this.keybindings, "interrupt")} to cancel)`,
|
|
2606
2616
|
);
|
|
2607
2617
|
this.statusContainer.addChild(this.retryLoader);
|
|
@@ -2617,7 +2627,7 @@ export class InteractiveMode {
|
|
|
2617
2627
|
}
|
|
2618
2628
|
// Stop loader
|
|
2619
2629
|
if (this.retryLoader) {
|
|
2620
|
-
this.retryLoader.stop();
|
|
2630
|
+
(this.retryLoader as PencilLoader).stop();
|
|
2621
2631
|
this.retryLoader = undefined;
|
|
2622
2632
|
this.statusContainer.clear();
|
|
2623
2633
|
}
|
|
@@ -4033,7 +4043,7 @@ export class InteractiveMode {
|
|
|
4033
4043
|
}
|
|
4034
4044
|
|
|
4035
4045
|
// Set up escape handler and loader if summarizing
|
|
4036
|
-
let summaryLoader:
|
|
4046
|
+
let summaryLoader: Component | undefined;
|
|
4037
4047
|
const originalOnEscape = this.defaultEditor.onEscape;
|
|
4038
4048
|
|
|
4039
4049
|
if (wantsSummary) {
|
|
@@ -4041,10 +4051,9 @@ export class InteractiveMode {
|
|
|
4041
4051
|
this.session.abortBranchSummary();
|
|
4042
4052
|
};
|
|
4043
4053
|
this.chatContainer.addChild(new Spacer(1));
|
|
4044
|
-
summaryLoader = new
|
|
4054
|
+
summaryLoader = new PencilLoader(
|
|
4045
4055
|
this.ui,
|
|
4046
|
-
|
|
4047
|
-
(text) => theme.fg("muted", text),
|
|
4056
|
+
theme,
|
|
4048
4057
|
`Summarizing branch... (${appKey(this.keybindings, "interrupt")} to cancel)`,
|
|
4049
4058
|
);
|
|
4050
4059
|
this.statusContainer.addChild(summaryLoader);
|
|
@@ -4081,7 +4090,7 @@ export class InteractiveMode {
|
|
|
4081
4090
|
);
|
|
4082
4091
|
} finally {
|
|
4083
4092
|
if (summaryLoader) {
|
|
4084
|
-
summaryLoader.stop();
|
|
4093
|
+
(summaryLoader as PencilLoader).stop();
|
|
4085
4094
|
this.statusContainer.clear();
|
|
4086
4095
|
}
|
|
4087
4096
|
this.defaultEditor.onEscape = originalOnEscape;
|
|
@@ -4146,7 +4155,7 @@ export class InteractiveMode {
|
|
|
4146
4155
|
private async handleResumeSession(sessionPath: string): Promise<void> {
|
|
4147
4156
|
// Stop loading animation
|
|
4148
4157
|
if (this.loadingAnimation) {
|
|
4149
|
-
this.loadingAnimation.stop();
|
|
4158
|
+
(this.loadingAnimation as PencilLoader).stop();
|
|
4150
4159
|
this.loadingAnimation = undefined;
|
|
4151
4160
|
}
|
|
4152
4161
|
this.statusContainer.clear();
|
|
@@ -4794,7 +4803,7 @@ export class InteractiveMode {
|
|
|
4794
4803
|
private async handleClearCommand(): Promise<void> {
|
|
4795
4804
|
// Stop loading animation
|
|
4796
4805
|
if (this.loadingAnimation) {
|
|
4797
|
-
this.loadingAnimation.stop();
|
|
4806
|
+
(this.loadingAnimation as PencilLoader).stop();
|
|
4798
4807
|
this.loadingAnimation = undefined;
|
|
4799
4808
|
}
|
|
4800
4809
|
this.statusContainer.clear();
|
|
@@ -5011,7 +5020,7 @@ export class InteractiveMode {
|
|
|
5011
5020
|
): Promise<CompactionResult | undefined> {
|
|
5012
5021
|
// Stop loading animation
|
|
5013
5022
|
if (this.loadingAnimation) {
|
|
5014
|
-
this.loadingAnimation.stop();
|
|
5023
|
+
(this.loadingAnimation as PencilLoader).stop();
|
|
5015
5024
|
this.loadingAnimation = undefined;
|
|
5016
5025
|
}
|
|
5017
5026
|
this.statusContainer.clear();
|
|
@@ -5028,12 +5037,7 @@ export class InteractiveMode {
|
|
|
5028
5037
|
const label = isAuto
|
|
5029
5038
|
? `Auto-compacting context... ${cancelHint}`
|
|
5030
5039
|
: `Compacting context... ${cancelHint}`;
|
|
5031
|
-
const compactingLoader = new
|
|
5032
|
-
this.ui,
|
|
5033
|
-
(spinner) => theme.fg("accent", spinner),
|
|
5034
|
-
(text) => theme.fg("muted", text),
|
|
5035
|
-
label,
|
|
5036
|
-
);
|
|
5040
|
+
const compactingLoader = new PencilLoader(this.ui, theme, label);
|
|
5037
5041
|
this.statusContainer.addChild(compactingLoader);
|
|
5038
5042
|
this.ui.requestRender();
|
|
5039
5043
|
|
|
@@ -5065,7 +5069,7 @@ export class InteractiveMode {
|
|
|
5065
5069
|
this.showError(`Compaction failed: ${message}`);
|
|
5066
5070
|
}
|
|
5067
5071
|
} finally {
|
|
5068
|
-
compactingLoader.stop();
|
|
5072
|
+
(compactingLoader as PencilLoader).stop();
|
|
5069
5073
|
this.statusContainer.clear();
|
|
5070
5074
|
this.defaultEditor.onEscape = originalOnEscape;
|
|
5071
5075
|
}
|
|
@@ -5075,7 +5079,7 @@ export class InteractiveMode {
|
|
|
5075
5079
|
|
|
5076
5080
|
stop(): void {
|
|
5077
5081
|
if (this.loadingAnimation) {
|
|
5078
|
-
this.loadingAnimation.stop();
|
|
5082
|
+
(this.loadingAnimation as PencilLoader).stop();
|
|
5079
5083
|
this.loadingAnimation = undefined;
|
|
5080
5084
|
}
|
|
5081
5085
|
this.clearExtensionTerminalInputListeners();
|
|
@@ -5089,4 +5093,56 @@ export class InteractiveMode {
|
|
|
5089
5093
|
this.isInitialized = false;
|
|
5090
5094
|
}
|
|
5091
5095
|
}
|
|
5096
|
+
|
|
5097
|
+
private handleSoulCommand(): void {
|
|
5098
|
+
const soulManager = (this.session as any)._soulManager;
|
|
5099
|
+
if (!soulManager) {
|
|
5100
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
5101
|
+
this.chatContainer.addChild(
|
|
5102
|
+
new Text(theme.fg("warning", "⚠️ Soul 未启用"), 1, 0),
|
|
5103
|
+
);
|
|
5104
|
+
this.chatContainer.addChild(
|
|
5105
|
+
new Text(
|
|
5106
|
+
theme.fg(
|
|
5107
|
+
"dim",
|
|
5108
|
+
"Soul (AI 性格系统) 未启用。请确保使用 NanoPencil 1.3.0 或更高版本。",
|
|
5109
|
+
),
|
|
5110
|
+
1,
|
|
5111
|
+
0,
|
|
5112
|
+
),
|
|
5113
|
+
);
|
|
5114
|
+
this.ui.requestRender();
|
|
5115
|
+
return;
|
|
5116
|
+
}
|
|
5117
|
+
|
|
5118
|
+
const { formatSoulStats } = require("./components/soul-stats.js");
|
|
5119
|
+
const stats = formatSoulStats(soulManager, { compact: false });
|
|
5120
|
+
|
|
5121
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
5122
|
+
this.chatContainer.addChild(new Text(stats, 1, 0));
|
|
5123
|
+
this.ui.requestRender();
|
|
5124
|
+
}
|
|
5125
|
+
|
|
5126
|
+
private handleMemoryCommand(): void {
|
|
5127
|
+
const lines: string[] = [];
|
|
5128
|
+
lines.push(theme.fg("accent", "📚 Project Memory - NanoMem"));
|
|
5129
|
+
lines.push("");
|
|
5130
|
+
lines.push(theme.fg("dim", "存储位置: ~/.nanopencil/agent/memory/"));
|
|
5131
|
+
lines.push(theme.fg("dim", " - knowledge.json (项目知识)"));
|
|
5132
|
+
lines.push(theme.fg("dim", " - lessons.json (经验教训)"));
|
|
5133
|
+
lines.push(theme.fg("dim", " - preferences.json (用户偏好)"));
|
|
5134
|
+
lines.push(theme.fg("dim", " - patterns.json (行为模式)"));
|
|
5135
|
+
lines.push(theme.fg("dim", " - facets.json (模式/困境)"));
|
|
5136
|
+
lines.push("");
|
|
5137
|
+
lines.push(
|
|
5138
|
+
theme.fg("dim", "💡 提示: NanoMem 自动从对话中提取和记忆项目知识"),
|
|
5139
|
+
);
|
|
5140
|
+
lines.push(theme.fg("dim", " - 记住 API 端点、配置选项"));
|
|
5141
|
+
lines.push(theme.fg("dim", " - 学习错误模式和解决方案"));
|
|
5142
|
+
lines.push(theme.fg("dim", " - 识别用户偏好和编码风格"));
|
|
5143
|
+
|
|
5144
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
5145
|
+
this.chatContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
5146
|
+
this.ui.requestRender();
|
|
5147
|
+
}
|
|
5092
5148
|
}
|
|
@@ -1,81 +1,81 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
|
3
|
+
"name": "warm",
|
|
4
|
+
"vars": {
|
|
5
|
+
"warmBrown": "#c4a574",
|
|
6
|
+
"warmLight": "#d4a574",
|
|
7
|
+
"warmBorder": "#a08060",
|
|
8
|
+
"blue": "#5f87ff",
|
|
9
|
+
"green": "#b5bd68",
|
|
10
|
+
"red": "#cc6666",
|
|
11
|
+
"yellow": "#d4a84b",
|
|
12
|
+
"gray": "#808080",
|
|
13
|
+
"dimGray": "#666666",
|
|
14
|
+
"darkGray": "#505050",
|
|
15
|
+
"accent": "warmBrown",
|
|
16
|
+
"selectedBg": "#3d362a",
|
|
17
|
+
"userMsgBg": "#2e2a24",
|
|
18
|
+
"toolPendingBg": "#2a2820",
|
|
19
|
+
"toolSuccessBg": "#2a3024",
|
|
20
|
+
"toolErrorBg": "#3c2828",
|
|
21
|
+
"customMsgBg": "#2d2838"
|
|
22
|
+
},
|
|
23
|
+
"colors": {
|
|
24
|
+
"accent": "accent",
|
|
25
|
+
"border": "warmBorder",
|
|
26
|
+
"borderAccent": "warmLight",
|
|
27
|
+
"borderMuted": "darkGray",
|
|
28
|
+
"success": "green",
|
|
29
|
+
"error": "red",
|
|
30
|
+
"warning": "yellow",
|
|
31
|
+
"muted": "gray",
|
|
32
|
+
"dim": "dimGray",
|
|
33
|
+
"text": "",
|
|
34
|
+
"thinkingText": "gray",
|
|
35
|
+
"selectedBg": "selectedBg",
|
|
36
|
+
"userMessageBg": "userMsgBg",
|
|
37
|
+
"userMessageText": "warmLight",
|
|
38
|
+
"customMessageBg": "customMsgBg",
|
|
39
|
+
"customMessageText": "",
|
|
40
|
+
"customMessageLabel": "#b8956b",
|
|
41
|
+
"toolPendingBg": "toolPendingBg",
|
|
42
|
+
"toolSuccessBg": "toolSuccessBg",
|
|
43
|
+
"toolErrorBg": "toolErrorBg",
|
|
44
|
+
"toolTitle": "",
|
|
45
|
+
"toolOutput": "gray",
|
|
46
|
+
"mdHeading": "warmLight",
|
|
47
|
+
"mdLink": "#81a2be",
|
|
48
|
+
"mdLinkUrl": "dimGray",
|
|
49
|
+
"mdCode": "accent",
|
|
50
|
+
"mdCodeBlock": "green",
|
|
51
|
+
"mdCodeBlockBorder": "gray",
|
|
52
|
+
"mdQuote": "gray",
|
|
53
|
+
"mdQuoteBorder": "gray",
|
|
54
|
+
"mdHr": "gray",
|
|
55
|
+
"mdListBullet": "accent",
|
|
56
|
+
"toolDiffAdded": "green",
|
|
57
|
+
"toolDiffRemoved": "red",
|
|
58
|
+
"toolDiffContext": "gray",
|
|
59
|
+
"syntaxComment": "#6A9955",
|
|
60
|
+
"syntaxKeyword": "#569CD6",
|
|
61
|
+
"syntaxFunction": "#DCDCAA",
|
|
62
|
+
"syntaxVariable": "#9CDCFE",
|
|
63
|
+
"syntaxString": "#CE9178",
|
|
64
|
+
"syntaxNumber": "#B5CEA8",
|
|
65
|
+
"syntaxType": "#4EC9B0",
|
|
66
|
+
"syntaxOperator": "#D4D4D4",
|
|
67
|
+
"syntaxPunctuation": "#D4D4D4",
|
|
68
|
+
"thinkingOff": "darkGray",
|
|
69
|
+
"thinkingMinimal": "#6e6e6e",
|
|
70
|
+
"thinkingLow": "#5f87af",
|
|
71
|
+
"thinkingMedium": "warmBrown",
|
|
72
|
+
"thinkingHigh": "#b294bb",
|
|
73
|
+
"thinkingXhigh": "#d183e8",
|
|
74
|
+
"bashMode": "green"
|
|
75
|
+
},
|
|
76
|
+
"export": {
|
|
77
|
+
"pageBg": "#1a1814",
|
|
78
|
+
"cardBg": "#242018",
|
|
79
|
+
"infoBg": "#3c3728"
|
|
80
|
+
}
|
|
81
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pencil-agent/nano-pencil",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "CLI writing agent with read, bash, edit, write tools and session management. Based on pi; supports DashScope Coding Plan. Soul enabled by default for AI personality evolution.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|