@haus-tech/haus-workflow 0.30.0 → 0.31.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 +6 -0
- package/README.md +4 -0
- package/dist/cli.js +168 -8
- package/library/catalog/manifest.json +41 -9
- package/library/global/commands/haus-setup.md +29 -5
- package/library/global/skills/haus-workflow/SKILL.md +30 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.31.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.30.0...v0.31.0) (2026-06-23)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- opt-in UX for tiered skills + config scaffold ([#141](https://github.com/WeAreHausTech/haus-workflow/issues/141)) ([f60207b](https://github.com/WeAreHausTech/haus-workflow/commit/f60207b78af4651656d457c6f80665bd25deaa7f)), closes [#33](https://github.com/WeAreHausTech/haus-workflow/issues/33) [#33](https://github.com/WeAreHausTech/haus-workflow/issues/33)
|
|
8
|
+
|
|
3
9
|
## [0.30.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.29.0...v0.30.0) (2026-06-22)
|
|
4
10
|
|
|
5
11
|
### Features
|
package/README.md
CHANGED
|
@@ -87,6 +87,10 @@ On `haus apply` / `haus update`, items **removed from the catalog** or marked
|
|
|
87
87
|
lock hash; user-edited copies are kept. Approved items deselected via `--select` are
|
|
88
88
|
not pruned.
|
|
89
89
|
|
|
90
|
+
> **Upgrading from a pre-`0.30.0` CLI:** deprecated-skill pruning shipped in `0.30.0`.
|
|
91
|
+
> If an older CLI left deprecated skills on disk, upgrade first
|
|
92
|
+
> (`npm i -g @haus-tech/haus-workflow`) then run `haus update` to prune them.
|
|
93
|
+
|
|
90
94
|
## Internal docs
|
|
91
95
|
|
|
92
96
|
[docs/SUMMARY.md](docs/SUMMARY.md) — full documentation index
|
package/dist/cli.js
CHANGED
|
@@ -2398,6 +2398,13 @@ async function cleanupStaleCatalogItems(root, manifestById, dryRun) {
|
|
|
2398
2398
|
}
|
|
2399
2399
|
}
|
|
2400
2400
|
|
|
2401
|
+
// src/utils/args.ts
|
|
2402
|
+
function parseIdList(value) {
|
|
2403
|
+
if (!value) return [];
|
|
2404
|
+
const raw = Array.isArray(value) ? value : [value];
|
|
2405
|
+
return raw.flatMap((v) => v.split(",")).map((v) => v.trim()).filter((v) => v.length > 0);
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2401
2408
|
// src/commands/apply.ts
|
|
2402
2409
|
async function cacheHasItems() {
|
|
2403
2410
|
const data = await readJson(path13.join(getCacheDir(), "manifest.json"));
|
|
@@ -2414,6 +2421,34 @@ async function runApply(options) {
|
|
|
2414
2421
|
const root = process.cwd();
|
|
2415
2422
|
const isDryRun = Boolean(options.dryRun) && !options.write;
|
|
2416
2423
|
let selectedIds;
|
|
2424
|
+
const explicitIds = parseIdList(options.ids);
|
|
2425
|
+
if (explicitIds.length > 0) {
|
|
2426
|
+
if (options.select) {
|
|
2427
|
+
error("Use either --select (interactive) or --ids (explicit), not both");
|
|
2428
|
+
process.exitCode = 1;
|
|
2429
|
+
return;
|
|
2430
|
+
}
|
|
2431
|
+
const rec = await readJson(hausPath(root, "recommendation.json"));
|
|
2432
|
+
const recommended = rec?.recommended ?? [];
|
|
2433
|
+
const installableIds = new Set(installableRecommendedItems(recommended).map((i) => i.id));
|
|
2434
|
+
const configIds = new Set(recommended.filter((i) => i.install === false).map((i) => i.id));
|
|
2435
|
+
const skippedIds = new Set((rec?.skipped ?? []).map((s) => s.id));
|
|
2436
|
+
for (const id of explicitIds.filter((id2) => !installableIds.has(id2))) {
|
|
2437
|
+
if (configIds.has(id)) {
|
|
2438
|
+
warn(`--ids: "${id}" is a config item \u2014 install it with \`haus scaffold ${id}\`. Ignoring.`);
|
|
2439
|
+
} else if (skippedIds.has(id)) {
|
|
2440
|
+
warn(
|
|
2441
|
+
`--ids: "${id}" is not currently recommended (skipped) \u2014 add it with \`haus recommend --include ${id}\` first. Ignoring.`
|
|
2442
|
+
);
|
|
2443
|
+
} else {
|
|
2444
|
+
warn(
|
|
2445
|
+
`--ids: "${id}" is not in recommendation.json \u2014 run \`haus recommend\` (or \`--include ${id}\`) first. Ignoring.`
|
|
2446
|
+
);
|
|
2447
|
+
}
|
|
2448
|
+
}
|
|
2449
|
+
selectedIds = explicitIds.filter((id) => installableIds.has(id));
|
|
2450
|
+
log(`Applying ${selectedIds.length} explicitly selected catalog item(s).`);
|
|
2451
|
+
}
|
|
2417
2452
|
if (options.select) {
|
|
2418
2453
|
if (!process.stdin.isTTY) {
|
|
2419
2454
|
error("--select requires an interactive terminal (stdin is not a TTY)");
|
|
@@ -3546,7 +3581,7 @@ function mergeRecommendationWarnings(context) {
|
|
|
3546
3581
|
}
|
|
3547
3582
|
|
|
3548
3583
|
// src/recommender/recommend.ts
|
|
3549
|
-
async function recommend(root, context) {
|
|
3584
|
+
async function recommend(root, context, opts = {}) {
|
|
3550
3585
|
const items = await loadCatalog(root);
|
|
3551
3586
|
const sources = await readJson(
|
|
3552
3587
|
hausPath(root, "sources-report.json")
|
|
@@ -3719,6 +3754,8 @@ async function recommend(root, context) {
|
|
|
3719
3754
|
}
|
|
3720
3755
|
}
|
|
3721
3756
|
applyCoInstallSuppression(recommended, skipped);
|
|
3757
|
+
const includeWarnings = applyManualIncludes(opts.include ?? [], items, recommended, skipped);
|
|
3758
|
+
const optInEligible = buildOptInEligible(items, skipped);
|
|
3722
3759
|
recommended.sort((a, b) => a.id.localeCompare(b.id));
|
|
3723
3760
|
skipped.sort((a, b) => a.id.localeCompare(b.id));
|
|
3724
3761
|
const installableRecommended = recommended.filter((item) => item.install);
|
|
@@ -3729,7 +3766,8 @@ async function recommend(root, context) {
|
|
|
3729
3766
|
return {
|
|
3730
3767
|
recommended,
|
|
3731
3768
|
skipped,
|
|
3732
|
-
|
|
3769
|
+
optInEligible,
|
|
3770
|
+
warnings: [...mergeRecommendationWarnings(context), ...includeWarnings],
|
|
3733
3771
|
estimatedContextTokens,
|
|
3734
3772
|
selectedRules,
|
|
3735
3773
|
skippedRules,
|
|
@@ -3815,6 +3853,80 @@ function applyCoInstallSuppression(recommended, skipped) {
|
|
|
3815
3853
|
});
|
|
3816
3854
|
}
|
|
3817
3855
|
}
|
|
3856
|
+
var HARD_SKIP_CODES = /* @__PURE__ */ new Set([
|
|
3857
|
+
"unsupported-policy",
|
|
3858
|
+
"deprecated",
|
|
3859
|
+
"curated-not-approved",
|
|
3860
|
+
"curated-risk-blocked",
|
|
3861
|
+
"sensitive-policy",
|
|
3862
|
+
"source-trust",
|
|
3863
|
+
"source-approval"
|
|
3864
|
+
]);
|
|
3865
|
+
function applyManualIncludes(include, items, recommended, skipped) {
|
|
3866
|
+
const warnings = [];
|
|
3867
|
+
if (include.length === 0) return warnings;
|
|
3868
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
3869
|
+
const recommendedIds = new Set(recommended.map((r) => r.id));
|
|
3870
|
+
for (const id of include) {
|
|
3871
|
+
const item = byId.get(id);
|
|
3872
|
+
if (!item) {
|
|
3873
|
+
warnings.push(`--include: unknown catalog id "${id}" (not in manifest)`);
|
|
3874
|
+
continue;
|
|
3875
|
+
}
|
|
3876
|
+
if (recommendedIds.has(id)) continue;
|
|
3877
|
+
const skipIdx = skipped.findIndex((s) => s.id === id);
|
|
3878
|
+
const skipCode = skipIdx >= 0 ? skipped[skipIdx].skipReasons[0]?.code : void 0;
|
|
3879
|
+
if (skipCode && HARD_SKIP_CODES.has(skipCode)) {
|
|
3880
|
+
warnings.push(`--include: "${id}" cannot be force-installed (blocked by ${skipCode})`);
|
|
3881
|
+
continue;
|
|
3882
|
+
}
|
|
3883
|
+
if (skipCode === "requires-any-unsatisfied") {
|
|
3884
|
+
warnings.push(`--include: "${id}" added manually though its requiresAny gate is unsatisfied`);
|
|
3885
|
+
}
|
|
3886
|
+
if (skipIdx >= 0) skipped.splice(skipIdx, 1);
|
|
3887
|
+
const isConfig = item.type === "config";
|
|
3888
|
+
recommended.push({
|
|
3889
|
+
id: item.id,
|
|
3890
|
+
type: item.type,
|
|
3891
|
+
reason: "manually included",
|
|
3892
|
+
reasons: [
|
|
3893
|
+
{
|
|
3894
|
+
code: "manual-include",
|
|
3895
|
+
message: "manually included via --include",
|
|
3896
|
+
signal: "selection:manual"
|
|
3897
|
+
}
|
|
3898
|
+
],
|
|
3899
|
+
selectionMode: "manual",
|
|
3900
|
+
install: !isConfig,
|
|
3901
|
+
tags: item.tags,
|
|
3902
|
+
ecosystem: item.ecosystem,
|
|
3903
|
+
tokenEstimate: item.tokenEstimate
|
|
3904
|
+
});
|
|
3905
|
+
recommendedIds.add(id);
|
|
3906
|
+
}
|
|
3907
|
+
return warnings;
|
|
3908
|
+
}
|
|
3909
|
+
function buildOptInEligible(items, skipped) {
|
|
3910
|
+
const byId = new Map(items.map((i) => [i.id, i]));
|
|
3911
|
+
const eligible = [];
|
|
3912
|
+
for (const entry of skipped) {
|
|
3913
|
+
if (entry.skipReasons[0]?.code !== "requires-any-unsatisfied") continue;
|
|
3914
|
+
const item = byId.get(entry.id);
|
|
3915
|
+
if (!item?.optInTier || !item.optInGroup) continue;
|
|
3916
|
+
eligible.push({
|
|
3917
|
+
id: item.id,
|
|
3918
|
+
type: item.type,
|
|
3919
|
+
title: item.title,
|
|
3920
|
+
optInTier: item.optInTier,
|
|
3921
|
+
optInGroup: item.optInGroup,
|
|
3922
|
+
purpose: item.purpose,
|
|
3923
|
+
tokenEstimate: item.tokenEstimate,
|
|
3924
|
+
requires: entry.skipReasons[0]?.signal ?? describeRequiresAny(item.requiresAny ?? [])
|
|
3925
|
+
});
|
|
3926
|
+
}
|
|
3927
|
+
eligible.sort((a, b) => a.optInGroup.localeCompare(b.optInGroup) || a.id.localeCompare(b.id));
|
|
3928
|
+
return eligible;
|
|
3929
|
+
}
|
|
3818
3930
|
|
|
3819
3931
|
// src/commands/setup-core.ts
|
|
3820
3932
|
async function runSetupCore(root, opts) {
|
|
@@ -4275,7 +4387,8 @@ async function runInstall(options) {
|
|
|
4275
4387
|
async function runRecommend(options) {
|
|
4276
4388
|
const root = process.cwd();
|
|
4277
4389
|
const context = await readContextOrScan(root);
|
|
4278
|
-
const
|
|
4390
|
+
const include = parseIdList(options.include);
|
|
4391
|
+
const result = await recommend(root, context, { include });
|
|
4279
4392
|
await writeJson(hausPath(root, "recommendation.json"), result);
|
|
4280
4393
|
if (options.json) {
|
|
4281
4394
|
log(JSON.stringify(result, null, 2));
|
|
@@ -4284,6 +4397,12 @@ async function runRecommend(options) {
|
|
|
4284
4397
|
log("Haus recommendation ready");
|
|
4285
4398
|
log(`Recommended: ${result.recommended.length}`);
|
|
4286
4399
|
log(`Skipped: ${result.skipped.length}`);
|
|
4400
|
+
if (result.optInEligible && result.optInEligible.length > 0) {
|
|
4401
|
+
log(
|
|
4402
|
+
`Opt-in available: ${result.optInEligible.length} (add with \`haus recommend --include <id>\`)`
|
|
4403
|
+
);
|
|
4404
|
+
}
|
|
4405
|
+
for (const w of result.warnings.filter((w2) => w2.startsWith("--include:"))) log(w);
|
|
4287
4406
|
}
|
|
4288
4407
|
|
|
4289
4408
|
// src/commands/refresh.ts
|
|
@@ -4345,19 +4464,20 @@ async function scaffoldConfigItems(projectRoot, catalogRoot, items, opts = {}) {
|
|
|
4345
4464
|
if (stat.isFile()) {
|
|
4346
4465
|
const filename = path22.basename(sourcePath);
|
|
4347
4466
|
const dest = path22.join(projectRoot, filename);
|
|
4348
|
-
record(filename, await scaffoldFile(sourcePath, dest, item.id, opts));
|
|
4467
|
+
record(filename, await scaffoldFile(sourcePath, dest, item.id, projectRoot, opts));
|
|
4349
4468
|
} else if (stat.isDirectory()) {
|
|
4350
4469
|
const entries = await fs17.readdir(sourcePath);
|
|
4351
4470
|
for (const entry of entries) {
|
|
4352
4471
|
const src = path22.join(sourcePath, entry);
|
|
4353
4472
|
const dest = path22.join(projectRoot, entry);
|
|
4354
|
-
record(entry, await scaffoldFile(src, dest, item.id, opts));
|
|
4473
|
+
record(entry, await scaffoldFile(src, dest, item.id, projectRoot, opts));
|
|
4355
4474
|
}
|
|
4356
4475
|
}
|
|
4357
4476
|
}
|
|
4358
4477
|
return { scaffolded, skipped, skippedExisting };
|
|
4359
4478
|
}
|
|
4360
|
-
async function scaffoldFile(src, dest, itemId, opts) {
|
|
4479
|
+
async function scaffoldFile(src, dest, itemId, projectRoot, opts) {
|
|
4480
|
+
const destDisplay = path22.relative(projectRoot, dest) || path22.basename(dest);
|
|
4361
4481
|
const srcStat = await fs17.lstat(src).catch(() => null);
|
|
4362
4482
|
if (!srcStat) {
|
|
4363
4483
|
warn(`Skipping ${path22.basename(src)}: source not found`);
|
|
@@ -4369,7 +4489,7 @@ async function scaffoldFile(src, dest, itemId, opts) {
|
|
|
4369
4489
|
}
|
|
4370
4490
|
const exists = await fs17.pathExists(dest);
|
|
4371
4491
|
if (exists && !opts.force) {
|
|
4372
|
-
warn(`Skipping ${
|
|
4492
|
+
warn(`Skipping ${destDisplay}: already exists \u2014 re-run with --force to overwrite it`);
|
|
4373
4493
|
return "skipped-exists";
|
|
4374
4494
|
}
|
|
4375
4495
|
if (opts.dryRun) {
|
|
@@ -4885,6 +5005,39 @@ function auditForbiddenStacks(items) {
|
|
|
4885
5005
|
}
|
|
4886
5006
|
return failures;
|
|
4887
5007
|
}
|
|
5008
|
+
var OPT_IN_TIERS = /* @__PURE__ */ new Set(["workflow", "ops", "review", "design"]);
|
|
5009
|
+
function auditOptInMetadata(items) {
|
|
5010
|
+
const failures = [];
|
|
5011
|
+
for (const item of items) {
|
|
5012
|
+
const hasTier = item.optInTier !== void 0;
|
|
5013
|
+
const hasGroup = item.optInGroup !== void 0;
|
|
5014
|
+
if (hasTier !== hasGroup) {
|
|
5015
|
+
failures.push(`${item.id}: optInTier and optInGroup must be set together`);
|
|
5016
|
+
}
|
|
5017
|
+
if (hasTier) {
|
|
5018
|
+
if (!OPT_IN_TIERS.has(item.optInTier)) {
|
|
5019
|
+
failures.push(
|
|
5020
|
+
`${item.id}: invalid optInTier "${item.optInTier}" (expected one of ${[...OPT_IN_TIERS].join(", ")})`
|
|
5021
|
+
);
|
|
5022
|
+
}
|
|
5023
|
+
if (typeof item.optInGroup !== "string" || item.optInGroup.length === 0) {
|
|
5024
|
+
failures.push(`${item.id}: optInGroup must be a non-empty string`);
|
|
5025
|
+
}
|
|
5026
|
+
if (item.default === true) {
|
|
5027
|
+
failures.push(`${item.id}: opt-in items (optInTier set) must not be default:true`);
|
|
5028
|
+
}
|
|
5029
|
+
if (typeof item.purpose !== "string" || item.purpose.length === 0) {
|
|
5030
|
+
failures.push(`${item.id}: opt-in items (optInTier set) must have a non-empty purpose`);
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
5033
|
+
const requiresAny = Array.isArray(item.requiresAny) ? item.requiresAny : [];
|
|
5034
|
+
const roleOnly = requiresAny.length > 0 && requiresAny.every((clause) => clause && clause.role);
|
|
5035
|
+
if (item.default !== true && roleOnly && !hasTier) {
|
|
5036
|
+
failures.push(`${item.id}: role-gated opt-in item missing optInTier/optInGroup`);
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
return failures;
|
|
5040
|
+
}
|
|
4888
5041
|
function auditManifestStructure(manifestVersion, items) {
|
|
4889
5042
|
const failures = [];
|
|
4890
5043
|
const seenIds = /* @__PURE__ */ new Map();
|
|
@@ -5058,6 +5211,7 @@ function auditMarkdownContent(manifestDir, items) {
|
|
|
5058
5211
|
function validateCatalogData(manifestDir, manifestVersion, items) {
|
|
5059
5212
|
const failures = [
|
|
5060
5213
|
...auditManifestStructure(manifestVersion, items),
|
|
5214
|
+
...auditOptInMetadata(items),
|
|
5061
5215
|
...auditForbiddenStacks(items),
|
|
5062
5216
|
...auditShippedFiles(manifestDir, items),
|
|
5063
5217
|
...auditMarkdownContent(manifestDir, items),
|
|
@@ -5770,10 +5924,16 @@ function validateRuntimeNodeVersion() {
|
|
|
5770
5924
|
validateRuntimeNodeVersion();
|
|
5771
5925
|
program.name("haus").description("Haus AI workflow CLI").version(cliVersion());
|
|
5772
5926
|
program.command("scan").option("--json").action(runScan);
|
|
5773
|
-
program.command("recommend").option("--json").
|
|
5927
|
+
program.command("recommend").option("--json").option(
|
|
5928
|
+
"--include <ids...>",
|
|
5929
|
+
"Force opt-in catalog items into the recommendation as manual selections (space- or comma-separated ids)"
|
|
5930
|
+
).action(runRecommend);
|
|
5774
5931
|
program.command("setup-project").option("--json").action(runSetupProject);
|
|
5775
5932
|
program.command("doctor").option("--hooks", "Verify .claude/settings.json matches the hook contract").action(runDoctor);
|
|
5776
5933
|
program.command("apply").option("--dry-run").option("--write").option("--select", "Interactively select catalog items before applying").option(
|
|
5934
|
+
"--ids <ids...>",
|
|
5935
|
+
"Install exactly these recommended item ids non-interactively (skill backend; space- or comma-separated)"
|
|
5936
|
+
).option(
|
|
5777
5937
|
"--allow-empty-cache",
|
|
5778
5938
|
"Apply core files only when catalog cache is empty (skip catalog items without error)"
|
|
5779
5939
|
).option(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.13.1",
|
|
3
3
|
"items": [
|
|
4
4
|
{
|
|
5
5
|
"id": "haus.agentic-workflow-standard",
|
|
@@ -619,6 +619,8 @@
|
|
|
619
619
|
"originUrl": "https://github.com/affaan-m/ECC/blob/ec92b528471df708c2384ebbcc82b390b60f535a/skills/laravel-plugin-discovery/SKILL.md",
|
|
620
620
|
"pinnedRef": "ec92b528471df708c2384ebbcc82b390b60f535a",
|
|
621
621
|
"ecosystem": "laravel",
|
|
622
|
+
"optInTier": "ops",
|
|
623
|
+
"optInGroup": "Laravel plugin discovery",
|
|
622
624
|
"requiresAny": [
|
|
623
625
|
{
|
|
624
626
|
"role": "laravel-plugins"
|
|
@@ -753,6 +755,8 @@
|
|
|
753
755
|
"originUrl": "https://github.com/affaan-m/ECC/blob/5b173d2e6c11b976a0f13b2f59125e08956c1d47/agents/performance-optimizer.md",
|
|
754
756
|
"pinnedRef": "5b173d2e6c11b976a0f13b2f59125e08956c1d47",
|
|
755
757
|
"ecosystem": "ecc",
|
|
758
|
+
"optInTier": "review",
|
|
759
|
+
"optInGroup": "Performance review",
|
|
756
760
|
"default": false,
|
|
757
761
|
"requiresAny": [
|
|
758
762
|
{
|
|
@@ -986,6 +990,8 @@
|
|
|
986
990
|
"originUrl": "https://github.com/affaan-m/ECC/blob/5b173d2e6c11b976a0f13b2f59125e08956c1d47/.kiro/agents/refactor-cleaner.md",
|
|
987
991
|
"pinnedRef": "5b173d2e6c11b976a0f13b2f59125e08956c1d47",
|
|
988
992
|
"ecosystem": "ecc",
|
|
993
|
+
"optInTier": "review",
|
|
994
|
+
"optInGroup": "Refactor cleanup",
|
|
989
995
|
"default": false,
|
|
990
996
|
"requiresAny": [
|
|
991
997
|
{
|
|
@@ -1015,6 +1021,8 @@
|
|
|
1015
1021
|
"tags": ["agent", "review", "security"],
|
|
1016
1022
|
"tokenEstimate": 1369,
|
|
1017
1023
|
"ecosystem": "security",
|
|
1024
|
+
"optInTier": "review",
|
|
1025
|
+
"optInGroup": "Security review",
|
|
1018
1026
|
"originUrl": "https://github.com/affaan-m/ECC/blob/ec92b528471df708c2384ebbcc82b390b60f535a/agents/security-reviewer.md",
|
|
1019
1027
|
"requiresAny": [
|
|
1020
1028
|
{
|
|
@@ -1374,6 +1382,8 @@
|
|
|
1374
1382
|
"originUrl": "https://github.com/yeachan-heo/oh-my-claudecode/blob/50f6ff05eb5d9ebed66f05d8c4580c0b119f37af/agents/designer.md",
|
|
1375
1383
|
"pinnedRef": "50f6ff05eb5d9ebed66f05d8c4580c0b119f37af",
|
|
1376
1384
|
"ecosystem": "oh-my-claudecode",
|
|
1385
|
+
"optInTier": "design",
|
|
1386
|
+
"optInGroup": "UI design",
|
|
1377
1387
|
"default": false,
|
|
1378
1388
|
"requiresAny": [
|
|
1379
1389
|
{
|
|
@@ -1437,6 +1447,8 @@
|
|
|
1437
1447
|
"originUrl": "https://github.com/yeachan-heo/oh-my-claudecode/blob/50f6ff05eb5d9ebed66f05d8c4580c0b119f37af/agents/tracer.md",
|
|
1438
1448
|
"pinnedRef": "50f6ff05eb5d9ebed66f05d8c4580c0b119f37af",
|
|
1439
1449
|
"ecosystem": "oh-my-claudecode",
|
|
1450
|
+
"optInTier": "review",
|
|
1451
|
+
"optInGroup": "Incident tracing",
|
|
1440
1452
|
"default": false,
|
|
1441
1453
|
"requiresAny": [
|
|
1442
1454
|
{
|
|
@@ -1609,6 +1621,8 @@
|
|
|
1609
1621
|
"originUrl": "https://github.com/redis/agent-skills/blob/5ca2e1a2d82a768221e8f71a02e3ca095a37d38e/skills/redis-observability/SKILL.md",
|
|
1610
1622
|
"pinnedRef": "5ca2e1a2d82a768221e8f71a02e3ca095a37d38e",
|
|
1611
1623
|
"ecosystem": "redis",
|
|
1624
|
+
"optInTier": "ops",
|
|
1625
|
+
"optInGroup": "Redis security & observability",
|
|
1612
1626
|
"requiresAny": [
|
|
1613
1627
|
{
|
|
1614
1628
|
"role": "redis-ops"
|
|
@@ -1639,6 +1653,8 @@
|
|
|
1639
1653
|
"originUrl": "https://github.com/redis/agent-skills/blob/5ca2e1a2d82a768221e8f71a02e3ca095a37d38e/skills/redis-security/SKILL.md",
|
|
1640
1654
|
"pinnedRef": "5ca2e1a2d82a768221e8f71a02e3ca095a37d38e",
|
|
1641
1655
|
"ecosystem": "redis",
|
|
1656
|
+
"optInTier": "ops",
|
|
1657
|
+
"optInGroup": "Redis security & observability",
|
|
1642
1658
|
"requiresAny": [
|
|
1643
1659
|
{
|
|
1644
1660
|
"role": "redis-ops"
|
|
@@ -1714,7 +1730,7 @@
|
|
|
1714
1730
|
},
|
|
1715
1731
|
{
|
|
1716
1732
|
"id": "haus.sentry-sentry-nextjs-sdk",
|
|
1717
|
-
"version": "1.0.
|
|
1733
|
+
"version": "1.0.1",
|
|
1718
1734
|
"source": "curated",
|
|
1719
1735
|
"type": "skill",
|
|
1720
1736
|
"path": "skills/sentry/sentry-nextjs-sdk",
|
|
@@ -1724,7 +1740,7 @@
|
|
|
1724
1740
|
"whenNotToUse": "Do not use when a more specific skill or command applies.",
|
|
1725
1741
|
"tags": ["workflow", "sentry", "nextjs", "observability"],
|
|
1726
1742
|
"repoRoles": [],
|
|
1727
|
-
"tokenEstimate":
|
|
1743
|
+
"tokenEstimate": 4544,
|
|
1728
1744
|
"installMode": "copy-selected",
|
|
1729
1745
|
"default": false,
|
|
1730
1746
|
"reviewStatus": "approved",
|
|
@@ -1733,8 +1749,8 @@
|
|
|
1733
1749
|
"license": "MIT",
|
|
1734
1750
|
"licenseConfidence": "high",
|
|
1735
1751
|
"originSourceId": "getsentry-sentry-for-ai",
|
|
1736
|
-
"originUrl": "https://github.com/getsentry/sentry-for-ai/blob/
|
|
1737
|
-
"pinnedRef": "
|
|
1752
|
+
"originUrl": "https://github.com/getsentry/sentry-for-ai/blob/bd84576db74e3e1d13361c7eda3bb303ae017b90/skills/sentry-nextjs-sdk/SKILL.md",
|
|
1753
|
+
"pinnedRef": "bd84576db74e3e1d13361c7eda3bb303ae017b90",
|
|
1738
1754
|
"ecosystem": "sentry",
|
|
1739
1755
|
"requiresAny": [
|
|
1740
1756
|
{
|
|
@@ -1747,7 +1763,7 @@
|
|
|
1747
1763
|
},
|
|
1748
1764
|
{
|
|
1749
1765
|
"id": "haus.sentry-sentry-node-sdk",
|
|
1750
|
-
"version": "1.0.
|
|
1766
|
+
"version": "1.0.1",
|
|
1751
1767
|
"source": "curated",
|
|
1752
1768
|
"type": "skill",
|
|
1753
1769
|
"path": "skills/sentry/sentry-node-sdk",
|
|
@@ -1757,7 +1773,7 @@
|
|
|
1757
1773
|
"whenNotToUse": "Prefer sentry-nestjs-sdk for NestJS apps using @sentry/nestjs.",
|
|
1758
1774
|
"tags": ["workflow", "sentry", "backend", "observability"],
|
|
1759
1775
|
"repoRoles": [],
|
|
1760
|
-
"tokenEstimate":
|
|
1776
|
+
"tokenEstimate": 8087,
|
|
1761
1777
|
"installMode": "copy-selected",
|
|
1762
1778
|
"default": false,
|
|
1763
1779
|
"reviewStatus": "approved",
|
|
@@ -1766,8 +1782,8 @@
|
|
|
1766
1782
|
"license": "MIT",
|
|
1767
1783
|
"licenseConfidence": "high",
|
|
1768
1784
|
"originSourceId": "getsentry-sentry-for-ai",
|
|
1769
|
-
"originUrl": "https://github.com/getsentry/sentry-for-ai/blob/
|
|
1770
|
-
"pinnedRef": "
|
|
1785
|
+
"originUrl": "https://github.com/getsentry/sentry-for-ai/blob/bd84576db74e3e1d13361c7eda3bb303ae017b90/skills/sentry-node-sdk/SKILL.md",
|
|
1786
|
+
"pinnedRef": "bd84576db74e3e1d13361c7eda3bb303ae017b90",
|
|
1771
1787
|
"ecosystem": "sentry",
|
|
1772
1788
|
"requiresAny": [
|
|
1773
1789
|
{
|
|
@@ -2124,6 +2140,8 @@
|
|
|
2124
2140
|
"originSourceId": "superpowers-pcvelz",
|
|
2125
2141
|
"originUrl": "https://github.com/pcvelz/superpowers/tree/bd000c6ec1df53449a74397f55305b22802b9a2b/skills/finishing-a-development-branch",
|
|
2126
2142
|
"ecosystem": "superpowers",
|
|
2143
|
+
"optInTier": "workflow",
|
|
2144
|
+
"optInGroup": "Git worktrees & branch finishing",
|
|
2127
2145
|
"requiresAny": [
|
|
2128
2146
|
{
|
|
2129
2147
|
"role": "branch-completion"
|
|
@@ -2153,6 +2171,8 @@
|
|
|
2153
2171
|
"originSourceId": "superpowers-pcvelz",
|
|
2154
2172
|
"originUrl": "https://github.com/pcvelz/superpowers/tree/bd000c6ec1df53449a74397f55305b22802b9a2b/skills/receiving-code-review",
|
|
2155
2173
|
"ecosystem": "superpowers",
|
|
2174
|
+
"optInTier": "workflow",
|
|
2175
|
+
"optInGroup": "Code review workflow",
|
|
2156
2176
|
"requiresAny": [
|
|
2157
2177
|
{
|
|
2158
2178
|
"role": "code-review"
|
|
@@ -2182,6 +2202,8 @@
|
|
|
2182
2202
|
"originSourceId": "superpowers-pcvelz",
|
|
2183
2203
|
"originUrl": "https://github.com/pcvelz/superpowers/tree/bd000c6ec1df53449a74397f55305b22802b9a2b/skills/requesting-code-review",
|
|
2184
2204
|
"ecosystem": "superpowers",
|
|
2205
|
+
"optInTier": "workflow",
|
|
2206
|
+
"optInGroup": "Code review workflow",
|
|
2185
2207
|
"requiresAny": [
|
|
2186
2208
|
{
|
|
2187
2209
|
"role": "code-review"
|
|
@@ -2211,6 +2233,8 @@
|
|
|
2211
2233
|
"originSourceId": "superpowers-pcvelz",
|
|
2212
2234
|
"originUrl": "https://github.com/pcvelz/superpowers/tree/bd000c6ec1df53449a74397f55305b22802b9a2b/skills/specifying-gates",
|
|
2213
2235
|
"ecosystem": "superpowers",
|
|
2236
|
+
"optInTier": "workflow",
|
|
2237
|
+
"optInGroup": "Quality gates",
|
|
2214
2238
|
"requiresAny": [
|
|
2215
2239
|
{
|
|
2216
2240
|
"role": "user-gate"
|
|
@@ -2240,6 +2264,8 @@
|
|
|
2240
2264
|
"originSourceId": "superpowers-pcvelz",
|
|
2241
2265
|
"originUrl": "https://github.com/pcvelz/superpowers/tree/48f5123e18a1a92dfba962da8dc2509391c821ee/skills/subagent-driven-development",
|
|
2242
2266
|
"ecosystem": "superpowers",
|
|
2267
|
+
"optInTier": "workflow",
|
|
2268
|
+
"optInGroup": "Subagent-driven development",
|
|
2243
2269
|
"pinnedRef": "48f5123e18a1a92dfba962da8dc2509391c821ee",
|
|
2244
2270
|
"requiresAny": [
|
|
2245
2271
|
{
|
|
@@ -2294,6 +2320,8 @@
|
|
|
2294
2320
|
"originSourceId": "superpowers-pcvelz",
|
|
2295
2321
|
"originUrl": "https://github.com/pcvelz/superpowers/tree/bd000c6ec1df53449a74397f55305b22802b9a2b/skills/test-driven-development",
|
|
2296
2322
|
"ecosystem": "superpowers",
|
|
2323
|
+
"optInTier": "workflow",
|
|
2324
|
+
"optInGroup": "TDD workflow",
|
|
2297
2325
|
"requiresAny": [
|
|
2298
2326
|
{
|
|
2299
2327
|
"role": "tdd-workflow"
|
|
@@ -2323,6 +2351,8 @@
|
|
|
2323
2351
|
"originSourceId": "superpowers-pcvelz",
|
|
2324
2352
|
"originUrl": "https://github.com/pcvelz/superpowers/tree/20c0b1ed71cb073c847432fe01379f48c5278a5f/skills/using-git-worktrees",
|
|
2325
2353
|
"ecosystem": "superpowers",
|
|
2354
|
+
"optInTier": "workflow",
|
|
2355
|
+
"optInGroup": "Git worktrees & branch finishing",
|
|
2326
2356
|
"pinnedRef": "20c0b1ed71cb073c847432fe01379f48c5278a5f",
|
|
2327
2357
|
"requiresAny": [
|
|
2328
2358
|
{
|
|
@@ -2427,6 +2457,8 @@
|
|
|
2427
2457
|
"originSourceId": "superpowers-pcvelz",
|
|
2428
2458
|
"originUrl": "https://github.com/pcvelz/superpowers/tree/bd000c6ec1df53449a74397f55305b22802b9a2b/skills/writing-skills",
|
|
2429
2459
|
"ecosystem": "superpowers",
|
|
2460
|
+
"optInTier": "workflow",
|
|
2461
|
+
"optInGroup": "Skill authoring",
|
|
2430
2462
|
"requiresAny": [
|
|
2431
2463
|
{
|
|
2432
2464
|
"role": "skill-authoring"
|
|
@@ -31,13 +31,37 @@ Do this in order:
|
|
|
31
31
|
deep read discovered. You MUST run this before the next apply — `haus apply`
|
|
32
32
|
does not re-calculate recommendations on its own.
|
|
33
33
|
|
|
34
|
-
5. **
|
|
35
|
-
|
|
34
|
+
5. **Offer optional helpers.** Read `.haus-workflow/recommendation.json` yourself
|
|
35
|
+
(never show the JSON). Two kinds of optional extras may be listed; offer only
|
|
36
|
+
the ones present, all **unchecked** by default — these are opt-in:
|
|
37
|
+
- **Optional skills & agents** (`optInEligible[]`): workflow/ops/review/design
|
|
38
|
+
helpers that don't auto-install for this stack. Group them by their
|
|
39
|
+
`optInGroup` label and present one `AskUserQuestion` option per group
|
|
40
|
+
(use each item's `purpose` and `tokenEstimate` to describe it in plain
|
|
41
|
+
language, e.g. "Code review workflow — request/receive structured reviews").
|
|
42
|
+
Collect the item ids from **all** the groups the user picked into one list
|
|
43
|
+
and run a **single** `haus recommend --include <id> <id> …` (one command with
|
|
44
|
+
every chosen id). Don't run it once per group — each run rewrites
|
|
45
|
+
`recommendation.json`, so repeated calls would drop the earlier includes.
|
|
46
|
+
- **Project config** (entries in `recommended[]` with `install: false`, e.g.
|
|
47
|
+
Haus ESLint / Prettier baselines): offer each as a plain choice ("Add the
|
|
48
|
+
Haus ESLint baseline"). On confirm, run `haus scaffold <id>`. `haus scaffold`
|
|
49
|
+
**preserves existing config files by default** and tells you when a file
|
|
50
|
+
already exists — only re-run with `haus scaffold <id> --force` if the user
|
|
51
|
+
explicitly chooses to **replace** their current config.
|
|
36
52
|
|
|
37
|
-
|
|
53
|
+
If neither list has anything, skip this step silently — don't ask an empty
|
|
54
|
+
question.
|
|
55
|
+
|
|
56
|
+
6. **Apply the rest.** Run `haus apply --write` again. It only writes what changed,
|
|
57
|
+
so this adds the newly-matched helpers from step 4 plus any opt-ins from step 5.
|
|
58
|
+
|
|
59
|
+
7. **Confirm.** End with one plain-language line, for example:
|
|
38
60
|
"✅ Your project is configured — I wrote your project docs, added N guardrails
|
|
39
|
-
and M coding helpers (K matched after reading your code in depth)
|
|
40
|
-
`/haus-doctor` any time to
|
|
61
|
+
and M coding helpers (K matched after reading your code in depth), plus the J
|
|
62
|
+
optional helpers you chose: <name them>. Run `/haus-doctor` any time to
|
|
63
|
+
re-check." Fill the numbers from the apply output, and name the opt-ins the
|
|
64
|
+
user picked in step 5 (omit that clause if they chose none).
|
|
41
65
|
|
|
42
66
|
If anything fails, explain what happened in plain language and what you'll try
|
|
43
67
|
next — don't dump raw errors.
|
|
@@ -26,6 +26,7 @@ The short legacy aliases still work but the names below are canonical.
|
|
|
26
26
|
| `project:init` (`setup`, `init`) | _Setup procedure below_ | project | First-time setup of an **existing** repo: adds AI skills, commands, workflow + project docs |
|
|
27
27
|
| `project:clone [name]` (`clone`) | _Clone procedure below_ | project | No name: clone a **workspace**'s repos from `repos.manifest.json`. With a `name`: find & clone one repo by name from GitHub |
|
|
28
28
|
| `project:cloneandsetup [name]` (`cloneandsetup`) | _Clone & setup procedure below_ | project | Run `project:clone`, then set up each repo for local dev — deps, databases, cross-repo links, and env — via each repo's `.haus-workflow/localdev.yml` (+ the workspace's order/links/env) |
|
|
29
|
+
| `project:add-skills` (`add-skills`, `opt-in`) | _Add-skills procedure below_ | project | Add optional skills, agents, or config (ESLint/Prettier) to an already-set-up project without re-running full setup |
|
|
29
30
|
| `project:refresh` (`apply`, `refresh`, `claude-md`, `regenerate`) | `haus apply --write` | project | Re-run setup / refresh `.claude/` context + regenerate root `CLAUDE.md` import block |
|
|
30
31
|
| `project:doctor` (`doctor`, `check`) | `haus doctor` | project | Check for install drift |
|
|
31
32
|
| `update` (`upgrade`) | `haus update` | global | Update npm package + catalog + `~/.claude/` (also refreshes this project) |
|
|
@@ -51,6 +52,8 @@ Options:
|
|
|
51
52
|
(no name: clone a workspace from repos.manifest.json; with a name: find & clone one repo by name from GitHub)
|
|
52
53
|
5. [project] project:cloneandsetup [name] — clone repos, then set them up for local dev
|
|
53
54
|
(project:clone, then per-repo deps + databases + cross-repo links + env from localdev.yml)
|
|
55
|
+
6. [project] project:add-skills — add optional skills, agents, or config to this project
|
|
56
|
+
(offers opt-in helpers matching your stack that aren't installed yet — no full re-setup)
|
|
54
57
|
```
|
|
55
58
|
|
|
56
59
|
Map the user's selection to the command from the alias table, then continue to Step 2.
|
|
@@ -65,6 +68,8 @@ Run the mapped command via Bash. Quote the exact command you are running before
|
|
|
65
68
|
|
|
66
69
|
**Exception — `project:cloneandsetup` (`cloneandsetup`):** clone followed by a per-repo setup pass, with confirmations. Skip to **Clone & setup (`project:cloneandsetup`)** under Step 3 and follow it.
|
|
67
70
|
|
|
71
|
+
**Exception — `project:add-skills` (`add-skills` / `opt-in`):** a short scan → recommend → choose → apply procedure. Skip to **Add optional skills (`project:add-skills`)** under Step 3 and follow it.
|
|
72
|
+
|
|
68
73
|
## Step 3 — Post-run steps
|
|
69
74
|
|
|
70
75
|
After the command completes, follow the relevant post-run steps below.
|
|
@@ -82,6 +87,31 @@ After the command completes, follow the relevant post-run steps below.
|
|
|
82
87
|
|
|
83
88
|
1. Open and follow `~/.claude/commands/haus-cloneandsetup.md` — it runs the full `project:clone` flow, then sets up each cloned repo locally: selects the node version (`nvm install` from `.nvmrc`), enables corepack, installs JS/PHP dependencies, and seeds `.env`, confirming before each phase. It does not start servers.
|
|
84
89
|
|
|
90
|
+
### Add optional skills (`project:add-skills`)
|
|
91
|
+
|
|
92
|
+
For a project that's already set up but where the user wants more helpers later —
|
|
93
|
+
without re-running the full `project:init` flow. Never show raw JSON; offer plain
|
|
94
|
+
choices.
|
|
95
|
+
|
|
96
|
+
1. Refresh the picture: run `haus scan --json` then `haus recommend` (it re-reads
|
|
97
|
+
`.haus-workflow/deep-context.json` if present).
|
|
98
|
+
2. Read `.haus-workflow/recommendation.json` and `.haus-workflow/haus.lock.json`
|
|
99
|
+
yourself. Build the choices from items **not already installed** (lock ids):
|
|
100
|
+
- **Optional skills & agents** — `optInEligible[]`, grouped by `optInGroup`
|
|
101
|
+
(each carries `purpose` + `tokenEstimate` for a plain-language label).
|
|
102
|
+
- **Project config** — `recommended[]` entries with `install: false` (Haus
|
|
103
|
+
ESLint / Prettier) whose files aren't already present.
|
|
104
|
+
3. If nothing is eligible, say so plainly and stop — e.g. "Everything matching
|
|
105
|
+
your stack is already installed" or "No optional helpers for this stack."
|
|
106
|
+
4. Otherwise present a single `AskUserQuestion` (multi-select, all unchecked)
|
|
107
|
+
with one option per group / config item. Then:
|
|
108
|
+
- Skills/agents → `haus recommend --include <id> <id> …`, then
|
|
109
|
+
`haus apply --write`.
|
|
110
|
+
- Config → `haus scaffold <id>` (add `--force` only if the user chose to
|
|
111
|
+
replace an existing file; scaffold preserves files by default).
|
|
112
|
+
5. Confirm with the names added and their combined token estimate, e.g.
|
|
113
|
+
"Added 2 optional helpers: Code review workflow, TDD workflow (~3k tokens)."
|
|
114
|
+
|
|
85
115
|
### After `haus apply --write`
|
|
86
116
|
|
|
87
117
|
Verify that the root `CLAUDE.md` imports all three haus files:
|