@ijfw/install 1.3.1 → 1.4.0
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 +19 -1
- package/dist/ijfw.js +1414 -149
- package/dist/install.js +43 -11
- package/dist/uninstall.js +40 -0
- package/package.json +1 -1
package/dist/install.js
CHANGED
|
@@ -19,11 +19,15 @@ import {
|
|
|
19
19
|
chmodSync,
|
|
20
20
|
mkdirSync as mkdirSync2,
|
|
21
21
|
realpathSync,
|
|
22
|
-
statSync
|
|
22
|
+
statSync,
|
|
23
|
+
lstatSync,
|
|
24
|
+
openSync,
|
|
25
|
+
closeSync,
|
|
26
|
+
constants as fsConstants
|
|
23
27
|
} from "node:fs";
|
|
24
|
-
import { dirname as dirname3, basename, join as join3, normalize, delimiter } from "node:path";
|
|
28
|
+
import { dirname as dirname3, basename, join as join3, normalize, delimiter, resolve as resolve3, sep } from "node:path";
|
|
25
29
|
import { homedir as homedir2 } from "node:os";
|
|
26
|
-
import { createHash } from "node:crypto";
|
|
30
|
+
import { createHash, randomBytes as randomBytes2 } from "node:crypto";
|
|
27
31
|
function printOk(msg) {
|
|
28
32
|
process.stdout.write(` [ok] ${msg}
|
|
29
33
|
`);
|
|
@@ -60,7 +64,7 @@ function nativePath(p) {
|
|
|
60
64
|
function writeAtomic(path3, contents, opts = {}) {
|
|
61
65
|
const mode = opts.mode ?? 384;
|
|
62
66
|
mkdirSync2(dirname3(path3), { recursive: true });
|
|
63
|
-
const tmp = `${path3}.tmp.${process.pid}`;
|
|
67
|
+
const tmp = `${path3}.tmp.${process.pid}.${randomBytes2(4).toString("hex")}`;
|
|
64
68
|
writeFileSync2(tmp, contents, { mode });
|
|
65
69
|
renameSync2(tmp, path3);
|
|
66
70
|
try {
|
|
@@ -385,7 +389,7 @@ function mergeYamlPluginsEnabled(dst, pluginName, ts) {
|
|
|
385
389
|
if (inPluginsBlock) {
|
|
386
390
|
if (/^\S/.test(line) && line.trim() !== "") {
|
|
387
391
|
inPluginsBlock = false;
|
|
388
|
-
} else if (
|
|
392
|
+
} else if (isIndentedEnabledLine(line)) {
|
|
389
393
|
enabledLineIdx = i;
|
|
390
394
|
} else if (enabledLineIdx >= 0 && itemRe.test(line)) {
|
|
391
395
|
alreadyListed = true;
|
|
@@ -423,6 +427,11 @@ function mergeYamlPluginsEnabled(dst, pluginName, ts) {
|
|
|
423
427
|
outText += "# IJFW-PLUGINS-END\n";
|
|
424
428
|
writeAtomic(dst, outText, { mode: 384 });
|
|
425
429
|
}
|
|
430
|
+
function isIndentedEnabledLine(line) {
|
|
431
|
+
if (!line || !/\s/.test(line[0])) return false;
|
|
432
|
+
const trimmed = line.trim();
|
|
433
|
+
return trimmed === "enabled:" || trimmed === "enabled: []" || trimmed.startsWith("enabled: [") && trimmed.endsWith("]");
|
|
434
|
+
}
|
|
426
435
|
function opencodeMerge(dst, serverJs, ts) {
|
|
427
436
|
mkdirSync2(dirname3(dst), { recursive: true });
|
|
428
437
|
if (ts) backup(dst, ts);
|
|
@@ -493,10 +502,22 @@ function clineMerge(serverJs, home, ts) {
|
|
|
493
502
|
writeAtomic(dst, JSON.stringify(doc, null, 2), { mode: 384 });
|
|
494
503
|
return dst;
|
|
495
504
|
}
|
|
496
|
-
var IS_WIN;
|
|
505
|
+
var IS_WIN, EXTENSION_PLATFORM_SKILL_DIRS;
|
|
497
506
|
var init_install_helpers = __esm({
|
|
498
507
|
"src/install-helpers.js"() {
|
|
499
508
|
IS_WIN = process.platform === "win32";
|
|
509
|
+
EXTENSION_PLATFORM_SKILL_DIRS = Object.freeze([
|
|
510
|
+
{ id: "claude", rel: "claude/skills" },
|
|
511
|
+
{ id: "codex", rel: "codex/skills" },
|
|
512
|
+
{ id: "gemini", rel: "gemini/extensions/ijfw/skills" },
|
|
513
|
+
{ id: "cursor", rel: "cursor/skills" },
|
|
514
|
+
{ id: "windsurf", rel: "windsurf/skills" },
|
|
515
|
+
{ id: "copilot", rel: "copilot/skills" },
|
|
516
|
+
{ id: "hermes", rel: "hermes/skills" },
|
|
517
|
+
{ id: "wayland", rel: "wayland/skills" },
|
|
518
|
+
{ id: "shared", rel: "shared/skills" },
|
|
519
|
+
{ id: "universal", rel: "universal/skills" }
|
|
520
|
+
]);
|
|
500
521
|
}
|
|
501
522
|
});
|
|
502
523
|
|
|
@@ -759,6 +780,12 @@ async function installCodex(ctx) {
|
|
|
759
780
|
for (const sd of listSubdirs(repoSkills)) {
|
|
760
781
|
copyDirIfAbsent(sd.path, join4(userSkills, sd.name));
|
|
761
782
|
}
|
|
783
|
+
const userCommands = join4(ctx.home, ".codex", "commands");
|
|
784
|
+
ensureDir(userCommands);
|
|
785
|
+
const repoCommands = join4(ctx.repoRoot, "codex", "commands");
|
|
786
|
+
for (const f of listFiles(repoCommands, ".md")) {
|
|
787
|
+
copyIfAbsent(f.path, join4(userCommands, f.name));
|
|
788
|
+
}
|
|
762
789
|
const cwd = ctx.cwd || process.cwd();
|
|
763
790
|
if (existsSync4(join4(cwd, ".codex", "config.toml")) || existsSync4(join4(cwd, ".ijfw"))) {
|
|
764
791
|
const projSkills = join4(cwd, ".codex", "skills");
|
|
@@ -766,8 +793,13 @@ async function installCodex(ctx) {
|
|
|
766
793
|
for (const sd of listSubdirs(repoSkills)) {
|
|
767
794
|
copyDirIfAbsent(sd.path, join4(projSkills, sd.name));
|
|
768
795
|
}
|
|
796
|
+
const projCommands = join4(cwd, ".codex", "commands");
|
|
797
|
+
ensureDir(projCommands);
|
|
798
|
+
for (const f of listFiles(repoCommands, ".md")) {
|
|
799
|
+
copyIfAbsent(f.path, join4(projCommands, f.name));
|
|
800
|
+
}
|
|
769
801
|
}
|
|
770
|
-
ctx.log.ok("Installed Codex bundle: MCP + hooks + 19 skills + context");
|
|
802
|
+
ctx.log.ok("Installed Codex bundle: MCP + hooks + 19 skills + 22 command aliases + context");
|
|
771
803
|
return { status: "ok" };
|
|
772
804
|
}
|
|
773
805
|
async function installGemini(ctx) {
|
|
@@ -1765,7 +1797,7 @@ var init_install_flow = __esm({
|
|
|
1765
1797
|
// src/install.js
|
|
1766
1798
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
1767
1799
|
import { existsSync as existsSync5, rmSync, mkdirSync as mkdirSync4, realpathSync as realpathSync2, renameSync as renameSync3 } from "node:fs";
|
|
1768
|
-
import { resolve as
|
|
1800
|
+
import { resolve as resolve4, join as join5, dirname as dirname5 } from "node:path";
|
|
1769
1801
|
import { homedir as homedir3, platform as platform2 } from "node:os";
|
|
1770
1802
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1771
1803
|
|
|
@@ -2026,8 +2058,8 @@ function findBash() {
|
|
|
2026
2058
|
return null;
|
|
2027
2059
|
}
|
|
2028
2060
|
function resolveTarget(opt) {
|
|
2029
|
-
if (opt.dir) return
|
|
2030
|
-
if (process.env.IJFW_HOME) return
|
|
2061
|
+
if (opt.dir) return resolve4(opt.dir);
|
|
2062
|
+
if (process.env.IJFW_HOME) return resolve4(process.env.IJFW_HOME);
|
|
2031
2063
|
return join5(homedir3(), ".ijfw");
|
|
2032
2064
|
}
|
|
2033
2065
|
function runCheck(cmd, args, opts) {
|
|
@@ -2091,7 +2123,7 @@ function cloneOrPull(dir, branch) {
|
|
|
2091
2123
|
}
|
|
2092
2124
|
async function runInstallScript(dir) {
|
|
2093
2125
|
const canonicalDir = join5(homedir3(), ".ijfw");
|
|
2094
|
-
const isCustomDir =
|
|
2126
|
+
const isCustomDir = resolve4(dir) !== canonicalDir;
|
|
2095
2127
|
const { runInstall: runInstall2 } = await Promise.resolve().then(() => (init_install_flow(), install_flow_exports));
|
|
2096
2128
|
await runInstall2({
|
|
2097
2129
|
targets: void 0,
|
package/dist/uninstall.js
CHANGED
|
@@ -241,9 +241,11 @@ import os; os.replace(p + ".tmp", p)
|
|
|
241
241
|
return true;
|
|
242
242
|
}
|
|
243
243
|
const stripped = raw.replace(
|
|
244
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- raw is a small local YAML config file; pattern is line-anchored to the IJFW-owned block.
|
|
244
245
|
/^ ijfw-memory:\n(?: .*\n)*(?:# IJFW-MCP-END ijfw-memory\n)?/m,
|
|
245
246
|
""
|
|
246
247
|
).replace(
|
|
248
|
+
// eslint-disable-next-line security/detect-unsafe-regex -- raw is a small local YAML config file; pattern is bounded by exact IJFW sentinel markers.
|
|
247
249
|
/# IJFW-MCP-BEGIN ijfw-memory\n(?:.*\n)*?# IJFW-MCP-END ijfw-memory\n/,
|
|
248
250
|
""
|
|
249
251
|
);
|
|
@@ -263,6 +265,42 @@ function removeIjfwSkills(dir) {
|
|
|
263
265
|
}
|
|
264
266
|
return count;
|
|
265
267
|
}
|
|
268
|
+
var CODEX_COMMAND_FILES = [
|
|
269
|
+
"compress.md",
|
|
270
|
+
"consolidate.md",
|
|
271
|
+
"cross-audit.md",
|
|
272
|
+
"cross-critique.md",
|
|
273
|
+
"cross-research.md",
|
|
274
|
+
"doctor.md",
|
|
275
|
+
"handoff.md",
|
|
276
|
+
"ijfw-audit.md",
|
|
277
|
+
"ijfw-execute.md",
|
|
278
|
+
"ijfw-help.md",
|
|
279
|
+
"ijfw-plan.md",
|
|
280
|
+
"ijfw-ship.md",
|
|
281
|
+
"ijfw-verify.md",
|
|
282
|
+
"ijfw.md",
|
|
283
|
+
"memory-audit.md",
|
|
284
|
+
"memory-consent.md",
|
|
285
|
+
"memory-why.md",
|
|
286
|
+
"metrics.md",
|
|
287
|
+
"mode.md",
|
|
288
|
+
"status.md",
|
|
289
|
+
"team.md",
|
|
290
|
+
"workflow.md"
|
|
291
|
+
];
|
|
292
|
+
function removeCodexCommands(dir) {
|
|
293
|
+
if (!existsSync2(dir)) return 0;
|
|
294
|
+
let count = 0;
|
|
295
|
+
for (const name of CODEX_COMMAND_FILES) {
|
|
296
|
+
const path = join2(dir, name);
|
|
297
|
+
if (existsSync2(path)) {
|
|
298
|
+
rmSync(path, { force: true });
|
|
299
|
+
count++;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return count;
|
|
303
|
+
}
|
|
266
304
|
function cleanPlatforms() {
|
|
267
305
|
const removed = [];
|
|
268
306
|
if (removeTomlSection(join2(HOME, ".codex", "config.toml"))) {
|
|
@@ -273,6 +311,8 @@ function cleanPlatforms() {
|
|
|
273
311
|
}
|
|
274
312
|
const codexSkills = removeIjfwSkills(join2(HOME, ".codex", "skills"));
|
|
275
313
|
if (codexSkills > 0) removed.push(`~/.codex/skills/ijfw-* (removed ${codexSkills} skill dirs)`);
|
|
314
|
+
const codexCommands = removeCodexCommands(join2(HOME, ".codex", "commands"));
|
|
315
|
+
if (codexCommands > 0) removed.push(`~/.codex/commands (removed ${codexCommands} IJFW command aliases)`);
|
|
276
316
|
const codexMd = join2(HOME, ".codex", "IJFW.md");
|
|
277
317
|
if (existsSync2(codexMd)) {
|
|
278
318
|
rmSync(codexMd, { force: true });
|