@pencil-agent/nano-pencil 1.11.8 → 1.11.10

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.
Files changed (43) hide show
  1. package/dist/builtin-extensions.js +11 -0
  2. package/dist/cli/args.d.ts +1 -0
  3. package/dist/cli/args.js +13 -8
  4. package/dist/core/extensions/loader.js +1 -0
  5. package/dist/core/extensions/types.d.ts +2 -0
  6. package/dist/core/footer-data-provider.d.ts +2 -1
  7. package/dist/core/footer-data-provider.js +7 -5
  8. package/dist/core/runtime/agent-session.d.ts +1 -0
  9. package/dist/core/runtime/agent-session.js +4 -1
  10. package/dist/core/runtime/sdk.js +2 -0
  11. package/dist/extensions/defaults/interview/index.js +25 -0
  12. package/dist/extensions/defaults/mcp/index.js +1 -0
  13. package/dist/extensions/defaults/team/index.d.ts +3 -0
  14. package/dist/extensions/defaults/team/index.js +985 -0
  15. package/dist/extensions/defaults/team/team-controller.d.ts +18 -0
  16. package/dist/extensions/defaults/team/team-controller.js +92 -0
  17. package/dist/extensions/defaults/team/team-parser.d.ts +16 -0
  18. package/dist/extensions/defaults/team/team-parser.js +45 -0
  19. package/dist/extensions/defaults/team/team-types.d.ts +55 -0
  20. package/dist/extensions/defaults/team/team-types.js +2 -0
  21. package/dist/main.js +12 -6
  22. package/dist/modes/interactive/components/footer.js +1 -1
  23. package/dist/modes/interactive/interactive-mode.js +6 -6
  24. package/dist/nanopencil-defaults.d.ts +5 -3
  25. package/dist/nanopencil-defaults.js +16 -15
  26. package/dist/packages/mem-core/config.d.ts +7 -0
  27. package/dist/packages/mem-core/config.js +10 -3
  28. package/dist/packages/mem-core/consolidation.js +48 -1
  29. package/dist/packages/mem-core/engine.d.ts +20 -1
  30. package/dist/packages/mem-core/engine.js +324 -55
  31. package/dist/packages/mem-core/eviction.js +7 -1
  32. package/dist/packages/mem-core/extension.js +191 -1
  33. package/dist/packages/mem-core/extraction.js +35 -1
  34. package/dist/packages/mem-core/i18n.d.ts +3 -0
  35. package/dist/packages/mem-core/i18n.js +8 -3
  36. package/dist/packages/mem-core/index.d.ts +2 -2
  37. package/dist/packages/mem-core/index.js +1 -1
  38. package/dist/packages/mem-core/linking.d.ts +11 -1
  39. package/dist/packages/mem-core/linking.js +151 -0
  40. package/dist/packages/mem-core/scoring.js +15 -1
  41. package/dist/packages/mem-core/types.d.ts +51 -2
  42. package/dist/packages/mem-core/update.js +82 -0
  43. package/package.json +2 -2
@@ -28,6 +28,7 @@ const BUNDLED_SECURITY_AUDIT_EXTENSION = join(__dirname, "extensions", "defaults
28
28
  const BUNDLED_SOUL_EXTENSION = join(__dirname, "extensions", "defaults", "soul", "index.js");
29
29
  const BUNDLED_INTERVIEW_EXTENSION = join(__dirname, "extensions", "defaults", "interview", "index.js");
30
30
  const BUNDLED_LOOP_EXTENSION = join(__dirname, "extensions", "defaults", "loop", "index.js");
31
+ const BUNDLED_TEAM_EXTENSION = join(__dirname, "extensions", "defaults", "team", "index.js");
31
32
  const BUNDLED_MCP_EXTENSION = join(__dirname, "extensions", "defaults", "mcp", "index.js");
32
33
  const BUNDLED_EXPORT_HTML_EXTENSION = join(__dirname, "extensions", "optional", "export-html", "index.js");
33
34
  /** 从当前模块位置向上查找包根(含 package.json 且 name 为 nano-pencil 相关) */
@@ -148,6 +149,16 @@ export function getBuiltinExtensionPaths() {
148
149
  paths.push(loopTs);
149
150
  }
150
151
  // === MCP 扩展(MCP 工具协议适配) ===
152
+ // Built-in team extension
153
+ if (existsSync(BUNDLED_TEAM_EXTENSION)) {
154
+ paths.push(BUNDLED_TEAM_EXTENSION);
155
+ }
156
+ else {
157
+ const teamTs = join(__dirname, "extensions", "defaults", "team", "index.ts");
158
+ if (existsSync(teamTs))
159
+ paths.push(teamTs);
160
+ }
161
+ // Built-in MCP extension
151
162
  if (existsSync(BUNDLED_MCP_EXTENSION)) {
152
163
  paths.push(BUNDLED_MCP_EXTENSION);
153
164
  }
@@ -5,6 +5,7 @@ import type { ThinkingLevel } from "@pencil-agent/agent-core";
5
5
  import { type ToolName } from "../core/tools/index.js";
6
6
  export type Mode = "text" | "json" | "rpc";
7
7
  export interface Args {
8
+ cwd?: string;
8
9
  provider?: string;
9
10
  model?: string;
10
11
  apiKey?: string;
package/dist/cli/args.js CHANGED
@@ -37,6 +37,9 @@ export function parseArgs(args, extensionFlags) {
37
37
  else if (arg === "--provider" && i + 1 < args.length) {
38
38
  result.provider = args[++i];
39
39
  }
40
+ else if (arg === "--cwd" && i + 1 < args.length) {
41
+ result.cwd = args[++i];
42
+ }
40
43
  else if (arg === "--model" && i + 1 < args.length) {
41
44
  result.model = args[++i];
42
45
  }
@@ -182,8 +185,9 @@ ${chalk.bold("Commands:")}
182
185
  ${APP_NAME} <command> --help Show help for install/remove/update/list
183
186
 
184
187
  ${chalk.bold("Options:")}
185
- --provider <name> Provider name (default: google)
186
- --model <pattern> Model pattern or ID (supports "provider/id" and optional ":<thinking>")
188
+ --provider <name> Provider name (default: google)
189
+ --cwd <dir> Working directory to use for project-local discovery
190
+ --model <pattern> Model pattern or ID (supports "provider/id" and optional ":<thinking>")
187
191
  --api-key <key> API key (defaults to env vars)
188
192
  --system-prompt <text> System prompt (default: coding assistant prompt)
189
193
  --append-system-prompt <text> Append text or file contents to the system prompt
@@ -287,12 +291,13 @@ ${chalk.bold("Environment Variables:")}
287
291
  MINIMAX_API_KEY - MiniMax API key
288
292
  KIMI_API_KEY - Kimi For Coding API key
289
293
  AWS_PROFILE - AWS profile for Amazon Bedrock
290
- AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock
291
- AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock
292
- AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)
293
- AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)
294
- ${ENV_AGENT_DIR.padEnd(32)} - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
295
- PI_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)
294
+ AWS_ACCESS_KEY_ID - AWS access key for Amazon Bedrock
295
+ AWS_SECRET_ACCESS_KEY - AWS secret key for Amazon Bedrock
296
+ AWS_BEARER_TOKEN_BEDROCK - Bedrock API key (bearer token)
297
+ AWS_REGION - AWS region for Amazon Bedrock (e.g., us-east-1)
298
+ ${`${APP_NAME.toUpperCase()}_CWD`.padEnd(32)} - Working directory override for project-local discovery
299
+ ${ENV_AGENT_DIR.padEnd(32)} - Session storage directory (default: ~/${CONFIG_DIR_NAME}/agent)
300
+ PI_PACKAGE_DIR - Override package directory (for Nix/Guix store paths)
296
301
  PI_OFFLINE - Disable startup network operations when set to 1/true/yes
297
302
  PI_TOOLS_DOWNLOAD_TIMEOUT_MS - Timeout in ms for downloading fd/ripgrep (default: 60000)
298
303
  PI_SHARE_VIEWER_URL - Base URL for /share command (default: https://pi.dev/session/)
@@ -107,6 +107,7 @@ export function createExtensionRuntime() {
107
107
  */
108
108
  function createExtensionAPI(extension, runtime, cwd, eventBus) {
109
109
  const api = {
110
+ cwd,
110
111
  // Registration methods - write to extension
111
112
  on(event, handler) {
112
113
  const list = extension.handlers.get(event) ?? [];
@@ -649,6 +649,8 @@ export type ExtensionHandler<E, R = undefined> = (event: E, ctx: ExtensionContex
649
649
  * ExtensionAPI passed to extension factory functions.
650
650
  */
651
651
  export interface ExtensionAPI {
652
+ /** Working directory resolved for this extension load */
653
+ cwd: string;
652
654
  on(event: "resources_discover", handler: ExtensionHandler<ResourcesDiscoverEvent, ResourcesDiscoverResult>): void;
653
655
  on(event: "session_start", handler: ExtensionHandler<SessionStartEvent>): void;
654
656
  on(event: "session_before_switch", handler: ExtensionHandler<SessionBeforeSwitchEvent, SessionBeforeSwitchResult>): void;
@@ -8,7 +8,8 @@ export declare class FooterDataProvider {
8
8
  private gitWatcher;
9
9
  private branchChangeCallbacks;
10
10
  private availableProviderCount;
11
- constructor();
11
+ private cwd;
12
+ constructor(cwd: string);
12
13
  /** Current git branch, null if not in repo, "detached" if detached HEAD */
13
14
  getGitBranch(): string | null;
14
15
  /** Extension status texts set via ctx.ui.setStatus() */
@@ -4,8 +4,8 @@ import { dirname, join, resolve } from "path";
4
4
  * Find the git HEAD path by walking up from cwd.
5
5
  * Handles both regular git repos (.git is a directory) and worktrees (.git is a file).
6
6
  */
7
- function findGitHeadPath() {
8
- let dir = process.cwd();
7
+ function findGitHeadPath(cwd) {
8
+ let dir = cwd;
9
9
  while (true) {
10
10
  const gitPath = join(dir, ".git");
11
11
  if (existsSync(gitPath)) {
@@ -46,7 +46,9 @@ export class FooterDataProvider {
46
46
  gitWatcher = null;
47
47
  branchChangeCallbacks = new Set();
48
48
  availableProviderCount = 0;
49
- constructor() {
49
+ cwd;
50
+ constructor(cwd) {
51
+ this.cwd = cwd;
50
52
  this.setupGitWatcher();
51
53
  }
52
54
  /** Current git branch, null if not in repo, "detached" if detached HEAD */
@@ -54,7 +56,7 @@ export class FooterDataProvider {
54
56
  if (this.cachedBranch !== undefined)
55
57
  return this.cachedBranch;
56
58
  try {
57
- const gitHeadPath = findGitHeadPath();
59
+ const gitHeadPath = findGitHeadPath(this.cwd);
58
60
  if (!gitHeadPath) {
59
61
  this.cachedBranch = null;
60
62
  return null;
@@ -110,7 +112,7 @@ export class FooterDataProvider {
110
112
  this.gitWatcher.close();
111
113
  this.gitWatcher = null;
112
114
  }
113
- const gitHeadPath = findGitHeadPath();
115
+ const gitHeadPath = findGitHeadPath(this.cwd);
114
116
  if (!gitHeadPath)
115
117
  return;
116
118
  // Watch the directory containing HEAD, not HEAD itself.
@@ -210,6 +210,7 @@ export declare class AgentSession {
210
210
  get compactionCoordinator(): CompactionCoordinator | undefined;
211
211
  /** Model registry for API key resolution and model discovery */
212
212
  get modelRegistry(): ModelRegistry;
213
+ get cwd(): string;
213
214
  /** Emit an event to all listeners */
214
215
  private _emit;
215
216
  private _lastAssistantMessage;
@@ -216,6 +216,9 @@ export class AgentSession {
216
216
  get modelRegistry() {
217
217
  return this._modelRegistry;
218
218
  }
219
+ get cwd() {
220
+ return this._cwd;
221
+ }
219
222
  // =========================================================================
220
223
  // Event Subscription
221
224
  // =========================================================================
@@ -2048,7 +2051,7 @@ export class AgentSession {
2048
2051
  const resolvedCommand = prefix ? `${prefix}\n${command}` : command;
2049
2052
  try {
2050
2053
  const result = options?.operations
2051
- ? await executeBashWithOperations(resolvedCommand, process.cwd(), options.operations, {
2054
+ ? await executeBashWithOperations(resolvedCommand, this._cwd, options.operations, {
2052
2055
  onChunk,
2053
2056
  signal: this._bashAbortController.signal,
2054
2057
  })
@@ -240,6 +240,7 @@ export async function createAgentSession(options = {}) {
240
240
  if (options.enableMCP) {
241
241
  try {
242
242
  currentMcpManager = new MCPManager();
243
+ currentMcpManager.setWorkingDir(cwd);
243
244
  await currentMcpManager.initialize();
244
245
  initialMcpTools = [...currentMcpManager.getTools()];
245
246
  time("mcp.initialize");
@@ -282,6 +283,7 @@ export async function createAgentSession(options = {}) {
282
283
  // ignore
283
284
  }
284
285
  currentMcpManager = new MCPManager();
286
+ currentMcpManager.setWorkingDir(cwd);
285
287
  await currentMcpManager.initialize();
286
288
  time("mcp.initialize");
287
289
  return currentMcpManager.getTools();
@@ -61,6 +61,15 @@ function truncateForPrompt(str, maxChars) {
61
61
  return str;
62
62
  return str.slice(0, maxChars);
63
63
  }
64
+ const LOOP_PROMPT_PREFIX = "[LOOP:";
65
+ function isLoopManagedPrompt(prompt) {
66
+ const trimmed = prompt.trim();
67
+ if (!trimmed)
68
+ return false;
69
+ return (trimmed.startsWith(LOOP_PROMPT_PREFIX) ||
70
+ trimmed.includes("Autonomous loop goal:") ||
71
+ trimmed.includes("You are inside a managed loop."));
72
+ }
64
73
  /**
65
74
  * Check if interview should be triggered based on prompt content and context.
66
75
  * Only triggers for:
@@ -121,6 +130,10 @@ function shouldRunInterview(prompt) {
121
130
  if (process.env.PI_JUST_SWITCHED_PERSONA === "true") {
122
131
  return false;
123
132
  }
133
+ // Loop-generated follow-up turns should stay autonomous and never re-open interview.
134
+ if (isLoopManagedPrompt(prompt)) {
135
+ return false;
136
+ }
124
137
  // Avoid triggering interview for greetings/small-talk/memory questions.
125
138
  if (isNonTaskPrompt(prompt))
126
139
  return false;
@@ -335,6 +348,18 @@ export default async function interviewExtension(pi) {
335
348
  // Start from provided answers (if any) then allow extra clarifications.
336
349
  const referenceSystemPrompt = ctx.getSystemPrompt();
337
350
  let answers = { ...answersFromModel };
351
+ if (isLoopManagedPrompt(query)) {
352
+ return {
353
+ content: [{ type: "text", text: query }],
354
+ details: {
355
+ original_intent: query,
356
+ refined_intent: query,
357
+ completion_score: 1,
358
+ answers,
359
+ missing_slots: [],
360
+ },
361
+ };
362
+ }
338
363
  // UI mode guard: prevent the model from triggering interactive interview for non-task prompts.
339
364
  if (ctx.hasUI && !isTaskLikePrompt(query)) {
340
365
  return {
@@ -19,6 +19,7 @@ export default async function mcpExtension(pi) {
19
19
  console.log("[mcp] Initializing MCP...");
20
20
  try {
21
21
  mcpManager = new MCPManager();
22
+ mcpManager.setWorkingDir(pi.cwd);
22
23
  await mcpManager.initialize();
23
24
  const mcpTools = mcpManager.getTools();
24
25
  // Register MCP tools
@@ -0,0 +1,3 @@
1
+ import type { ExtensionAPI } from "../../../core/extensions/types.js";
2
+ export default function teamExtension(pi: ExtensionAPI): Promise<void>;
3
+ //# sourceMappingURL=index.d.ts.map