mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
Merge branch 'my-pi-worktree1'
# Conflicts: # AGENTS.md
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
- `pi-subagents/`:提供 Claude Code 风格的隔离子 Agent、后台执行、steering、resume、嵌套代理与自定义 Agent。
|
||||
- 上游来源:<https://github.com/tintinweb/pi-subagents>
|
||||
- 初始导入快照:`3f9d35cd078d18a141eb5a6d8f4fc5010d756280`(`v0.18.0`)。
|
||||
- 该目录从明确 tag 导入并由本仓库直接维护,不使用 npm 预编译产物,不是 submodule,也不保留上游 `.pi` 状态;当前通过本地 `file:` 根依赖和 `extensions/subagents.ts` 默认加载,已实现权限 child lifecycle、根 authority `ask` 转发、根 transcript auto-review 身份桥接和 exact-path mandatory permission wrapper,3 个根编排工具由 Tool Search 的 `subagents` 组按需加载。
|
||||
- 该目录从明确 tag 导入并由本仓库直接维护,不使用 npm 预编译产物,不是 submodule,也不保留上游 `.pi` 状态;源码、测试、wrapper 与权限集成实现继续保留,但当前不属于根组合包运行时依赖,`extensions/subagents.ts` 也不在默认扩展加载列表中。
|
||||
- `extensions/fff-override.ts`:强制使用 FFF 官方 `override` 模式,统一接管 `find`、`grep`、`multi_grep` 和 FFF 的 `@` 补全;显式 CLI flag 仍遵循 FFF 官方优先级。
|
||||
- `extensions/tavily-override.ts`:加载 `@tavily/pi-extension`,把上游工具重命名为 `tavily_web_search` / `tavily_web_fetch`,并同步改写 label 与工具提示中的内部名称。
|
||||
- `@keenable/pi-search`:加载 `keenable_search` / `keenable_fetch` 及其 skill,默认 keyless,`KEENABLE_API_KEY` 仅用于提高速率限制。
|
||||
@@ -66,7 +66,7 @@
|
||||
- 初始导入快照:`f1d0efd24a7f4ae99d19e10c5f4c3770a3bdd845`(`@firstpick/pi-extension-codex-fast-mode@0.1.1`)。
|
||||
- 该目录从上游源码仓库导入并由本仓库直接维护,不使用 npm 预编译产物,不是 submodule,也不保留嵌套 `.git`。
|
||||
- `pi-condense@2.9.1`:总结已完成的工具调用批次,以短 stub 替换历史原始输出,并通过 `context_tree_query` 按需恢复;组合包在用户尚未配置 `contextPrune.enabled` 时默认开启。
|
||||
- 根包还固定安装 `@tavily/pi-extension@0.1.2`、`@keenable/pi-search@0.1.2`、`pi-hashline-edit@0.8.3`、`typescript-language-server@5.3.0`、`typescript@6.0.3` 和 `pi-context-view`;`@firstpick/pi-extension-codex-fast-mode`、`@gotgenes/pi-permission-system`、`@smoose/pi-notify`、`@tintinweb/pi-subagents`、`pi-ask-user`、`pi-chrome`、`pi-minimal-footer`、`pi-ssh` 与 `pi-tool-search` 分别指向仓库内对应目录的本地 `file:` 依赖。
|
||||
- 根包还固定安装 `@tavily/pi-extension@0.1.2`、`@keenable/pi-search@0.1.2`、`pi-hashline-edit@0.8.3`、`typescript-language-server@5.3.0`、`typescript@6.0.3` 和 `pi-context-view`;`@firstpick/pi-extension-codex-fast-mode`、`@gotgenes/pi-permission-system`、`@smoose/pi-notify`、`pi-ask-user`、`pi-chrome`、`pi-minimal-footer`、`pi-ssh` 与 `pi-tool-search` 分别指向仓库内对应目录的本地 `file:` 依赖;`pi-subagents/` 仅保留源码,不作为根运行时依赖。
|
||||
- `install.sh`:先安装根组合包,再交互检查 Hippo Memory CLI、Kitty/Solarized Dark、Oh My Zsh/Powerlevel10k/Zsh 插件、CodeGraph、Kotlin LSP、Java 21+ 和 JDT LS;缺失项目只在用户明确选择 `Y` 后安装或配置。Hippo 项目目录与 `hippo init` 始终由用户自行决定。Powerlevel10k 默认配置来自仓库内置的 `config/p10k.zsh`(当前 Rainbow/ASCII 单行紧凑主题)。
|
||||
- `update.sh`:先通过 `pi update` 升级根组合包,再只升级当前已安装的终端环境和机器级依赖;未安装项直接跳过。不得在 `pi update` 前预清理安装缓存;仅当默认 Git 来源首次升级失败、Pi 的精确中断标记存在、canonical checkout 根目录和 `origin` 均通过身份校验时,才允许对该缓存执行最多 4 次连续 `git clean -fdx`,随后只重试一次 `pi update`,由 Pi 自己恢复依赖和清除标记;自定义来源、标记缺失或身份不匹配必须保持 fail-fast。升级成功后只能调用可定位的更新后组合包 Node helper 检查 Pi agent `settings.json`:只有 `toolSearch` 完整精确匹配旧版 `3` 组 / `20` 工具默认快照时,才创建从生成起即为权限 `600` 的时间戳备份、以同目录临时文件原子迁移到 `5` / `28` 并写入 `bundleDefaultsVersion: 2`;任何自定义或无效配置必须保留,提交前检测到内容变化必须中止。无法从 `PI_PACKAGE_SOURCE` 定位更新后 helper 时必须保留配置并报告失败,不能回退到可能陈旧的脚本目录 helper。其他组件升级前先查询并比较本地与远端版本,只有版本不同时才下载或替换;Powerlevel10k 配置优先从 `pi update` 后的已安装组合包读取,并与 `.zshrc` 受管块一起按内容比较后增量同步。
|
||||
- `uninstall.sh`:先移除根组合包;若检测到 Hippo Memory CLI,再明确询问是否卸载确认属于 npm 全局安装的 `hippo-memory`,默认保留且始终不删除 `.hippo/` 或用户记忆数据。其他共享终端环境和机器工具不卸载。
|
||||
@@ -189,9 +189,9 @@
|
||||
|
||||
- 扩展入口是 `pi-subagents/src/index.ts`,测试保留在该目录的 `test/`,来源记录是 `pi-subagents/UPSTREAM.md`。
|
||||
- 同步上游必须从明确 tag/commit 移植并保留 README、CHANGELOG、SECURITY、作者和 LICENSE;不得导入嵌套 `.git`、上游 `.pi` 状态、`node_modules`、覆盖率或构建产物。
|
||||
- 根包必须通过 `file:./pi-subagents` 提供运行时依赖,并只通过 `extensions/subagents.ts` wrapper 直接加载仓库源码;不得绕过 wrapper 直接列出上游入口,不得同时安装或加载 npm 预编译入口,也不得把 Pi host runtime 作为组合包开发依赖带入根安装。
|
||||
- 根包当前必须保持禁用 `pi-subagents`:不得声明 `file:./pi-subagents` 或 npm 运行时依赖,也不得在 `pi.extensions` 中加载 `extensions/subagents.ts` 或上游入口;源码、测试、来源记录和 wrapper 继续保留,且不得把 Pi host runtime 作为组合包开发依赖带入根安装。
|
||||
- 子会话必须在 `bindExtensions()` 前注册到 `pi-permission-system` 的 process-global child registry,并在真实 dispose 时清理;resume、并发和嵌套 Agent 必须维持正确 parent/child session identity。
|
||||
- `extensions/subagents.ts` 是组合包配置入口:它只能把组合包内权威 `extensions/permission-system.ts` 的 canonical absolute path 作为 mandatory extension 注入。Agent frontmatter 的 `extensions: false`、`isolated` 和 `exclude_extensions` 均不得移除该 lifecycle;路径缺失、被同名其他路径冒充或 reload 后未精确命中必须在创建 AgentSession 前 fail-closed。
|
||||
- `extensions/subagents.ts` 是当前禁用但保留的组合包 wrapper:若以后经明确决定恢复加载,它只能把组合包内权威 `extensions/permission-system.ts` 的 canonical absolute path 作为 mandatory extension 注入。Agent frontmatter 的 `extensions: false`、`isolated` 和 `exclude_extensions` 均不得移除该 lifecycle;路径缺失、被同名其他路径冒充或 reload 后未精确命中必须在创建 AgentSession 前 fail-closed。
|
||||
- mandatory extension 只强制绑定 handlers,不自动开放它注册的工具;工具可见性仍由 Agent 的正常 extensions/tools 策略决定。mandatory 列表不得从 Agent Markdown 或项目配置读取。
|
||||
- `ask` 只能由 auto-review 在 delegation envelope 内明确 `allow` 时自动批准;reviewer `defer`、异常或不在 envelope 内必须转发父会话人工 authority,不得因子会话无 UI 静默放宽。父模型生成的子 Agent prompt 不得伪装成人类直接授权证据。
|
||||
- 子 Agent 转发的 access facts 必须先由根 authority 的 `SessionRules` 匹配;父会话 whole-session grant 可覆盖语义一致的后代操作,普通单次批准不得复制为可重复消费的子 Agent grant。auto-review 必须运行在根会话并只使用根 transcript 的可信用户证据。
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
- 本仓库维护的 `pi-chrome`:从上游 `0.15.46` 源码导入,通过显式会话授权接入用户当前已登录的 Chrome Profile;大体积页面快照写入 Session 临时文件并交给 Context Mode 隔离分析,21 个 `chrome_*` 工具仍由 Tool Search 按工作流加载。
|
||||
- 本仓库维护的 `pi-permission-auto-review`:作为 `pi-permission-system` authorizer,使用 Codex Guardian 风格策略自动复核 `ask` 请求。
|
||||
- 本仓库维护的 `pi-permission-system`:从 `@gotgenes/pi-permission-system@26.2.1` 源码导入,负责工具、路径、MCP、硬拒绝和兜底权限基线。
|
||||
- 本仓库维护的 `pi-subagents`:从 `@tintinweb/pi-subagents@0.18.0` 源码导入并默认加载,提供隔离子 Agent、后台执行、steering、resume、嵌套代理与自定义 Agent;通过 mandatory permission wrapper 接入权限 child lifecycle、根 authority `ask` 转发和根 transcript auto-review 身份桥接,其 3 个编排工具由 Tool Search 的 `subagents` 组按需加载。
|
||||
- 本仓库保留维护的 `pi-subagents` 源码及权限集成实现,但当前已从根组合包依赖和默认扩展加载列表中移除,不会随核心包启用。
|
||||
- 本仓库维护的 `pi-ssh`:通过纯 Node `ssh2` 持久连接、显式 `ssh_cd` 远端工作区切换、SFTP 与自适应有界搜索提供独立的远端工具;Agent 只在用户明确指定已导入主机及具体任务后发起受 AutoReview 复核的连接。
|
||||
- `extensions/tool-routing.ts`:保留 Pi 默认系统提示词,并按当前激活工具追加简短的工具与搜索路由规则;提供 `/dump-system-prompt` 导出当前有效提示词。
|
||||
|
||||
|
||||
Generated
+3
-1951
File diff suppressed because it is too large
Load Diff
@@ -65,7 +65,6 @@
|
||||
"./extensions/condense.ts",
|
||||
"./pi-permission-auto-review/index.ts",
|
||||
"./extensions/permission-system.ts",
|
||||
"./extensions/subagents.ts",
|
||||
"./pi-ssh/index.ts",
|
||||
"./pi-ask-user/index.ts",
|
||||
"./pi-chrome/extensions/chrome-profile-bridge/index.ts",
|
||||
@@ -86,7 +85,6 @@
|
||||
"@ogulcancelik/pi-minimal-footer": "file:./pi-minimal-footer",
|
||||
"@smoose/pi-notify": "file:./pi-notify",
|
||||
"@tavily/pi-extension": "0.1.2",
|
||||
"@tintinweb/pi-subagents": "file:./pi-subagents",
|
||||
"context-mode": "1.0.169",
|
||||
"jiti": "2.7.0",
|
||||
"pi-ask-user": "file:./pi-ask-user",
|
||||
|
||||
@@ -6,7 +6,7 @@ A locally maintained Pi extension that consolidates the upstream `question.ts` a
|
||||
|
||||
The extension registers `ask_user_question`. The model can present one to eight questions in one sequential TUI interaction:
|
||||
|
||||
- `select`: one to eight stable `{ value, label, description? }` choices, with an optional free-form choice;
|
||||
- `select`: one to eight stable `{ value, label, description? }` choices, always followed by a free-form choice that the model cannot disable;
|
||||
- `text`: a free-form Editor answer;
|
||||
- `required: false`: lets the user explicitly skip the question;
|
||||
- multiple questions: progress tabs plus a final review page.
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
|
||||
Initial reference snapshot: `dcd461925db2edf69a43c8135db1180d418afd54` (`main`, inspected 2026-08-24).
|
||||
|
||||
The upstream `qna.ts` command is intentionally not included: this package only supports model-initiated questions. Local changes consolidate single and multi-question flows into one tool, add text questions, validation, bounded schemas, cancellation/abort handling, width-aware rendering, and IME focus propagation.
|
||||
The upstream `qna.ts` command is intentionally not included: this package only supports model-initiated questions. Local changes consolidate single and multi-question flows into one tool, add text questions, keep a user-controlled free-form escape hatch on every select question, validation, bounded schemas, cancellation/abort handling, width-aware rendering, and IME focus propagation.
|
||||
|
||||
The copied MIT license and original copyright notice are retained in `LICENSE`.
|
||||
|
||||
@@ -45,9 +45,6 @@ export function normalizeQuestions(input: readonly QuestionInput[]): NormalizedQ
|
||||
throw new Error(`questions[${index}].type must be select or text`);
|
||||
}
|
||||
|
||||
if (question.allowCustom !== undefined && typeof question.allowCustom !== "boolean") {
|
||||
throw new Error(`questions[${index}].allowCustom must be a boolean`);
|
||||
}
|
||||
if (question.required !== undefined && typeof question.required !== "boolean") {
|
||||
throw new Error(`questions[${index}].required must be a boolean`);
|
||||
}
|
||||
@@ -77,7 +74,6 @@ export function normalizeQuestions(input: readonly QuestionInput[]): NormalizedQ
|
||||
prompt,
|
||||
type: question.type,
|
||||
options,
|
||||
allowCustom: question.type === "select" && question.allowCustom !== false,
|
||||
required: question.required !== false,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -20,9 +20,8 @@ const QuestionSchema = Type.Object({
|
||||
type: Type.String({ enum: ["select", "text"], description: "select for choices; text for a free-form answer" }),
|
||||
options: Type.Optional(Type.Array(OptionSchema, {
|
||||
maxItems: MAX_OPTIONS,
|
||||
description: "Required and non-empty for select questions; omit for text questions",
|
||||
description: "Required and non-empty for select questions; omit for text questions. The tool always appends a free-form answer choice.",
|
||||
})),
|
||||
allowCustom: Type.Optional(Type.Boolean({ description: "For select questions, append a free-form answer choice (default: true)" })),
|
||||
required: Type.Optional(Type.Boolean({ description: "Whether the question must be answered (default: true); optional questions can be skipped" })),
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ export interface QuestionInput {
|
||||
prompt: string;
|
||||
type: QuestionType;
|
||||
options?: QuestionOptionInput[];
|
||||
allowCustom?: boolean;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
@@ -22,7 +21,6 @@ export interface NormalizedQuestion {
|
||||
prompt: string;
|
||||
type: QuestionType;
|
||||
options: QuestionOptionInput[];
|
||||
allowCustom: boolean;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ export class AskUserView implements Component, Focusable {
|
||||
const question = this.currentQuestion();
|
||||
if (!question || question.type !== "select") return [];
|
||||
const options: RenderOption[] = question.options.map((option) => ({ ...option, kind: "option" }));
|
||||
if (question.allowCustom) options.push({ value: "", label: "Type something.", kind: "custom" });
|
||||
options.push({ value: "", label: "Type something.", kind: "custom" });
|
||||
if (!question.required) options.push({ value: "", label: "Skip this question.", kind: "skip" });
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { formatAnswers, normalizeQuestions, orderedAnswers } from "../src/normalize.ts";
|
||||
import type { UserAnswer } from "../src/types.ts";
|
||||
import type { QuestionInput, UserAnswer } from "../src/types.ts";
|
||||
|
||||
test("normalizes select and text questions with bounded defaults", () => {
|
||||
const questions = normalizeQuestions([
|
||||
@@ -27,14 +27,26 @@ test("normalizes select and text questions with bounded defaults", () => {
|
||||
prompt: "Choose a language",
|
||||
type: "select",
|
||||
options: [{ value: "ts", label: "TypeScript" }],
|
||||
allowCustom: true,
|
||||
required: true,
|
||||
});
|
||||
assert.equal(questions[1]?.label, "Q2");
|
||||
assert.equal(questions[1]?.allowCustom, false);
|
||||
assert.equal(questions[1]?.required, false);
|
||||
});
|
||||
|
||||
test("ignores the removed allowCustom input from legacy callers", () => {
|
||||
const legacyQuestion = {
|
||||
id: "language",
|
||||
prompt: "Choose a language",
|
||||
type: "select",
|
||||
options: [{ value: "ts", label: "TypeScript" }],
|
||||
allowCustom: false,
|
||||
} satisfies QuestionInput & { allowCustom: boolean };
|
||||
|
||||
const [question] = normalizeQuestions([legacyQuestion]);
|
||||
assert.ok(question);
|
||||
assert.equal("allowCustom" in question, false);
|
||||
});
|
||||
|
||||
test("rejects invalid ids, duplicate ids, invalid option combinations, and duplicate values", () => {
|
||||
assert.throws(() => normalizeQuestions([{ id: "bad id", prompt: "Bad", type: "text" }]), /must start with a letter/);
|
||||
assert.throws(() => normalizeQuestions([null as never]), /must be an object/);
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
A [pi](https://pi.dev) extension that brings **Claude Code-style autonomous sub-agents** to pi. Spawn specialized agents that run in isolated sessions — each with its own tools, system prompt, model, and thinking level. Run them in the background (the default) or block on them, steer them mid-run, resume completed sessions, and define your own custom agent types.
|
||||
|
||||
> **my-pi maintenance note:** This directory was imported from upstream tag `v0.18.0` at commit `3f9d35cd078d18a141eb5a6d8f4fc5010d756280` and is maintained directly from source. The root bundle installs it through `file:./pi-subagents`, default-loads the host-owned `extensions/subagents.ts` wrapper, and exposes its orchestration tools through Tool Search's checked-in `subagents` group. The local source implements the `pi-permission-system` child lifecycle bridge described below. See [`UPSTREAM.md`](UPSTREAM.md).
|
||||
> **my-pi maintenance note:** This directory was imported from upstream tag `v0.18.0` at commit `3f9d35cd078d18a141eb5a6d8f4fc5010d756280` and is maintained directly from source. Its source, tests, host-owned `extensions/subagents.ts` wrapper, and `pi-permission-system` child lifecycle bridge are retained, but the root bundle currently declares no `pi-subagents` runtime dependency and does not default-load the wrapper. See [`UPSTREAM.md`](UPSTREAM.md).
|
||||
|
||||
## my-pi permission integration
|
||||
|
||||
Before a child calls `bindExtensions()`, it publishes `subagents:child:session-created` with the child session id and the interactive root session id. This lets `pi-permission-system` classify the headless session as a registered child before its own `session_start`, prevents the child permission service from replacing the root process-global service, and routes unresolved `ask` requests to the root authority. Nested descendants retain the same interactive root instead of forwarding to an intermediate child with no UI. The matching `subagents:child:disposed` event is published once, after the child has actually closed.
|
||||
|
||||
The bundle loads this extension through `createPiSubagentsExtension({ mandatoryExtensionPaths })`. These exact, host-owned absolute paths are canonicalized and merged *after* Agent frontmatter, so `extensions: false`, `isolated: true`, and `exclude_extensions:` cannot remove the permission wrapper. The child aborts before session creation if an exact mandatory entry is unavailable or does not survive loader filtering. Mandatory means lifecycle handlers bind; it does **not** expose that extension's tools when normal Agent extension policy hid them. Agent files cannot add to or alter this list.
|
||||
The retained bundle wrapper configures this extension through `createPiSubagentsExtension({ mandatoryExtensionPaths })`, although that wrapper is currently disabled in the root extension list. If re-enabled, these exact, host-owned absolute paths are canonicalized and merged *after* Agent frontmatter, so `extensions: false`, `isolated: true`, and `exclude_extensions:` cannot remove the permission wrapper. The child aborts before session creation if an exact mandatory entry is unavailable or does not survive loader filtering. Mandatory means lifecycle handlers bind; it does **not** expose that extension's tools when normal Agent extension policy hid them. Agent files cannot add to or alter this list.
|
||||
|
||||
Each child system prompt carries one sanitized `<active_agent name="..."/>` identity. Any inherited parent identity is removed first, so per-agent permission configuration and forwarded-request attribution resolve against the child definition rather than the orchestrator.
|
||||
|
||||
|
||||
@@ -9,38 +9,19 @@ import test from "node:test";
|
||||
const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const hasPi = spawnSync("pi", ["--version"], { encoding: "utf8" }).status === 0;
|
||||
|
||||
test("enabled subagents wrapper loads with its bundle-owned mandatory permission entry", { skip: !hasPi }, async () => {
|
||||
test("subagents source is retained but disabled in the root bundle", async () => {
|
||||
const packageJson = JSON.parse(await readFile(join(repositoryRoot, "package.json"), "utf8")) as {
|
||||
dependencies: Record<string, string>;
|
||||
files: string[];
|
||||
pi: { extensions: string[] };
|
||||
};
|
||||
assert.ok(packageJson.files.includes("pi-subagents"));
|
||||
assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], "file:./pi-subagents");
|
||||
assert.ok(
|
||||
assert.ok(packageJson.files.includes("pi-subagents"), "the bundle must retain the maintained subagents source");
|
||||
assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], undefined);
|
||||
assert.equal(
|
||||
packageJson.pi.extensions.includes("./extensions/subagents.ts"),
|
||||
"the bundle must default-load the locally maintained subagents wrapper",
|
||||
false,
|
||||
"the bundle must not default-load the disabled subagents wrapper",
|
||||
);
|
||||
|
||||
const home = await mkdtemp(join(tmpdir(), "my-pi-subagents-wrapper-load-"));
|
||||
const result = spawnSync(
|
||||
"pi",
|
||||
["--no-extensions", "-e", join(repositoryRoot, "extensions", "subagents.ts"), "--mode", "rpc"],
|
||||
{
|
||||
encoding: "utf8",
|
||||
timeout: 60_000,
|
||||
env: {
|
||||
...process.env,
|
||||
HOME: home,
|
||||
XDG_CONFIG_HOME: join(home, ".config"),
|
||||
PI_CODING_AGENT_DIR: join(home, ".pi-agent"),
|
||||
PI_OFFLINE: "1",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`);
|
||||
assert.doesNotMatch(`${result.stdout}\n${result.stderr}`, /Failed to load extension/u);
|
||||
});
|
||||
|
||||
test("all package extensions load together without global registration conflicts", { skip: !hasPi }, async () => {
|
||||
@@ -122,8 +103,8 @@ test("all package extensions load together without global registration conflicts
|
||||
"the packed bundle must include the locally maintained notification source",
|
||||
);
|
||||
assert.equal(packageJson.dependencies["@ogulcancelik/pi-minimal-footer"], "file:./pi-minimal-footer");
|
||||
assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], "file:./pi-subagents");
|
||||
assert.ok(packageJson.pi.extensions.includes("./extensions/subagents.ts"));
|
||||
assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], undefined);
|
||||
assert.equal(packageJson.pi.extensions.includes("./extensions/subagents.ts"), false);
|
||||
assert.equal(
|
||||
packageJson.pi.extensions.some((entry) => entry.includes("pi-hermes-memory") || entry.includes("the-forge-flow")),
|
||||
false,
|
||||
|
||||
Reference in New Issue
Block a user