privateer-agent 0.10.0 → 0.12.0
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 +13 -0
- package/bin/privateer-launch.mjs +144 -66
- package/bin/privateer-subagent.mjs +35 -22
- package/extensions/privateer-brand.ts +14 -1
- package/extensions/privateer-gate.ts +20 -28
- package/extensions/privateer-media.ts +20 -0
- package/extensions/privateer-privacy.ts +3 -3
- package/package.json +17 -7
- package/patches/@earendil-works+pi-coding-agent+0.80.3.patch +876 -5
- package/src/acp/run.ts +16 -19
- package/src/channels/run.ts +18 -27
- package/src/cli/chat.ts +70 -25
- package/src/config/hosted.ts +34 -0
- package/src/config/moat.ts +236 -0
- package/src/config/moatManifest.json +26 -0
- package/src/config/moatManifest.ts +71 -0
- package/src/harbor/index.ts +49 -113
- package/src/outbox/cloudOutbox.ts +95 -0
- package/src/permissions/classify.ts +122 -0
- package/src/providers/account.ts +141 -19
- package/src/providers/defaultModel.ts +66 -11
- package/src/providers/phala/aci-verifier/VENDORED.md +16 -0
- package/src/providers/phala/reportBinding.ts +193 -0
- package/src/providers/phalaSeal.ts +7 -3
- package/src/providers/sealedShim.ts +35 -6
- package/src/remote/extensionsControl.ts +12 -22
- package/src/remote/liveTaskSession.ts +15 -24
- package/src/remote/relayClient.ts +47 -7
- package/src/remote/remoteBridge.ts +65 -0
- package/src/tools/media.ts +554 -0
- package/src/tools/relayFileTools.ts +10 -11
- package/src/tools/videoCompose.ts +579 -0
- package/src/tools/web.ts +12 -8
- package/src/config/harborDaemon.ts +0 -31
- package/src/config/inlineMoat.ts +0 -67
|
@@ -1,3 +1,72 @@
|
|
|
1
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/config.js b/node_modules/@earendil-works/pi-coding-agent/dist/config.js
|
|
2
|
+
index 4600b23..075ecae 100644
|
|
3
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/config.js
|
|
4
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/config.js
|
|
5
|
+
@@ -393,6 +393,64 @@ export const APP_NAME = piConfigName || "pi";
|
|
6
|
+
export const APP_TITLE = piConfigName ? APP_NAME : "π";
|
|
7
|
+
export const CONFIG_DIR_NAME = pkg.piConfig?.configDir || ".pi";
|
|
8
|
+
export const VERSION = pkg.version || "0.0.0";
|
|
9
|
+
+// --- Privateer patch: dual project config dirs -------------------------------
|
|
10
|
+
+//
|
|
11
|
+
+// Stock Pi resolves EVERY project-scoped path as `<cwd>/CONFIG_DIR_NAME/...`, where
|
|
12
|
+
+// CONFIG_DIR_NAME is a build-time constant read from this package's own package.json
|
|
13
|
+
+// (`piConfig.configDir`, default ".pi"). PI_CODING_AGENT_DIR moves only the USER dir,
|
|
14
|
+
+// which is why Privateer's global config lives in ~/.privateer/agent while every
|
|
15
|
+
+// project still had to spell its config `.pi/` — a directory named after a tool the
|
|
16
|
+
+// user never installed.
|
|
17
|
+
+//
|
|
18
|
+
+// We do not rename it (that would orphan every existing `.pi/` folder and break
|
|
19
|
+
+// interop with stock Pi). Instead BOTH names are live, `.privateer` taking precedence:
|
|
20
|
+
+//
|
|
21
|
+
+// read — every project dir that exists is searched, `.privateer` first; on a
|
|
22
|
+
+// conflict (same settings key, same skill/prompt/theme/extension name,
|
|
23
|
+
+// SYSTEM.md) the `.privateer` copy wins and the `.pi` one is ignored.
|
|
24
|
+
+// write — new project config is always created under `.privateer/`. Package
|
|
25
|
+
+// payloads (npm/, git/) stay in whichever dir already holds them, so an
|
|
26
|
+
+// existing `.pi/npm` tree is reused rather than re-downloaded.
|
|
27
|
+
+//
|
|
28
|
+
+// Precedence order, highest first. `.pi` is filtered out if a fork ever renames
|
|
29
|
+
+// CONFIG_DIR_NAME to `.privateer`, so the list never holds duplicates.
|
|
30
|
+
+export const PROJECT_CONFIG_DIR_NAMES = [".privateer", CONFIG_DIR_NAME].filter((name, i, all) => all.indexOf(name) === i);
|
|
31
|
+
+/** Every candidate project config dir under cwd, highest precedence first (may not exist). */
|
|
32
|
+
+export function projectConfigDirCandidates(cwd) {
|
|
33
|
+
+ return PROJECT_CONFIG_DIR_NAMES.map((name) => join(cwd, name));
|
|
34
|
+
+}
|
|
35
|
+
+/** The project config dirs that actually exist under cwd, highest precedence first. */
|
|
36
|
+
+export function projectConfigDirs(cwd) {
|
|
37
|
+
+ return projectConfigDirCandidates(cwd).filter((dir) => existsSync(dir));
|
|
38
|
+
+}
|
|
39
|
+
+/** Where NEW project config is written: always `.privateer/`. */
|
|
40
|
+
+export function projectConfigWriteDir(cwd) {
|
|
41
|
+
+ return join(cwd, PROJECT_CONFIG_DIR_NAMES[0]);
|
|
42
|
+
+}
|
|
43
|
+
+/**
|
|
44
|
+
+ * The highest-precedence project dir that already contains `...rel`, or undefined.
|
|
45
|
+
+ * Use for "one file wins" lookups (settings.json, SYSTEM.md, an npm/ payload root).
|
|
46
|
+
+ */
|
|
47
|
+
+export function findProjectConfigPath(cwd, ...rel) {
|
|
48
|
+
+ for (const dir of projectConfigDirCandidates(cwd)) {
|
|
49
|
+
+ const candidate = join(dir, ...rel);
|
|
50
|
+
+ if (existsSync(candidate))
|
|
51
|
+
+ return candidate;
|
|
52
|
+
+ }
|
|
53
|
+
+ return undefined;
|
|
54
|
+
+}
|
|
55
|
+
+/**
|
|
56
|
+
+ * Where a project-scoped `...rel` lives: the existing copy if there is one, else the
|
|
57
|
+
+ * path it would be created at under `.privateer/`.
|
|
58
|
+
+ */
|
|
59
|
+
+export function resolveProjectConfigPath(cwd, ...rel) {
|
|
60
|
+
+ return findProjectConfigPath(cwd, ...rel) ?? join(projectConfigWriteDir(cwd), ...rel);
|
|
61
|
+
+}
|
|
62
|
+
+/** Human-readable list of the project dir names, for prompts and help text. */
|
|
63
|
+
+export function formatProjectConfigDirNames() {
|
|
64
|
+
+ return PROJECT_CONFIG_DIR_NAMES.map((n) => `${n}/`).join(" or ");
|
|
65
|
+
+}
|
|
66
|
+
+// --- end Privateer patch -----------------------------------------------------
|
|
67
|
+
// e.g., PI_CODING_AGENT_DIR or TAU_CODING_AGENT_DIR
|
|
68
|
+
export const ENV_AGENT_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_DIR`;
|
|
69
|
+
export const ENV_SESSION_DIR = `${APP_NAME.toUpperCase()}_CODING_AGENT_SESSION_DIR`;
|
|
1
70
|
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js
|
|
2
71
|
index e223ce1..cc853e7 100644
|
|
3
72
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js
|
|
@@ -53,7 +122,7 @@ index e223ce1..cc853e7 100644
|
|
|
53
122
|
}
|
|
54
123
|
/**
|
|
55
124
|
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-guidance.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-guidance.js
|
|
56
|
-
index 197bccc..
|
|
125
|
+
index 197bccc..bc9ac3f 100644
|
|
57
126
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-guidance.js
|
|
58
127
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-guidance.js
|
|
59
128
|
@@ -1,21 +1,33 @@
|
|
@@ -96,11 +165,773 @@ index 197bccc..27f6429 100644
|
|
|
96
165
|
+ return [join(getDocsPath(), "providers.md"), join(getDocsPath(), "models.md")];
|
|
97
166
|
+}
|
|
98
167
|
//# sourceMappingURL=auth-guidance.js.map
|
|
168
|
+
|
|
169
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
|
|
170
|
+
index 4b79482..a81b452 100644
|
|
171
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
|
|
172
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js
|
|
173
|
+
@@ -17,7 +17,7 @@ import { createJiti } from "jiti/static";
|
|
174
|
+
import * as _bundledTypebox from "typebox";
|
|
175
|
+
import * as _bundledTypeboxCompile from "typebox/compile";
|
|
176
|
+
import * as _bundledTypeboxValue from "typebox/value";
|
|
177
|
+
-import { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from "../../config.js";
|
|
178
|
+
+import { getAgentDir, isBunBinary, projectConfigDirCandidates } from "../../config.js";
|
|
179
|
+
// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
|
|
180
|
+
// avoiding a circular dependency. Extensions can import from @earendil-works/pi-coding-agent.
|
|
181
|
+
import * as _bundledPiCodingAgent from "../../index.js";
|
|
182
|
+
@@ -508,9 +508,23 @@ export async function discoverAndLoadExtensions(configuredPaths, cwd, agentDir =
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
- // 1. Project-local extensions: cwd/${CONFIG_DIR_NAME}/extensions/
|
|
187
|
+
- const localExtDir = path.join(resolvedCwd, CONFIG_DIR_NAME, "extensions");
|
|
188
|
+
- addPaths(discoverExtensionsInDir(localExtDir));
|
|
189
|
+
+ // 1. Project-local extensions: cwd/.privateer/extensions/, then cwd/.pi/extensions/
|
|
190
|
+
+ //
|
|
191
|
+
+ // Privateer patch: both project dirs are discovered, `.privateer` first. Extensions
|
|
192
|
+
+ // are files, so a same-named file in the lower-precedence dir would otherwise load
|
|
193
|
+
+ // as a SECOND copy of the same extension — skip it by basename, which is what makes
|
|
194
|
+
+ // `.privateer` genuinely supersede `.pi` here rather than merely precede it.
|
|
195
|
+
+ const seenLocalNames = new Set();
|
|
196
|
+
+ for (const projectDir of projectConfigDirCandidates(resolvedCwd)) {
|
|
197
|
+
+ const discovered = discoverExtensionsInDir(path.join(projectDir, "extensions"));
|
|
198
|
+
+ addPaths(discovered.filter((p) => {
|
|
199
|
+
+ const name = path.basename(p);
|
|
200
|
+
+ if (seenLocalNames.has(name))
|
|
201
|
+
+ return false;
|
|
202
|
+
+ seenLocalNames.add(name);
|
|
203
|
+
+ return true;
|
|
204
|
+
+ }));
|
|
205
|
+
+ }
|
|
206
|
+
// 2. Global extensions: agentDir/extensions/
|
|
207
|
+
const globalExtDir = path.join(resolvedAgentDir, "extensions");
|
|
208
|
+
addPaths(discoverExtensionsInDir(globalExtDir));
|
|
209
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js
|
|
210
|
+
index a125c9a..5ca0ccc 100644
|
|
211
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js
|
|
212
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js
|
|
213
|
+
@@ -25,7 +25,7 @@ import { globSync } from "glob";
|
|
214
|
+
import ignore from "ignore";
|
|
215
|
+
import { minimatch } from "minimatch";
|
|
216
|
+
import { maxSatisfying, rcompare, satisfies, valid, validRange } from "semver";
|
|
217
|
+
-import { CONFIG_DIR_NAME } from "../config.js";
|
|
218
|
+
+import { projectConfigDirs, projectConfigWriteDir, resolveProjectConfigPath } from "../config.js";
|
|
219
|
+
import { spawnProcess, spawnProcessSync } from "../utils/child-process.js";
|
|
220
|
+
import { parseGitUrl } from "../utils/git.js";
|
|
221
|
+
import { canonicalizePath, isLocalPath, markPathIgnoredByCloudSync, resolvePath } from "../utils/paths.js";
|
|
222
|
+
@@ -693,23 +693,29 @@ export class DefaultPackageManager {
|
|
223
|
+
const packageSources = this.dedupePackages(allPackages);
|
|
224
|
+
await this.resolvePackageSources(packageSources, accumulator, onMissing);
|
|
225
|
+
const globalBaseDir = this.agentDir;
|
|
226
|
+
- const projectBaseDir = join(this.cwd, CONFIG_DIR_NAME);
|
|
227
|
+
+ // Privateer patch: relative entries in project settings are resolved against
|
|
228
|
+
+ // every project config dir, `.privateer` first. Paths that don't exist under a
|
|
229
|
+
+ // given dir are skipped silently (collectFilesFromPaths), so a `.pi`-relative
|
|
230
|
+
+ // entry still resolves when `.privateer` exists but doesn't contain it.
|
|
231
|
+
+ const projectBaseDirs = this.projectBaseDirs();
|
|
232
|
+
for (const resourceType of RESOURCE_TYPES) {
|
|
233
|
+
const target = this.getTargetMap(accumulator, resourceType);
|
|
234
|
+
const globalEntries = (globalSettings[resourceType] ?? []);
|
|
235
|
+
const projectEntries = (projectSettings[resourceType] ?? []);
|
|
236
|
+
- this.resolveLocalEntries(projectEntries, resourceType, target, {
|
|
237
|
+
- source: "local",
|
|
238
|
+
- scope: "project",
|
|
239
|
+
- origin: "top-level",
|
|
240
|
+
- }, projectBaseDir);
|
|
241
|
+
+ for (const projectBaseDir of projectBaseDirs) {
|
|
242
|
+
+ this.resolveLocalEntries(projectEntries, resourceType, target, {
|
|
243
|
+
+ source: "local",
|
|
244
|
+
+ scope: "project",
|
|
245
|
+
+ origin: "top-level",
|
|
246
|
+
+ }, projectBaseDir);
|
|
247
|
+
+ }
|
|
248
|
+
this.resolveLocalEntries(globalEntries, resourceType, target, {
|
|
249
|
+
source: "local",
|
|
250
|
+
scope: "user",
|
|
251
|
+
origin: "top-level",
|
|
252
|
+
}, globalBaseDir);
|
|
253
|
+
}
|
|
254
|
+
- this.addAutoDiscoveredResources(accumulator, globalSettings, projectSettings, globalBaseDir, projectBaseDir);
|
|
255
|
+
+ this.addAutoDiscoveredResources(accumulator, globalSettings, projectSettings, globalBaseDir, projectBaseDirs);
|
|
256
|
+
return this.toResolvedPaths(accumulator);
|
|
257
|
+
}
|
|
258
|
+
async resolveExtensionSources(sources, options) {
|
|
259
|
+
@@ -1586,13 +1592,30 @@ export class DefaultPackageManager {
|
|
260
|
+
writeFileSync(ignorePath, "*\n!.gitignore\n", "utf-8");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
+ // --- Privateer patch: project config dirs -------------------------------
|
|
264
|
+
+ /**
|
|
265
|
+
+ * Every project config dir that exists under cwd, `.privateer` before `.pi`.
|
|
266
|
+
+ * Falls back to the write dir so callers always have a base to resolve against.
|
|
267
|
+
+ */
|
|
268
|
+
+ projectBaseDirs() {
|
|
269
|
+
+ const dirs = projectConfigDirs(this.cwd);
|
|
270
|
+
+ return dirs.length > 0 ? dirs : [projectConfigWriteDir(this.cwd)];
|
|
271
|
+
+ }
|
|
272
|
+
+ /** An existing project-scoped payload path, else the path to create under `.privateer`. */
|
|
273
|
+
+ projectPayloadPath(...rel) {
|
|
274
|
+
+ return resolveProjectConfigPath(this.cwd, ...rel);
|
|
275
|
+
+ }
|
|
276
|
+
+ // --- end Privateer patch -------------------------------------------------
|
|
277
|
+
getNpmInstallRoot(scope, temporary) {
|
|
278
|
+
if (temporary) {
|
|
279
|
+
return this.getTemporaryDir("npm");
|
|
280
|
+
}
|
|
281
|
+
if (scope === "project") {
|
|
282
|
+
this.assertProjectTrustedForScope(scope);
|
|
283
|
+
- return join(this.cwd, CONFIG_DIR_NAME, "npm");
|
|
284
|
+
+ // Privateer patch: reuse an existing `.pi/npm` tree rather than
|
|
285
|
+
+ // re-downloading everything under `.privateer/npm`; only a project with no
|
|
286
|
+
+ // payload tree at all gets one created under `.privateer`.
|
|
287
|
+
+ return this.projectPayloadPath("npm");
|
|
288
|
+
}
|
|
289
|
+
return join(this.agentDir, "npm");
|
|
290
|
+
}
|
|
291
|
+
@@ -1631,7 +1654,9 @@ export class DefaultPackageManager {
|
|
292
|
+
}
|
|
293
|
+
if (scope === "project") {
|
|
294
|
+
this.assertProjectTrustedForScope(scope);
|
|
295
|
+
- return join(this.cwd, CONFIG_DIR_NAME, "npm", "node_modules", source.name);
|
|
296
|
+
+ // Privateer patch: an already-installed package is found in whichever
|
|
297
|
+
+ // project dir holds it; a new one lands in the payload root above.
|
|
298
|
+
+ return this.projectPayloadPath("npm", "node_modules", source.name);
|
|
299
|
+
}
|
|
300
|
+
return join(this.agentDir, "npm", "node_modules", source.name);
|
|
301
|
+
}
|
|
302
|
+
@@ -1667,7 +1692,7 @@ export class DefaultPackageManager {
|
|
303
|
+
}
|
|
304
|
+
if (scope === "project") {
|
|
305
|
+
this.assertProjectTrustedForScope(scope);
|
|
306
|
+
- return join(this.cwd, CONFIG_DIR_NAME, "git");
|
|
307
|
+
+ return this.projectPayloadPath("git");
|
|
308
|
+
}
|
|
309
|
+
return join(this.agentDir, "git");
|
|
310
|
+
}
|
|
311
|
+
@@ -1690,7 +1715,7 @@ export class DefaultPackageManager {
|
|
312
|
+
getBaseDirForScope(scope) {
|
|
313
|
+
if (scope === "project") {
|
|
314
|
+
this.assertProjectTrustedForScope(scope);
|
|
315
|
+
- return join(this.cwd, CONFIG_DIR_NAME);
|
|
316
|
+
+ return this.projectBaseDirs()[0];
|
|
317
|
+
}
|
|
318
|
+
if (scope === "user") {
|
|
319
|
+
return this.agentDir;
|
|
320
|
+
@@ -1701,7 +1726,30 @@ export class DefaultPackageManager {
|
|
321
|
+
return resolvePath(input, this.cwd, { homeDir: getHomeDir(), trim: true });
|
|
322
|
+
}
|
|
323
|
+
resolvePathFromBase(input, baseDir) {
|
|
324
|
+
- return resolvePath(input, baseDir, { homeDir: getHomeDir(), trim: true });
|
|
325
|
+
+ const resolved = resolvePath(input, baseDir, { homeDir: getHomeDir(), trim: true });
|
|
326
|
+
+ // Privateer patch: a relative entry lives relative to the settings file that
|
|
327
|
+
+ // declared it, but the merged project scope has only ONE base — `.privateer`
|
|
328
|
+
+ // once it exists. Without this fallback a `./foo` package or skill declared in
|
|
329
|
+
+ // `.pi/settings.json` resolves to a non-existent `.privateer/foo` and silently
|
|
330
|
+
+ // disappears. Try the other project config dirs and take the first real hit;
|
|
331
|
+
+ // absolute entries and genuine typos are unaffected (both fall through).
|
|
332
|
+
+ if (existsSync(resolved)) {
|
|
333
|
+
+ return resolved;
|
|
334
|
+
+ }
|
|
335
|
+
+ const projectDirs = this.projectBaseDirs();
|
|
336
|
+
+ if (!projectDirs.some((dir) => resolve(dir) === resolve(baseDir))) {
|
|
337
|
+
+ return resolved;
|
|
338
|
+
+ }
|
|
339
|
+
+ for (const dir of projectDirs) {
|
|
340
|
+
+ if (resolve(dir) === resolve(baseDir)) {
|
|
341
|
+
+ continue;
|
|
342
|
+
+ }
|
|
343
|
+
+ const alternate = resolvePath(input, dir, { homeDir: getHomeDir(), trim: true });
|
|
344
|
+
+ if (existsSync(alternate)) {
|
|
345
|
+
+ return alternate;
|
|
346
|
+
+ }
|
|
347
|
+
+ }
|
|
348
|
+
+ return resolved;
|
|
349
|
+
}
|
|
350
|
+
collectPackageResources(packageRoot, accumulator, filter, metadata) {
|
|
351
|
+
if (filter) {
|
|
352
|
+
@@ -1847,19 +1895,24 @@ export class DefaultPackageManager {
|
|
353
|
+
this.addResource(target, f, metadata, enabledPaths.has(f));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
- addAutoDiscoveredResources(accumulator, globalSettings, projectSettings, globalBaseDir, projectBaseDir) {
|
|
357
|
+
+ // Privateer patch: `projectBaseDirs` is every project config dir that exists,
|
|
358
|
+
+ // highest precedence first (`.privateer` before `.pi`). Auto-discovery runs once
|
|
359
|
+
+ // per dir; same-named resources collide downstream where the first one wins, so
|
|
360
|
+
+ // `.privateer` supersedes `.pi`.
|
|
361
|
+
+ addAutoDiscoveredResources(accumulator, globalSettings, projectSettings, globalBaseDir, projectBaseDirs) {
|
|
362
|
+
const userMetadata = {
|
|
363
|
+
source: "auto",
|
|
364
|
+
scope: "user",
|
|
365
|
+
origin: "top-level",
|
|
366
|
+
baseDir: globalBaseDir,
|
|
367
|
+
};
|
|
368
|
+
- const projectMetadata = {
|
|
369
|
+
+ const projectMetadataFor = (baseDir) => ({
|
|
370
|
+
source: "auto",
|
|
371
|
+
scope: "project",
|
|
372
|
+
origin: "top-level",
|
|
373
|
+
- baseDir: projectBaseDir,
|
|
374
|
+
- };
|
|
375
|
+
+ baseDir,
|
|
376
|
+
+ });
|
|
377
|
+
+ const projectMetadata = projectMetadataFor(projectBaseDirs[0]);
|
|
378
|
+
const userOverrides = {
|
|
379
|
+
extensions: (globalSettings.extensions ?? []),
|
|
380
|
+
skills: (globalSettings.skills ?? []),
|
|
381
|
+
@@ -1878,12 +1931,12 @@ export class DefaultPackageManager {
|
|
382
|
+
prompts: join(globalBaseDir, "prompts"),
|
|
383
|
+
themes: join(globalBaseDir, "themes"),
|
|
384
|
+
};
|
|
385
|
+
- const projectDirs = {
|
|
386
|
+
- extensions: join(projectBaseDir, "extensions"),
|
|
387
|
+
- skills: join(projectBaseDir, "skills"),
|
|
388
|
+
- prompts: join(projectBaseDir, "prompts"),
|
|
389
|
+
- themes: join(projectBaseDir, "themes"),
|
|
390
|
+
- };
|
|
391
|
+
+ const projectDirsFor = (baseDir) => ({
|
|
392
|
+
+ extensions: join(baseDir, "extensions"),
|
|
393
|
+
+ skills: join(baseDir, "skills"),
|
|
394
|
+
+ prompts: join(baseDir, "prompts"),
|
|
395
|
+
+ themes: join(baseDir, "themes"),
|
|
396
|
+
+ });
|
|
397
|
+
const userAgentsSkillsDir = join(getHomeDir(), ".agents", "skills");
|
|
398
|
+
const projectTrusted = this.settingsManager.isProjectTrusted();
|
|
399
|
+
const projectAgentsSkillDirs = projectTrusted
|
|
400
|
+
@@ -1897,10 +1950,13 @@ export class DefaultPackageManager {
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
if (projectTrusted) {
|
|
404
|
+
- // Project extensions from .pi/
|
|
405
|
+
- addResources("extensions", collectAutoExtensionEntries(projectDirs.extensions), projectMetadata, projectOverrides.extensions, projectBaseDir);
|
|
406
|
+
- // Project skills from .pi/
|
|
407
|
+
- addResources("skills", collectAutoSkillEntries(projectDirs.skills, "pi"), projectMetadata, projectOverrides.skills, projectBaseDir);
|
|
408
|
+
+ for (const projectBaseDir of projectBaseDirs) {
|
|
409
|
+
+ const projectDirs = projectDirsFor(projectBaseDir);
|
|
410
|
+
+ // Project extensions from .privateer/ then .pi/
|
|
411
|
+
+ addResources("extensions", collectAutoExtensionEntries(projectDirs.extensions), projectMetadataFor(projectBaseDir), projectOverrides.extensions, projectBaseDir);
|
|
412
|
+
+ // Project skills from .privateer/ then .pi/
|
|
413
|
+
+ addResources("skills", collectAutoSkillEntries(projectDirs.skills, "pi"), projectMetadataFor(projectBaseDir), projectOverrides.skills, projectBaseDir);
|
|
414
|
+
+ }
|
|
415
|
+
}
|
|
416
|
+
// Project skills from .agents/ (each with its own baseDir)
|
|
417
|
+
for (const agentsSkillsDir of projectAgentsSkillDirs) {
|
|
418
|
+
@@ -1912,8 +1968,11 @@ export class DefaultPackageManager {
|
|
419
|
+
addResources("skills", collectAutoSkillEntries(agentsSkillsDir, "agents"), agentsMetadata, projectOverrides.skills, agentsBaseDir);
|
|
420
|
+
}
|
|
421
|
+
if (projectTrusted) {
|
|
422
|
+
- addResources("prompts", collectAutoPromptEntries(projectDirs.prompts), projectMetadata, projectOverrides.prompts, projectBaseDir);
|
|
423
|
+
- addResources("themes", collectAutoThemeEntries(projectDirs.themes), projectMetadata, projectOverrides.themes, projectBaseDir);
|
|
424
|
+
+ for (const projectBaseDir of projectBaseDirs) {
|
|
425
|
+
+ const projectDirs = projectDirsFor(projectBaseDir);
|
|
426
|
+
+ addResources("prompts", collectAutoPromptEntries(projectDirs.prompts), projectMetadataFor(projectBaseDir), projectOverrides.prompts, projectBaseDir);
|
|
427
|
+
+ addResources("themes", collectAutoThemeEntries(projectDirs.themes), projectMetadataFor(projectBaseDir), projectOverrides.themes, projectBaseDir);
|
|
428
|
+
+ }
|
|
429
|
+
}
|
|
430
|
+
// User extensions from ~/.pi/agent/
|
|
431
|
+
addResources("extensions", collectAutoExtensionEntries(userDirs.extensions), userMetadata, userOverrides.extensions, globalBaseDir);
|
|
432
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js
|
|
433
|
+
index c4f2509..a4402df 100644
|
|
434
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js
|
|
435
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/project-trust.js
|
|
436
|
+
@@ -1,8 +1,8 @@
|
|
437
|
+
-import { CONFIG_DIR_NAME } from "../config.js";
|
|
438
|
+
+import { formatProjectConfigDirNames } from "../config.js";
|
|
439
|
+
import { emitProjectTrustEvent } from "./extensions/runner.js";
|
|
440
|
+
import { getProjectTrustOptions, hasTrustRequiringProjectResources, } from "./trust-manager.js";
|
|
441
|
+
function formatProjectTrustPrompt(cwd) {
|
|
442
|
+
- return `Trust project folder?\n${cwd}\n\nThis allows pi to load ${CONFIG_DIR_NAME} settings and resources, install missing project packages, and execute project extensions.`;
|
|
443
|
+
+ return `Trust project folder?\n${cwd}\n\nThis allows pi to load ${formatProjectConfigDirNames()} settings and resources, install missing project packages, and execute project extensions.`;
|
|
444
|
+
}
|
|
445
|
+
async function selectProjectTrustOption(cwd, ctx) {
|
|
446
|
+
const options = getProjectTrustOptions(cwd, { includeSessionOnly: true });
|
|
447
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js
|
|
448
|
+
index ae4a7d4..a251c37 100644
|
|
449
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js
|
|
450
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/prompt-templates.js
|
|
451
|
+
@@ -1,6 +1,6 @@
|
|
452
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
453
|
+
import { basename, dirname, join, resolve, sep } from "path";
|
|
454
|
+
-import { CONFIG_DIR_NAME } from "../config.js";
|
|
455
|
+
+import { projectConfigDirCandidates } from "../config.js";
|
|
456
|
+
import { parseFrontmatter } from "../utils/frontmatter.js";
|
|
457
|
+
import { resolvePath } from "../utils/paths.js";
|
|
458
|
+
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
459
|
+
@@ -157,7 +157,8 @@ export function loadPromptTemplates(options) {
|
|
460
|
+
const includeDefaults = options.includeDefaults;
|
|
461
|
+
const templates = [];
|
|
462
|
+
const globalPromptsDir = join(resolvedAgentDir, "prompts");
|
|
463
|
+
- const projectPromptsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, "prompts");
|
|
464
|
+
+ // Privateer patch: `.privateer/prompts` then `.pi/prompts`.
|
|
465
|
+
+ const projectPromptsDirs = projectConfigDirCandidates(resolvedCwd).map((dir) => resolve(dir, "prompts"));
|
|
466
|
+
const isUnderPath = (target, root) => {
|
|
467
|
+
const normalizedRoot = resolve(root);
|
|
468
|
+
if (target === normalizedRoot) {
|
|
469
|
+
@@ -174,7 +175,8 @@ export function loadPromptTemplates(options) {
|
|
470
|
+
baseDir: globalPromptsDir,
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
- if (isUnderPath(resolvedPath, projectPromptsDir)) {
|
|
474
|
+
+ const projectPromptsDir = projectPromptsDirs.find((dir) => isUnderPath(resolvedPath, dir));
|
|
475
|
+
+ if (projectPromptsDir) {
|
|
476
|
+
return createSyntheticSourceInfo(resolvedPath, {
|
|
477
|
+
source: "local",
|
|
478
|
+
scope: "project",
|
|
479
|
+
@@ -188,7 +190,9 @@ export function loadPromptTemplates(options) {
|
|
480
|
+
};
|
|
481
|
+
if (includeDefaults) {
|
|
482
|
+
templates.push(...loadTemplatesFromDir(globalPromptsDir, getSourceInfo));
|
|
483
|
+
- templates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo));
|
|
484
|
+
+ for (const projectPromptsDir of projectPromptsDirs) {
|
|
485
|
+
+ templates.push(...loadTemplatesFromDir(projectPromptsDir, getSourceInfo));
|
|
486
|
+
+ }
|
|
487
|
+
}
|
|
488
|
+
// 3. Load explicit prompt paths
|
|
489
|
+
for (const rawPath of promptPaths) {
|
|
490
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js
|
|
491
|
+
index 74e36fe..7dfef45 100644
|
|
492
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js
|
|
493
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js
|
|
494
|
+
@@ -1,7 +1,7 @@
|
|
495
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
496
|
+
import { join, resolve, sep } from "node:path";
|
|
497
|
+
import chalk from "chalk";
|
|
498
|
+
-import { CONFIG_DIR_NAME } from "../config.js";
|
|
499
|
+
+import { findProjectConfigPath, projectConfigDirCandidates, projectConfigDirs } from "../config.js";
|
|
500
|
+
import { loadThemeFromPath } from "../modes/interactive/theme/theme.js";
|
|
501
|
+
import { canonicalizePath, isLocalPath, resolvePath } from "../utils/paths.js";
|
|
502
|
+
import { createEventBus } from "./event-bus.js";
|
|
503
|
+
@@ -570,12 +570,14 @@ export class DefaultResourceLoader {
|
|
504
|
+
join(this.agentDir, "themes"),
|
|
505
|
+
join(this.agentDir, "extensions"),
|
|
506
|
+
];
|
|
507
|
+
- const projectRoots = [
|
|
508
|
+
- join(this.cwd, CONFIG_DIR_NAME, "skills"),
|
|
509
|
+
- join(this.cwd, CONFIG_DIR_NAME, "prompts"),
|
|
510
|
+
- join(this.cwd, CONFIG_DIR_NAME, "themes"),
|
|
511
|
+
- join(this.cwd, CONFIG_DIR_NAME, "extensions"),
|
|
512
|
+
- ];
|
|
513
|
+
+ // Privateer patch: attribute paths under EITHER project config dir to the
|
|
514
|
+
+ // project scope, so a `.privateer/skills/…` file isn't reported as temporary.
|
|
515
|
+
+ const projectRoots = projectConfigDirCandidates(this.cwd).flatMap((dir) => [
|
|
516
|
+
+ join(dir, "skills"),
|
|
517
|
+
+ join(dir, "prompts"),
|
|
518
|
+
+ join(dir, "themes"),
|
|
519
|
+
+ join(dir, "extensions"),
|
|
520
|
+
+ ]);
|
|
521
|
+
for (const root of agentRoots) {
|
|
522
|
+
if (this.isUnderPath(normalizedPath, root)) {
|
|
523
|
+
return { path: filePath, source: "local", scope: "user", origin: "top-level", baseDir: root };
|
|
524
|
+
@@ -614,7 +616,11 @@ export class DefaultResourceLoader {
|
|
525
|
+
const themes = [];
|
|
526
|
+
const diagnostics = [];
|
|
527
|
+
if (includeDefaults) {
|
|
528
|
+
- const defaultDirs = [join(this.agentDir, "themes"), join(this.cwd, CONFIG_DIR_NAME, "themes")];
|
|
529
|
+
+ // Privateer patch: `.privateer/themes` first, then `.pi/themes`.
|
|
530
|
+
+ const defaultDirs = [
|
|
531
|
+
+ join(this.agentDir, "themes"),
|
|
532
|
+
+ ...projectConfigDirs(this.cwd).map((dir) => join(dir, "themes")),
|
|
533
|
+
+ ];
|
|
534
|
+
for (const dir of defaultDirs) {
|
|
535
|
+
this.loadThemesFromDir(dir, themes, diagnostics);
|
|
536
|
+
}
|
|
537
|
+
@@ -749,8 +755,9 @@ export class DefaultResourceLoader {
|
|
538
|
+
return { themes: Array.from(seen.values()), diagnostics };
|
|
539
|
+
}
|
|
540
|
+
discoverSystemPromptFile() {
|
|
541
|
+
- const projectPath = join(this.cwd, CONFIG_DIR_NAME, "SYSTEM.md");
|
|
542
|
+
- if (this.settingsManager.isProjectTrusted() && existsSync(projectPath)) {
|
|
543
|
+
+ // Privateer patch: `.privateer/SYSTEM.md` wins over `.pi/SYSTEM.md`.
|
|
544
|
+
+ const projectPath = findProjectConfigPath(this.cwd, "SYSTEM.md");
|
|
545
|
+
+ if (this.settingsManager.isProjectTrusted() && projectPath) {
|
|
546
|
+
return projectPath;
|
|
547
|
+
}
|
|
548
|
+
const globalPath = join(this.agentDir, "SYSTEM.md");
|
|
549
|
+
@@ -760,8 +767,9 @@ export class DefaultResourceLoader {
|
|
550
|
+
return undefined;
|
|
551
|
+
}
|
|
552
|
+
discoverAppendSystemPromptFile() {
|
|
553
|
+
- const projectPath = join(this.cwd, CONFIG_DIR_NAME, "APPEND_SYSTEM.md");
|
|
554
|
+
- if (this.settingsManager.isProjectTrusted() && existsSync(projectPath)) {
|
|
555
|
+
+ // Privateer patch: `.privateer/APPEND_SYSTEM.md` wins over `.pi/`.
|
|
556
|
+
+ const projectPath = findProjectConfigPath(this.cwd, "APPEND_SYSTEM.md");
|
|
557
|
+
+ if (this.settingsManager.isProjectTrusted() && projectPath) {
|
|
558
|
+
return projectPath;
|
|
559
|
+
}
|
|
560
|
+
const globalPath = join(this.agentDir, "APPEND_SYSTEM.md");
|
|
561
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
|
|
562
|
+
index 5910aac..f379c0f 100644
|
|
563
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
|
|
564
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js
|
|
565
|
+
@@ -2,7 +2,7 @@ import { randomUUID } from "crypto";
|
|
566
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
567
|
+
import { dirname, join } from "path";
|
|
568
|
+
import lockfile from "proper-lockfile";
|
|
569
|
+
-import { CONFIG_DIR_NAME, getAgentDir } from "../config.js";
|
|
570
|
+
+import { getAgentDir, projectConfigDirCandidates, projectConfigWriteDir } from "../config.js";
|
|
571
|
+
import { normalizePath, resolvePath } from "../utils/paths.js";
|
|
572
|
+
import { DEFAULT_HTTP_IDLE_TIMEOUT_MS, parseHttpIdleTimeoutMs } from "./http-dispatcher.js";
|
|
573
|
+
/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
|
|
574
|
+
@@ -40,14 +40,112 @@ function parseTimeoutSetting(value, settingName) {
|
|
575
|
+
}
|
|
576
|
+
return undefined;
|
|
577
|
+
}
|
|
578
|
+
+/**
|
|
579
|
+
+ * Privateer patch: drop the keys of `settings` that are byte-identical to `base`.
|
|
580
|
+
+ *
|
|
581
|
+
+ * Project settings are read as `.pi/settings.json` ⊕ `.privateer/settings.json`, but
|
|
582
|
+
+ * written back to `.privateer` alone. Without this reduction every save would copy the
|
|
583
|
+
+ * whole inherited `.pi` file into `.privateer` — two files claiming to own the same
|
|
584
|
+
+ * values, silently diverging the first time one is edited. Reducing keeps `.privateer`
|
|
585
|
+
+ * holding only what it actually overrides.
|
|
586
|
+
+ *
|
|
587
|
+
+ * Known limit (shared with stock Pi's global→project layering): a key can be
|
|
588
|
+
+ * overridden but not un-set — removing it from `.privateer` lets `.pi`'s value show
|
|
589
|
+
+ * through again.
|
|
590
|
+
+ */
|
|
591
|
+
+function subtractSettings(settings, base) {
|
|
592
|
+
+ const result = {};
|
|
593
|
+
+ for (const key of Object.keys(settings)) {
|
|
594
|
+
+ const value = settings[key];
|
|
595
|
+
+ const baseValue = base[key];
|
|
596
|
+
+ if (value === undefined)
|
|
597
|
+
+ continue;
|
|
598
|
+
+ if (JSON.stringify(value) === JSON.stringify(baseValue))
|
|
599
|
+
+ continue;
|
|
600
|
+
+ if (typeof value === "object" &&
|
|
601
|
+
+ value !== null &&
|
|
602
|
+
+ !Array.isArray(value) &&
|
|
603
|
+
+ typeof baseValue === "object" &&
|
|
604
|
+
+ baseValue !== null &&
|
|
605
|
+
+ !Array.isArray(baseValue)) {
|
|
606
|
+
+ const nested = subtractSettings(value, baseValue);
|
|
607
|
+
+ if (Object.keys(nested).length > 0) {
|
|
608
|
+
+ result[key] = nested;
|
|
609
|
+
+ }
|
|
610
|
+
+ continue;
|
|
611
|
+
+ }
|
|
612
|
+
+ result[key] = value;
|
|
613
|
+
+ }
|
|
614
|
+
+ return result;
|
|
615
|
+
+}
|
|
616
|
+
export class FileSettingsStorage {
|
|
617
|
+
globalSettingsPath;
|
|
618
|
+
projectSettingsPath;
|
|
619
|
+
+ // Privateer patch: lower-precedence project settings files (`.pi/settings.json`),
|
|
620
|
+
+ // highest precedence first. Read and merged underneath projectSettingsPath, never
|
|
621
|
+
+ // written to.
|
|
622
|
+
+ projectBaseSettingsPaths;
|
|
623
|
+
constructor(cwd, agentDir) {
|
|
624
|
+
const resolvedCwd = resolvePath(cwd);
|
|
625
|
+
const resolvedAgentDir = resolvePath(agentDir);
|
|
626
|
+
this.globalSettingsPath = join(resolvedAgentDir, "settings.json");
|
|
627
|
+
- this.projectSettingsPath = join(resolvedCwd, CONFIG_DIR_NAME, "settings.json");
|
|
628
|
+
+ // Privateer patch: writes always land in `.privateer/settings.json`.
|
|
629
|
+
+ this.projectSettingsPath = join(projectConfigWriteDir(resolvedCwd), "settings.json");
|
|
630
|
+
+ this.projectBaseSettingsPaths = projectConfigDirCandidates(resolvedCwd)
|
|
631
|
+
+ .map((dir) => join(dir, "settings.json"))
|
|
632
|
+
+ .filter((path) => path !== this.projectSettingsPath);
|
|
633
|
+
+ }
|
|
634
|
+
+ /**
|
|
635
|
+
+ * Privateer patch: the project scope is the MERGE of every project settings file
|
|
636
|
+
+ * that exists — `.pi/settings.json` first, `.privateer/settings.json` layered on
|
|
637
|
+
+ * top — presented to callers as one document so the rest of Pi is unchanged.
|
|
638
|
+
+ * Writes are reduced against the inherited base and land in `.privateer` only.
|
|
639
|
+
+ */
|
|
640
|
+
+ withProjectLock(fn) {
|
|
641
|
+
+ const path = this.projectSettingsPath;
|
|
642
|
+
+ let base = {};
|
|
643
|
+
+ let sawBase = false;
|
|
644
|
+
+ // Lowest precedence first, so higher-precedence files overwrite them.
|
|
645
|
+
+ for (const basePath of [...this.projectBaseSettingsPaths].reverse()) {
|
|
646
|
+
+ if (!existsSync(basePath))
|
|
647
|
+
+ continue;
|
|
648
|
+
+ const raw = readFileSync(basePath, "utf-8");
|
|
649
|
+
+ if (!raw)
|
|
650
|
+
+ continue;
|
|
651
|
+
+ // A malformed `.pi/settings.json` throws here exactly as it would in stock
|
|
652
|
+
+ // Pi, and is reported by the caller as a project settings load error.
|
|
653
|
+
+ base = deepMergeSettings(base, JSON.parse(raw));
|
|
654
|
+
+ sawBase = true;
|
|
655
|
+
+ }
|
|
656
|
+
+ const dir = dirname(path);
|
|
657
|
+
+ let release;
|
|
658
|
+
+ try {
|
|
659
|
+
+ const fileExists = existsSync(path);
|
|
660
|
+
+ if (fileExists) {
|
|
661
|
+
+ release = this.acquireLockSyncWithRetry(path);
|
|
662
|
+
+ }
|
|
663
|
+
+ const ownRaw = fileExists ? readFileSync(path, "utf-8") : undefined;
|
|
664
|
+
+ const merged = ownRaw ? deepMergeSettings(base, JSON.parse(ownRaw)) : base;
|
|
665
|
+
+ // `undefined` means "no project settings at all" — the signal stock callers
|
|
666
|
+
+ // use to fall back to an empty document.
|
|
667
|
+
+ const current = ownRaw === undefined && !sawBase ? undefined : JSON.stringify(merged, null, 2);
|
|
668
|
+
+ const next = fn(current);
|
|
669
|
+
+ if (next !== undefined) {
|
|
670
|
+
+ const reduced = subtractSettings(JSON.parse(next), base);
|
|
671
|
+
+ if (!existsSync(dir)) {
|
|
672
|
+
+ mkdirSync(dir, { recursive: true });
|
|
673
|
+
+ }
|
|
674
|
+
+ if (!release) {
|
|
675
|
+
+ release = this.acquireLockSyncWithRetry(path);
|
|
676
|
+
+ }
|
|
677
|
+
+ writeFileSync(path, JSON.stringify(reduced, null, 2), "utf-8");
|
|
678
|
+
+ }
|
|
679
|
+
+ }
|
|
680
|
+
+ finally {
|
|
681
|
+
+ if (release) {
|
|
682
|
+
+ release();
|
|
683
|
+
+ }
|
|
684
|
+
+ }
|
|
685
|
+
}
|
|
686
|
+
acquireLockSyncWithRetry(path) {
|
|
687
|
+
const maxAttempts = 10;
|
|
688
|
+
@@ -74,7 +172,13 @@ export class FileSettingsStorage {
|
|
689
|
+
throw lastError ?? new Error("Failed to acquire settings lock");
|
|
690
|
+
}
|
|
691
|
+
withLock(scope, fn) {
|
|
692
|
+
- const path = scope === "global" ? this.globalSettingsPath : this.projectSettingsPath;
|
|
693
|
+
+ // Privateer patch: the project scope spans two directories, so it needs its own
|
|
694
|
+
+ // read-merge / write-reduce path.
|
|
695
|
+
+ if (scope !== "global") {
|
|
696
|
+
+ this.withProjectLock(fn);
|
|
697
|
+
+ return;
|
|
698
|
+
+ }
|
|
699
|
+
+ const path = this.globalSettingsPath;
|
|
700
|
+
const dir = dirname(path);
|
|
701
|
+
let release;
|
|
702
|
+
try {
|
|
703
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js
|
|
704
|
+
index 4e7e784..7383a2b 100644
|
|
705
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js
|
|
706
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/skills.js
|
|
707
|
+
@@ -1,7 +1,7 @@
|
|
708
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
709
|
+
import ignore from "ignore";
|
|
710
|
+
import { basename, dirname, join, relative, resolve, sep } from "path";
|
|
711
|
+
-import { CONFIG_DIR_NAME, getAgentDir } from "../config.js";
|
|
712
|
+
+import { getAgentDir, projectConfigDirCandidates } from "../config.js";
|
|
713
|
+
import { parseFrontmatter } from "../utils/frontmatter.js";
|
|
714
|
+
import { canonicalizePath, resolvePath } from "../utils/paths.js";
|
|
715
|
+
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
716
|
+
@@ -328,10 +328,15 @@ export function loadSkills(options) {
|
|
717
|
+
}
|
|
718
|
+
if (includeDefaults) {
|
|
719
|
+
addSkills(loadSkillsFromDirInternal(join(resolvedAgentDir, "skills"), "user", true));
|
|
720
|
+
- addSkills(loadSkillsFromDirInternal(resolve(resolvedCwd, CONFIG_DIR_NAME, "skills"), "project", true));
|
|
721
|
+
+ // Privateer patch: `.privateer/skills` is added before `.pi/skills`, and the
|
|
722
|
+
+ // first skill to claim a name wins — so `.privateer` supersedes on a collision
|
|
723
|
+
+ // (the loser is reported as a normal collision diagnostic).
|
|
724
|
+
+ for (const projectDir of projectConfigDirCandidates(resolvedCwd)) {
|
|
725
|
+
+ addSkills(loadSkillsFromDirInternal(resolve(projectDir, "skills"), "project", true));
|
|
726
|
+
+ }
|
|
727
|
+
}
|
|
728
|
+
const userSkillsDir = join(resolvedAgentDir, "skills");
|
|
729
|
+
- const projectSkillsDir = resolve(resolvedCwd, CONFIG_DIR_NAME, "skills");
|
|
730
|
+
+ const projectSkillsDirs = projectConfigDirCandidates(resolvedCwd).map((dir) => resolve(dir, "skills"));
|
|
731
|
+
const isUnderPath = (target, root) => {
|
|
732
|
+
const normalizedRoot = resolve(root);
|
|
733
|
+
if (target === normalizedRoot) {
|
|
734
|
+
@@ -344,7 +349,7 @@ export function loadSkills(options) {
|
|
735
|
+
if (!includeDefaults) {
|
|
736
|
+
if (isUnderPath(resolvedPath, userSkillsDir))
|
|
737
|
+
return "user";
|
|
738
|
+
- if (isUnderPath(resolvedPath, projectSkillsDir))
|
|
739
|
+
+ if (projectSkillsDirs.some((dir) => isUnderPath(resolvedPath, dir)))
|
|
740
|
+
return "project";
|
|
741
|
+
}
|
|
742
|
+
return "path";
|
|
743
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js b/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js
|
|
744
|
+
index 17af938..6d97caa 100644
|
|
745
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js
|
|
746
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/core/trust-manager.js
|
|
747
|
+
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
748
|
+
import { homedir } from "node:os";
|
|
749
|
+
import { dirname, join } from "node:path";
|
|
750
|
+
import lockfile from "proper-lockfile";
|
|
751
|
+
-import { CONFIG_DIR_NAME } from "../config.js";
|
|
752
|
+
+import { projectConfigDirCandidates } from "../config.js";
|
|
753
|
+
import { canonicalizePath, resolvePath } from "../utils/paths.js";
|
|
754
|
+
const TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES = [
|
|
755
|
+
"settings.json",
|
|
756
|
+
@@ -150,9 +150,13 @@ export function hasTrustRequiringProjectResources(cwd) {
|
|
757
|
+
const homeDir = canonicalizePath(resolvePath(process.env.HOME || homedir()));
|
|
758
|
+
const userAgentsSkillsDir = join(homeDir, ".agents", "skills");
|
|
759
|
+
let currentDir = canonicalizePath(resolvePath(cwd));
|
|
760
|
+
- const configDir = join(currentDir, CONFIG_DIR_NAME);
|
|
761
|
+
- if (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {
|
|
762
|
+
- return true;
|
|
763
|
+
+ // Privateer patch: a project needs a trust decision if EITHER config dir carries
|
|
764
|
+
+ // trust-requiring resources — otherwise `.privateer/extensions` would execute
|
|
765
|
+
+ // without ever prompting.
|
|
766
|
+
+ for (const configDir of projectConfigDirCandidates(currentDir)) {
|
|
767
|
+
+ if (TRUST_REQUIRING_PROJECT_CONFIG_RESOURCES.some((entry) => existsSync(join(configDir, entry)))) {
|
|
768
|
+
+ return true;
|
|
769
|
+
+ }
|
|
770
|
+
}
|
|
771
|
+
while (true) {
|
|
772
|
+
const agentsSkillsDir = join(currentDir, ".agents", "skills");
|
|
773
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/main.js b/node_modules/@earendil-works/pi-coding-agent/dist/main.js
|
|
774
|
+
index fc62585..8b08615 100644
|
|
775
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/main.js
|
|
776
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/main.js
|
|
777
|
+
@@ -693,7 +693,58 @@ export async function main(args, options) {
|
|
778
|
+
if (exitCode !== 0) {
|
|
779
|
+
process.exitCode = exitCode;
|
|
780
|
+
}
|
|
781
|
+
+ // Privateer patch: one-shot runs must EXIT, not merely stop working.
|
|
782
|
+
+ //
|
|
783
|
+
+ // Stock Pi returns here and trusts the event loop to drain. It usually does —
|
|
784
|
+
+ // but a single ref'd resource anywhere in the process pins it open forever, and
|
|
785
|
+
+ // `pi -p` then hangs with the answer already printed, looking for all the world
|
|
786
|
+
+ // like a stuck model call.
|
|
787
|
+
+ //
|
|
788
|
+
+ // Measured on a normal Privateer install: with `-ne`, or an agent dir holding no
|
|
789
|
+
+ // extensions, one-shot runs exit. Load the moat and they never do. Bisecting the
|
|
790
|
+
+ // moat one extension at a time puts it on privateer-gate; the other nine exit
|
|
791
|
+
+ // cleanly. What the gate retains could not be identified from JS, because while
|
|
792
|
+
+ // the process sits there BOTH process._getActiveHandles() and
|
|
793
|
+
+ // process.getActiveResourcesInfo() come back empty — whatever holds libuv's loop
|
|
794
|
+
+ // open is native, so nothing in JS can find it, let alone close it.
|
|
795
|
+
+ //
|
|
796
|
+
+ // It is not only ours to fix, either: extensions are arbitrary third-party code.
|
|
797
|
+
+ // A user package on this same machine (context-mode) spawns MCP stdio servers on
|
|
798
|
+
+ // before_agent_start, which is its own reason a one-shot run may never end.
|
|
799
|
+
+ //
|
|
800
|
+
+ // Exiting here is safe because the work is genuinely finished — runPrintMode's
|
|
801
|
+
+ // finally block has already awaited disposeRuntime() (which runs extension
|
|
802
|
+
+ // session_shutdown handlers, so session indexing and flushes complete) and
|
|
803
|
+
+ // flushRawStdout(). All that remains is the exit itself. We still drain stdout
|
|
804
|
+
+ // and stderr first, because with a pipe (`privateer -p … | tail`) the last write
|
|
805
|
+
+ // may be buffered, and a bare process.exit() would truncate it.
|
|
806
|
+
+ //
|
|
807
|
+
+ // Interactive and RPC modes are untouched: the TUI owns its own teardown, and
|
|
808
|
+
+ // an RPC server is supposed to stay up.
|
|
809
|
+
+ await flushStdioAndExit(process.exitCode ?? 0);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
+// Privateer patch: flush stdout/stderr, then exit — even if something in the process
|
|
814
|
+
+// (native addon, extension, stray timer) would otherwise keep the event loop alive.
|
|
815
|
+
+// The 2s backstop covers the pathological case where a stream never drains; an
|
|
816
|
+
+// unref'd timer so it can never itself delay a clean exit.
|
|
817
|
+
+function flushStdioAndExit(code) {
|
|
818
|
+
+ return new Promise(() => {
|
|
819
|
+
+ let pending = 2;
|
|
820
|
+
+ const done = () => {
|
|
821
|
+
+ if (--pending === 0)
|
|
822
|
+
+ process.exit(code);
|
|
823
|
+
+ };
|
|
824
|
+
+ const backstop = setTimeout(() => process.exit(code), 2000);
|
|
825
|
+
+ backstop.unref?.();
|
|
826
|
+
+ try {
|
|
827
|
+
+ process.stdout.write("", done);
|
|
828
|
+
+ process.stderr.write("", done);
|
|
829
|
+
+ }
|
|
830
|
+
+ catch {
|
|
831
|
+
+ process.exit(code);
|
|
832
|
+
+ }
|
|
833
|
+
+ });
|
|
834
|
+
+}
|
|
835
|
+
//# sourceMappingURL=main.js.map
|
|
836
|
+
|
|
837
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js b/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
|
|
838
|
+
index e3a5ccf..a561a8a 100644
|
|
839
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
|
|
840
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/migrations.js
|
|
841
|
+
@@ -4,7 +4,7 @@
|
|
842
|
+
import chalk from "chalk";
|
|
843
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
844
|
+
import { dirname, join } from "path";
|
|
845
|
+
-import { CONFIG_DIR_NAME, getAgentDir, getBinDir } from "./config.js";
|
|
846
|
+
+import { getAgentDir, getBinDir, projectConfigDirs } from "./config.js";
|
|
847
|
+
import { migrateKeybindingsConfig } from "./core/keybindings.js";
|
|
848
|
+
const MIGRATION_GUIDE_URL = "https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/CHANGELOG.md#extensions-migration";
|
|
849
|
+
const EXTENSIONS_DOC_URL = "https://github.com/earendil-works/pi-mono/blob/main/packages/coding-agent/docs/extensions.md";
|
|
850
|
+
@@ -230,14 +230,17 @@ function checkDeprecatedExtensionDirs(baseDir, label) {
|
|
851
|
+
*/
|
|
852
|
+
function migrateExtensionSystem(cwd) {
|
|
853
|
+
const agentDir = getAgentDir();
|
|
854
|
+
- const projectDir = join(cwd, CONFIG_DIR_NAME);
|
|
855
|
+
+ // Privateer patch: migrate/scan every project config dir that exists.
|
|
856
|
+
+ const projectDirs = projectConfigDirs(cwd);
|
|
857
|
+
// Migrate commands/ to prompts/
|
|
858
|
+
migrateCommandsToPrompts(agentDir, "Global");
|
|
859
|
+
- migrateCommandsToPrompts(projectDir, "Project");
|
|
860
|
+
+ for (const projectDir of projectDirs) {
|
|
861
|
+
+ migrateCommandsToPrompts(projectDir, "Project");
|
|
862
|
+
+ }
|
|
863
|
+
// Check for deprecated directories
|
|
864
|
+
const warnings = [
|
|
865
|
+
...checkDeprecatedExtensionDirs(agentDir, "Global"),
|
|
866
|
+
- ...checkDeprecatedExtensionDirs(projectDir, "Project"),
|
|
867
|
+
+ ...projectDirs.flatMap((projectDir) => checkDeprecatedExtensionDirs(projectDir, "Project")),
|
|
868
|
+
];
|
|
869
|
+
return warnings;
|
|
870
|
+
}
|
|
871
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js
|
|
872
|
+
index 3355a47..8c6bba9 100644
|
|
873
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js
|
|
874
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/config-selector.js
|
|
875
|
+
@@ -4,7 +4,7 @@
|
|
876
|
+
import { homedir } from "node:os";
|
|
877
|
+
import { basename, dirname, join, relative } from "node:path";
|
|
878
|
+
import { Container, getKeybindings, Input, matchesKey, Spacer, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
|
|
879
|
+
-import { CONFIG_DIR_NAME } from "../../../config.js";
|
|
880
|
+
+import { formatProjectConfigDirNames, projectConfigDirs, projectConfigWriteDir } from "../../../config.js";
|
|
881
|
+
import { theme } from "../theme/theme.js";
|
|
882
|
+
import { DynamicBorder } from "./dynamic-border.js";
|
|
883
|
+
import { rawKeyHint } from "./keybinding-hints.js";
|
|
884
|
+
@@ -41,7 +41,7 @@ function getGroupLabel(metadata, agentDir) {
|
|
885
|
+
? `User (${formatBaseDir(metadata.baseDir)})`
|
|
886
|
+
: `Project (${formatBaseDir(metadata.baseDir)})`;
|
|
887
|
+
}
|
|
888
|
+
- return metadata.scope === "user" ? `User (${formatBaseDir(agentDir)})` : `Project (${CONFIG_DIR_NAME}/)`;
|
|
889
|
+
+ return metadata.scope === "user" ? `User (${formatBaseDir(agentDir)})` : `Project (${formatProjectConfigDirNames()})`;
|
|
890
|
+
}
|
|
891
|
+
return metadata.scope === "user" ? "User settings" : "Project settings";
|
|
892
|
+
}
|
|
893
|
+
@@ -458,7 +458,12 @@ class ResourceList {
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
getTopLevelBaseDir(scope) {
|
|
897
|
+
- return scope === "project" ? join(this.cwd, CONFIG_DIR_NAME) : this.agentDir;
|
|
898
|
+
+ // Privateer patch: patterns are made relative to the highest-precedence project
|
|
899
|
+
+ // dir that exists (`.privateer` when present), else where one would be created.
|
|
900
|
+
+ if (scope !== "project") {
|
|
901
|
+
+ return this.agentDir;
|
|
902
|
+
+ }
|
|
903
|
+
+ return projectConfigDirs(this.cwd)[0] ?? projectConfigWriteDir(this.cwd);
|
|
904
|
+
}
|
|
905
|
+
getResourcePattern(item) {
|
|
906
|
+
const scope = item.metadata.scope;
|
|
99
907
|
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
|
|
100
|
-
index 5d65200..
|
|
908
|
+
index 5d65200..f827cd7 100644
|
|
101
909
|
--- a/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
|
|
102
910
|
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js
|
|
103
|
-
@@ -
|
|
911
|
+
@@ -10,7 +10,7 @@ import { getProviders, } from "@earendil-works/pi-ai/compat";
|
|
912
|
+
import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@earendil-works/pi-tui";
|
|
913
|
+
import chalk from "chalk";
|
|
914
|
+
import { spawn, spawnSync } from "child_process";
|
|
915
|
+
-import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
|
|
916
|
+
+import { APP_NAME, APP_TITLE, formatProjectConfigDirNames, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
|
|
917
|
+
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
918
|
+
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
919
|
+
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
920
|
+
@@ -110,7 +110,12 @@ export function formatResumeCommand(sessionManager) {
|
|
921
|
+
const sessionFile = sessionManager.getSessionFile();
|
|
922
|
+
if (!sessionFile || !fs.existsSync(sessionFile))
|
|
923
|
+
return undefined;
|
|
924
|
+
- const args = [APP_NAME];
|
|
925
|
+
+ // Privateer rename: APP_NAME is "pi", and no `pi` binary exists on a Privateer
|
|
926
|
+
+ // machine — the published bin is `privateer`. So this hint, whose entire job is to
|
|
927
|
+
+ // be copy-pasted, resolved to "bash: pi: command not found". The launcher exports
|
|
928
|
+
+ // PRIVATEER_CMD with the name it was actually invoked under (npm's bin symlink, or
|
|
929
|
+
+ // a user's own symlink of bin/privateer-tui); fall back to the published name.
|
|
930
|
+
+ const args = [process.env.PRIVATEER_CMD || "privateer"];
|
|
931
|
+
if (!sessionManager.usesDefaultSessionDir()) {
|
|
932
|
+
args.push("--session-dir", quoteIfNeeded(sessionManager.getSessionDir()));
|
|
933
|
+
}
|
|
934
|
+
@@ -301,9 +306,15 @@ export class InteractiveMode {
|
|
104
935
|
}
|
|
105
936
|
getBuiltInCommandConflictDiagnostics(extensionRunner) {
|
|
106
937
|
const builtinNames = new Set(BUILTIN_SLASH_COMMANDS.map((command) => command.name));
|
|
@@ -117,7 +948,7 @@ index 5d65200..0a08971 100644
|
|
|
117
948
|
.map((command) => ({
|
|
118
949
|
type: "warning",
|
|
119
950
|
message: command.invocationName === command.name
|
|
120
|
-
@@ -2042,7 +
|
|
951
|
+
@@ -2042,7 +2053,17 @@ export class InteractiveMode {
|
|
121
952
|
if (text === "/model" || text.startsWith("/model ")) {
|
|
122
953
|
const searchTerm = text.startsWith("/model ") ? text.slice(7).trim() : undefined;
|
|
123
954
|
this.editor.setText("");
|
|
@@ -136,7 +967,7 @@ index 5d65200..0a08971 100644
|
|
|
136
967
|
return;
|
|
137
968
|
}
|
|
138
969
|
if (text === "/export" || text.startsWith("/export ")) {
|
|
139
|
-
@@ -2105,14 +
|
|
970
|
+
@@ -2105,14 +2126,44 @@ export class InteractiveMode {
|
|
140
971
|
this.editor.setText("");
|
|
141
972
|
return;
|
|
142
973
|
}
|
|
@@ -184,3 +1015,43 @@ index 5d65200..0a08971 100644
|
|
|
184
1015
|
return;
|
|
185
1016
|
}
|
|
186
1017
|
if (text === "/new") {
|
|
1018
|
+
@@ -2650,7 +2701,7 @@ export class InteractiveMode {
|
|
1019
|
+
if (this.chatContainer.children.length > 0) {
|
|
1020
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
1021
|
+
}
|
|
1022
|
+
- this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart pi.`), 1, 0));
|
|
1023
|
+
+ this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${formatProjectConfigDirNames()} resources and packages are ignored. Use /trust to save a trust decision, then restart ${process.env.PRIVATEER_CMD || APP_NAME}.`), 1, 0));
|
|
1024
|
+
}
|
|
1025
|
+
async getUserInput() {
|
|
1026
|
+
const queuedInput = this.pendingUserInputs.shift();
|
|
1027
|
+
diff --git a/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js b/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js
|
|
1028
|
+
index f81b4b4..9712a37 100644
|
|
1029
|
+
--- a/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js
|
|
1030
|
+
+++ b/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js
|
|
1031
|
+
@@ -2,7 +2,7 @@ import { Markdown } from "@earendil-works/pi-tui";
|
|
1032
|
+
import chalk from "chalk";
|
|
1033
|
+
import { selectConfig } from "./cli/config-selector.js";
|
|
1034
|
+
import { createProjectTrustContext } from "./cli/project-trust.js";
|
|
1035
|
+
-import { APP_NAME, CONFIG_DIR_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, VERSION, } from "./config.js";
|
|
1036
|
+
+import { APP_NAME, detectInstallMethod, getAgentDir, getPackageDir, getSelfUpdateCommand, getSelfUpdateUnavailableInstruction, PACKAGE_NAME, PROJECT_CONFIG_DIR_NAMES, VERSION, } from "./config.js";
|
|
1037
|
+
import { DefaultPackageManager } from "./core/package-manager.js";
|
|
1038
|
+
import { resolveProjectTrusted } from "./core/project-trust.js";
|
|
1039
|
+
import { DefaultResourceLoader } from "./core/resource-loader.js";
|
|
1040
|
+
@@ -57,7 +57,7 @@ function printPackageCommandHelp(command) {
|
|
1041
|
+
Install a package and add it to settings.
|
|
1042
|
+
|
|
1043
|
+
Options:
|
|
1044
|
+
- -l, --local Install project-locally (${CONFIG_DIR_NAME}/settings.json)
|
|
1045
|
+
+ -l, --local Install project-locally (${PROJECT_CONFIG_DIR_NAMES[0]}/settings.json)
|
|
1046
|
+
-a, --approve Trust project-local files for this command
|
|
1047
|
+
-na, --no-approve Ignore project-local files for this command
|
|
1048
|
+
|
|
1049
|
+
@@ -78,7 +78,7 @@ Remove a package and its source from settings.
|
|
1050
|
+
Alias: ${APP_NAME} uninstall <source> [-l]
|
|
1051
|
+
|
|
1052
|
+
Options:
|
|
1053
|
+
- -l, --local Remove from project settings (${CONFIG_DIR_NAME}/settings.json)
|
|
1054
|
+
+ -l, --local Remove from project settings (${PROJECT_CONFIG_DIR_NAMES[0]}/settings.json)
|
|
1055
|
+
-a, --approve Trust project-local files for this command
|
|
1056
|
+
-na, --no-approve Ignore project-local files for this command
|
|
1057
|
+
|