pi-agents-switch 0.2.7 → 0.2.8
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/index.ts +4 -13
- package/package.json +1 -1
- package/profile-manager.ts +105 -15
package/index.ts
CHANGED
|
@@ -888,23 +888,16 @@ export default function agentsSwitch(pi: ExtensionAPI) {
|
|
|
888
888
|
const skillsOpen = "\n<available_skills>";
|
|
889
889
|
const skillsIdx = content.indexOf(skillsOpen);
|
|
890
890
|
if (skillsIdx >= 0) {
|
|
891
|
-
const skillsEnd = content.indexOf(
|
|
892
|
-
"\n</available_skills>",
|
|
893
|
-
skillsIdx,
|
|
894
|
-
);
|
|
891
|
+
const skillsEnd = content.indexOf("\n</available_skills>", skillsIdx);
|
|
895
892
|
if (skillsEnd >= 0) {
|
|
896
893
|
content =
|
|
897
894
|
content.substring(0, skillsIdx) +
|
|
898
|
-
content.substring(
|
|
899
|
-
skillsEnd + "\n</available_skills>".length,
|
|
900
|
-
);
|
|
895
|
+
content.substring(skillsEnd + "\n</available_skills>".length);
|
|
901
896
|
}
|
|
902
897
|
}
|
|
903
898
|
|
|
904
899
|
// Rebuild clean skills block from our resolved config
|
|
905
|
-
const selectedSkills = pm.getSkillObjects(
|
|
906
|
-
currentResolved.skillNames,
|
|
907
|
-
);
|
|
900
|
+
const selectedSkills = pm.getSkillObjects(currentResolved.skillNames);
|
|
908
901
|
if (selectedSkills.length > 0) {
|
|
909
902
|
const skillsBlock = formatSkillsBlock(selectedSkills);
|
|
910
903
|
// Insert before "Current date:"
|
|
@@ -912,9 +905,7 @@ export default function agentsSwitch(pi: ExtensionAPI) {
|
|
|
912
905
|
const idx = content.indexOf(currentDateMarker);
|
|
913
906
|
if (idx >= 0) {
|
|
914
907
|
content =
|
|
915
|
-
content.substring(0, idx) +
|
|
916
|
-
skillsBlock +
|
|
917
|
-
content.substring(idx);
|
|
908
|
+
content.substring(0, idx) + skillsBlock + content.substring(idx);
|
|
918
909
|
}
|
|
919
910
|
}
|
|
920
911
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-agents-switch",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Tab to switch primary agents in Pi — like OpenCode's agent switching. Each agent gets an isolated profile with its own AGENTS.md, extensions, skills, and settings.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"pi": {
|
package/profile-manager.ts
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
statSync,
|
|
36
36
|
rmSync,
|
|
37
37
|
} from "node:fs";
|
|
38
|
-
import { join } from "node:path";
|
|
38
|
+
import { join, basename } from "node:path";
|
|
39
39
|
import { homedir } from "node:os";
|
|
40
40
|
import { parseFrontmatter, serializeFrontmatter } from "./frontmatter";
|
|
41
41
|
import type {
|
|
@@ -65,6 +65,9 @@ export class ProfileManager {
|
|
|
65
65
|
readonly configPath: string;
|
|
66
66
|
readonly piAgentDir: string;
|
|
67
67
|
|
|
68
|
+
/** Cached skill index keyed by name (rebuilt on cwd change) */
|
|
69
|
+
private skillCache: Map<string, { name: string; description: string; filePath: string }> | null = null;
|
|
70
|
+
|
|
68
71
|
constructor(private cwd?: string) {
|
|
69
72
|
const piRoot = join(homedir(), ".pi");
|
|
70
73
|
this.userAgentsDir = join(piRoot, "agents");
|
|
@@ -460,6 +463,10 @@ export class ProfileManager {
|
|
|
460
463
|
* Look up full skill objects by name from Pi's standard skill directories.
|
|
461
464
|
* Returns name, description, and filePath for each skill found.
|
|
462
465
|
*/
|
|
466
|
+
/**
|
|
467
|
+
* Look up full skill objects by name.
|
|
468
|
+
* Scans Pi's standard skill locations recursively (flat + nested).
|
|
469
|
+
*/
|
|
463
470
|
getSkillObjects(
|
|
464
471
|
skillNames: string[],
|
|
465
472
|
): Array<{ name: string; description: string; filePath: string }> {
|
|
@@ -480,28 +487,111 @@ export class ProfileManager {
|
|
|
480
487
|
private findSkillOnDisk(
|
|
481
488
|
name: string,
|
|
482
489
|
): { name: string; description: string; filePath: string } | undefined {
|
|
483
|
-
|
|
490
|
+
if (!this.skillCache) {
|
|
491
|
+
this.rebuildSkillIndex();
|
|
492
|
+
}
|
|
493
|
+
return this.skillCache!.get(name);
|
|
494
|
+
}
|
|
484
495
|
|
|
485
|
-
|
|
486
|
-
|
|
496
|
+
/**
|
|
497
|
+
* Rebuild the skill index by scanning all known skill directories.
|
|
498
|
+
*
|
|
499
|
+
* Locations scanned:
|
|
500
|
+
* 1. ~/.pi/agent/skills/<name>/SKILL.md (flat, user-installed)
|
|
501
|
+
* 2. ~/.pi/agent/npm/node_modules/<ext>/skills/ (extension skills)
|
|
502
|
+
* 3. ~/.pi/agents/<agent>/skills/ (agent-specific skills)
|
|
503
|
+
* 4. <cwd>/.pi/skills/ (project skills, flat)
|
|
504
|
+
*/
|
|
505
|
+
rebuildSkillIndex(): void {
|
|
506
|
+
this.skillCache = new Map();
|
|
507
|
+
|
|
508
|
+
const scanDir = (dir: string, maxDepth: number): void => {
|
|
509
|
+
if (maxDepth <= 0) return;
|
|
510
|
+
|
|
511
|
+
// Check for SKILL.md in this directory
|
|
512
|
+
const skillMd = join(dir, "SKILL.md");
|
|
513
|
+
if (existsSync(skillMd)) {
|
|
514
|
+
try {
|
|
515
|
+
const content = readFileSync(skillMd, "utf8");
|
|
516
|
+
const { frontmatter } = parseFrontmatter(content);
|
|
517
|
+
const skillName = frontmatter.name ?? basename(dir);
|
|
518
|
+
// Last-write-wins: later scan locations override earlier ones
|
|
519
|
+
this.skillCache!.set(skillName, {
|
|
520
|
+
name: skillName,
|
|
521
|
+
description: frontmatter.description ?? "",
|
|
522
|
+
filePath: skillMd,
|
|
523
|
+
});
|
|
524
|
+
} catch {
|
|
525
|
+
// Skip unreadable files
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Recurse into subdirectories
|
|
530
|
+
try {
|
|
531
|
+
for (const entry of readdirSync(dir)) {
|
|
532
|
+
if (entry === "node_modules") continue; // skip nested node_modules
|
|
533
|
+
const sub = join(dir, entry);
|
|
534
|
+
try {
|
|
535
|
+
if (statSync(sub).isDirectory()) {
|
|
536
|
+
scanDir(sub, maxDepth - 1);
|
|
537
|
+
}
|
|
538
|
+
} catch {
|
|
539
|
+
// Skip unreadable entries
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
} catch {
|
|
543
|
+
// Skip unreadable directories
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
|
|
547
|
+
// 1. Global Pi skills (flat: skills/<name>/SKILL.md)
|
|
548
|
+
scanDir(join(this.piAgentDir, "skills"), 2);
|
|
549
|
+
|
|
550
|
+
// 2. Extension skills from npm packages (nested: node_modules/<ext>/skills/<name>/SKILL.md)
|
|
551
|
+
const npmDir = join(this.piAgentDir, "npm", "node_modules");
|
|
552
|
+
if (existsSync(npmDir)) {
|
|
553
|
+
for (const ext of readdirSync(npmDir)) {
|
|
554
|
+
const extSkills = join(npmDir, ext, "skills");
|
|
555
|
+
if (existsSync(extSkills)) {
|
|
556
|
+
try {
|
|
557
|
+
if (statSync(extSkills).isDirectory()) {
|
|
558
|
+
scanDir(extSkills, 3);
|
|
559
|
+
}
|
|
560
|
+
} catch {
|
|
561
|
+
// Skip
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
487
565
|
}
|
|
488
566
|
|
|
489
|
-
|
|
490
|
-
|
|
567
|
+
// 3. Agent-specific skills (nested: agents/<agent>/skills/<name>/SKILL.md)
|
|
568
|
+
const agentsDir = join(homedir(), ".pi", "agents");
|
|
569
|
+
if (existsSync(agentsDir)) {
|
|
491
570
|
try {
|
|
492
|
-
const
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
571
|
+
for (const agent of readdirSync(agentsDir)) {
|
|
572
|
+
const agentSkills = join(agentsDir, agent, "skills");
|
|
573
|
+
if (existsSync(agentSkills)) {
|
|
574
|
+
try {
|
|
575
|
+
if (statSync(agentSkills).isDirectory()) {
|
|
576
|
+
scanDir(agentSkills, 3);
|
|
577
|
+
}
|
|
578
|
+
} catch {
|
|
579
|
+
// Skip
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
499
583
|
} catch {
|
|
500
|
-
// Skip unreadable
|
|
584
|
+
// Skip unreadable agents dir
|
|
501
585
|
}
|
|
502
586
|
}
|
|
503
587
|
|
|
504
|
-
|
|
588
|
+
// 4. Project-level skills (flat)
|
|
589
|
+
if (this.cwd) {
|
|
590
|
+
const projSkillsDir = join(this.cwd, ".pi", "skills");
|
|
591
|
+
if (existsSync(projSkillsDir)) {
|
|
592
|
+
scanDir(projSkillsDir, 2);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
505
595
|
}
|
|
506
596
|
|
|
507
597
|
// ─── Agent creation / deletion ───────────────────────
|