@delorenj/pjangler 1.5.14 → 1.5.16
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/dist/index.js
CHANGED
|
@@ -7674,7 +7674,7 @@ function skillCoreOptions(ctx) {
|
|
|
7674
7674
|
function relaySkillexCommands(text2) {
|
|
7675
7675
|
return text2.replace(SKILLEX_COMMAND, PJ_SKILLS);
|
|
7676
7676
|
}
|
|
7677
|
-
var SKILLEX_COMMAND = /(?<![\w/@.:-])skillex(?=\s+(?:skill|set|pack|init|enable|disable|inherit|sync|status|explain|doctor|vendor|profile|migrate)\b)/g;
|
|
7677
|
+
var SKILLEX_COMMAND = /(?<![\w/@.:-])skillex(?=\s+(?:skill|set|pack|bmad|init|enable|disable|inherit|normalize|sync|status|explain|doctor|vendor|profile|migrate|integrations|clients|root)\b)/g;
|
|
7678
7678
|
function skillDiagnostics(findings) {
|
|
7679
7679
|
return findings.map(
|
|
7680
7680
|
(finding2) => `${finding2.code}: ${finding2.message}${finding2.path ? ` (${finding2.path})` : ""}${finding2.fix ? ` \u2014 ${relaySkillexCommands(finding2.fix)}` : ""}`
|
|
@@ -8560,20 +8560,13 @@ var HOOKS_COMMENT_HEADER = `# This block will handle the linking of
|
|
|
8560
8560
|
# TODO: Ensure this works for all levels of nesting.
|
|
8561
8561
|
# i.e. All linked agent files MUST be siblings at
|
|
8562
8562
|
# any given level of nesting.`;
|
|
8563
|
-
var SKILLEX_TOOL_VERSION = "0.1.2";
|
|
8564
|
-
var SKILLS_SYNC_TOOLS = `{ "npm:@delorenj/skillex" = { version = "${SKILLEX_TOOL_VERSION}", allow_low_downloads = true }, node = "24" }`;
|
|
8565
8563
|
var LINK_AGENTFILES_WATCH_TASK_BLOCK = `[[watch_files]]
|
|
8566
8564
|
patterns = ["AGENTS.md"]
|
|
8567
8565
|
task = "${LINK_AGENTFILES_TASK}"
|
|
8568
8566
|
|
|
8569
8567
|
${taskHeader(LINK_AGENTFILES_TASK)}
|
|
8570
8568
|
description = "Symlink all agent files to AGENTS.md"
|
|
8571
|
-
run = ${JSON.stringify(LINK_AGENTFILES_SCRIPT)}
|
|
8572
|
-
|
|
8573
|
-
${taskHeader(SKILLS_SYNC_TASK)}
|
|
8574
|
-
description = "Reconcile this project's selected skills"
|
|
8575
|
-
tools = ${SKILLS_SYNC_TOOLS}
|
|
8576
|
-
run = "skillex sync --scope project --project '{{config_root}}'"`;
|
|
8569
|
+
run = ${JSON.stringify(LINK_AGENTFILES_SCRIPT)}`;
|
|
8577
8570
|
var VERSIONING_BLOCK = `# >>> mise-versioning >>> (managed block \u2014 do not edit by hand; re-run init to update)
|
|
8578
8571
|
[tasks."version"]
|
|
8579
8572
|
description = "Print the current version (vX.Y.Z)"
|
|
@@ -9038,6 +9031,7 @@ function managedLinkHookIssues(parsed) {
|
|
|
9038
9031
|
}
|
|
9039
9032
|
var RETIRED_LINK_TASKS = [
|
|
9040
9033
|
"skills-sync",
|
|
9034
|
+
SKILLS_SYNC_TASK,
|
|
9041
9035
|
"skills-provision-packs",
|
|
9042
9036
|
PROVISION_PACKS_TASK,
|
|
9043
9037
|
"skills-provision-bmad",
|
|
@@ -9051,8 +9045,7 @@ function managedLinkTables() {
|
|
|
9051
9045
|
const parsed = parseToml2(LINK_AGENTFILES_WATCH_TASK_BLOCK);
|
|
9052
9046
|
return {
|
|
9053
9047
|
watch: { lines: chunks[0], value: parsed.watch_files[0] },
|
|
9054
|
-
link: { lines: chunks[1], value: parsed.tasks[LINK_AGENTFILES_TASK] }
|
|
9055
|
-
sync: { lines: chunks[2], value: parsed.tasks[SKILLS_SYNC_TASK] }
|
|
9048
|
+
link: { lines: chunks[1], value: parsed.tasks[LINK_AGENTFILES_TASK] }
|
|
9056
9049
|
};
|
|
9057
9050
|
}
|
|
9058
9051
|
function managedWatchPlan(watches) {
|
|
@@ -9098,7 +9091,7 @@ function reconcileManagedLinkTables(text2) {
|
|
|
9098
9091
|
edits.push({ start: span.start, end: span.end, lines: [] });
|
|
9099
9092
|
continue;
|
|
9100
9093
|
}
|
|
9101
|
-
const table =
|
|
9094
|
+
const table = managed.link;
|
|
9102
9095
|
found.add(name);
|
|
9103
9096
|
if (isDeepStrictEqualLoose(parseBody(lines, statements, index), table.value)) continue;
|
|
9104
9097
|
if (name === LINK_AGENTFILES_TASK && !lines.slice(span.start, span.end).join("\n").includes("link-agentfiles")) {
|
|
@@ -9122,8 +9115,7 @@ function reconcileManagedLinkTables(text2) {
|
|
|
9122
9115
|
});
|
|
9123
9116
|
const missing = [
|
|
9124
9117
|
...plan.managed >= 0 ? [] : [managed.watch.lines],
|
|
9125
|
-
...found.has(LINK_AGENTFILES_TASK) ? [] : [managed.link.lines]
|
|
9126
|
-
...found.has(SKILLS_SYNC_TASK) ? [] : [managed.sync.lines]
|
|
9118
|
+
...found.has(LINK_AGENTFILES_TASK) ? [] : [managed.link.lines]
|
|
9127
9119
|
];
|
|
9128
9120
|
if (missing.length) {
|
|
9129
9121
|
const block = missing.flatMap((chunk, index) => index ? ["", ...chunk] : chunk);
|
|
@@ -9207,7 +9199,6 @@ function expectedLinkModel(base, required) {
|
|
|
9207
9199
|
const tasks = isTable(model.tasks) ? model.tasks : model.tasks = {};
|
|
9208
9200
|
for (const name of RETIRED_LINK_TASKS) delete tasks[name];
|
|
9209
9201
|
tasks[LINK_AGENTFILES_TASK] = deepClone(managed.link.value);
|
|
9210
|
-
tasks[SKILLS_SYNC_TASK] = deepClone(managed.sync.value);
|
|
9211
9202
|
const watches = Array.isArray(model.watch_files) ? model.watch_files : [];
|
|
9212
9203
|
const plan = managedWatchPlan(watches);
|
|
9213
9204
|
model.watch_files = [
|
|
@@ -10519,35 +10510,26 @@ function retiredSkillsScripts(ctx) {
|
|
|
10519
10510
|
return RETIRED_SKILL_SCRIPTS.map((path) => join7(ctx.repoRoot, path)).filter((path) => Boolean(lstatIfPresent(path)));
|
|
10520
10511
|
}
|
|
10521
10512
|
function skillsWiringFindings(text2) {
|
|
10522
|
-
if (text2 === null) return { fixable: [
|
|
10513
|
+
if (text2 === null) return { fixable: [], manual: [] };
|
|
10523
10514
|
const fixable = [];
|
|
10524
10515
|
const manual = [];
|
|
10525
10516
|
let parsed = parsedOrUndefined(text2);
|
|
10526
10517
|
if (!parsed) {
|
|
10527
10518
|
const baseline = parseBaseline(text2);
|
|
10528
|
-
if ("refused" in baseline) return { fixable: [], manual: ["mise.toml is invalid TOML; repair it before
|
|
10519
|
+
if ("refused" in baseline) return { fixable: [], manual: ["mise.toml is invalid TOML; repair it before pjangler can retire skill tasks"] };
|
|
10529
10520
|
parsed = baseline.parsed;
|
|
10530
10521
|
fixable.push("mise.toml repeats the close of a [hooks] array (an earlier pjangler run wrote it); migrate repairs it");
|
|
10531
10522
|
}
|
|
10532
10523
|
const record3 = (value) => isTable(value) ? value : {};
|
|
10533
|
-
const tasks = record3(parsed.tasks)
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
fixable.push("skills:sync must explicitly target {{config_root}} with the Node CLI");
|
|
10537
|
-
}
|
|
10538
|
-
const pin = record3(task.tools)["npm:@delorenj/skillex"];
|
|
10539
|
-
const pinned = typeof pin === "string" ? pin : record3(pin).version;
|
|
10540
|
-
if (pinned !== SKILLEX_TOOL_VERSION) {
|
|
10541
|
-
fixable.push(`skills:sync must pin @delorenj/skillex ${SKILLEX_TOOL_VERSION} in its own tools table`);
|
|
10542
|
-
} else if (typeof pin === "string") {
|
|
10543
|
-
fixable.push(`skills:sync pins @delorenj/skillex as the plain string "${pin}", which mise 2026.9 refuses to install inside its 30-day minimumPackageAge; pin { version = "${SKILLEX_TOOL_VERSION}", allow_low_downloads = true }`);
|
|
10544
|
-
} else if (record3(pin).allow_low_downloads !== true) {
|
|
10545
|
-
fixable.push(`skills:sync must approve @delorenj/skillex ${SKILLEX_TOOL_VERSION} with allow_low_downloads = true (mise 2026.9 refuses to install it otherwise)`);
|
|
10524
|
+
const tasks = record3(parsed.tasks);
|
|
10525
|
+
if (tasks[SKILLS_SYNC_TASK] !== void 0) {
|
|
10526
|
+
fixable.push(`Retire the ${SKILLS_SYNC_TASK} mise task; invoke skillex directly (pj skills sync --scope project --project .)`);
|
|
10546
10527
|
}
|
|
10547
10528
|
const retired = /sync-skills\.py|provision-(?:packs|bmad-skills)\.py|skills[:\-]provision/;
|
|
10548
10529
|
const renames = new Map(RETIRED_TASK_RENAMES);
|
|
10549
|
-
const replaced = /* @__PURE__ */ new Set([...RETIRED_LINK_TASKS, LINK_AGENTFILES_TASK
|
|
10530
|
+
const replaced = /* @__PURE__ */ new Set([...RETIRED_LINK_TASKS, LINK_AGENTFILES_TASK]);
|
|
10550
10531
|
for (const [name, entry] of Object.entries(tasks)) {
|
|
10532
|
+
if (name === SKILLS_SYNC_TASK) continue;
|
|
10551
10533
|
if (!retired.test(name) && !retired.test(JSON.stringify(record3(entry).run ?? ""))) continue;
|
|
10552
10534
|
if (replaced.has(renames.get(name) ?? name)) {
|
|
10553
10535
|
if (!fixable.includes("Retire Python skill sync/provision tasks from mise.toml")) fixable.push("Retire Python skill sync/provision tasks from mise.toml");
|
|
@@ -10562,7 +10544,7 @@ function skillsWiringFindings(text2) {
|
|
|
10562
10544
|
}
|
|
10563
10545
|
manual.push(...decisions.manual.filter((entry) => SKILL_WRITER_MENTION.test(entry.command)).map(manualHookLine));
|
|
10564
10546
|
const watches = Array.isArray(parsed.watch_files) ? parsed.watch_files : [];
|
|
10565
|
-
if (managedWatchPlan(watches).skills.length) fixable.push("Remove the skills watch hook; run skills
|
|
10547
|
+
if (managedWatchPlan(watches).skills.length) fixable.push("Remove the skills watch hook; run pj skills sync explicitly, never automatically");
|
|
10566
10548
|
return { fixable, manual };
|
|
10567
10549
|
}
|
|
10568
10550
|
function skillsWiringIssues(text2) {
|
|
@@ -10581,10 +10563,11 @@ function createAgentHooksChecks() {
|
|
|
10581
10563
|
const retired = retiredSkillsScripts(ctx);
|
|
10582
10564
|
const unsafe = retired.filter((path) => lstatIfPresent(path)?.isDirectory());
|
|
10583
10565
|
const details = [...finding2.details, ...wiring, ...retired.map((path) => unsafe.includes(path) ? `Inspect and preserve retired script directory: ${path}` : `${path} is a retired skill writer`)];
|
|
10566
|
+
const miseDrift = mise !== null && upsertLinkAgentfilesBlock(mise, ctx) !== mise;
|
|
10584
10567
|
return {
|
|
10585
10568
|
...finding2,
|
|
10586
10569
|
status: wiring.length || retired.length ? "fail" : finding2.status,
|
|
10587
|
-
fixable: unsafe.length ? false : finding2.fixable || wiring.length > 0 || retired.length > 0,
|
|
10570
|
+
fixable: unsafe.length ? false : finding2.fixable || wiring.length > 0 || retired.length > 0 || miseDrift,
|
|
10588
10571
|
summary: details.length ? `${details.length} Skillex finding(s)` : finding2.summary,
|
|
10589
10572
|
details
|
|
10590
10573
|
};
|
|
@@ -10629,7 +10612,7 @@ function createAgentHooksChecks() {
|
|
|
10629
10612
|
if (!reconciled.ok || !roots.ok) {
|
|
10630
10613
|
return result2(changedFiles.length ? "partial" : "blocked", "Skillex requires attention before project migration");
|
|
10631
10614
|
}
|
|
10632
|
-
return changedFiles.length ? result2("applied", "Project skills use the Node core and
|
|
10615
|
+
return changedFiles.length ? result2("applied", "Project skills use the Node core and direct invocation") : result2("noop", "No changes required");
|
|
10633
10616
|
}
|
|
10634
10617
|
},
|
|
10635
10618
|
{
|
|
@@ -11779,7 +11762,7 @@ var AgentHooksRecipe = class extends Recipe {
|
|
|
11779
11762
|
printNextSteps() {
|
|
11780
11763
|
console.log("\u{1FA9D} Agent-hooks layer installed!");
|
|
11781
11764
|
console.log(" Next steps:");
|
|
11782
|
-
console.log(" 1.
|
|
11765
|
+
console.log(" 1. pj skills sync --scope project --project . # sync .agents/skills.json into local CLI dirs");
|
|
11783
11766
|
console.log(" 2. mise run hooks:sync # generate .claude/settings.json + inject codex/kimi/hermes");
|
|
11784
11767
|
console.log(" 3. git add .claude/settings.json .agents/hooks .agents/skills.json && commit (codex/kimi/hermes are per-dev)");
|
|
11785
11768
|
console.log(" 4. mise run hindsight:setup # set HINDSIGHT_OP_KEY_REF to your 1Password item first");
|
|
@@ -15136,21 +15119,13 @@ ${leaveBlock}`);
|
|
|
15136
15119
|
}
|
|
15137
15120
|
wiredHooks = true;
|
|
15138
15121
|
}
|
|
15139
|
-
const skillsSync = [
|
|
15140
|
-
'[tasks."skills:sync"]',
|
|
15141
|
-
`description = "Reconcile this project's selected skills"`,
|
|
15142
|
-
`tools = ${SKILLS_SYNC_TOOLS}`,
|
|
15143
|
-
`run = "skillex sync --scope project --project '${cr}'"`,
|
|
15144
|
-
""
|
|
15145
|
-
];
|
|
15146
|
-
const managedSkillsSync = parseToml3(skillsSync.join("\n")).tasks["skills:sync"];
|
|
15147
|
-
const keepSkillsSync = existingTasks["skills:sync"] !== void 0 && canonicalJson(existingTasks["skills:sync"]) === canonicalJson(managedSkillsSync);
|
|
15148
15122
|
const appended = [
|
|
15149
15123
|
"",
|
|
15150
15124
|
_WireMiseAgentHooks.MARKER + " (generated \u2014 see .agents/hooks/README.md)",
|
|
15151
15125
|
// PJAN-61: task names use the colon namespace form. A colon is not legal
|
|
15152
15126
|
// in a BARE toml key, so every header here MUST stay quoted.
|
|
15153
|
-
|
|
15127
|
+
// PJAN-135 retired 2026-10-10: no skills:sync task is appended — skill
|
|
15128
|
+
// operations invoke skillex directly (`pj skills …`), never a mise task.
|
|
15154
15129
|
"[[watch_files]]",
|
|
15155
15130
|
'patterns = [".agents/hooks/hooks.master.json"]',
|
|
15156
15131
|
'task = "hooks:sync"',
|
package/dist/mcp-server.js
CHANGED
|
@@ -6837,7 +6837,7 @@ function skillCoreOptions(ctx) {
|
|
|
6837
6837
|
function relaySkillexCommands(text) {
|
|
6838
6838
|
return text.replace(SKILLEX_COMMAND, PJ_SKILLS);
|
|
6839
6839
|
}
|
|
6840
|
-
var SKILLEX_COMMAND = /(?<![\w/@.:-])skillex(?=\s+(?:skill|set|pack|init|enable|disable|inherit|sync|status|explain|doctor|vendor|profile|migrate)\b)/g;
|
|
6840
|
+
var SKILLEX_COMMAND = /(?<![\w/@.:-])skillex(?=\s+(?:skill|set|pack|bmad|init|enable|disable|inherit|normalize|sync|status|explain|doctor|vendor|profile|migrate|integrations|clients|root)\b)/g;
|
|
6841
6841
|
function skillDiagnostics(findings) {
|
|
6842
6842
|
return findings.map(
|
|
6843
6843
|
(finding2) => `${finding2.code}: ${finding2.message}${finding2.path ? ` (${finding2.path})` : ""}${finding2.fix ? ` \u2014 ${relaySkillexCommands(finding2.fix)}` : ""}`
|
|
@@ -7723,20 +7723,13 @@ var HOOKS_COMMENT_HEADER = `# This block will handle the linking of
|
|
|
7723
7723
|
# TODO: Ensure this works for all levels of nesting.
|
|
7724
7724
|
# i.e. All linked agent files MUST be siblings at
|
|
7725
7725
|
# any given level of nesting.`;
|
|
7726
|
-
var SKILLEX_TOOL_VERSION = "0.1.2";
|
|
7727
|
-
var SKILLS_SYNC_TOOLS = `{ "npm:@delorenj/skillex" = { version = "${SKILLEX_TOOL_VERSION}", allow_low_downloads = true }, node = "24" }`;
|
|
7728
7726
|
var LINK_AGENTFILES_WATCH_TASK_BLOCK = `[[watch_files]]
|
|
7729
7727
|
patterns = ["AGENTS.md"]
|
|
7730
7728
|
task = "${LINK_AGENTFILES_TASK}"
|
|
7731
7729
|
|
|
7732
7730
|
${taskHeader(LINK_AGENTFILES_TASK)}
|
|
7733
7731
|
description = "Symlink all agent files to AGENTS.md"
|
|
7734
|
-
run = ${JSON.stringify(LINK_AGENTFILES_SCRIPT)}
|
|
7735
|
-
|
|
7736
|
-
${taskHeader(SKILLS_SYNC_TASK)}
|
|
7737
|
-
description = "Reconcile this project's selected skills"
|
|
7738
|
-
tools = ${SKILLS_SYNC_TOOLS}
|
|
7739
|
-
run = "skillex sync --scope project --project '{{config_root}}'"`;
|
|
7732
|
+
run = ${JSON.stringify(LINK_AGENTFILES_SCRIPT)}`;
|
|
7740
7733
|
var VERSIONING_BLOCK = `# >>> mise-versioning >>> (managed block \u2014 do not edit by hand; re-run init to update)
|
|
7741
7734
|
[tasks."version"]
|
|
7742
7735
|
description = "Print the current version (vX.Y.Z)"
|
|
@@ -8201,6 +8194,7 @@ function managedLinkHookIssues(parsed) {
|
|
|
8201
8194
|
}
|
|
8202
8195
|
var RETIRED_LINK_TASKS = [
|
|
8203
8196
|
"skills-sync",
|
|
8197
|
+
SKILLS_SYNC_TASK,
|
|
8204
8198
|
"skills-provision-packs",
|
|
8205
8199
|
PROVISION_PACKS_TASK,
|
|
8206
8200
|
"skills-provision-bmad",
|
|
@@ -8214,8 +8208,7 @@ function managedLinkTables() {
|
|
|
8214
8208
|
const parsed = parseToml2(LINK_AGENTFILES_WATCH_TASK_BLOCK);
|
|
8215
8209
|
return {
|
|
8216
8210
|
watch: { lines: chunks[0], value: parsed.watch_files[0] },
|
|
8217
|
-
link: { lines: chunks[1], value: parsed.tasks[LINK_AGENTFILES_TASK] }
|
|
8218
|
-
sync: { lines: chunks[2], value: parsed.tasks[SKILLS_SYNC_TASK] }
|
|
8211
|
+
link: { lines: chunks[1], value: parsed.tasks[LINK_AGENTFILES_TASK] }
|
|
8219
8212
|
};
|
|
8220
8213
|
}
|
|
8221
8214
|
function managedWatchPlan(watches) {
|
|
@@ -8261,7 +8254,7 @@ function reconcileManagedLinkTables(text) {
|
|
|
8261
8254
|
edits.push({ start: span.start, end: span.end, lines: [] });
|
|
8262
8255
|
continue;
|
|
8263
8256
|
}
|
|
8264
|
-
const table =
|
|
8257
|
+
const table = managed.link;
|
|
8265
8258
|
found.add(name);
|
|
8266
8259
|
if (isDeepStrictEqualLoose(parseBody(lines, statements, index), table.value)) continue;
|
|
8267
8260
|
if (name === LINK_AGENTFILES_TASK && !lines.slice(span.start, span.end).join("\n").includes("link-agentfiles")) {
|
|
@@ -8285,8 +8278,7 @@ function reconcileManagedLinkTables(text) {
|
|
|
8285
8278
|
});
|
|
8286
8279
|
const missing = [
|
|
8287
8280
|
...plan.managed >= 0 ? [] : [managed.watch.lines],
|
|
8288
|
-
...found.has(LINK_AGENTFILES_TASK) ? [] : [managed.link.lines]
|
|
8289
|
-
...found.has(SKILLS_SYNC_TASK) ? [] : [managed.sync.lines]
|
|
8281
|
+
...found.has(LINK_AGENTFILES_TASK) ? [] : [managed.link.lines]
|
|
8290
8282
|
];
|
|
8291
8283
|
if (missing.length) {
|
|
8292
8284
|
const block = missing.flatMap((chunk, index) => index ? ["", ...chunk] : chunk);
|
|
@@ -8370,7 +8362,6 @@ function expectedLinkModel(base, required) {
|
|
|
8370
8362
|
const tasks = isTable(model.tasks) ? model.tasks : model.tasks = {};
|
|
8371
8363
|
for (const name of RETIRED_LINK_TASKS) delete tasks[name];
|
|
8372
8364
|
tasks[LINK_AGENTFILES_TASK] = deepClone(managed.link.value);
|
|
8373
|
-
tasks[SKILLS_SYNC_TASK] = deepClone(managed.sync.value);
|
|
8374
8365
|
const watches = Array.isArray(model.watch_files) ? model.watch_files : [];
|
|
8375
8366
|
const plan = managedWatchPlan(watches);
|
|
8376
8367
|
model.watch_files = [
|
|
@@ -9330,35 +9321,26 @@ function retiredSkillsScripts(ctx) {
|
|
|
9330
9321
|
return RETIRED_SKILL_SCRIPTS.map((path) => join7(ctx.repoRoot, path)).filter((path) => Boolean(lstatIfPresent(path)));
|
|
9331
9322
|
}
|
|
9332
9323
|
function skillsWiringFindings(text) {
|
|
9333
|
-
if (text === null) return { fixable: [
|
|
9324
|
+
if (text === null) return { fixable: [], manual: [] };
|
|
9334
9325
|
const fixable = [];
|
|
9335
9326
|
const manual = [];
|
|
9336
9327
|
let parsed = parsedOrUndefined(text);
|
|
9337
9328
|
if (!parsed) {
|
|
9338
9329
|
const baseline = parseBaseline(text);
|
|
9339
|
-
if ("refused" in baseline) return { fixable: [], manual: ["mise.toml is invalid TOML; repair it before
|
|
9330
|
+
if ("refused" in baseline) return { fixable: [], manual: ["mise.toml is invalid TOML; repair it before pjangler can retire skill tasks"] };
|
|
9340
9331
|
parsed = baseline.parsed;
|
|
9341
9332
|
fixable.push("mise.toml repeats the close of a [hooks] array (an earlier pjangler run wrote it); migrate repairs it");
|
|
9342
9333
|
}
|
|
9343
9334
|
const record3 = (value) => isTable(value) ? value : {};
|
|
9344
|
-
const tasks = record3(parsed.tasks)
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
fixable.push("skills:sync must explicitly target {{config_root}} with the Node CLI");
|
|
9348
|
-
}
|
|
9349
|
-
const pin = record3(task.tools)["npm:@delorenj/skillex"];
|
|
9350
|
-
const pinned = typeof pin === "string" ? pin : record3(pin).version;
|
|
9351
|
-
if (pinned !== SKILLEX_TOOL_VERSION) {
|
|
9352
|
-
fixable.push(`skills:sync must pin @delorenj/skillex ${SKILLEX_TOOL_VERSION} in its own tools table`);
|
|
9353
|
-
} else if (typeof pin === "string") {
|
|
9354
|
-
fixable.push(`skills:sync pins @delorenj/skillex as the plain string "${pin}", which mise 2026.9 refuses to install inside its 30-day minimumPackageAge; pin { version = "${SKILLEX_TOOL_VERSION}", allow_low_downloads = true }`);
|
|
9355
|
-
} else if (record3(pin).allow_low_downloads !== true) {
|
|
9356
|
-
fixable.push(`skills:sync must approve @delorenj/skillex ${SKILLEX_TOOL_VERSION} with allow_low_downloads = true (mise 2026.9 refuses to install it otherwise)`);
|
|
9335
|
+
const tasks = record3(parsed.tasks);
|
|
9336
|
+
if (tasks[SKILLS_SYNC_TASK] !== void 0) {
|
|
9337
|
+
fixable.push(`Retire the ${SKILLS_SYNC_TASK} mise task; invoke skillex directly (pj skills sync --scope project --project .)`);
|
|
9357
9338
|
}
|
|
9358
9339
|
const retired = /sync-skills\.py|provision-(?:packs|bmad-skills)\.py|skills[:\-]provision/;
|
|
9359
9340
|
const renames = new Map(RETIRED_TASK_RENAMES);
|
|
9360
|
-
const replaced = /* @__PURE__ */ new Set([...RETIRED_LINK_TASKS, LINK_AGENTFILES_TASK
|
|
9341
|
+
const replaced = /* @__PURE__ */ new Set([...RETIRED_LINK_TASKS, LINK_AGENTFILES_TASK]);
|
|
9361
9342
|
for (const [name, entry] of Object.entries(tasks)) {
|
|
9343
|
+
if (name === SKILLS_SYNC_TASK) continue;
|
|
9362
9344
|
if (!retired.test(name) && !retired.test(JSON.stringify(record3(entry).run ?? ""))) continue;
|
|
9363
9345
|
if (replaced.has(renames.get(name) ?? name)) {
|
|
9364
9346
|
if (!fixable.includes("Retire Python skill sync/provision tasks from mise.toml")) fixable.push("Retire Python skill sync/provision tasks from mise.toml");
|
|
@@ -9373,7 +9355,7 @@ function skillsWiringFindings(text) {
|
|
|
9373
9355
|
}
|
|
9374
9356
|
manual.push(...decisions.manual.filter((entry) => SKILL_WRITER_MENTION.test(entry.command)).map(manualHookLine));
|
|
9375
9357
|
const watches = Array.isArray(parsed.watch_files) ? parsed.watch_files : [];
|
|
9376
|
-
if (managedWatchPlan(watches).skills.length) fixable.push("Remove the skills watch hook; run skills
|
|
9358
|
+
if (managedWatchPlan(watches).skills.length) fixable.push("Remove the skills watch hook; run pj skills sync explicitly, never automatically");
|
|
9377
9359
|
return { fixable, manual };
|
|
9378
9360
|
}
|
|
9379
9361
|
function skillsWiringIssues(text) {
|
|
@@ -9392,10 +9374,11 @@ function createAgentHooksChecks() {
|
|
|
9392
9374
|
const retired = retiredSkillsScripts(ctx);
|
|
9393
9375
|
const unsafe = retired.filter((path) => lstatIfPresent(path)?.isDirectory());
|
|
9394
9376
|
const details = [...finding2.details, ...wiring, ...retired.map((path) => unsafe.includes(path) ? `Inspect and preserve retired script directory: ${path}` : `${path} is a retired skill writer`)];
|
|
9377
|
+
const miseDrift = mise !== null && upsertLinkAgentfilesBlock(mise, ctx) !== mise;
|
|
9395
9378
|
return {
|
|
9396
9379
|
...finding2,
|
|
9397
9380
|
status: wiring.length || retired.length ? "fail" : finding2.status,
|
|
9398
|
-
fixable: unsafe.length ? false : finding2.fixable || wiring.length > 0 || retired.length > 0,
|
|
9381
|
+
fixable: unsafe.length ? false : finding2.fixable || wiring.length > 0 || retired.length > 0 || miseDrift,
|
|
9399
9382
|
summary: details.length ? `${details.length} Skillex finding(s)` : finding2.summary,
|
|
9400
9383
|
details
|
|
9401
9384
|
};
|
|
@@ -9440,7 +9423,7 @@ function createAgentHooksChecks() {
|
|
|
9440
9423
|
if (!reconciled.ok || !roots.ok) {
|
|
9441
9424
|
return result2(changedFiles.length ? "partial" : "blocked", "Skillex requires attention before project migration");
|
|
9442
9425
|
}
|
|
9443
|
-
return changedFiles.length ? result2("applied", "Project skills use the Node core and
|
|
9426
|
+
return changedFiles.length ? result2("applied", "Project skills use the Node core and direct invocation") : result2("noop", "No changes required");
|
|
9444
9427
|
}
|
|
9445
9428
|
},
|
|
9446
9429
|
{
|
|
@@ -10510,7 +10493,7 @@ var AgentHooksRecipe = class extends Recipe {
|
|
|
10510
10493
|
printNextSteps() {
|
|
10511
10494
|
console.log("\u{1FA9D} Agent-hooks layer installed!");
|
|
10512
10495
|
console.log(" Next steps:");
|
|
10513
|
-
console.log(" 1.
|
|
10496
|
+
console.log(" 1. pj skills sync --scope project --project . # sync .agents/skills.json into local CLI dirs");
|
|
10514
10497
|
console.log(" 2. mise run hooks:sync # generate .claude/settings.json + inject codex/kimi/hermes");
|
|
10515
10498
|
console.log(" 3. git add .claude/settings.json .agents/hooks .agents/skills.json && commit (codex/kimi/hermes are per-dev)");
|
|
10516
10499
|
console.log(" 4. mise run hindsight:setup # set HINDSIGHT_OP_KEY_REF to your 1Password item first");
|
|
@@ -13630,21 +13613,13 @@ ${leaveBlock}`);
|
|
|
13630
13613
|
}
|
|
13631
13614
|
wiredHooks = true;
|
|
13632
13615
|
}
|
|
13633
|
-
const skillsSync = [
|
|
13634
|
-
'[tasks."skills:sync"]',
|
|
13635
|
-
`description = "Reconcile this project's selected skills"`,
|
|
13636
|
-
`tools = ${SKILLS_SYNC_TOOLS}`,
|
|
13637
|
-
`run = "skillex sync --scope project --project '${cr}'"`,
|
|
13638
|
-
""
|
|
13639
|
-
];
|
|
13640
|
-
const managedSkillsSync = parseToml3(skillsSync.join("\n")).tasks["skills:sync"];
|
|
13641
|
-
const keepSkillsSync = existingTasks["skills:sync"] !== void 0 && canonicalJson(existingTasks["skills:sync"]) === canonicalJson(managedSkillsSync);
|
|
13642
13616
|
const appended = [
|
|
13643
13617
|
"",
|
|
13644
13618
|
_WireMiseAgentHooks.MARKER + " (generated \u2014 see .agents/hooks/README.md)",
|
|
13645
13619
|
// PJAN-61: task names use the colon namespace form. A colon is not legal
|
|
13646
13620
|
// in a BARE toml key, so every header here MUST stay quoted.
|
|
13647
|
-
|
|
13621
|
+
// PJAN-135 retired 2026-10-10: no skills:sync task is appended — skill
|
|
13622
|
+
// operations invoke skillex directly (`pj skills …`), never a mise task.
|
|
13648
13623
|
"[[watch_files]]",
|
|
13649
13624
|
'patterns = [".agents/hooks/hooks.master.json"]',
|
|
13650
13625
|
'task = "hooks:sync"',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delorenj/pjangler",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.16",
|
|
4
4
|
"description": "Project subsystem bootstrapper CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"license": "MIT",
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@clack/prompts": "^1.4.0",
|
|
62
|
-
"@delorenj/skillex": "0.
|
|
62
|
+
"@delorenj/skillex": "0.3.0",
|
|
63
63
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
64
64
|
"commander": "^14.0.2",
|
|
65
65
|
"pg": "^8.16.0",
|
|
@@ -231,8 +231,10 @@ _tasks:
|
|
|
231
231
|
# must not touch, so a re-render into a live project destroyed the file and
|
|
232
232
|
# reported success.
|
|
233
233
|
- "./.mise/scripts/link-agentfiles.sh \"$(pwd)\""
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
|
|
234
|
+
# Skill activation is NOT a copier task: the skills:sync mise task retired
|
|
235
|
+
# with the 2026-10-10 skill-operation-task policy. `pj init` provisions the
|
|
236
|
+
# fresh project's skills itself through the bundled skillex core in its
|
|
237
|
+
# post-render parity pass; operators invoke `pj skills …` / `skillex …`
|
|
238
|
+
# directly afterwards.
|
|
237
239
|
# Stamp the repo's absolute path into .project.json (the SOT) post-render.
|
|
238
240
|
- "python3 -c \"import json,os,pathlib; p=pathlib.Path('.project.json'); d=json.loads(p.read_text()); d['repo_path']=os.getcwd(); p.write_text(json.dumps(d,indent=2)+chr(10))\""
|
|
@@ -23,6 +23,13 @@ cleanup() {
|
|
|
23
23
|
}
|
|
24
24
|
trap cleanup EXIT HUP INT TERM
|
|
25
25
|
|
|
26
|
-
op
|
|
26
|
+
# op opens /dev/tty for prompts. This hook runs inside whatever terminal
|
|
27
|
+
# entered the project (often an agent's pane), so op is detached from it:
|
|
28
|
+
# a prompt must fail, never read the user's keystrokes. macOS has no setsid.
|
|
29
|
+
if command -v setsid >/dev/null 2>&1; then
|
|
30
|
+
setsid -w op inject -i "$source_file" -o "$temp_file" --force </dev/null
|
|
31
|
+
else
|
|
32
|
+
op inject -i "$source_file" -o "$temp_file" --force </dev/null
|
|
33
|
+
fi
|
|
27
34
|
mv -f -- "$temp_file" "$target_file"
|
|
28
35
|
trap - EXIT HUP INT TERM
|
|
@@ -52,11 +52,6 @@ task = "hooks:sync"
|
|
|
52
52
|
[tasks."link:agentfiles"]
|
|
53
53
|
description = "Symlink all agent files to AGENTS.md"
|
|
54
54
|
run = "'{% raw %}{{config_root}}{% endraw %}/.mise/scripts/link-agentfiles.sh' '{% raw %}{{config_root}}{% endraw %}'"
|
|
55
|
-
|
|
56
|
-
[tasks."skills:sync"]
|
|
57
|
-
description = "Reconcile this project's selected skills"
|
|
58
|
-
tools = { "npm:@delorenj/skillex" = { version = "0.1.2", allow_low_downloads = true }, node = "24" }
|
|
59
|
-
run = "skillex sync --scope project --project '{% raw %}{{config_root}}{% endraw %}'"
|
|
60
55
|
{% if agent_hooks_layer %}
|
|
61
56
|
# --- Project-scoped agent hooks (see .agents/hooks/README.md) ---
|
|
62
57
|
|