@delorenj/pjangler 1.5.14 → 1.5.15

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 = name === LINK_AGENTFILES_TASK ? managed.link : managed.sync;
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: ["mise.toml is missing; add the explicit skills:sync task"], manual: [] };
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 running skills:sync"] };
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), task = record3(tasks[SKILLS_SYNC_TASK]);
10534
- const run = Array.isArray(task.run) && task.run.length === 1 ? task.run[0] : task.run;
10535
- if (typeof run !== "string" || !/^skillex\s+sync\s+--scope\s+project\s+--project\s+(['"])\{\{config_root\}\}\1\s*$/.test(run)) {
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, SKILLS_SYNC_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:sync explicitly");
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 explicit sync task") : result2("noop", "No changes required");
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. mise run skills:sync # sync .agents/skills.json into local CLI dirs");
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
- ...keepSkillsSync ? [] : skillsSync,
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"',
@@ -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 = name === LINK_AGENTFILES_TASK ? managed.link : managed.sync;
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: ["mise.toml is missing; add the explicit skills:sync task"], manual: [] };
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 running skills:sync"] };
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), task = record3(tasks[SKILLS_SYNC_TASK]);
9345
- const run = Array.isArray(task.run) && task.run.length === 1 ? task.run[0] : task.run;
9346
- if (typeof run !== "string" || !/^skillex\s+sync\s+--scope\s+project\s+--project\s+(['"])\{\{config_root\}\}\1\s*$/.test(run)) {
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, SKILLS_SYNC_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:sync explicitly");
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 explicit sync task") : result2("noop", "No changes required");
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. mise run skills:sync # sync .agents/skills.json into local CLI dirs");
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
- ...keepSkillsSync ? [] : skillsSync,
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.14",
3
+ "version": "1.5.15",
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.1.2",
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
- # One explicit project activation after rendering. Trust only this generated
235
- # config for this process; do not persist trust or execute enter/watch hooks.
236
- - "MISE_TRUSTED_CONFIG_PATHS=\"$(pwd)/mise.toml\" mise run skills:sync"
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))\""
@@ -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