@psnext/slingcli 2.5.20260602-4 → 2.5.20260607-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/node_modules/@aws-sdk/core/dist-cjs/index.js +12 -6
  2. package/node_modules/@aws-sdk/core/dist-cjs/submodules/httpAuthSchemes/index.js +12 -6
  3. package/node_modules/@aws-sdk/core/dist-es/submodules/httpAuthSchemes/aws_sdk/AwsSdkSigV4ASigner.js +1 -0
  4. package/node_modules/@aws-sdk/core/dist-es/submodules/httpAuthSchemes/aws_sdk/AwsSdkSigV4Signer.js +11 -6
  5. package/node_modules/@aws-sdk/core/package.json +7 -7
  6. package/node_modules/@aws-sdk/credential-provider-env/package.json +6 -6
  7. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-cjs/index.js +6 -4
  8. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +7 -6
  9. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +2 -1
  10. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-http2.js +2 -0
  11. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-es/node-https.js +2 -0
  12. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +2 -2
  13. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +1 -1
  14. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-http2.d.ts +2 -0
  15. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/dist-types/node-https.d.ts +2 -0
  16. package/node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler/package.json +1 -1
  17. package/node_modules/@aws-sdk/credential-provider-http/package.json +8 -8
  18. package/node_modules/@aws-sdk/credential-provider-ini/package.json +14 -14
  19. package/node_modules/@aws-sdk/credential-provider-login/package.json +7 -7
  20. package/node_modules/@aws-sdk/credential-provider-node/package.json +12 -12
  21. package/node_modules/@aws-sdk/credential-provider-process/package.json +6 -6
  22. package/node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers/package.json +7 -7
  23. package/node_modules/@aws-sdk/credential-provider-sso/package.json +8 -8
  24. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +7 -7
  25. package/node_modules/@aws-sdk/eventstream-handler-node/package.json +5 -5
  26. package/node_modules/@aws-sdk/middleware-eventstream/package.json +5 -5
  27. package/node_modules/@aws-sdk/middleware-websocket/package.json +8 -8
  28. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +99 -106
  29. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +62 -120
  30. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +55 -86
  31. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +73 -104
  32. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +102 -185
  33. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/auth/httpAuthSchemeProvider.js +4 -5
  34. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/endpoint/bdd.js +21 -57
  35. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/runtimeConfig.browser.js +1 -2
  36. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/runtimeConfig.js +2 -4
  37. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/schemas/schemas_0.js +73 -41
  38. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/auth/httpAuthSchemeProvider.js +4 -5
  39. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/endpoint/bdd.js +20 -52
  40. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/runtimeConfig.browser.js +1 -2
  41. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/runtimeConfig.js +2 -4
  42. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/schemas/schemas_0.js +36 -59
  43. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/auth/httpAuthSchemeProvider.js +4 -5
  44. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/endpoint/bdd.js +17 -45
  45. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/runtimeConfig.browser.js +1 -2
  46. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/runtimeConfig.js +2 -4
  47. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/schemas/schemas_0.js +32 -32
  48. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/auth/httpAuthSchemeProvider.js +4 -5
  49. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/endpoint/bdd.js +17 -45
  50. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/runtimeConfig.browser.js +1 -2
  51. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/runtimeConfig.js +2 -4
  52. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/schemas/schemas_0.js +50 -50
  53. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/auth/httpAuthSchemeProvider.js +4 -5
  54. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/defaultRoleAssumers.js +1 -1
  55. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/endpoint/bdd.js +34 -96
  56. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/runtimeConfig.browser.js +1 -2
  57. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/runtimeConfig.js +3 -6
  58. package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/schemas/schemas_0.js +63 -80
  59. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-cjs/index.js +6 -4
  60. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js +7 -6
  61. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js +2 -1
  62. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-http2.js +2 -0
  63. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-es/node-https.js +2 -0
  64. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http-handler.d.ts +2 -2
  65. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http2-handler.d.ts +1 -1
  66. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-http2.d.ts +2 -0
  67. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/dist-types/node-https.d.ts +2 -0
  68. package/node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler/package.json +1 -1
  69. package/node_modules/@aws-sdk/nested-clients/package.json +9 -9
  70. package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +5 -5
  71. package/node_modules/@aws-sdk/types/package.json +3 -3
  72. package/node_modules/@aws-sdk/util-locate-window/package.json +2 -2
  73. package/node_modules/@aws-sdk/xml-builder/package.json +3 -3
  74. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  75. package/node_modules/@earendil-works/pi-ai/dist/env-api-keys.js +3 -0
  76. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +45 -0
  77. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +827 -140
  78. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-completions.js +32 -12
  79. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +13 -1
  80. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/openai-codex.js +4 -2
  81. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  82. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/args.js +15 -2
  83. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +1 -0
  84. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js +4 -3
  85. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/branch-summarization.js +9 -3
  86. package/node_modules/@earendil-works/pi-coding-agent/dist/core/export-html/template.js +19 -6
  87. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +3 -0
  88. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +47 -13
  89. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js +72 -0
  90. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-display-names.js +3 -0
  91. package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +28 -20
  92. package/node_modules/@earendil-works/pi-coding-agent/dist/core/sdk.js +2 -30
  93. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +71 -30
  94. package/node_modules/@earendil-works/pi-coding-agent/dist/core/slash-commands.js +1 -0
  95. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js +1 -1
  96. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js +1 -1
  97. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js +1 -1
  98. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +1 -1
  99. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +1 -1
  100. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +1 -1
  101. package/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js +130 -0
  102. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +1 -0
  103. package/node_modules/@earendil-works/pi-coding-agent/dist/main.js +54 -2
  104. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -2
  105. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js +1 -1
  106. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/footer.js +7 -0
  107. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/index.js +1 -0
  108. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +3 -12
  109. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +22 -0
  110. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/trust-selector.js +86 -0
  111. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +40 -0
  112. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +55 -8
  113. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/git.js +54 -22
  114. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/open-browser.js +22 -0
  115. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
  116. package/node_modules/@earendil-works/pi-coding-agent/package.json +5 -4
  117. package/node_modules/@earendil-works/pi-tui/dist/utils.js +3 -0
  118. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  119. package/node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/httpRequest.js +2 -2
  120. package/node_modules/@smithy/credential-provider-imds/dist-es/remoteProvider/node-http.js +2 -0
  121. package/node_modules/@smithy/credential-provider-imds/dist-types/remoteProvider/httpRequest.d.ts +1 -1
  122. package/node_modules/@smithy/credential-provider-imds/dist-types/remoteProvider/node-http.d.ts +2 -0
  123. package/node_modules/@smithy/credential-provider-imds/package.json +1 -1
  124. package/node_modules/@types/node/README.md +1 -1
  125. package/node_modules/@types/node/http2.d.ts +3 -0
  126. package/node_modules/@types/node/package.json +2 -2
  127. package/node_modules/gaxios/build/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  128. package/node_modules/gaxios/build/esm/tsconfig.tsbuildinfo +1 -1
  129. package/node_modules/gaxios/package.json +7 -9
  130. package/node_modules/google-auth-library/build/src/auth/gdchclient.d.ts +47 -0
  131. package/node_modules/google-auth-library/build/src/auth/gdchclient.js +307 -0
  132. package/node_modules/google-auth-library/build/src/auth/googleauth.d.ts +5 -4
  133. package/node_modules/google-auth-library/build/src/auth/googleauth.js +18 -1
  134. package/node_modules/google-auth-library/build/src/index.d.ts +1 -0
  135. package/node_modules/google-auth-library/build/src/index.js +4 -1
  136. package/node_modules/google-auth-library/package.json +2 -4
  137. package/node_modules/semver/classes/semver.js +19 -2
  138. package/node_modules/semver/package.json +1 -1
  139. package/node_modules/undici/lib/global.js +1 -1
  140. package/node_modules/undici/lib/handler/unwrap-handler.js +6 -0
  141. package/node_modules/undici/package.json +1 -1
  142. package/node_modules/undici/types/dispatcher.d.ts +2 -0
  143. package/package.json +6 -6
  144. package/slingshot/index.js +275 -273
@@ -27,6 +27,7 @@ import { formatMissingSessionCwdPrompt, getMissingSessionCwdIssue, MissingSessio
27
27
  import { assertValidSessionId, SessionManager } from "./core/session-manager.js";
28
28
  import { SettingsManager } from "./core/settings-manager.js";
29
29
  import { printTimings, resetTimings, time } from "./core/timings.js";
30
+ import { hasProjectTrustInputs, ProjectTrustStore } from "./core/trust-manager.js";
30
31
  import { runMigrations, showDeprecationWarnings } from "./migrations.js";
31
32
  import { InteractiveMode, runPrintMode, runRpcMode } from "./modes/index.js";
32
33
  import { ExtensionSelectorComponent } from "./modes/interactive/components/extension-selector.js";
@@ -339,7 +340,7 @@ function buildSessionOptions(parsed, scopedModels, hasExistingSession, modelRegi
339
340
  function resolveCliPaths(cwd, paths) {
340
341
  return paths?.map((value) => (isLocalPath(value) ? resolvePath(value, cwd) : value));
341
342
  }
342
- async function promptForMissingSessionCwd(issue, settingsManager) {
343
+ async function showStartupSelector(settingsManager, title, options) {
343
344
  initTheme(settingsManager.getTheme());
344
345
  setKeybindings(KeybindingsManager.create());
345
346
  return new Promise((resolve) => {
@@ -354,12 +355,49 @@ async function promptForMissingSessionCwd(issue, settingsManager) {
354
355
  ui.stop();
355
356
  resolve(result);
356
357
  };
357
- const selector = new ExtensionSelectorComponent(formatMissingSessionCwdPrompt(issue), ["Continue", "Cancel"], (option) => finish(option === "Continue" ? issue.fallbackCwd : undefined), () => finish(undefined), { tui: ui });
358
+ const selector = new ExtensionSelectorComponent(title, options.map((option) => option.label), (option) => finish(options.find((entry) => entry.label === option)?.value), () => finish(undefined), { tui: ui });
358
359
  ui.addChild(selector);
359
360
  ui.setFocus(selector);
360
361
  ui.start();
361
362
  });
362
363
  }
364
+ async function promptForMissingSessionCwd(issue, settingsManager) {
365
+ return showStartupSelector(settingsManager, formatMissingSessionCwdPrompt(issue), [
366
+ { label: "Continue", value: issue.fallbackCwd },
367
+ { label: "Cancel", value: undefined },
368
+ ]);
369
+ }
370
+ async function promptForProjectTrust(cwd, settingsManager) {
371
+ return showStartupSelector(settingsManager, `Trust project folder?\n${cwd}\n\nThis allows sling to read project instructions (AGENTS.md/CLAUDE.md), load .sling settings and resources, install missing project packages, and execute project extensions.`, [
372
+ { label: "Trust", value: { trusted: true, remember: true } },
373
+ { label: "Trust (this session only)", value: { trusted: true, remember: false } },
374
+ { label: "Do not trust", value: { trusted: false, remember: true } },
375
+ { label: "Do not trust (this session only)", value: { trusted: false, remember: false } },
376
+ ]);
377
+ }
378
+ async function resolveProjectTrusted(options) {
379
+ if (options.trustOverride !== undefined) {
380
+ return options.trustOverride;
381
+ }
382
+ if (!hasProjectTrustInputs(options.cwd)) {
383
+ return true;
384
+ }
385
+ const decision = options.trustStore.get(options.cwd);
386
+ if (decision !== null) {
387
+ return decision;
388
+ }
389
+ if (options.appMode !== "interactive") {
390
+ return false;
391
+ }
392
+ const selected = await promptForProjectTrust(options.cwd, options.settingsManagerForPrompt);
393
+ if (selected !== undefined) {
394
+ if (selected.remember) {
395
+ options.trustStore.set(options.cwd, selected.trusted);
396
+ }
397
+ return selected.trusted;
398
+ }
399
+ return false;
400
+ }
363
401
  export async function main(args, options) {
364
402
  resetTimings();
365
403
  const offlineMode = args.includes("--offline") || isTruthyEnvFlag(process.env.PI_OFFLINE);
@@ -456,16 +494,30 @@ export async function main(args, options) {
456
494
  sessionManager.appendSessionInfo(name);
457
495
  }
458
496
  time("createSessionManager");
497
+ const trustStore = new ProjectTrustStore(agentDir);
498
+ const trustPromptMode = parsed.help || parsed.listModels !== undefined ? "print" : appMode;
499
+ const projectTrustedForSession = await resolveProjectTrusted({
500
+ cwd: sessionManager.getCwd(),
501
+ trustStore,
502
+ trustOverride: parsed.projectTrustOverride,
503
+ appMode: trustPromptMode,
504
+ settingsManagerForPrompt: startupSettingsManager,
505
+ });
459
506
  const resolvedExtensionPaths = resolveCliPaths(cwd, parsed.extensions);
460
507
  const resolvedSkillPaths = resolveCliPaths(cwd, parsed.skills);
461
508
  const resolvedPromptTemplatePaths = resolveCliPaths(cwd, parsed.promptTemplates);
462
509
  const resolvedThemePaths = resolveCliPaths(cwd, parsed.themes);
463
510
  const authStorage = AuthStorage.create();
464
511
  const createRuntime = async ({ cwd, agentDir, sessionManager, sessionStartEvent, }) => {
512
+ const projectTrusted = cwd === sessionManager.getCwd()
513
+ ? projectTrustedForSession
514
+ : (parsed.projectTrustOverride ?? (!hasProjectTrustInputs(cwd) || trustStore.get(cwd) === true));
515
+ const runtimeSettingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted });
465
516
  const services = await createAgentSessionServices({
466
517
  cwd,
467
518
  agentDir,
468
519
  authStorage,
520
+ settingsManager: runtimeSettingsManager,
469
521
  extensionFlagValues: parsed.unknownFlags,
470
522
  resourceLoaderOptions: {
471
523
  additionalExtensionPaths: resolvedExtensionPaths,
@@ -137,10 +137,10 @@ export class BashExecutionComponent extends Container {
137
137
  // Show how many lines are hidden (collapsed preview)
138
138
  if (hiddenLineCount > 0) {
139
139
  if (this.expanded) {
140
- statusParts.push(`(${keyHint("app.tools.expand", "to collapse")})`);
140
+ statusParts.push(`${theme.fg("muted", "(")}${keyHint("app.tools.expand", "to collapse")}${theme.fg("muted", ")")}`);
141
141
  }
142
142
  else {
143
- statusParts.push(`${theme.fg("muted", `... ${hiddenLineCount} more lines`)} (${keyHint("app.tools.expand", "to expand")})`);
143
+ statusParts.push(`${theme.fg("muted", `... ${hiddenLineCount} more lines (`)}${keyHint("app.tools.expand", "to expand")}${theme.fg("muted", ")")}`);
144
144
  }
145
145
  }
146
146
  if (this.status === "cancelled") {
@@ -41,7 +41,7 @@ function getGroupLabel(metadata) {
41
41
  ? `User (${formatBaseDir(metadata.baseDir)})`
42
42
  : `Project (${formatBaseDir(metadata.baseDir)})`;
43
43
  }
44
- return metadata.scope === "user" ? "User (~/.sling/agent/)" : "Project (.sling/)";
44
+ return metadata.scope === "user" ? "User (~/.pi/agent/)" : "Project (.pi/)";
45
45
  }
46
46
  return metadata.scope === "user" ? "User settings" : "Project settings";
47
47
  }
@@ -78,6 +78,7 @@ export class FooterComponent {
78
78
  let totalCacheRead = 0;
79
79
  let totalCacheWrite = 0;
80
80
  let totalCost = 0;
81
+ let latestCacheHitRate;
81
82
  for (const entry of this.session.sessionManager.getEntries()) {
82
83
  if (entry.type === "message" && entry.message.role === "assistant") {
83
84
  totalInput += entry.message.usage.input;
@@ -85,6 +86,9 @@ export class FooterComponent {
85
86
  totalCacheRead += entry.message.usage.cacheRead;
86
87
  totalCacheWrite += entry.message.usage.cacheWrite;
87
88
  totalCost += entry.message.usage.cost.total;
89
+ const latestPromptTokens = entry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;
90
+ latestCacheHitRate =
91
+ latestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined;
88
92
  }
89
93
  }
90
94
  // Calculate context usage from session (handles compaction correctly).
@@ -115,6 +119,9 @@ export class FooterComponent {
115
119
  statsParts.push(`R${formatTokens(totalCacheRead)}`);
116
120
  if (totalCacheWrite)
117
121
  statsParts.push(`W${formatTokens(totalCacheWrite)}`);
122
+ if ((totalCacheRead > 0 || totalCacheWrite > 0) && latestCacheHitRate !== undefined) {
123
+ statsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`);
124
+ }
118
125
  // Show cost with "(sub)" indicator if using OAuth subscription
119
126
  const usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;
120
127
  if (totalCost || usingSubscription) {
@@ -27,6 +27,7 @@ export { ThemeSelectorComponent } from "./theme-selector.js";
27
27
  export { ThinkingSelectorComponent } from "./thinking-selector.js";
28
28
  export { ToolExecutionComponent } from "./tool-execution.js";
29
29
  export { TreeSelectorComponent } from "./tree-selector.js";
30
+ export { TrustSelectorComponent } from "./trust-selector.js";
30
31
  export { UserMessageComponent } from "./user-message.js";
31
32
  export { UserMessageSelectorComponent } from "./user-message-selector.js";
32
33
  export { truncateToVisualLines } from "./visual-truncate.js";
@@ -1,6 +1,6 @@
1
1
  import { getOAuthProviders } from "@earendil-works/pi-ai/oauth";
2
2
  import { Container, getKeybindings, Input, Spacer, Text } from "@earendil-works/pi-tui";
3
- import { exec } from "child_process";
3
+ import { openBrowser } from "../../../utils/open-browser.js";
4
4
  import { theme } from "../theme/theme.js";
5
5
  import { DynamicBorder } from "./dynamic-border.js";
6
6
  import { keyHint } from "./keybinding-hints.js";
@@ -80,7 +80,7 @@ export class LoginDialogComponent extends Container {
80
80
  this.contentContainer.addChild(new Spacer(1));
81
81
  this.contentContainer.addChild(new Text(theme.fg("warning", instructions), 1, 0));
82
82
  }
83
- this.openUrl(url);
83
+ openBrowser(url);
84
84
  this.tui.requestRender();
85
85
  }
86
86
  /**
@@ -96,18 +96,9 @@ export class LoginDialogComponent extends Container {
96
96
  this.contentContainer.addChild(new Text(theme.fg("dim", hyperlink), 1, 0));
97
97
  this.contentContainer.addChild(new Spacer(1));
98
98
  this.contentContainer.addChild(new Text(theme.fg("warning", `Enter code: ${info.userCode}`), 1, 0));
99
- this.openUrl(info.verificationUri);
99
+ openBrowser(info.verificationUri);
100
100
  this.tui.requestRender();
101
101
  }
102
- openUrl(url) {
103
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
104
- try {
105
- exec(`${openCmd} "${url}"`, () => { });
106
- }
107
- catch {
108
- // Ignore browser launch failures. The URL remains visible for manual opening/copying.
109
- }
110
- }
111
102
  /**
112
103
  * Show input for manual code/URL entry (for callback server providers)
113
104
  */
@@ -178,6 +178,28 @@ export class ToolExecutionComponent extends Container {
178
178
  if (this.hideComponent) {
179
179
  return [];
180
180
  }
181
+ if (this.hasRendererDefinition() && this.getRenderShell() === "self") {
182
+ const contentLines = this.selfRenderContainer.render(width);
183
+ if (contentLines.length === 0 && this.imageComponents.length === 0) {
184
+ return [];
185
+ }
186
+ const lines = [];
187
+ if (contentLines.length > 0) {
188
+ lines.push("");
189
+ lines.push(...contentLines);
190
+ }
191
+ for (let i = 0; i < this.imageComponents.length; i++) {
192
+ const spacer = this.imageSpacers[i];
193
+ if (spacer) {
194
+ lines.push(...spacer.render(width));
195
+ }
196
+ const imageComponent = this.imageComponents[i];
197
+ if (imageComponent) {
198
+ lines.push(...imageComponent.render(width));
199
+ }
200
+ }
201
+ return lines;
202
+ }
181
203
  return super.render(width);
182
204
  }
183
205
  updateDisplay() {
@@ -0,0 +1,86 @@
1
+ import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
2
+ import { theme } from "../theme/theme.js";
3
+ import { DynamicBorder } from "./dynamic-border.js";
4
+ import { keyHint, rawKeyHint } from "./keybinding-hints.js";
5
+ const TRUST_OPTIONS = [
6
+ { label: "Trust", trusted: true },
7
+ { label: "Do not trust", trusted: false },
8
+ ];
9
+ function formatDecision(decision) {
10
+ if (decision === true) {
11
+ return "trusted";
12
+ }
13
+ if (decision === false) {
14
+ return "untrusted";
15
+ }
16
+ return "none";
17
+ }
18
+ export class TrustSelectorComponent extends Container {
19
+ selectedIndex;
20
+ listContainer;
21
+ savedDecision;
22
+ onSelectCallback;
23
+ onCancelCallback;
24
+ constructor(options) {
25
+ super();
26
+ this.savedDecision = options.savedDecision;
27
+ this.selectedIndex = Math.max(0, TRUST_OPTIONS.findIndex((option) => option.trusted === options.savedDecision));
28
+ this.onSelectCallback = options.onSelect;
29
+ this.onCancelCallback = options.onCancel;
30
+ this.addChild(new DynamicBorder());
31
+ this.addChild(new Spacer(1));
32
+ this.addChild(new Text(theme.fg("accent", theme.bold("Project trust")), 1, 0));
33
+ this.addChild(new Text(theme.fg("muted", options.cwd), 1, 0));
34
+ this.addChild(new Spacer(1));
35
+ this.addChild(new Text(theme.fg("muted", `Saved decision: ${formatDecision(options.savedDecision)}`), 1, 0));
36
+ this.addChild(new Text(theme.fg("muted", `Current session: ${options.projectTrusted ? "trusted" : "untrusted"}`), 1, 0));
37
+ this.addChild(new Spacer(1));
38
+ this.listContainer = new Container();
39
+ this.addChild(this.listContainer);
40
+ this.addChild(new Spacer(1));
41
+ this.addChild(new Text(rawKeyHint("↑↓", "navigate") +
42
+ " " +
43
+ keyHint("tui.select.confirm", "save") +
44
+ " " +
45
+ keyHint("tui.select.cancel", "cancel"), 1, 0));
46
+ this.addChild(new Spacer(1));
47
+ this.addChild(new DynamicBorder());
48
+ this.updateList();
49
+ }
50
+ updateList() {
51
+ this.listContainer.clear();
52
+ for (let i = 0; i < TRUST_OPTIONS.length; i++) {
53
+ const option = TRUST_OPTIONS[i];
54
+ if (!option) {
55
+ continue;
56
+ }
57
+ const isSelected = i === this.selectedIndex;
58
+ const isCurrent = option.trusted === this.savedDecision;
59
+ const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
60
+ const prefix = isSelected ? theme.fg("accent", "→ ") : " ";
61
+ const label = isSelected ? theme.fg("accent", option.label) : theme.fg("text", option.label);
62
+ this.listContainer.addChild(new Text(`${prefix}${label}${checkmark}`, 1, 0));
63
+ }
64
+ }
65
+ handleInput(keyData) {
66
+ const kb = getKeybindings();
67
+ if (kb.matches(keyData, "tui.select.up") || keyData === "k") {
68
+ this.selectedIndex = Math.max(0, this.selectedIndex - 1);
69
+ this.updateList();
70
+ }
71
+ else if (kb.matches(keyData, "tui.select.down") || keyData === "j") {
72
+ this.selectedIndex = Math.min(TRUST_OPTIONS.length - 1, this.selectedIndex + 1);
73
+ this.updateList();
74
+ }
75
+ else if (kb.matches(keyData, "tui.select.confirm") || keyData === "\n") {
76
+ const selected = TRUST_OPTIONS[this.selectedIndex];
77
+ if (selected) {
78
+ this.onSelectCallback(selected.trusted);
79
+ }
80
+ }
81
+ else if (kb.matches(keyData, "tui.select.cancel")) {
82
+ this.onCancelCallback();
83
+ }
84
+ }
85
+ }
86
+ //# sourceMappingURL=trust-selector.js.map
@@ -24,6 +24,7 @@ import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../cor
24
24
  import { SessionManager } from "../../core/session-manager.js";
25
25
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
26
26
  import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
27
+ import { hasProjectTrustInputs, ProjectTrustStore } from "../../core/trust-manager.js";
27
28
  import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
28
29
  import { copyToClipboard } from "../../utils/clipboard.js";
29
30
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
@@ -59,6 +60,7 @@ import { SettingsSelectorComponent } from "./components/settings-selector.js";
59
60
  import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
60
61
  import { ToolExecutionComponent } from "./components/tool-execution.js";
61
62
  import { TreeSelectorComponent } from "./components/tree-selector.js";
63
+ import { TrustSelectorComponent } from "./components/trust-selector.js";
62
64
  import { UserMessageComponent } from "./components/user-message.js";
63
65
  import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
64
66
  import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, initTheme, onThemeChange, setRegisteredThemes, setTheme, setThemeInstance, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
@@ -2059,6 +2061,11 @@ export class InteractiveMode {
2059
2061
  this.editor.setText("");
2060
2062
  return;
2061
2063
  }
2064
+ if (text === "/trust") {
2065
+ this.showTrustSelector();
2066
+ this.editor.setText("");
2067
+ return;
2068
+ }
2062
2069
  if (text === "/login") {
2063
2070
  this.showOAuthSelector("login");
2064
2071
  this.editor.setText("");
@@ -2528,6 +2535,7 @@ export class InteractiveMode {
2528
2535
  this.chatContainer.addChild(component);
2529
2536
  // Render user message separately if present
2530
2537
  if (skillBlock.userMessage) {
2538
+ this.chatContainer.addChild(new Spacer(1));
2531
2539
  const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings());
2532
2540
  this.chatContainer.addChild(userComponent);
2533
2541
  }
@@ -2627,6 +2635,7 @@ export class InteractiveMode {
2627
2635
  updateFooter: true,
2628
2636
  populateHistory: true,
2629
2637
  });
2638
+ this.renderProjectTrustWarningIfNeeded();
2630
2639
  // Show compaction info if session was compacted
2631
2640
  const allEntries = this.sessionManager.getEntries();
2632
2641
  const compactionCount = allEntries.filter((e) => e.type === "compaction").length;
@@ -2635,6 +2644,15 @@ export class InteractiveMode {
2635
2644
  this.showStatus(`Session compacted ${times}`);
2636
2645
  }
2637
2646
  }
2647
+ renderProjectTrustWarningIfNeeded() {
2648
+ if (this.settingsManager.isProjectTrusted() || !hasProjectTrustInputs(this.sessionManager.getCwd())) {
2649
+ return;
2650
+ }
2651
+ if (this.chatContainer.children.length > 0) {
2652
+ this.chatContainer.addChild(new Spacer(1));
2653
+ }
2654
+ this.chatContainer.addChild(new Text(theme.fg("warning", "This project is not trusted. Project instructions (AGENTS.md/CLAUDE.md), .pi resources, and project packages are ignored. Use /trust to save a trust decision, then restart pi."), 1, 0));
2655
+ }
2638
2656
  async getUserInput() {
2639
2657
  const queuedInput = this.pendingUserInputs.shift();
2640
2658
  if (queuedInput !== undefined) {
@@ -3450,6 +3468,28 @@ export class InteractiveMode {
3450
3468
  // Ignore auth lookup failures for warning-only checks.
3451
3469
  }
3452
3470
  }
3471
+ showTrustSelector() {
3472
+ const cwd = this.sessionManager.getCwd();
3473
+ const trustStore = new ProjectTrustStore(this.runtimeHost.services.agentDir);
3474
+ const savedDecision = trustStore.get(cwd);
3475
+ this.showSelector((done) => {
3476
+ const selector = new TrustSelectorComponent({
3477
+ cwd,
3478
+ savedDecision,
3479
+ projectTrusted: this.settingsManager.isProjectTrusted(),
3480
+ onSelect: (trusted) => {
3481
+ trustStore.set(cwd, trusted);
3482
+ done();
3483
+ this.showStatus(`Saved trust decision: ${trusted ? "trusted" : "untrusted"}. Restart pi for this to take effect.`);
3484
+ },
3485
+ onCancel: () => {
3486
+ done();
3487
+ this.ui.requestRender();
3488
+ },
3489
+ });
3490
+ return { component: selector, focus: selector };
3491
+ });
3492
+ }
3453
3493
  showModelSelector(initialSearchInput) {
3454
3494
  this.showSelector((done) => {
3455
3495
  const selector = new ModelSelectorComponent(this.ui, this.session.model, this.settingsManager, this.session.modelRegistry, this.session.scopedModels, async (model) => {
@@ -4,6 +4,7 @@ import { selectConfig } from "./cli/config-selector.js";
4
4
  import { APP_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, VERSION, } from "./config.js";
5
5
  import { DefaultPackageManager } from "./core/package-manager.js";
6
6
  import { SettingsManager } from "./core/settings-manager.js";
7
+ import { hasProjectTrustInputs, ProjectTrustStore } from "./core/trust-manager.js";
7
8
  import { spawnProcess } from "./utils/child-process.js";
8
9
  import { getLatestPiRelease, isNewerPackageVersion } from "./utils/version-check.js";
9
10
  import { cleanupWindowsSelfUpdateQuarantine, quarantineWindowsNativeDependencies, } from "./utils/windows-self-update.js";
@@ -35,13 +36,13 @@ function reportSettingsErrors(settingsManager, context) {
35
36
  function getPackageCommandUsage(command) {
36
37
  switch (command) {
37
38
  case "install":
38
- return `${APP_NAME} install <source> [-l]`;
39
+ return `${APP_NAME} install <source> [-l] [--approve|--no-approve]`;
39
40
  case "remove":
40
- return `${APP_NAME} remove <source> [-l]`;
41
+ return `${APP_NAME} remove <source> [-l] [--approve|--no-approve]`;
41
42
  case "update":
42
- return `${APP_NAME} update [source|self|pi] [--self] [--extensions] [--extension <source>] [--force]`;
43
+ return `${APP_NAME} update [source|self|pi] [--self] [--extensions] [--extension <source>] [--approve|--no-approve] [--force]`;
43
44
  case "list":
44
- return `${APP_NAME} list`;
45
+ return `${APP_NAME} list [--approve|--no-approve]`;
45
46
  }
46
47
  }
47
48
  function printPackageCommandHelp(command) {
@@ -53,7 +54,9 @@ function printPackageCommandHelp(command) {
53
54
  Install a package and add it to settings.
54
55
 
55
56
  Options:
56
- -l, --local Install project-locally (.pi/settings.json)
57
+ -l, --local Install project-locally (.pi/settings.json)
58
+ -a, --approve Trust project-local files for this command
59
+ -na, --no-approve Ignore project-local files for this command
57
60
 
58
61
  Examples:
59
62
  ${APP_NAME} install npm:@foo/bar
@@ -72,7 +75,9 @@ Remove a package and its source from settings.
72
75
  Alias: ${APP_NAME} uninstall <source> [-l]
73
76
 
74
77
  Options:
75
- -l, --local Remove from project settings (.pi/settings.json)
78
+ -l, --local Remove from project settings (.pi/settings.json)
79
+ -a, --approve Trust project-local files for this command
80
+ -na, --no-approve Ignore project-local files for this command
76
81
 
77
82
  Examples:
78
83
  ${APP_NAME} remove npm:@foo/bar
@@ -89,6 +94,8 @@ Options:
89
94
  --self Update pi only
90
95
  --extensions Update installed packages only
91
96
  --extension <source> Update one package only
97
+ -a, --approve Trust project-local files for this command
98
+ -na, --no-approve Ignore project-local files for this command
92
99
  --force Reinstall pi even if the current version is latest
93
100
 
94
101
  Short forms:
@@ -102,6 +109,10 @@ Short forms:
102
109
  ${getPackageCommandUsage("list")}
103
110
 
104
111
  List installed packages from user and project settings.
112
+
113
+ Options:
114
+ -a, --approve Trust project-local files for this command
115
+ -na, --no-approve Ignore project-local files for this command
105
116
  `);
106
117
  return;
107
118
  }
@@ -120,6 +131,7 @@ function parsePackageCommand(args) {
120
131
  }
121
132
  let local = false;
122
133
  let force = false;
134
+ let projectTrustOverride;
123
135
  let help = false;
124
136
  let invalidOption;
125
137
  let invalidArgument;
@@ -162,6 +174,14 @@ function parsePackageCommand(args) {
162
174
  }
163
175
  continue;
164
176
  }
177
+ if (arg === "--approve" || arg === "-a") {
178
+ projectTrustOverride = true;
179
+ continue;
180
+ }
181
+ if (arg === "--no-approve" || arg === "-na") {
182
+ projectTrustOverride = false;
183
+ continue;
184
+ }
165
185
  if (arg === "--force") {
166
186
  if (command === "update") {
167
187
  force = true;
@@ -244,6 +264,7 @@ function parsePackageCommand(args) {
244
264
  updateTarget,
245
265
  local,
246
266
  force,
267
+ projectTrustOverride,
247
268
  help,
248
269
  invalidOption,
249
270
  invalidArgument,
@@ -337,13 +358,32 @@ function prepareWindowsNpmSelfUpdate() {
337
358
  cleanupWindowsSelfUpdateQuarantine(packageDir);
338
359
  quarantineWindowsNativeDependencies(packageDir);
339
360
  }
361
+ function parseProjectTrustOverride(args) {
362
+ let trustOverride;
363
+ for (const arg of args) {
364
+ if (arg === "--approve" || arg === "-a") {
365
+ trustOverride = true;
366
+ }
367
+ else if (arg === "--no-approve" || arg === "-na") {
368
+ trustOverride = false;
369
+ }
370
+ }
371
+ return trustOverride;
372
+ }
373
+ function resolveProjectTrusted(cwd, agentDir, trustOverride) {
374
+ if (trustOverride !== undefined) {
375
+ return trustOverride;
376
+ }
377
+ return !hasProjectTrustInputs(cwd) || new ProjectTrustStore(agentDir).get(cwd) === true;
378
+ }
340
379
  export async function handleConfigCommand(args) {
341
380
  if (args[0] !== "config") {
342
381
  return false;
343
382
  }
344
383
  const cwd = process.cwd();
345
384
  const agentDir = getAgentDir();
346
- const settingsManager = SettingsManager.create(cwd, agentDir);
385
+ const projectTrusted = parseProjectTrustOverride(args) ?? true;
386
+ const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted });
347
387
  reportSettingsErrors(settingsManager, "config command");
348
388
  const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
349
389
  const resolvedPaths = await packageManager.resolve();
@@ -397,7 +437,14 @@ export async function handlePackageCommand(args) {
397
437
  }
398
438
  const cwd = process.cwd();
399
439
  const agentDir = getAgentDir();
400
- const settingsManager = SettingsManager.create(cwd, agentDir);
440
+ const writesProjectPackageConfig = (options.command === "install" || options.command === "remove") && options.local;
441
+ const projectTrusted = resolveProjectTrusted(cwd, agentDir, options.projectTrustOverride);
442
+ if (!projectTrusted && writesProjectPackageConfig) {
443
+ console.error(chalk.red("Project is not trusted. Use --approve to modify local package config."));
444
+ process.exitCode = 1;
445
+ return true;
446
+ }
447
+ const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted });
401
448
  reportSettingsErrors(settingsManager, "package command");
402
449
  const selfUpdateNpmCommand = settingsManager.getGlobalSettings().npmCommand;
403
450
  const packageManager = new DefaultPackageManager({ cwd, agentDir, settingsManager });
@@ -56,6 +56,53 @@ function splitRef(url) {
56
56
  ref,
57
57
  };
58
58
  }
59
+ function decodeForValidation(value) {
60
+ try {
61
+ return decodeURIComponent(value);
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ }
67
+ function hasUnsafeGitInstallPart(value, allowSlash) {
68
+ const decoded = decodeForValidation(value);
69
+ if (decoded === null) {
70
+ return true;
71
+ }
72
+ const candidates = [value, decoded];
73
+ for (const candidate of candidates) {
74
+ if (candidate.includes("\0") || candidate.includes("\\") || candidate.startsWith("/")) {
75
+ return true;
76
+ }
77
+ if (!allowSlash && candidate.includes("/")) {
78
+ return true;
79
+ }
80
+ if (candidate.split("/").includes("..")) {
81
+ return true;
82
+ }
83
+ }
84
+ return false;
85
+ }
86
+ function buildGitSource(args) {
87
+ if (args.path.startsWith("/")) {
88
+ return null;
89
+ }
90
+ const normalizedPath = args.path.replace(/\.git$/, "").replace(/^\/+/, "");
91
+ if (!args.host || !normalizedPath || normalizedPath.split("/").length < 2) {
92
+ return null;
93
+ }
94
+ if (hasUnsafeGitInstallPart(args.host, false) || hasUnsafeGitInstallPart(normalizedPath, true)) {
95
+ return null;
96
+ }
97
+ return {
98
+ type: "git",
99
+ repo: args.repo,
100
+ host: args.host,
101
+ path: normalizedPath,
102
+ ref: args.ref,
103
+ pinned: Boolean(args.ref),
104
+ };
105
+ }
59
106
  function parseGenericGitUrl(url) {
60
107
  const { repo: repoWithoutRef, ref } = splitRef(url);
61
108
  let repo = repoWithoutRef;
@@ -91,18 +138,7 @@ function parseGenericGitUrl(url) {
91
138
  }
92
139
  repo = `https://${repoWithoutRef}`;
93
140
  }
94
- const normalizedPath = path.replace(/\.git$/, "").replace(/^\/+/, "");
95
- if (!host || !normalizedPath || normalizedPath.split("/").length < 2) {
96
- return null;
97
- }
98
- return {
99
- type: "git",
100
- repo,
101
- host,
102
- path: normalizedPath,
103
- ref,
104
- pinned: Boolean(ref),
105
- };
141
+ return buildGitSource({ repo, host, path, ref });
106
142
  }
107
143
  /**
108
144
  * Parse git source into a GitSource.
@@ -131,14 +167,12 @@ export function parseGitUrl(source) {
131
167
  !split.repo.startsWith("ssh://") &&
132
168
  !split.repo.startsWith("git://") &&
133
169
  !split.repo.startsWith("git@");
134
- return {
135
- type: "git",
170
+ return buildGitSource({
136
171
  repo: useHttpsPrefix ? `https://${split.repo}` : split.repo,
137
172
  host: info.domain || "",
138
- path: `${info.user}/${info.project}`.replace(/\.git$/, ""),
173
+ path: `${info.user}/${info.project}`,
139
174
  ref: info.committish || split.ref || undefined,
140
- pinned: Boolean(info.committish || split.ref),
141
- };
175
+ });
142
176
  }
143
177
  }
144
178
  const httpsCandidates = [split.ref ? `https://${split.repo}#${split.ref}` : undefined, `https://${url}`].filter((value) => Boolean(value));
@@ -148,14 +182,12 @@ export function parseGitUrl(source) {
148
182
  if (split.ref && info.project?.includes("@")) {
149
183
  continue;
150
184
  }
151
- return {
152
- type: "git",
185
+ return buildGitSource({
153
186
  repo: `https://${split.repo}`,
154
187
  host: info.domain || "",
155
- path: `${info.user}/${info.project}`.replace(/\.git$/, ""),
188
+ path: `${info.user}/${info.project}`,
156
189
  ref: info.committish || split.ref || undefined,
157
- pinned: Boolean(info.committish || split.ref),
158
- };
190
+ });
159
191
  }
160
192
  }
161
193
  return parseGenericGitUrl(url);