frida-pi-studio 0.6.1
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 +100 -0
- package/LICENSE +21 -0
- package/README.md +149 -0
- package/assets/logo.png +0 -0
- package/assets/logo.svg +35 -0
- package/docs/feature-catalog.md +148 -0
- package/docs/theme-gallery.html +197 -0
- package/package.json +126 -0
- package/packages/setup/bin/setup.mjs +430 -0
- package/packages/studio/extensions/branding.ts +153 -0
- package/packages/studio/extensions/frida-eval.ts +135 -0
- package/packages/studio/extensions/frida-footer.ts +98 -0
- package/packages/studio/extensions/frida-uris.ts +148 -0
- package/packages/studio/prompts/.gitkeep +0 -0
- package/packages/studio/prompts/rol-datos-ia.md +19 -0
- package/packages/studio/prompts/rol-dev.md +20 -0
- package/packages/studio/prompts/rol-fullstack.md +20 -0
- package/packages/studio/prompts/rol-seguridad.md +19 -0
- package/packages/studio/skills/.gitkeep +0 -0
- package/packages/studio/skills/gh-quoting/SKILL.md +49 -0
- package/packages/studio/skills/learn-review/SKILL.md +58 -0
- package/packages/studio/themes/arctic-aurora.json +84 -0
- package/packages/studio/themes/aurora-circuit.json +85 -0
- package/packages/studio/themes/frida.json +48 -0
- package/packages/studio/themes/violet-breakwater.json +83 -0
package/package.json
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "frida-pi-studio",
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "Distribución FRIDA sobre pi: kit (25 extensiones + theme + branding) — este package.json ES el paquete; packages/setup = CLI npx",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Edgar F. Fuentes Perea",
|
|
7
|
+
"bin": {
|
|
8
|
+
"frida-pi-studio": "packages/setup/bin/setup.mjs"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"pi-package"
|
|
12
|
+
],
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"@earendil-works/pi-ai": "*",
|
|
15
|
+
"@earendil-works/pi-agent-core": "*",
|
|
16
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
17
|
+
"@earendil-works/pi-tui": "*",
|
|
18
|
+
"typebox": "*"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@bacnh85/pi-notebooklm": "^0.1.8",
|
|
22
|
+
"@ff-labs/pi-fff": "^0.10.6",
|
|
23
|
+
"@gotgenes/pi-permission-system": "^31.1.1",
|
|
24
|
+
"@jachy/pi-git-sync": "^0.7.1",
|
|
25
|
+
"@juicesharp/rpiv-advisor": "^2.9.0",
|
|
26
|
+
"@juicesharp/rpiv-args": "^2.9.0",
|
|
27
|
+
"@juicesharp/rpiv-ask-user-question": "^2.9.0",
|
|
28
|
+
"@juicesharp/rpiv-i18n": "^2.9.0",
|
|
29
|
+
"@juicesharp/rpiv-pi": "^2.9.0",
|
|
30
|
+
"@juicesharp/rpiv-todo": "^2.9.0",
|
|
31
|
+
"@juicesharp/rpiv-web-tools": "^2.9.0",
|
|
32
|
+
"@juicesharp/rpiv-workflow": "^2.9.0",
|
|
33
|
+
"@llblab/pi-telegram": "^0.42.4",
|
|
34
|
+
"@monopi/analytics-db": "^0.6.1",
|
|
35
|
+
"@monopi/analytics-extension": "^0.6.1",
|
|
36
|
+
"@monopi/background-tasks": "^0.6.1",
|
|
37
|
+
"@monopi/diagnostics": "^0.6.1",
|
|
38
|
+
"@monopi/extension-git-guard": "^0.6.1",
|
|
39
|
+
"@monopi/extension-shared": "^0.6.1",
|
|
40
|
+
"@monopi/extension-watchdog": "^0.6.1",
|
|
41
|
+
"@monopi/remote-tailscale": "^0.6.1",
|
|
42
|
+
"@monopi/web-server": "^0.6.1",
|
|
43
|
+
"@mrclrchtr/supi-context": "^6.3.0",
|
|
44
|
+
"@mrclrchtr/supi-web": "^6.3.0",
|
|
45
|
+
"@narumitw/pi-worktree": "^0.51.7",
|
|
46
|
+
"@tintinweb/pi-subagents": "^0.19.0",
|
|
47
|
+
"pi-agent-browser-native": "^0.6.6",
|
|
48
|
+
"pi-antigravity": "^0.7.2",
|
|
49
|
+
"pi-extensible-workflows": "^5.13.0",
|
|
50
|
+
"pi-hermes-memory": "^0.9.8",
|
|
51
|
+
"pi-lens": "^4.1.3",
|
|
52
|
+
"pi-mcp-adapter": "^2.32.1",
|
|
53
|
+
"pi-multi-skills": "^1.1.3"
|
|
54
|
+
},
|
|
55
|
+
"pi": {
|
|
56
|
+
"extensions": [
|
|
57
|
+
"packages/studio/extensions",
|
|
58
|
+
"node_modules/@bacnh85/pi-notebooklm/extensions/index.ts",
|
|
59
|
+
"node_modules/@ff-labs/pi-fff/src/index.ts",
|
|
60
|
+
"node_modules/@gotgenes/pi-permission-system/src/index.ts",
|
|
61
|
+
"node_modules/@jachy/pi-git-sync/index.ts",
|
|
62
|
+
"node_modules/@juicesharp/rpiv-advisor/index.ts",
|
|
63
|
+
"node_modules/@juicesharp/rpiv-args/index.ts",
|
|
64
|
+
"node_modules/@juicesharp/rpiv-ask-user-question/index.ts",
|
|
65
|
+
"node_modules/@juicesharp/rpiv-i18n/index.ts",
|
|
66
|
+
"node_modules/@juicesharp/rpiv-pi/extensions",
|
|
67
|
+
"node_modules/@juicesharp/rpiv-todo/index.ts",
|
|
68
|
+
"node_modules/@juicesharp/rpiv-web-tools/index.ts",
|
|
69
|
+
"node_modules/@juicesharp/rpiv-workflow/extension.ts",
|
|
70
|
+
"node_modules/@llblab/pi-telegram/index.ts",
|
|
71
|
+
"node_modules/@monopi/analytics-extension/index.ts",
|
|
72
|
+
"node_modules/@monopi/background-tasks/index.ts",
|
|
73
|
+
"node_modules/@monopi/diagnostics/index.ts",
|
|
74
|
+
"node_modules/@monopi/extension-git-guard/index.ts",
|
|
75
|
+
"node_modules/@monopi/extension-watchdog/index.ts",
|
|
76
|
+
"node_modules/@monopi/remote-tailscale/index.ts",
|
|
77
|
+
"node_modules/@mrclrchtr/supi-context/src/extension.ts",
|
|
78
|
+
"node_modules/@mrclrchtr/supi-web/src/extension.ts",
|
|
79
|
+
"node_modules/@narumitw/pi-worktree/dist/index.ts",
|
|
80
|
+
"node_modules/@tintinweb/pi-subagents/src/index.ts",
|
|
81
|
+
"node_modules/pi-agent-browser-native/dist/extensions/agent-browser/index.js",
|
|
82
|
+
"node_modules/pi-antigravity/src/index.ts",
|
|
83
|
+
"node_modules/pi-extensible-workflows/dist/src/index.js",
|
|
84
|
+
"node_modules/pi-extensible-workflows/dist/starter/index.js",
|
|
85
|
+
"node_modules/pi-extensible-workflows/dist/subagents/index.js",
|
|
86
|
+
"node_modules/pi-extensible-workflows/dist/trajectory/index.js",
|
|
87
|
+
"node_modules/pi-hermes-memory/src/index.ts",
|
|
88
|
+
"node_modules/pi-lens/dist/index.js",
|
|
89
|
+
"node_modules/pi-mcp-adapter/index.ts",
|
|
90
|
+
"node_modules/pi-multi-skills/index.ts"
|
|
91
|
+
],
|
|
92
|
+
"skills": [
|
|
93
|
+
"packages/studio/skills",
|
|
94
|
+
"node_modules/@bacnh85/pi-notebooklm/skills",
|
|
95
|
+
"node_modules/@juicesharp/rpiv-pi/skills",
|
|
96
|
+
"node_modules/@llblab/pi-telegram/skills",
|
|
97
|
+
"node_modules/pi-lens/skills",
|
|
98
|
+
"node_modules/pi-mcp-adapter/skills"
|
|
99
|
+
],
|
|
100
|
+
"prompts": [
|
|
101
|
+
"packages/studio/prompts",
|
|
102
|
+
"node_modules/pi-extensible-workflows/starter/prompts"
|
|
103
|
+
],
|
|
104
|
+
"themes": [
|
|
105
|
+
"packages/studio/themes"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"files": [
|
|
109
|
+
"packages/studio/extensions",
|
|
110
|
+
"packages/studio/themes",
|
|
111
|
+
"packages/studio/skills",
|
|
112
|
+
"packages/studio/prompts",
|
|
113
|
+
"packages/setup/bin",
|
|
114
|
+
"README.md",
|
|
115
|
+
"CHANGELOG.md",
|
|
116
|
+
"docs/feature-catalog.md",
|
|
117
|
+
"docs/theme-gallery.html",
|
|
118
|
+
"assets",
|
|
119
|
+
".npmrc"
|
|
120
|
+
],
|
|
121
|
+
"overrides": {
|
|
122
|
+
"@jachy/pi-git-sync": {
|
|
123
|
+
"@earendil-works/pi-tui": "*"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* frida-pi-studio-setup — meta-installer del kit (#4):
|
|
4
|
+
* npx github:efuentesp/frida-pi-studio [flags]
|
|
5
|
+
*
|
|
6
|
+
* Qué hace (idempotente):
|
|
7
|
+
* 1. Verifica node ≥ 24 y que `pi` exista (si no, imprime cómo
|
|
8
|
+
* instalarlo managed y sale 1 — NO instala pi solo).
|
|
9
|
+
* 2. Resuelve la última versión (gh api → git ls-remote) y detecta
|
|
10
|
+
* el transporte (HTTPS con credenciales de gh, o llave SSH) para
|
|
11
|
+
* usar la forma de URL correcta (mata el bug v0.1.0).
|
|
12
|
+
* 3. `pi install <source>` (+ smoke: `pi list` muestra el kit).
|
|
13
|
+
* 4. Merge (nunca overwrite) de settings: theme default
|
|
14
|
+
* "arctic-aurora" (override con --theme).
|
|
15
|
+
* 5. Checklist de pasos manuales (auth/secrets — patrón buzz-.env).
|
|
16
|
+
*
|
|
17
|
+
* Flags: --check (dry-run) · --doctor (diagnóstico) · --version vX.Y.Z
|
|
18
|
+
* (pin) · --theme NAME · --local (settings de proyecto) ·
|
|
19
|
+
* --remove · --completions bash|zsh|fish · --profile lite|full
|
|
20
|
+
* (lite = marca + núcleo; filtra extensions via Package
|
|
21
|
+
* Filtering de pi — issue #30)
|
|
22
|
+
*
|
|
23
|
+
* Contrato (setup/AGENTS.md): magro, sin deps, sin bundlear el kit.
|
|
24
|
+
*/
|
|
25
|
+
import { spawnSync } from "node:child_process";
|
|
26
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync } from "node:fs";
|
|
27
|
+
import { join, relative, resolve } from "node:path";
|
|
28
|
+
import os from "node:os";
|
|
29
|
+
import process from "node:process";
|
|
30
|
+
|
|
31
|
+
const REPO = "efuentesp/frida-pi-studio";
|
|
32
|
+
const DEFAULT_THEME = "arctic-aurora";
|
|
33
|
+
|
|
34
|
+
// Perfil lite (#30): marca + núcleo. Whitelist por globs de paquete —
|
|
35
|
+
// robusta a renombres de entry-files upstream. Solo extensions v1
|
|
36
|
+
// (skills/prompts son markdown ligero y cargan siempre).
|
|
37
|
+
const LITE_EXTENSIONS = [
|
|
38
|
+
"packages/studio/**",
|
|
39
|
+
"node_modules/@ff-labs/pi-fff/**",
|
|
40
|
+
"node_modules/@gotgenes/pi-permission-system/**",
|
|
41
|
+
"node_modules/@juicesharp/rpiv-args/**",
|
|
42
|
+
"node_modules/@juicesharp/rpiv-ask-user-question/**",
|
|
43
|
+
"node_modules/@juicesharp/rpiv-todo/**",
|
|
44
|
+
"node_modules/@monopi/background-tasks/**",
|
|
45
|
+
"node_modules/@monopi/diagnostics/**",
|
|
46
|
+
"node_modules/@monopi/extension-git-guard/**",
|
|
47
|
+
"node_modules/@monopi/extension-watchdog/**",
|
|
48
|
+
];
|
|
49
|
+
const args = process.argv.slice(2);
|
|
50
|
+
|
|
51
|
+
const log = (icon, msg) => console.log(`${icon} ${msg}`);
|
|
52
|
+
const fail = (msg) => {
|
|
53
|
+
console.error(`✗ ${msg}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
};
|
|
56
|
+
const flag = (name) => {
|
|
57
|
+
const i = args.indexOf(name);
|
|
58
|
+
return i === -1 ? undefined : args[i + 1];
|
|
59
|
+
};
|
|
60
|
+
const run = (cmd, cmdArgs, opts = {}) =>
|
|
61
|
+
spawnSync(cmd, cmdArgs, { encoding: "utf8", ...opts });
|
|
62
|
+
|
|
63
|
+
function printCompletions() {
|
|
64
|
+
const shell = flag("--completions");
|
|
65
|
+
const c = {
|
|
66
|
+
bash: `_frida_setup() { COMPREPLY=($(compgen -W "--check --doctor --version --theme --profile --local --remove --completions" -- "$2")); };
|
|
67
|
+
complete -F _frida_setup frida-pi-studio-setup npx`,
|
|
68
|
+
zsh: `#compdef frida-pi-studio-setup
|
|
69
|
+
_frida_setup() { _values "flags" --check --doctor "--version[pin vX.Y.Z]" --theme "--profile[lite|full]" --local --remove --completions; };
|
|
70
|
+
_frida_setup "$@"`,
|
|
71
|
+
fish: `complete -c frida-pi-studio-setup -a "--check --doctor --version --theme --profile --local --remove --completions"`,
|
|
72
|
+
}[shell];
|
|
73
|
+
if (!c) fail("--completions bash|zsh|fish");
|
|
74
|
+
console.log(c);
|
|
75
|
+
process.exit(0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── doctor (#29): diagnóstico sin instalar nada ───────────────────
|
|
79
|
+
function doctor() {
|
|
80
|
+
let bad = 0;
|
|
81
|
+
const ok = (m) => log("✓", m);
|
|
82
|
+
const warn = (m) => log("⚠", m);
|
|
83
|
+
const bad1 = (m) => {
|
|
84
|
+
log("✗", m);
|
|
85
|
+
bad += 1;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
ok(`node ${process.versions.node}`);
|
|
89
|
+
|
|
90
|
+
const which = run("which", ["pi"]);
|
|
91
|
+
const piPath = which.stdout?.trim() ?? "";
|
|
92
|
+
if (which.status !== 0 || !piPath) {
|
|
93
|
+
bad1("pi no encontrado — instala layout managed:");
|
|
94
|
+
console.log(
|
|
95
|
+
" curl -fsSL https://pi.dev/install.sh | PI_EXPERIMENTAL=1 sh",
|
|
96
|
+
);
|
|
97
|
+
console.log(` (salgo con el diagnóstico; nada se tocó)`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
const ver = run(piPath, ["--version"]).stdout?.trim() ?? "?";
|
|
101
|
+
ok(`pi ${ver} (${piPath})`);
|
|
102
|
+
if (!piPath.includes(".pi/agent/bin")) {
|
|
103
|
+
warn(
|
|
104
|
+
"layout NO managed: `pi update` no funcionará — reinstala con PI_EXPERIMENTAL=1",
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const list = run(piPath, ["list"]);
|
|
109
|
+
const listed = list.stdout ?? "";
|
|
110
|
+
if (/frida-pi-studio/.test(listed)) {
|
|
111
|
+
ok("kit instalado (pi list)");
|
|
112
|
+
} else if (/\.\.\//.test(listed) || /\/repo/.test(listed)) {
|
|
113
|
+
warn("kit instalado por path local (dev) — el equipo usa la forma git");
|
|
114
|
+
} else {
|
|
115
|
+
warn("kit NO instalado — ejecuta este mismo comando sin --doctor");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const settingsPath = join(os.homedir(), ".pi", "agent", "settings.json");
|
|
119
|
+
try {
|
|
120
|
+
const theme = JSON.parse(readFileSync(settingsPath, "utf8")).theme;
|
|
121
|
+
if (theme === DEFAULT_THEME || theme === "frida") {
|
|
122
|
+
ok(`theme activo: ${theme}`);
|
|
123
|
+
} else {
|
|
124
|
+
warn(
|
|
125
|
+
`theme activo: ${theme ?? "(por defecto)"} — kit espera ${DEFAULT_THEME}`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
warn(`settings ilegibles o inexistentes (${settingsPath})`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
log(
|
|
133
|
+
bad === 0 ? "✓" : "✗",
|
|
134
|
+
bad === 0 ? "doctor: sano" : `doctor: ${bad} problema(s)`,
|
|
135
|
+
);
|
|
136
|
+
process.exit(bad === 0 ? 0 : 1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ── resolución de versión y transporte ───────────────────────────
|
|
140
|
+
function latestTag() {
|
|
141
|
+
const gh = run("gh", [
|
|
142
|
+
"api",
|
|
143
|
+
`repos/${REPO}/releases/latest`,
|
|
144
|
+
"--jq",
|
|
145
|
+
".tag_name",
|
|
146
|
+
]);
|
|
147
|
+
if (gh.status === 0 && /^v\d/.test(gh.stdout?.trim() ?? "")) {
|
|
148
|
+
return gh.stdout.trim();
|
|
149
|
+
}
|
|
150
|
+
const ls = run("git", [
|
|
151
|
+
"ls-remote",
|
|
152
|
+
"--tags",
|
|
153
|
+
"--sort=-v:refname",
|
|
154
|
+
`https://github.com/${REPO}.git`,
|
|
155
|
+
]);
|
|
156
|
+
const m = ls.stdout?.match(/refs\/tags\/(v[\d.]+)\n/);
|
|
157
|
+
return m ? m[1] : undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function transportSource(tag) {
|
|
161
|
+
// HTTPS con credenciales de gh (gh auth setup-git) — forma con slash.
|
|
162
|
+
const probe = run("git", [
|
|
163
|
+
"ls-remote",
|
|
164
|
+
"--exit-code",
|
|
165
|
+
`https://github.com/${REPO}.git`,
|
|
166
|
+
tag,
|
|
167
|
+
]);
|
|
168
|
+
if (probe.status === 0) return `git:github.com/${REPO}@${tag}`;
|
|
169
|
+
// Llave SSH — forma scp (⚠️ jamás git:github.com:… sin git@: bug v0.1.0).
|
|
170
|
+
const ssh = run("ssh", ["-T", "-o", "BatchMode=yes", "git@github.com"]);
|
|
171
|
+
if (ssh.status === 1 || /successfully authenticated/.test(ssh.stderr ?? "")) {
|
|
172
|
+
return `git:git@github.com:${REPO}@${tag}`;
|
|
173
|
+
}
|
|
174
|
+
log("⚠", "sin acceso al repo (ni HTTPS con credenciales gh ni llave SSH)");
|
|
175
|
+
log("ℹ", "ver README §Acceso del equipo: gh auth login + gh auth setup-git");
|
|
176
|
+
return fail("sin credenciales para github.com");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function resolveSource() {
|
|
180
|
+
const pinned = flag("--version");
|
|
181
|
+
const tag = pinned ?? latestTag();
|
|
182
|
+
if (!tag) fail("no pude resolver la última versión — usa --version vX.Y.Z");
|
|
183
|
+
log("✓", pinned ? `versión pineada: ${pinned}` : `última versión: ${tag}`);
|
|
184
|
+
return transportSource(tag);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── flujo principal ──────────────────────────────────────────────
|
|
188
|
+
function checkPrereqs() {
|
|
189
|
+
const [major] = process.versions.node.split(".").map(Number);
|
|
190
|
+
if (major < 24)
|
|
191
|
+
fail(`node >= 24 requerido (actual: ${process.versions.node})`);
|
|
192
|
+
log("✓", `node ${process.versions.node}`);
|
|
193
|
+
|
|
194
|
+
const which = run("which", ["pi"]);
|
|
195
|
+
if (which.status !== 0 || !which.stdout?.trim()) {
|
|
196
|
+
log("ℹ", "pi no encontrado. Instálalo primero (layout managed):");
|
|
197
|
+
console.log(
|
|
198
|
+
" curl -fsSL https://pi.dev/install.sh | PI_EXPERIMENTAL=1 sh",
|
|
199
|
+
);
|
|
200
|
+
console.log(
|
|
201
|
+
" (sin PI_EXPERIMENTAL=1 hace npm install y pi update no funciona)",
|
|
202
|
+
);
|
|
203
|
+
fail("pi ausente");
|
|
204
|
+
}
|
|
205
|
+
log("✓", `pi en ${which.stdout.trim()}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function installKit(source, local) {
|
|
209
|
+
const extra = local ? ["-l"] : [];
|
|
210
|
+
log("→", `pi install ${source}${local ? " (proyecto)" : ""}`);
|
|
211
|
+
const install = spawnSync("pi", ["install", ...extra, source], {
|
|
212
|
+
stdio: "inherit",
|
|
213
|
+
});
|
|
214
|
+
if (install.status !== 0) fail("pi install falló");
|
|
215
|
+
|
|
216
|
+
// smoke: el paquete aparece en pi list (git/npm → nombre; path → ruta)
|
|
217
|
+
const list = run("pi", ["list"]);
|
|
218
|
+
const needle = source.includes("frida-pi-studio")
|
|
219
|
+
? "frida-pi-studio"
|
|
220
|
+
: source;
|
|
221
|
+
if ((list.stdout ?? "").includes(needle)) {
|
|
222
|
+
log("✓", "smoke: pi list muestra el kit");
|
|
223
|
+
} else {
|
|
224
|
+
log("⚠", "smoke: no vi el kit en pi list — revisa la instalación");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function settingsPathFor(local) {
|
|
229
|
+
return local
|
|
230
|
+
? join(process.cwd(), ".pi", "settings.json")
|
|
231
|
+
: join(os.homedir(), ".pi", "agent", "settings.json");
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function readSettings(sp) {
|
|
235
|
+
if (!existsSync(sp)) return {};
|
|
236
|
+
try {
|
|
237
|
+
return JSON.parse(readFileSync(sp, "utf8"));
|
|
238
|
+
} catch {
|
|
239
|
+
log("⚠", `settings ilegibles (${sp})`);
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function mergeTheme(themeName, local) {
|
|
245
|
+
const settingsPath = settingsPathFor(local);
|
|
246
|
+
const settings = readSettings(settingsPath);
|
|
247
|
+
if (!settings || typeof settings !== "object") return;
|
|
248
|
+
if (settings.theme === themeName) {
|
|
249
|
+
log("✓", `theme ya era "${themeName}"`);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
settings.theme = themeName;
|
|
253
|
+
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
|
|
254
|
+
log("✓", `theme → "${themeName}" (merge; el resto intacto)`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// #30: perfil del kit vía Package Filtering de pi (settings.packages
|
|
258
|
+
// en forma objeto). Entrada plana = full (sin filtro).
|
|
259
|
+
function applyProfile(source, profile, local) {
|
|
260
|
+
if (profile !== "lite" && profile !== "full") {
|
|
261
|
+
fail(`--profile espera lite|full (recibí: "${profile}")`);
|
|
262
|
+
}
|
|
263
|
+
const settingsPath = settingsPathFor(local);
|
|
264
|
+
const settings = readSettings(settingsPath);
|
|
265
|
+
if (!settings || typeof settings !== "object") return;
|
|
266
|
+
const pkgs = Array.isArray(settings.packages) ? settings.packages : [];
|
|
267
|
+
const isKit = (entry) => {
|
|
268
|
+
const s = typeof entry === "string" ? entry : entry?.source;
|
|
269
|
+
if (typeof s !== "string") return false;
|
|
270
|
+
if (s === source) return true;
|
|
271
|
+
if (
|
|
272
|
+
s.startsWith(`git:github.com/${REPO}`) ||
|
|
273
|
+
s.startsWith(`git:git@github.com:${REPO}`)
|
|
274
|
+
) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
// Fuentes por ruta (dev/E2E): pi las registra relativas al cwd —
|
|
278
|
+
// comparar por ruta absoluta resuelta.
|
|
279
|
+
try {
|
|
280
|
+
return resolve(s) === resolve(source);
|
|
281
|
+
} catch {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
const i = pkgs.findIndex(isKit);
|
|
286
|
+
if (i === -1) {
|
|
287
|
+
log("⚠", "perfil: no encontré el kit en settings.packages — omito");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const current = pkgs[i];
|
|
291
|
+
const src = typeof current === "string" ? current : current.source;
|
|
292
|
+
pkgs[i] =
|
|
293
|
+
profile === "lite" ? { source: src, extensions: LITE_EXTENSIONS } : src;
|
|
294
|
+
settings.packages = pkgs;
|
|
295
|
+
writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`);
|
|
296
|
+
log(
|
|
297
|
+
"✓",
|
|
298
|
+
profile === "lite"
|
|
299
|
+
? `perfil lite: ${LITE_EXTENSIONS.length} grupos de extensiones (Package Filtering)`
|
|
300
|
+
: "perfil full: sin filtro de extensiones",
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function printChecklist() {
|
|
305
|
+
console.log("");
|
|
306
|
+
log("ℹ", "Pasos manuales one-time (secrets, por máquina — jamás al repo):");
|
|
307
|
+
console.log(" - pi auth login (o export de API keys del proveedor)");
|
|
308
|
+
console.log(" - Telegram: /telegram-setup en la sesión pi (si se usa)");
|
|
309
|
+
console.log(" - devengine: export FRIDA_DEV_ENGINE_API_KEY=… (si se usa)");
|
|
310
|
+
log("✓", "FRIDA-PI-STUDIO listo — ejecuta `pi` y prueba /frida");
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ── importadores de harness ajenos (#16) ─────────────────────
|
|
314
|
+
function findHarnessConfigs() {
|
|
315
|
+
const proj = process.cwd();
|
|
316
|
+
const found = [];
|
|
317
|
+
const push = (harness, abs) =>
|
|
318
|
+
found.push({ harness, abs, rel: relative(proj, abs) });
|
|
319
|
+
const files = [
|
|
320
|
+
["Claude Code", "CLAUDE.md"],
|
|
321
|
+
["Cursor", ".cursorrules"],
|
|
322
|
+
["Cline", ".clinerules"],
|
|
323
|
+
["Copilot", ".github/copilot-instructions.md"],
|
|
324
|
+
["Windsurf", ".windsurfrules"],
|
|
325
|
+
];
|
|
326
|
+
for (const [h, rel] of files) {
|
|
327
|
+
if (existsSync(join(proj, rel))) push(h, join(proj, rel));
|
|
328
|
+
}
|
|
329
|
+
const dirs = [
|
|
330
|
+
["Claude Code", ".claude/commands"],
|
|
331
|
+
["Cursor", ".cursor/rules"],
|
|
332
|
+
];
|
|
333
|
+
for (const [h, dir] of dirs) {
|
|
334
|
+
const abs = join(proj, dir);
|
|
335
|
+
if (!existsSync(abs)) continue;
|
|
336
|
+
try {
|
|
337
|
+
for (const f of readdirSync(abs)) {
|
|
338
|
+
if (/\.(md|mdc)$/i.test(f)) push(h, join(abs, f));
|
|
339
|
+
}
|
|
340
|
+
} catch {
|
|
341
|
+
/* dir ilegible — seguir */
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const homeClaude = join(os.homedir(), ".claude", "CLAUDE.md");
|
|
345
|
+
if (existsSync(homeClaude)) {
|
|
346
|
+
found.push({
|
|
347
|
+
harness: "Claude Code (global)",
|
|
348
|
+
abs: homeClaude,
|
|
349
|
+
rel: homeClaude,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return found;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function scanImports() {
|
|
356
|
+
const found = findHarnessConfigs();
|
|
357
|
+
if (found.length === 0) {
|
|
358
|
+
log("ℹ", "sin configs de otros harness en este proyecto/home");
|
|
359
|
+
process.exit(0);
|
|
360
|
+
}
|
|
361
|
+
log("✓", `${found.length} config(s) de otros harness detectadas:`);
|
|
362
|
+
for (const f of found) {
|
|
363
|
+
const lines = readFileSync(f.abs, "utf8").split("\n").length;
|
|
364
|
+
console.log(` ${f.harness.padEnd(18)} ${f.rel} (${lines} líneas)`);
|
|
365
|
+
}
|
|
366
|
+
log("ℹ", "importar: `npx github:efuentesp/frida-pi-studio --import`");
|
|
367
|
+
process.exit(0);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function importConfigs() {
|
|
371
|
+
const found = findHarnessConfigs();
|
|
372
|
+
if (found.length === 0) {
|
|
373
|
+
log("ℹ", "nada que importar");
|
|
374
|
+
process.exit(0);
|
|
375
|
+
}
|
|
376
|
+
const target = join(process.cwd(), "AGENTS.md");
|
|
377
|
+
let current = existsSync(target) ? readFileSync(target, "utf8") : "";
|
|
378
|
+
let added = 0;
|
|
379
|
+
let skipped = 0;
|
|
380
|
+
for (const f of found) {
|
|
381
|
+
const header = `## Importado de ${f.harness} — ${f.rel}`;
|
|
382
|
+
if (current.includes(header)) {
|
|
383
|
+
skipped += 1;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (added === 0 && existsSync(target)) {
|
|
387
|
+
writeFileSync(`${target}.bak`, current);
|
|
388
|
+
log("ℹ", `backup: AGENTS.md.bak`);
|
|
389
|
+
}
|
|
390
|
+
const body = readFileSync(f.abs, "utf8").trimEnd();
|
|
391
|
+
current += `\n\n${header}\n\n${body}\n`;
|
|
392
|
+
added += 1;
|
|
393
|
+
}
|
|
394
|
+
if (added > 0) {
|
|
395
|
+
writeFileSync(target, current);
|
|
396
|
+
log("✓", `importadas ${added} config(s) a AGENTS.md (pi las lee nativo)`);
|
|
397
|
+
}
|
|
398
|
+
if (skipped > 0) log("ℹ", `${skipped} ya importada(s) antes — idempotente`);
|
|
399
|
+
process.exit(0);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Completions primero: salida limpia, sin prerequisitos ni logs.
|
|
403
|
+
if (args.includes("--completions")) printCompletions();
|
|
404
|
+
if (args.includes("--doctor")) doctor();
|
|
405
|
+
if (args.includes("--scan-imports")) scanImports();
|
|
406
|
+
if (args.includes("--import")) importConfigs();
|
|
407
|
+
|
|
408
|
+
checkPrereqs();
|
|
409
|
+
|
|
410
|
+
if (args.includes("--remove")) {
|
|
411
|
+
log("→", `pi remove git:github.com/${REPO}`);
|
|
412
|
+
const rm = spawnSync("pi", ["remove", `git:github.com/${REPO}`], {
|
|
413
|
+
stdio: "inherit",
|
|
414
|
+
});
|
|
415
|
+
if (rm.status !== 0) fail("pi remove falló (¿estaba instalado?)");
|
|
416
|
+
log("✓", "kit removido");
|
|
417
|
+
process.exit(0);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (args.includes("--check")) {
|
|
421
|
+
log("ℹ", "--check: prerequisitos OK, sin instalar nada más");
|
|
422
|
+
process.exit(0);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const source = flag("--source") ?? resolveSource();
|
|
426
|
+
const local = args.includes("--local");
|
|
427
|
+
installKit(source, local);
|
|
428
|
+
mergeTheme(flag("--theme") ?? DEFAULT_THEME, local);
|
|
429
|
+
applyProfile(source, flag("--profile") ?? "full", local);
|
|
430
|
+
printChecklist();
|