@pencil-agent/nano-pencil 1.13.16 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/build-meta.json +4 -4
- package/dist/builtin-extensions.js +20 -0
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.js +6 -1
- package/dist/config.d.ts +26 -3
- package/dist/config.js +92 -16
- package/dist/core/agent-dir/agent-dir-context.d.ts +60 -0
- package/dist/core/agent-dir/agent-dir-context.js +84 -0
- package/dist/core/agent-dir/agent-metadata.d.ts +48 -0
- package/dist/core/agent-dir/agent-metadata.js +68 -0
- package/dist/core/agent-dir/migration-tool.d.ts +39 -0
- package/dist/core/agent-dir/migration-tool.js +179 -0
- package/dist/core/bash-executor.d.ts +2 -0
- package/dist/core/bash-executor.js +1 -0
- package/dist/core/config/auth-storage.d.ts +2 -1
- package/dist/core/config/auth-storage.js +4 -2
- package/dist/core/config/resource-loader.d.ts +3 -0
- package/dist/core/config/resource-loader.js +7 -4
- package/dist/core/config/settings-manager.d.ts +4 -3
- package/dist/core/config/settings-manager.js +10 -7
- package/dist/core/extensions/loader.d.ts +2 -2
- package/dist/core/extensions/loader.js +9 -8
- package/dist/core/extensions/runner.d.ts +3 -1
- package/dist/core/extensions/runner.js +7 -1
- package/dist/core/extensions/types.d.ts +45 -7
- package/dist/core/extensions/wrapper.js +21 -5
- package/dist/core/i18n/slash-commands.d.ts +1 -0
- package/dist/core/i18n/slash-commands.js +1 -0
- package/dist/core/i18n/slash-commands.zh.d.ts +1 -0
- package/dist/core/i18n/slash-commands.zh.js +1 -0
- package/dist/core/keybindings.d.ts +2 -1
- package/dist/core/keybindings.js +3 -3
- package/dist/core/mcp/mcp-client.d.ts +6 -1
- package/dist/core/mcp/mcp-client.js +10 -3
- package/dist/core/mcp/mcp-config.d.ts +21 -10
- package/dist/core/mcp/mcp-config.js +41 -28
- package/dist/core/messages.js +1 -1
- package/dist/core/model-registry.d.ts +4 -0
- package/dist/core/model-registry.js +20 -0
- package/dist/core/persona/persona-manager.d.ts +19 -0
- package/dist/core/persona/persona-manager.js +104 -60
- package/dist/core/runtime/agent-session.d.ts +17 -8
- package/dist/core/runtime/agent-session.js +53 -229
- package/dist/core/runtime/default-tools.d.ts +9 -0
- package/dist/core/runtime/default-tools.js +10 -0
- package/dist/core/runtime/extension-core-bindings.d.ts +45 -0
- package/dist/core/runtime/extension-core-bindings.js +177 -0
- package/dist/core/runtime/sdk.d.ts +4 -1
- package/dist/core/runtime/sdk.js +14 -12
- package/dist/core/runtime/slash-command-catalog.d.ts +26 -0
- package/dist/core/runtime/slash-command-catalog.js +75 -0
- package/dist/core/session/session-manager.d.ts +22 -11
- package/dist/core/session/session-manager.js +48 -30
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/soul-integration.d.ts +5 -2
- package/dist/core/soul-integration.js +10 -8
- package/dist/core/sub-agent/sub-agent-backend.js +4 -2
- package/dist/core/tools/bash.js +3 -1
- package/dist/core/tools/find.d.ts +2 -2
- package/dist/core/tools/find.js +4 -1
- package/dist/core/tools/grep.d.ts +4 -4
- package/dist/core/tools/grep.js +8 -4
- package/dist/core/tools/index.d.ts +7 -6
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/input-validation.d.ts +13 -0
- package/dist/core/tools/input-validation.js +14 -0
- package/dist/core/tools/ls.d.ts +2 -2
- package/dist/core/tools/ls.js +10 -2
- package/dist/core/tools/read.d.ts +4 -4
- package/dist/core/tools/read.js +6 -2
- package/dist/core/tools/time.js +1 -0
- package/dist/core/tools/write-guard.d.ts +2 -0
- package/dist/core/tools/write-guard.js +26 -0
- package/dist/core/utils/logger.js +2 -2
- package/dist/core/workspace/worktree-manager.js +8 -1
- package/dist/extensions/defaults/CLAUDE.md +6 -0
- package/dist/extensions/defaults/loop/cron/cron-scheduler.d.ts +2 -0
- package/dist/extensions/defaults/loop/cron/cron-scheduler.js +2 -2
- package/dist/extensions/defaults/loop/cron/cron-tasks.d.ts +5 -2
- package/dist/extensions/defaults/loop/cron/cron-tasks.js +48 -2
- package/dist/extensions/defaults/loop/cron-tools/cron-create-tool.js +1 -1
- package/dist/extensions/defaults/loop/cron-tools/cron-delete-tool.js +1 -1
- package/dist/extensions/defaults/loop/cron-tools/cron-list-tool.js +1 -1
- package/dist/extensions/defaults/loop/index.js +23 -22
- package/dist/extensions/defaults/recap/CLAUDE.md +15 -0
- package/dist/extensions/defaults/recap/index.d.ts +8 -0
- package/dist/extensions/defaults/recap/index.js +99 -0
- package/dist/extensions/defaults/recap/recap-budget.d.ts +24 -0
- package/dist/extensions/defaults/recap/recap-budget.js +21 -0
- package/dist/extensions/defaults/recap/recap-extractor.d.ts +34 -0
- package/dist/extensions/defaults/recap/recap-extractor.js +128 -0
- package/dist/extensions/defaults/recap/recap-renderer.d.ts +19 -0
- package/dist/extensions/defaults/recap/recap-renderer.js +55 -0
- package/dist/extensions/defaults/recap/recap-synthesizer.d.ts +51 -0
- package/dist/extensions/defaults/recap/recap-synthesizer.js +113 -0
- package/dist/extensions/defaults/recap/recap-types.d.ts +40 -0
- package/dist/extensions/defaults/recap/recap-types.js +16 -0
- package/dist/extensions/defaults/sal/eval/types.d.ts +1 -1
- package/dist/extensions/defaults/sal/index.js +4 -1
- package/dist/extensions/defaults/security-audit/engine/logger.d.ts +19 -1
- package/dist/extensions/defaults/security-audit/engine/logger.js +44 -46
- package/dist/extensions/defaults/security-audit/index.js +84 -200
- package/dist/extensions/defaults/subagent/subagent-runner.d.ts +2 -1
- package/dist/extensions/defaults/token-save/README.md +56 -0
- package/dist/extensions/defaults/token-save/config.d.ts +8 -0
- package/dist/extensions/defaults/token-save/config.js +66 -0
- package/dist/extensions/defaults/token-save/filters.d.ts +14 -0
- package/dist/extensions/defaults/token-save/filters.js +279 -0
- package/dist/extensions/defaults/token-save/index.d.ts +8 -0
- package/dist/extensions/defaults/token-save/index.js +169 -0
- package/dist/extensions/defaults/token-save/lexer.d.ts +11 -0
- package/dist/extensions/defaults/token-save/lexer.js +51 -0
- package/dist/extensions/defaults/token-save/recovery.d.ts +1 -0
- package/dist/extensions/defaults/token-save/recovery.js +21 -0
- package/dist/extensions/defaults/token-save/rewrite.d.ts +19 -0
- package/dist/extensions/defaults/token-save/rewrite.js +73 -0
- package/dist/extensions/defaults/token-save/runner.d.ts +14 -0
- package/dist/extensions/defaults/token-save/runner.js +55 -0
- package/dist/extensions/defaults/token-save/stream.d.ts +20 -0
- package/dist/extensions/defaults/token-save/stream.js +33 -0
- package/dist/extensions/defaults/token-save/toml-dsl.d.ts +25 -0
- package/dist/extensions/defaults/token-save/toml-dsl.js +34 -0
- package/dist/extensions/defaults/token-save/tracking.d.ts +24 -0
- package/dist/extensions/defaults/token-save/tracking.js +74 -0
- package/dist/main.js +94 -45
- package/dist/migrations.d.ts +4 -3
- package/dist/migrations.js +13 -15
- package/dist/modes/acp/acp-mode.js +21 -0
- package/dist/modes/interactive/components/config-selector.js +5 -5
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +41 -64
- package/dist/modes/rpc/rpc-client.d.ts +7 -1
- package/dist/modes/rpc/rpc-client.js +6 -0
- package/dist/modes/rpc/rpc-mode.js +8 -0
- package/dist/modes/rpc/rpc-types.d.ts +13 -1
- package/dist/nanopencil-defaults.d.ts +2 -2
- package/dist/nanopencil-defaults.js +4 -6
- package/dist/node_modules/@pencil-agent/agent-core/agent.d.ts +15 -2
- package/dist/node_modules/@pencil-agent/agent-core/agent.js +23 -4
- package/dist/node_modules/@pencil-agent/agent-core/index.d.ts +3 -1
- package/dist/node_modules/@pencil-agent/agent-core/index.js +3 -1
- package/dist/node_modules/@pencil-agent/agent-core/proxy.js +13 -6
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-agent-loop.d.ts +15 -0
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-agent-loop.js +409 -0
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-tool-orchestration.d.ts +25 -0
- package/dist/node_modules/@pencil-agent/agent-core/structured-adaptive-tool-orchestration.js +294 -0
- package/dist/node_modules/@pencil-agent/agent-core/types.d.ts +100 -2
- package/dist/node_modules/@pencil-agent/agent-core/types.js +8 -2
- package/dist/node_modules/@pencil-agent/ai/models.d.ts +5 -3
- package/dist/node_modules/@pencil-agent/ai/models.generated.d.ts +1534 -2556
- package/dist/node_modules/@pencil-agent/ai/models.generated.js +552 -1544
- package/dist/node_modules/@pencil-agent/ai/stream.js +17 -4
- package/dist/node_modules/@pencil-agent/ai/types.d.ts +8 -1
- package/dist/node_modules/@pencil-agent/ai/types.js +1 -1
- package/dist/node_modules/@pencil-agent/ai/utils/typebox-helpers.js +1 -1
- package/dist/node_modules/@pencil-agent/ai/utils/validation.js +2 -1
- package/dist/packages/mem-core/engine-scoring-v2.js +3 -3
- package/dist/packages/soul-core/evolution.js +12 -3
- package/dist/packages/soul-core/src/evolution.js +12 -3
- package/docs/Recap/346/211/251/345/261/225.md +410 -0
- package/docs/agent-loop-frameworks.md +96 -0
- package/docs/multi-agent-fs-design.md +247 -36
- package/docs/pencil-platform-charter.md +457 -0
- package/docs/remote-tool-register-design.md +436 -0
- package/docs//346/226/207/346/241/243/344/270/255/345/277/203.md +7 -1
- package/package.json +8 -3
- package/docs/Insight /346/264/236/345/257/237/346/212/245/345/221/212.md" +0 -352
- package/docs/SAL/345/256/236/351/252/214/350/257/204/344/274/260/346/226/271/345/274/217/357/274/210/344/273/243/347/240/201/345/257/271/346/257/224/344/270/216/345/244/232worktree/357/274/211.md +0 -158
- package/docs/SAL/346/200/273/344/275/223/350/267/257/347/272/277/344/270/216/345/256/236/351/252/214/345/244/247/347/272/262.md +0 -213
- package/docs//350/256/244/347/237/245/345/234/260/345/233/276/346/236/266/346/236/204/350/215/211/346/241/210.md +0 -600
package/README.md
CHANGED
|
@@ -127,7 +127,7 @@ nanopencil
|
|
|
127
127
|
```
|
|
128
128
|
|
|
129
129
|
1. **Select your model** — Choose from available providers
|
|
130
|
-
2. **Enter API key** — Securely stored in `~/.
|
|
130
|
+
2. **Enter API key** — Securely stored in `~/.pencils/agents/default/auth.json`
|
|
131
131
|
3. **Start coding** — Just type what you want to build
|
|
132
132
|
|
|
133
133
|
### Example Session
|
package/dist/build-meta.json
CHANGED
|
@@ -23,11 +23,13 @@ const BUNDLED_LOOP_EXTENSION = join(__dirname, "extensions", "defaults", "loop",
|
|
|
23
23
|
const BUNDLED_PLAN_EXTENSION = join(__dirname, "extensions", "defaults", "plan", "index.js");
|
|
24
24
|
const BUNDLED_DIAGNOSTICS_EXTENSION = join(__dirname, "extensions", "defaults", "diagnostics", "index.js");
|
|
25
25
|
const BUNDLED_SAL_EXTENSION = join(__dirname, "extensions", "defaults", "sal", "index.js");
|
|
26
|
+
const BUNDLED_TOKEN_SAVE_EXTENSION = join(__dirname, "extensions", "defaults", "token-save", "index.js");
|
|
26
27
|
const BUNDLED_GRUB_EXTENSION = join(__dirname, "extensions", "defaults", "grub", "index.js");
|
|
27
28
|
const BUNDLED_SUBAGENT_EXTENSION = join(__dirname, "extensions", "defaults", "subagent", "index.js");
|
|
28
29
|
const BUNDLED_TEAM_EXTENSION = join(__dirname, "extensions", "defaults", "team", "index.js");
|
|
29
30
|
const BUNDLED_IDLE_THINK_EXTENSION = join(__dirname, "extensions", "defaults", "idle-think", "index.js");
|
|
30
31
|
const BUNDLED_BTW_EXTENSION = join(__dirname, "extensions", "defaults", "btw", "index.js");
|
|
32
|
+
const BUNDLED_RECAP_EXTENSION = join(__dirname, "extensions", "defaults", "recap", "index.js");
|
|
31
33
|
const BUNDLED_DEBUG_EXTENSION = join(__dirname, "extensions", "defaults", "debug", "index.js");
|
|
32
34
|
const BUNDLED_MCP_EXTENSION = join(__dirname, "extensions", "defaults", "mcp", "index.js");
|
|
33
35
|
const BUNDLED_EXPORT_HTML_EXTENSION = join(__dirname, "extensions", "optional", "export-html", "index.js");
|
|
@@ -95,6 +97,15 @@ export function getBuiltinExtensionPaths() {
|
|
|
95
97
|
if (existsSync(salTs))
|
|
96
98
|
paths.push(salTs);
|
|
97
99
|
}
|
|
100
|
+
// === TokenSave extension (default-on command output filtering and savings analytics) ===
|
|
101
|
+
if (existsSync(BUNDLED_TOKEN_SAVE_EXTENSION)) {
|
|
102
|
+
paths.push(BUNDLED_TOKEN_SAVE_EXTENSION);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const tokenSaveTs = join(__dirname, "extensions", "defaults", "token-save", "index.ts");
|
|
106
|
+
if (existsSync(tokenSaveTs))
|
|
107
|
+
paths.push(tokenSaveTs);
|
|
108
|
+
}
|
|
98
109
|
// === NanoMem extension ===
|
|
99
110
|
// 1) Prefer extension bundled to dist/packages during build
|
|
100
111
|
if (existsSync(BUNDLED_NANOMEM_EXTENSION_PACKAGES)) {
|
|
@@ -252,6 +263,15 @@ export function getBuiltinExtensionPaths() {
|
|
|
252
263
|
if (existsSync(btwTs))
|
|
253
264
|
paths.push(btwTs);
|
|
254
265
|
}
|
|
266
|
+
// === Recap extension (on-demand ※ recap situational summaries) ===
|
|
267
|
+
if (existsSync(BUNDLED_RECAP_EXTENSION)) {
|
|
268
|
+
paths.push(BUNDLED_RECAP_EXTENSION);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
const recapTs = join(__dirname, "extensions", "defaults", "recap", "index.ts");
|
|
272
|
+
if (existsSync(recapTs))
|
|
273
|
+
paths.push(recapTs);
|
|
274
|
+
}
|
|
255
275
|
// === Debug extension (system diagnostics with three-layer analysis) ===
|
|
256
276
|
if (existsSync(BUNDLED_DEBUG_EXTENSION)) {
|
|
257
277
|
paths.push(BUNDLED_DEBUG_EXTENSION);
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface Args {
|
|
|
45
45
|
disableSoul?: boolean;
|
|
46
46
|
/** Enable ACP (Agent Client Protocol) mode for editor integration */
|
|
47
47
|
acp?: boolean;
|
|
48
|
+
/** Multi-Agent: ID of the agent to use. Default: "default" */
|
|
49
|
+
agent?: string;
|
|
48
50
|
messages: string[];
|
|
49
51
|
fileArgs: string[];
|
|
50
52
|
/** Unknown flags (potentially extension flags) - map of flag name to value */
|
package/dist/cli/args.js
CHANGED
|
@@ -144,6 +144,9 @@ export function parseArgs(args, extensionFlags) {
|
|
|
144
144
|
else if (arg === "--acp") {
|
|
145
145
|
result.acp = true;
|
|
146
146
|
}
|
|
147
|
+
else if (arg === "--agent" && i + 1 < args.length) {
|
|
148
|
+
result.agent = args[++i];
|
|
149
|
+
}
|
|
147
150
|
else if (arg.startsWith("@")) {
|
|
148
151
|
result.fileArgs.push(arg.slice(1)); // Remove @ prefix
|
|
149
152
|
}
|
|
@@ -179,7 +182,8 @@ ${chalk.bold("Commands:")}
|
|
|
179
182
|
${APP_NAME} update [source] Update installed extensions (skips pinned sources)
|
|
180
183
|
${APP_NAME} list List installed extensions from settings
|
|
181
184
|
${APP_NAME} config Open TUI to enable/disable package resources
|
|
182
|
-
${APP_NAME}
|
|
185
|
+
${APP_NAME} migrate Migrate data from legacy ~/.nanopencil to ~/.pencils
|
|
186
|
+
${APP_NAME} <command> --help Show help for commands
|
|
183
187
|
|
|
184
188
|
${chalk.bold("Options:")}
|
|
185
189
|
--provider <name> Provider name (default: google)
|
|
@@ -216,6 +220,7 @@ ${chalk.bold("Options:")}
|
|
|
216
220
|
--disable-soul Disable Soul (AI personality evolution)
|
|
217
221
|
--no-mcp Disable MCP (Model Context Protocol) tools
|
|
218
222
|
--acp Run as ACP Agent (for editor integration)
|
|
223
|
+
--agent <id> Multi-Agent: Select a specific agent by ID (default: "default")
|
|
219
224
|
--help, -h Show this help
|
|
220
225
|
--version, -v Show version number
|
|
221
226
|
|
package/dist/config.d.ts
CHANGED
|
@@ -50,7 +50,7 @@ export declare const PACKAGE_NAME: string;
|
|
|
50
50
|
export declare const ENV_AGENT_DIR: string;
|
|
51
51
|
/** Get the share viewer URL for a gist ID */
|
|
52
52
|
export declare function getShareViewerUrl(gistId: string): string;
|
|
53
|
-
/** Get the agent config directory (e.g., ~/.
|
|
53
|
+
/** Get the agent config directory (e.g., ~/.pencils/agents/default/) */
|
|
54
54
|
export declare function getAgentDir(): string;
|
|
55
55
|
/** Get path to user's custom themes directory */
|
|
56
56
|
export declare function getCustomThemesDir(): string;
|
|
@@ -72,7 +72,30 @@ export declare function getSessionsDir(): string;
|
|
|
72
72
|
export declare function getDebugLogPath(): string;
|
|
73
73
|
/** Get the root nanopencil config directory (e.g., ~/.nanopencil/) */
|
|
74
74
|
export declare function getConfigRoot(): string;
|
|
75
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the Pencils ecosystem root directory.
|
|
77
|
+
* Priority: PENCILS_HOME > NANOPENCIL_HOME > ~/.pencils
|
|
78
|
+
*
|
|
79
|
+
* Design doc §3: only PENCILS_HOME is the canonical name;
|
|
80
|
+
* NANOPENCIL_HOME is a compat alias for existing users.
|
|
81
|
+
*/
|
|
82
|
+
export declare function getPencilsHome(): string;
|
|
83
|
+
/** New Pencils agents root (e.g., ~/.pencils/agents/) */
|
|
84
|
+
export declare function getPencilsAgentsDir(): string;
|
|
85
|
+
/**
|
|
86
|
+
* Resolve an agent directory context for a given agent id.
|
|
87
|
+
* If no id is provided, returns the legacy single-agent context.
|
|
88
|
+
*
|
|
89
|
+
* Resolution order for per-agent path:
|
|
90
|
+
* 1. NANOPENCIL_CODING_AGENT_DIR env (legacy single-agent override)
|
|
91
|
+
* 2. PENCILS_AGENTS_DIR/<id> (new multi-agent path)
|
|
92
|
+
* 3. ~/.pencils/agents/<id> (default)
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolveAgentDirContext(agentId?: string): {
|
|
95
|
+
id: string;
|
|
96
|
+
path: string;
|
|
97
|
+
};
|
|
98
|
+
/** Get path to global browser-workspace directory (e.g., ~/.pencils/workspaces/browser-workspace) */
|
|
76
99
|
export declare function getBrowserWorkspaceDir(): string;
|
|
77
|
-
/** Get path to global link-world-workspace directory (e.g., ~/.
|
|
100
|
+
/** Get path to global link-world-workspace directory (e.g., ~/.pencils/workspaces/link-world-workspace) */
|
|
78
101
|
export declare function getLinkWorldWorkspaceDir(): string;
|
package/dist/config.js
CHANGED
|
@@ -168,20 +168,11 @@ export function getShareViewerUrl(gistId) {
|
|
|
168
168
|
return `${baseUrl}#${gistId}`;
|
|
169
169
|
}
|
|
170
170
|
// =============================================================================
|
|
171
|
-
// User Config Paths (~/.
|
|
171
|
+
// User Config Paths (~/.pencils/agents/default/*)
|
|
172
172
|
// =============================================================================
|
|
173
|
-
/** Get the agent config directory (e.g., ~/.
|
|
173
|
+
/** Get the agent config directory (e.g., ~/.pencils/agents/default/) */
|
|
174
174
|
export function getAgentDir() {
|
|
175
|
-
|
|
176
|
-
if (envDir) {
|
|
177
|
-
// Expand tilde to home directory
|
|
178
|
-
if (envDir === "~")
|
|
179
|
-
return homedir();
|
|
180
|
-
if (envDir.startsWith("~/"))
|
|
181
|
-
return homedir() + envDir.slice(1);
|
|
182
|
-
return envDir;
|
|
183
|
-
}
|
|
184
|
-
return join(homedir(), CONFIG_DIR_NAME, "agent");
|
|
175
|
+
return resolveAgentDirContext().path;
|
|
185
176
|
}
|
|
186
177
|
/** Get path to user's custom themes directory */
|
|
187
178
|
export function getCustomThemesDir() {
|
|
@@ -240,11 +231,96 @@ export function getConfigRoot() {
|
|
|
240
231
|
}
|
|
241
232
|
return join(homedir(), CONFIG_DIR_NAME);
|
|
242
233
|
}
|
|
243
|
-
|
|
234
|
+
// =============================================================================
|
|
235
|
+
// Multi-Agent: PENCILS_HOME & AgentDirContext support (N2)
|
|
236
|
+
// =============================================================================
|
|
237
|
+
/**
|
|
238
|
+
* Regex for a valid agent <id>.
|
|
239
|
+
* ASCII slug: lowercase alphanumeric start, then [a-z0-9._-], max 64 chars.
|
|
240
|
+
* Design doc §4.1.
|
|
241
|
+
*/
|
|
242
|
+
const AGENT_ID_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
243
|
+
/**
|
|
244
|
+
* Validate an agent id. Returns the id if valid, throws otherwise.
|
|
245
|
+
*/
|
|
246
|
+
function validateAgentId(id) {
|
|
247
|
+
if (!AGENT_ID_RE.test(id)) {
|
|
248
|
+
throw new Error(`Invalid agent id "${id}". Must match ${AGENT_ID_RE.source} (lowercase ASCII slug, max 64 chars).`);
|
|
249
|
+
}
|
|
250
|
+
return id;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Resolve the Pencils ecosystem root directory.
|
|
254
|
+
* Priority: PENCILS_HOME > NANOPENCIL_HOME > ~/.pencils
|
|
255
|
+
*
|
|
256
|
+
* Design doc §3: only PENCILS_HOME is the canonical name;
|
|
257
|
+
* NANOPENCIL_HOME is a compat alias for existing users.
|
|
258
|
+
*/
|
|
259
|
+
export function getPencilsHome() {
|
|
260
|
+
const envPencils = process.env.PENCILS_HOME;
|
|
261
|
+
if (envPencils) {
|
|
262
|
+
if (envPencils === "~")
|
|
263
|
+
return homedir();
|
|
264
|
+
if (envPencils.startsWith("~/"))
|
|
265
|
+
return homedir() + envPencils.slice(1);
|
|
266
|
+
return envPencils;
|
|
267
|
+
}
|
|
268
|
+
// Compat alias
|
|
269
|
+
const envNano = process.env.NANOPENCIL_HOME;
|
|
270
|
+
if (envNano) {
|
|
271
|
+
if (envNano === "~")
|
|
272
|
+
return homedir();
|
|
273
|
+
if (envNano.startsWith("~/"))
|
|
274
|
+
return homedir() + envNano.slice(1);
|
|
275
|
+
return envNano;
|
|
276
|
+
}
|
|
277
|
+
// Default: ~/.pencils (future target; for now fallback to legacy behavior)
|
|
278
|
+
return join(homedir(), ".pencils");
|
|
279
|
+
}
|
|
280
|
+
/** New Pencils agents root (e.g., ~/.pencils/agents/) */
|
|
281
|
+
export function getPencilsAgentsDir() {
|
|
282
|
+
const envAgents = process.env.PENCILS_AGENTS_DIR;
|
|
283
|
+
if (envAgents) {
|
|
284
|
+
if (envAgents === "~")
|
|
285
|
+
return homedir();
|
|
286
|
+
if (envAgents.startsWith("~/"))
|
|
287
|
+
return homedir() + envAgents.slice(1);
|
|
288
|
+
return envAgents;
|
|
289
|
+
}
|
|
290
|
+
return join(getPencilsHome(), "agents");
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Resolve an agent directory context for a given agent id.
|
|
294
|
+
* If no id is provided, returns the legacy single-agent context.
|
|
295
|
+
*
|
|
296
|
+
* Resolution order for per-agent path:
|
|
297
|
+
* 1. NANOPENCIL_CODING_AGENT_DIR env (legacy single-agent override)
|
|
298
|
+
* 2. PENCILS_AGENTS_DIR/<id> (new multi-agent path)
|
|
299
|
+
* 3. ~/.pencils/agents/<id> (default)
|
|
300
|
+
*/
|
|
301
|
+
export function resolveAgentDirContext(agentId) {
|
|
302
|
+
const id = agentId || "default";
|
|
303
|
+
validateAgentId(id);
|
|
304
|
+
// 1. Check legacy env override first (single-agent mode)
|
|
305
|
+
const envDir = process.env[ENV_AGENT_DIR];
|
|
306
|
+
if (envDir && id === "default") {
|
|
307
|
+
const resolvedEnv = envDir.startsWith("~/") ? homedir() + envDir.slice(1) : envDir;
|
|
308
|
+
return { id, path: resolvedEnv };
|
|
309
|
+
}
|
|
310
|
+
// 2. Check explicit per-agent env override
|
|
311
|
+
const envAgentsDir = process.env.PENCILS_AGENTS_DIR;
|
|
312
|
+
if (envAgentsDir) {
|
|
313
|
+
const base = envAgentsDir.startsWith("~/") ? homedir() + envAgentsDir.slice(1) : envAgentsDir;
|
|
314
|
+
return { id, path: join(base, id) };
|
|
315
|
+
}
|
|
316
|
+
// 3. Default multi-agent path under PENCILS_HOME
|
|
317
|
+
return { id, path: join(getPencilsHome(), "agents", id) };
|
|
318
|
+
}
|
|
319
|
+
/** Get path to global browser-workspace directory (e.g., ~/.pencils/workspaces/browser-workspace) */
|
|
244
320
|
export function getBrowserWorkspaceDir() {
|
|
245
|
-
return join(getConfigRoot(), "browser-workspace");
|
|
321
|
+
return join(getConfigRoot(), "workspaces", "browser-workspace");
|
|
246
322
|
}
|
|
247
|
-
/** Get path to global link-world-workspace directory (e.g., ~/.
|
|
323
|
+
/** Get path to global link-world-workspace directory (e.g., ~/.pencils/workspaces/link-world-workspace) */
|
|
248
324
|
export function getLinkWorldWorkspaceDir() {
|
|
249
|
-
return join(getConfigRoot(), "link-world-workspace");
|
|
325
|
+
return join(getConfigRoot(), "workspaces", "link-world-workspace");
|
|
250
326
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: AgentDirContext interface, defaultAgentDirContext(), agentDirContextOf(), validateAgentId()
|
|
3
|
+
* [FROM]: Depends on config.ts (getAgentDir)
|
|
4
|
+
* [TO]: Consumed by core/persona, core/session, core/soul-integration, core/mcp, extensions, future --agent flag
|
|
5
|
+
* [HERE]: core/agent-dir/agent-dir-context.ts - multi-agent directory abstraction
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Regex for a valid agent <id>.
|
|
9
|
+
* ASCII slug: lowercase alphanumeric start, then [a-z0-9._-], max 64 chars.
|
|
10
|
+
* Design doc §4.1.
|
|
11
|
+
*/
|
|
12
|
+
export declare const AGENT_ID_RE: RegExp;
|
|
13
|
+
/**
|
|
14
|
+
* Validate an agent id. Returns the id if valid, throws otherwise.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateAgentId(id: string): string;
|
|
17
|
+
export interface AgentOriginMetadata {
|
|
18
|
+
type: "local" | "cloud-adopted" | "imported";
|
|
19
|
+
asgard?: {
|
|
20
|
+
templateId: string;
|
|
21
|
+
templateVersion: string;
|
|
22
|
+
originUrl: string;
|
|
23
|
+
externalId: string;
|
|
24
|
+
lastSyncedAt: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Represents the resolved filesystem context for one agent.
|
|
29
|
+
*
|
|
30
|
+
* - `id` : machine-readable slug (directory name, route key, Asgard externalId)
|
|
31
|
+
* - `path`: absolute path to the agent's data directory
|
|
32
|
+
* - `origin`: optional metadata if adopted from cloud (future, §4.2)
|
|
33
|
+
*/
|
|
34
|
+
export interface AgentDirContext {
|
|
35
|
+
/** Slug id, [a-z0-9._-]{1,64}; matches the directory name. Immutable once created. */
|
|
36
|
+
readonly id: string;
|
|
37
|
+
/** Absolute path; trusted to exist or be creatable. */
|
|
38
|
+
readonly path: string;
|
|
39
|
+
/** Optional — if the agent was adopted from cloud, the origin metadata. */
|
|
40
|
+
readonly origin?: AgentOriginMetadata;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build the default context for the legacy single-agent path.
|
|
44
|
+
* This is the fallback when no `--agent` flag is provided.
|
|
45
|
+
* Resolves to whatever `getAgentDir()` returns today (~/.pencils/agents/default).
|
|
46
|
+
*/
|
|
47
|
+
export declare function defaultAgentDirContext(): AgentDirContext;
|
|
48
|
+
/**
|
|
49
|
+
* Build an AgentDirContext for a specific agent id + resolved path.
|
|
50
|
+
* Throws if the id fails validation.
|
|
51
|
+
*/
|
|
52
|
+
export declare function agentDirContextOf(id: string, path: string, origin?: AgentOriginMetadata): AgentDirContext;
|
|
53
|
+
/**
|
|
54
|
+
* Load AgentDirContext from an agent directory (loads agent.json if it exists).
|
|
55
|
+
*/
|
|
56
|
+
export declare function loadAgentDirContext(id: string): AgentDirContext;
|
|
57
|
+
/**
|
|
58
|
+
* Save AgentDirContext to agent.json.
|
|
59
|
+
*/
|
|
60
|
+
export declare function saveAgentDirContext(ctx: AgentDirContext): void;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: AgentDirContext interface, defaultAgentDirContext(), agentDirContextOf(), validateAgentId()
|
|
3
|
+
* [FROM]: Depends on config.ts (getAgentDir)
|
|
4
|
+
* [TO]: Consumed by core/persona, core/session, core/soul-integration, core/mcp, extensions, future --agent flag
|
|
5
|
+
* [HERE]: core/agent-dir/agent-dir-context.ts - multi-agent directory abstraction
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { getAgentDir, getPencilsAgentsDir } from "../../config.js";
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// ID validation
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
/**
|
|
14
|
+
* Regex for a valid agent <id>.
|
|
15
|
+
* ASCII slug: lowercase alphanumeric start, then [a-z0-9._-], max 64 chars.
|
|
16
|
+
* Design doc §4.1.
|
|
17
|
+
*/
|
|
18
|
+
export const AGENT_ID_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
19
|
+
/**
|
|
20
|
+
* Validate an agent id. Returns the id if valid, throws otherwise.
|
|
21
|
+
*/
|
|
22
|
+
export function validateAgentId(id) {
|
|
23
|
+
if (!AGENT_ID_RE.test(id)) {
|
|
24
|
+
throw new Error(`Invalid agent id "${id}". Must match ${AGENT_ID_RE.source} (lowercase ASCII slug, max 64 chars).`);
|
|
25
|
+
}
|
|
26
|
+
return id;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build the default context for the legacy single-agent path.
|
|
30
|
+
* This is the fallback when no `--agent` flag is provided.
|
|
31
|
+
* Resolves to whatever `getAgentDir()` returns today (~/.pencils/agents/default).
|
|
32
|
+
*/
|
|
33
|
+
export function defaultAgentDirContext() {
|
|
34
|
+
return { id: "default", path: getAgentDir() };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build an AgentDirContext for a specific agent id + resolved path.
|
|
38
|
+
* Throws if the id fails validation.
|
|
39
|
+
*/
|
|
40
|
+
export function agentDirContextOf(id, path, origin) {
|
|
41
|
+
validateAgentId(id);
|
|
42
|
+
return { id, path, origin };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Load AgentDirContext from an agent directory (loads agent.json if it exists).
|
|
46
|
+
*/
|
|
47
|
+
export function loadAgentDirContext(id) {
|
|
48
|
+
const path = join(getPencilsAgentsDir(), id);
|
|
49
|
+
const agentJsonPath = join(path, "agent.json");
|
|
50
|
+
if (existsSync(agentJsonPath)) {
|
|
51
|
+
try {
|
|
52
|
+
const content = readFileSync(agentJsonPath, "utf-8");
|
|
53
|
+
const metadata = JSON.parse(content);
|
|
54
|
+
return {
|
|
55
|
+
id: metadata.id || id,
|
|
56
|
+
path,
|
|
57
|
+
origin: metadata.origin,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// Fallback on parse error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// Default for agents under PENCILS_AGENTS_DIR
|
|
65
|
+
if (id !== "default" || existsSync(path)) {
|
|
66
|
+
return { id, path };
|
|
67
|
+
}
|
|
68
|
+
// Ultimate fallback to legacy default
|
|
69
|
+
return defaultAgentDirContext();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Save AgentDirContext to agent.json.
|
|
73
|
+
*/
|
|
74
|
+
export function saveAgentDirContext(ctx) {
|
|
75
|
+
if (!existsSync(ctx.path)) {
|
|
76
|
+
mkdirSync(ctx.path, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
const agentJsonPath = join(ctx.path, "agent.json");
|
|
79
|
+
const metadata = {
|
|
80
|
+
id: ctx.id,
|
|
81
|
+
origin: ctx.origin,
|
|
82
|
+
};
|
|
83
|
+
writeFileSync(agentJsonPath, JSON.stringify(metadata, null, 2), "utf-8");
|
|
84
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: AgentMetadata interface, loadAgentMetadata(), saveAgentMetadata(), ensureAgentMetadata()
|
|
3
|
+
* [FROM]: Depends on agent-dir-context.ts, node:fs, node:path
|
|
4
|
+
* [TO]: Consumed by main.ts, future Gateway integration
|
|
5
|
+
* [HERE]: core/agent-dir/agent-metadata.ts - agent.json (machine-readable metadata)
|
|
6
|
+
*
|
|
7
|
+
* Design doc: docs/multi-agent-fs-design.md §4.2
|
|
8
|
+
*/
|
|
9
|
+
import type { AgentDirContext } from "./agent-dir-context.js";
|
|
10
|
+
export interface AgentAsgardMetadata {
|
|
11
|
+
templateId: string;
|
|
12
|
+
templateVersion: string;
|
|
13
|
+
originUrl: string;
|
|
14
|
+
externalId: string;
|
|
15
|
+
lastSyncedAt: string;
|
|
16
|
+
}
|
|
17
|
+
export interface AgentMetadata {
|
|
18
|
+
version: string;
|
|
19
|
+
/** Slug id, [a-z0-9._-]{1,64}; matches the directory name. Immutable. */
|
|
20
|
+
id: string;
|
|
21
|
+
/** Human-readable name; can contain Chinese/emoji. */
|
|
22
|
+
displayName: string;
|
|
23
|
+
/** Human-readable description. */
|
|
24
|
+
description?: string;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
updatedAt: string;
|
|
27
|
+
origin: {
|
|
28
|
+
type: "local" | "cloud-adopted" | "imported";
|
|
29
|
+
asgard?: AgentAsgardMetadata;
|
|
30
|
+
};
|
|
31
|
+
tags: string[];
|
|
32
|
+
engine: string;
|
|
33
|
+
extensions: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
export declare const CURRENT_METADATA_VERSION = "1.0.0";
|
|
36
|
+
/**
|
|
37
|
+
* Load agent.json from the agent directory.
|
|
38
|
+
* Returns undefined if the file does not exist or is invalid.
|
|
39
|
+
*/
|
|
40
|
+
export declare function loadAgentMetadata(agentDir: string): AgentMetadata | undefined;
|
|
41
|
+
/**
|
|
42
|
+
* Save agent.json to the agent directory.
|
|
43
|
+
*/
|
|
44
|
+
export declare function saveAgentMetadata(agentDir: string, metadata: AgentMetadata): void;
|
|
45
|
+
/**
|
|
46
|
+
* Ensure agent.json exists. If not, creates a default one based on context.
|
|
47
|
+
*/
|
|
48
|
+
export declare function ensureAgentMetadata(ctx: AgentDirContext): AgentMetadata;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: AgentMetadata interface, loadAgentMetadata(), saveAgentMetadata(), ensureAgentMetadata()
|
|
3
|
+
* [FROM]: Depends on agent-dir-context.ts, node:fs, node:path
|
|
4
|
+
* [TO]: Consumed by main.ts, future Gateway integration
|
|
5
|
+
* [HERE]: core/agent-dir/agent-metadata.ts - agent.json (machine-readable metadata)
|
|
6
|
+
*
|
|
7
|
+
* Design doc: docs/multi-agent-fs-design.md §4.2
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
export const CURRENT_METADATA_VERSION = "1.0.0";
|
|
12
|
+
/**
|
|
13
|
+
* Load agent.json from the agent directory.
|
|
14
|
+
* Returns undefined if the file does not exist or is invalid.
|
|
15
|
+
*/
|
|
16
|
+
export function loadAgentMetadata(agentDir) {
|
|
17
|
+
const path = join(agentDir, "agent.json");
|
|
18
|
+
if (!existsSync(path))
|
|
19
|
+
return undefined;
|
|
20
|
+
try {
|
|
21
|
+
const raw = readFileSync(path, "utf-8");
|
|
22
|
+
return JSON.parse(raw);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Save agent.json to the agent directory.
|
|
30
|
+
*/
|
|
31
|
+
export function saveAgentMetadata(agentDir, metadata) {
|
|
32
|
+
const path = join(agentDir, "agent.json");
|
|
33
|
+
if (!existsSync(agentDir)) {
|
|
34
|
+
mkdirSync(agentDir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
metadata.updatedAt = new Date().toISOString();
|
|
37
|
+
writeFileSync(path, JSON.stringify(metadata, null, 2), "utf-8");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Ensure agent.json exists. If not, creates a default one based on context.
|
|
41
|
+
*/
|
|
42
|
+
export function ensureAgentMetadata(ctx) {
|
|
43
|
+
const existing = loadAgentMetadata(ctx.path);
|
|
44
|
+
if (existing) {
|
|
45
|
+
// Basic migration: ensure ID matches context (id is immutable)
|
|
46
|
+
if (existing.id !== ctx.id) {
|
|
47
|
+
existing.id = ctx.id;
|
|
48
|
+
saveAgentMetadata(ctx.path, existing);
|
|
49
|
+
}
|
|
50
|
+
return existing;
|
|
51
|
+
}
|
|
52
|
+
const now = new Date().toISOString();
|
|
53
|
+
const metadata = {
|
|
54
|
+
version: CURRENT_METADATA_VERSION,
|
|
55
|
+
id: ctx.id,
|
|
56
|
+
displayName: ctx.id === "default" ? "Default Agent" : ctx.id,
|
|
57
|
+
createdAt: now,
|
|
58
|
+
updatedAt: now,
|
|
59
|
+
origin: {
|
|
60
|
+
type: "local",
|
|
61
|
+
},
|
|
62
|
+
tags: [],
|
|
63
|
+
engine: "nano-pencil",
|
|
64
|
+
extensions: {},
|
|
65
|
+
};
|
|
66
|
+
saveAgentMetadata(ctx.path, metadata);
|
|
67
|
+
return metadata;
|
|
68
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [WHO]: MigrationManager class, handleMigration()
|
|
3
|
+
* [FROM]: Depends on node:fs, node:path, node:os, chalk, config.ts
|
|
4
|
+
* [TO]: Consumed by main.ts (migrate command)
|
|
5
|
+
* [HERE]: core/agent-dir/migration-tool.ts - Safe copy-first migration from ~/.nanopencil to ~/.pencils
|
|
6
|
+
*
|
|
7
|
+
* Design doc: docs/multi-agent-fs-design.md §12.2
|
|
8
|
+
*/
|
|
9
|
+
export interface MigrationOptions {
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
apply: boolean;
|
|
12
|
+
copy: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface MigrationTask {
|
|
15
|
+
id: string;
|
|
16
|
+
source: string;
|
|
17
|
+
target: string;
|
|
18
|
+
label: string;
|
|
19
|
+
description: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class MigrationManager {
|
|
22
|
+
private readonly legacyRoot;
|
|
23
|
+
private readonly newRoot;
|
|
24
|
+
private readonly migrationLogPath;
|
|
25
|
+
constructor();
|
|
26
|
+
run(options: MigrationOptions): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Check if any migration is currently needed (legacy data exists and hasn't been migrated).
|
|
29
|
+
*/
|
|
30
|
+
isMigrationNeeded(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Run migration silently (no preview, direct apply) and return the list of migrated labels.
|
|
33
|
+
*/
|
|
34
|
+
runSilent(): string[];
|
|
35
|
+
private plan;
|
|
36
|
+
private execute;
|
|
37
|
+
private isAlreadyApplied;
|
|
38
|
+
private logMigration;
|
|
39
|
+
}
|