@bensandee/tooling 0.7.3 → 0.8.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/dist/bin.mjs +113 -35
- package/package.json +2 -2
package/dist/bin.mjs
CHANGED
|
@@ -160,6 +160,32 @@ function detectProjectType(targetDir) {
|
|
|
160
160
|
if (pkg.exports || pkg.main || pkg.types || pkg.typings) return "library";
|
|
161
161
|
return "default";
|
|
162
162
|
}
|
|
163
|
+
const WEB_UI_DEPS = new Set([
|
|
164
|
+
"react",
|
|
165
|
+
"react-dom",
|
|
166
|
+
"vue",
|
|
167
|
+
"svelte",
|
|
168
|
+
"solid-js",
|
|
169
|
+
"next",
|
|
170
|
+
"nuxt",
|
|
171
|
+
"@angular/core",
|
|
172
|
+
"preact"
|
|
173
|
+
]);
|
|
174
|
+
/** Check whether a package.json depends on a web UI framework. */
|
|
175
|
+
function packageHasWebUIDeps(pkg) {
|
|
176
|
+
const deps = {
|
|
177
|
+
...pkg.dependencies,
|
|
178
|
+
...pkg.devDependencies
|
|
179
|
+
};
|
|
180
|
+
for (const dep of WEB_UI_DEPS) if (dep in deps) return true;
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
/** Check whether a package.json in the given directory depends on a web UI framework. */
|
|
184
|
+
function hasWebUIDeps(targetDir) {
|
|
185
|
+
const pkg = readPackageJson(targetDir);
|
|
186
|
+
if (!pkg) return false;
|
|
187
|
+
return packageHasWebUIDeps(pkg);
|
|
188
|
+
}
|
|
163
189
|
/** List packages in a monorepo's packages/ directory. */
|
|
164
190
|
function getMonorepoPackages(targetDir) {
|
|
165
191
|
const packagesDir = path.join(targetDir, "packages");
|
|
@@ -805,13 +831,12 @@ function generateMigratePrompt(results, config, detected) {
|
|
|
805
831
|
async function generateTsconfig(ctx) {
|
|
806
832
|
const filePath = "tsconfig.json";
|
|
807
833
|
const existing = ctx.read(filePath);
|
|
808
|
-
if (ctx.config.structure === "monorepo") return [generateMonorepoRootTsconfig(ctx
|
|
834
|
+
if (ctx.config.structure === "monorepo") return [generateMonorepoRootTsconfig(ctx), ...ctx.config.detectPackageTypes ? generateMonorepoPackageTsconfigs(ctx) : []];
|
|
809
835
|
const extendsValue = `@bensandee/config/tsconfig/${ctx.config.projectType}`;
|
|
810
836
|
if (!existing) {
|
|
811
837
|
const config = {
|
|
812
838
|
extends: extendsValue,
|
|
813
|
-
...ctx.exists("src") ? { include: ["src"] } : {}
|
|
814
|
-
exclude: ["node_modules", "dist"]
|
|
839
|
+
...ctx.exists("src") ? { include: ["src"] } : {}
|
|
815
840
|
};
|
|
816
841
|
ctx.write(filePath, JSON.stringify(config, null, 2) + "\n");
|
|
817
842
|
return [{
|
|
@@ -885,31 +910,18 @@ function mergeSingleTsconfig(ctx, filePath, extendsValue) {
|
|
|
885
910
|
description: changes.join(", ")
|
|
886
911
|
};
|
|
887
912
|
}
|
|
888
|
-
function generateMonorepoRootTsconfig(ctx
|
|
913
|
+
function generateMonorepoRootTsconfig(ctx) {
|
|
889
914
|
const filePath = "tsconfig.json";
|
|
890
|
-
if (
|
|
891
|
-
const parsed = parseTsconfig(existing);
|
|
892
|
-
if (!parsed.references) {
|
|
893
|
-
parsed.files = [];
|
|
894
|
-
parsed.references = [];
|
|
895
|
-
ctx.write(filePath, JSON.stringify(parsed, null, 2) + "\n");
|
|
896
|
-
return {
|
|
897
|
-
filePath,
|
|
898
|
-
action: "updated",
|
|
899
|
-
description: "Added project references structure for monorepo"
|
|
900
|
-
};
|
|
901
|
-
}
|
|
902
|
-
return {
|
|
903
|
-
filePath,
|
|
904
|
-
action: "skipped",
|
|
905
|
-
description: "Already has project references"
|
|
906
|
-
};
|
|
907
|
-
}
|
|
908
|
-
return {
|
|
915
|
+
if (!ctx.read(filePath)) return {
|
|
909
916
|
filePath,
|
|
910
917
|
action: "skipped",
|
|
911
918
|
description: "No tsconfig.json found"
|
|
912
919
|
};
|
|
920
|
+
return {
|
|
921
|
+
filePath,
|
|
922
|
+
action: "skipped",
|
|
923
|
+
description: "Root tsconfig left as-is"
|
|
924
|
+
};
|
|
913
925
|
}
|
|
914
926
|
function generateMonorepoPackageTsconfigs(ctx) {
|
|
915
927
|
const packages = getMonorepoPackages(ctx.targetDir);
|
|
@@ -969,8 +981,7 @@ function generateMonorepoPackageTsconfigs(ctx) {
|
|
|
969
981
|
} else {
|
|
970
982
|
const config = {
|
|
971
983
|
extends: extendsValue,
|
|
972
|
-
...ctx.exists(path.join(relDir, "src")) ? { include: ["src"] } : {}
|
|
973
|
-
exclude: ["node_modules", "dist"]
|
|
984
|
+
...ctx.exists(path.join(relDir, "src")) ? { include: ["src"] } : {}
|
|
974
985
|
};
|
|
975
986
|
ctx.write(filePath, JSON.stringify(config, null, 2) + "\n");
|
|
976
987
|
results.push({
|
|
@@ -1472,7 +1483,9 @@ const ClaudeSettingsSchema = z.object({
|
|
|
1472
1483
|
allow: [],
|
|
1473
1484
|
deny: []
|
|
1474
1485
|
}),
|
|
1475
|
-
instructions: z.array(z.string()).default([])
|
|
1486
|
+
instructions: z.array(z.string()).default([]),
|
|
1487
|
+
enabledPlugins: z.record(z.string(), z.boolean()).default({}),
|
|
1488
|
+
extraKnownMarketplaces: z.record(z.string(), z.record(z.string(), z.unknown())).default({})
|
|
1476
1489
|
});
|
|
1477
1490
|
function parseClaudeSettings(raw) {
|
|
1478
1491
|
try {
|
|
@@ -1536,11 +1549,21 @@ function buildSettings(ctx) {
|
|
|
1536
1549
|
"Bash(test *)",
|
|
1537
1550
|
"Bash([ *)",
|
|
1538
1551
|
"Bash(find *)",
|
|
1552
|
+
"Bash(grep *)",
|
|
1539
1553
|
"Bash(which *)",
|
|
1540
1554
|
"Bash(node -e *)",
|
|
1541
1555
|
"Bash(node -p *)"
|
|
1542
1556
|
];
|
|
1543
1557
|
if (ctx.config.structure === "monorepo") allow.push(`Bash(${pm} --filter *)`, `Bash(${pm} -r *)`);
|
|
1558
|
+
const enabledPlugins = { "code-simplifier@claude-plugins-official": true };
|
|
1559
|
+
const extraKnownMarketplaces = {};
|
|
1560
|
+
if (ctx.config.structure === "monorepo" ? getMonorepoPackages(ctx.targetDir).some((p) => hasWebUIDeps(p.dir)) : ctx.packageJson ? packageHasWebUIDeps(ctx.packageJson) : false) {
|
|
1561
|
+
enabledPlugins["example-skills@anthropic-agent-skills"] = true;
|
|
1562
|
+
extraKnownMarketplaces["anthropic-agent-skills"] = { source: {
|
|
1563
|
+
source: "github",
|
|
1564
|
+
repo: "anthropics/skills"
|
|
1565
|
+
} };
|
|
1566
|
+
}
|
|
1544
1567
|
return {
|
|
1545
1568
|
permissions: {
|
|
1546
1569
|
allow,
|
|
@@ -1548,17 +1571,68 @@ function buildSettings(ctx) {
|
|
|
1548
1571
|
"Bash(npx *)",
|
|
1549
1572
|
"Bash(git push *)",
|
|
1550
1573
|
"Bash(git push)",
|
|
1551
|
-
"Bash(git
|
|
1552
|
-
"Bash(
|
|
1574
|
+
"Bash(git add *)",
|
|
1575
|
+
"Bash(git add)",
|
|
1576
|
+
"Bash(git commit *)",
|
|
1577
|
+
"Bash(git commit)",
|
|
1578
|
+
"Bash(git reset *)",
|
|
1579
|
+
"Bash(git merge *)",
|
|
1580
|
+
"Bash(git rebase *)",
|
|
1581
|
+
"Bash(git cherry-pick *)",
|
|
1582
|
+
"Bash(git checkout *)",
|
|
1583
|
+
"Bash(git switch *)",
|
|
1584
|
+
"Bash(git stash *)",
|
|
1585
|
+
"Bash(git tag *)",
|
|
1586
|
+
"Bash(git revert *)",
|
|
1587
|
+
"Bash(git clean *)",
|
|
1588
|
+
"Bash(git rm *)",
|
|
1589
|
+
"Bash(git mv *)",
|
|
1590
|
+
"Bash(rm -rf *)",
|
|
1591
|
+
"Bash(cat *.env)",
|
|
1592
|
+
"Bash(cat *.env.*)",
|
|
1593
|
+
"Bash(cat .env)",
|
|
1594
|
+
"Bash(cat .env.*)",
|
|
1595
|
+
"Bash(head *.env)",
|
|
1596
|
+
"Bash(head *.env.*)",
|
|
1597
|
+
"Bash(head .env)",
|
|
1598
|
+
"Bash(head .env.*)",
|
|
1599
|
+
"Bash(tail *.env)",
|
|
1600
|
+
"Bash(tail *.env.*)",
|
|
1601
|
+
"Bash(tail .env)",
|
|
1602
|
+
"Bash(tail .env.*)",
|
|
1603
|
+
"Bash(less *.env)",
|
|
1604
|
+
"Bash(less *.env.*)",
|
|
1605
|
+
"Bash(less .env)",
|
|
1606
|
+
"Bash(less .env.*)",
|
|
1607
|
+
"Bash(more *.env)",
|
|
1608
|
+
"Bash(more *.env.*)",
|
|
1609
|
+
"Bash(more .env)",
|
|
1610
|
+
"Bash(more .env.*)",
|
|
1611
|
+
"Bash(grep * .env)",
|
|
1612
|
+
"Bash(grep * .env.*)",
|
|
1613
|
+
"Read(.env)",
|
|
1614
|
+
"Read(.env.*)",
|
|
1615
|
+
"Read(*.env)",
|
|
1616
|
+
"Read(*.env.*)"
|
|
1553
1617
|
]
|
|
1554
1618
|
},
|
|
1555
1619
|
instructions: [
|
|
1556
1620
|
"Use pnpm, not npm/yarn/npx. Run binaries with `pnpm exec`.",
|
|
1557
1621
|
"No typecasts (as/any). Use zod schemas, type guards, or narrowing instead.",
|
|
1558
1622
|
"Fix lint violations instead of suppressing them. Only add disable comments when suppression is genuinely the best option."
|
|
1559
|
-
]
|
|
1623
|
+
],
|
|
1624
|
+
enabledPlugins,
|
|
1625
|
+
extraKnownMarketplaces
|
|
1560
1626
|
};
|
|
1561
1627
|
}
|
|
1628
|
+
/** Remove enabledPlugins/extraKnownMarketplaces when empty to keep the file clean. */
|
|
1629
|
+
function serializeSettings(settings) {
|
|
1630
|
+
const { enabledPlugins, extraKnownMarketplaces, ...rest } = settings;
|
|
1631
|
+
const out = { ...rest };
|
|
1632
|
+
if (Object.keys(enabledPlugins).length > 0) out["enabledPlugins"] = enabledPlugins;
|
|
1633
|
+
if (Object.keys(extraKnownMarketplaces).length > 0) out["extraKnownMarketplaces"] = extraKnownMarketplaces;
|
|
1634
|
+
return JSON.stringify(out, null, 2) + "\n";
|
|
1635
|
+
}
|
|
1562
1636
|
async function generateClaudeSettings(ctx) {
|
|
1563
1637
|
const filePath = ".claude/settings.json";
|
|
1564
1638
|
const existing = ctx.read(filePath);
|
|
@@ -1573,7 +1647,10 @@ async function generateClaudeSettings(ctx) {
|
|
|
1573
1647
|
const missingAllow = generated.permissions.allow.filter((rule) => !parsed.permissions.allow.includes(rule));
|
|
1574
1648
|
const missingDeny = generated.permissions.deny.filter((rule) => !parsed.permissions.deny.includes(rule));
|
|
1575
1649
|
const missingInstructions = generated.instructions.filter((inst) => !parsed.instructions.includes(inst));
|
|
1576
|
-
|
|
1650
|
+
const missingPlugins = Object.entries(generated.enabledPlugins).filter(([key]) => !(key in parsed.enabledPlugins));
|
|
1651
|
+
const missingMarketplaces = Object.entries(generated.extraKnownMarketplaces).filter(([key]) => !(key in parsed.extraKnownMarketplaces));
|
|
1652
|
+
const added = missingAllow.length + missingDeny.length + missingInstructions.length + missingPlugins.length + missingMarketplaces.length;
|
|
1653
|
+
if (added === 0) return {
|
|
1577
1654
|
filePath,
|
|
1578
1655
|
action: "skipped",
|
|
1579
1656
|
description: "Already has all rules and instructions"
|
|
@@ -1581,15 +1658,16 @@ async function generateClaudeSettings(ctx) {
|
|
|
1581
1658
|
parsed.permissions.allow = [...parsed.permissions.allow, ...missingAllow];
|
|
1582
1659
|
parsed.permissions.deny = [...parsed.permissions.deny, ...missingDeny];
|
|
1583
1660
|
parsed.instructions = [...parsed.instructions, ...missingInstructions];
|
|
1584
|
-
const
|
|
1585
|
-
|
|
1661
|
+
for (const [key, value] of missingPlugins) parsed.enabledPlugins[key] = value;
|
|
1662
|
+
for (const [key, value] of missingMarketplaces) parsed.extraKnownMarketplaces[key] = value;
|
|
1663
|
+
ctx.write(filePath, serializeSettings(parsed));
|
|
1586
1664
|
return {
|
|
1587
1665
|
filePath,
|
|
1588
1666
|
action: "updated",
|
|
1589
1667
|
description: `Added ${String(added)} rules/instructions`
|
|
1590
1668
|
};
|
|
1591
1669
|
}
|
|
1592
|
-
ctx.write(filePath,
|
|
1670
|
+
ctx.write(filePath, serializeSettings(generated));
|
|
1593
1671
|
return {
|
|
1594
1672
|
filePath,
|
|
1595
1673
|
action: "created",
|
|
@@ -2985,7 +3063,7 @@ function mergeGitHub(dryRun) {
|
|
|
2985
3063
|
const main = defineCommand({
|
|
2986
3064
|
meta: {
|
|
2987
3065
|
name: "tooling",
|
|
2988
|
-
version: "0.
|
|
3066
|
+
version: "0.8.0",
|
|
2989
3067
|
description: "Bootstrap and maintain standardized TypeScript project tooling"
|
|
2990
3068
|
},
|
|
2991
3069
|
subCommands: {
|
|
@@ -2998,7 +3076,7 @@ const main = defineCommand({
|
|
|
2998
3076
|
"release:merge": releaseMergeCommand
|
|
2999
3077
|
}
|
|
3000
3078
|
});
|
|
3001
|
-
console.log(`@bensandee/tooling v0.
|
|
3079
|
+
console.log(`@bensandee/tooling v0.8.0`);
|
|
3002
3080
|
runMain(main);
|
|
3003
3081
|
|
|
3004
3082
|
//#endregion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bensandee/tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "CLI tool to bootstrap and maintain standardized TypeScript project tooling",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tooling": "./dist/bin.mjs"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"tsdown": "0.20.3",
|
|
34
34
|
"typescript": "5.9.3",
|
|
35
35
|
"vitest": "4.0.18",
|
|
36
|
-
"@bensandee/config": "0.6.
|
|
36
|
+
"@bensandee/config": "0.6.5"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsdown",
|