@pencil-agent/nano-pencil 1.14.2 → 1.14.3
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/build-meta.json +3 -3
- package/dist/builtin-extensions.d.ts +4 -0
- package/dist/builtin-extensions.js +16 -16
- package/dist/core/i18n/slash-commands.d.ts +11 -0
- package/dist/core/i18n/slash-commands.js +15 -4
- package/dist/core/i18n/slash-commands.zh.d.ts +11 -0
- package/dist/core/i18n/slash-commands.zh.js +15 -4
- package/dist/core/runtime/slash-command-catalog.d.ts +2 -1
- package/dist/core/runtime/slash-command-catalog.js +9 -1
- package/dist/core/slash-commands.d.ts +9 -1
- package/dist/core/slash-commands.js +77 -32
- package/dist/extensions/defaults/browser/index.js +1 -1
- package/dist/extensions/defaults/debug/index.js +28 -3
- package/dist/extensions/defaults/link-world/index.js +1 -1
- package/dist/extensions/defaults/loop/cron/cron-scheduler.js +19 -0
- package/dist/extensions/defaults/security-audit/index.js +4 -4
- package/dist/extensions/defaults/team/team-ui.js +4 -14
- package/dist/extensions/defaults/token-save/index.js +8 -1
- package/dist/modes/interactive/interactive-mode.js +5 -15
- package/dist/node_modules/@pencil-agent/ai/models.generated.js +14 -14
- package/dist/node_modules/@pencil-agent/tui/autocomplete.js +7 -2
- package/dist/node_modules/@pencil-agent/tui/tui.d.ts +8 -3
- package/dist/node_modules/@pencil-agent/tui/tui.js +63 -31
- package/dist/packages/mem-core/extension.js +150 -83
- package/package.json +2 -2
package/dist/build-meta.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* [HERE]: builtin-extensions.ts - built-in extension registry for NanoPencil
|
|
6
6
|
*/
|
|
7
7
|
export type BuiltinExtensionRiskLevel = "passive" | "command" | "tool" | "background" | "write-capable";
|
|
8
|
+
export type BuiltinExtensionTestContract = "lifecycle" | "external-process" | "resource-discovery" | "write-guard";
|
|
8
9
|
export interface BuiltinExtension {
|
|
9
10
|
id: string;
|
|
10
11
|
category: "default" | "optional" | "package";
|
|
@@ -14,6 +15,9 @@ export interface BuiltinExtension {
|
|
|
14
15
|
startsTimers: boolean;
|
|
15
16
|
writesWorkspace: boolean;
|
|
16
17
|
externalProcess: boolean;
|
|
18
|
+
resourceDiscovery?: boolean;
|
|
19
|
+
testContracts?: readonly BuiltinExtensionTestContract[];
|
|
20
|
+
testFiles?: readonly string[];
|
|
17
21
|
}
|
|
18
22
|
export declare const builtInExtensions: readonly BuiltinExtension[];
|
|
19
23
|
/**
|
|
@@ -33,29 +33,29 @@ const BUNDLED_RECAP_EXTENSION = join(__dirname, "extensions", "defaults", "recap
|
|
|
33
33
|
const BUNDLED_DEBUG_EXTENSION = join(__dirname, "extensions", "defaults", "debug", "index.js");
|
|
34
34
|
const BUNDLED_MCP_EXTENSION = join(__dirname, "extensions", "defaults", "mcp", "index.js");
|
|
35
35
|
export const builtInExtensions = [
|
|
36
|
-
{ id: "diagnostics", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: true, writesWorkspace: false, externalProcess: false },
|
|
37
|
-
{ id: "sal", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
36
|
+
{ id: "diagnostics", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: true, writesWorkspace: false, externalProcess: false, testContracts: ["lifecycle"], testFiles: ["test/diagnostic-buffer-throttle.test.ts", "test/diagnostics-runtime.test.ts"] },
|
|
37
|
+
{ id: "sal", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false, testContracts: ["lifecycle"], testFiles: ["test/sal-lifecycle.test.ts"] },
|
|
38
38
|
{ id: "token-save", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
39
|
-
{ id: "nanomem", category: "package", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
40
|
-
{ id: "link-world", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true },
|
|
41
|
-
{ id: "browser", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true },
|
|
39
|
+
{ id: "nanomem", category: "package", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false, testContracts: ["lifecycle"], testFiles: ["packages/mem-core/test/extension-commands.test.ts"] },
|
|
40
|
+
{ id: "link-world", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true, resourceDiscovery: true, testContracts: ["external-process", "resource-discovery"], testFiles: ["test/link-world-extension-registration.test.ts"] },
|
|
41
|
+
{ id: "browser", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true, resourceDiscovery: true, testContracts: ["external-process", "resource-discovery"], testFiles: ["test/browser-extension-registration.test.ts"] },
|
|
42
42
|
{ id: "security-audit", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
43
|
-
{ id: "soul", category: "default", defaultEnabled: true, riskLevel: "
|
|
44
|
-
{ id: "presence", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: true, startsTimers: true, writesWorkspace: false, externalProcess: false },
|
|
43
|
+
{ id: "soul", category: "default", defaultEnabled: true, riskLevel: "passive", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
44
|
+
{ id: "presence", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: true, startsTimers: true, writesWorkspace: false, externalProcess: false, testContracts: ["lifecycle"], testFiles: ["test/presence-opening.test.ts", "test/presence-locale.test.ts"] },
|
|
45
45
|
{ id: "interview", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
46
|
-
{ id: "grub", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true },
|
|
47
|
-
{ id: "loop", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: true, writesWorkspace: false, externalProcess: false },
|
|
46
|
+
{ id: "grub", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true, testContracts: ["lifecycle", "external-process"], testFiles: ["test/grub-controller.test.ts"] },
|
|
47
|
+
{ id: "loop", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: true, writesWorkspace: false, externalProcess: false, testContracts: ["lifecycle"], testFiles: ["test/loop-lifecycle.test.ts"] },
|
|
48
48
|
{ id: "plan", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
49
|
-
{ id: "discipline", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
50
|
-
{ id: "subagent", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true },
|
|
51
|
-
{ id: "team", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true },
|
|
52
|
-
{ id: "idle-think", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: true, startsTimers: true, writesWorkspace: false, externalProcess: true },
|
|
49
|
+
{ id: "discipline", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false, resourceDiscovery: true, testContracts: ["resource-discovery"], testFiles: ["test/discipline-extension.test.ts", "test/extension-smoke.test.ts"] },
|
|
50
|
+
{ id: "subagent", category: "default", defaultEnabled: true, riskLevel: "tool", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true, testContracts: ["external-process"], testFiles: ["test/subagent-parser.test.ts", "test/worktree-manager.test.ts"] },
|
|
51
|
+
{ id: "team", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true, testContracts: ["lifecycle", "external-process"], testFiles: ["test/team-runtime.test.ts"] },
|
|
52
|
+
{ id: "idle-think", category: "default", defaultEnabled: true, riskLevel: "background", requiresUI: true, startsTimers: true, writesWorkspace: false, externalProcess: true, testContracts: ["lifecycle", "external-process"], testFiles: ["test/idle-think-runtime.test.ts", "test/extension-smoke.test.ts"] },
|
|
53
53
|
{ id: "btw", category: "default", defaultEnabled: true, riskLevel: "command", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
54
54
|
{ id: "recap", category: "default", defaultEnabled: true, riskLevel: "command", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
55
55
|
{ id: "debug", category: "default", defaultEnabled: true, riskLevel: "command", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: false },
|
|
56
|
-
{ id: "mcp", category: "default", defaultEnabled: true, riskLevel: "command", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true },
|
|
57
|
-
{ id: "simplify", category: "optional", defaultEnabled: false, riskLevel: "write-capable", requiresUI: false, startsTimers: false, writesWorkspace: true, externalProcess: true },
|
|
58
|
-
{ id: "export-html", category: "optional", defaultEnabled: false, riskLevel: "write-capable", requiresUI: false, startsTimers: false, writesWorkspace: true, externalProcess: false },
|
|
56
|
+
{ id: "mcp", category: "default", defaultEnabled: true, riskLevel: "command", requiresUI: false, startsTimers: false, writesWorkspace: false, externalProcess: true, resourceDiscovery: true, testContracts: ["external-process", "resource-discovery"], testFiles: ["test/resource-discovery-contract.test.ts"] },
|
|
57
|
+
{ id: "simplify", category: "optional", defaultEnabled: false, riskLevel: "write-capable", requiresUI: false, startsTimers: false, writesWorkspace: true, externalProcess: true, testContracts: ["external-process", "write-guard"], testFiles: ["test/simplify-extension.test.ts"] },
|
|
58
|
+
{ id: "export-html", category: "optional", defaultEnabled: false, riskLevel: "write-capable", requiresUI: false, startsTimers: false, writesWorkspace: true, externalProcess: false, testContracts: ["write-guard"], testFiles: ["test/extension-smoke.test.ts", "test/export-html-branch-navigation.test.ts"] },
|
|
59
59
|
];
|
|
60
60
|
/** Find package root from current module location (containing package.json with nano-pencil related name) */
|
|
61
61
|
function findPackageRoot(startDir) {
|
|
@@ -5,6 +5,16 @@
|
|
|
5
5
|
* [HERE]: core/i18n/slash-commands.ts - English slash command translations
|
|
6
6
|
*/
|
|
7
7
|
export declare const slashCommands: {
|
|
8
|
+
categories: {
|
|
9
|
+
core: string;
|
|
10
|
+
model: string;
|
|
11
|
+
memory: string;
|
|
12
|
+
session: string;
|
|
13
|
+
workflow: string;
|
|
14
|
+
agents: string;
|
|
15
|
+
tools: string;
|
|
16
|
+
admin: string;
|
|
17
|
+
};
|
|
8
18
|
settings: string;
|
|
9
19
|
model: string;
|
|
10
20
|
"agent-loop": string;
|
|
@@ -24,6 +34,7 @@ export declare const slashCommands: {
|
|
|
24
34
|
usage: string;
|
|
25
35
|
changelog: string;
|
|
26
36
|
hotkeys: string;
|
|
37
|
+
resources: string;
|
|
27
38
|
fork: string;
|
|
28
39
|
tree: string;
|
|
29
40
|
login: string;
|
|
@@ -5,16 +5,26 @@
|
|
|
5
5
|
* [HERE]: core/i18n/slash-commands.ts - English slash command translations
|
|
6
6
|
*/
|
|
7
7
|
export const slashCommands = {
|
|
8
|
+
categories: {
|
|
9
|
+
core: "Core",
|
|
10
|
+
model: "Models",
|
|
11
|
+
memory: "Memory",
|
|
12
|
+
session: "Sessions",
|
|
13
|
+
workflow: "Workflows",
|
|
14
|
+
agents: "Agents",
|
|
15
|
+
tools: "Tools",
|
|
16
|
+
admin: "Admin",
|
|
17
|
+
},
|
|
8
18
|
settings: "Open settings menu",
|
|
9
19
|
model: "Select model (opens selector UI)",
|
|
10
|
-
"agent-loop": "
|
|
11
|
-
"scoped-models": "
|
|
20
|
+
"agent-loop": "Choose how the agent keeps working through a task",
|
|
21
|
+
"scoped-models": "Choose which models appear in quick switching",
|
|
12
22
|
apikey: "Update API key for current provider",
|
|
13
23
|
mcp: "Manage MCP servers (list, enable, disable)",
|
|
14
24
|
soul: "Show AI personality and stats (Soul)",
|
|
15
25
|
persona: "Switch AI persona/personality pack",
|
|
16
26
|
memory: "Show project memory and knowledge (NanoMem)",
|
|
17
|
-
dream: "
|
|
27
|
+
dream: "Refresh long-term project memory (NanoMem)",
|
|
18
28
|
export: "Export session to HTML file",
|
|
19
29
|
share: "Share session as a secret GitHub gist",
|
|
20
30
|
copy: "Copy last agent message to clipboard",
|
|
@@ -24,6 +34,7 @@ export const slashCommands = {
|
|
|
24
34
|
usage: "Show token usage and cost stats",
|
|
25
35
|
changelog: "Show changelog entries",
|
|
26
36
|
hotkeys: "Show all keyboard shortcuts",
|
|
37
|
+
resources: "Show loaded extensions, prompts, skills, and themes",
|
|
27
38
|
fork: "Create a new fork from a previous message",
|
|
28
39
|
tree: "Navigate session tree (switch branches)",
|
|
29
40
|
login: "Login with OAuth provider",
|
|
@@ -34,7 +45,7 @@ export const slashCommands = {
|
|
|
34
45
|
compact: "Manually compact the session context",
|
|
35
46
|
resume: "Resume a different session",
|
|
36
47
|
reload: "Reload extensions, skills, prompts, and themes",
|
|
37
|
-
"link-world": "
|
|
48
|
+
"link-world": "Set up internet access tools",
|
|
38
49
|
quit: "Quit NanoPencil",
|
|
39
50
|
language: "Switch language (English/Chinese)",
|
|
40
51
|
};
|
|
@@ -5,6 +5,16 @@
|
|
|
5
5
|
* [HERE]: core/i18n/slash-commands.zh.ts - Chinese slash command translations
|
|
6
6
|
*/
|
|
7
7
|
export declare const slashCommands: {
|
|
8
|
+
categories: {
|
|
9
|
+
core: string;
|
|
10
|
+
model: string;
|
|
11
|
+
memory: string;
|
|
12
|
+
session: string;
|
|
13
|
+
workflow: string;
|
|
14
|
+
agents: string;
|
|
15
|
+
tools: string;
|
|
16
|
+
admin: string;
|
|
17
|
+
};
|
|
8
18
|
settings: string;
|
|
9
19
|
model: string;
|
|
10
20
|
"agent-loop": string;
|
|
@@ -24,6 +34,7 @@ export declare const slashCommands: {
|
|
|
24
34
|
usage: string;
|
|
25
35
|
changelog: string;
|
|
26
36
|
hotkeys: string;
|
|
37
|
+
resources: string;
|
|
27
38
|
fork: string;
|
|
28
39
|
tree: string;
|
|
29
40
|
login: string;
|
|
@@ -5,16 +5,26 @@
|
|
|
5
5
|
* [HERE]: core/i18n/slash-commands.zh.ts - Chinese slash command translations
|
|
6
6
|
*/
|
|
7
7
|
export const slashCommands = {
|
|
8
|
+
categories: {
|
|
9
|
+
core: "核心",
|
|
10
|
+
model: "模型",
|
|
11
|
+
memory: "记忆",
|
|
12
|
+
session: "会话",
|
|
13
|
+
workflow: "工作流",
|
|
14
|
+
agents: "Agent",
|
|
15
|
+
tools: "工具",
|
|
16
|
+
admin: "管理",
|
|
17
|
+
},
|
|
8
18
|
settings: "打开设置菜单",
|
|
9
19
|
model: "选择模型(打开选择器界面)",
|
|
10
|
-
"agent-loop": "
|
|
11
|
-
"scoped-models": "
|
|
20
|
+
"agent-loop": "选择 agent 推进任务的方式",
|
|
21
|
+
"scoped-models": "选择快速切换里出现的模型",
|
|
12
22
|
apikey: "更新当前提供商的 API 密钥",
|
|
13
23
|
mcp: "管理 MCP 服务器(列出、启用、禁用)",
|
|
14
24
|
soul: "显示 AI 人格和统计(灵魂)",
|
|
15
25
|
persona: "切换 AI 人格/个性包",
|
|
16
26
|
memory: "显示项目记忆和知识(纳米记忆)",
|
|
17
|
-
dream: "
|
|
27
|
+
dream: "刷新长期项目记忆(纳米记忆)",
|
|
18
28
|
export: "将会话导出为 HTML 文件",
|
|
19
29
|
share: "将会话分享为保密的 GitHub gist",
|
|
20
30
|
copy: "复制上一条 AI 消息到剪贴板",
|
|
@@ -24,6 +34,7 @@ export const slashCommands = {
|
|
|
24
34
|
usage: "显示 token 使用量和费用统计",
|
|
25
35
|
changelog: "显示更新日志条目",
|
|
26
36
|
hotkeys: "显示所有键盘快捷键",
|
|
37
|
+
resources: "显示已加载的扩展、提示、技能和主题",
|
|
27
38
|
fork: "从上一条消息创建新分支",
|
|
28
39
|
tree: "导航会话树(切换分支)",
|
|
29
40
|
login: "通过 OAuth 提供商登录",
|
|
@@ -34,7 +45,7 @@ export const slashCommands = {
|
|
|
34
45
|
compact: "手动压缩会话上下文",
|
|
35
46
|
resume: "恢复其他会话",
|
|
36
47
|
reload: "重新加载扩展、技能、提示和主题",
|
|
37
|
-
"link-world": "
|
|
48
|
+
"link-world": "设置联网访问工具",
|
|
38
49
|
quit: "退出 NanoPencil",
|
|
39
50
|
language: "切换语言(English/中文)",
|
|
40
51
|
};
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
import type { ResourceLoader } from "../config/resource-loader.js";
|
|
8
8
|
import type { ExtensionRunner } from "../extensions/index.js";
|
|
9
9
|
import type { PromptTemplate } from "../prompt/prompt-templates.js";
|
|
10
|
-
import { getLocalizedCommands, type SlashCommandInfo } from "../slash-commands.js";
|
|
10
|
+
import { getLocalizedCommands, type SlashCommandInfo, type SlashCommandCategory } from "../slash-commands.js";
|
|
11
11
|
export interface SessionSlashCommandDescriptor {
|
|
12
12
|
name: string;
|
|
13
13
|
description?: string;
|
|
14
14
|
source: "builtin" | SlashCommandInfo["source"];
|
|
15
|
+
category?: SlashCommandCategory;
|
|
15
16
|
}
|
|
16
17
|
type Translate = Parameters<typeof getLocalizedCommands>[0];
|
|
17
18
|
export interface SlashCommandCatalogSource {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BUILTIN_SLASH_COMMANDS, getLocalizedCommands, } from "../slash-commands.js";
|
|
1
|
+
import { BUILTIN_SLASH_COMMANDS, getLocalizedCommands, inferSlashCommandCategory, } from "../slash-commands.js";
|
|
2
2
|
function normalizeLocation(source) {
|
|
3
3
|
if (source === "user" || source === "project" || source === "path") {
|
|
4
4
|
return source;
|
|
@@ -16,6 +16,7 @@ function getExtensionCommands(runner, reservedBuiltins) {
|
|
|
16
16
|
name: command.name,
|
|
17
17
|
description: command.description,
|
|
18
18
|
path: extensionPath,
|
|
19
|
+
category: inferSlashCommandCategory(command.name, "extension"),
|
|
19
20
|
})) ?? []);
|
|
20
21
|
}
|
|
21
22
|
export function buildSessionSlashCommands(source, translate) {
|
|
@@ -23,22 +24,26 @@ export function buildSessionSlashCommands(source, translate) {
|
|
|
23
24
|
name: command.name,
|
|
24
25
|
description: command.description,
|
|
25
26
|
source: "builtin",
|
|
27
|
+
category: command.category,
|
|
26
28
|
}));
|
|
27
29
|
const reservedBuiltins = getReservedBuiltinNames();
|
|
28
30
|
const extensionCommands = getExtensionCommands(source.extensionRunner, reservedBuiltins).map((command) => ({
|
|
29
31
|
name: command.name,
|
|
30
32
|
description: command.description,
|
|
31
33
|
source: "extension",
|
|
34
|
+
category: command.category,
|
|
32
35
|
}));
|
|
33
36
|
const promptCommands = source.promptTemplates.map((template) => ({
|
|
34
37
|
name: template.name,
|
|
35
38
|
description: template.description,
|
|
36
39
|
source: "prompt",
|
|
40
|
+
category: inferSlashCommandCategory(template.name, "prompt"),
|
|
37
41
|
}));
|
|
38
42
|
const skillCommands = source.resourceLoader.getSkills().skills.map((skill) => ({
|
|
39
43
|
name: `skill:${skill.name}`,
|
|
40
44
|
description: skill.description,
|
|
41
45
|
source: "skill",
|
|
46
|
+
category: inferSlashCommandCategory(skill.name, "skill"),
|
|
42
47
|
}));
|
|
43
48
|
return [
|
|
44
49
|
...builtins,
|
|
@@ -53,12 +58,14 @@ export function buildExtensionSlashCommands(source) {
|
|
|
53
58
|
name: command.name,
|
|
54
59
|
description: command.description,
|
|
55
60
|
source: "extension",
|
|
61
|
+
category: command.category,
|
|
56
62
|
path: command.path,
|
|
57
63
|
}));
|
|
58
64
|
const templates = source.promptTemplates.map((template) => ({
|
|
59
65
|
name: template.name,
|
|
60
66
|
description: template.description,
|
|
61
67
|
source: "prompt",
|
|
68
|
+
category: inferSlashCommandCategory(template.name, "prompt"),
|
|
62
69
|
location: normalizeLocation(template.source),
|
|
63
70
|
path: template.filePath,
|
|
64
71
|
}));
|
|
@@ -68,6 +75,7 @@ export function buildExtensionSlashCommands(source) {
|
|
|
68
75
|
name: `skill:${skill.name}`,
|
|
69
76
|
description: skill.description,
|
|
70
77
|
source: "skill",
|
|
78
|
+
category: inferSlashCommandCategory(skill.name, "skill"),
|
|
71
79
|
location: normalizeLocation(skill.source),
|
|
72
80
|
path: skill.filePath,
|
|
73
81
|
}));
|
|
@@ -1,25 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* [WHO]: SlashCommandInfo, BuiltinSlashCommand, slashCommand definitions, getLocalizedCommands()
|
|
2
|
+
* [WHO]: SlashCommandInfo, BuiltinSlashCommand, slashCommand definitions, category helpers, getLocalizedCommands()
|
|
3
3
|
* [FROM]: No external dependencies
|
|
4
4
|
* [TO]: Consumed by modes/interactive/interactive-mode.ts, modes/acp/acp-mode.ts
|
|
5
5
|
* [HERE]: core/slash-commands.ts - slash command types and registry
|
|
6
6
|
*/
|
|
7
7
|
export type SlashCommandSource = "extension" | "prompt" | "skill";
|
|
8
8
|
export type SlashCommandLocation = "user" | "project" | "path";
|
|
9
|
+
export type SlashCommandCategory = "core" | "model" | "memory" | "session" | "workflow" | "agents" | "tools" | "admin";
|
|
9
10
|
export interface SlashCommandInfo {
|
|
10
11
|
name: string;
|
|
11
12
|
description?: string;
|
|
12
13
|
source: SlashCommandSource;
|
|
14
|
+
category?: SlashCommandCategory;
|
|
13
15
|
location?: SlashCommandLocation;
|
|
14
16
|
path?: string;
|
|
15
17
|
}
|
|
16
18
|
export interface BuiltinSlashCommand {
|
|
17
19
|
name: string;
|
|
18
20
|
descriptionKey: string;
|
|
21
|
+
category: SlashCommandCategory;
|
|
19
22
|
}
|
|
20
23
|
export declare const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand>;
|
|
24
|
+
export declare function inferSlashCommandCategory(name: string, source?: SlashCommandSource): SlashCommandCategory;
|
|
25
|
+
export declare function getSlashCommandCategoryLabel(category: SlashCommandCategory, t: (key: string) => string): string;
|
|
26
|
+
export declare function formatSlashCommandDescription(description: string | undefined, category: SlashCommandCategory | undefined, t: (key: string) => string): string | undefined;
|
|
21
27
|
export interface LocalizedSlashCommand {
|
|
22
28
|
name: string;
|
|
23
29
|
description: string;
|
|
30
|
+
category: SlashCommandCategory;
|
|
31
|
+
categoryLabel: string;
|
|
24
32
|
}
|
|
25
33
|
export declare function getLocalizedCommands(t: (key: string) => string): LocalizedSlashCommand[];
|
|
@@ -1,40 +1,85 @@
|
|
|
1
1
|
export const BUILTIN_SLASH_COMMANDS = [
|
|
2
|
-
{ name: "settings", descriptionKey: "slash.settings" },
|
|
3
|
-
{ name: "model", descriptionKey: "slash.model" },
|
|
4
|
-
{ name: "agent-loop", descriptionKey: "slash.agent-loop" },
|
|
5
|
-
{ name: "scoped-models", descriptionKey: "slash.scoped-models" },
|
|
6
|
-
{ name: "apikey", descriptionKey: "slash.apikey" },
|
|
7
|
-
{ name: "mcp", descriptionKey: "slash.mcp" },
|
|
8
|
-
{ name: "soul", descriptionKey: "slash.soul" },
|
|
9
|
-
{ name: "persona", descriptionKey: "slash.persona" },
|
|
10
|
-
{ name: "memory", descriptionKey: "slash.memory" },
|
|
11
|
-
{ name: "dream", descriptionKey: "slash.dream" },
|
|
12
|
-
{ name: "export", descriptionKey: "slash.export" },
|
|
13
|
-
{ name: "share", descriptionKey: "slash.share" },
|
|
14
|
-
{ name: "copy", descriptionKey: "slash.copy" },
|
|
15
|
-
{ name: "name", descriptionKey: "slash.name" },
|
|
16
|
-
{ name: "session", descriptionKey: "slash.session" },
|
|
17
|
-
{ name: "status", descriptionKey: "slash.status" },
|
|
18
|
-
{ name: "usage", descriptionKey: "slash.usage" },
|
|
19
|
-
{ name: "changelog", descriptionKey: "slash.changelog" },
|
|
20
|
-
{ name: "hotkeys", descriptionKey: "slash.hotkeys" },
|
|
21
|
-
{ name: "
|
|
22
|
-
{ name: "
|
|
23
|
-
{ name: "
|
|
24
|
-
{ name: "
|
|
25
|
-
{ name: "
|
|
26
|
-
{ name: "
|
|
27
|
-
{ name: "
|
|
28
|
-
{ name: "
|
|
29
|
-
{ name: "
|
|
30
|
-
{ name: "
|
|
31
|
-
{ name: "
|
|
32
|
-
{ name: "
|
|
33
|
-
{ name: "
|
|
2
|
+
{ name: "settings", descriptionKey: "slash.settings", category: "core" },
|
|
3
|
+
{ name: "model", descriptionKey: "slash.model", category: "model" },
|
|
4
|
+
{ name: "agent-loop", descriptionKey: "slash.agent-loop", category: "model" },
|
|
5
|
+
{ name: "scoped-models", descriptionKey: "slash.scoped-models", category: "model" },
|
|
6
|
+
{ name: "apikey", descriptionKey: "slash.apikey", category: "model" },
|
|
7
|
+
{ name: "mcp", descriptionKey: "slash.mcp", category: "tools" },
|
|
8
|
+
{ name: "soul", descriptionKey: "slash.soul", category: "memory" },
|
|
9
|
+
{ name: "persona", descriptionKey: "slash.persona", category: "core" },
|
|
10
|
+
{ name: "memory", descriptionKey: "slash.memory", category: "memory" },
|
|
11
|
+
{ name: "dream", descriptionKey: "slash.dream", category: "memory" },
|
|
12
|
+
{ name: "export", descriptionKey: "slash.export", category: "tools" },
|
|
13
|
+
{ name: "share", descriptionKey: "slash.share", category: "tools" },
|
|
14
|
+
{ name: "copy", descriptionKey: "slash.copy", category: "core" },
|
|
15
|
+
{ name: "name", descriptionKey: "slash.name", category: "session" },
|
|
16
|
+
{ name: "session", descriptionKey: "slash.session", category: "session" },
|
|
17
|
+
{ name: "status", descriptionKey: "slash.status", category: "core" },
|
|
18
|
+
{ name: "usage", descriptionKey: "slash.usage", category: "core" },
|
|
19
|
+
{ name: "changelog", descriptionKey: "slash.changelog", category: "core" },
|
|
20
|
+
{ name: "hotkeys", descriptionKey: "slash.hotkeys", category: "core" },
|
|
21
|
+
{ name: "resources", descriptionKey: "slash.resources", category: "core" },
|
|
22
|
+
{ name: "fork", descriptionKey: "slash.fork", category: "session" },
|
|
23
|
+
{ name: "tree", descriptionKey: "slash.tree", category: "session" },
|
|
24
|
+
{ name: "login", descriptionKey: "slash.login", category: "model" },
|
|
25
|
+
{ name: "logout", descriptionKey: "slash.logout", category: "model" },
|
|
26
|
+
{ name: "new", descriptionKey: "slash.new", category: "session" },
|
|
27
|
+
{ name: "update", descriptionKey: "slash.update", category: "admin" },
|
|
28
|
+
{ name: "reinstall", descriptionKey: "slash.reinstall", category: "admin" },
|
|
29
|
+
{ name: "compact", descriptionKey: "slash.compact", category: "session" },
|
|
30
|
+
{ name: "resume", descriptionKey: "slash.resume", category: "session" },
|
|
31
|
+
{ name: "reload", descriptionKey: "slash.reload", category: "admin" },
|
|
32
|
+
{ name: "link-world", descriptionKey: "slash.link-world", category: "tools" },
|
|
33
|
+
{ name: "language", descriptionKey: "slash.language", category: "core" },
|
|
34
|
+
{ name: "quit", descriptionKey: "slash.quit", category: "core" },
|
|
34
35
|
];
|
|
36
|
+
export function inferSlashCommandCategory(name, source) {
|
|
37
|
+
if (source === "prompt")
|
|
38
|
+
return "workflow";
|
|
39
|
+
if (source === "skill")
|
|
40
|
+
return "tools";
|
|
41
|
+
if (name === "dream" || name === "memory" || name.startsWith("mem-"))
|
|
42
|
+
return "memory";
|
|
43
|
+
if (name === "team" || name.startsWith("team:") || name === "subagent" || name.startsWith("subagent:"))
|
|
44
|
+
return "agents";
|
|
45
|
+
if (name === "grub" ||
|
|
46
|
+
name === "loop" ||
|
|
47
|
+
name === "plan" ||
|
|
48
|
+
name.startsWith("plan:") ||
|
|
49
|
+
name === "recap" ||
|
|
50
|
+
name === "btw" ||
|
|
51
|
+
name === "interview" ||
|
|
52
|
+
name === "grill-me" ||
|
|
53
|
+
name === "simplify") {
|
|
54
|
+
return "workflow";
|
|
55
|
+
}
|
|
56
|
+
if (name === "browser" || name === "figma" || name === "link-world" || name === "export")
|
|
57
|
+
return "tools";
|
|
58
|
+
if (name === "debug" ||
|
|
59
|
+
name === "set-locale" ||
|
|
60
|
+
name === "report-issue" ||
|
|
61
|
+
name === "tokensave" ||
|
|
62
|
+
name === "security" ||
|
|
63
|
+
name.startsWith("security-") ||
|
|
64
|
+
name.startsWith("sal:")) {
|
|
65
|
+
return "admin";
|
|
66
|
+
}
|
|
67
|
+
return "tools";
|
|
68
|
+
}
|
|
69
|
+
export function getSlashCommandCategoryLabel(category, t) {
|
|
70
|
+
return t(`slash.categories.${category}`);
|
|
71
|
+
}
|
|
72
|
+
export function formatSlashCommandDescription(description, category, t) {
|
|
73
|
+
if (!description || !category)
|
|
74
|
+
return description;
|
|
75
|
+
const label = getSlashCommandCategoryLabel(category, t);
|
|
76
|
+
return label && !label.startsWith("slash.") ? `${label} · ${description}` : description;
|
|
77
|
+
}
|
|
35
78
|
export function getLocalizedCommands(t) {
|
|
36
79
|
return BUILTIN_SLASH_COMMANDS.map((cmd) => ({
|
|
37
80
|
name: cmd.name,
|
|
38
81
|
description: t(cmd.descriptionKey),
|
|
82
|
+
category: cmd.category,
|
|
83
|
+
categoryLabel: getSlashCommandCategoryLabel(cmd.category, t),
|
|
39
84
|
}));
|
|
40
85
|
}
|
|
@@ -328,7 +328,7 @@ export default function browserExtension(api) {
|
|
|
328
328
|
ensureBrowserWorkspace();
|
|
329
329
|
});
|
|
330
330
|
api.registerCommand("browser", {
|
|
331
|
-
description: "
|
|
331
|
+
description: "Set up or inspect browser automation tools",
|
|
332
332
|
getArgumentCompletions: (argumentPrefix) => {
|
|
333
333
|
const prefix = argumentPrefix.trim();
|
|
334
334
|
const values = ["install", "status", "setup", "reload", "workspace", "help"]
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* [HERE]: extensions/defaults/debug/index.ts - system diagnostics with three-layer analysis through full agent loop
|
|
6
6
|
*/
|
|
7
7
|
import { Box, Container, Spacer, Text } from "@pencil-agent/tui";
|
|
8
|
-
import { collectSystemInfo, collectModelInfo, collectSessionInfo, collectConfigInfo, collectGitInfo, collectAgentState, sanitizeForLLM, formatDiagnosticData, } from "./collectors.js";
|
|
8
|
+
import { collectSystemInfo, collectModelInfo, collectSessionInfo, collectConfigInfo, collectGitInfo, collectAgentState, collectPreferencesInfo, sanitizeForLLM, formatDiagnosticData, } from "./collectors.js";
|
|
9
9
|
const DEBUG_MESSAGE_TYPE = "debug";
|
|
10
10
|
const DEBUG_PROMPT_PREFIX = "[DEBUG:";
|
|
11
11
|
const DEBUG_TAG = "[DEBUG]";
|
|
@@ -55,6 +55,8 @@ function parseDebugArgs(args) {
|
|
|
55
55
|
return { subcommand: "session" };
|
|
56
56
|
if (trimmed === "model")
|
|
57
57
|
return { subcommand: "model" };
|
|
58
|
+
if (trimmed === "preferences")
|
|
59
|
+
return { subcommand: "preferences" };
|
|
58
60
|
return { subcommand: "full", issueDescription: args.trim() || undefined };
|
|
59
61
|
}
|
|
60
62
|
// ============================================================================
|
|
@@ -132,6 +134,15 @@ async function handleQuickSub(subcommand, ctx, api) {
|
|
|
132
134
|
: `> Collection failed: ${info.error}`;
|
|
133
135
|
break;
|
|
134
136
|
}
|
|
137
|
+
case "preferences": {
|
|
138
|
+
const info = await collectPreferencesInfo(ctx);
|
|
139
|
+
result = info.data
|
|
140
|
+
? `| Preferences | |\n|---|---|\n${Object.entries(info.data)
|
|
141
|
+
.map(([k, v]) => `| ${k} | ${typeof v === "string" ? v : JSON.stringify(v)} |`)
|
|
142
|
+
.join("\n")}`
|
|
143
|
+
: `> Collection failed: ${info.error}`;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
135
146
|
}
|
|
136
147
|
api.sendMessage({
|
|
137
148
|
customType: DEBUG_MESSAGE_TYPE,
|
|
@@ -170,7 +181,7 @@ export default async function debugExtension(api) {
|
|
|
170
181
|
return container;
|
|
171
182
|
});
|
|
172
183
|
api.on("before_agent_start", (event) => {
|
|
173
|
-
if (!isDebugPrompt(event.prompt))
|
|
184
|
+
if (!isDebugPrompt(event.prompt) || event.prompt !== pendingDiagnosticPrompt)
|
|
174
185
|
return;
|
|
175
186
|
return { appendSystemPrompt: DEBUG_SYSTEM_PROMPT };
|
|
176
187
|
});
|
|
@@ -180,12 +191,26 @@ export default async function debugExtension(api) {
|
|
|
180
191
|
}
|
|
181
192
|
});
|
|
182
193
|
api.registerCommand("debug", {
|
|
183
|
-
description: "
|
|
194
|
+
description: "Check NanoPencil health or investigate an issue. Usage: /debug [env|session|model|preferences|<issue>]",
|
|
195
|
+
getArgumentCompletions: (argumentPrefix) => {
|
|
196
|
+
const prefix = argumentPrefix.trim().toLowerCase();
|
|
197
|
+
const values = ["env", "session", "model", "preferences"]
|
|
198
|
+
.filter((value) => value.startsWith(prefix))
|
|
199
|
+
.map((value) => ({ value, label: value }));
|
|
200
|
+
return values.length > 0 ? values : null;
|
|
201
|
+
},
|
|
184
202
|
handler: (args, ctx) => handleDebugCommand(args, ctx, api),
|
|
185
203
|
});
|
|
186
204
|
// Register /set-locale command
|
|
187
205
|
api.registerCommand("set-locale", {
|
|
188
206
|
description: "Set language preference (/set-locale zh|en)",
|
|
207
|
+
getArgumentCompletions: (argumentPrefix) => {
|
|
208
|
+
const prefix = argumentPrefix.trim().toLowerCase();
|
|
209
|
+
const values = ["zh", "en"]
|
|
210
|
+
.filter((value) => value.startsWith(prefix))
|
|
211
|
+
.map((value) => ({ value, label: value }));
|
|
212
|
+
return values.length > 0 ? values : null;
|
|
213
|
+
},
|
|
189
214
|
handler: async (args, ctx) => {
|
|
190
215
|
const trimmed = args.trim().toLowerCase();
|
|
191
216
|
if (trimmed !== "zh" && trimmed !== "en") {
|
|
@@ -325,7 +325,7 @@ export default function linkWorldExtension(api) {
|
|
|
325
325
|
ensureLinkWorldWorkspace();
|
|
326
326
|
});
|
|
327
327
|
api.registerCommand("link-world", {
|
|
328
|
-
description: "
|
|
328
|
+
description: "Set up or inspect internet access tools",
|
|
329
329
|
getArgumentCompletions: (argumentPrefix) => {
|
|
330
330
|
const prefix = argumentPrefix.trim();
|
|
331
331
|
const values = ["status", "doctor", "version", "install", "workspace", "help"]
|
|
@@ -233,12 +233,30 @@ export function createCronScheduler(options) {
|
|
|
233
233
|
}, WATCH_INTERVAL_MS);
|
|
234
234
|
}
|
|
235
235
|
async function enable() {
|
|
236
|
+
if (isKilled())
|
|
237
|
+
return;
|
|
236
238
|
enabled = true;
|
|
237
239
|
if (dir) {
|
|
238
240
|
await acquireLock();
|
|
241
|
+
if (isKilled()) {
|
|
242
|
+
await releaseLock();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
239
245
|
await loadTasks();
|
|
246
|
+
if (isKilled()) {
|
|
247
|
+
await releaseLock();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
240
250
|
startWatching();
|
|
241
251
|
}
|
|
252
|
+
if (isKilled()) {
|
|
253
|
+
if (watchIntervalId) {
|
|
254
|
+
clearInterval(watchIntervalId);
|
|
255
|
+
watchIntervalId = null;
|
|
256
|
+
}
|
|
257
|
+
await releaseLock();
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
242
260
|
intervalId = setInterval(check, TICK_MS);
|
|
243
261
|
console.log("[Cron-Scheduler] Scheduler enabled and ticking");
|
|
244
262
|
}
|
|
@@ -266,6 +284,7 @@ export function createCronScheduler(options) {
|
|
|
266
284
|
},
|
|
267
285
|
stop() {
|
|
268
286
|
killed = true;
|
|
287
|
+
enabled = false;
|
|
269
288
|
if (intervalId) {
|
|
270
289
|
clearInterval(intervalId);
|
|
271
290
|
intervalId = null;
|
|
@@ -99,7 +99,7 @@ function auditAndGateToolCall(api, event, ctx) {
|
|
|
99
99
|
export default function securityAuditExtension(api) {
|
|
100
100
|
// /security - Show security dashboard
|
|
101
101
|
api.registerCommand("security", {
|
|
102
|
-
description: "Show security
|
|
102
|
+
description: "Show recent security warnings and blocked actions",
|
|
103
103
|
handler: async (_args, ctx) => {
|
|
104
104
|
const logger = getLogger(ctx);
|
|
105
105
|
const stats = logger.getStats();
|
|
@@ -133,7 +133,7 @@ export default function securityAuditExtension(api) {
|
|
|
133
133
|
});
|
|
134
134
|
// /security-logs - Show detailed logs
|
|
135
135
|
api.registerCommand("security-logs", {
|
|
136
|
-
description: "Show detailed security
|
|
136
|
+
description: "Show detailed security event history",
|
|
137
137
|
handler: async (args, ctx) => {
|
|
138
138
|
const logger = getLogger(ctx);
|
|
139
139
|
const limit = parseInt(args) || 50;
|
|
@@ -158,7 +158,7 @@ export default function securityAuditExtension(api) {
|
|
|
158
158
|
});
|
|
159
159
|
// /security-stats - Show statistics
|
|
160
160
|
api.registerCommand("security-stats", {
|
|
161
|
-
description: "Show security
|
|
161
|
+
description: "Show security event counts",
|
|
162
162
|
handler: async (_args, ctx) => {
|
|
163
163
|
const logger = getLogger(ctx);
|
|
164
164
|
const stats = logger.getStats();
|
|
@@ -189,7 +189,7 @@ export default function securityAuditExtension(api) {
|
|
|
189
189
|
});
|
|
190
190
|
// /security-clear - Clear logs
|
|
191
191
|
api.registerCommand("security-clear", {
|
|
192
|
-
description: "Clear security
|
|
192
|
+
description: "Clear saved security events",
|
|
193
193
|
handler: async (_args, ctx) => {
|
|
194
194
|
const logger = getLogger(ctx);
|
|
195
195
|
logger.clear();
|