@haus-tech/haus-workflow 1.6.0 → 1.7.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 +9 -0
- package/dist/cli.js +329 -77
- package/library/catalog/manifest.json +12 -403
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.7.0](https://github.com/WeAreHausTech/haus-workflow/compare/v1.6.0...v1.7.0) (2026-08-13)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- **cli:** --dry-run on destructive commands; doctor states what --force discards ([#283](https://github.com/WeAreHausTech/haus-workflow/issues/283)) ([35dfe55](https://github.com/WeAreHausTech/haus-workflow/commit/35dfe553e8282011d365492a3168fee7aee819b4)), closes [#237](https://github.com/WeAreHausTech/haus-workflow/issues/237) [#248](https://github.com/WeAreHausTech/haus-workflow/issues/248)
|
|
8
|
+
- **cli:** add --json to workspace worktree and backups subcommands ([#280](https://github.com/WeAreHausTech/haus-workflow/issues/280)) ([bf1f7b2](https://github.com/WeAreHausTech/haus-workflow/commit/bf1f7b219432ee7cf2dfe6a70e8ae74ab8963b46)), closes [#235](https://github.com/WeAreHausTech/haus-workflow/issues/235) [#236](https://github.com/WeAreHausTech/haus-workflow/issues/236)
|
|
9
|
+
- **cli:** doctor --json and explain-recommendation hint in recommend output ([#281](https://github.com/WeAreHausTech/haus-workflow/issues/281)) ([12fa8ff](https://github.com/WeAreHausTech/haus-workflow/commit/12fa8ff3723a07b735529cfdb32e193302a90bc2)), closes [#234](https://github.com/WeAreHausTech/haus-workflow/issues/234) [#234](https://github.com/WeAreHausTech/haus-workflow/issues/234) [#247](https://github.com/WeAreHausTech/haus-workflow/issues/247)
|
|
10
|
+
- **cli:** upgrade visibility — release notes on update, lingering deprecated-item detection in doctor ([#284](https://github.com/WeAreHausTech/haus-workflow/issues/284)) ([e8d0c04](https://github.com/WeAreHausTech/haus-workflow/commit/e8d0c04b2e7d457cc0e4ea20f18a55fd4d8ee394)), closes [#242](https://github.com/WeAreHausTech/haus-workflow/issues/242) [#251](https://github.com/WeAreHausTech/haus-workflow/issues/251)
|
|
11
|
+
|
|
3
12
|
## [1.6.0](https://github.com/WeAreHausTech/haus-workflow/compare/v1.5.8...v1.6.0) (2026-08-13)
|
|
4
13
|
|
|
5
14
|
### Features
|
package/dist/cli.js
CHANGED
|
@@ -4644,14 +4644,24 @@ function formatAge(mtimeMs, now) {
|
|
|
4644
4644
|
if (hours < 48) return `${hours}h ago`;
|
|
4645
4645
|
return `${Math.round(hours / 24)}d ago`;
|
|
4646
4646
|
}
|
|
4647
|
-
async function runList(root) {
|
|
4647
|
+
async function runList(root, json) {
|
|
4648
4648
|
const entries = await listBackups(root);
|
|
4649
|
+
const newestFirst = [...entries].reverse();
|
|
4650
|
+
if (json) {
|
|
4651
|
+
log(
|
|
4652
|
+
JSON.stringify(
|
|
4653
|
+
newestFirst.map(({ id, kind, mtimeMs, absPath }) => ({ id, kind, mtimeMs, absPath })),
|
|
4654
|
+
null,
|
|
4655
|
+
2
|
|
4656
|
+
)
|
|
4657
|
+
);
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4649
4660
|
if (entries.length === 0) {
|
|
4650
4661
|
log("No backups found under .haus-workflow/backups/.");
|
|
4651
4662
|
return;
|
|
4652
4663
|
}
|
|
4653
4664
|
const now = Date.now();
|
|
4654
|
-
const newestFirst = [...entries].reverse();
|
|
4655
4665
|
for (const entry of newestFirst) {
|
|
4656
4666
|
log(`${entry.id} [${entry.kind}] ${formatAge(entry.mtimeMs, now)}`);
|
|
4657
4667
|
}
|
|
@@ -4685,13 +4695,13 @@ async function lstatIfRegularFile(abs) {
|
|
|
4685
4695
|
throw err;
|
|
4686
4696
|
}
|
|
4687
4697
|
}
|
|
4688
|
-
async function restoreLockBackup(root, entry, yes) {
|
|
4698
|
+
async function restoreLockBackup(root, entry, yes, json, dryRun) {
|
|
4689
4699
|
const target = hausPath(root, "haus.lock.json");
|
|
4690
4700
|
if (await isUnsafeWriteTarget(root, target)) {
|
|
4691
4701
|
error(
|
|
4692
4702
|
`Refusing to restore: ${target} (or a parent directory) is a symlink or other non-regular file \u2014 never written through.`
|
|
4693
4703
|
);
|
|
4694
|
-
return false;
|
|
4704
|
+
return { ok: false, restored: 0 };
|
|
4695
4705
|
}
|
|
4696
4706
|
if (await fs22.pathExists(target)) {
|
|
4697
4707
|
const targetStat = await fs22.stat(target);
|
|
@@ -4701,22 +4711,30 @@ async function restoreLockBackup(root, entry, yes) {
|
|
|
4701
4711
|
);
|
|
4702
4712
|
}
|
|
4703
4713
|
}
|
|
4714
|
+
if (dryRun) {
|
|
4715
|
+
if (!json) {
|
|
4716
|
+
log(
|
|
4717
|
+
`[dry-run] Would restore .haus-workflow/haus.lock.json from ${entry.id}. Nothing was changed.`
|
|
4718
|
+
);
|
|
4719
|
+
}
|
|
4720
|
+
return { ok: true, restored: 0, dryRun: true, wouldRestore: [".haus-workflow/haus.lock.json"] };
|
|
4721
|
+
}
|
|
4704
4722
|
if (!yes) {
|
|
4705
4723
|
const ok = await confirm(
|
|
4706
4724
|
`Restore .haus-workflow/haus.lock.json from backup ${entry.id}? This overwrites the current lockfile.`
|
|
4707
4725
|
);
|
|
4708
4726
|
if (!ok) {
|
|
4709
|
-
log("Cancelled.");
|
|
4710
|
-
return false;
|
|
4727
|
+
if (!json) log("Cancelled.");
|
|
4728
|
+
return { ok: false, restored: 0 };
|
|
4711
4729
|
}
|
|
4712
4730
|
}
|
|
4713
4731
|
if (!await lstatIfRegularFile(entry.absPath) || await isUnsafeWriteTarget(root, target)) {
|
|
4714
4732
|
error(`Refusing to restore: ${entry.id} or ${target} changed since being checked.`);
|
|
4715
|
-
return false;
|
|
4733
|
+
return { ok: false, restored: 0 };
|
|
4716
4734
|
}
|
|
4717
4735
|
await fs22.copy(entry.absPath, target, { overwrite: true });
|
|
4718
|
-
log(`Restored .haus-workflow/haus.lock.json from ${entry.id}.`);
|
|
4719
|
-
return true;
|
|
4736
|
+
if (!json) log(`Restored .haus-workflow/haus.lock.json from ${entry.id}.`);
|
|
4737
|
+
return { ok: true, restored: 1 };
|
|
4720
4738
|
}
|
|
4721
4739
|
async function collectFilesRecursive(dir) {
|
|
4722
4740
|
const files = [];
|
|
@@ -4743,7 +4761,7 @@ async function collectFilesRecursive(dir) {
|
|
|
4743
4761
|
}
|
|
4744
4762
|
return { files, skippedLinks, skippedOther };
|
|
4745
4763
|
}
|
|
4746
|
-
async function restoreDirBackup(root, entry, yes) {
|
|
4764
|
+
async function restoreDirBackup(root, entry, yes, json, dryRun) {
|
|
4747
4765
|
const { files, skippedLinks, skippedOther } = await collectFilesRecursive(entry.absPath);
|
|
4748
4766
|
if (skippedLinks.length > 0) {
|
|
4749
4767
|
warn(
|
|
@@ -4756,8 +4774,8 @@ async function restoreDirBackup(root, entry, yes) {
|
|
|
4756
4774
|
);
|
|
4757
4775
|
}
|
|
4758
4776
|
if (files.length === 0) {
|
|
4759
|
-
log(`Backup ${entry.id} contains no files.`);
|
|
4760
|
-
return false;
|
|
4777
|
+
if (!json) log(`Backup ${entry.id} contains no files.`);
|
|
4778
|
+
return { ok: false, restored: 0 };
|
|
4761
4779
|
}
|
|
4762
4780
|
const safeFiles = [];
|
|
4763
4781
|
const unsafeTargets = [];
|
|
@@ -4773,8 +4791,8 @@ async function restoreDirBackup(root, entry, yes) {
|
|
|
4773
4791
|
);
|
|
4774
4792
|
}
|
|
4775
4793
|
if (safeFiles.length === 0) {
|
|
4776
|
-
log(`Backup ${entry.id} has no restorable targets.`);
|
|
4777
|
-
return false;
|
|
4794
|
+
if (!json) log(`Backup ${entry.id} has no restorable targets.`);
|
|
4795
|
+
return { ok: false, restored: 0 };
|
|
4778
4796
|
}
|
|
4779
4797
|
const stale = [];
|
|
4780
4798
|
const vanished = [];
|
|
@@ -4805,8 +4823,8 @@ async function restoreDirBackup(root, entry, yes) {
|
|
|
4805
4823
|
);
|
|
4806
4824
|
}
|
|
4807
4825
|
if (toRestore.length === 0) {
|
|
4808
|
-
log(`Backup ${entry.id} has no restorable files left after re-validation.`);
|
|
4809
|
-
return false;
|
|
4826
|
+
if (!json) log(`Backup ${entry.id} has no restorable files left after re-validation.`);
|
|
4827
|
+
return { ok: false, restored: 0 };
|
|
4810
4828
|
}
|
|
4811
4829
|
if (stale.length > 0) {
|
|
4812
4830
|
const detail = stale.map(
|
|
@@ -4816,13 +4834,22 @@ async function restoreDirBackup(root, entry, yes) {
|
|
|
4816
4834
|
`${stale.length} file(s) at the restore destination are newer than backup ${entry.id} and will be overwritten: ${detail}`
|
|
4817
4835
|
);
|
|
4818
4836
|
}
|
|
4837
|
+
if (dryRun) {
|
|
4838
|
+
const rels = toRestore.map((abs) => path26.relative(entry.absPath, abs));
|
|
4839
|
+
if (!json) {
|
|
4840
|
+
log(`[dry-run] Would restore ${rels.length} file(s) from ${entry.id}:`);
|
|
4841
|
+
for (const rel of rels) log(` ${rel}`);
|
|
4842
|
+
log("[dry-run] Nothing was changed.");
|
|
4843
|
+
}
|
|
4844
|
+
return { ok: true, restored: 0, dryRun: true, wouldRestore: rels };
|
|
4845
|
+
}
|
|
4819
4846
|
if (!yes) {
|
|
4820
4847
|
const ok = await confirm(
|
|
4821
4848
|
`Restore ${toRestore.length} file(s) from backup ${entry.id} to their original paths?`
|
|
4822
4849
|
);
|
|
4823
4850
|
if (!ok) {
|
|
4824
|
-
log("Cancelled.");
|
|
4825
|
-
return false;
|
|
4851
|
+
if (!json) log("Cancelled.");
|
|
4852
|
+
return { ok: false, restored: 0 };
|
|
4826
4853
|
}
|
|
4827
4854
|
}
|
|
4828
4855
|
let restoredCount = 0;
|
|
@@ -4843,23 +4870,20 @@ async function restoreDirBackup(root, entry, yes) {
|
|
|
4843
4870
|
`Skipped ${skippedAtWrite.length} file(s) that changed since being checked, right before restoring: ${skippedAtWrite.join(", ")}`
|
|
4844
4871
|
);
|
|
4845
4872
|
}
|
|
4846
|
-
log(`Restored ${restoredCount} file(s) from ${entry.id}.`);
|
|
4847
|
-
return restoredCount > 0;
|
|
4873
|
+
if (!json) log(`Restored ${restoredCount} file(s) from ${entry.id}.`);
|
|
4874
|
+
return { ok: restoredCount > 0, restored: restoredCount };
|
|
4848
4875
|
}
|
|
4849
|
-
async function runRestore(root, id, yes) {
|
|
4876
|
+
async function runRestore(root, id, yes, json, dryRun) {
|
|
4850
4877
|
const entries = await listBackups(root);
|
|
4851
4878
|
const entry = entries.find((e) => e.id === id);
|
|
4852
4879
|
if (!entry) {
|
|
4853
4880
|
error(`No backup found with id "${id}". Run \`haus backups list\` to see available backups.`);
|
|
4854
|
-
return false;
|
|
4881
|
+
return { ok: false, restored: 0 };
|
|
4855
4882
|
}
|
|
4856
|
-
return entry.kind === "lock" ? restoreLockBackup(root, entry, yes) : restoreDirBackup(root, entry, yes);
|
|
4883
|
+
return entry.kind === "lock" ? restoreLockBackup(root, entry, yes, json, dryRun) : restoreDirBackup(root, entry, yes, json, dryRun);
|
|
4857
4884
|
}
|
|
4858
4885
|
async function runPrune(root, opts) {
|
|
4859
|
-
|
|
4860
|
-
error("Specify --older-than <days> or --keep <n> \u2014 refusing to prune with no bound.");
|
|
4861
|
-
return false;
|
|
4862
|
-
}
|
|
4886
|
+
const json = Boolean(opts.json);
|
|
4863
4887
|
const entries = await listBackups(root);
|
|
4864
4888
|
const now = Date.now();
|
|
4865
4889
|
const toRemove = /* @__PURE__ */ new Set();
|
|
@@ -4872,11 +4896,32 @@ async function runPrune(root, opts) {
|
|
|
4872
4896
|
if (excess > 0) for (const entry of entries.slice(0, excess)) toRemove.add(entry.id);
|
|
4873
4897
|
}
|
|
4874
4898
|
if (toRemove.size === 0) {
|
|
4875
|
-
log("Nothing to prune.");
|
|
4876
|
-
return
|
|
4899
|
+
if (!json) log("Nothing to prune.");
|
|
4900
|
+
return {
|
|
4901
|
+
ok: true,
|
|
4902
|
+
removed: [],
|
|
4903
|
+
failed: [],
|
|
4904
|
+
kept: entries.length,
|
|
4905
|
+
...opts.dryRun ? { dryRun: true, wouldRemove: [] } : {}
|
|
4906
|
+
};
|
|
4877
4907
|
}
|
|
4878
4908
|
const removeList = entries.filter((e) => toRemove.has(e.id));
|
|
4879
4909
|
const keepCount = entries.length - removeList.length;
|
|
4910
|
+
if (opts.dryRun) {
|
|
4911
|
+
if (!json) {
|
|
4912
|
+
log(`[dry-run] Would remove ${removeList.length} backup(s), keeping ${keepCount}:`);
|
|
4913
|
+
for (const e of removeList) log(` ${e.id}`);
|
|
4914
|
+
log("[dry-run] Nothing was changed.");
|
|
4915
|
+
}
|
|
4916
|
+
return {
|
|
4917
|
+
ok: true,
|
|
4918
|
+
removed: [],
|
|
4919
|
+
failed: [],
|
|
4920
|
+
kept: keepCount,
|
|
4921
|
+
dryRun: true,
|
|
4922
|
+
wouldRemove: removeList.map((e) => e.id)
|
|
4923
|
+
};
|
|
4924
|
+
}
|
|
4880
4925
|
if (!opts.yes) {
|
|
4881
4926
|
const ok = await confirm(
|
|
4882
4927
|
`Remove ${removeList.length} backup(s)?
|
|
@@ -4884,8 +4929,8 @@ async function runPrune(root, opts) {
|
|
|
4884
4929
|
Keeping ${keepCount}.`
|
|
4885
4930
|
);
|
|
4886
4931
|
if (!ok) {
|
|
4887
|
-
log("Cancelled.");
|
|
4888
|
-
return false;
|
|
4932
|
+
if (!json) log("Cancelled.");
|
|
4933
|
+
return { ok: false, removed: [], failed: [], kept: entries.length, cancelled: true };
|
|
4889
4934
|
}
|
|
4890
4935
|
}
|
|
4891
4936
|
const removed = [];
|
|
@@ -4898,37 +4943,52 @@ Keeping ${keepCount}.`
|
|
|
4898
4943
|
failed.push({ id: entry.id, message: err instanceof Error ? err.message : String(err) });
|
|
4899
4944
|
}
|
|
4900
4945
|
}
|
|
4901
|
-
log(`Removed ${removed.length} backup(s), kept ${entries.length - removed.length}.`);
|
|
4946
|
+
if (!json) log(`Removed ${removed.length} backup(s), kept ${entries.length - removed.length}.`);
|
|
4902
4947
|
if (failed.length > 0) {
|
|
4903
4948
|
warn(
|
|
4904
4949
|
`Failed to remove ${failed.length} backup(s): ${failed.map((f) => `${f.id} (${f.message})`).join(", ")}`
|
|
4905
4950
|
);
|
|
4906
|
-
return false;
|
|
4907
4951
|
}
|
|
4908
|
-
return
|
|
4952
|
+
return {
|
|
4953
|
+
ok: failed.length === 0,
|
|
4954
|
+
removed,
|
|
4955
|
+
failed,
|
|
4956
|
+
kept: entries.length - removed.length
|
|
4957
|
+
};
|
|
4909
4958
|
}
|
|
4910
4959
|
async function runBackups(action, opts = {}) {
|
|
4911
4960
|
const root = opts.root ?? process.cwd();
|
|
4961
|
+
const json = Boolean(opts.json);
|
|
4912
4962
|
if (action === "list") {
|
|
4913
|
-
await runList(root);
|
|
4963
|
+
await runList(root, json);
|
|
4914
4964
|
return;
|
|
4915
4965
|
}
|
|
4916
4966
|
if (action === "restore") {
|
|
4917
4967
|
if (!opts.id) {
|
|
4968
|
+
if (json) log(JSON.stringify({ ok: false, error: "Usage: haus backups restore <id>" }));
|
|
4918
4969
|
error("Usage: haus backups restore <id>");
|
|
4919
4970
|
process.exitCode = 1;
|
|
4920
4971
|
return;
|
|
4921
4972
|
}
|
|
4922
|
-
|
|
4973
|
+
const result2 = await runRestore(root, opts.id, Boolean(opts.yes), json, Boolean(opts.dryRun));
|
|
4974
|
+
if (json) log(JSON.stringify({ id: opts.id, ...result2 }, null, 2));
|
|
4975
|
+
if (!result2.ok) process.exitCode = 1;
|
|
4976
|
+
return;
|
|
4977
|
+
}
|
|
4978
|
+
if (opts.olderThan === void 0 && opts.keep === void 0) {
|
|
4979
|
+
const message = "Specify --older-than <days> or --keep <n> \u2014 refusing to prune with no bound.";
|
|
4980
|
+
if (json) log(JSON.stringify({ ok: false, error: message }));
|
|
4981
|
+
error(message);
|
|
4982
|
+
process.exitCode = 1;
|
|
4923
4983
|
return;
|
|
4924
4984
|
}
|
|
4925
4985
|
let olderThanDays;
|
|
4926
4986
|
if (opts.olderThan !== void 0) {
|
|
4927
4987
|
const n = Number(opts.olderThan);
|
|
4928
4988
|
if (!Number.isFinite(n) || n < 0) {
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
);
|
|
4989
|
+
const message = `Invalid --older-than value "${opts.olderThan}" \u2014 expected a non-negative number of days.`;
|
|
4990
|
+
if (json) log(JSON.stringify({ ok: false, error: message }));
|
|
4991
|
+
error(message);
|
|
4932
4992
|
process.exitCode = 1;
|
|
4933
4993
|
return;
|
|
4934
4994
|
}
|
|
@@ -4938,13 +4998,23 @@ async function runBackups(action, opts = {}) {
|
|
|
4938
4998
|
if (opts.keep !== void 0) {
|
|
4939
4999
|
const n = Number(opts.keep);
|
|
4940
5000
|
if (!Number.isFinite(n) || n < 0 || !Number.isInteger(n)) {
|
|
4941
|
-
|
|
5001
|
+
const message = `Invalid --keep value "${opts.keep}" \u2014 expected a non-negative integer.`;
|
|
5002
|
+
if (json) log(JSON.stringify({ ok: false, error: message }));
|
|
5003
|
+
error(message);
|
|
4942
5004
|
process.exitCode = 1;
|
|
4943
5005
|
return;
|
|
4944
5006
|
}
|
|
4945
5007
|
keep = n;
|
|
4946
5008
|
}
|
|
4947
|
-
|
|
5009
|
+
const result = await runPrune(root, {
|
|
5010
|
+
olderThanDays,
|
|
5011
|
+
keep,
|
|
5012
|
+
yes: Boolean(opts.yes),
|
|
5013
|
+
json,
|
|
5014
|
+
dryRun: Boolean(opts.dryRun)
|
|
5015
|
+
});
|
|
5016
|
+
if (json) log(JSON.stringify(result, null, 2));
|
|
5017
|
+
if (!result.ok) process.exitCode = 1;
|
|
4948
5018
|
}
|
|
4949
5019
|
|
|
4950
5020
|
// src/commands/catalog-audit.ts
|
|
@@ -6194,12 +6264,19 @@ async function runDoctor(options) {
|
|
|
6194
6264
|
const root = process.cwd();
|
|
6195
6265
|
if (options?.hooks) {
|
|
6196
6266
|
const hooks2 = await verifyProjectSettingsHooksContract(root);
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6267
|
+
const hooksOk = !hooks2.skipped && hooks2.ok;
|
|
6268
|
+
if (options.json) {
|
|
6269
|
+
log(
|
|
6270
|
+
JSON.stringify(
|
|
6271
|
+
{ ok: hooksOk, skipped: Boolean(hooks2.skipped), message: hooks2.message },
|
|
6272
|
+
null,
|
|
6273
|
+
2
|
|
6274
|
+
)
|
|
6275
|
+
);
|
|
6276
|
+
if (!hooksOk) process.exitCode = 1;
|
|
6200
6277
|
return;
|
|
6201
6278
|
}
|
|
6202
|
-
if (!
|
|
6279
|
+
if (!hooksOk) {
|
|
6203
6280
|
error(`Haus doctor --hooks: ${hooks2.message}`);
|
|
6204
6281
|
process.exitCode = 1;
|
|
6205
6282
|
return;
|
|
@@ -6339,10 +6416,17 @@ async function runDoctor(options) {
|
|
|
6339
6416
|
const templateContent = await readText(templatePath);
|
|
6340
6417
|
const verdict = checkManagedTamper(bodyContent, storedHashMatch?.[1], templateContent ?? null);
|
|
6341
6418
|
if (verdict.status === "tampered" || verdict.status === "legacy-diverged") {
|
|
6419
|
+
let diffNote = "";
|
|
6420
|
+
if (templateContent != null) {
|
|
6421
|
+
const summary = summarizeDiff(
|
|
6422
|
+
createUnifiedDiff(".haus-workflow/WORKFLOW.md", bodyContent, templateContent)
|
|
6423
|
+
);
|
|
6424
|
+
diffNote = ` \u2014 restoring would discard ${summary.deletions} local line(s) and add ${summary.additions}`;
|
|
6425
|
+
}
|
|
6342
6426
|
flag(
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
"haus apply --write --force"
|
|
6427
|
+
`- .haus-workflow/WORKFLOW.md: modified locally${diffNote} (preview: \`haus apply --dry-run\`; restore: \`haus apply --write --force\`)`,
|
|
6428
|
+
`The workflow standard file was edited after haus wrote it${diffNote}`,
|
|
6429
|
+
"preview with `haus apply --dry-run`, then `haus apply --write --force` to restore"
|
|
6346
6430
|
);
|
|
6347
6431
|
} else if (verdict.status === "stale") {
|
|
6348
6432
|
suggest(
|
|
@@ -6524,6 +6608,40 @@ async function runDoctor(options) {
|
|
|
6524
6608
|
}
|
|
6525
6609
|
}
|
|
6526
6610
|
}
|
|
6611
|
+
{
|
|
6612
|
+
const lockRows = await readJson(
|
|
6613
|
+
hausPath(root, "haus.lock.json")
|
|
6614
|
+
);
|
|
6615
|
+
if (lockRows && lockRows.length > 0) {
|
|
6616
|
+
const catalogItems = await loadCatalog(root);
|
|
6617
|
+
const byId = new Map(catalogItems.map((item) => [item.id, item]));
|
|
6618
|
+
const activePinIds = activePins.map((pin) => pin.id);
|
|
6619
|
+
const lingering = [];
|
|
6620
|
+
for (const row of lockRows) {
|
|
6621
|
+
if (typeof row.id !== "string") continue;
|
|
6622
|
+
if (isSupersededByPlugin(row.id, activePinIds)) continue;
|
|
6623
|
+
const manifestItem = byId.get(row.id);
|
|
6624
|
+
if (manifestItem && manifestItem.reviewStatus !== "deprecated") continue;
|
|
6625
|
+
let onDisk = false;
|
|
6626
|
+
for (const rel of row.paths ?? []) {
|
|
6627
|
+
const abs = resolveContainedPath(root, rel);
|
|
6628
|
+
if (abs !== null && await fs30.pathExists(abs)) {
|
|
6629
|
+
onDisk = true;
|
|
6630
|
+
break;
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6633
|
+
if (onDisk) lingering.push(row.id);
|
|
6634
|
+
}
|
|
6635
|
+
if (lingering.length > 0) {
|
|
6636
|
+
const idList = `${lingering.slice(0, 3).join(", ")}${lingering.length > 3 ? ", \u2026" : ""}`;
|
|
6637
|
+
suggest(
|
|
6638
|
+
`- CATALOG ITEMS: ${lingering.length} installed item(s) are removed or deprecated in the current catalog (${idList}) \u2014 pre-0.30.0 CLIs never pruned these`,
|
|
6639
|
+
`${lingering.length} installed item(s) no longer exist in the catalog (removed or deprecated)`,
|
|
6640
|
+
"haus update (prunes them when unmodified; see docs/runbook.md for locally-edited copies)"
|
|
6641
|
+
);
|
|
6642
|
+
}
|
|
6643
|
+
}
|
|
6644
|
+
}
|
|
6527
6645
|
const pkgJson = await readJson(path40.join(packageRoot(), "package.json"));
|
|
6528
6646
|
const currentVersion = pkgJson?.version ?? "0.0.0";
|
|
6529
6647
|
const npmStatus = await fetchNpmVersionStatus(currentVersion);
|
|
@@ -6539,6 +6657,10 @@ async function runDoctor(options) {
|
|
|
6539
6657
|
ok(`- CLI: ${currentVersion} (version check unavailable)`);
|
|
6540
6658
|
}
|
|
6541
6659
|
if (attention.length > 0) process.exitCode = 1;
|
|
6660
|
+
if (options?.json) {
|
|
6661
|
+
log(JSON.stringify({ ok: attention.length === 0, attention, suggestions, detail }, null, 2));
|
|
6662
|
+
return;
|
|
6663
|
+
}
|
|
6542
6664
|
if (attention.length === 0) {
|
|
6543
6665
|
log("\u2705 Your project is set up and healthy.");
|
|
6544
6666
|
} else {
|
|
@@ -6968,6 +7090,31 @@ async function hasLocalOverrides(root) {
|
|
|
6968
7090
|
}
|
|
6969
7091
|
}
|
|
6970
7092
|
|
|
7093
|
+
// src/update/release-notes.ts
|
|
7094
|
+
var RELEASES_API_BASE = "https://api.github.com/repos/WeAreHausTech/haus-workflow/releases/tags";
|
|
7095
|
+
var MAX_NOTE_LINES = 40;
|
|
7096
|
+
async function fetchReleaseNotes(version) {
|
|
7097
|
+
try {
|
|
7098
|
+
const headers = await getGithubApiHeaders();
|
|
7099
|
+
const res = await fetch(`${RELEASES_API_BASE}/v${version}`, {
|
|
7100
|
+
headers,
|
|
7101
|
+
// Same bound npm-version.ts uses — best-effort must also mean bounded-time.
|
|
7102
|
+
signal: AbortSignal.timeout(8e3)
|
|
7103
|
+
});
|
|
7104
|
+
if (!res.ok) return null;
|
|
7105
|
+
const release = await res.json();
|
|
7106
|
+
if (typeof release.body !== "string" || release.body.trim().length === 0) return null;
|
|
7107
|
+
const lines = release.body.trim().split("\n");
|
|
7108
|
+
if (lines.length <= MAX_NOTE_LINES) return lines.join("\n");
|
|
7109
|
+
return [
|
|
7110
|
+
...lines.slice(0, MAX_NOTE_LINES),
|
|
7111
|
+
`\u2026 (${lines.length - MAX_NOTE_LINES} more lines)`
|
|
7112
|
+
].join("\n");
|
|
7113
|
+
} catch {
|
|
7114
|
+
return null;
|
|
7115
|
+
}
|
|
7116
|
+
}
|
|
7117
|
+
|
|
6971
7118
|
// src/commands/update.ts
|
|
6972
7119
|
var NPM_PACKAGE_NAME2 = "@haus-tech/haus-workflow";
|
|
6973
7120
|
async function runUpdate(options) {
|
|
@@ -7032,6 +7179,13 @@ async function runUpdate(options) {
|
|
|
7032
7179
|
if (npmStatus.updateAvailable && npmStatus.latest !== null) {
|
|
7033
7180
|
log(`npm update available: ${currentVersion} \u2192 ${npmStatus.latest}`);
|
|
7034
7181
|
log(`Run: npm install -g ${NPM_PACKAGE_NAME2}`);
|
|
7182
|
+
const notes = await fetchReleaseNotes(npmStatus.latest);
|
|
7183
|
+
if (notes !== null) {
|
|
7184
|
+
log(`
|
|
7185
|
+
What changed in ${npmStatus.latest}:`);
|
|
7186
|
+
log(notes);
|
|
7187
|
+
log("");
|
|
7188
|
+
}
|
|
7035
7189
|
} else if (npmStatus.latest !== null) {
|
|
7036
7190
|
log(`npm package up to date: ${currentVersion}`);
|
|
7037
7191
|
}
|
|
@@ -8118,13 +8272,6 @@ function applyCoInstallSuppression(recommended, skipped) {
|
|
|
8118
8272
|
message: "Skip TDD superpower when a stack testing skill is selected",
|
|
8119
8273
|
signal: "co-install:stack-testing"
|
|
8120
8274
|
},
|
|
8121
|
-
{
|
|
8122
|
-
suppress: "haus.superpowers-specifying-gates",
|
|
8123
|
-
whenAnyRecommended: ["haus.superpowers-checking-gates"],
|
|
8124
|
-
code: "co-install-gate-workflow",
|
|
8125
|
-
message: "Skip specifying-gates when checking-gates is selected",
|
|
8126
|
-
signal: "co-install:checking-gates"
|
|
8127
|
-
},
|
|
8128
8275
|
{
|
|
8129
8276
|
suppress: "haus.ecc-redis-patterns",
|
|
8130
8277
|
whenAnyRecommended: ["haus.redis-redis-connections"],
|
|
@@ -8687,6 +8834,7 @@ async function runRecommend(options) {
|
|
|
8687
8834
|
);
|
|
8688
8835
|
}
|
|
8689
8836
|
for (const w of result.warnings) log(`- WARN: ${w}`);
|
|
8837
|
+
log("Run `haus explain-recommendation` for per-item reasons and skip explanations.");
|
|
8690
8838
|
}
|
|
8691
8839
|
|
|
8692
8840
|
// src/commands/refresh.ts
|
|
@@ -8996,6 +9144,12 @@ async function runUndo(options) {
|
|
|
8996
9144
|
`(preserving ${lockPreserved.length} locally-modified catalog item file(s) \u2014 not shown above)`
|
|
8997
9145
|
);
|
|
8998
9146
|
}
|
|
9147
|
+
if (options.dryRun) {
|
|
9148
|
+
log(`[dry-run] Would remove:
|
|
9149
|
+
${summaryParts.join("\n ")}`);
|
|
9150
|
+
log("[dry-run] User-owned .claude/ files would be preserved. Nothing was changed.");
|
|
9151
|
+
return;
|
|
9152
|
+
}
|
|
8999
9153
|
if (!options.yes) {
|
|
9000
9154
|
const ok = await confirm(
|
|
9001
9155
|
`Remove haus-managed files?
|
|
@@ -9121,6 +9275,21 @@ function printUninstallResult(result) {
|
|
|
9121
9275
|
// src/commands/uninstall.ts
|
|
9122
9276
|
async function runUninstallCommand(options) {
|
|
9123
9277
|
try {
|
|
9278
|
+
if (options.dryRun) {
|
|
9279
|
+
const manifest = await readManifest();
|
|
9280
|
+
if (!manifest) {
|
|
9281
|
+
log("[dry-run] No install manifest found \u2014 nothing would be removed.");
|
|
9282
|
+
return;
|
|
9283
|
+
}
|
|
9284
|
+
if (manifest.files.length > 0) {
|
|
9285
|
+
log(`[dry-run] Would remove ${manifest.files.length} haus-managed file(s) from ~/.claude/:`);
|
|
9286
|
+
for (const f of manifest.files) log(` ${f.destPath}`);
|
|
9287
|
+
}
|
|
9288
|
+
log(
|
|
9289
|
+
"[dry-run] Would strip haus hook entries from ~/.claude/settings.json and remove the install manifest. Nothing was changed."
|
|
9290
|
+
);
|
|
9291
|
+
return;
|
|
9292
|
+
}
|
|
9124
9293
|
if (!options.yes) {
|
|
9125
9294
|
const manifest = await readManifest();
|
|
9126
9295
|
if (manifest) {
|
|
@@ -10338,6 +10507,9 @@ async function runRemove(opts) {
|
|
|
10338
10507
|
}
|
|
10339
10508
|
|
|
10340
10509
|
// src/commands/workspace/worktree.ts
|
|
10510
|
+
function emitJson(result) {
|
|
10511
|
+
log(JSON.stringify(result, null, 2));
|
|
10512
|
+
}
|
|
10341
10513
|
function describeInstall(hydration) {
|
|
10342
10514
|
if (hydration.install.plan.manager === "none") return "no install step (no recognized lockfile)";
|
|
10343
10515
|
if (!hydration.install.ran)
|
|
@@ -10346,7 +10518,8 @@ function describeInstall(hydration) {
|
|
|
10346
10518
|
}
|
|
10347
10519
|
async function handleAdd(opts) {
|
|
10348
10520
|
if (!opts.slug) {
|
|
10349
|
-
error
|
|
10521
|
+
if (opts.json) emitJson({ ok: false, error: "Usage: haus workspace worktree add <slug>" });
|
|
10522
|
+
else error("Usage: haus workspace worktree add <slug>");
|
|
10350
10523
|
process.exitCode = 1;
|
|
10351
10524
|
return;
|
|
10352
10525
|
}
|
|
@@ -10359,10 +10532,16 @@ async function handleAdd(opts) {
|
|
|
10359
10532
|
};
|
|
10360
10533
|
const result = await runAdd(addOpts);
|
|
10361
10534
|
if ("error" in result) {
|
|
10362
|
-
|
|
10535
|
+
if (opts.json) emitJson(result);
|
|
10536
|
+
else error(result.error);
|
|
10363
10537
|
process.exitCode = 1;
|
|
10364
10538
|
return;
|
|
10365
10539
|
}
|
|
10540
|
+
if (opts.json) {
|
|
10541
|
+
emitJson(result);
|
|
10542
|
+
if (!result.dryRun && !result.ok) process.exitCode = 1;
|
|
10543
|
+
return;
|
|
10544
|
+
}
|
|
10366
10545
|
if (result.dryRun) {
|
|
10367
10546
|
log(
|
|
10368
10547
|
`[dry-run] Would create the workspace worktree at ${result.worktreePath} on branch "${result.branch}" (${result.workspaceWorktree.branchAction}).`
|
|
@@ -10402,10 +10581,16 @@ async function handleHydrate(opts) {
|
|
|
10402
10581
|
};
|
|
10403
10582
|
const result = await runHydrateWorktree(hydrateOpts);
|
|
10404
10583
|
if (!result.ok && "error" in result) {
|
|
10405
|
-
|
|
10584
|
+
if (opts.json) emitJson(result);
|
|
10585
|
+
else error(result.error);
|
|
10406
10586
|
process.exitCode = 1;
|
|
10407
10587
|
return;
|
|
10408
10588
|
}
|
|
10589
|
+
if (opts.json) {
|
|
10590
|
+
emitJson(result);
|
|
10591
|
+
if (!result.dryRun && !result.ok) process.exitCode = 1;
|
|
10592
|
+
return;
|
|
10593
|
+
}
|
|
10409
10594
|
if (result.dryRun) {
|
|
10410
10595
|
log(`[dry-run] Would hydrate "${result.slug}":`);
|
|
10411
10596
|
for (const r of result.results) {
|
|
@@ -10427,7 +10612,8 @@ async function handleHydrate(opts) {
|
|
|
10427
10612
|
}
|
|
10428
10613
|
async function handleRemove(opts) {
|
|
10429
10614
|
if (!opts.slug) {
|
|
10430
|
-
error
|
|
10615
|
+
if (opts.json) emitJson({ ok: false, error: "Usage: haus workspace worktree remove <slug>" });
|
|
10616
|
+
else error("Usage: haus workspace worktree remove <slug>");
|
|
10431
10617
|
process.exitCode = 1;
|
|
10432
10618
|
return;
|
|
10433
10619
|
}
|
|
@@ -10437,6 +10623,11 @@ async function handleRemove(opts) {
|
|
|
10437
10623
|
dryRun: Boolean(opts.dryRun)
|
|
10438
10624
|
};
|
|
10439
10625
|
const result = await runRemove(removeOpts);
|
|
10626
|
+
if (opts.json) {
|
|
10627
|
+
emitJson(result);
|
|
10628
|
+
if (!result.ok || "removed" in result && result.failed.length > 0) process.exitCode = 1;
|
|
10629
|
+
return;
|
|
10630
|
+
}
|
|
10440
10631
|
if (!result.ok && "error" in result) {
|
|
10441
10632
|
error(result.error);
|
|
10442
10633
|
process.exitCode = 1;
|
|
@@ -10461,8 +10652,13 @@ async function handleRemove(opts) {
|
|
|
10461
10652
|
}
|
|
10462
10653
|
}
|
|
10463
10654
|
}
|
|
10464
|
-
async function handleList() {
|
|
10655
|
+
async function handleList(opts) {
|
|
10465
10656
|
const result = await runList2();
|
|
10657
|
+
if (opts.json) {
|
|
10658
|
+
emitJson(result);
|
|
10659
|
+
if (!result.ok) process.exitCode = 1;
|
|
10660
|
+
return;
|
|
10661
|
+
}
|
|
10466
10662
|
if (!result.ok) {
|
|
10467
10663
|
error(result.error);
|
|
10468
10664
|
process.exitCode = 1;
|
|
@@ -10533,7 +10729,7 @@ async function runWorktree(action, opts = {}) {
|
|
|
10533
10729
|
case "remove":
|
|
10534
10730
|
return handleRemove(opts);
|
|
10535
10731
|
case "list":
|
|
10536
|
-
return handleList();
|
|
10732
|
+
return handleList(opts);
|
|
10537
10733
|
case "doctor":
|
|
10538
10734
|
return handleDoctor(opts);
|
|
10539
10735
|
}
|
|
@@ -11361,6 +11557,52 @@ async function runWorkspaceUndo(workspaceRoot, options = {}) {
|
|
|
11361
11557
|
if (config.repos.length === 0) {
|
|
11362
11558
|
log(`No repos configured in ${WORKSPACE_FILE}.`);
|
|
11363
11559
|
}
|
|
11560
|
+
if (options.dryRun) {
|
|
11561
|
+
for (const repo of config.repos) {
|
|
11562
|
+
const repoRoot = path71.resolve(workspaceRoot, repo.path);
|
|
11563
|
+
log(`
|
|
11564
|
+
\u2192 ${repo.name} (${repo.path})`);
|
|
11565
|
+
try {
|
|
11566
|
+
if (!existsSync9(repoRoot) || !statSync2(repoRoot).isDirectory()) {
|
|
11567
|
+
throw new Error(`Repo path is not a directory: ${repo.path}`);
|
|
11568
|
+
}
|
|
11569
|
+
await runUndo({ dryRun: true, root: repoRoot });
|
|
11570
|
+
} catch (err) {
|
|
11571
|
+
warn(
|
|
11572
|
+
`[dry-run] Undo preview failed for ${repo.name}: ${err instanceof Error ? err.message : String(err)}`
|
|
11573
|
+
);
|
|
11574
|
+
}
|
|
11575
|
+
}
|
|
11576
|
+
const linkedContext2 = await readLinkedContext(workspaceRoot);
|
|
11577
|
+
for (const entry of linkedContext2) {
|
|
11578
|
+
const abs = resolveContainedPath(workspaceRoot, entry.path);
|
|
11579
|
+
if (abs === null) {
|
|
11580
|
+
warn(`Refusing to remove linked context path outside the workspace root: ${entry.path}`);
|
|
11581
|
+
continue;
|
|
11582
|
+
}
|
|
11583
|
+
if (await fs48.pathExists(abs)) {
|
|
11584
|
+
log(`[dry-run] Would remove ${entry.path} (linked ${entry.type} from ${entry.repo})`);
|
|
11585
|
+
}
|
|
11586
|
+
}
|
|
11587
|
+
const workspaceArtifacts = [
|
|
11588
|
+
...WORKSPACE_AGGREGATE_FILES.map((rel) => hausPath(workspaceRoot, rel)),
|
|
11589
|
+
manifestPath(workspaceRoot),
|
|
11590
|
+
hausPath(workspaceRoot, "WORKSPACE.md")
|
|
11591
|
+
];
|
|
11592
|
+
for (const abs of workspaceArtifacts) {
|
|
11593
|
+
if (await fs48.pathExists(abs)) {
|
|
11594
|
+
log(`[dry-run] Would remove ${path71.relative(workspaceRoot, abs)}`);
|
|
11595
|
+
}
|
|
11596
|
+
}
|
|
11597
|
+
if (!await fs48.pathExists(hausPath(workspaceRoot, "WORKSPACE.md"))) {
|
|
11598
|
+
const claudeMd = await readText(path71.join(workspaceRoot, "CLAUDE.md"));
|
|
11599
|
+
if (claudeMd?.includes(WORKSPACE_BLOCK_BEGIN) || claudeMd && hasLegacyWorkspaceBlock(claudeMd)) {
|
|
11600
|
+
log("[dry-run] Would remove the workspace haus import block from CLAUDE.md");
|
|
11601
|
+
}
|
|
11602
|
+
}
|
|
11603
|
+
log(`[dry-run] ${WORKSPACE_FILE} would be left in place. Nothing was changed.`);
|
|
11604
|
+
return;
|
|
11605
|
+
}
|
|
11364
11606
|
if (!options.yes) {
|
|
11365
11607
|
const ok = await confirm(
|
|
11366
11608
|
`Undo haus setup for ${config.repos.length} repo(s) in this workspace, plus workspace-root artifacts?`
|
|
@@ -11560,7 +11802,7 @@ async function runWorkspace(action, options = {}) {
|
|
|
11560
11802
|
await runWorkspaceDoctor(workspaceRoot, { json: options.json });
|
|
11561
11803
|
return;
|
|
11562
11804
|
case "undo":
|
|
11563
|
-
await runWorkspaceUndo(workspaceRoot, { yes: options.yes });
|
|
11805
|
+
await runWorkspaceUndo(workspaceRoot, { yes: options.yes, dryRun: options.dryRun });
|
|
11564
11806
|
return;
|
|
11565
11807
|
case "link-context":
|
|
11566
11808
|
await runWorkspaceLinkContext(workspaceRoot, { write: options.write, json: options.json });
|
|
@@ -11608,7 +11850,7 @@ program.command("setup-project").description("Fast full setup: scan, recommend,
|
|
|
11608
11850
|
).action(runSetupProject);
|
|
11609
11851
|
program.command("doctor").description(
|
|
11610
11852
|
"Validate project setup: hooks contract, managed files, cache freshness, CLI version"
|
|
11611
|
-
).option("--hooks", "Verify .claude/settings.json matches the hook contract").action(runDoctor);
|
|
11853
|
+
).option("--hooks", "Verify .claude/settings.json matches the hook contract").option("--json", "Emit all findings as one JSON document on stdout instead of human output").action(runDoctor);
|
|
11612
11854
|
program.command("fetch-refs").description("Fetch and cache llms.txt content from catalog item references").option("--all", "Fetch for all catalog items (default)").option("--id <id>", "Fetch for a single catalog item by id").option("--json", "Emit machine-readable JSON summary to stdout").action(runFetchRefs);
|
|
11613
11855
|
program.command("apply").description("Write recommended catalog items and core Claude files to .claude/").option("--dry-run").option("--write").option("--select", "Interactively select catalog items before applying").option(
|
|
11614
11856
|
"--ids <ids...>",
|
|
@@ -11623,12 +11865,20 @@ program.command("apply").description("Write recommended catalog items and core C
|
|
|
11623
11865
|
"--prune",
|
|
11624
11866
|
"Remove lock-tracked catalog items no longer in the current recommendation (hash-gated, backed up first)"
|
|
11625
11867
|
).action(runApply);
|
|
11626
|
-
program.command("undo").description("Remove haus-managed project files; preserves user-owned .claude/ content").option("-y, --yes", "Skip confirmation").action(runUndo);
|
|
11868
|
+
program.command("undo").description("Remove haus-managed project files; preserves user-owned .claude/ content").option("-y, --yes", "Skip confirmation").option("--dry-run", "Print what would be removed without deleting anything").action(runUndo);
|
|
11627
11869
|
var backups = program.command("backups").description("List, restore, or prune backups under .haus-workflow/backups/");
|
|
11628
|
-
backups.command("list").description("List backups under .haus-workflow/backups/").action(() => runBackups("list"));
|
|
11629
|
-
backups.command("restore <id>").description("Restore a specific backup by id (see `haus backups list`)").option("-y, --yes", "Skip confirmation").
|
|
11630
|
-
|
|
11631
|
-
|
|
11870
|
+
backups.command("list").description("List backups under .haus-workflow/backups/").option("--json", "Emit the backup entries as JSON (newest first)").action((opts) => runBackups("list", { json: opts.json }));
|
|
11871
|
+
backups.command("restore <id>").description("Restore a specific backup by id (see `haus backups list`)").option("-y, --yes", "Skip confirmation").option("--json", "Emit a machine-readable result (JSON-only stdout; warnings on stderr)").option("--dry-run", "Print what would be restored without writing anything").action(
|
|
11872
|
+
(id, opts) => runBackups("restore", { id, yes: opts.yes, json: opts.json, dryRun: opts.dryRun })
|
|
11873
|
+
);
|
|
11874
|
+
backups.command("prune").description("Delete old backups by age and/or count (requires --older-than or --keep)").option("--older-than <days>", "Remove backups older than N days").option("--keep <n>", "Keep only the N most recent backups").option("-y, --yes", "Skip confirmation").option("--json", "Emit a machine-readable result (JSON-only stdout; warnings on stderr)").option("--dry-run", "Print what would be pruned without deleting anything").action(
|
|
11875
|
+
(opts) => runBackups("prune", {
|
|
11876
|
+
olderThan: opts.olderThan,
|
|
11877
|
+
dryRun: opts.dryRun,
|
|
11878
|
+
keep: opts.keep,
|
|
11879
|
+
yes: opts.yes,
|
|
11880
|
+
json: opts.json
|
|
11881
|
+
})
|
|
11632
11882
|
);
|
|
11633
11883
|
program.command("explain-recommendation").description("Print a per-item breakdown of the current recommendation, with skip reasons").option("--json").action(runExplainRecommendation);
|
|
11634
11884
|
program.command("init").description("First-run setup; delegates to setup-project if .haus-workflow/ does not exist").option("--json").action(runInit);
|
|
@@ -11646,7 +11896,7 @@ program.command("install").description("Seed ~/.claude/ with Haus-managed skills
|
|
|
11646
11896
|
program.command("scaffold [ids...]").description(
|
|
11647
11897
|
"Copy catalog config items (ESLint, Prettier) into the project root. Pass item IDs to scaffold specific items, or omit to scaffold all available config items."
|
|
11648
11898
|
).option("--force", "Overwrite existing files").option("--dry-run", "Preview what would be written without changing anything").option("--root <path>", "Project root (defaults to cwd)", process.cwd()).action(runScaffold);
|
|
11649
|
-
program.command("uninstall").description("Remove Haus-managed skills, agents, and hook entries from ~/.claude/").option("--force").option("-y, --yes", "Skip confirmation").action(runUninstallCommand);
|
|
11899
|
+
program.command("uninstall").description("Remove Haus-managed skills, agents, and hook entries from ~/.claude/").option("--force").option("-y, --yes", "Skip confirmation").option("--dry-run", "Print what would be removed without deleting anything").action(runUninstallCommand);
|
|
11650
11900
|
var guard = program.command("guard").description("PreToolUse hook guards: block dangerous bash commands and sensitive file access");
|
|
11651
11901
|
guard.command("file-access").description("Block reads/edits of sensitive paths (run by the Claude Code hook)").option("--from-hook").action((opts) => runGuard("file-access", opts));
|
|
11652
11902
|
guard.command("bash").description("Block dangerous shell commands (run by the Claude Code hook)").option("--from-hook").action((opts) => runGuard("bash", opts));
|
|
@@ -11668,7 +11918,7 @@ workspace.command("discover").description("Auto-find member repos and write/merg
|
|
|
11668
11918
|
workspace.command("scan").description("Aggregate a cross-repo summary from a fast scan of each repo").option("--json", "Output the written artifact paths as JSON").action((opts) => runWorkspace("scan", opts));
|
|
11669
11919
|
workspace.command("setup").description("Per-repo setup loop + workspace layer + manifest").option("--write", "Apply changes (default previews only)").option("--dry-run", "Preview changes without writing").option("--json", "Emit machine-readable per-repo output").option("--continue-on-error", "Keep going past a failed repo (default fail-fast)").option("--only <names>", "Restrict to comma-separated repo names").action((opts) => runWorkspace("setup", opts));
|
|
11670
11920
|
workspace.command("doctor").description("Report workspace drift against the manifest").option("--json", "Output the manifest and drift array as JSON").action((opts) => runWorkspace("doctor", opts));
|
|
11671
|
-
workspace.command("undo").description("Revert haus setup for every configured repo, plus workspace-root artifacts").option("-y, --yes", "Skip confirmation").action((opts) => runWorkspace("undo", opts));
|
|
11921
|
+
workspace.command("undo").description("Revert haus setup for every configured repo, plus workspace-root artifacts").option("-y, --yes", "Skip confirmation").option("--dry-run", "Print what would be removed without deleting anything").action((opts) => runWorkspace("undo", opts));
|
|
11672
11922
|
workspace.command("link-context").description(
|
|
11673
11923
|
"Copy each member repo's skill/agent/command into the workspace root's .claude/, prefixed to avoid collisions"
|
|
11674
11924
|
).option("--write", "Persist copies + manifest + gitignore (default previews only)").option("--json", "Output the link result as JSON").action((opts) => runWorkspace("link-context", opts));
|
|
@@ -11677,29 +11927,31 @@ var worktree = workspace.command("worktree").description(
|
|
|
11677
11927
|
);
|
|
11678
11928
|
worktree.command("add <slug>").description(
|
|
11679
11929
|
"Create a workspace worktree plus one member worktree per repo, on a mirrored branch"
|
|
11680
|
-
).option("--branch <name>", "Branch to create/checkout (default: the slug)").option("--only <repos>", "Restrict to comma- or space-separated member ids/folders").option("--no-hydrate", "Skip hydration (CoW clone + install-reconciliation)").option("--dry-run", "Preview without writing or running anything").action(
|
|
11930
|
+
).option("--branch <name>", "Branch to create/checkout (default: the slug)").option("--only <repos>", "Restrict to comma- or space-separated member ids/folders").option("--no-hydrate", "Skip hydration (CoW clone + install-reconciliation)").option("--dry-run", "Preview without writing or running anything").option("--json", "Emit the structured add result as JSON").action(
|
|
11681
11931
|
(slug, opts) => runWorktree("add", {
|
|
11682
11932
|
slug,
|
|
11683
11933
|
branch: opts.branch,
|
|
11684
11934
|
only: opts.only,
|
|
11685
11935
|
hydrate: opts.hydrate,
|
|
11686
|
-
dryRun: opts.dryRun
|
|
11936
|
+
dryRun: opts.dryRun,
|
|
11937
|
+
json: opts.json
|
|
11687
11938
|
})
|
|
11688
11939
|
);
|
|
11689
11940
|
worktree.command("hydrate").description("Re-run CoW clone + install-reconciliation for the current workspace worktree").option("--only <repos>", "Restrict to comma- or space-separated member ids/folders").option("--force", "Re-clone hydration targets even if already present").option(
|
|
11690
11941
|
"--materialize",
|
|
11691
11942
|
"Also `git worktree add` any member missing entirely (e.g. a worktree created by plain `git worktree add` outside haus) before hydrating it"
|
|
11692
|
-
).option("--dry-run", "Preview without writing or running anything").action(
|
|
11943
|
+
).option("--dry-run", "Preview without writing or running anything").option("--json", "Emit the structured hydrate result as JSON").action(
|
|
11693
11944
|
(opts) => runWorktree("hydrate", {
|
|
11694
11945
|
only: opts.only,
|
|
11695
11946
|
force: opts.force,
|
|
11696
11947
|
materialize: opts.materialize,
|
|
11697
|
-
dryRun: opts.dryRun
|
|
11948
|
+
dryRun: opts.dryRun,
|
|
11949
|
+
json: opts.json
|
|
11698
11950
|
})
|
|
11699
11951
|
);
|
|
11700
|
-
worktree.command("list").description("List materialized workspace worktrees and their member status").action(() => runWorktree("list"));
|
|
11701
|
-
worktree.command("remove <slug>").description("Remove a workspace worktree and its member worktrees").option("--force", "Bypass the uncommitted/unpushed-work refusal").option("--dry-run", "Preview without removing anything").action(
|
|
11702
|
-
(slug, opts) => runWorktree("remove", { slug, force: opts.force, dryRun: opts.dryRun })
|
|
11952
|
+
worktree.command("list").description("List materialized workspace worktrees and their member status").option("--json", "Emit the structured list result as JSON").action((opts) => runWorktree("list", { json: opts.json }));
|
|
11953
|
+
worktree.command("remove <slug>").description("Remove a workspace worktree and its member worktrees").option("--force", "Bypass the uncommitted/unpushed-work refusal").option("--dry-run", "Preview without removing anything").option("--json", "Emit the structured remove result as JSON").action(
|
|
11954
|
+
(slug, opts) => runWorktree("remove", { slug, force: opts.force, dryRun: opts.dryRun, json: opts.json })
|
|
11703
11955
|
);
|
|
11704
11956
|
worktree.command("doctor").description("Fast, side-effect-free health check for the current workspace worktree").option("--from-hook", "Emit for the SessionStart hook \u2014 always exits 0").action((opts) => runWorktree("doctor", { fromHook: opts.fromHook }));
|
|
11705
11957
|
program.parseAsync(process.argv).catch((err) => {
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "5.0.0",
|
|
3
|
+
"globalPlugins": [
|
|
4
|
+
{
|
|
5
|
+
"id": "superpowers",
|
|
6
|
+
"plugin": "superpowers-extended-cc",
|
|
7
|
+
"marketplace": "superpowers-extended-cc-marketplace",
|
|
8
|
+
"url": "https://github.com/pcvelz/superpowers.git",
|
|
9
|
+
"ref": "v6.4.0"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
3
12
|
"items": [
|
|
4
13
|
{
|
|
5
14
|
"id": "haus.agentic-workflow-standard",
|
|
@@ -1539,7 +1548,7 @@
|
|
|
1539
1548
|
},
|
|
1540
1549
|
{
|
|
1541
1550
|
"id": "haus.package-manager-yarn4-pnpm89",
|
|
1542
|
-
"version": "1.0.
|
|
1551
|
+
"version": "1.0.2",
|
|
1543
1552
|
"source": "haus",
|
|
1544
1553
|
"type": "skill",
|
|
1545
1554
|
"path": "skills/haus-owned/package-managers/package-manager-yarn4-pnpm89",
|
|
@@ -2102,406 +2111,6 @@
|
|
|
2102
2111
|
}
|
|
2103
2112
|
]
|
|
2104
2113
|
},
|
|
2105
|
-
{
|
|
2106
|
-
"id": "haus.superpowers-brainstorming",
|
|
2107
|
-
"version": "1.0.3",
|
|
2108
|
-
"source": "curated",
|
|
2109
|
-
"type": "skill",
|
|
2110
|
-
"path": "skills/superpowers/brainstorming",
|
|
2111
|
-
"title": "Superpowers Brainstorming",
|
|
2112
|
-
"purpose": "You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.",
|
|
2113
|
-
"whenToUse": "You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.",
|
|
2114
|
-
"whenNotToUse": "Do not use for mechanical edits, bug fixes with known cause, or read-only questions.",
|
|
2115
|
-
"tags": ["workflow", "core-skill"],
|
|
2116
|
-
"repoRoles": [],
|
|
2117
|
-
"tokenEstimate": 2887,
|
|
2118
|
-
"installMode": "copy-selected",
|
|
2119
|
-
"default": true,
|
|
2120
|
-
"reviewStatus": "approved",
|
|
2121
|
-
"riskLevel": "low",
|
|
2122
|
-
"useMode": "copy",
|
|
2123
|
-
"license": "MIT",
|
|
2124
|
-
"licenseConfidence": "high",
|
|
2125
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2126
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/brainstorming",
|
|
2127
|
-
"ecosystem": "superpowers",
|
|
2128
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2129
|
-
},
|
|
2130
|
-
{
|
|
2131
|
-
"id": "haus.superpowers-checking-gates",
|
|
2132
|
-
"version": "1.0.1",
|
|
2133
|
-
"source": "curated",
|
|
2134
|
-
"type": "skill",
|
|
2135
|
-
"path": "skills/superpowers/checking-gates",
|
|
2136
|
-
"title": "Superpowers Checking Gates",
|
|
2137
|
-
"purpose": "Use when picking up a user-gate task OR when a hook demands re-validation. Runs the \"do I know HOW?\" self-check; if the HOW is clear, executes the verification and posts evidence; if not, hands off to specifying-gates. Kept deliberately separate from executing-plans so that without the opt-in hook, the main flow stays untouched.",
|
|
2138
|
-
"whenToUse": "Use when picking up a user-gate task OR when a hook demands re-validation. Runs the \"do I know HOW?\" self-check; if the HOW is clear, executes the verification and posts evidence; if not, hands off to specifying-gates. Kept deliberately separate from executing-plans so that without the opt-in hook, the main flow stays untouched.",
|
|
2139
|
-
"whenNotToUse": "Do not use when the user-gate hook is not registered or the task is not user-gated.",
|
|
2140
|
-
"tags": ["workflow", "workflow-skill"],
|
|
2141
|
-
"repoRoles": [],
|
|
2142
|
-
"tokenEstimate": 1228,
|
|
2143
|
-
"installMode": "copy-selected",
|
|
2144
|
-
"default": true,
|
|
2145
|
-
"reviewStatus": "approved",
|
|
2146
|
-
"riskLevel": "low",
|
|
2147
|
-
"useMode": "copy",
|
|
2148
|
-
"license": "MIT",
|
|
2149
|
-
"licenseConfidence": "high",
|
|
2150
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2151
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/checking-gates",
|
|
2152
|
-
"ecosystem": "superpowers",
|
|
2153
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2154
|
-
},
|
|
2155
|
-
{
|
|
2156
|
-
"id": "haus.superpowers-dispatching-parallel-agents",
|
|
2157
|
-
"version": "1.0.1",
|
|
2158
|
-
"source": "curated",
|
|
2159
|
-
"type": "skill",
|
|
2160
|
-
"path": "skills/superpowers/dispatching-parallel-agents",
|
|
2161
|
-
"title": "Superpowers Dispatching Parallel Agents",
|
|
2162
|
-
"purpose": "Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies",
|
|
2163
|
-
"whenToUse": "Use when facing 2+ independent tasks that can be worked on without shared state or sequential dependencies",
|
|
2164
|
-
"whenNotToUse": "Do not use when tasks share state or must run sequentially.",
|
|
2165
|
-
"tags": ["workflow", "workflow-skill"],
|
|
2166
|
-
"repoRoles": [],
|
|
2167
|
-
"tokenEstimate": 1793,
|
|
2168
|
-
"installMode": "copy-selected",
|
|
2169
|
-
"default": true,
|
|
2170
|
-
"reviewStatus": "approved",
|
|
2171
|
-
"riskLevel": "low",
|
|
2172
|
-
"useMode": "copy",
|
|
2173
|
-
"license": "MIT",
|
|
2174
|
-
"licenseConfidence": "high",
|
|
2175
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2176
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/dispatching-parallel-agents",
|
|
2177
|
-
"ecosystem": "superpowers",
|
|
2178
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2179
|
-
},
|
|
2180
|
-
{
|
|
2181
|
-
"id": "haus.superpowers-executing-plans",
|
|
2182
|
-
"version": "1.0.2",
|
|
2183
|
-
"source": "curated",
|
|
2184
|
-
"type": "skill",
|
|
2185
|
-
"path": "skills/superpowers/executing-plans",
|
|
2186
|
-
"title": "Superpowers Executing Plans",
|
|
2187
|
-
"purpose": "Use when you have a written implementation plan to execute in a separate session with review checkpoints",
|
|
2188
|
-
"whenToUse": "Use when you have a written implementation plan to execute in a separate session with review checkpoints",
|
|
2189
|
-
"whenNotToUse": "Do not use before a written plan exists or for single-step trivial changes.",
|
|
2190
|
-
"tags": ["workflow", "core-skill"],
|
|
2191
|
-
"repoRoles": [],
|
|
2192
|
-
"tokenEstimate": 1601,
|
|
2193
|
-
"installMode": "copy-selected",
|
|
2194
|
-
"default": true,
|
|
2195
|
-
"reviewStatus": "approved",
|
|
2196
|
-
"riskLevel": "low",
|
|
2197
|
-
"useMode": "copy",
|
|
2198
|
-
"license": "MIT",
|
|
2199
|
-
"licenseConfidence": "high",
|
|
2200
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2201
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/fc6d376756b605f35c399b1b00a16bb27ec55171/skills/executing-plans",
|
|
2202
|
-
"ecosystem": "superpowers",
|
|
2203
|
-
"pinnedRef": "fc6d376756b605f35c399b1b00a16bb27ec55171"
|
|
2204
|
-
},
|
|
2205
|
-
{
|
|
2206
|
-
"id": "haus.superpowers-finishing-a-development-branch",
|
|
2207
|
-
"version": "1.0.3",
|
|
2208
|
-
"source": "curated",
|
|
2209
|
-
"type": "skill",
|
|
2210
|
-
"path": "skills/superpowers/finishing-a-development-branch",
|
|
2211
|
-
"title": "Superpowers Finishing A Development Branch",
|
|
2212
|
-
"purpose": "Use when implementation is complete, all tests pass, and you need to decide how to integrate the work",
|
|
2213
|
-
"whenToUse": "Use when implementation is complete, all tests pass, and you need to decide how to integrate the work",
|
|
2214
|
-
"whenNotToUse": "Do not use while implementation or verification is still incomplete.",
|
|
2215
|
-
"tags": ["workflow", "quality"],
|
|
2216
|
-
"repoRoles": [],
|
|
2217
|
-
"tokenEstimate": 1756,
|
|
2218
|
-
"installMode": "copy-selected",
|
|
2219
|
-
"default": true,
|
|
2220
|
-
"reviewStatus": "approved",
|
|
2221
|
-
"riskLevel": "low",
|
|
2222
|
-
"useMode": "copy",
|
|
2223
|
-
"license": "MIT",
|
|
2224
|
-
"licenseConfidence": "high",
|
|
2225
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2226
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/finishing-a-development-branch",
|
|
2227
|
-
"ecosystem": "superpowers",
|
|
2228
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2229
|
-
},
|
|
2230
|
-
{
|
|
2231
|
-
"id": "haus.superpowers-receiving-code-review",
|
|
2232
|
-
"version": "1.0.3",
|
|
2233
|
-
"source": "curated",
|
|
2234
|
-
"type": "skill",
|
|
2235
|
-
"path": "skills/superpowers/receiving-code-review",
|
|
2236
|
-
"title": "Superpowers Receiving Code Review",
|
|
2237
|
-
"purpose": "Use when receiving code review feedback, before implementing suggestions, especially if feedback seems unclear or technically questionable - requires technical rigor and verification, not performative agreement or blind implementation",
|
|
2238
|
-
"whenToUse": "Use when receiving code review feedback, before implementing suggestions, especially if feedback seems unclear or technically questionable - requires technical rigor and verification, not performative agreement or blind implementation",
|
|
2239
|
-
"whenNotToUse": "Do not use to blindly apply feedback without technical verification.",
|
|
2240
|
-
"tags": ["review", "review-skill"],
|
|
2241
|
-
"repoRoles": [],
|
|
2242
|
-
"tokenEstimate": 1551,
|
|
2243
|
-
"installMode": "copy-selected",
|
|
2244
|
-
"default": true,
|
|
2245
|
-
"reviewStatus": "approved",
|
|
2246
|
-
"riskLevel": "low",
|
|
2247
|
-
"useMode": "copy",
|
|
2248
|
-
"license": "MIT",
|
|
2249
|
-
"licenseConfidence": "high",
|
|
2250
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2251
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/receiving-code-review",
|
|
2252
|
-
"ecosystem": "superpowers",
|
|
2253
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2254
|
-
},
|
|
2255
|
-
{
|
|
2256
|
-
"id": "haus.superpowers-requesting-code-review",
|
|
2257
|
-
"version": "1.0.3",
|
|
2258
|
-
"source": "curated",
|
|
2259
|
-
"type": "skill",
|
|
2260
|
-
"path": "skills/superpowers/requesting-code-review",
|
|
2261
|
-
"title": "Superpowers Requesting Code Review",
|
|
2262
|
-
"purpose": "Use when completing tasks, implementing major features, or before merging to verify work meets requirements",
|
|
2263
|
-
"whenToUse": "Use when completing tasks, implementing major features, or before merging to verify work meets requirements",
|
|
2264
|
-
"whenNotToUse": "Do not use for trivial one-line fixes with no review surface.",
|
|
2265
|
-
"tags": ["review", "review-skill"],
|
|
2266
|
-
"repoRoles": [],
|
|
2267
|
-
"tokenEstimate": 739,
|
|
2268
|
-
"installMode": "copy-selected",
|
|
2269
|
-
"default": true,
|
|
2270
|
-
"reviewStatus": "approved",
|
|
2271
|
-
"riskLevel": "low",
|
|
2272
|
-
"useMode": "copy",
|
|
2273
|
-
"license": "MIT",
|
|
2274
|
-
"licenseConfidence": "high",
|
|
2275
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2276
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/requesting-code-review",
|
|
2277
|
-
"ecosystem": "superpowers",
|
|
2278
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2279
|
-
},
|
|
2280
|
-
{
|
|
2281
|
-
"id": "haus.superpowers-specifying-gates",
|
|
2282
|
-
"version": "1.0.3",
|
|
2283
|
-
"source": "curated",
|
|
2284
|
-
"type": "skill",
|
|
2285
|
-
"path": "skills/superpowers/specifying-gates",
|
|
2286
|
-
"title": "Superpowers Specifying Gates",
|
|
2287
|
-
"purpose": "Use when a user-gate task has requiresUserSpecification=true OR the agent's \"do I know HOW?\" self-check returns no. Locks down verification mechanics through a short AskUserQuestion sequence and writes the answers back into the task's metadata. Does NOT run the verification itself.",
|
|
2288
|
-
"whenToUse": "Use when a user-gate task has requiresUserSpecification=true OR the agent's \"do I know HOW?\" self-check returns no. Locks down verification mechanics through a short AskUserQuestion sequence and writes the answers back into the task's metadata. Does NOT run the verification itself.",
|
|
2289
|
-
"whenNotToUse": "Do not use when verification mechanics are already specified or hook is off.",
|
|
2290
|
-
"tags": ["workflow", "workflow-skill"],
|
|
2291
|
-
"repoRoles": [],
|
|
2292
|
-
"tokenEstimate": 1785,
|
|
2293
|
-
"installMode": "copy-selected",
|
|
2294
|
-
"default": true,
|
|
2295
|
-
"reviewStatus": "approved",
|
|
2296
|
-
"riskLevel": "low",
|
|
2297
|
-
"useMode": "copy",
|
|
2298
|
-
"license": "MIT",
|
|
2299
|
-
"licenseConfidence": "high",
|
|
2300
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2301
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/specifying-gates",
|
|
2302
|
-
"ecosystem": "superpowers",
|
|
2303
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2304
|
-
},
|
|
2305
|
-
{
|
|
2306
|
-
"id": "haus.superpowers-subagent-driven-development",
|
|
2307
|
-
"version": "1.0.4",
|
|
2308
|
-
"source": "curated",
|
|
2309
|
-
"type": "skill",
|
|
2310
|
-
"path": "skills/superpowers/subagent-driven-development",
|
|
2311
|
-
"title": "Superpowers Subagent Driven Development",
|
|
2312
|
-
"purpose": "Use when executing implementation plans with independent tasks in the current session",
|
|
2313
|
-
"whenToUse": "Use when executing implementation plans with independent tasks in the current session",
|
|
2314
|
-
"whenNotToUse": "Do not use for tightly coupled sequential work or when subagents are unavailable.",
|
|
2315
|
-
"tags": ["workflow", "workflow-skill"],
|
|
2316
|
-
"repoRoles": [],
|
|
2317
|
-
"tokenEstimate": 8024,
|
|
2318
|
-
"installMode": "copy-selected",
|
|
2319
|
-
"default": true,
|
|
2320
|
-
"reviewStatus": "approved",
|
|
2321
|
-
"riskLevel": "low",
|
|
2322
|
-
"useMode": "copy",
|
|
2323
|
-
"license": "MIT",
|
|
2324
|
-
"licenseConfidence": "high",
|
|
2325
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2326
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/subagent-driven-development",
|
|
2327
|
-
"ecosystem": "superpowers",
|
|
2328
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2329
|
-
},
|
|
2330
|
-
{
|
|
2331
|
-
"id": "haus.superpowers-systematic-debugging",
|
|
2332
|
-
"version": "1.0.1",
|
|
2333
|
-
"source": "curated",
|
|
2334
|
-
"type": "skill",
|
|
2335
|
-
"path": "skills/superpowers/systematic-debugging",
|
|
2336
|
-
"title": "Superpowers Systematic Debugging",
|
|
2337
|
-
"purpose": "Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes",
|
|
2338
|
-
"whenToUse": "Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes",
|
|
2339
|
-
"whenNotToUse": "Do not use to guess fixes without reproducing or investigating the failure.",
|
|
2340
|
-
"tags": ["workflow", "quality"],
|
|
2341
|
-
"repoRoles": [],
|
|
2342
|
-
"tokenEstimate": 2373,
|
|
2343
|
-
"installMode": "copy-selected",
|
|
2344
|
-
"default": true,
|
|
2345
|
-
"reviewStatus": "approved",
|
|
2346
|
-
"riskLevel": "low",
|
|
2347
|
-
"useMode": "copy",
|
|
2348
|
-
"license": "MIT",
|
|
2349
|
-
"licenseConfidence": "high",
|
|
2350
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2351
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/systematic-debugging",
|
|
2352
|
-
"ecosystem": "superpowers",
|
|
2353
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2354
|
-
},
|
|
2355
|
-
{
|
|
2356
|
-
"id": "haus.superpowers-test-driven-development",
|
|
2357
|
-
"version": "1.0.3",
|
|
2358
|
-
"source": "curated",
|
|
2359
|
-
"type": "skill",
|
|
2360
|
-
"path": "skills/superpowers/test-driven-development",
|
|
2361
|
-
"title": "Superpowers Test Driven Development",
|
|
2362
|
-
"purpose": "Use when implementing any feature or bugfix, before writing implementation code",
|
|
2363
|
-
"whenToUse": "Use when implementing any feature or bugfix, before writing implementation code",
|
|
2364
|
-
"whenNotToUse": "Do not use for exploratory spikes, docs-only changes, or throwaway prototypes.",
|
|
2365
|
-
"tags": ["testing", "workflow"],
|
|
2366
|
-
"repoRoles": [],
|
|
2367
|
-
"tokenEstimate": 2254,
|
|
2368
|
-
"installMode": "copy-selected",
|
|
2369
|
-
"default": true,
|
|
2370
|
-
"reviewStatus": "approved",
|
|
2371
|
-
"riskLevel": "low",
|
|
2372
|
-
"useMode": "copy",
|
|
2373
|
-
"license": "MIT",
|
|
2374
|
-
"licenseConfidence": "high",
|
|
2375
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2376
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/test-driven-development",
|
|
2377
|
-
"ecosystem": "superpowers",
|
|
2378
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2379
|
-
},
|
|
2380
|
-
{
|
|
2381
|
-
"id": "haus.superpowers-using-git-worktrees",
|
|
2382
|
-
"version": "1.0.4",
|
|
2383
|
-
"source": "curated",
|
|
2384
|
-
"type": "skill",
|
|
2385
|
-
"path": "skills/superpowers/using-git-worktrees",
|
|
2386
|
-
"title": "Superpowers Using Git Worktrees",
|
|
2387
|
-
"purpose": "Use when starting feature work that needs isolation from current workspace or before executing implementation plans - ensures an isolated workspace exists via native tools or git worktree fallback",
|
|
2388
|
-
"whenToUse": "Use when starting feature work that needs isolation from current workspace or before executing implementation plans - ensures an isolated workspace exists via native tools or git worktree fallback",
|
|
2389
|
-
"whenNotToUse": "Do not use when current workspace isolation is already sufficient.",
|
|
2390
|
-
"tags": ["workflow", "workflow-skill"],
|
|
2391
|
-
"repoRoles": [],
|
|
2392
|
-
"tokenEstimate": 1848,
|
|
2393
|
-
"installMode": "copy-selected",
|
|
2394
|
-
"default": true,
|
|
2395
|
-
"reviewStatus": "approved",
|
|
2396
|
-
"riskLevel": "low",
|
|
2397
|
-
"useMode": "copy",
|
|
2398
|
-
"license": "MIT",
|
|
2399
|
-
"licenseConfidence": "high",
|
|
2400
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2401
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/using-git-worktrees",
|
|
2402
|
-
"ecosystem": "superpowers",
|
|
2403
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2404
|
-
},
|
|
2405
|
-
{
|
|
2406
|
-
"id": "haus.superpowers-using-superpowers",
|
|
2407
|
-
"version": "1.0.2",
|
|
2408
|
-
"source": "curated",
|
|
2409
|
-
"type": "skill",
|
|
2410
|
-
"path": "skills/superpowers/using-superpowers",
|
|
2411
|
-
"title": "Superpowers Using Superpowers",
|
|
2412
|
-
"purpose": "Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions",
|
|
2413
|
-
"whenToUse": "Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions",
|
|
2414
|
-
"whenNotToUse": "Do not skip when any skill might apply — this establishes the skill workflow.",
|
|
2415
|
-
"tags": ["workflow", "core-skill"],
|
|
2416
|
-
"repoRoles": [],
|
|
2417
|
-
"tokenEstimate": 1185,
|
|
2418
|
-
"installMode": "copy-selected",
|
|
2419
|
-
"default": true,
|
|
2420
|
-
"reviewStatus": "approved",
|
|
2421
|
-
"riskLevel": "low",
|
|
2422
|
-
"useMode": "copy",
|
|
2423
|
-
"license": "MIT",
|
|
2424
|
-
"licenseConfidence": "high",
|
|
2425
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2426
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/using-superpowers",
|
|
2427
|
-
"ecosystem": "superpowers",
|
|
2428
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2429
|
-
},
|
|
2430
|
-
{
|
|
2431
|
-
"id": "haus.superpowers-verification-before-completion",
|
|
2432
|
-
"version": "1.0.1",
|
|
2433
|
-
"source": "curated",
|
|
2434
|
-
"type": "skill",
|
|
2435
|
-
"path": "skills/superpowers/verification-before-completion",
|
|
2436
|
-
"title": "Superpowers Verification Before Completion",
|
|
2437
|
-
"purpose": "Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always",
|
|
2438
|
-
"whenToUse": "Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always",
|
|
2439
|
-
"whenNotToUse": "Do not use to claim success before running the relevant verification commands.",
|
|
2440
|
-
"tags": ["quality", "workflow"],
|
|
2441
|
-
"repoRoles": [],
|
|
2442
|
-
"tokenEstimate": 912,
|
|
2443
|
-
"installMode": "copy-selected",
|
|
2444
|
-
"default": true,
|
|
2445
|
-
"reviewStatus": "approved",
|
|
2446
|
-
"riskLevel": "low",
|
|
2447
|
-
"useMode": "copy",
|
|
2448
|
-
"license": "MIT",
|
|
2449
|
-
"licenseConfidence": "high",
|
|
2450
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2451
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/verification-before-completion",
|
|
2452
|
-
"ecosystem": "superpowers",
|
|
2453
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2454
|
-
},
|
|
2455
|
-
{
|
|
2456
|
-
"id": "haus.superpowers-writing-plans",
|
|
2457
|
-
"version": "1.0.3",
|
|
2458
|
-
"source": "curated",
|
|
2459
|
-
"type": "skill",
|
|
2460
|
-
"path": "skills/superpowers/writing-plans",
|
|
2461
|
-
"title": "Superpowers Writing Plans",
|
|
2462
|
-
"purpose": "Use when you have a spec or requirements for a multi-step task, before touching code",
|
|
2463
|
-
"whenToUse": "Use when you have a spec or requirements for a multi-step task, before touching code",
|
|
2464
|
-
"whenNotToUse": "Do not use for single-step tasks that need no multi-step plan.",
|
|
2465
|
-
"tags": ["workflow", "core-skill"],
|
|
2466
|
-
"repoRoles": [],
|
|
2467
|
-
"tokenEstimate": 5850,
|
|
2468
|
-
"installMode": "copy-selected",
|
|
2469
|
-
"default": true,
|
|
2470
|
-
"reviewStatus": "approved",
|
|
2471
|
-
"riskLevel": "low",
|
|
2472
|
-
"useMode": "copy",
|
|
2473
|
-
"license": "MIT",
|
|
2474
|
-
"licenseConfidence": "high",
|
|
2475
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2476
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/fc6d376756b605f35c399b1b00a16bb27ec55171/skills/writing-plans",
|
|
2477
|
-
"ecosystem": "superpowers",
|
|
2478
|
-
"pinnedRef": "fc6d376756b605f35c399b1b00a16bb27ec55171"
|
|
2479
|
-
},
|
|
2480
|
-
{
|
|
2481
|
-
"id": "haus.superpowers-writing-skills",
|
|
2482
|
-
"version": "1.0.3",
|
|
2483
|
-
"source": "curated",
|
|
2484
|
-
"type": "skill",
|
|
2485
|
-
"path": "skills/superpowers/writing-skills",
|
|
2486
|
-
"title": "Superpowers Writing Skills",
|
|
2487
|
-
"purpose": "Use when creating new skills, editing existing skills, or verifying skills work before deployment",
|
|
2488
|
-
"whenToUse": "Use when creating new skills, editing existing skills, or verifying skills work before deployment",
|
|
2489
|
-
"whenNotToUse": "Do not use for routine code changes unrelated to skill authoring.",
|
|
2490
|
-
"tags": ["workflow", "workflow-skill"],
|
|
2491
|
-
"repoRoles": [],
|
|
2492
|
-
"tokenEstimate": 5555,
|
|
2493
|
-
"installMode": "copy-selected",
|
|
2494
|
-
"default": true,
|
|
2495
|
-
"reviewStatus": "approved",
|
|
2496
|
-
"riskLevel": "low",
|
|
2497
|
-
"useMode": "copy",
|
|
2498
|
-
"license": "MIT",
|
|
2499
|
-
"licenseConfidence": "high",
|
|
2500
|
-
"originSourceId": "superpowers-pcvelz",
|
|
2501
|
-
"originUrl": "https://github.com/pcvelz/superpowers/tree/e748382ff059bcc0219dae8cc1059ef7f3a938d5/skills/writing-skills",
|
|
2502
|
-
"ecosystem": "superpowers",
|
|
2503
|
-
"pinnedRef": "e748382ff059bcc0219dae8cc1059ef7f3a938d5"
|
|
2504
|
-
},
|
|
2505
2114
|
{
|
|
2506
2115
|
"id": "haus.tanstack-query-router-patterns",
|
|
2507
2116
|
"version": "1.0.2",
|
|
@@ -3346,7 +2955,7 @@
|
|
|
3346
2955
|
"purpose": "Write modern, high-performance C# code using records, pattern matching, value objects, async/await, Span<T>/Memory<T>, and best-practice API design patterns. Emphasizes functional-style programming with C# 12+ features.",
|
|
3347
2956
|
"whenToUse": "Write modern, high-performance C# code using records, pattern matching, value objects, async/await, Span<T>/Memory<T>, and best-practice API design patterns. Emphasizes functional-style programming with C# 12+ features.",
|
|
3348
2957
|
"whenNotToUse": "Do not use when a more specific skill or command applies.",
|
|
3349
|
-
"tags": ["dotnet", "csharp"
|
|
2958
|
+
"tags": ["dotnet", "csharp"],
|
|
3350
2959
|
"repoRoles": [],
|
|
3351
2960
|
"tokenEstimate": 2664,
|
|
3352
2961
|
"installMode": "copy-selected",
|