@mrclrchtr/supi-skills 7.2.0 → 7.2.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "7.2.0",
3
+ "version": "7.2.1",
4
4
  "description": "Shared settings, configuration, reporting, and session infrastructure",
5
5
  "license": "MIT",
6
6
  "repository": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-skills",
3
- "version": "7.2.0",
3
+ "version": "7.2.1",
4
4
  "description": "Scoped skill controls and skill input shortcuts for PI",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -30,7 +30,7 @@
30
30
  "README.md"
31
31
  ],
32
32
  "dependencies": {
33
- "@mrclrchtr/supi-core": "7.2.0"
33
+ "@mrclrchtr/supi-core": "7.2.1"
34
34
  },
35
35
  "bundledDependencies": [
36
36
  "@mrclrchtr/supi-core"
@@ -2,6 +2,7 @@ import {
2
2
  type BuildSystemPromptOptions,
3
3
  formatSkillsForPrompt,
4
4
  } from "@earendil-works/pi-coding-agent";
5
+ import { recordDebugEvent, truncateDebugIdentity } from "@mrclrchtr/supi-core/debug";
5
6
  import { resolveInvocation } from "./skill-model-invocation-config.ts";
6
7
 
7
8
  export type { ModelInvocationState } from "./skill-model-invocation-config.ts";
@@ -12,7 +13,25 @@ export const ENABLED = "Enabled";
12
13
  export const MODEL_DISABLED = "Model invocation disabled";
13
14
  export const DISABLED = "Disabled";
14
15
 
15
- /** Replace PI's generated skill block with the effective scoped invocation state. */
16
+ /** Replace PI's rendered skills section while preserving the rest of a forced prompt. */
17
+ function replaceStructuredSkillsSection(
18
+ systemPrompt: string,
19
+ original: string,
20
+ replacement: string,
21
+ ): string | undefined {
22
+ const content = original.trim();
23
+ if (!content) return undefined;
24
+ const currentSection = `<skills>\n${content}\n</skills>`;
25
+ const start = systemPrompt.indexOf(currentSection);
26
+ if (start < 0 || systemPrompt.indexOf(currentSection, start + currentSection.length) >= 0) {
27
+ return undefined;
28
+ }
29
+ const replacementContent = replacement.trim();
30
+ const section = replacementContent ? `<skills>\n${replacementContent}\n</skills>` : "";
31
+ return `${systemPrompt.slice(0, start)}${section}${systemPrompt.slice(start + currentSection.length)}`;
32
+ }
33
+
34
+ /** Apply the effective scoped invocation state to PI's mutable prompt options. */
16
35
  export function applyPromptOverrides({
17
36
  options,
18
37
  systemPrompt,
@@ -26,7 +45,12 @@ export function applyPromptOverrides({
26
45
  projectTrusted: boolean;
27
46
  homeDir?: string;
28
47
  }): string | undefined {
29
- if (options.selectedTools && !options.selectedTools.includes("read")) return undefined;
48
+ const selectedTools = options.selectedTools;
49
+ const skillFileReadTool =
50
+ selectedTools === undefined
51
+ ? "read"
52
+ : (["read", "bash"] as const).find((tool) => selectedTools.includes(tool));
53
+ if (!skillFileReadTool) return undefined;
30
54
  const skills = options.skills ?? [];
31
55
  const effective = skills.map((skill) => ({
32
56
  ...skill,
@@ -39,11 +63,52 @@ export function applyPromptOverrides({
39
63
  homeDir,
40
64
  }).disabled,
41
65
  }));
42
- const original = formatSkillsForPrompt(skills);
43
- const replacement = formatSkillsForPrompt(effective);
66
+ const original = formatSkillsForPrompt(skills, skillFileReadTool);
67
+ const replacement = formatSkillsForPrompt(effective, skillFileReadTool);
44
68
  if (original === replacement) return undefined;
45
- if (!original) return `${systemPrompt}${replacement}`;
46
- if (systemPrompt.includes(original)) return systemPrompt.replace(original, replacement);
69
+
70
+ // PI renders skills inside its structured <skills> section. Mutate the
71
+ // normalized options so PI can render the replacement without byte matching
72
+ // the already-rendered prompt.
73
+ if (options.forceSystemPrompt === undefined) {
74
+ options.skills = effective;
75
+ return undefined;
76
+ }
77
+
78
+ const structuredPrompt = replaceStructuredSkillsSection(systemPrompt, original, replacement);
79
+ if (structuredPrompt !== undefined) {
80
+ options.skills = effective;
81
+ options.forceSystemPrompt = structuredPrompt;
82
+ return undefined;
83
+ }
84
+
85
+ if (!original) {
86
+ const content = replacement.trim();
87
+ options.skills = effective;
88
+ return `${systemPrompt}${content ? `\n<skills>\n${content}\n</skills>` : ""}`;
89
+ }
90
+
91
+ recordDebugEvent({
92
+ source: "supi-skills",
93
+ level: "warning",
94
+ category: "prompt-overrides",
95
+ message: "Could not apply skill model-invocation overrides",
96
+ cwd: truncateDebugIdentity(cwd),
97
+ data: {
98
+ skillCount: skills.length,
99
+ changedSkills: skills
100
+ .filter(
101
+ (skill, index) =>
102
+ skill.disableModelInvocation !== effective[index]?.disableModelInvocation,
103
+ )
104
+ .map((skill) => skill.name),
105
+ forceSystemPrompt: true,
106
+ hasSkillsSection: systemPrompt.includes("<skills>"),
107
+ originalLength: original.length,
108
+ replacementLength: replacement.length,
109
+ systemPromptLength: systemPrompt.length,
110
+ },
111
+ });
47
112
  // biome-ignore lint/suspicious/noConsole: prompt mismatch must not fail silently
48
113
  console.warn("[supi-skills] Could not apply skill model-invocation overrides");
49
114
  return undefined;