@tekmidian/pai 1.0.0 → 1.0.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/.claude-plugin/plugin.json +1 -1
- package/README.md +67 -5
- package/dist/aibroker-client-BxzeEHPF.mjs +3 -0
- package/dist/{aibroker-client-CXEYBzbf.mjs → aibroker-client-CQPGqLxL.mjs} +1 -1
- package/dist/atomic-file-BorMa4T3.mjs +21 -0
- package/dist/{auto-route-D5SXrMpB.mjs → auto-route-B4azPk4l.mjs} +3 -3
- package/dist/auto-route-BjoMHKyI.mjs +11 -0
- package/dist/budget-Dod6rnA9.mjs +15 -0
- package/dist/{caller-identity-CVTuiLSs.mjs → caller-identity-yLFb7CKq.mjs} +6 -5
- package/dist/{capacity-DaXrVj3J.mjs → capacity-DEveQdFV.mjs} +3 -3
- package/dist/{chain-BcQlIlEd.mjs → chain-D-yvET_y.mjs} +5 -5
- package/dist/cli/index.mjs +1621 -357
- package/dist/cockpit-actions-BWunx3rY.mjs +20 -0
- package/dist/{cockpit-actions-ODmt8yF9.mjs → cockpit-actions-C8GlTzts.mjs} +84 -42
- package/dist/cockpit-memory-vTlFWV7F.mjs +20 -0
- package/dist/{config-BSXKyeJ1.mjs → config-Bu1_vbY8.mjs} +42 -3
- package/dist/{config-B955yYmo.mjs → config-Cxi-aBf4.mjs} +2 -2
- package/dist/{config-TaWmdTLx.mjs → config-DtENJ6cL.mjs} +1 -1
- package/dist/{config-D5chi2LO.mjs → config-XpZWOYOG.mjs} +2 -2
- package/dist/{controls-BGkK_Bdi.mjs → controls-BcKFHeG1.mjs} +37 -22
- package/dist/daemon/index.mjs +43 -42
- package/dist/{daemon-CorGh6Ub.mjs → daemon-ColVHwGZ.mjs} +103 -55
- package/dist/daemon-N7WhYi6I.mjs +45 -0
- package/dist/daemon-mcp/index.mjs +40 -39
- package/dist/{define-DGFu-sCf.mjs → define-CjJsmq-6.mjs} +1 -1
- package/dist/{detect-C1d5cUkT.mjs → detect-9CZNO0yf.mjs} +2 -2
- package/dist/detect-D1ziFFfA.mjs +10 -0
- package/dist/{embed-pass-CeQD8kRE.mjs → embed-pass-DIWjA68P.mjs} +5 -5
- package/dist/{embedding-gate-BHuBsgHD.mjs → embedding-gate-6rZrWRXa.mjs} +5 -5
- package/dist/{embedding-gate-Z7CJSOJk.mjs → embedding-gate-Cnk4YCyX.mjs} +3 -3
- package/dist/{embeddings-D4t6L-EH.mjs → embeddings-BVq3esBF.mjs} +1 -1
- package/dist/{embeddings-OF9Pt4by.mjs → embeddings-CIkJBKd-.mjs} +1 -1
- package/dist/{endreason-x1KOSrcO.mjs → endreason-C577xIQz.mjs} +7 -6
- package/dist/{env-DArqGq-Z.mjs → env-BE8mrsxr.mjs} +1 -1
- package/dist/factory-Bsib-qDG.mjs +9 -0
- package/dist/{factory-BtjbQ0np.mjs → factory-DjAAsqr_.mjs} +15 -13
- package/dist/{fallback-BIyEpPsQ.mjs → fallback-DFlItw1Z.mjs} +5 -5
- package/dist/{fmt-age-BiSHrTKN.mjs → fmt-age-ByDkvkoU.mjs} +1 -1
- package/dist/{health-C6jZWvRG.mjs → health-Dnym-qcT.mjs} +4 -4
- package/dist/{helpers-DFmYRCOa.mjs → helpers-BD6V-p9w.mjs} +2 -2
- package/dist/helpers-CMGjy_0N.mjs +10 -0
- package/dist/{helpers-CJpb_MNB.mjs → helpers-CRuBUas3.mjs} +87 -38
- package/dist/hooks/capture-all-events.mjs +52 -11
- package/dist/hooks/capture-session-summary.mjs +64 -47
- package/dist/hooks/capture-tool-output.mjs +49 -8
- package/dist/hooks/context-compression-hook.mjs +30 -10
- package/dist/hooks/initialize-session.mjs +1 -0
- package/dist/hooks/load-core-context.mjs +1 -1
- package/dist/hooks/load-project-context.mjs +32 -29
- package/dist/hooks/security-validator.mjs +45 -13
- package/dist/hooks/stop-hook.mjs +54 -16
- package/dist/hooks/supervision-banner.mjs +9 -0
- package/dist/hooks/sync-todo-to-md.mjs +147 -113
- package/dist/hooks/worker-edit-repair.mjs +2 -1
- package/dist/hooks/worker-guard.mjs +40 -16
- package/dist/hooks/worker-note.mjs +12 -5
- package/dist/hooks/worker-path-repair.mjs +1 -1
- package/dist/hooks/worker-pool-host.mjs +214 -116
- package/dist/hooks/worker-status-line.mjs +195 -114
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +12 -12
- package/dist/{indexer-backend-BM2CUSu8.mjs → indexer-backend-_a6UNhXJ.mjs} +149 -96
- package/dist/{ipc-client-BMqWw_gv.mjs → ipc-client-CaiGJnSM.mjs} +1 -1
- package/dist/{ipc-client-YYDD6zzU.mjs → ipc-client-ZRmDKd5Z.mjs} +1 -1
- package/dist/kill-tree-BG7dRVtp.mjs +10 -0
- package/dist/{kill-tree-BjYz5BxK.mjs → kill-tree-EWYaEk5v.mjs} +8 -8
- package/dist/{launch-CLp0OJxV.mjs → launch-CXVlZjHt.mjs} +8 -8
- package/dist/{launch-DgqQdAjI.mjs → launch-Jh5NVeNe.mjs} +3 -3
- package/dist/local-candidate-DLdwQt0s.mjs +112 -0
- package/dist/local-instances-DOxSgT_L.mjs +210 -0
- package/dist/local-instances-ZeRkkuS5.mjs +6 -0
- package/dist/{local-tiers-DPZYqv9m.mjs → local-tiers-BE4jGaMa.mjs} +2 -2
- package/dist/{local-tiers-DnBIAkbK.mjs → local-tiers-DNMI5W86.mjs} +3 -3
- package/dist/{main-config-ops-C0SflOWk.mjs → main-config-ops-BEMpuhDz.mjs} +3 -3
- package/dist/{main-resolver-DVPpSWVy.mjs → main-resolver-3Ns1HPmS.mjs} +18 -17
- package/dist/{migrate-BUJHadA8.mjs → migrate-DrA_OwlZ.mjs} +45 -14
- package/dist/min-max-DrSRUaWQ.mjs +16 -0
- package/dist/{model-inventory-Dasber3S.mjs → model-inventory-BFVSNFT3.mjs} +13 -12
- package/dist/{models-add-Bt7aUHDs.mjs → models-add-BtB3Se6f.mjs} +39 -38
- package/dist/operator-BTqvTIc8.mjs +7 -0
- package/dist/{operator-DZxcP-g8.mjs → operator-CyuB3aVw.mjs} +3 -3
- package/dist/{pai-marker-kIYuNt3c.mjs → pai-marker-DurzIbw8.mjs} +7 -6
- package/dist/{pane-audit-OGYrOAik.mjs → pane-audit-BQytYa60.mjs} +7 -7
- package/dist/{paths-DoRuFc60.mjs → paths-CFHTloSe.mjs} +3 -3
- package/dist/{paths-DoIyRnhx.mjs → paths-CHifubDh.mjs} +3 -3
- package/dist/{planner-DLGOYlVb.mjs → planner-BM27GBfC.mjs} +31 -30
- package/dist/{pool-CZ5MZaXg.mjs → pool-BRvoOkv6.mjs} +3 -3
- package/dist/{pool-cli-BxNSPzzY.mjs → pool-cli-B_YR8_Gl.mjs} +5 -5
- package/dist/{pool-cli-CbJfl37y.mjs → pool-cli-DkZTAlSW.mjs} +6 -6
- package/dist/postgres-BdBNkL_l.mjs +7 -0
- package/dist/{postgres-CDnd_k5f.mjs → postgres-C6ti4haA.mjs} +55 -34
- package/dist/{project-setup-DM4npnL3.mjs → project-setup-CfpApz5o.mjs} +131 -74
- package/dist/providers-BeDiVEb-.mjs +16 -0
- package/dist/{providers-UsKtLcUR.mjs → providers-CblTFrhz.mjs} +6 -6
- package/dist/{query-feedback-I-txSnld.mjs → query-feedback-BRfPtqki.mjs} +2 -2
- package/dist/registrable-C7b6givx.mjs +9 -0
- package/dist/{registrable-LrRIVYQc.mjs → registrable-Uzgmfyhz.mjs} +4 -4
- package/dist/render-Cc2j5QG9.mjs +13 -0
- package/dist/{render-BDbb4CF-.mjs → render-CwIrgxLi.mjs} +8 -8
- package/dist/{reranker-B4r4vykM.mjs → reranker-8g6xMwP7.mjs} +1 -1
- package/dist/{reranker-DxxMCtA0.mjs → reranker-CqCbpGnX.mjs} +1 -1
- package/dist/{root-BMeJuHOb.mjs → root-BWy0c2Lq.mjs} +11 -11
- package/dist/root-wC_6Fx0B.mjs +36 -0
- package/dist/route-table-B7zpoypi.mjs +15 -0
- package/dist/{router-L8Nu-OHF.mjs → router-CQSc3lk3.mjs} +17 -7
- package/dist/{router-D6Ec6Fid.mjs → router-DwHJRgG9.mjs} +1 -1
- package/dist/{routing-CUyiglfu.mjs → routing-BAAST9vT.mjs} +27 -308
- package/dist/{routing-ZuGlc0ge.mjs → routing-CENt4HH1.mjs} +13 -12
- package/dist/{run-ulAi5dFD.mjs → run-6Q9kCPem.mjs} +35 -33
- package/dist/{scan-B7I0gPe-.mjs → scan-ZQWm-_se.mjs} +6 -6
- package/dist/{local-candidate-jOjTNlOL.mjs → scheduler-CtUyWJ3O.mjs} +7 -109
- package/dist/scheduler-dmHXoRvm.mjs +3 -0
- package/dist/{scope-D5VSQxe4.mjs → scope-D-h6F6DN.mjs} +4 -4
- package/dist/{search-CPoE0830.mjs → search-RSyeQqj1.mjs} +18 -18
- package/dist/session-Bok2PqRS.mjs +35 -0
- package/dist/{session-CNXF6DLP.mjs → session-DVUExUnX.mjs} +55 -31
- package/dist/session-scan-BJI_ZO8M.mjs +9 -0
- package/dist/{session-scan-BDLEc4w1.mjs → session-scan-W-gbwJHM.mjs} +6 -6
- package/dist/{sources-CjhUKKtO.mjs → sources-ESUkZaWv.mjs} +1 -1
- package/dist/{status-DYydKQUI.mjs → status-Dm4dYciN.mjs} +4 -4
- package/dist/{status-CvVi9cDf.mjs → status-ggJ7lgVg.mjs} +17 -9
- package/dist/{stop-words-SdSL1WKH.mjs → stop-words-DSEYQdNe.mjs} +1 -1
- package/dist/{test-isolation-CoDeOVXb.mjs → test-isolation-lqSmYxr0.mjs} +1 -1
- package/dist/{timeouts-OwEONmc8.mjs → timeouts-1MLooEBB.mjs} +1 -1
- package/dist/{tool-errors-AeRQuG3Y.mjs → tool-errors-7E13StWd.mjs} +4 -3
- package/dist/{tool-errors-R5E0fCgf.mjs → tool-errors-CJWODvlo.mjs} +6 -6
- package/dist/{viewer-DtWj5Vti.mjs → viewer-DcpJwGKE.mjs} +15 -15
- package/dist/viewer-MWBVJP3u.mjs +22 -0
- package/dist/{wait-BMUR-U-U.mjs → wait-ByV0OyMZ.mjs} +3 -3
- package/dist/{worker-config-BLqa1yiD.mjs → worker-config-BDYNxbYz.mjs} +294 -92
- package/dist/worker-config-gmAq_-rI.mjs +41 -0
- package/dist/{workers-config-DEI9lrV4.mjs → workers-config-f8KrkY4L.mjs} +2 -2
- package/dist/{worktree-zmRf556h.mjs → worktree-CrnOqTBW.mjs} +37 -7
- package/dist/{zettelkasten-nigq7fqs.mjs → zettelkasten-B_rRRRvD.mjs} +5 -5
- package/dist/{zettelkasten-Cr_Xj0mG.mjs → zettelkasten-BzMm8REH.mjs} +29 -19
- package/docs/README.md +2 -0
- package/docs/cockpit.md +10 -4
- package/docs/commands/README.md +4 -4
- package/docs/commands/backup.md +2 -0
- package/docs/commands/install.md +29 -4
- package/docs/commands/providers.md +1 -1
- package/docs/commands/restore.md +4 -0
- package/docs/commands/setup.md +29 -4
- package/docs/commands/worker.md +1 -1
- package/docs/config.md +2 -0
- package/docs/features.md +299 -0
- package/docs/images/architecture.drawio +51 -0
- package/docs/images/architecture.svg +3 -0
- package/docs/images/cockpit-01-system-tokens.jpg +0 -0
- package/docs/images/cockpit-02-totals-cache.jpg +0 -0
- package/docs/images/cockpit-03-local-servers-models-pool.jpg +0 -0
- package/docs/images/cockpit-04-workers-memory.jpg +0 -0
- package/docs/images/cockpit-05-performance.jpg +0 -0
- package/docs/images/cockpit-06-runs-speed-routing.jpg +0 -0
- package/docs/images/cockpit-07-settings-registry.jpg +0 -0
- package/docs/install/index.md +29 -0
- package/docs/three-tier-coding.md +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +1 -5
- package/pai-plugin.json +1 -1
- package/plugins/context-preservation/plugin.json +1 -1
- package/plugins/core/plugin.json +1 -1
- package/plugins/core/skills/setup/SKILL.md +41 -9
- package/plugins/creative/plugin.json +1 -1
- package/plugins/observability/plugin.json +1 -1
- package/plugins/productivity/plugin.json +1 -1
- package/plugins/semantic-search/plugin.json +1 -1
- package/plugins/ui/plugin.json +1 -1
- package/plugins/zettelkasten/plugin.json +1 -1
- package/src/hooks/session-autosave.sh +8 -1
- package/src/hooks/session-stop.sh +8 -1
- package/src/hooks/ts/capture-all-events.ts +7 -12
- package/src/hooks/ts/lib/context-fill.ts +2 -1
- package/src/hooks/ts/lib/path-repair.ts +1 -1
- package/src/hooks/ts/lib/project-utils/notify.ts +1 -0
- package/src/hooks/ts/lib/project-utils/session-notes.ts +23 -6
- package/src/hooks/ts/lib/raw-capture.ts +36 -0
- package/src/hooks/ts/lib/session-summary.ts +129 -0
- package/src/hooks/ts/lib/todo-sync.ts +120 -0
- package/src/hooks/ts/lib/worker-git-guard.ts +1 -1
- package/src/hooks/ts/post-tool-use/capture-tool-output.ts +6 -10
- package/src/hooks/ts/post-tool-use/sync-todo-to-md.ts +5 -105
- package/src/hooks/ts/pre-compact/context-compression-hook.ts +1 -1
- package/src/hooks/ts/pre-tool-use/security-validator.ts +35 -14
- package/src/hooks/ts/session-end/capture-session-summary.ts +20 -128
- package/src/hooks/ts/session-start/load-core-context.ts +3 -3
- package/src/hooks/ts/session-start/load-project-context.ts +18 -22
- package/src/hooks/ts/stop/stop-hook.ts +13 -5
- package/src/hooks/ztk-auto.sh +7 -2
- package/dist/aibroker-client-JjE74jo0.mjs +0 -3
- package/dist/auto-route-BRk8NEFv.mjs +0 -11
- package/dist/budget-DuhQ2zzG.mjs +0 -14
- package/dist/cockpit-actions-DcQ-ucU8.mjs +0 -19
- package/dist/cockpit-memory-KV45PPO2.mjs +0 -19
- package/dist/daemon-C7yt_ptC.mjs +0 -44
- package/dist/detect-DRUkmHnb.mjs +0 -10
- package/dist/factory-DAGG9_n8.mjs +0 -9
- package/dist/helpers-DPJEHPq6.mjs +0 -10
- package/dist/kill-tree-LV0Q7Mte.mjs +0 -10
- package/dist/operator-CfL0HiBK.mjs +0 -7
- package/dist/postgres-DgqWyqhN.mjs +0 -7
- package/dist/providers-CTu2s1z2.mjs +0 -15
- package/dist/registrable-D9aNZOTh.mjs +0 -9
- package/dist/render-BSfVlYHj.mjs +0 -13
- package/dist/root-xI9DqVIe.mjs +0 -35
- package/dist/route-table-HjvnLVEU.mjs +0 -14
- package/dist/session-BJHzmUg2.mjs +0 -34
- package/dist/session-scan-YORKE1NO.mjs +0 -9
- package/dist/viewer-B8709jDN.mjs +0 -22
- package/dist/worker-config-D8tRuvo7.mjs +0 -40
- package/docs/images/cockpit-limits-routing.jpg +0 -0
- package/docs/images/cockpit-models.jpg +0 -0
- package/docs/images/cockpit-usage.jpg +0 -0
- package/docs/images/cockpit-workers-system.jpg +0 -0
- package/scripts/build-docs.mjs +0 -41
- package/scripts/build-hooks.mjs +0 -311
- package/scripts/build-skill-stubs.mjs +0 -216
- package/scripts/copy-defaults.mjs +0 -46
package/scripts/build-hooks.mjs
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Build TypeScript hooks into standalone .mjs files using esbuild.
|
|
4
|
-
*
|
|
5
|
-
* Each hook is fully self-contained — lib/ dependencies are inlined.
|
|
6
|
-
* Output: dist/hooks/<name>.mjs with #!/usr/bin/env node shebang.
|
|
7
|
-
*
|
|
8
|
-
* With --sync: also creates/updates symlinks (or copies on Windows) from
|
|
9
|
-
* ~/.claude/Hooks/ and ~/.claude/ to the built/source files, plus the
|
|
10
|
-
* PAI_HOME/agents and PAI_HOME/commands adapter symlinks at ~/.claude/agents
|
|
11
|
-
* and ~/.claude/commands (once `pai config migrate` has populated them once).
|
|
12
|
-
* This ensures that `bun run build` is the only step needed to deploy updates.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { buildSync } from "esbuild";
|
|
16
|
-
import {
|
|
17
|
-
readdirSync,
|
|
18
|
-
statSync,
|
|
19
|
-
chmodSync,
|
|
20
|
-
existsSync,
|
|
21
|
-
mkdirSync,
|
|
22
|
-
symlinkSync,
|
|
23
|
-
lstatSync,
|
|
24
|
-
readlinkSync,
|
|
25
|
-
unlinkSync,
|
|
26
|
-
copyFileSync,
|
|
27
|
-
readFileSync,
|
|
28
|
-
writeFileSync,
|
|
29
|
-
renameSync,
|
|
30
|
-
rmSync,
|
|
31
|
-
} from "fs";
|
|
32
|
-
import { join, resolve, basename } from "path";
|
|
33
|
-
import { homedir, platform } from "os";
|
|
34
|
-
import { paiHomeDir as resolvePaiHomeDir, syncSkipReason } from "./lib/sync-guard.mjs";
|
|
35
|
-
|
|
36
|
-
const HOOKS_SRC = "src/hooks/ts";
|
|
37
|
-
const HOOKS_OUT = "dist/hooks";
|
|
38
|
-
const doSync = process.argv.includes("--sync");
|
|
39
|
-
|
|
40
|
-
// Bundled CJS deps (e.g. yaml) call require() for Node builtins like
|
|
41
|
-
// "process". esbuild's ESM output leaves those calls as a `require` that
|
|
42
|
-
// only exists at runtime if the module defines it — plain .mjs run via node
|
|
43
|
-
// has no `require`, so the call throws "Dynamic require of ... is not
|
|
44
|
-
// supported". createRequire gives every bundle a real one.
|
|
45
|
-
//
|
|
46
|
-
// PAI_QUIET_NOTICES=1 silences one-time stderr notices (e.g. the PAI_DIR
|
|
47
|
-
// deprecation warning in pai-paths.ts) for every hook and standalone script
|
|
48
|
-
// built here — their stderr is machinery Claude Code inspects for errors and
|
|
49
|
-
// the statusline pipes through, never a human terminal, so it must stay
|
|
50
|
-
// clean. Set only if unset, so a caller that deliberately wants the notices
|
|
51
|
-
// (debugging) can still get them.
|
|
52
|
-
const REQUIRE_SHIM = {
|
|
53
|
-
js: "import { createRequire as __paiCreateRequire } from 'node:module';\nconst require = __paiCreateRequire(import.meta.url);\nif (!process.env.PAI_QUIET_NOTICES) process.env.PAI_QUIET_NOTICES = '1';",
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// Collect all .ts entry points (skip lib/ — those are bundled into each hook)
|
|
57
|
-
function collectEntryPoints(dir) {
|
|
58
|
-
const entries = [];
|
|
59
|
-
for (const name of readdirSync(dir)) {
|
|
60
|
-
const full = join(dir, name);
|
|
61
|
-
if (name === "lib") continue;
|
|
62
|
-
if (statSync(full).isDirectory()) {
|
|
63
|
-
entries.push(...collectEntryPoints(full));
|
|
64
|
-
} else if (name.endsWith(".ts") && !name.endsWith(".test.ts")) {
|
|
65
|
-
entries.push(full);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return entries;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const entryPoints = collectEntryPoints(HOOKS_SRC);
|
|
72
|
-
|
|
73
|
-
console.log(`Building ${entryPoints.length} hooks with esbuild...`);
|
|
74
|
-
|
|
75
|
-
// Build into a staging directory, then rename each artefact into place.
|
|
76
|
-
//
|
|
77
|
-
// ~/.claude/Hooks/*.mjs are symlinks to these files, and every LIVE Claude Code
|
|
78
|
-
// session executes them on each tool call. esbuild truncates-then-writes, so
|
|
79
|
-
// building straight to the output path leaves a window in which the file does
|
|
80
|
-
// not exist — any hook firing during a build in another session dies with
|
|
81
|
-
// "No such file or directory". That is not hypothetical: a rebuild here breaks
|
|
82
|
-
// hooks in every other session that happens to fire during it.
|
|
83
|
-
//
|
|
84
|
-
// rename(2) within one filesystem is atomic, so a concurrent session sees
|
|
85
|
-
// either the previous build or the new one, never a partial or absent file.
|
|
86
|
-
const STAGING = join(HOOKS_OUT, ".staging");
|
|
87
|
-
rmSync(STAGING, { recursive: true, force: true });
|
|
88
|
-
mkdirSync(STAGING, { recursive: true });
|
|
89
|
-
|
|
90
|
-
for (const entry of entryPoints) {
|
|
91
|
-
const name = basename(entry).replace(/\.ts$/, ".mjs");
|
|
92
|
-
const staged = join(STAGING, name);
|
|
93
|
-
|
|
94
|
-
buildSync({
|
|
95
|
-
entryPoints: [entry],
|
|
96
|
-
bundle: true,
|
|
97
|
-
platform: "node",
|
|
98
|
-
target: "node20",
|
|
99
|
-
format: "esm",
|
|
100
|
-
outfile: staged,
|
|
101
|
-
sourcemap: true,
|
|
102
|
-
banner: REQUIRE_SHIM,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
chmodSync(staged, 0o755);
|
|
106
|
-
|
|
107
|
-
// The sourceMappingURL comment is a bare filename, so the map must land in
|
|
108
|
-
// the same directory as the module. Move it first: a stale map for a moment
|
|
109
|
-
// is harmless, a missing module is not.
|
|
110
|
-
const stagedMap = `${staged}.map`;
|
|
111
|
-
if (existsSync(stagedMap)) {
|
|
112
|
-
renameSync(stagedMap, join(HOOKS_OUT, `${name}.map`));
|
|
113
|
-
}
|
|
114
|
-
renameSync(staged, join(HOOKS_OUT, name));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
rmSync(STAGING, { recursive: true, force: true });
|
|
118
|
-
|
|
119
|
-
console.log(`✔ ${entryPoints.length} hooks built to ${HOOKS_OUT}/`);
|
|
120
|
-
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
// Standalone worker status-line: src/workers/standalone/status-line.ts →
|
|
123
|
-
// dist/worker-status-line.mjs (same atomic-staging rules; it runs on every
|
|
124
|
-
// statusline refresh of every live session).
|
|
125
|
-
// ---------------------------------------------------------------------------
|
|
126
|
-
|
|
127
|
-
// [entry, output name in dist/hooks/]
|
|
128
|
-
const STANDALONE_ENTRIES = [
|
|
129
|
-
["src/workers/standalone/status-line.ts", "worker-status-line.mjs"],
|
|
130
|
-
// detached proxy for openai-protocol providers; spawned from dist, not
|
|
131
|
-
// symlinked into ~/.claude, so it is NOT in the --sync list below
|
|
132
|
-
["src/workers/standalone/proxy.ts", "worker-proxy.mjs"],
|
|
133
|
-
// detached worker session pool host (src/workers/pool-host.ts); spawned
|
|
134
|
-
// from dist like the proxy, NOT symlinked into ~/.claude
|
|
135
|
-
["src/workers/standalone/pool-host.ts", "worker-pool-host.mjs"],
|
|
136
|
-
];
|
|
137
|
-
|
|
138
|
-
mkdirSync(STAGING, { recursive: true });
|
|
139
|
-
for (const [entry, name] of STANDALONE_ENTRIES) {
|
|
140
|
-
const staged = join(STAGING, name);
|
|
141
|
-
buildSync({
|
|
142
|
-
entryPoints: [entry],
|
|
143
|
-
bundle: true,
|
|
144
|
-
platform: "node",
|
|
145
|
-
target: "node20",
|
|
146
|
-
format: "esm",
|
|
147
|
-
outfile: staged,
|
|
148
|
-
sourcemap: true,
|
|
149
|
-
banner: REQUIRE_SHIM,
|
|
150
|
-
// the pool host pulls the storage factory (via run.ts → project-config),
|
|
151
|
-
// whose embeddings backend needs the native onnx binding — native .node
|
|
152
|
-
// files can never be bundled; resolve it at runtime like the rest of
|
|
153
|
-
// node_modules (the host runs from dist/, inside the repo)
|
|
154
|
-
external: ["onnxruntime-node"],
|
|
155
|
-
});
|
|
156
|
-
chmodSync(staged, 0o755);
|
|
157
|
-
const stagedMap = `${staged}.map`;
|
|
158
|
-
if (existsSync(stagedMap)) {
|
|
159
|
-
renameSync(stagedMap, join(HOOKS_OUT, `${name}.map`));
|
|
160
|
-
}
|
|
161
|
-
mkdirSync(HOOKS_OUT, { recursive: true });
|
|
162
|
-
renameSync(staged, join(HOOKS_OUT, name));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
rmSync(STAGING, { recursive: true, force: true });
|
|
166
|
-
|
|
167
|
-
console.log(
|
|
168
|
-
`✔ ${STANDALONE_ENTRIES.length} standalone script(s) built to ${HOOKS_OUT}/ (${STANDALONE_ENTRIES.map(([, n]) => n).join(", ")})`
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
// ---------------------------------------------------------------------------
|
|
172
|
-
// --sync: Symlink (or copy on Windows) all deployable files to ~/.claude/
|
|
173
|
-
// ---------------------------------------------------------------------------
|
|
174
|
-
|
|
175
|
-
const skipReason = doSync ? syncSkipReason() : null;
|
|
176
|
-
if (skipReason) {
|
|
177
|
-
// Only the canonical install repoints the live ~/.claude symlinks — see
|
|
178
|
-
// scripts/lib/sync-guard.mjs.
|
|
179
|
-
console.log(skipReason.startsWith("skipping") ? skipReason : `✔ Hook symlinks skipped: ${skipReason}`);
|
|
180
|
-
} else if (doSync) {
|
|
181
|
-
const useSymlinks = platform() !== "win32";
|
|
182
|
-
const claudeDir = join(homedir(), ".claude");
|
|
183
|
-
const hooksTarget = join(claudeDir, "Hooks");
|
|
184
|
-
mkdirSync(hooksTarget, { recursive: true });
|
|
185
|
-
|
|
186
|
-
let created = 0;
|
|
187
|
-
let updated = 0;
|
|
188
|
-
let current = 0;
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Ensure `target` is a symlink (or copy on Windows) pointing to `source`.
|
|
192
|
-
* Replaces stale symlinks and plain-file copies with correct symlinks.
|
|
193
|
-
* Never overwrites non-symlink, non-PAI files (user's own scripts).
|
|
194
|
-
*/
|
|
195
|
-
function syncFile(source, target) {
|
|
196
|
-
const absSource = resolve(source);
|
|
197
|
-
|
|
198
|
-
if (!existsSync(absSource)) {
|
|
199
|
-
console.warn(` ⚠ Source not found: ${source}`);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Check existing target (lstat doesn't follow symlinks)
|
|
204
|
-
let isUpdate = false;
|
|
205
|
-
try {
|
|
206
|
-
const stat = lstatSync(target);
|
|
207
|
-
if (stat.isSymbolicLink()) {
|
|
208
|
-
if (resolve(readlinkSync(target)) === absSource) {
|
|
209
|
-
current++;
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
unlinkSync(target);
|
|
213
|
-
isUpdate = true;
|
|
214
|
-
} else if (stat.isFile()) {
|
|
215
|
-
unlinkSync(target);
|
|
216
|
-
isUpdate = true;
|
|
217
|
-
} else {
|
|
218
|
-
return; // Directory or something unexpected — don't touch
|
|
219
|
-
}
|
|
220
|
-
} catch {
|
|
221
|
-
// Target doesn't exist — fresh install
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (useSymlinks) {
|
|
225
|
-
symlinkSync(absSource, target);
|
|
226
|
-
} else {
|
|
227
|
-
copyFileSync(absSource, target);
|
|
228
|
-
chmodSync(target, 0o755);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (isUpdate) {
|
|
232
|
-
updated++;
|
|
233
|
-
} else {
|
|
234
|
-
created++;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// 1. TypeScript hooks: dist/hooks/*.mjs → ~/.claude/Hooks/*.mjs
|
|
239
|
-
// (worker-proxy.mjs and worker-pool-host.mjs are spawned from dist, not
|
|
240
|
-
// hooks — skip them)
|
|
241
|
-
const mjsFiles = readdirSync(HOOKS_OUT).filter(
|
|
242
|
-
(f) => f.endsWith(".mjs") && f !== "worker-proxy.mjs" && f !== "worker-pool-host.mjs"
|
|
243
|
-
);
|
|
244
|
-
for (const filename of mjsFiles) {
|
|
245
|
-
syncFile(join(HOOKS_OUT, filename), join(hooksTarget, filename));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// 2. Shell hooks: src/hooks/*.sh → ~/.claude/Hooks/pai-*.sh
|
|
249
|
-
const shellHooks = [
|
|
250
|
-
["src/hooks/pre-compact.sh", "pai-pre-compact.sh"],
|
|
251
|
-
["src/hooks/session-stop.sh", "pai-session-stop.sh"],
|
|
252
|
-
["src/hooks/session-autosave.sh", "pai-session-autosave.sh"],
|
|
253
|
-
["src/hooks/ztk-auto.sh", "ztk-auto.sh"],
|
|
254
|
-
["src/hooks/ztk-passthrough.mjs", "ztk-passthrough.mjs"],
|
|
255
|
-
];
|
|
256
|
-
for (const [src, destName] of shellHooks) {
|
|
257
|
-
if (existsSync(src)) {
|
|
258
|
-
syncFile(src, join(hooksTarget, destName));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// 3. Root scripts: statusline + tab-color → ~/.claude/
|
|
263
|
-
const rootScripts = ["statusline-command.sh", "tab-color-command.sh"];
|
|
264
|
-
for (const script of rootScripts) {
|
|
265
|
-
if (existsSync(script)) {
|
|
266
|
-
syncFile(script, join(claudeDir, script));
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// 4. Standalone worker status-line: dist/worker-status-line.mjs → ~/.claude/
|
|
271
|
-
syncFile(join(HOOKS_OUT, "worker-status-line.mjs"), join(claudeDir, "worker-status-line.mjs"));
|
|
272
|
-
|
|
273
|
-
// 5. Content dirs with an adapter symlink: PAI_HOME/agents, PAI_HOME/commands
|
|
274
|
-
// → ~/.claude/agents, ~/.claude/commands. Only acts once `pai config migrate`
|
|
275
|
-
// has populated the PAI_HOME copy on this machine — a real (non-symlink)
|
|
276
|
-
// agents/commands dir is the operator's un-migrated authored content and is
|
|
277
|
-
// never touched here, same rule as syncFile() above.
|
|
278
|
-
function syncContentDirSymlink(paiHomeSubdir, targetDir) {
|
|
279
|
-
if (!existsSync(paiHomeSubdir)) return; // not migrated yet — nothing to link to
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
const stat = lstatSync(targetDir);
|
|
283
|
-
if (stat.isSymbolicLink()) {
|
|
284
|
-
if (resolve(readlinkSync(targetDir)) === resolve(paiHomeSubdir)) {
|
|
285
|
-
current++;
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
unlinkSync(targetDir);
|
|
289
|
-
symlinkSync(resolve(paiHomeSubdir), targetDir);
|
|
290
|
-
updated++;
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
// Real directory — leave it alone (`pai config migrate` is what moves it).
|
|
294
|
-
return;
|
|
295
|
-
} catch {
|
|
296
|
-
// Target doesn't exist — fresh symlink.
|
|
297
|
-
symlinkSync(resolve(paiHomeSubdir), targetDir);
|
|
298
|
-
created++;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
const paiHomeDir = resolvePaiHomeDir();
|
|
303
|
-
syncContentDirSymlink(join(paiHomeDir, "agents"), join(claudeDir, "agents"));
|
|
304
|
-
syncContentDirSymlink(join(paiHomeDir, "commands"), join(claudeDir, "commands"));
|
|
305
|
-
|
|
306
|
-
const parts = [];
|
|
307
|
-
if (created > 0) parts.push(`${created} created`);
|
|
308
|
-
if (updated > 0) parts.push(`${updated} updated`);
|
|
309
|
-
if (current > 0) parts.push(`${current} current`);
|
|
310
|
-
console.log(`✔ Hook symlinks synced: ${parts.join(", ")}`);
|
|
311
|
-
}
|
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Generate SKILL.md files from MCP prompt definitions.
|
|
4
|
-
*
|
|
5
|
-
* Sources (in order):
|
|
6
|
-
* 1. src/daemon-mcp/prompts/*.ts — PAI built-in prompts (tracked in git)
|
|
7
|
-
* 2. src/daemon-mcp/prompts/custom/*.ts — User-created prompts (gitignored)
|
|
8
|
-
*
|
|
9
|
-
* Each prompt becomes a discoverable Claude Code skill at:
|
|
10
|
-
* dist/skills/<TitleCase>/SKILL.md
|
|
11
|
-
*
|
|
12
|
-
* Installation: `pai setup` or `pai skills sync` symlinks (macOS/Linux)
|
|
13
|
-
* or copies (Windows) each skill directory into ~/.claude/skills/.
|
|
14
|
-
*
|
|
15
|
-
* With --sync: also creates/updates symlinks in ~/.claude/skills/ after
|
|
16
|
-
* generating stubs. This is called automatically during `bun run build`.
|
|
17
|
-
*
|
|
18
|
-
* Source of truth: the TypeScript prompt files. Skills are regenerated on
|
|
19
|
-
* every build — never edit the generated SKILL.md files by hand.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import {
|
|
23
|
-
readFileSync,
|
|
24
|
-
writeFileSync,
|
|
25
|
-
mkdirSync,
|
|
26
|
-
existsSync,
|
|
27
|
-
readdirSync,
|
|
28
|
-
symlinkSync,
|
|
29
|
-
lstatSync,
|
|
30
|
-
readlinkSync,
|
|
31
|
-
unlinkSync,
|
|
32
|
-
} from "fs";
|
|
33
|
-
import { join, resolve } from "path";
|
|
34
|
-
import { homedir, platform } from "os";
|
|
35
|
-
import { syncSkipReason } from "./lib/sync-guard.mjs";
|
|
36
|
-
|
|
37
|
-
const PROMPTS_DIR = "src/daemon-mcp/prompts";
|
|
38
|
-
const CUSTOM_DIR = join(PROMPTS_DIR, "custom");
|
|
39
|
-
const STUBS_OUT = "dist/skills";
|
|
40
|
-
const doSync = process.argv.includes("--sync");
|
|
41
|
-
|
|
42
|
-
// kebab-case → TitleCase for Claude Code's skill scanner
|
|
43
|
-
function toTitleCase(promptName) {
|
|
44
|
-
return promptName
|
|
45
|
-
.split("-")
|
|
46
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
47
|
-
.join("");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Extract description and full content from a prompt .ts file
|
|
51
|
-
function parsePrompt(filePath) {
|
|
52
|
-
const src = readFileSync(filePath, "utf-8");
|
|
53
|
-
|
|
54
|
-
// Extract the description field
|
|
55
|
-
const descMatch = src.match(/description:\s*["'`]([^"'`]+)["'`]/);
|
|
56
|
-
if (!descMatch) return null;
|
|
57
|
-
const description = descMatch[1];
|
|
58
|
-
|
|
59
|
-
// Extract USE WHEN line
|
|
60
|
-
const useWhenMatch = src.match(/USE WHEN[^\n]+/);
|
|
61
|
-
const useWhen = useWhenMatch ? useWhenMatch[0] : "";
|
|
62
|
-
|
|
63
|
-
// Extract the full content template string
|
|
64
|
-
// Match content: `...` handling escaped backticks (\`) inside
|
|
65
|
-
const contentMatch = src.match(/content:\s*`((?:[^`\\]|\\[\s\S])*)`/);
|
|
66
|
-
if (!contentMatch) return null;
|
|
67
|
-
// Unescape template literal escapes
|
|
68
|
-
const content = contentMatch[1]
|
|
69
|
-
.replace(/\\`/g, "`")
|
|
70
|
-
.replace(/\\\$/g, "$")
|
|
71
|
-
.replace(/\\'/g, "'")
|
|
72
|
-
.replace(/\\n/g, "\n");
|
|
73
|
-
|
|
74
|
-
return { description, useWhen, content };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Read export list from index.ts to get built-in prompt file names
|
|
78
|
-
function getBuiltinPromptNames() {
|
|
79
|
-
const indexSrc = readFileSync(join(PROMPTS_DIR, "index.ts"), "utf-8");
|
|
80
|
-
const names = [];
|
|
81
|
-
for (const match of indexSrc.matchAll(
|
|
82
|
-
/export\s+\{[^}]+\}\s+from\s+["']\.\/([^"']+)\.js["']/g
|
|
83
|
-
)) {
|
|
84
|
-
names.push(match[1]);
|
|
85
|
-
}
|
|
86
|
-
return names;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Scan custom/ directory for user-created prompt files
|
|
90
|
-
function getCustomPromptNames() {
|
|
91
|
-
if (!existsSync(CUSTOM_DIR)) return [];
|
|
92
|
-
return readdirSync(CUSTOM_DIR)
|
|
93
|
-
.filter((f) => f.endsWith(".ts") && f !== "index.ts")
|
|
94
|
-
.map((f) => f.replace(/\.ts$/, ""));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Sync symlinks: create/update symlinks in ~/.claude/skills/
|
|
98
|
-
function syncSymlinks(generatedNames) {
|
|
99
|
-
const skillsDir = join(homedir(), ".claude", "skills");
|
|
100
|
-
mkdirSync(skillsDir, { recursive: true });
|
|
101
|
-
|
|
102
|
-
const useSymlinks = platform() !== "win32";
|
|
103
|
-
let created = 0;
|
|
104
|
-
let updated = 0;
|
|
105
|
-
let current = 0;
|
|
106
|
-
|
|
107
|
-
for (const name of generatedNames) {
|
|
108
|
-
const source = resolve(join(STUBS_OUT, name));
|
|
109
|
-
const target = join(skillsDir, name);
|
|
110
|
-
|
|
111
|
-
// lstat once: dangling links must count as symlinks (existsSync misses them)
|
|
112
|
-
let entry;
|
|
113
|
-
try {
|
|
114
|
-
entry = lstatSync(target);
|
|
115
|
-
} catch {
|
|
116
|
-
// absent — fresh install
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Never overwrite non-symlink directories (user's own skills)
|
|
120
|
-
if (entry && !entry.isSymbolicLink()) {
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Check existing symlink (may be dangling)
|
|
125
|
-
if (entry) {
|
|
126
|
-
if (resolve(readlinkSync(target)) === source) {
|
|
127
|
-
current++;
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
unlinkSync(target);
|
|
131
|
-
updated++;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (useSymlinks) {
|
|
135
|
-
symlinkSync(source, target);
|
|
136
|
-
} else {
|
|
137
|
-
mkdirSync(target, { recursive: true });
|
|
138
|
-
writeFileSync(
|
|
139
|
-
join(target, "SKILL.md"),
|
|
140
|
-
readFileSync(join(source, "SKILL.md")),
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
created++;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Clean up stale symlinks from old user/ location
|
|
147
|
-
const oldUserDir = join(skillsDir, "user");
|
|
148
|
-
if (existsSync(oldUserDir)) {
|
|
149
|
-
for (const name of generatedNames) {
|
|
150
|
-
const oldTarget = join(oldUserDir, name);
|
|
151
|
-
if (existsSync(oldTarget) && lstatSync(oldTarget).isSymbolicLink()) {
|
|
152
|
-
unlinkSync(oldTarget);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const parts = [];
|
|
158
|
-
if (created > 0) parts.push(`${created} created`);
|
|
159
|
-
if (updated > 0) parts.push(`${updated} updated`);
|
|
160
|
-
if (current > 0) parts.push(`${current} current`);
|
|
161
|
-
console.log(`✔ Skill symlinks synced: ${parts.join(", ")}`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// --- Main ---
|
|
165
|
-
|
|
166
|
-
const builtinNames = getBuiltinPromptNames();
|
|
167
|
-
const customNames = getCustomPromptNames();
|
|
168
|
-
let generated = 0;
|
|
169
|
-
const generatedDirNames = [];
|
|
170
|
-
|
|
171
|
-
mkdirSync(STUBS_OUT, { recursive: true });
|
|
172
|
-
|
|
173
|
-
// Process all prompts (built-in + custom)
|
|
174
|
-
for (const [fileName, dir] of [
|
|
175
|
-
...builtinNames.map((n) => [n, PROMPTS_DIR]),
|
|
176
|
-
...customNames.map((n) => [n, CUSTOM_DIR]),
|
|
177
|
-
]) {
|
|
178
|
-
const filePath = join(dir, `${fileName}.ts`);
|
|
179
|
-
const parsed = parsePrompt(filePath);
|
|
180
|
-
if (!parsed) {
|
|
181
|
-
console.warn(`⚠ Skipping ${fileName}: could not parse`);
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const dirName = toTitleCase(fileName);
|
|
186
|
-
const outDir = join(STUBS_OUT, dirName);
|
|
187
|
-
const outFile = join(outDir, "SKILL.md");
|
|
188
|
-
|
|
189
|
-
mkdirSync(outDir, { recursive: true });
|
|
190
|
-
|
|
191
|
-
const skill = [
|
|
192
|
-
"---",
|
|
193
|
-
`name: ${dirName}`,
|
|
194
|
-
`description: "${parsed.description}. ${parsed.useWhen}"`,
|
|
195
|
-
"---",
|
|
196
|
-
"",
|
|
197
|
-
parsed.content.trim(),
|
|
198
|
-
"",
|
|
199
|
-
].join("\n");
|
|
200
|
-
|
|
201
|
-
writeFileSync(outFile, skill);
|
|
202
|
-
generatedDirNames.push(dirName);
|
|
203
|
-
generated++;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const customLabel = customNames.length > 0 ? ` (${builtinNames.length} built-in + ${customNames.length} custom)` : "";
|
|
207
|
-
console.log(`✔ ${generated} skill stubs generated in ${STUBS_OUT}/${customLabel}`);
|
|
208
|
-
|
|
209
|
-
const skipReason = doSync ? syncSkipReason() : null;
|
|
210
|
-
if (skipReason) {
|
|
211
|
-
// Only the canonical install repoints the live ~/.claude/skills symlinks —
|
|
212
|
-
// see scripts/lib/sync-guard.mjs.
|
|
213
|
-
console.log(skipReason.startsWith("skipping") ? skipReason : `✔ Skill symlinks skipped: ${skipReason}`);
|
|
214
|
-
} else if (doSync) {
|
|
215
|
-
syncSymlinks(generatedDirNames);
|
|
216
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* copy-defaults.mjs — ship the defaults files into dist.
|
|
4
|
-
*
|
|
5
|
-
* src/config/defaults/config.yaml and workers.yaml are the shipped policy
|
|
6
|
-
* values (thresholds, limits, intervals, lists, mappings) that
|
|
7
|
-
* src/config/shipped-defaults.ts loads first and deep-merges UNDER the user's
|
|
8
|
-
* ~/.pai/config.yaml and workers.yaml. tsdown bundles code only, so this step
|
|
9
|
-
* copies the YAML files to dist/config/defaults/ — the same directory the
|
|
10
|
-
* bundled loader module resolves them from (dirname(import.meta.url)).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { copyFileSync, mkdirSync, existsSync, readFileSync } from "node:fs";
|
|
14
|
-
import { join, resolve } from "node:path";
|
|
15
|
-
|
|
16
|
-
const SRC_DIR = resolve("src/config/defaults");
|
|
17
|
-
const DIST_DIR = resolve(process.argv[2] ?? "dist", "config/defaults");
|
|
18
|
-
const FILES = ["config.yaml", "workers.yaml"];
|
|
19
|
-
|
|
20
|
-
mkdirSync(DIST_DIR, { recursive: true });
|
|
21
|
-
for (const name of FILES) {
|
|
22
|
-
const src = join(SRC_DIR, name);
|
|
23
|
-
if (!existsSync(src)) {
|
|
24
|
-
console.error(`copy-defaults: ${src} is missing — the shipped defaults must ship with the build`);
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
// sanity: the file must parse as a YAML mapping (cheap check, no yaml dep here)
|
|
28
|
-
const text = readFileSync(src, "utf8");
|
|
29
|
-
if (!text.trim().length) {
|
|
30
|
-
console.error(`copy-defaults: ${src} is empty`);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
copyFileSync(src, join(DIST_DIR, name));
|
|
34
|
-
console.log(`copy-defaults: ${src} → ${join(DIST_DIR, name)}`);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// the saved cockpit layout is optional: it exists once `pai cockpit layout export` has written it
|
|
38
|
-
const LAYOUT = join(SRC_DIR, "cockpit-layout.json");
|
|
39
|
-
if (existsSync(LAYOUT)) {
|
|
40
|
-
copyFileSync(LAYOUT, join(DIST_DIR, "cockpit-layout.json"));
|
|
41
|
-
console.log(`copy-defaults: ${LAYOUT} → ${join(DIST_DIR, "cockpit-layout.json")}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// esbuild bundles (tests, hooks) emit into directories next to where the
|
|
45
|
-
// loader module sits; a bundle's shippedDefaultsDir() falls back to
|
|
46
|
-
// <cwd>/src/config/defaults, which is SRC_DIR itself — nothing extra needed.
|