pi-crew 0.7.7 → 0.8.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.
- package/CHANGELOG.md +325 -0
- package/package.json +1 -1
- package/src/agents/agent-config.ts +101 -1
- package/src/agents/discover-agents.ts +34 -3
- package/src/config/types.ts +8 -0
- package/src/errors.ts +9 -0
- package/src/extension/context-status-injection.ts +14 -5
- package/src/extension/register.ts +4 -18
- package/src/extension/registration/compaction-guard.ts +44 -13
- package/src/extension/team-tool/handle-settings.ts +2 -0
- package/src/runtime/live-session-runtime.ts +69 -7
- package/src/runtime/model-fallback.ts +39 -1
- package/src/runtime/model-scope.ts +141 -0
- package/src/runtime/pi-args.ts +21 -6
- package/src/runtime/skill-effectiveness.ts +84 -10
- package/src/runtime/skill-instructions.ts +14 -4
- package/src/runtime/task-runner.ts +21 -0
- package/src/skills/discover-skills.ts +31 -2
- package/src/ui/agent-management-overlay.ts +1 -1
- package/src/utils/session-utils.ts +30 -0
|
@@ -21,6 +21,8 @@ const PACKAGE_SKILLS_DIR = path.resolve(
|
|
|
21
21
|
"..",
|
|
22
22
|
"skills",
|
|
23
23
|
);
|
|
24
|
+
import * as os from "node:os";
|
|
25
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
24
26
|
const MAX_SKILL_CHARS = 1500;
|
|
25
27
|
const MAX_TOTAL_CHARS = 6000;
|
|
26
28
|
const MAX_SKILL_NAME_CHARS = 80;
|
|
@@ -139,16 +141,24 @@ export function resolveTaskSkillNames(input: ResolveTaskSkillsInput): string[] {
|
|
|
139
141
|
|
|
140
142
|
function candidateSkillDirs(
|
|
141
143
|
cwd: string,
|
|
142
|
-
): Array<{ root: string; source: "project" | "package" }> {
|
|
144
|
+
): Array<{ root: string; source: "project" | "package" | "project-pi" | "user-pi" | "project-agents" | "user-agents" }> {
|
|
143
145
|
return [
|
|
144
146
|
{ root: PACKAGE_SKILLS_DIR, source: "package" }, // ✓ Trusted first
|
|
145
|
-
|
|
147
|
+
// F6 (v0.7.9): same five roots as discover-skills, in the same precedence
|
|
148
|
+
// order. The first hit wins, so a project `.pi/skills/foo/SKILL.md`
|
|
149
|
+
// overrides both the bundled `foo` and any legacy `<cwd>/skills/foo`.
|
|
150
|
+
{ root: path.resolve(cwd, ".pi", "skills"), source: "project-pi" },
|
|
151
|
+
{ root: path.resolve(cwd, ".agents", "skills"), source: "project-agents" },
|
|
152
|
+
{ root: path.resolve(cwd, "skills"), source: "project" },
|
|
153
|
+
{ root: path.join(getAgentDir(), "skills"), source: "user-pi" },
|
|
154
|
+
{ root: path.join(os.homedir(), ".agents", "skills"), source: "user-agents" },
|
|
155
|
+
{ root: path.join(os.homedir(), ".pi", "skills"), source: "user-pi" },
|
|
146
156
|
];
|
|
147
157
|
}
|
|
148
158
|
|
|
149
159
|
interface CachedSkillMarkdown {
|
|
150
160
|
path: string;
|
|
151
|
-
source: "project" | "package";
|
|
161
|
+
source: "project" | "package" | "project-pi" | "user-pi" | "project-agents" | "user-agents";
|
|
152
162
|
content: string;
|
|
153
163
|
mtimeMs: number;
|
|
154
164
|
size: number;
|
|
@@ -187,7 +197,7 @@ function readSkillMarkdown(
|
|
|
187
197
|
cwd: string,
|
|
188
198
|
name: string,
|
|
189
199
|
):
|
|
190
|
-
| { path: string; source: "project" | "package"; content: string }
|
|
200
|
+
| { path: string; source: "project" | "package" | "project-pi" | "user-pi" | "project-agents" | "user-agents"; content: string }
|
|
191
201
|
| undefined {
|
|
192
202
|
if (!isValidSkillName(name)) return undefined;
|
|
193
203
|
const cacheKey = `${path.resolve(cwd)}:${name}`;
|
|
@@ -32,6 +32,8 @@ import {
|
|
|
32
32
|
isRetryableModelFailure,
|
|
33
33
|
type ModelAttemptSummary,
|
|
34
34
|
} from "./model-fallback.ts";
|
|
35
|
+
import { readEnabledModelsPatterns } from "./model-scope.ts";
|
|
36
|
+
import { loadConfig } from "../config/config.ts";
|
|
35
37
|
import { tailReadWithLineSnap } from "./task-runner/tail-read.ts";
|
|
36
38
|
import {
|
|
37
39
|
parsePiJsonOutput,
|
|
@@ -385,6 +387,7 @@ export async function runTeamTask(
|
|
|
385
387
|
parentModel: input.parentModel,
|
|
386
388
|
modelRegistry: input.modelRegistry,
|
|
387
389
|
cwd: task.cwd,
|
|
390
|
+
scopeModelsPatterns: await resolveTaskScopeModelsPatterns(task.cwd),
|
|
388
391
|
});
|
|
389
392
|
const candidates = modelRoutingPlan.candidates;
|
|
390
393
|
const attemptModels =
|
|
@@ -1306,3 +1309,21 @@ export async function runTeamTask(
|
|
|
1306
1309
|
streamBridge?.dispose();
|
|
1307
1310
|
}
|
|
1308
1311
|
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* F7: resolve the enabledModels allowlist for the child-process spawn path,
|
|
1315
|
+
* but only if `runtime.reliability.scopeModels` is ON. Returns [] (no-op)
|
|
1316
|
+
* when the toggle is off or the allowlist is empty. Best-effort: any failure
|
|
1317
|
+
* to read config or the allowlist silently disables the gate so spawn is
|
|
1318
|
+
* never blocked by a misconfiguration.
|
|
1319
|
+
*/
|
|
1320
|
+
async function resolveTaskScopeModelsPatterns(cwd: string): Promise<string[]> {
|
|
1321
|
+
let scopeModels = false;
|
|
1322
|
+
try {
|
|
1323
|
+
scopeModels = loadConfig(cwd).config.reliability?.scopeModels === true;
|
|
1324
|
+
} catch {
|
|
1325
|
+
return [];
|
|
1326
|
+
}
|
|
1327
|
+
if (!scopeModels) return [];
|
|
1328
|
+
return readEnabledModelsPatterns(cwd);
|
|
1329
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
4
6
|
import { logInternalError } from "../utils/internal-error.ts";
|
|
5
7
|
import { isSafePathId, resolveContainedPath, resolveRealContainedPath } from "../utils/safe-paths.ts";
|
|
6
8
|
|
|
@@ -12,13 +14,40 @@ let cache: { skills: SkillDescriptor[]; cachedAt: number; cwd: string } | null =
|
|
|
12
14
|
export interface SkillDescriptor {
|
|
13
15
|
name: string;
|
|
14
16
|
description: string;
|
|
15
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Source of the skill. F6 (v0.7.9) adds the Agent Skills spec roots:
|
|
19
|
+
* - `project-pi` / `user-pi` — Pi's standard `.pi/skills/`
|
|
20
|
+
* - `project-agents` / `user-agents` — cross-tool Agent Skills spec (`.agents/skills/`)
|
|
21
|
+
* The original `project` / `package` are kept for back-compat.
|
|
22
|
+
*/
|
|
23
|
+
source: "project" | "package" | "project-pi" | "user-pi" | "project-agents" | "user-agents";
|
|
16
24
|
path: string;
|
|
17
25
|
}
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
/**
|
|
28
|
+
* F6 (v0.7.9): discover skills from all five roots (matching pi-subagents'
|
|
29
|
+
* skill-loader so users authoring skills under either convention find them).
|
|
30
|
+
* Roots, in precedence order (first hit wins):
|
|
31
|
+
* 1. <cwd>/.pi/skills (project, Pi standard)
|
|
32
|
+
* 2. <cwd>/.agents/skills (project, Agent Skills spec — agentskills.io)
|
|
33
|
+
* 3. <cwd>/skills (project, legacy pi-crew convention)
|
|
34
|
+
* 4. <getAgentDir>/skills (user, Pi standard)
|
|
35
|
+
* 5. <homedir>/.agents/skills (user, Agent Skills spec)
|
|
36
|
+
* 6. <homedir>/.pi/skills (user, legacy Pi — pre-standard)
|
|
37
|
+
* 7. PACKAGE_SKILLS_DIR (bundled, trusted)
|
|
38
|
+
* The `PACKAGE_SKILLS_DIR` (bundled) and the legacy `<cwd>/skills` root are
|
|
39
|
+
* kept as separate `source` values to preserve the existing capability
|
|
40
|
+
* inventory shape — callers that key on `source === "package"` / `source ===
|
|
41
|
+
* "project"` keep working.
|
|
42
|
+
*/
|
|
43
|
+
function listSkillDirs(cwd: string): Array<{ root: string; source: SkillDescriptor["source"] }> {
|
|
20
44
|
return [
|
|
45
|
+
{ root: path.resolve(cwd, ".pi", "skills"), source: "project-pi" },
|
|
46
|
+
{ root: path.resolve(cwd, ".agents", "skills"), source: "project-agents" },
|
|
21
47
|
{ root: path.resolve(cwd, "skills"), source: "project" },
|
|
48
|
+
{ root: path.join(getAgentDir(), "skills"), source: "user-pi" },
|
|
49
|
+
{ root: path.join(os.homedir(), ".agents", "skills"), source: "user-agents" },
|
|
50
|
+
{ root: path.join(os.homedir(), ".pi", "skills"), source: "user-pi" },
|
|
22
51
|
{ root: PACKAGE_SKILLS_DIR, source: "package" },
|
|
23
52
|
];
|
|
24
53
|
}
|
|
@@ -65,7 +65,7 @@ export interface AgentOverlayState {
|
|
|
65
65
|
export function createAgentOverlayState(entries: AgentEntry[], maxVisible = 20): AgentOverlayState {
|
|
66
66
|
return {
|
|
67
67
|
entries: entries.sort((a, b) => {
|
|
68
|
-
const order: Record<ResourceSource, number> = { project: 0,
|
|
68
|
+
const order: Record<ResourceSource, number> = { project: 0, "project-pi": 1, user: 2, git: 3, builtin: 4, dynamic: 5 };
|
|
69
69
|
const diff = (order[a.source] ?? 4) - (order[b.source] ?? 4);
|
|
70
70
|
return diff !== 0 ? diff : a.name.localeCompare(b.name);
|
|
71
71
|
}),
|
|
@@ -49,4 +49,34 @@ export function safeToPiSessionId(runId: string): string | undefined {
|
|
|
49
49
|
} catch {
|
|
50
50
|
return undefined;
|
|
51
51
|
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extract the current Pi session id from an ExtensionContext.
|
|
56
|
+
*
|
|
57
|
+
* `ExtensionContext` does not declare `sessionId` in its type, but the runtime
|
|
58
|
+
* attaches it as an own property. We read it via `getOwnPropertyDescriptor`
|
|
59
|
+
* to safely bypass any Proxy traps, then validate it as a non-empty string.
|
|
60
|
+
*
|
|
61
|
+
* This is the canonical accessor — every site that filters the SHARED
|
|
62
|
+
* per-project `.crew/state/` tree down to the current session MUST use this,
|
|
63
|
+
* otherwise cross-session state leaks (e.g. compaction-guard resuming another
|
|
64
|
+
* session's runs, ambient-status injecting another session's runs).
|
|
65
|
+
*
|
|
66
|
+
* Returns undefined when the session id is absent or unparseable — callers
|
|
67
|
+
* must decide whether to treat that as "no filter" (back-compat) or "no runs".
|
|
68
|
+
*/
|
|
69
|
+
export function extractSessionId(ctx: unknown): string | undefined {
|
|
70
|
+
if (typeof ctx !== "object" || ctx === null) return undefined;
|
|
71
|
+
let raw: unknown;
|
|
72
|
+
try {
|
|
73
|
+
raw = Object.getOwnPropertyDescriptor(ctx, "sessionId")?.value;
|
|
74
|
+
} catch {
|
|
75
|
+
// Defensive: a hostile Proxy or exotic object may trap descriptor
|
|
76
|
+
// access. Real Pi ExtensionContext objects are plain, so this is
|
|
77
|
+
// only hit by adversarial/degenerate inputs — treat as no session id.
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
if (typeof raw !== "string" || raw.length === 0) return undefined;
|
|
81
|
+
return raw;
|
|
52
82
|
}
|