pi-skillful 0.4.0 → 0.5.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 -0
- package/CONTRIBUTING.md +14 -0
- package/README.md +14 -1
- package/SECURITY.md +2 -0
- package/package.json +8 -5
- package/src/extensions/session-skill-toggles.ts +10 -12
- package/src/extensions/skill-visibility.ts +37 -15
- package/src/install-telemetry.ts +20 -47
- package/src/skill-prompt.ts +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,25 @@ This project follows the spirit of [Keep a Changelog](https://keepachangelog.com
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.5.0] - 2026-10-09
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Read the current theme when rendering session-toggle borders and fit the complete border at very narrow widths. Extend editor composition coverage for paste, padding, indicators, and disposal.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Update skill visibility and session toggles through Pi's structured prompt API instead of replacing the rendered system prompt. Preserve unrelated sections and deliberate full-prompt overrides.
|
|
18
|
+
- Update the shared Pi development and contract-test baseline to 1.1.0; require Node.js >=22.19.0 to match the host runtime. Pi remains a host-supplied peer dependency.
|
|
19
|
+
- Share install telemetry mechanics through `@mocito/install-telemetry` while preserving Pi-specific settings and state paths.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Apply visibility settings to Pi 1.1.0's wrapped skills section, including when file readers are hidden behind codemode.
|
|
24
|
+
- Keep frontmatter and earlier extensions' visibility restrictions when session toggle slots are active.
|
|
25
|
+
- Restore hidden-skill startup colors on Pi 1.x while preserving theme changes, expanded resource paths, and reloads.
|
|
26
|
+
- Let `enableInstallTelemetry: false` override an enabled `PI_TELEMETRY` environment flag.
|
|
27
|
+
|
|
9
28
|
## [0.4.0] - 2026-07-28
|
|
10
29
|
|
|
11
30
|
### Added
|
package/CONTRIBUTING.md
CHANGED
|
@@ -33,6 +33,14 @@ pi -e /path/to/pi-mono/packages/pi-skillful
|
|
|
33
33
|
|
|
34
34
|
## Pull request checklist
|
|
35
35
|
|
|
36
|
+
For prompt changes, also run the shared real-session contracts from the monorepo root:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
node --import tsx --test tests/structured-prompts.test.mjs
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
These load both Skillful and Scout through Pi's SDK, inspect serialized provider requests and session history, and exercise the toggle editor wrapper. They use disposable profiles, synthetic credentials, temporary reference directories, and mocked provider responses; they do not use a live model or existing user settings. Coverage includes extension load order, explicit prompt overrides, hidden skill invocation, codemode, reload, resume, fork/tree navigation, and providers that collapse system updates.
|
|
43
|
+
|
|
36
44
|
Before opening a pull request:
|
|
37
45
|
|
|
38
46
|
- Run `npm run check`.
|
|
@@ -52,3 +60,9 @@ Before opening a pull request:
|
|
|
52
60
|
## Code of conduct
|
|
53
61
|
|
|
54
62
|
This project follows the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md).
|
|
63
|
+
## Pi 1.1.0 terminal audit
|
|
64
|
+
|
|
65
|
+
From the monorepo root, run `node --import tsx --test tests/tui-contracts.test.mjs`
|
|
66
|
+
alongside the package tests. `tests/TUI_AUDIT.md` records coverage, known host
|
|
67
|
+
limitations, and the disposable-profile physical-terminal acceptance procedure.
|
|
68
|
+
The startup patch remains isolated; no unreleased cursor API is required.
|
package/README.md
CHANGED
|
@@ -102,6 +102,19 @@ Configured slots appear on the prompt editor's top border as `N skill-name`. Pro
|
|
|
102
102
|
|
|
103
103
|
On app startup, non-hidden skills are active and hidden skills are inactive. Within a running Pi process, `/new` preserves the current toggle state for the new session. Resuming, forking, cloning, reloading, or restarting Pi resets toggle state from settings. Inline `/skill:name` invocation remains explicit and works even when that skill is inactive. Skills bundled in Pi packages are never modified by these toggles.
|
|
104
104
|
|
|
105
|
+
### Prompt updates and extension composition
|
|
106
|
+
|
|
107
|
+
Toggle-border colors follow the current theme without another toggle or reload.
|
|
108
|
+
At very narrow widths, the complete border is clipped to the available columns.
|
|
109
|
+
|
|
110
|
+
Visibility and toggles use Pi's structured `systemPromptOptions.skills` API (Pi 1.1.0 or newer), not text replacement of the full system prompt. Pi records skill-list changes in the session transcript, alongside independent updates from other extensions such as Scout.
|
|
111
|
+
|
|
112
|
+
An active slot can lift a restriction from `skillful.hiddenSkills`, but does not clear `disable-model-invocation` frontmatter or a restriction applied by an earlier prompt handler. Later handlers see the updated list and can apply their own policy. Package-bundled skills remain outside Skillful's visibility controls and toggle slots.
|
|
113
|
+
|
|
114
|
+
A deliberate full-prompt override from another extension (`systemPrompt` or `forceSystemPrompt`) takes precedence. Skillful updates the structured skill list but does not rewrite that override; its author controls any skill descriptions embedded in the replacement text. Explicit skill invocation remains available.
|
|
115
|
+
|
|
116
|
+
Structured changes let Pi update individual prompt sections. Providers that cannot accept mid-conversation system changes may still need a full prompt checkpoint; cache savings are not guaranteed.
|
|
117
|
+
|
|
105
118
|
## Installation
|
|
106
119
|
|
|
107
120
|
Install from npm:
|
|
@@ -148,7 +161,7 @@ This package is source-distributed. Pi loads the TypeScript extensions directly
|
|
|
148
161
|
|
|
149
162
|
Requirements:
|
|
150
163
|
|
|
151
|
-
- Node.js >=
|
|
164
|
+
- Node.js >= 22.19.0
|
|
152
165
|
- npm for local development commands
|
|
153
166
|
|
|
154
167
|
Common commands:
|
package/SECURITY.md
CHANGED
|
@@ -21,6 +21,8 @@ The maintainer will acknowledge reports as soon as practical and coordinate disc
|
|
|
21
21
|
|
|
22
22
|
`pi-skillful` is a Pi package. Pi extensions execute with the same permissions as the local user running Pi. Users should review installed Pi packages and only install packages from sources they trust.
|
|
23
23
|
|
|
24
|
+
At startup, `@mocito/install-telemetry` sends a best-effort install/update ping to the configured telemetry endpoint once per package version unless CI, Pi offline/telemetry settings, or `enableInstallTelemetry: false` disables it. It contains only the package name/version and parsed platform/runtime/architecture; it does not include prompts, paths, configuration values, credentials, or provider responses.
|
|
25
|
+
|
|
24
26
|
### Progressive skill loading and trust boundary
|
|
25
27
|
|
|
26
28
|
`pi-skillful` extends Pi's skill discovery to ancestor directories above the git repository root, loading `.agents/skills/` from every parent directory up to the filesystem root (excluding `~/.agents/skills/`, which Pi already loads globally).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-skillful",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Pi package with skill invocation and visibility improvements.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"@earendil-works/pi-tui": "*"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@earendil-works/pi-coding-agent": "
|
|
49
|
-
"@earendil-works/pi-tui": "
|
|
50
|
-
"@types/node": "^26.
|
|
48
|
+
"@earendil-works/pi-coding-agent": "1.1.0",
|
|
49
|
+
"@earendil-works/pi-tui": "1.1.0",
|
|
50
|
+
"@types/node": "^26.6.3",
|
|
51
51
|
"typescript": "^7.0.2"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
@@ -60,6 +60,9 @@
|
|
|
60
60
|
"access": "public"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
|
-
"node": ">=
|
|
63
|
+
"node": ">=22.19.0"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@mocito/install-telemetry": "0.1.1"
|
|
64
67
|
}
|
|
65
68
|
}
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
type AppKeybinding,
|
|
4
4
|
type ExtensionAPI,
|
|
5
5
|
type KeybindingsManager,
|
|
6
|
-
type Skill,
|
|
7
6
|
type Theme,
|
|
8
7
|
} from "@earendil-works/pi-coding-agent";
|
|
9
8
|
import type { AutocompleteProvider, EditorComponent, EditorTheme, Focusable, KeyId, TUI } from "@earendil-works/pi-tui";
|
|
@@ -16,7 +15,6 @@ import {
|
|
|
16
15
|
type SkillToggleModifier,
|
|
17
16
|
type SkillToggleSlot,
|
|
18
17
|
} from "../config.js";
|
|
19
|
-
import { replaceSkillsSection } from "../skill-prompt.js";
|
|
20
18
|
import { isTopLevelSkill, listLoadedSkills } from "../skills.js";
|
|
21
19
|
|
|
22
20
|
const STORE_KEY = Symbol.for("pi-skillful.sessionSkillTogglesStore");
|
|
@@ -40,7 +38,7 @@ interface SessionToggleState {
|
|
|
40
38
|
installedEditor: boolean;
|
|
41
39
|
previousEditorFactory: SkillfulEditorFactory | undefined;
|
|
42
40
|
activeTui: TUI | undefined;
|
|
43
|
-
theme: Theme | undefined;
|
|
41
|
+
theme: (() => Theme) | undefined;
|
|
44
42
|
notify: (message: string, type?: "info" | "warning" | "error") => void;
|
|
45
43
|
}
|
|
46
44
|
|
|
@@ -79,7 +77,7 @@ export default function sessionSkillToggles(pi: ExtensionAPI) {
|
|
|
79
77
|
installedEditor: false,
|
|
80
78
|
previousEditorFactory: undefined,
|
|
81
79
|
activeTui: undefined,
|
|
82
|
-
theme: ctx.ui.theme,
|
|
80
|
+
theme: () => ctx.ui.theme,
|
|
83
81
|
notify: ctx.ui.notify.bind(ctx.ui),
|
|
84
82
|
};
|
|
85
83
|
|
|
@@ -90,13 +88,13 @@ export default function sessionSkillToggles(pi: ExtensionAPI) {
|
|
|
90
88
|
pi.on("before_agent_start", (event) => {
|
|
91
89
|
if (state.slots.length === 0 || !event.systemPromptOptions.skills?.length) return;
|
|
92
90
|
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
// Active slots lift only Skillful's own restriction, never frontmatter or
|
|
92
|
+
// restrictions already applied by another prompt handler.
|
|
93
|
+
event.systemPromptOptions.skills = event.systemPromptOptions.skills.map((skill) =>
|
|
94
|
+
isTopLevelSkill(skill) && !isSkillActive(normalizeSkillName(skill.name))
|
|
95
|
+
? { ...skill, disableModelInvocation: true }
|
|
96
|
+
: skill,
|
|
95
97
|
);
|
|
96
|
-
|
|
97
|
-
const systemPrompt = replaceSkillsSection(event.systemPrompt, updatedSkills);
|
|
98
|
-
if (!systemPrompt) return;
|
|
99
|
-
return { systemPrompt };
|
|
100
98
|
});
|
|
101
99
|
|
|
102
100
|
pi.on("session_shutdown", (event, ctx) => {
|
|
@@ -373,7 +371,7 @@ function renderToggleBorder(width: number, borderColor: (text: string) => string
|
|
|
373
371
|
const fittedContent = truncateToWidth(renderToggleSegments(available), available, "");
|
|
374
372
|
const used = BORDER_PREFIX_WIDTH + visibleWidth(fittedContent) + BORDER_SUFFIX_WIDTH;
|
|
375
373
|
const fill = borderColor("─".repeat(Math.max(0, width - used)));
|
|
376
|
-
return `${borderColor(BORDER_PREFIX)}${fittedContent}${BORDER_SUFFIX}${fill}
|
|
374
|
+
return truncateToWidth(`${borderColor(BORDER_PREFIX)}${fittedContent}${BORDER_SUFFIX}${fill}`, width, "");
|
|
377
375
|
}
|
|
378
376
|
|
|
379
377
|
function renderToggleSegments(availableWidth: number): string {
|
|
@@ -396,5 +394,5 @@ function renderToggleSegments(availableWidth: number): string {
|
|
|
396
394
|
}
|
|
397
395
|
|
|
398
396
|
function stateThemeFg(color: "accent" | "muted", text: string): string {
|
|
399
|
-
return state.theme?.fg(color, text) ?? text;
|
|
397
|
+
return state.theme?.().fg(color, text) ?? text;
|
|
400
398
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { stripVTControlCharacters } from "node:util";
|
|
1
2
|
import {
|
|
2
3
|
DynamicBorder,
|
|
3
4
|
getSettingsListTheme,
|
|
@@ -19,20 +20,23 @@ import {
|
|
|
19
20
|
writeProjectSkillfulOverride,
|
|
20
21
|
writeToggleSlots,
|
|
21
22
|
} from "../config.js";
|
|
22
|
-
import { replaceSkillsSection } from "../skill-prompt.js";
|
|
23
23
|
import { isTopLevelSkill, listLoadedSkills, type LoadedSkillInfo } from "../skills.js";
|
|
24
24
|
import { hasActiveSessionSkillToggles, refreshSessionSkillToggles } from "./session-skill-toggles.js";
|
|
25
25
|
const SCOPES: SkillfulScope[] = ["global", "project"];
|
|
26
26
|
const STORE_KEY = Symbol.for("pi-skillful.skillVisibilityStore");
|
|
27
|
-
|
|
27
|
+
// Install the new component contract even after reloading a V3-patched process.
|
|
28
|
+
const STARTUP_PATCH_KEY = Symbol.for("pi-skillful.startupPatchV4");
|
|
28
29
|
|
|
29
30
|
interface SkillVisibilityStore {
|
|
30
31
|
hiddenSkillsByCwd: Map<string, Set<string>>;
|
|
31
32
|
theme: Theme | null;
|
|
33
|
+
getTheme?: () => Theme;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
interface ExpandableTextLike {
|
|
35
|
-
getCollapsedText
|
|
37
|
+
getCollapsedText?: () => string;
|
|
38
|
+
build?: () => string;
|
|
39
|
+
invalidate?: () => void;
|
|
36
40
|
setText: (text: string) => void;
|
|
37
41
|
}
|
|
38
42
|
|
|
@@ -43,6 +47,7 @@ interface BoxLike {
|
|
|
43
47
|
interface InteractiveModeLike {
|
|
44
48
|
loadedResourcesContainer?: BoxLike;
|
|
45
49
|
showLoadedResources?: (options?: unknown) => void;
|
|
50
|
+
getStartupExpansionState?: () => boolean;
|
|
46
51
|
session?: { resourceLoader?: { getSkills: () => { skills: Skill[]; diagnostics: unknown[] } } };
|
|
47
52
|
sessionManager?: { getCwd?: () => string };
|
|
48
53
|
}
|
|
@@ -84,6 +89,7 @@ export default function skillVisibility(pi: ExtensionAPI) {
|
|
|
84
89
|
|
|
85
90
|
pi.on("session_start", async (_event, ctx) => {
|
|
86
91
|
store.theme = ctx.ui.theme;
|
|
92
|
+
store.getTheme = () => ctx.ui.theme;
|
|
87
93
|
await refreshHiddenSkillCache(ctx.cwd, ctx.isProjectTrusted());
|
|
88
94
|
});
|
|
89
95
|
|
|
@@ -93,12 +99,9 @@ export default function skillVisibility(pi: ExtensionAPI) {
|
|
|
93
99
|
const hidden = await refreshHiddenSkillCache(ctx.cwd, ctx.isProjectTrusted());
|
|
94
100
|
if (hidden.size === 0 || !event.systemPromptOptions.skills?.length) return;
|
|
95
101
|
|
|
96
|
-
|
|
102
|
+
event.systemPromptOptions.skills = event.systemPromptOptions.skills.map((skill) =>
|
|
97
103
|
isTopLevelSkill(skill) && hidden.has(skill.name) ? { ...skill, disableModelInvocation: true } : skill,
|
|
98
104
|
);
|
|
99
|
-
const systemPrompt = replaceSkillsSection(event.systemPrompt, filteredSkills);
|
|
100
|
-
if (!systemPrompt) return;
|
|
101
|
-
return { systemPrompt };
|
|
102
105
|
});
|
|
103
106
|
|
|
104
107
|
pi.registerCommand("skillful", {
|
|
@@ -184,7 +187,6 @@ export function installStartupSkillListPatch(
|
|
|
184
187
|
return result;
|
|
185
188
|
};
|
|
186
189
|
|
|
187
|
-
const childrenBefore = this.loadedResourcesContainer?.children.length ?? 0;
|
|
188
190
|
try {
|
|
189
191
|
original.call(this, options);
|
|
190
192
|
} finally {
|
|
@@ -193,16 +195,36 @@ export function installStartupSkillListPatch(
|
|
|
193
195
|
|
|
194
196
|
if (rawSkillNames.length === 0 || !cwd || !this.loadedResourcesContainer) return;
|
|
195
197
|
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
const colorized = () => buildColorizedSkillList(
|
|
199
|
+
rawSkillNames,
|
|
200
|
+
store.hiddenSkillsByCwd.get(cwd) ?? new Set<string>(),
|
|
201
|
+
store.getTheme?.() ?? store.theme,
|
|
202
|
+
);
|
|
203
|
+
const collapsedText = buildColorizedSkillList(rawSkillNames, new Set(), null);
|
|
204
|
+
// Pi clears and rebuilds this container, including during /reload.
|
|
205
|
+
for (const entry of this.loadedResourcesContainer.children) {
|
|
206
|
+
const child = entry as ExpandableTextLike | undefined;
|
|
207
|
+
if (child && typeof child.build === "function") {
|
|
208
|
+
// Pi 1.x closes over expansion state in ThemedText.build instead of
|
|
209
|
+
// exposing getCollapsedText. Decorate only the compact skill list;
|
|
210
|
+
// leave expanded paths and other sections to Pi. Rebuild colors on
|
|
211
|
+
// invalidation so theme changes never retain captured ANSI strings.
|
|
212
|
+
const build = child.build;
|
|
213
|
+
if (!stripVTControlCharacters(build.call(child)).startsWith("[Skills]\n")) continue;
|
|
214
|
+
child.build = () => {
|
|
215
|
+
const text = build.call(child);
|
|
216
|
+
return stripVTControlCharacters(text) === collapsedText ? colorized() : text;
|
|
217
|
+
};
|
|
218
|
+
child.invalidate?.();
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
200
221
|
if (!child || typeof child.getCollapsedText !== "function") continue;
|
|
201
222
|
if (!child.getCollapsedText().includes("[Skills]")) continue;
|
|
202
223
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
child.
|
|
224
|
+
// Retain the older Pi component contract without collapsing an
|
|
225
|
+
// initially expanded resource listing.
|
|
226
|
+
child.getCollapsedText = colorized;
|
|
227
|
+
if (!this.getStartupExpansionState?.()) child.setText(colorized());
|
|
206
228
|
break;
|
|
207
229
|
}
|
|
208
230
|
};
|
package/src/install-telemetry.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
2
|
import { join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { reportInstallTelemetry as report } from "@mocito/install-telemetry";
|
|
5
5
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
|
|
7
7
|
const PACKAGE_NAME = "pi-skillful";
|
|
8
|
-
const
|
|
9
|
-
const INSTALL_TELEMETRY_TIMEOUT_MS = 5000;
|
|
8
|
+
const INSTALL_TELEMETRY_ENDPOINT = "https://mocito.dev/api/report-install";
|
|
10
9
|
const CI_ENVIRONMENT_VARIABLES = [
|
|
11
10
|
"APPVEYOR",
|
|
12
11
|
"BITBUCKET_BUILD_NUMBER",
|
|
@@ -24,10 +23,6 @@ const CI_ENVIRONMENT_VARIABLES = [
|
|
|
24
23
|
"VERCEL",
|
|
25
24
|
];
|
|
26
25
|
|
|
27
|
-
interface InstallTelemetryState {
|
|
28
|
-
lastReportedVersion?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
26
|
interface PiSettingsDocument {
|
|
32
27
|
enableInstallTelemetry?: unknown;
|
|
33
28
|
}
|
|
@@ -51,18 +46,15 @@ function isPresentEnvFlag(value: string | undefined): boolean {
|
|
|
51
46
|
return normalized !== "0" && normalized !== "false" && normalized !== "no";
|
|
52
47
|
}
|
|
53
48
|
|
|
54
|
-
function
|
|
55
|
-
if (isTruthyEnvFlag(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
function isInstallTelemetryEnabled(): boolean {
|
|
60
|
-
if (isCiEnvironment()) return false;
|
|
61
|
-
if (isTruthyEnvFlag(process.env.PI_OFFLINE)) return false;
|
|
62
|
-
if (process.env.PI_TELEMETRY !== undefined) return isTruthyEnvFlag(process.env.PI_TELEMETRY);
|
|
49
|
+
export function isInstallTelemetryEnabled(env: NodeJS.ProcessEnv = process.env, settingsPath = join(getAgentDir(), "settings.json")): boolean {
|
|
50
|
+
if (isTruthyEnvFlag(env.CI)) return false;
|
|
51
|
+
if (CI_ENVIRONMENT_VARIABLES.some((name) => isPresentEnvFlag(env[name]))) return false;
|
|
52
|
+
if (isTruthyEnvFlag(env.PI_OFFLINE)) return false;
|
|
63
53
|
|
|
64
|
-
const settings = readJsonFile(
|
|
65
|
-
|
|
54
|
+
const settings = readJsonFile(settingsPath) as PiSettingsDocument;
|
|
55
|
+
if (settings.enableInstallTelemetry === false) return false;
|
|
56
|
+
if (env.PI_TELEMETRY !== undefined) return isTruthyEnvFlag(env.PI_TELEMETRY);
|
|
57
|
+
return true;
|
|
66
58
|
}
|
|
67
59
|
|
|
68
60
|
function getPackageVersion(): string {
|
|
@@ -70,35 +62,16 @@ function getPackageVersion(): string {
|
|
|
70
62
|
return typeof packageJson.version === "string" && packageJson.version.length > 0 ? packageJson.version : "0.0.0";
|
|
71
63
|
}
|
|
72
64
|
|
|
73
|
-
function
|
|
74
|
-
const runtimeVersions = process.versions as NodeJS.ProcessVersions & { bun?: string };
|
|
75
|
-
const runtime = runtimeVersions.bun ? `bun/${runtimeVersions.bun}` : `node/${process.version}`;
|
|
76
|
-
return `${PACKAGE_NAME}/${version} (${process.platform}; ${runtime}; ${process.arch})`;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function reportInstallTelemetryAsync(): Promise<void> {
|
|
65
|
+
export function reportInstallTelemetry(): void {
|
|
80
66
|
try {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
await mkdir(extensionsDir, { recursive: true });
|
|
90
|
-
await writeFile(statePath, `${JSON.stringify({ lastReportedVersion: version }, null, 2)}\n`, "utf8");
|
|
91
|
-
|
|
92
|
-
const params = new URLSearchParams({ tool: PACKAGE_NAME, version });
|
|
93
|
-
await fetch(`${INSTALL_TELEMETRY_URL}?${params.toString()}`, {
|
|
94
|
-
headers: { "User-Agent": getInstallTelemetryUserAgent(version) },
|
|
95
|
-
signal: AbortSignal.timeout(INSTALL_TELEMETRY_TIMEOUT_MS),
|
|
96
|
-
});
|
|
67
|
+
void report({
|
|
68
|
+
endpoint: INSTALL_TELEMETRY_ENDPOINT,
|
|
69
|
+
tool: PACKAGE_NAME,
|
|
70
|
+
version: getPackageVersion(),
|
|
71
|
+
statePath: join(getAgentDir(), "extensions", "skillful-install.json"),
|
|
72
|
+
enabled: isInstallTelemetryEnabled(),
|
|
73
|
+
}).catch(() => undefined);
|
|
97
74
|
} catch {
|
|
98
|
-
// Best-effort telemetry: ignore
|
|
75
|
+
// Best-effort telemetry: ignore local policy and filesystem failures.
|
|
99
76
|
}
|
|
100
77
|
}
|
|
101
|
-
|
|
102
|
-
export function reportInstallTelemetry(): void {
|
|
103
|
-
void reportInstallTelemetryAsync();
|
|
104
|
-
}
|
package/src/skill-prompt.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { formatSkillsForPrompt, type Skill } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
|
|
3
|
-
export const SKILLS_SECTION_PATTERN = /\n\nThe following skills provide specialized instructions for specific tasks\.[\s\S]*?<\/available_skills>/;
|
|
4
|
-
|
|
5
|
-
export function replaceSkillsSection(systemPrompt: string, skills: Skill[]): string | undefined {
|
|
6
|
-
const next = systemPrompt.replace(SKILLS_SECTION_PATTERN, formatSkillsForPrompt(skills));
|
|
7
|
-
return next === systemPrompt ? undefined : next;
|
|
8
|
-
}
|