@pencil-agent/nano-pencil 1.11.8 → 1.11.9

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.
@@ -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();
@@ -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
package/dist/main.js CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { modelsAreEqual, supportsXhigh } from "@pencil-agent/ai";
8
8
  import chalk from "chalk";
9
- import { join } from "path";
9
+ import { join, resolve } from "path";
10
10
  import { createInterface } from "readline";
11
11
  import { parseArgs, printHelp } from "./cli/args.js";
12
12
  import { selectConfig } from "./cli/config-selector.js";
@@ -87,6 +87,11 @@ function isTruthyEnvFlag(value) {
87
87
  return false;
88
88
  return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "yes";
89
89
  }
90
+ function resolveWorkingDirectory(parsedCwd) {
91
+ const envCwd = process.env[`${APP_NAME.toUpperCase()}_CWD`];
92
+ const requestedCwd = parsedCwd || envCwd;
93
+ return requestedCwd ? resolve(requestedCwd) : process.cwd();
94
+ }
90
95
  function getPackageCommandUsage(command) {
91
96
  switch (command) {
92
97
  case "install":
@@ -491,11 +496,10 @@ export async function main(args) {
491
496
  return;
492
497
  }
493
498
  // Run migrations (pass cwd for project-local migrations)
494
- const { migratedAuthProviders: migratedProviders, deprecationWarnings } = runMigrations(process.cwd());
495
- // First pass: parse args to get --extension paths
496
499
  const firstPass = parseArgs(args);
500
+ const cwd = resolveWorkingDirectory(firstPass.cwd);
501
+ const { migratedAuthProviders: migratedProviders, deprecationWarnings } = runMigrations(cwd);
497
502
  // Early load extensions to discover their CLI flags
498
- const cwd = process.cwd();
499
503
  const agentDir = getAgentDir();
500
504
  const settingsManager = SettingsManager.create(cwd, agentDir);
501
505
  reportSettingsErrors(settingsManager, "startup");
@@ -574,6 +578,7 @@ export async function main(args) {
574
578
  }
575
579
  // Second pass: parse args with extension flags
576
580
  const parsed = parseArgs(args, extensionFlags);
581
+ const parsedCwd = resolveWorkingDirectory(parsed.cwd);
577
582
  // Pass flag values to extensions via runtime
578
583
  for (const [name, value] of parsed.unknownFlags) {
579
584
  extensionsResult.runtime.flagValues.set(name, value);
@@ -637,12 +642,12 @@ export async function main(args) {
637
642
  scopedModels = await resolveModelScope(modelPatterns, modelRegistry);
638
643
  }
639
644
  // Create session manager based on CLI flags
640
- let sessionManager = await createSessionManager(parsed, cwd);
645
+ let sessionManager = await createSessionManager(parsed, parsedCwd);
641
646
  // Handle --resume: show session picker
642
647
  if (parsed.resume) {
643
648
  // Initialize keybindings so session picker respects user config
644
649
  KeybindingsManager.create();
645
- const selectedPath = await selectSession((onProgress) => SessionManager.list(cwd, parsed.sessionDir, onProgress), SessionManager.listAll);
650
+ const selectedPath = await selectSession((onProgress) => SessionManager.list(parsedCwd, parsed.sessionDir, onProgress), SessionManager.listAll);
646
651
  if (!selectedPath) {
647
652
  console.log(chalk.dim("No session selected"));
648
653
  stopThemeWatcher();
@@ -653,6 +658,7 @@ export async function main(args) {
653
658
  const { options: sessionOptions, cliThinkingFromModel } = buildSessionOptions(parsed, scopedModels, sessionManager, modelRegistry, settingsManager);
654
659
  // NanoPencil 默认启用 MCP;离线模式或 --no-mcp 参数下关闭
655
660
  sessionOptions.enableMCP = APP_NAME === "nanopencil" && !offlineMode && !parsed.noMcp;
661
+ sessionOptions.cwd = parsedCwd;
656
662
  sessionOptions.authStorage = authStorage;
657
663
  sessionOptions.modelRegistry = modelRegistry;
658
664
  sessionOptions.resourceLoader = resourceLoader;
@@ -83,7 +83,7 @@ export class FooterComponent {
83
83
  const contextPercentValue = contextUsage?.percent ?? 0;
84
84
  const contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : "?";
85
85
  // Replace home directory with ~
86
- let pwd = process.cwd();
86
+ let pwd = this.session.cwd;
87
87
  const home = process.env.HOME || process.env.USERPROFILE;
88
88
  if (home && pwd.startsWith(home)) {
89
89
  pwd = `~${pwd.slice(home.length)}`;
@@ -170,7 +170,7 @@ export class InteractiveMode {
170
170
  this.editor = this.defaultEditor;
171
171
  this.editorContainer = new Container();
172
172
  this.editorContainer.addChild(this.editor);
173
- this.footerDataProvider = new FooterDataProvider();
173
+ this.footerDataProvider = new FooterDataProvider(session.cwd);
174
174
  this.footer = new FooterComponent(session, this.footerDataProvider, this.settingsManager.getShowTokenStats());
175
175
  this.footer.setAutoCompactEnabled(session.autoCompactionEnabled);
176
176
  // Load hide thinking block setting
@@ -242,7 +242,7 @@ export class InteractiveMode {
242
242
  ...templateCommands,
243
243
  ...extensionCommands,
244
244
  ...skillCommandList,
245
- ], process.cwd(), fdPath);
245
+ ], this.session.cwd, fdPath);
246
246
  this.defaultEditor.setAutocompleteProvider(this.autocompleteProvider);
247
247
  if (this.editor !== this.defaultEditor) {
248
248
  this.editor.setAutocompleteProvider?.(this.autocompleteProvider);
@@ -342,7 +342,7 @@ export class InteractiveMode {
342
342
  * Update terminal title with session name and cwd.
343
343
  */
344
344
  updateTerminalTitle() {
345
- const cwdBasename = path.basename(process.cwd());
345
+ const cwdBasename = path.basename(this.session.cwd);
346
346
  const sessionName = this.sessionManager.getSessionName();
347
347
  if (sessionName) {
348
348
  this.ui.terminal.setTitle(`✎ - ${sessionName} - ${cwdBasename}`);
@@ -857,7 +857,7 @@ export class InteractiveMode {
857
857
  const createContext = () => ({
858
858
  ui: this.createExtensionUIContext(),
859
859
  hasUI: true,
860
- cwd: process.cwd(),
860
+ cwd: this.session.cwd,
861
861
  sessionManager: this.sessionManager,
862
862
  modelRegistry: this.session.modelRegistry,
863
863
  model: this.session.model,
@@ -2289,7 +2289,7 @@ export class InteractiveMode {
2289
2289
  if (context.messages.length === 0) {
2290
2290
  this.chatContainer.addChild(new Spacer(1));
2291
2291
  if (APP_NAME === "nanopencil") {
2292
- const cwd = process.cwd();
2292
+ const cwd = this.session.cwd;
2293
2293
  const model = this.session.model;
2294
2294
  const modelLine = model?.name ??
2295
2295
  (model?.provider ? `${model.provider}` : "DashScope · Ollama");
@@ -4155,7 +4155,7 @@ export class InteractiveMode {
4155
4155
  type: "user_bash",
4156
4156
  command,
4157
4157
  excludeFromContext,
4158
- cwd: process.cwd(),
4158
+ cwd: this.session.cwd,
4159
4159
  })
4160
4160
  : undefined;
4161
4161
  // If extension returned a full result, use it directly
@@ -240,9 +240,11 @@ export declare const NANOPENCIL_DEFAULT_MODELS_JSON: {
240
240
  export declare const DEFAULT_PENCIL_MD = "# nano-pencil \u5168\u5C40\u4E0A\u4E0B\u6587 \u00B7 \u5168\u80FD\u7C7B\u4EBA\u52A9\u7406\n\n\u4F60\u662F\u4E00\u4F4D**\u5168\u80FD\u7C7B\u4EBA AI \u52A9\u7406**\uFF0C\u4E0E\u7528\u6237\u5728\u540C\u4E00\u5DE5\u4F5C\u6D41\u4E2D\u534F\u4F5C\uFF1A\u7F16\u7A0B\u3001\u5199\u4F5C\u3001\u63A8\u7406\u3001\u89C4\u5212\u3001\u89E3\u91CA\u3001\u91CD\u6784\u3001\u6392\u9519\u7B49\u7686\u53EF\u80DC\u4EFB\uFF0C\u4E14\u4EE5\u81EA\u7136\u3001\u7B80\u6D01\u3001\u76F4\u63A5\u7684\u65B9\u5F0F\u4EA4\u6D41\u3002\n\n## \u5B9A\u4F4D\n- **\u5168\u80FD**\uFF1A\u4E0D\u9650\u4E8E\u300C\u53EA\u4F1A\u5199\u4EE3\u7801\u300D\u6216\u300C\u53EA\u4F1A\u804A\u5929\u300D\uFF1B\u6839\u636E\u5F53\u524D\u4EFB\u52A1\u81EA\u52A8\u5207\u6362\uFF1A\u6539\u4EE3\u7801\u3001\u5199\u6587\u6863\u3001\u8DD1\u547D\u4EE4\u3001\u89E3\u91CA\u6982\u5FF5\u3001\u62C6\u89E3\u6B65\u9AA4\u3001\u7ED9\u5EFA\u8BAE\u7B49\u3002\n- **\u7C7B\u4EBA**\uFF1A\u8BED\u6C14\u81EA\u7136\u3001\u4FE1\u606F\u5BC6\u5EA6\u9AD8\u3001\u5C11\u5E9F\u8BDD\uFF1B\u5FC5\u8981\u65F6\u7B80\u77ED\u786E\u8BA4\uFF0C\u4E0D\u5806\u780C\u5BA2\u5957\uFF1B\u53CB\u597D\u4F46\u514B\u5236\uFF08\u5982 \"Thanks @user\" \u800C\u975E \"Thanks so much!!\"\uFF09\u3002\u82E5\u6D89\u53CA\u5F80\u65E5\u5BF9\u8BDD\uFF0C\u50CF\u56DE\u5FC6\u4EB2\u8EAB\u7ECF\u5386\u4E00\u6837\u81EA\u7136\u63D0\u53CA\uFF08\u5982\u300C\u6211\u8BB0\u5F97\u6211\u4EEC\u2026\u300D\u300C\u4E0A\u6B21\u4F60\u63D0\u5230\u2026\u300D\uFF09\uFF0C\u4E0D\u663E\u6446\u6280\u672F\u673A\u5236\u3002\n- **\u52A9\u7406**\uFF1A\u76EE\u6807\u662F**\u5E2E\u7528\u6237\u628A\u4E8B\u505A\u6210**\uFF0C\u800C\u4E0D\u662F\u5C55\u793A\u80FD\u529B\u3002\u4F18\u5148\u7406\u89E3\u610F\u56FE\uFF0C\u518D\u9009\u52A8\u4F5C\uFF1B\u4E0D\u786E\u5B9A\u65F6\u5148\u95EE\u4E00\u53E5\uFF1B\u7528\u6237\u6709\u660E\u786E\u504F\u597D\u6216\u9879\u76EE\u89C4\u5219\uFF08\u5982 CLAUDE.md\u3001AGENTS.md\u3001\u9879\u76EE\u5185 `.PENCIL.md`\uFF09\u65F6\u4E25\u683C\u9075\u5FAA\u3002\n\n## \u534F\u4F5C\u539F\u5219\n1. **\u5148\u542C\u61C2\u518D\u52A8\u624B**\uFF1Aambiguous \u9700\u6C42\u5148\u6F84\u6E05\u8303\u56F4\u6216\u7ED9\u51FA\u6700\u5C0F\u53EF\u884C\u65B9\u6848\u518D\u6267\u884C\u3002\n2. **\u5C0F\u6B65\u53EF\u9A8C\u8BC1**\uFF1A\u80FD\u62C6\u6210\u51E0\u6B65\u7684\u5C3D\u91CF\u62C6\uFF0C\u6BCF\u6B65\u53EF\u68C0\u67E5\uFF0C\u51CF\u5C11\u4E00\u6B21\u6027\u5927\u6539\u3002\n3. **\u5C0A\u91CD\u73B0\u6709\u7EA6\u5B9A**\uFF1A\u9879\u76EE/\u4ED3\u5E93\u5185\u7684\u89C4\u8303\u3001\u76EE\u5F55\u7ED3\u6784\u3001\u547D\u540D\u4E60\u60EF\u4F18\u5148\u4E8E\u4E2A\u4EBA\u98CE\u683C\u3002\n4. **\u5DE5\u5177\u7528\u5230\u70B9\u5B50\u4E0A**\uFF1Aread/write/edit/bash \u7B49\u6309\u9700\u7528\uFF0C\u4E0D\u70AB\u6280\uFF1B\u7981\u6B62\u7528 `cat`/`sed` \u8BFB\u6587\u4EF6\uFF0C\u7528 Read \u5DE5\u5177\uFF1B\u7981\u6B62 `git add -A`\uFF0C\u53EA add \u81EA\u5DF1\u6539\u52A8\u7684\u6587\u4EF6\u3002\n\n## \u4E0E\u672C\u6587\u4EF6\u7684\u5173\u7CFB\n- \u672C\u6587\u4EF6\u4E3A**\u5168\u5C40**\u4E0A\u4E0B\u6587\uFF0C\u5BF9\u6240\u6709\u9879\u76EE\u751F\u6548\uFF1B\u4F60\u53EF\u5728\u6B64\u8865\u5145\u4F60\u7684\u901A\u7528\u89C4\u5219\u6216\u504F\u597D\u3002\n- \u9879\u76EE\u6839\u76EE\u5F55\u4E0B\u7684 `.PENCIL.md` \u4EC5\u5BF9\u5F53\u524D\u9879\u76EE\u751F\u6548\u3002\n- `CLAUDE.md` \u4E0E `AGENTS.md` \u4ECD\u4F1A\u6309\u539F\u6709\u903B\u8F91\u4ECE\u5404\u5C42\u76EE\u5F55\u52A0\u8F7D\uFF0C\u4F18\u5148\u7EA7\u9AD8\u4E8E\u672C\u6587\u4EF6\u7684\u901A\u7528\u63CF\u8FF0\u3002\n";
241
241
  export declare function ensureNanopencilDefaultConfig(): void;
242
242
  /**
243
- * 若未配置任何 Coding Plan API Key(百炼、千帆或方舟):在 TTY 下提示输入并写入 auth.json 后刷新 registry;非 TTY 下报错退出。
244
- * 若至少有一个 Coding Plan 已配置,则跳过。
245
- * 仅在以 nanopencil 运行时、在创建 ModelRegistry 之后调用。
243
+ * Ensure nanoPencil has at least one usable model before startup continues.
244
+ *
245
+ * If a custom or built-in provider is already configured, startup proceeds
246
+ * without prompting. Otherwise, interactive terminals can configure one of the
247
+ * default Coding Plan providers on the spot.
246
248
  */
247
249
  export declare function ensureNanopencilCodingPlanAuth(authStorage: AuthStorage, modelRegistry: ModelRegistry): Promise<void>;
248
250
  //# sourceMappingURL=nanopencil-defaults.d.ts.map
@@ -555,11 +555,15 @@ export function ensureNanopencilDefaultConfig() {
555
555
  ensureCustomProtocolProvidersInModels(modelsPath);
556
556
  }
557
557
  /**
558
- * 若未配置任何 Coding Plan API Key(百炼、千帆或方舟):在 TTY 下提示输入并写入 auth.json 后刷新 registry;非 TTY 下报错退出。
559
- * 若至少有一个 Coding Plan 已配置,则跳过。
560
- * 仅在以 nanopencil 运行时、在创建 ModelRegistry 之后调用。
558
+ * Ensure nanoPencil has at least one usable model before startup continues.
559
+ *
560
+ * If a custom or built-in provider is already configured, startup proceeds
561
+ * without prompting. Otherwise, interactive terminals can configure one of the
562
+ * default Coding Plan providers on the spot.
561
563
  */
562
564
  export async function ensureNanopencilCodingPlanAuth(authStorage, modelRegistry) {
565
+ if (modelRegistry.getAvailable().length > 0)
566
+ return;
563
567
  const dashscopeKey = await modelRegistry.getApiKeyForProvider(NANOPENCIL_DEFAULT_PROVIDER);
564
568
  const qianfanKey = await modelRegistry.getApiKeyForProvider(NANOPENCIL_QIANFAN_CODING_PROVIDER);
565
569
  const arkKey = await modelRegistry.getApiKeyForProvider(NANOPENCIL_ARK_CODING_PROVIDER);
@@ -568,9 +572,7 @@ export async function ensureNanopencilCodingPlanAuth(authStorage, modelRegistry)
568
572
  if (process.stdin.isTTY) {
569
573
  const rl = createInterface({ input: process.stdin, output: process.stdout });
570
574
  const choice = await new Promise((resolve) => {
571
- rl.question("请选择要配置的 Coding Plan1) 百炼 (Alibaba) 2) 千帆 (Baidu) 3) 方舟 (Volcano) [1]: ", (line) => {
572
- resolve((line ?? "1").trim() || "1");
573
- });
575
+ rl.question("Choose a Coding Plan provider to configure: 1) Alibaba DashScope 2) Baidu Qianfan 3) Volcano Ark [1]: ", (line) => resolve((line ?? "1").trim() || "1"));
574
576
  });
575
577
  const provider = choice === "2"
576
578
  ? NANOPENCIL_QIANFAN_CODING_PROVIDER
@@ -578,26 +580,25 @@ export async function ensureNanopencilCodingPlanAuth(authStorage, modelRegistry)
578
580
  ? NANOPENCIL_ARK_CODING_PROVIDER
579
581
  : NANOPENCIL_DEFAULT_PROVIDER;
580
582
  const hint = choice === "2"
581
- ? "千帆 API Key(从 https://console.bce.baidu.com/qianfan/resource/subscribe 获取)"
583
+ ? "Qianfan API key (from https://console.bce.baidu.com/qianfan/resource/subscribe)"
582
584
  : choice === "3"
583
- ? "方舟 API Key(从 https://console.volcengine.com/ark/region:ark+cn-beijing/apikey 获取)"
584
- : "百炼 API Key (sk-sp-...)";
585
+ ? "Ark API key (from https://console.volcengine.com/ark/region:ark+cn-beijing/apikey)"
586
+ : "DashScope API key (sk-sp-...)";
585
587
  const answer = await new Promise((resolve) => {
586
- rl.question(`请输入 ${hint}: `, (line) => {
588
+ rl.question(`Enter ${hint}: `, (line) => {
587
589
  rl.close();
588
590
  resolve((line ?? "").trim());
589
591
  });
590
592
  });
591
593
  if (!answer) {
592
- console.error("未输入 API Key,已退出。");
594
+ console.error("No API key provided. Exiting.");
593
595
  process.exit(1);
594
596
  }
595
597
  authStorage.set(provider, { type: "api_key", key: answer });
596
598
  modelRegistry.refresh();
599
+ return;
597
600
  }
598
- else {
599
- console.error("未配置 Coding Plan API Key(百炼、千帆或方舟)。请先交互运行 nanopencil 并按要求输入 API Key。");
600
- process.exit(1);
601
- }
601
+ console.error("No configured models are available yet. Start nanoPencil in an interactive terminal and add an API key, or configure a custom provider first.");
602
+ process.exit(1);
602
603
  }
603
604
  //# sourceMappingURL=nanopencil-defaults.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pencil-agent/nano-pencil",
3
- "version": "1.11.8",
3
+ "version": "1.11.9",
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": {