haac-aikit 0.14.1 → 0.15.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/catalog/skills/tier1/decide/SKILL.md +1 -1
- package/catalog/skills/tier1/directions/SKILL.md +1 -1
- package/catalog/skills/tier1/roadmap/SKILL.md +1 -1
- package/catalog/skills/tier1/test-driven-development/SKILL.md +1 -1
- package/dist/cli.mjs +75 -11
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: decide
|
|
3
|
-
description: Use when the user types "/decide <topic
|
|
3
|
+
description: "Use when the user types \"/decide <topic>\", \"use the decide skill\", or \"make me a decision page on X\" — a non-trivial choice with 2-4 viable options that needs tradeoffs visualized as one rich HTML page. Generates a self-contained file at `docs/decisions/YYYY-MM-DD-<slug>.html`. Opt-in: do not invoke proactively."
|
|
4
4
|
version: "1.0.0"
|
|
5
5
|
source: haac-aikit
|
|
6
6
|
license: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: directions
|
|
3
|
-
description: Use when the user types "/directions <surface
|
|
3
|
+
description: "Use when the user types \"/directions <surface>\", \"show me design directions for X\", or \"explore visual options for the empty state / hero / dashboard card\" — 2-4 rendered visual takes side-by-side on one self-contained HTML page with a light/dark toggle. Output: `docs/directions/YYYY-MM-DD-<slug>.html`. Opt-in only."
|
|
4
4
|
version: "1.0.0"
|
|
5
5
|
source: haac-aikit
|
|
6
6
|
license: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: roadmap
|
|
3
|
-
description: Use when the user types "/roadmap <feature
|
|
3
|
+
description: "Use when the user types \"/roadmap <feature>\", \"draw up a roadmap for X\", or \"give me a one-page implementation doc\" — the approach is already settled and they want milestones + data-flow diagram + mockups + key code + risks + open questions on one committed HTML page. Output: `docs/roadmaps/YYYY-MM-DD-<slug>.html`. Opt-in only."
|
|
4
4
|
version: "1.0.0"
|
|
5
5
|
source: haac-aikit
|
|
6
6
|
license: MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: test-driven-development
|
|
3
|
-
description: Use when implementing new behaviour, fixing a confirmed bug, or when the user says "TDD", "test-first", "write a test for X", or "add tests". Enforces red-green-refactor: every behaviour gets a failing test first, then the minimal code to pass, then refactor — so untested code never enters the codebase.
|
|
3
|
+
description: "Use when implementing new behaviour, fixing a confirmed bug, or when the user says \"TDD\", \"test-first\", \"write a test for X\", or \"add tests\". Enforces red-green-refactor: every behaviour gets a failing test first, then the minimal code to pass, then refactor — so untested code never enters the codebase."
|
|
4
4
|
version: "1.0.0"
|
|
5
5
|
source: obra/superpowers
|
|
6
6
|
license: MIT
|
package/dist/cli.mjs
CHANGED
|
@@ -1422,12 +1422,33 @@ function parseFrontmatter(content) {
|
|
|
1422
1422
|
inToolsList = true;
|
|
1423
1423
|
continue;
|
|
1424
1424
|
}
|
|
1425
|
-
frontmatter[key] = val;
|
|
1425
|
+
frontmatter[key] = unquoteYamlScalar(val);
|
|
1426
1426
|
}
|
|
1427
1427
|
const doc = { frontmatter, body: body.trimStart() };
|
|
1428
1428
|
if (toolsList) doc.toolsList = toolsList;
|
|
1429
1429
|
return doc;
|
|
1430
1430
|
}
|
|
1431
|
+
function unquoteYamlScalar(value) {
|
|
1432
|
+
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
1433
|
+
const inner = value.slice(1, -1);
|
|
1434
|
+
return inner.replace(/\\(.)/g, (_2, ch) => {
|
|
1435
|
+
switch (ch) {
|
|
1436
|
+
case "n":
|
|
1437
|
+
return "\n";
|
|
1438
|
+
case "t":
|
|
1439
|
+
return " ";
|
|
1440
|
+
case "r":
|
|
1441
|
+
return "\r";
|
|
1442
|
+
default:
|
|
1443
|
+
return ch;
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
1448
|
+
return value.slice(1, -1).replace(/''/g, "'");
|
|
1449
|
+
}
|
|
1450
|
+
return value;
|
|
1451
|
+
}
|
|
1431
1452
|
function skillToCursorMdc(skill) {
|
|
1432
1453
|
const description = skill.frontmatter["description"] ?? "";
|
|
1433
1454
|
const escaped = description.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
@@ -1442,9 +1463,10 @@ ${skill.body}
|
|
|
1442
1463
|
function skillToWindsurfRule(skill) {
|
|
1443
1464
|
const description = skill.frontmatter["description"] ?? "";
|
|
1444
1465
|
const name = skill.frontmatter["name"] ?? "(unnamed)";
|
|
1466
|
+
const escapedDesc = description.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1445
1467
|
const header = `---
|
|
1446
1468
|
trigger: model_decision
|
|
1447
|
-
description: ${
|
|
1469
|
+
description: "${escapedDesc}"
|
|
1448
1470
|
---
|
|
1449
1471
|
|
|
1450
1472
|
`;
|
|
@@ -1464,9 +1486,10 @@ _Truncated to fit Windsurf's 12k-char rule limit. Full skill: \`.claude/skills/$
|
|
|
1464
1486
|
}
|
|
1465
1487
|
function skillToCopilotInstruction(skill) {
|
|
1466
1488
|
const description = skill.frontmatter["description"] ?? "";
|
|
1489
|
+
const escapedDesc = description.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1467
1490
|
return `---
|
|
1468
1491
|
applyTo: '**'
|
|
1469
|
-
description: ${
|
|
1492
|
+
description: "${escapedDesc}"
|
|
1470
1493
|
---
|
|
1471
1494
|
|
|
1472
1495
|
${skill.body}
|
|
@@ -1492,8 +1515,9 @@ ${escaped}
|
|
|
1492
1515
|
function agentToCopilotAgent(agent) {
|
|
1493
1516
|
const name = agent.frontmatter["name"] ?? "";
|
|
1494
1517
|
const description = agent.frontmatter["description"] ?? "";
|
|
1518
|
+
const escapedDesc = description.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1495
1519
|
const model = agent.frontmatter["model"] ?? "";
|
|
1496
|
-
const fm = [`name: ${name}`, `description: ${
|
|
1520
|
+
const fm = [`name: ${name}`, `description: "${escapedDesc}"`];
|
|
1497
1521
|
if (model) fm.push(`model: ${model}`);
|
|
1498
1522
|
if (agent.toolsList && agent.toolsList.length > 0) {
|
|
1499
1523
|
fm.push(`tools:`);
|
|
@@ -1503,6 +1527,19 @@ function agentToCopilotAgent(agent) {
|
|
|
1503
1527
|
${fm.join("\n")}
|
|
1504
1528
|
---
|
|
1505
1529
|
|
|
1530
|
+
${agent.body}
|
|
1531
|
+
`;
|
|
1532
|
+
}
|
|
1533
|
+
function agentToCursorAgent(agent) {
|
|
1534
|
+
const name = agent.frontmatter["name"] ?? "";
|
|
1535
|
+
const description = agent.frontmatter["description"] ?? "";
|
|
1536
|
+
const escapedDesc = description.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1537
|
+
return `---
|
|
1538
|
+
name: ${name}
|
|
1539
|
+
description: "${escapedDesc}"
|
|
1540
|
+
model: inherit
|
|
1541
|
+
---
|
|
1542
|
+
|
|
1506
1543
|
${agent.body}
|
|
1507
1544
|
`;
|
|
1508
1545
|
}
|
|
@@ -1741,6 +1778,15 @@ async function runSync(argv) {
|
|
|
1741
1778
|
if (!name) continue;
|
|
1742
1779
|
results.push(safeWrite(`.cursor/rules/skill-${name}.mdc`, skillToCursorMdc(skill), { ...opts, useMarkers: false }));
|
|
1743
1780
|
}
|
|
1781
|
+
results.push(...syncSkills("tier1", opts, ".cursor/skills"));
|
|
1782
|
+
results.push(...syncSkills("tier2", opts, ".cursor/skills"));
|
|
1783
|
+
if (config.integrations.subagents) {
|
|
1784
|
+
for (const agent of allAgents) {
|
|
1785
|
+
const name = agent.frontmatter["name"];
|
|
1786
|
+
if (!name) continue;
|
|
1787
|
+
results.push(safeWrite(`.cursor/agents/${name}.md`, agentToCursorAgent(agent), { ...opts, useMarkers: false }));
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1744
1790
|
if (config.integrations.hooks) {
|
|
1745
1791
|
const srcDir = join2(CATALOG_ROOT, "hooks");
|
|
1746
1792
|
const destDir = ".cursor/hooks";
|
|
@@ -1758,6 +1804,8 @@ async function runSync(argv) {
|
|
|
1758
1804
|
}
|
|
1759
1805
|
if (config.tools.includes("windsurf")) {
|
|
1760
1806
|
results.push(safeWrite(".windsurf/rules/project.md", catalog.windsurfRules(), { ...opts, useMarkers: false }));
|
|
1807
|
+
results.push(...syncSkills("tier1", opts, ".windsurf/skills"));
|
|
1808
|
+
results.push(...syncSkills("tier2", opts, ".windsurf/skills"));
|
|
1761
1809
|
for (const skill of allSkills) {
|
|
1762
1810
|
const name = skill.frontmatter["name"];
|
|
1763
1811
|
if (!name) continue;
|
|
@@ -1771,6 +1819,8 @@ async function runSync(argv) {
|
|
|
1771
1819
|
if (!name) continue;
|
|
1772
1820
|
results.push(safeWrite(`.github/instructions/${name}.instructions.md`, skillToCopilotInstruction(skill), { ...opts, useMarkers: false }));
|
|
1773
1821
|
}
|
|
1822
|
+
results.push(...syncSkills("tier1", opts, ".github/skills"));
|
|
1823
|
+
results.push(...syncSkills("tier2", opts, ".github/skills"));
|
|
1774
1824
|
if (config.integrations.subagents) {
|
|
1775
1825
|
for (const agent of allAgents) {
|
|
1776
1826
|
const name = agent.frontmatter["name"];
|
|
@@ -1802,6 +1852,8 @@ async function runSync(argv) {
|
|
|
1802
1852
|
const subdir = htmlSkillNames.has(name) ? "html/" : "";
|
|
1803
1853
|
results.push(safeWrite(`.gemini/commands/${subdir}${name}.toml`, skillToGeminiCommand(skill), { ...opts, useMarkers: false }));
|
|
1804
1854
|
}
|
|
1855
|
+
results.push(...syncSkills("tier1", opts, ".gemini/skills"));
|
|
1856
|
+
results.push(...syncSkills("tier2", opts, ".gemini/skills"));
|
|
1805
1857
|
if (config.integrations.mcp) {
|
|
1806
1858
|
results.push(safeWrite(".gemini/settings.json", catalog.mcpJson(), { ...opts, useMarkers: false }));
|
|
1807
1859
|
}
|
|
@@ -1923,12 +1975,22 @@ function copyAction(srcPath, destPath, opts) {
|
|
|
1923
1975
|
}
|
|
1924
1976
|
return { path: destPath, action: existed ? "updated" : "created", src: srcPath };
|
|
1925
1977
|
}
|
|
1978
|
+
function assertSafeName(name, source) {
|
|
1979
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) {
|
|
1980
|
+
throw new Error(
|
|
1981
|
+
`aikit: refusing to sync '${source}' \u2014 name '${name}' contains characters outside [a-z0-9-]. This would either break the target tool's loader or (if it contains '/' or '..') escape the target directory. Rename the skill/agent so its frontmatter 'name:' matches /^[a-z0-9][a-z0-9-]*$/.`
|
|
1982
|
+
);
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1926
1985
|
function loadSkillsFromCatalog() {
|
|
1927
1986
|
const out = [];
|
|
1928
1987
|
for (const tier of ["tier1", "tier2"]) {
|
|
1929
|
-
for (const
|
|
1930
|
-
const content = readFileSync5(join2(skillFolder(tier,
|
|
1931
|
-
|
|
1988
|
+
for (const folder of listSkillFolders(tier)) {
|
|
1989
|
+
const content = readFileSync5(join2(skillFolder(tier, folder), "SKILL.md"), "utf8");
|
|
1990
|
+
const parsed = parseFrontmatter(content);
|
|
1991
|
+
const name = parsed.frontmatter["name"];
|
|
1992
|
+
if (name) assertSafeName(name, `catalog/skills/${tier}/${folder}/SKILL.md`);
|
|
1993
|
+
out.push(parsed);
|
|
1932
1994
|
}
|
|
1933
1995
|
}
|
|
1934
1996
|
return out;
|
|
@@ -1944,7 +2006,10 @@ function loadAgentsFromCatalog(config) {
|
|
|
1944
2006
|
const include = selection === "all" ? allNames : allNames.filter((a2) => selection.includes(a2));
|
|
1945
2007
|
for (const name of include) {
|
|
1946
2008
|
const content = readFileSync5(join2(dir, `${name}.md`), "utf8");
|
|
1947
|
-
|
|
2009
|
+
const parsed = parseFrontmatter(content);
|
|
2010
|
+
const fmName = parsed.frontmatter["name"];
|
|
2011
|
+
if (fmName) assertSafeName(fmName, `catalog/agents/${tier}/${name}.md`);
|
|
2012
|
+
out.push(parsed);
|
|
1948
2013
|
}
|
|
1949
2014
|
}
|
|
1950
2015
|
return out;
|
|
@@ -1954,8 +2019,7 @@ function listHookFiles() {
|
|
|
1954
2019
|
if (!existsSync5(dir)) return [];
|
|
1955
2020
|
return readdirSync2(dir).filter((f2) => f2.endsWith(".sh"));
|
|
1956
2021
|
}
|
|
1957
|
-
function syncSkills(tier, opts) {
|
|
1958
|
-
const destRoot = `.claude/skills`;
|
|
2022
|
+
function syncSkills(tier, opts, destRoot = ".claude/skills") {
|
|
1959
2023
|
const results = [];
|
|
1960
2024
|
for (const name of listSkillFolders(tier)) {
|
|
1961
2025
|
const srcRoot = skillFolder(tier, name);
|
|
@@ -3516,7 +3580,7 @@ function isInteractive() {
|
|
|
3516
3580
|
}
|
|
3517
3581
|
|
|
3518
3582
|
// src/cli.ts
|
|
3519
|
-
var VERSION = "0.
|
|
3583
|
+
var VERSION = "0.15.0";
|
|
3520
3584
|
var HELP = `
|
|
3521
3585
|
haac-aikit \u2014 the batteries-included AI-coding kit
|
|
3522
3586
|
|