@tekmidian/pai 0.6.1 → 0.6.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/ARCHITECTURE.md +73 -8
- package/dist/cli/index.mjs +100 -3
- package/dist/cli/index.mjs.map +1 -1
- package/dist/daemon-mcp/index.mjs +37 -6
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/skills/Art/SKILL.md +32 -0
- package/dist/skills/Createskill/SKILL.md +23 -0
- package/dist/skills/Journal/SKILL.md +34 -0
- package/dist/skills/Name/SKILL.md +12 -0
- package/dist/skills/Observability/SKILL.md +25 -0
- package/dist/skills/Plan/SKILL.md +28 -0
- package/dist/skills/Research/SKILL.md +30 -0
- package/dist/skills/Review/SKILL.md +58 -0
- package/dist/skills/Route/SKILL.md +14 -0
- package/dist/skills/SearchHistory/SKILL.md +31 -0
- package/dist/skills/Sessions/SKILL.md +28 -0
- package/dist/skills/Share/SKILL.md +35 -0
- package/dist/skills/StoryExplanation/SKILL.md +21 -0
- package/dist/skills/VaultConnect/SKILL.md +10 -0
- package/dist/skills/VaultContext/SKILL.md +12 -0
- package/dist/skills/VaultEmerge/SKILL.md +10 -0
- package/dist/skills/VaultOrphans/SKILL.md +10 -0
- package/dist/skills/VaultTrace/SKILL.md +10 -0
- package/package.json +3 -2
- package/scripts/build-skill-stubs.mjs +202 -0
package/ARCHITECTURE.md
CHANGED
|
@@ -103,14 +103,15 @@ The interactive wizard walks through 14 steps:
|
|
|
103
103
|
4. CLAUDE.md template installation
|
|
104
104
|
5. PAI skill installation
|
|
105
105
|
6. Steering rules installation
|
|
106
|
-
7.
|
|
107
|
-
8.
|
|
108
|
-
9.
|
|
109
|
-
10.
|
|
110
|
-
11.
|
|
111
|
-
12.
|
|
112
|
-
13.
|
|
113
|
-
14.
|
|
106
|
+
7. MCP skill stub symlinks
|
|
107
|
+
8. Hook system deployment
|
|
108
|
+
9. TypeScript hook compilation
|
|
109
|
+
10. Claude Code settings configuration
|
|
110
|
+
11. Daemon installation
|
|
111
|
+
12. MCP server registration
|
|
112
|
+
13. Directory creation
|
|
113
|
+
14. Initial indexing
|
|
114
|
+
15. Verification
|
|
114
115
|
|
|
115
116
|
### 4. Install the Daemon
|
|
116
117
|
|
|
@@ -642,6 +643,68 @@ The build script compiles each `.ts` hook to a self-contained `.mjs` module usin
|
|
|
642
643
|
|
|
643
644
|
---
|
|
644
645
|
|
|
646
|
+
## Skill Stub System
|
|
647
|
+
|
|
648
|
+
PAI's 18 MCP prompts are exposed to Claude Code as discoverable skills via auto-generated SKILL.md files. This bridges the gap between MCP prompts (protocol-level, invoked via `prompts/get`) and Claude Code's skill scanner (filesystem-based, scans `~/.claude/skills/`).
|
|
649
|
+
|
|
650
|
+
### How It Works
|
|
651
|
+
|
|
652
|
+
```
|
|
653
|
+
Source (TypeScript) Build Claude Code
|
|
654
|
+
───────────────── ───── ──────────
|
|
655
|
+
src/daemon-mcp/prompts/*.ts → dist/skills/<Name>/ → ~/.claude/skills/<Name>/
|
|
656
|
+
src/daemon-mcp/prompts/ SKILL.md (symlink)
|
|
657
|
+
custom/*.ts (gitignored)
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
1. **Source of truth**: TypeScript files in `src/daemon-mcp/prompts/`. Each exports `{ description, content }`.
|
|
661
|
+
2. **Build**: `node scripts/build-skill-stubs.mjs --sync` extracts content and generates `dist/skills/<TitleCase>/SKILL.md` with YAML frontmatter.
|
|
662
|
+
3. **Sync**: The `--sync` flag creates/updates symlinks in `~/.claude/skills/`. Runs automatically on every `bun run build`.
|
|
663
|
+
4. **Discovery**: Claude Code scans `~/.claude/skills/<Name>/SKILL.md` at session start, loads descriptions, and auto-invokes matching skills.
|
|
664
|
+
|
|
665
|
+
**Important**: Skills MUST be at `~/.claude/skills/<Name>/SKILL.md` (one level deep). Subdirectories like `~/.claude/skills/user/<Name>/` are NOT discovered by Claude Code's scanner.
|
|
666
|
+
|
|
667
|
+
### Adding a New Skill
|
|
668
|
+
|
|
669
|
+
**Built-in (shipped with PAI):**
|
|
670
|
+
|
|
671
|
+
1. Create `src/daemon-mcp/prompts/my-skill.ts`:
|
|
672
|
+
```typescript
|
|
673
|
+
export const mySkill = {
|
|
674
|
+
description: "What the skill does",
|
|
675
|
+
content: `## My Skill
|
|
676
|
+
|
|
677
|
+
USE WHEN user says 'trigger phrase', 'another trigger', ...
|
|
678
|
+
|
|
679
|
+
### Instructions
|
|
680
|
+
...your skill content here...`,
|
|
681
|
+
};
|
|
682
|
+
```
|
|
683
|
+
2. Add export to `src/daemon-mcp/prompts/index.ts`:
|
|
684
|
+
```typescript
|
|
685
|
+
export { mySkill } from "./my-skill.js";
|
|
686
|
+
```
|
|
687
|
+
3. Run `bun run build` — generates the stub AND syncs the symlink.
|
|
688
|
+
|
|
689
|
+
**Custom (user-created, survives `git pull`):**
|
|
690
|
+
|
|
691
|
+
1. Create `src/daemon-mcp/prompts/custom/my-local-skill.ts` (same format as above).
|
|
692
|
+
2. Run `bun run build` — custom prompts are picked up automatically.
|
|
693
|
+
|
|
694
|
+
The `custom/` directory is gitignored (only `.gitkeep` is tracked), so your local skills survive PAI updates.
|
|
695
|
+
|
|
696
|
+
### Updating After Changes
|
|
697
|
+
|
|
698
|
+
Symlinks point to `dist/skills/`, so any `bun run build` automatically updates what Claude Code sees. No manual steps needed.
|
|
699
|
+
|
|
700
|
+
If you reorganize prompts (rename, delete, add), the build script regenerates all stubs and the `--sync` flag updates symlinks accordingly. Stale symlinks pointing to removed stubs are cleaned up automatically.
|
|
701
|
+
|
|
702
|
+
### Setup Integration
|
|
703
|
+
|
|
704
|
+
`pai setup` (Step 7) runs the same symlink logic interactively, asking before creating symlinks. It also cleans up legacy symlinks from the old `~/.claude/skills/user/` location.
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
645
708
|
## Templates
|
|
646
709
|
|
|
647
710
|
PAI ships three templates used during setup and customizable for your workflow.
|
|
@@ -854,6 +917,7 @@ bun run lint # tsc --noEmit
|
|
|
854
917
|
| `dist/daemon/index.mjs` | Daemon server |
|
|
855
918
|
| `dist/daemon-mcp/index.mjs` | MCP shim (stdio → daemon socket) |
|
|
856
919
|
| `dist/hooks/*.mjs` | Compiled lifecycle hooks |
|
|
920
|
+
| `dist/skills/<Name>/SKILL.md` | Generated skill stubs (symlinked to ~/.claude/skills/) |
|
|
857
921
|
|
|
858
922
|
### Source Structure
|
|
859
923
|
|
|
@@ -885,6 +949,7 @@ src/
|
|
|
885
949
|
├── daemon-mcp/
|
|
886
950
|
│ ├── instructions.ts # MCP server instructions (~1.5KB routing table)
|
|
887
951
|
│ ├── prompts/ # 18 on-demand skill prompts
|
|
952
|
+
│ │ └── custom/ # User-created prompts (gitignored)
|
|
888
953
|
│ ├── resources/ # 11 reference resources (pai:// URIs)
|
|
889
954
|
│ └── index.ts # MCP shim entry point (stdio → socket)
|
|
890
955
|
├── hooks/
|
package/dist/cli/index.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import { t as PaiClient } from "../ipc-client-CoyUHPod.mjs";
|
|
|
15
15
|
import { a as expandHome, i as ensureConfigDir, n as CONFIG_FILE$2, o as loadConfig, t as CONFIG_DIR } from "../config-BuhHWyOK.mjs";
|
|
16
16
|
import { t as createStorageBackend } from "../factory-Ygqe_bVZ.mjs";
|
|
17
17
|
import { appendFileSync, chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
18
|
-
import { homedir, tmpdir } from "node:os";
|
|
18
|
+
import { homedir, platform, tmpdir } from "node:os";
|
|
19
19
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
20
20
|
import chalk from "chalk";
|
|
21
21
|
import { Command } from "commander";
|
|
@@ -4310,6 +4310,18 @@ function getDistHooksDir() {
|
|
|
4310
4310
|
for (const candidate of candidates) if (existsSync(join(candidate, "stop-hook.mjs"))) return candidate;
|
|
4311
4311
|
return fromModule;
|
|
4312
4312
|
}
|
|
4313
|
+
function getDistDir() {
|
|
4314
|
+
const moduleDir = new URL(".", import.meta.url).pathname;
|
|
4315
|
+
const fromModule = join(moduleDir, "..", "..", "..");
|
|
4316
|
+
const candidates = [
|
|
4317
|
+
fromModule,
|
|
4318
|
+
join(process.cwd(), "dist"),
|
|
4319
|
+
join(homedir(), "dev", "ai", "PAI", "dist"),
|
|
4320
|
+
join("/", "usr", "local", "lib", "node_modules", "@tekmidian", "pai", "dist")
|
|
4321
|
+
];
|
|
4322
|
+
for (const candidate of candidates) if (existsSync(join(candidate, "skills"))) return candidate;
|
|
4323
|
+
return fromModule;
|
|
4324
|
+
}
|
|
4313
4325
|
function getStatuslineScript() {
|
|
4314
4326
|
const candidates = [
|
|
4315
4327
|
join(process.cwd(), "statusline-command.sh"),
|
|
@@ -4685,6 +4697,89 @@ async function stepAiSteeringRules(rl) {
|
|
|
4685
4697
|
return true;
|
|
4686
4698
|
}
|
|
4687
4699
|
|
|
4700
|
+
//#endregion
|
|
4701
|
+
//#region src/cli/commands/setup/steps/07-skill-stubs.ts
|
|
4702
|
+
/**
|
|
4703
|
+
* Step 7: Install PAI MCP skill stubs to ~/.claude/skills/.
|
|
4704
|
+
*
|
|
4705
|
+
* Each PAI MCP prompt has a SKILL.md stub generated at build time
|
|
4706
|
+
* (dist/skills/<Name>/SKILL.md). This step symlinks (macOS/Linux) or
|
|
4707
|
+
* copies (Windows) them into Claude Code's skill discovery directory.
|
|
4708
|
+
*
|
|
4709
|
+
* Skills MUST live at ~/.claude/skills/<Name>/SKILL.md (top level),
|
|
4710
|
+
* NOT under a user/ subdirectory — Claude Code only scans one level deep.
|
|
4711
|
+
*
|
|
4712
|
+
* Symlinks keep the stubs in sync — rebuilding PAI updates them automatically.
|
|
4713
|
+
* Existing non-symlink directories with the same name are never overwritten.
|
|
4714
|
+
*/
|
|
4715
|
+
async function stepSkillStubs(rl) {
|
|
4716
|
+
section("Step 7: MCP Skill Stubs");
|
|
4717
|
+
line$1();
|
|
4718
|
+
line$1(" PAI MCP prompts need SKILL.md stubs so Claude Code can discover them.");
|
|
4719
|
+
line$1(" Stubs are symlinked from dist/skills/ — rebuilding PAI keeps them in sync.");
|
|
4720
|
+
line$1();
|
|
4721
|
+
const distSkills = join(getDistDir(), "skills");
|
|
4722
|
+
if (!existsSync(distSkills)) {
|
|
4723
|
+
console.log(c.warn("dist/skills/ not found — run `bun run build` first."));
|
|
4724
|
+
return false;
|
|
4725
|
+
}
|
|
4726
|
+
const skillNames = readdirSync(distSkills).filter((name) => {
|
|
4727
|
+
return existsSync(join(join(distSkills, name), "SKILL.md"));
|
|
4728
|
+
});
|
|
4729
|
+
if (skillNames.length === 0) {
|
|
4730
|
+
console.log(c.warn("No skill stubs found in dist/skills/."));
|
|
4731
|
+
return false;
|
|
4732
|
+
}
|
|
4733
|
+
console.log(c.dim(` Found ${skillNames.length} skill stubs to install.`));
|
|
4734
|
+
line$1();
|
|
4735
|
+
if (!await promptYesNo(rl, `Symlink ${skillNames.length} PAI skill stubs to ~/.claude/skills/?`, true)) {
|
|
4736
|
+
console.log(c.dim(" Skipping skill stub installation."));
|
|
4737
|
+
return false;
|
|
4738
|
+
}
|
|
4739
|
+
const targetBase = join(homedir(), ".claude", "skills");
|
|
4740
|
+
mkdirSync(targetBase, { recursive: true });
|
|
4741
|
+
const oldUserBase = join(targetBase, "user");
|
|
4742
|
+
if (existsSync(oldUserBase)) for (const name of skillNames) {
|
|
4743
|
+
const oldTarget = join(oldUserBase, name);
|
|
4744
|
+
if (existsSync(oldTarget) && lstatSync(oldTarget).isSymbolicLink()) unlinkSync(oldTarget);
|
|
4745
|
+
}
|
|
4746
|
+
const useSymlinks = platform() !== "win32";
|
|
4747
|
+
let installed = 0;
|
|
4748
|
+
let skipped = 0;
|
|
4749
|
+
let updated = 0;
|
|
4750
|
+
for (const name of skillNames) {
|
|
4751
|
+
const source = resolve(join(distSkills, name));
|
|
4752
|
+
const target = join(targetBase, name);
|
|
4753
|
+
if (existsSync(target) && !lstatSync(target).isSymbolicLink()) {
|
|
4754
|
+
console.log(c.dim(` SKIP ${name} (existing user skill, not a symlink)`));
|
|
4755
|
+
skipped++;
|
|
4756
|
+
continue;
|
|
4757
|
+
}
|
|
4758
|
+
if (existsSync(target) && lstatSync(target).isSymbolicLink()) {
|
|
4759
|
+
if (resolve(readlinkSync(target)) === source) {
|
|
4760
|
+
installed++;
|
|
4761
|
+
continue;
|
|
4762
|
+
}
|
|
4763
|
+
unlinkSync(target);
|
|
4764
|
+
updated++;
|
|
4765
|
+
}
|
|
4766
|
+
if (useSymlinks) symlinkSync(source, target);
|
|
4767
|
+
else {
|
|
4768
|
+
mkdirSync(target, { recursive: true });
|
|
4769
|
+
copyFileSync(join(source, "SKILL.md"), join(target, "SKILL.md"));
|
|
4770
|
+
}
|
|
4771
|
+
installed++;
|
|
4772
|
+
}
|
|
4773
|
+
line$1();
|
|
4774
|
+
const method = useSymlinks ? "symlinked" : "copied";
|
|
4775
|
+
if (updated > 0) console.log(c.ok(`${installed} stubs ${method}, ${updated} updated, ${skipped} skipped (user skills).`));
|
|
4776
|
+
else {
|
|
4777
|
+
console.log(c.ok(`${installed} skill stubs ${method} to ~/.claude/skills/.`));
|
|
4778
|
+
if (skipped > 0) console.log(c.dim(` ${skipped} skipped (existing user skills not managed by PAI).`));
|
|
4779
|
+
}
|
|
4780
|
+
return true;
|
|
4781
|
+
}
|
|
4782
|
+
|
|
4688
4783
|
//#endregion
|
|
4689
4784
|
//#region src/cli/commands/setup/steps/08-hooks.ts
|
|
4690
4785
|
/** Step 7: Shell lifecycle hooks installation (pre-compact, session-stop, statusline). */
|
|
@@ -5317,7 +5412,7 @@ async function stepInitialIndex(rl) {
|
|
|
5317
5412
|
//#endregion
|
|
5318
5413
|
//#region src/cli/commands/setup/steps/15-verify.ts
|
|
5319
5414
|
/** Step 13: Setup summary — displays all configuration choices made during setup. */
|
|
5320
|
-
function stepSummary(configUpdates, claudeMdGenerated, paiSkillInstalled, aiSteeringRulesInstalled, hooksInstalled, tsHooksInstalled, settingsPatched, daName, daemonInstalled, mcpRegistered) {
|
|
5415
|
+
function stepSummary(configUpdates, claudeMdGenerated, paiSkillInstalled, aiSteeringRulesInstalled, skillStubsInstalled, hooksInstalled, tsHooksInstalled, settingsPatched, daName, daemonInstalled, mcpRegistered) {
|
|
5321
5416
|
section("Setup Complete");
|
|
5322
5417
|
line$1();
|
|
5323
5418
|
console.log(chalk.green(" PAI Knowledge OS is configured!"));
|
|
@@ -5331,6 +5426,7 @@ function stepSummary(configUpdates, claudeMdGenerated, paiSkillInstalled, aiStee
|
|
|
5331
5426
|
console.log(chalk.dim(" CLAUDE.md: ") + chalk.cyan(claudeMdGenerated ? "~/.claude/CLAUDE.md (generated)" : "(unchanged)"));
|
|
5332
5427
|
console.log(chalk.dim(" PAI skill: ") + chalk.cyan(paiSkillInstalled ? "~/.claude/skills/PAI/SKILL.md (installed)" : "(unchanged)"));
|
|
5333
5428
|
console.log(chalk.dim(" Steering rules: ") + chalk.cyan(aiSteeringRulesInstalled ? "~/.claude/skills/PAI/AI-STEERING-RULES.md (installed)" : "(unchanged)"));
|
|
5429
|
+
console.log(chalk.dim(" Skill stubs: ") + chalk.cyan(skillStubsInstalled ? "18 MCP prompt stubs symlinked to ~/.claude/skills/" : "(unchanged)"));
|
|
5334
5430
|
console.log(chalk.dim(" Hooks (shell): ") + chalk.cyan(hooksInstalled ? "pai-pre-compact.sh, pai-session-stop.sh (installed)" : "(unchanged)"));
|
|
5335
5431
|
console.log(chalk.dim(" Hooks (TS): ") + chalk.cyan(tsHooksInstalled ? "14 .mjs hooks installed to ~/.claude/Hooks/" : "(unchanged)"));
|
|
5336
5432
|
console.log(chalk.dim(" Assistant name: ") + chalk.cyan(daName));
|
|
@@ -5394,6 +5490,7 @@ async function runSetup() {
|
|
|
5394
5490
|
const claudeMdGenerated = await stepClaudeMd(rl);
|
|
5395
5491
|
const paiSkillInstalled = await stepPaiSkill(rl);
|
|
5396
5492
|
const aiSteeringRulesInstalled = await stepAiSteeringRules(rl);
|
|
5493
|
+
const skillStubsInstalled = await stepSkillStubs(rl);
|
|
5397
5494
|
const hooksInstalled = await stepHooks(rl);
|
|
5398
5495
|
const tsHooksInstalled = await stepTsHooks(rl);
|
|
5399
5496
|
const daName = await stepDaName(rl);
|
|
@@ -5409,7 +5506,7 @@ async function runSetup() {
|
|
|
5409
5506
|
line$1();
|
|
5410
5507
|
console.log(chalk.green(" Configuration saved."));
|
|
5411
5508
|
await stepInitialIndex(rl);
|
|
5412
|
-
stepSummary(allUpdates, claudeMdGenerated, paiSkillInstalled, aiSteeringRulesInstalled, hooksInstalled, tsHooksInstalled, settingsPatched, daName, daemonInstalled, mcpRegistered);
|
|
5509
|
+
stepSummary(allUpdates, claudeMdGenerated, paiSkillInstalled, aiSteeringRulesInstalled, skillStubsInstalled, hooksInstalled, tsHooksInstalled, settingsPatched, daName, daemonInstalled, mcpRegistered);
|
|
5413
5510
|
} finally {
|
|
5414
5511
|
rl.close();
|
|
5415
5512
|
}
|