gentle-pi 0.3.9 → 0.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/README.md +11 -0
- package/extensions/gentle-ai.ts +295 -28
- package/extensions/skill-registry.ts +83 -10
- package/extensions/startup-banner.ts +189 -25
- package/package.json +1 -1
- package/scripts/verify-package-files.mjs +1 -0
- package/skills/comment-writer/SKILL.md +1 -1
- package/tests/gentle-ai.test.ts +36 -0
- package/tests/runtime-harness.mjs +161 -5
- package/tests/skill-registry.test.ts +70 -0
|
@@ -3,6 +3,7 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
|
+
import { pathToFileURL } from "node:url";
|
|
6
7
|
import { __testing } from "../extensions/skill-registry.ts";
|
|
7
8
|
|
|
8
9
|
test("project skill dirs include supported workspace roots", () => {
|
|
@@ -13,6 +14,7 @@ test("project skill dirs include supported workspace roots", () => {
|
|
|
13
14
|
".opencode/skills",
|
|
14
15
|
".claude/skills",
|
|
15
16
|
".gemini/skills",
|
|
17
|
+
".trae/skills",
|
|
16
18
|
".cursor/skills",
|
|
17
19
|
".github/skills",
|
|
18
20
|
".codex/skills",
|
|
@@ -49,6 +51,17 @@ test("registry renders indexed skill paths instead of compact rules", () => {
|
|
|
49
51
|
assert.doesNotMatch(registry, /Rules:/);
|
|
50
52
|
});
|
|
51
53
|
|
|
54
|
+
test("frontmatter parser accepts CRLF line endings", () => {
|
|
55
|
+
const parsed = __testing.parseFrontmatter("---\r\nname: windows-skill\r\ndescription: >\r\n Trigger: Windows-authored skills.\r\n Preserve frontmatter metadata.\r\n---\r\n\r\n## Body\r\n");
|
|
56
|
+
|
|
57
|
+
assert.equal(parsed.name, "windows-skill");
|
|
58
|
+
assert.equal(
|
|
59
|
+
parsed.description,
|
|
60
|
+
"Trigger: Windows-authored skills. Preserve frontmatter metadata.",
|
|
61
|
+
);
|
|
62
|
+
assert.match(parsed.body, /## Body/);
|
|
63
|
+
});
|
|
64
|
+
|
|
52
65
|
test("frontmatter parser keeps full multiline descriptions", () => {
|
|
53
66
|
const parsed = __testing.parseFrontmatter(`---
|
|
54
67
|
name: ai-sdk-5
|
|
@@ -101,6 +114,26 @@ test("uniqueExistingDirs normalizes duplicates and ignores missing roots", async
|
|
|
101
114
|
);
|
|
102
115
|
});
|
|
103
116
|
|
|
117
|
+
test("skill registry watchers close on shutdown", async () => {
|
|
118
|
+
const root = join(tmpdir(), `gentle-pi-watchers-${Date.now()}`);
|
|
119
|
+
const skillPath = join(root, "skills", "docs", "SKILL.md");
|
|
120
|
+
mkdirSync(dirname(skillPath), { recursive: true });
|
|
121
|
+
writeFileSync(skillPath, "---\nname: docs\ndescription: Docs.\n---\n");
|
|
122
|
+
|
|
123
|
+
await __testing.startSkillRegistryWatcher(root, () => undefined);
|
|
124
|
+
const attempted = __testing.activeWatcherCount();
|
|
125
|
+
__testing.closeSkillRegistryWatchers();
|
|
126
|
+
assert.equal(__testing.activeWatcherCount(), 0);
|
|
127
|
+
|
|
128
|
+
await __testing.startSkillRegistryWatcher(root, () => undefined);
|
|
129
|
+
assert.equal(
|
|
130
|
+
__testing.activeWatcherCount(),
|
|
131
|
+
attempted,
|
|
132
|
+
"shutdown must clear watched cwd state so a later session can re-watch",
|
|
133
|
+
);
|
|
134
|
+
__testing.closeSkillRegistryWatchers();
|
|
135
|
+
});
|
|
136
|
+
|
|
104
137
|
test("startup skip honors no skill registry controls", () => {
|
|
105
138
|
const enabled = { getFlag: () => true };
|
|
106
139
|
const disabled = { getFlag: () => false };
|
|
@@ -115,6 +148,43 @@ test("startup skip honors no skill registry controls", () => {
|
|
|
115
148
|
assert.equal(__testing.shouldSkipSkillRegistryStartup(disabled, [], {}), false);
|
|
116
149
|
});
|
|
117
150
|
|
|
151
|
+
test("duplicate extension load is skipped only across different sources", () => {
|
|
152
|
+
const state = {};
|
|
153
|
+
|
|
154
|
+
assert.equal(
|
|
155
|
+
__testing.shouldSkipDuplicateExtensionLoad("file:///repo/extensions/skill-registry.ts?first", "/workspace", state),
|
|
156
|
+
false,
|
|
157
|
+
);
|
|
158
|
+
assert.equal(
|
|
159
|
+
__testing.shouldSkipDuplicateExtensionLoad("file:///repo/extensions/skill-registry.ts?second", "/workspace", state),
|
|
160
|
+
false,
|
|
161
|
+
);
|
|
162
|
+
assert.equal(
|
|
163
|
+
__testing.shouldSkipDuplicateExtensionLoad("file:///home/.pi/node_modules/gentle-pi/extensions/skill-registry.ts", "/workspace", state),
|
|
164
|
+
true,
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("project-local skill registry extension wins over installed package copy", () => {
|
|
169
|
+
const cwd = join(tmpdir(), `gentle-pi-local-extension-${Date.now()}`);
|
|
170
|
+
const localExtension = join(cwd, "extensions", "skill-registry.ts");
|
|
171
|
+
mkdirSync(dirname(localExtension), { recursive: true });
|
|
172
|
+
writeFileSync(localExtension, "");
|
|
173
|
+
|
|
174
|
+
assert.equal(
|
|
175
|
+
__testing.shouldSkipDuplicateExtensionLoad(
|
|
176
|
+
"file:///home/.pi/agent/npm/node_modules/gentle-pi/extensions/skill-registry.ts",
|
|
177
|
+
cwd,
|
|
178
|
+
{},
|
|
179
|
+
),
|
|
180
|
+
true,
|
|
181
|
+
);
|
|
182
|
+
assert.equal(
|
|
183
|
+
__testing.shouldSkipDuplicateExtensionLoad(pathToFileURL(localExtension).href, cwd, {}),
|
|
184
|
+
false,
|
|
185
|
+
);
|
|
186
|
+
});
|
|
187
|
+
|
|
118
188
|
test("scope and markdown cells are represented in registry", () => {
|
|
119
189
|
const cwd = join(tmpdir(), `gentle-pi-scope-${Date.now()}`);
|
|
120
190
|
const projectPath = join(cwd, "skills", "docs", "SKILL.md");
|