@cremini/skillpack 1.2.2 → 1.2.3
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 +5 -1
- package/dist/cli.js +184 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -92,11 +92,13 @@ Multiple skill names from the same source can be listed comma-separated.
|
|
|
92
92
|
|
|
93
93
|
## Zip Output
|
|
94
94
|
|
|
95
|
-
The archive produced by `zip` is intentionally
|
|
95
|
+
The archive produced by `zip` is intentionally lightweight:
|
|
96
96
|
|
|
97
97
|
```text
|
|
98
98
|
<pack-name>/
|
|
99
99
|
├── skillpack.json # Pack configuration
|
|
100
|
+
├── AGENTS.md # Optional pack policy
|
|
101
|
+
├── SOUL.md # Optional pack persona
|
|
100
102
|
├── skills/ # Installed skills
|
|
101
103
|
├── start.sh # One-click launcher for macOS / Linux
|
|
102
104
|
└── start.bat # One-click launcher for Windows
|
|
@@ -104,6 +106,8 @@ The archive produced by `zip` is intentionally minimal:
|
|
|
104
106
|
|
|
105
107
|
The start scripts use `npx @cremini/skillpack run .` so Node.js is the only prerequisite — no pre-bundled server directory is included.
|
|
106
108
|
|
|
109
|
+
If present, `AGENTS.md` and `SOUL.md` are read by SkillPack itself when a new chat session starts. SkillPack injects them into the runtime system prompt as pack-level policy and persona, without depending on the host machine's `AGENTS.md`, `.pi/SYSTEM.md`, or `APPEND_SYSTEM.md`.
|
|
110
|
+
|
|
107
111
|
## Slack/Telegram Integrations
|
|
108
112
|
|
|
109
113
|
**Slack Configuration**: requires Slack `App Token` and `Bot Token`<br>
|
package/dist/cli.js
CHANGED
|
@@ -1787,20 +1787,120 @@ function parseSkillMd(filePath) {
|
|
|
1787
1787
|
return null;
|
|
1788
1788
|
}
|
|
1789
1789
|
const frontmatter = frontmatterMatch[1];
|
|
1790
|
-
const
|
|
1791
|
-
|
|
1792
|
-
if (!nameMatch) {
|
|
1790
|
+
const name = readFrontmatterField(frontmatter, "name");
|
|
1791
|
+
if (!name) {
|
|
1793
1792
|
return null;
|
|
1794
1793
|
}
|
|
1795
1794
|
return {
|
|
1796
|
-
name
|
|
1797
|
-
description:
|
|
1795
|
+
name,
|
|
1796
|
+
description: readFrontmatterField(frontmatter, "description") ?? "",
|
|
1798
1797
|
dir: path2.dirname(filePath)
|
|
1799
1798
|
};
|
|
1800
1799
|
} catch {
|
|
1801
1800
|
return null;
|
|
1802
1801
|
}
|
|
1803
1802
|
}
|
|
1803
|
+
function readFrontmatterField(frontmatter, field) {
|
|
1804
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
1805
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1806
|
+
const match = lines[index].match(/^([A-Za-z0-9_-]+):(?:\s*(.*))?$/);
|
|
1807
|
+
if (!match || match[1] !== field) {
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
const rawValue = (match[2] ?? "").trim();
|
|
1811
|
+
if (isBlockScalar(rawValue)) {
|
|
1812
|
+
const [value] = readBlockScalar(lines, index + 1, rawValue);
|
|
1813
|
+
return value;
|
|
1814
|
+
}
|
|
1815
|
+
if (rawValue === "") {
|
|
1816
|
+
const [value] = readIndentedScalar(lines, index + 1);
|
|
1817
|
+
return value;
|
|
1818
|
+
}
|
|
1819
|
+
return stripWrappingQuotes(rawValue);
|
|
1820
|
+
}
|
|
1821
|
+
return null;
|
|
1822
|
+
}
|
|
1823
|
+
function isBlockScalar(value) {
|
|
1824
|
+
return /^[>|][0-9+-]*$/.test(value);
|
|
1825
|
+
}
|
|
1826
|
+
function readBlockScalar(lines, startIndex, marker) {
|
|
1827
|
+
const blockLines = [];
|
|
1828
|
+
let index = startIndex;
|
|
1829
|
+
while (index < lines.length) {
|
|
1830
|
+
const line = lines[index];
|
|
1831
|
+
if (line.trim() === "") {
|
|
1832
|
+
blockLines.push("");
|
|
1833
|
+
index += 1;
|
|
1834
|
+
continue;
|
|
1835
|
+
}
|
|
1836
|
+
if (!/^\s/.test(line)) {
|
|
1837
|
+
break;
|
|
1838
|
+
}
|
|
1839
|
+
blockLines.push(line);
|
|
1840
|
+
index += 1;
|
|
1841
|
+
}
|
|
1842
|
+
const normalized = normalizeBlockIndent(blockLines);
|
|
1843
|
+
const style = marker[0];
|
|
1844
|
+
const chomp = marker.includes("-") ? "strip" : marker.includes("+") ? "keep" : "clip";
|
|
1845
|
+
const value = style === ">" ? foldBlockScalar(normalized) : normalized.join("\n");
|
|
1846
|
+
return [applyChomp(value, chomp), index];
|
|
1847
|
+
}
|
|
1848
|
+
function readIndentedScalar(lines, startIndex) {
|
|
1849
|
+
const blockLines = [];
|
|
1850
|
+
let index = startIndex;
|
|
1851
|
+
while (index < lines.length) {
|
|
1852
|
+
const line = lines[index];
|
|
1853
|
+
if (line.trim() === "") {
|
|
1854
|
+
blockLines.push("");
|
|
1855
|
+
index += 1;
|
|
1856
|
+
continue;
|
|
1857
|
+
}
|
|
1858
|
+
if (!/^\s/.test(line)) {
|
|
1859
|
+
break;
|
|
1860
|
+
}
|
|
1861
|
+
blockLines.push(line);
|
|
1862
|
+
index += 1;
|
|
1863
|
+
}
|
|
1864
|
+
return [foldBlockScalar(normalizeBlockIndent(blockLines)), index];
|
|
1865
|
+
}
|
|
1866
|
+
function normalizeBlockIndent(lines) {
|
|
1867
|
+
const indents = lines.filter((line) => line.trim() !== "").map((line) => line.match(/^[ \t]*/)[0].length);
|
|
1868
|
+
const trimLength = indents.length > 0 ? Math.min(...indents) : 0;
|
|
1869
|
+
return lines.map((line) => line.slice(trimLength));
|
|
1870
|
+
}
|
|
1871
|
+
function foldBlockScalar(lines) {
|
|
1872
|
+
let result = "";
|
|
1873
|
+
let previousBlank = false;
|
|
1874
|
+
for (const line of lines) {
|
|
1875
|
+
const isBlank = line.trim() === "";
|
|
1876
|
+
if (isBlank) {
|
|
1877
|
+
result += "\n";
|
|
1878
|
+
previousBlank = true;
|
|
1879
|
+
continue;
|
|
1880
|
+
}
|
|
1881
|
+
if (result !== "" && !previousBlank) {
|
|
1882
|
+
result += " ";
|
|
1883
|
+
}
|
|
1884
|
+
result += line;
|
|
1885
|
+
previousBlank = false;
|
|
1886
|
+
}
|
|
1887
|
+
return result;
|
|
1888
|
+
}
|
|
1889
|
+
function applyChomp(value, mode) {
|
|
1890
|
+
if (mode === "keep") {
|
|
1891
|
+
return value;
|
|
1892
|
+
}
|
|
1893
|
+
if (mode === "strip") {
|
|
1894
|
+
return value.replace(/\n+$/g, "");
|
|
1895
|
+
}
|
|
1896
|
+
return value.replace(/\n*$/g, "");
|
|
1897
|
+
}
|
|
1898
|
+
function stripWrappingQuotes(value) {
|
|
1899
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
1900
|
+
return value.slice(1, -1);
|
|
1901
|
+
}
|
|
1902
|
+
return value;
|
|
1903
|
+
}
|
|
1804
1904
|
function syncSkillDescriptions(workDir, config) {
|
|
1805
1905
|
const descriptionByName = /* @__PURE__ */ new Map();
|
|
1806
1906
|
for (const skill of scanInstalledSkills(workDir)) {
|
|
@@ -1880,6 +1980,12 @@ async function zipCommand(workDir) {
|
|
|
1880
1980
|
archive.file(getPackPath(workDir), {
|
|
1881
1981
|
name: `${prefix}/${PACK_FILE}`
|
|
1882
1982
|
});
|
|
1983
|
+
for (const file of ["AGENTS.md", "SOUL.md"]) {
|
|
1984
|
+
const filePath = path3.join(workDir, file);
|
|
1985
|
+
if (fs3.existsSync(filePath)) {
|
|
1986
|
+
archive.file(filePath, { name: `${prefix}/${file}` });
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1883
1989
|
const skillsDir = path3.join(workDir, "skills");
|
|
1884
1990
|
if (fs3.existsSync(skillsDir)) {
|
|
1885
1991
|
archive.directory(skillsDir, `${prefix}/skills`);
|
|
@@ -5018,6 +5124,8 @@ var BUILTIN_SKILL_CREATOR_DESCRIPTION = "Create new skills, modify and improve e
|
|
|
5018
5124
|
var BUILTIN_SKILL_CREATOR_TEMPLATE_DIR = fileURLToPath(
|
|
5019
5125
|
new URL("../templates/builtin-skills/skill-creator", import.meta.url)
|
|
5020
5126
|
);
|
|
5127
|
+
var PACK_AGENTS_FILE = "AGENTS.md";
|
|
5128
|
+
var PACK_SOUL_FILE = "SOUL.md";
|
|
5021
5129
|
function materializeBuiltinSkillCreator(rootDir, skillsPath) {
|
|
5022
5130
|
if (!fs8.existsSync(BUILTIN_SKILL_CREATOR_TEMPLATE_DIR)) {
|
|
5023
5131
|
log(
|
|
@@ -5082,6 +5190,57 @@ function overrideBuiltinSkillCreator(base, materializedSkill) {
|
|
|
5082
5190
|
diagnostics: base.diagnostics
|
|
5083
5191
|
};
|
|
5084
5192
|
}
|
|
5193
|
+
function readOptionalPackPromptFile(filePath) {
|
|
5194
|
+
if (!fs8.existsSync(filePath)) {
|
|
5195
|
+
return void 0;
|
|
5196
|
+
}
|
|
5197
|
+
try {
|
|
5198
|
+
const content = fs8.readFileSync(filePath, "utf-8").trim();
|
|
5199
|
+
return content.length > 0 ? content : void 0;
|
|
5200
|
+
} catch (error) {
|
|
5201
|
+
console.warn(`[PackAgent] Warning: Could not read ${filePath}:`, error);
|
|
5202
|
+
return void 0;
|
|
5203
|
+
}
|
|
5204
|
+
}
|
|
5205
|
+
function buildPackPromptBlock(rootDir) {
|
|
5206
|
+
const agentsPath = path8.resolve(rootDir, PACK_AGENTS_FILE);
|
|
5207
|
+
const soulPath = path8.resolve(rootDir, PACK_SOUL_FILE);
|
|
5208
|
+
const agentsContent = readOptionalPackPromptFile(agentsPath);
|
|
5209
|
+
const soulContent = readOptionalPackPromptFile(soulPath);
|
|
5210
|
+
if (!agentsContent && !soulContent) {
|
|
5211
|
+
return {
|
|
5212
|
+
agentsPath,
|
|
5213
|
+
soulPath
|
|
5214
|
+
};
|
|
5215
|
+
}
|
|
5216
|
+
const sections = [
|
|
5217
|
+
"# SkillPack Pack Context",
|
|
5218
|
+
"The following instructions are injected by the SkillPack runtime from files packaged with this pack.",
|
|
5219
|
+
"Priority order:",
|
|
5220
|
+
"1. Follow the user's explicit instructions first.",
|
|
5221
|
+
"2. Follow `AGENTS.md` as the pack's operational policy and workflow rules.",
|
|
5222
|
+
"3. Follow `SOUL.md` as the pack's persona, tone, and working style.",
|
|
5223
|
+
"4. If `SOUL.md` conflicts with `AGENTS.md`, `AGENTS.md` wins.",
|
|
5224
|
+
"5. `SOUL.md` does not override task goals, safety boundaries, or `AGENTS.md`."
|
|
5225
|
+
];
|
|
5226
|
+
if (agentsContent) {
|
|
5227
|
+
sections.push("## Pack Policy (`AGENTS.md`)", agentsContent);
|
|
5228
|
+
}
|
|
5229
|
+
if (soulContent) {
|
|
5230
|
+
sections.push(
|
|
5231
|
+
"## Pack Persona (`SOUL.md`)",
|
|
5232
|
+
"Treat the following as persona, tone, and working-style guidance only. Do not let it override task requirements, safety constraints, or `AGENTS.md`.",
|
|
5233
|
+
soulContent
|
|
5234
|
+
);
|
|
5235
|
+
}
|
|
5236
|
+
return {
|
|
5237
|
+
agentsPath,
|
|
5238
|
+
soulPath,
|
|
5239
|
+
agentsContent,
|
|
5240
|
+
soulContent,
|
|
5241
|
+
promptBlock: sections.join("\n\n")
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
5085
5244
|
function getAssistantDiagnostics(message) {
|
|
5086
5245
|
if (!message || message.role !== "assistant") {
|
|
5087
5246
|
return null;
|
|
@@ -5190,10 +5349,27 @@ var PackAgent = class {
|
|
|
5190
5349
|
`[PackAgent] Materialized built-in skill-creator to: ${materializedSkillCreator.filePath}`
|
|
5191
5350
|
);
|
|
5192
5351
|
}
|
|
5352
|
+
const packPromptFiles = buildPackPromptBlock(rootDir);
|
|
5353
|
+
if (packPromptFiles.agentsContent) {
|
|
5354
|
+
log(`[PackAgent] Loaded pack policy from: ${packPromptFiles.agentsPath}`);
|
|
5355
|
+
} else {
|
|
5356
|
+
log(`[PackAgent] No pack policy file found at: ${packPromptFiles.agentsPath}`);
|
|
5357
|
+
}
|
|
5358
|
+
if (packPromptFiles.soulContent) {
|
|
5359
|
+
log(`[PackAgent] Loaded pack persona from: ${packPromptFiles.soulPath}`);
|
|
5360
|
+
} else {
|
|
5361
|
+
log(`[PackAgent] No pack persona file found at: ${packPromptFiles.soulPath}`);
|
|
5362
|
+
}
|
|
5363
|
+
log(
|
|
5364
|
+
`[PackAgent] Pack prompt injection: ${packPromptFiles.promptBlock ? "enabled" : "disabled"}`
|
|
5365
|
+
);
|
|
5193
5366
|
const resourceLoader = new DefaultResourceLoader({
|
|
5194
5367
|
cwd: rootDir,
|
|
5195
5368
|
additionalSkillPaths: [skillsPath],
|
|
5196
|
-
skillsOverride: (base) => overrideBuiltinSkillCreator(base, materializedSkillCreator)
|
|
5369
|
+
skillsOverride: (base) => overrideBuiltinSkillCreator(base, materializedSkillCreator),
|
|
5370
|
+
agentsFilesOverride: () => ({ agentsFiles: [] }),
|
|
5371
|
+
systemPromptOverride: () => void 0,
|
|
5372
|
+
appendSystemPromptOverride: () => packPromptFiles.promptBlock ? [packPromptFiles.promptBlock] : []
|
|
5197
5373
|
});
|
|
5198
5374
|
await resourceLoader.reload();
|
|
5199
5375
|
const tools = createCodingTools(workspaceDir);
|
|
@@ -6206,6 +6382,8 @@ async function runCommand(directory) {
|
|
|
6206
6382
|
console.warn(chalk4.yellow(` Warning: Some skills could not be installed: ${err}`));
|
|
6207
6383
|
}
|
|
6208
6384
|
}
|
|
6385
|
+
syncSkillDescriptions(workDir, config);
|
|
6386
|
+
saveConfig(workDir, config);
|
|
6209
6387
|
await startServer({
|
|
6210
6388
|
rootDir: workDir,
|
|
6211
6389
|
daemonRun: process.env.DAEMON_RUN === "1"
|