@haus-tech/haus-workflow 0.8.0 → 0.10.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 +16 -0
- package/README.md +13 -16
- package/dist/cli.js +104 -64
- package/library/catalog/allowed-stacks.json +5 -1
- package/library/catalog/manifest.json +121 -0
- package/package.json +1 -2
- package/tests/fixtures/catalog/manifest.json +65 -0
- package/tests/fixtures/catalog/skills/qliro-patterns/SKILL.md +9 -0
- package/tests/fixtures/catalog/skills/stripe-patterns/SKILL.md +9 -0
- package/tests/fixtures/catalog/skills/supabase-patterns/SKILL.md +9 -0
- package/docs/user-guide.md +0 -176
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.9.0...v0.10.0) (2026-05-29)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* Add config command to manage hooks ([1f2c7b4](https://github.com/WeAreHausTech/haus-workflow/commit/1f2c7b4ced3437ef4468759ec7bfc4d8adbf3efc))
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* address PR review — remove stale module refs, fix plugin-era wording in docs and commands ([6520321](https://github.com/WeAreHausTech/haus-workflow/commit/6520321e1790a304036984c387fdf59d4febe3dd))
|
|
12
|
+
|
|
13
|
+
## [0.9.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.8.0...v0.9.0) (2026-05-28)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **scanner:** detect stripe, qliro, supabase (T26-T28) ([b2585b0](https://github.com/WeAreHausTech/haus-workflow/commit/b2585b027621be1442533d12255523f8361d967b))
|
|
18
|
+
|
|
3
19
|
## [0.8.0](https://github.com/WeAreHausTech/haus-workflow/compare/v0.7.0...v0.8.0) (2026-05-28)
|
|
4
20
|
|
|
5
21
|
### Features
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# haus
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CLI that scans a project, recommends AI context assets for the stack, and writes controlled outputs into `.claude/` and `.haus-workflow/`.
|
|
4
4
|
|
|
5
|
-
> **Internal Haus tool.**
|
|
5
|
+
> **Internal Haus tool.**
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -27,7 +27,7 @@ Run once inside each project:
|
|
|
27
27
|
haus init
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Scans the repo, recommends context assets, and writes `.claude/` and `.haus-workflow/`.
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
@@ -35,35 +35,32 @@ This scans the repo, recommends context assets, and writes `.claude/` and `.haus
|
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
37
|
haus init # first-run setup (scan → recommend → apply)
|
|
38
|
-
haus setup-project # re-run setup on
|
|
38
|
+
haus setup-project # re-run setup on existing project
|
|
39
|
+
haus scan # scan repo and write context-map
|
|
40
|
+
haus recommend # score and recommend catalog items
|
|
39
41
|
haus apply --dry-run # preview what would be written
|
|
40
42
|
haus apply --write # write .claude/ files
|
|
41
43
|
haus update # sync remote catalog + refresh lockfile
|
|
42
44
|
haus update --check # check for updates without applying
|
|
43
45
|
haus doctor # health check: hooks, CLAUDE.md, catalog cache
|
|
46
|
+
haus config # manage hook configuration
|
|
47
|
+
haus memory # view project memory store
|
|
48
|
+
haus guard # test bash/file-access guards
|
|
44
49
|
haus uninstall # remove Haus-managed files from ~/.claude/
|
|
45
50
|
```
|
|
46
51
|
|
|
47
52
|
---
|
|
48
53
|
|
|
49
|
-
##
|
|
54
|
+
## Development
|
|
50
55
|
|
|
51
56
|
```bash
|
|
52
57
|
yarn install
|
|
53
58
|
yarn verify # typecheck + lint + build + test
|
|
59
|
+
yarn dev <cmd> # run CLI without building (tsx)
|
|
54
60
|
```
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
### Internal docs
|
|
57
63
|
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Docs
|
|
61
|
-
|
|
62
|
-
- [User guide](docs/user-guide.md)
|
|
63
64
|
- [Architecture](docs/architecture.md)
|
|
64
|
-
- [
|
|
65
|
-
- [Global install layout](docs/global-install.md)
|
|
66
|
-
- [Generated files](docs/generated-files.md)
|
|
67
|
-
- [Updates and lockfile](docs/updates.md)
|
|
65
|
+
- [CLI reference](docs/cli.md)
|
|
68
66
|
- [Security](docs/security.md)
|
|
69
|
-
- [Memory](docs/memory.md)
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { readFileSync as readFileSync3 } from "fs";
|
|
5
|
-
import
|
|
5
|
+
import path27 from "path";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
|
|
8
8
|
// src/commands/apply.ts
|
|
@@ -903,6 +903,34 @@ async function runCatalogAudit() {
|
|
|
903
903
|
log("Catalog audit passed.");
|
|
904
904
|
}
|
|
905
905
|
|
|
906
|
+
// src/commands/config.ts
|
|
907
|
+
import path12 from "path";
|
|
908
|
+
var CONFIG_PATH2 = ".haus-workflow/config.json";
|
|
909
|
+
var HOOK_ALIASES = {
|
|
910
|
+
"hook.context": "context",
|
|
911
|
+
"hook.memory": "memoryInject"
|
|
912
|
+
};
|
|
913
|
+
async function runConfig(key, action) {
|
|
914
|
+
const hookKey = HOOK_ALIASES[key];
|
|
915
|
+
if (!hookKey) {
|
|
916
|
+
throw new Error(`Unknown config key "${key}". Valid keys: ${Object.keys(HOOK_ALIASES).join(", ")}`);
|
|
917
|
+
}
|
|
918
|
+
const root = process.cwd();
|
|
919
|
+
const configPath = path12.join(root, CONFIG_PATH2);
|
|
920
|
+
const existing = await readJson(configPath);
|
|
921
|
+
const cfg = existing ?? structuredClone(DEFAULT_HOOKS_CONFIG);
|
|
922
|
+
cfg.hooks ??= {};
|
|
923
|
+
cfg.hooks[hookKey] ??= {};
|
|
924
|
+
if (action === "status") {
|
|
925
|
+
const enabled = cfg.hooks[hookKey]?.enabled === true;
|
|
926
|
+
log(`${key}: ${enabled ? "enabled" : "disabled"}`);
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
cfg.hooks[hookKey].enabled = action === "enable";
|
|
930
|
+
await writeJson(configPath, cfg);
|
|
931
|
+
log(`${key} ${action}d`);
|
|
932
|
+
}
|
|
933
|
+
|
|
906
934
|
// src/recommender/explain-recommendation.ts
|
|
907
935
|
function normalizeRecommendation(input2) {
|
|
908
936
|
const recommended = (input2.recommended ?? []).map((item) => {
|
|
@@ -1241,7 +1269,7 @@ function computeRuleIntents(rule) {
|
|
|
1241
1269
|
|
|
1242
1270
|
// src/scanner/scan-project.ts
|
|
1243
1271
|
import { readFile } from "fs/promises";
|
|
1244
|
-
import
|
|
1272
|
+
import path14 from "path";
|
|
1245
1273
|
|
|
1246
1274
|
// src/utils/audit-checks.ts
|
|
1247
1275
|
function isRecord(v) {
|
|
@@ -1268,7 +1296,7 @@ function compareVersions(a, b) {
|
|
|
1268
1296
|
}
|
|
1269
1297
|
|
|
1270
1298
|
// src/scanner/detect-package-manager.ts
|
|
1271
|
-
import
|
|
1299
|
+
import path13 from "path";
|
|
1272
1300
|
import fs9 from "fs-extra";
|
|
1273
1301
|
function detectPackageManager(root, packageManagerField) {
|
|
1274
1302
|
const field = String(packageManagerField ?? "").trim();
|
|
@@ -1287,9 +1315,9 @@ function detectPackageManager(root, packageManagerField) {
|
|
|
1287
1315
|
if (satisfiesVersion(version, ">=9")) return "npm";
|
|
1288
1316
|
return "unknown";
|
|
1289
1317
|
}
|
|
1290
|
-
if (fs9.existsSync(
|
|
1291
|
-
if (fs9.existsSync(
|
|
1292
|
-
if (fs9.existsSync(
|
|
1318
|
+
if (fs9.existsSync(path13.join(root, "yarn.lock"))) return "yarn";
|
|
1319
|
+
if (fs9.existsSync(path13.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
1320
|
+
if (fs9.existsSync(path13.join(root, "package-lock.json"))) return "npm";
|
|
1293
1321
|
return "unknown";
|
|
1294
1322
|
}
|
|
1295
1323
|
|
|
@@ -1350,8 +1378,8 @@ function blocked(rel) {
|
|
|
1350
1378
|
return SENSITIVE.some((x) => x.test(rel));
|
|
1351
1379
|
}
|
|
1352
1380
|
async function scanProject(root, mode = "fast") {
|
|
1353
|
-
const pkg = await readJson(
|
|
1354
|
-
const composer = await readJson(
|
|
1381
|
+
const pkg = await readJson(path14.join(root, "package.json"));
|
|
1382
|
+
const composer = await readJson(path14.join(root, "composer.json"));
|
|
1355
1383
|
const files = await listFiles(root, SAFE_FILES);
|
|
1356
1384
|
const safeFiles = files.filter((f) => !blocked(f));
|
|
1357
1385
|
const deps = dependencySet(pkg, composer);
|
|
@@ -1377,7 +1405,7 @@ async function scanProject(root, mode = "fast") {
|
|
|
1377
1405
|
mode,
|
|
1378
1406
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1379
1407
|
root,
|
|
1380
|
-
repoName: String(pkg?.name ??
|
|
1408
|
+
repoName: String(pkg?.name ?? path14.basename(root)),
|
|
1381
1409
|
packageManager,
|
|
1382
1410
|
repoRoles: roles,
|
|
1383
1411
|
confidence: computeConfidence(roles, stacks),
|
|
@@ -1392,7 +1420,7 @@ async function scanProject(root, mode = "fast") {
|
|
|
1392
1420
|
composer: isRecord(composer?.require) ? Object.keys(composer.require) : []
|
|
1393
1421
|
};
|
|
1394
1422
|
const scanHashes = Object.fromEntries(
|
|
1395
|
-
await Promise.all(safeFiles.map(async (f) => [f, hashText(await readFile(
|
|
1423
|
+
await Promise.all(safeFiles.map(async (f) => [f, hashText(await readFile(path14.join(root, f), "utf8"))]))
|
|
1396
1424
|
);
|
|
1397
1425
|
const repoSummary = renderSummary(context);
|
|
1398
1426
|
await writeJson(hausPath(root, "context-map.json"), context);
|
|
@@ -1490,6 +1518,13 @@ async function detectStacks(root, deps, files, packageManager) {
|
|
|
1490
1518
|
if (deps.includes("deployer/deployer")) add("tooling", "deployer-php");
|
|
1491
1519
|
if (!deps.includes("prettier")) add("tooling", "missing-prettier");
|
|
1492
1520
|
if (!deps.includes("eslint")) add("tooling", "missing-eslint");
|
|
1521
|
+
if (deps.includes("@stripe/stripe-js") || deps.includes("@stripe/react-stripe-js")) {
|
|
1522
|
+
add("tooling", "stripe");
|
|
1523
|
+
}
|
|
1524
|
+
if (deps.includes("@haus-tech/qliro-plugin")) add("tooling", "qliro");
|
|
1525
|
+
if (deps.includes("@supabase/supabase-js") || deps.some((d) => d.startsWith("@supabase/"))) {
|
|
1526
|
+
add("databases", "supabase");
|
|
1527
|
+
}
|
|
1493
1528
|
if (deps.includes("@vendure/core")) add("backend", "vendure3");
|
|
1494
1529
|
if (deps.includes("@nestjs/core")) add("backend", "nestjs");
|
|
1495
1530
|
if (await hasNeedle(root, files, "NestFactory")) add("backend", "nestjs");
|
|
@@ -1541,7 +1576,7 @@ async function hasNeedle(root, files, needle) {
|
|
|
1541
1576
|
);
|
|
1542
1577
|
for (const rel of candidates.slice(0, 300)) {
|
|
1543
1578
|
try {
|
|
1544
|
-
const content = await readFile(
|
|
1579
|
+
const content = await readFile(path14.join(root, rel), "utf8");
|
|
1545
1580
|
if (content.includes(needle)) return true;
|
|
1546
1581
|
} catch {
|
|
1547
1582
|
continue;
|
|
@@ -1634,7 +1669,7 @@ async function runContext(options) {
|
|
|
1634
1669
|
}
|
|
1635
1670
|
|
|
1636
1671
|
// src/commands/doctor.ts
|
|
1637
|
-
import
|
|
1672
|
+
import path15 from "path";
|
|
1638
1673
|
import fs10 from "fs-extra";
|
|
1639
1674
|
|
|
1640
1675
|
// src/update/npm-version.ts
|
|
@@ -1702,7 +1737,7 @@ async function runDoctor(options) {
|
|
|
1702
1737
|
const enabled = await isHookEnabled(root, key);
|
|
1703
1738
|
log(`- HOOK ${key}: ${enabled ? "enabled" : "disabled (default)"}`);
|
|
1704
1739
|
}
|
|
1705
|
-
const rootClaudeMdPath =
|
|
1740
|
+
const rootClaudeMdPath = path15.join(root, "CLAUDE.md");
|
|
1706
1741
|
const rootClaudeMdContent = await readText(rootClaudeMdPath);
|
|
1707
1742
|
if (!rootClaudeMdContent) {
|
|
1708
1743
|
warn("- CLAUDE.md: missing (run `haus apply --write` to create)");
|
|
@@ -1722,7 +1757,7 @@ async function runDoctor(options) {
|
|
|
1722
1757
|
warn("- .haus-workflow/haus-way-of-work.md: no HAUS-MANAGED header (user-owned)");
|
|
1723
1758
|
} else {
|
|
1724
1759
|
const storedHashMatch = firstLine.match(/hash=(sha256-[a-f0-9]+)/);
|
|
1725
|
-
const templatePath =
|
|
1760
|
+
const templatePath = path15.join(packageRoot(), "library", "global", "templates", "haus-way-of-work.md");
|
|
1726
1761
|
const templateContent = await readText(templatePath);
|
|
1727
1762
|
if (storedHashMatch && templateContent) {
|
|
1728
1763
|
const currentHash = hashText(templateContent);
|
|
@@ -1760,7 +1795,7 @@ async function runDoctor(options) {
|
|
|
1760
1795
|
log(`- CATALOG CACHE: OK (${cacheAgeDays}d old)`);
|
|
1761
1796
|
}
|
|
1762
1797
|
}
|
|
1763
|
-
const pkgJson = await readJson(
|
|
1798
|
+
const pkgJson = await readJson(path15.join(packageRoot(), "package.json"));
|
|
1764
1799
|
const currentVersion = pkgJson?.version ?? "0.0.0";
|
|
1765
1800
|
const npmStatus = await fetchNpmVersionStatus(currentVersion);
|
|
1766
1801
|
if (npmStatus.updateAvailable && npmStatus.latest !== null) {
|
|
@@ -1925,7 +1960,7 @@ async function runGuard(kind, _options) {
|
|
|
1925
1960
|
}
|
|
1926
1961
|
|
|
1927
1962
|
// src/commands/init.ts
|
|
1928
|
-
import
|
|
1963
|
+
import path16 from "path";
|
|
1929
1964
|
import fs11 from "fs-extra";
|
|
1930
1965
|
|
|
1931
1966
|
// src/utils/exec.ts
|
|
@@ -1934,6 +1969,7 @@ async function runCommand(command, args = [], options = {}) {
|
|
|
1934
1969
|
try {
|
|
1935
1970
|
const result = await execa(command, args, {
|
|
1936
1971
|
reject: false,
|
|
1972
|
+
// non-zero exits are returned, not thrown
|
|
1937
1973
|
...options
|
|
1938
1974
|
});
|
|
1939
1975
|
return {
|
|
@@ -2447,7 +2483,7 @@ async function runSetupProject(options) {
|
|
|
2447
2483
|
// src/commands/init.ts
|
|
2448
2484
|
async function runInit(options) {
|
|
2449
2485
|
const root = process.cwd();
|
|
2450
|
-
const hausDir =
|
|
2486
|
+
const hausDir = path16.join(root, ".haus-workflow");
|
|
2451
2487
|
const alreadyInit = await fs11.pathExists(hausDir);
|
|
2452
2488
|
if (alreadyInit) {
|
|
2453
2489
|
log("Haus AI already initialized in this project.");
|
|
@@ -2460,7 +2496,7 @@ async function runInit(options) {
|
|
|
2460
2496
|
|
|
2461
2497
|
// src/install/apply.ts
|
|
2462
2498
|
import crypto2 from "crypto";
|
|
2463
|
-
import
|
|
2499
|
+
import path19 from "path";
|
|
2464
2500
|
import fs13 from "fs-extra";
|
|
2465
2501
|
|
|
2466
2502
|
// src/install/header.ts
|
|
@@ -2495,13 +2531,13 @@ ${content}`;
|
|
|
2495
2531
|
|
|
2496
2532
|
// src/install/manifest.ts
|
|
2497
2533
|
import os5 from "os";
|
|
2498
|
-
import
|
|
2534
|
+
import path17 from "path";
|
|
2499
2535
|
var MANIFEST_SCHEMA = "haus-install-manifest/1";
|
|
2500
2536
|
function globalClaudeDir() {
|
|
2501
|
-
return
|
|
2537
|
+
return path17.join(os5.homedir(), ".claude");
|
|
2502
2538
|
}
|
|
2503
2539
|
function hausManifestPath() {
|
|
2504
|
-
return
|
|
2540
|
+
return path17.join(globalClaudeDir(), "haus", "install-manifest.json");
|
|
2505
2541
|
}
|
|
2506
2542
|
async function readManifest() {
|
|
2507
2543
|
return readJson(hausManifestPath());
|
|
@@ -2520,10 +2556,10 @@ function buildManifest(source, files, hooks) {
|
|
|
2520
2556
|
}
|
|
2521
2557
|
|
|
2522
2558
|
// src/install/settings-merge.ts
|
|
2523
|
-
import
|
|
2559
|
+
import path18 from "path";
|
|
2524
2560
|
import fs12 from "fs-extra";
|
|
2525
2561
|
function settingsJsonPath() {
|
|
2526
|
-
return
|
|
2562
|
+
return path18.join(globalClaudeDir(), "settings.json");
|
|
2527
2563
|
}
|
|
2528
2564
|
async function readSettings() {
|
|
2529
2565
|
const parsed = await readJson(settingsJsonPath());
|
|
@@ -2594,7 +2630,7 @@ function hashContent(content) {
|
|
|
2594
2630
|
}
|
|
2595
2631
|
function sourceVersion() {
|
|
2596
2632
|
try {
|
|
2597
|
-
const pkgPath =
|
|
2633
|
+
const pkgPath = path19.join(packageRoot(), "package.json");
|
|
2598
2634
|
const pkg = JSON.parse(fs13.readFileSync(pkgPath, "utf8"));
|
|
2599
2635
|
return `${pkg.name ?? "haus"}@${pkg.version ?? "0.0.0"}`;
|
|
2600
2636
|
} catch {
|
|
@@ -2602,32 +2638,32 @@ function sourceVersion() {
|
|
|
2602
2638
|
}
|
|
2603
2639
|
}
|
|
2604
2640
|
function globalSrcDir() {
|
|
2605
|
-
return
|
|
2641
|
+
return path19.join(packageRoot(), "library", "global");
|
|
2606
2642
|
}
|
|
2607
2643
|
function collectSourceFiles(srcDir, claudeDir) {
|
|
2608
2644
|
const entries = [];
|
|
2609
|
-
const skillsDir =
|
|
2645
|
+
const skillsDir = path19.join(srcDir, "skills");
|
|
2610
2646
|
if (fs13.pathExistsSync(skillsDir)) {
|
|
2611
2647
|
for (const skillName of fs13.readdirSync(skillsDir)) {
|
|
2612
|
-
const skillFile =
|
|
2648
|
+
const skillFile = path19.join(skillsDir, skillName, "SKILL.md");
|
|
2613
2649
|
if (fs13.pathExistsSync(skillFile)) {
|
|
2614
2650
|
entries.push({
|
|
2615
2651
|
stableId: `skill.${skillName}`,
|
|
2616
|
-
srcRelPath:
|
|
2617
|
-
destPath:
|
|
2652
|
+
srcRelPath: path19.join("library", "global", "skills", skillName, "SKILL.md"),
|
|
2653
|
+
destPath: path19.join(claudeDir, "skills", skillName, "SKILL.md")
|
|
2618
2654
|
});
|
|
2619
2655
|
}
|
|
2620
2656
|
}
|
|
2621
2657
|
}
|
|
2622
|
-
const agentsDir =
|
|
2658
|
+
const agentsDir = path19.join(srcDir, "agents");
|
|
2623
2659
|
if (fs13.pathExistsSync(agentsDir)) {
|
|
2624
2660
|
for (const agentFile of fs13.readdirSync(agentsDir)) {
|
|
2625
2661
|
if (!agentFile.endsWith(".md")) continue;
|
|
2626
2662
|
const agentName = agentFile.replace(/\.md$/, "");
|
|
2627
2663
|
entries.push({
|
|
2628
2664
|
stableId: `agent.${agentName}`,
|
|
2629
|
-
srcRelPath:
|
|
2630
|
-
destPath:
|
|
2665
|
+
srcRelPath: path19.join("library", "global", "agents", agentFile),
|
|
2666
|
+
destPath: path19.join(claudeDir, "agents", agentFile)
|
|
2631
2667
|
});
|
|
2632
2668
|
}
|
|
2633
2669
|
}
|
|
@@ -2651,7 +2687,7 @@ async function applyInstall(options = {}) {
|
|
|
2651
2687
|
};
|
|
2652
2688
|
const manifestFiles = [];
|
|
2653
2689
|
for (const entry of sourceFiles) {
|
|
2654
|
-
const srcPath =
|
|
2690
|
+
const srcPath = path19.join(packageRoot(), entry.srcRelPath);
|
|
2655
2691
|
const rawContent = await readText(srcPath);
|
|
2656
2692
|
if (rawContent === void 0) {
|
|
2657
2693
|
warn(`Source file not found: ${entry.srcRelPath}`);
|
|
@@ -2707,7 +2743,7 @@ async function applyInstall(options = {}) {
|
|
|
2707
2743
|
schemaVersion: SCHEMA_VERSION3
|
|
2708
2744
|
});
|
|
2709
2745
|
}
|
|
2710
|
-
const fragmentPath =
|
|
2746
|
+
const fragmentPath = path19.join(srcDir, "settings-fragments", "hooks.json");
|
|
2711
2747
|
const fragments = await loadHooksFragment(fragmentPath);
|
|
2712
2748
|
const settings = await readSettings();
|
|
2713
2749
|
const { settings: mergedSettings, addedIds } = mergeHooks(settings, fragments);
|
|
@@ -2892,12 +2928,12 @@ async function runScan(options) {
|
|
|
2892
2928
|
}
|
|
2893
2929
|
|
|
2894
2930
|
// src/commands/undo.ts
|
|
2895
|
-
import
|
|
2931
|
+
import path20 from "path";
|
|
2896
2932
|
import fs14 from "fs-extra";
|
|
2897
2933
|
var CLAUDE_DIR = ".claude";
|
|
2898
2934
|
async function runUndo(options) {
|
|
2899
2935
|
const root = process.cwd();
|
|
2900
|
-
const targets = [
|
|
2936
|
+
const targets = [path20.join(root, CLAUDE_DIR), path20.join(root, HAUS_DIR)];
|
|
2901
2937
|
const existing = targets.filter((p) => fs14.existsSync(p));
|
|
2902
2938
|
if (existing.length === 0) {
|
|
2903
2939
|
log("Nothing to remove: no .claude/ or .haus-workflow/ in this directory.");
|
|
@@ -2905,7 +2941,7 @@ async function runUndo(options) {
|
|
|
2905
2941
|
}
|
|
2906
2942
|
if (!options.yes) {
|
|
2907
2943
|
const ok = await confirm(
|
|
2908
|
-
`Remove ${existing.map((p) =>
|
|
2944
|
+
`Remove ${existing.map((p) => path20.relative(root, p)).join(" and ")}? This cannot be undone.`
|
|
2909
2945
|
);
|
|
2910
2946
|
if (!ok) {
|
|
2911
2947
|
log("Cancelled.");
|
|
@@ -2914,13 +2950,13 @@ async function runUndo(options) {
|
|
|
2914
2950
|
}
|
|
2915
2951
|
for (const p of existing) {
|
|
2916
2952
|
await fs14.remove(p);
|
|
2917
|
-
log(`Removed ${
|
|
2953
|
+
log(`Removed ${path20.relative(root, p)}`);
|
|
2918
2954
|
}
|
|
2919
2955
|
}
|
|
2920
2956
|
|
|
2921
2957
|
// src/install/uninstall.ts
|
|
2922
2958
|
import crypto3 from "crypto";
|
|
2923
|
-
import
|
|
2959
|
+
import path21 from "path";
|
|
2924
2960
|
import fs15 from "fs-extra";
|
|
2925
2961
|
async function runUninstall(options = {}) {
|
|
2926
2962
|
const { force = false } = options;
|
|
@@ -2948,14 +2984,14 @@ async function runUninstall(options = {}) {
|
|
|
2948
2984
|
continue;
|
|
2949
2985
|
}
|
|
2950
2986
|
await fs15.remove(entry.destPath);
|
|
2951
|
-
await pruneEmptyDir(
|
|
2987
|
+
await pruneEmptyDir(path21.dirname(entry.destPath));
|
|
2952
2988
|
result.deleted.push(entry.destPath);
|
|
2953
2989
|
}
|
|
2954
2990
|
const settings = await readSettings();
|
|
2955
2991
|
const stripped = stripHausHooks(settings);
|
|
2956
2992
|
await writeSettings(stripped);
|
|
2957
2993
|
result.hooksStripped = true;
|
|
2958
|
-
const hausDir =
|
|
2994
|
+
const hausDir = path21.join(globalClaudeDir(), "haus");
|
|
2959
2995
|
const manifestPath = hausManifestPath();
|
|
2960
2996
|
if (fs15.pathExistsSync(manifestPath)) {
|
|
2961
2997
|
await fs15.remove(manifestPath);
|
|
@@ -3000,7 +3036,7 @@ async function runUninstallCommand(options) {
|
|
|
3000
3036
|
}
|
|
3001
3037
|
|
|
3002
3038
|
// src/commands/update.ts
|
|
3003
|
-
import
|
|
3039
|
+
import path23 from "path";
|
|
3004
3040
|
|
|
3005
3041
|
// src/update/diff-generated-files.ts
|
|
3006
3042
|
function diffGeneratedFiles() {
|
|
@@ -3027,7 +3063,7 @@ function summarizeLockDiff(before, after) {
|
|
|
3027
3063
|
|
|
3028
3064
|
// src/update/lockfile.ts
|
|
3029
3065
|
import { mkdir, readFile as readFile2, copyFile } from "fs/promises";
|
|
3030
|
-
import
|
|
3066
|
+
import path22 from "path";
|
|
3031
3067
|
async function checkLock(root) {
|
|
3032
3068
|
const lock = await readJson(hausPath(root, "haus.lock.json")) ?? [];
|
|
3033
3069
|
const hasValidVersions = lock.every((item) => !item.version || normalizeVersion(item.version) !== null);
|
|
@@ -3046,7 +3082,7 @@ async function applyLock(root) {
|
|
|
3046
3082
|
try {
|
|
3047
3083
|
const backupDir = hausPath(root, "backups");
|
|
3048
3084
|
await mkdir(backupDir, { recursive: true });
|
|
3049
|
-
await copyFile(lockPath,
|
|
3085
|
+
await copyFile(lockPath, path22.join(backupDir, `haus.lock.${Date.now()}.json`));
|
|
3050
3086
|
} catch {
|
|
3051
3087
|
}
|
|
3052
3088
|
const enriched = await Promise.all(
|
|
@@ -3068,7 +3104,7 @@ function diffLock(before, after) {
|
|
|
3068
3104
|
}
|
|
3069
3105
|
async function hasLocalOverrides(root) {
|
|
3070
3106
|
try {
|
|
3071
|
-
await readFile2(
|
|
3107
|
+
await readFile2(path22.join(root, ".claude", "settings.json"), "utf8");
|
|
3072
3108
|
return true;
|
|
3073
3109
|
} catch {
|
|
3074
3110
|
return false;
|
|
@@ -3080,7 +3116,7 @@ var NPM_PACKAGE_NAME2 = "@haus-tech/haus-workflow";
|
|
|
3080
3116
|
async function runUpdate(options) {
|
|
3081
3117
|
const root = process.cwd();
|
|
3082
3118
|
if (options.check) {
|
|
3083
|
-
const pkgJson2 = await readJson(
|
|
3119
|
+
const pkgJson2 = await readJson(path23.join(packageRoot(), "package.json"));
|
|
3084
3120
|
const currentVersion2 = pkgJson2?.version ?? "0.0.0";
|
|
3085
3121
|
const [status, npmVersion, latestCatalogTag] = await Promise.all([
|
|
3086
3122
|
checkLock(root),
|
|
@@ -3107,7 +3143,7 @@ async function runUpdate(options) {
|
|
|
3107
3143
|
if (!status.ok) process.exitCode = 1;
|
|
3108
3144
|
return;
|
|
3109
3145
|
}
|
|
3110
|
-
const pkgJson = await readJson(
|
|
3146
|
+
const pkgJson = await readJson(path23.join(packageRoot(), "package.json"));
|
|
3111
3147
|
const currentVersion = pkgJson?.version ?? "0.0.0";
|
|
3112
3148
|
const npmStatus = await fetchNpmVersionStatus(currentVersion);
|
|
3113
3149
|
if (npmStatus.updateAvailable && npmStatus.latest !== null) {
|
|
@@ -3138,12 +3174,12 @@ async function runUpdate(options) {
|
|
|
3138
3174
|
|
|
3139
3175
|
// src/commands/validate-catalog.ts
|
|
3140
3176
|
import fs16 from "fs";
|
|
3141
|
-
import
|
|
3177
|
+
import path25 from "path";
|
|
3142
3178
|
|
|
3143
3179
|
// src/catalog/allowed-stacks.ts
|
|
3144
|
-
import
|
|
3180
|
+
import path24 from "path";
|
|
3145
3181
|
async function readAllowedStacks(root) {
|
|
3146
|
-
const data = await readJson(
|
|
3182
|
+
const data = await readJson(path24.join(root, "library", "catalog", "allowed-stacks.json"));
|
|
3147
3183
|
return data?.stacks ?? [];
|
|
3148
3184
|
}
|
|
3149
3185
|
|
|
@@ -3242,11 +3278,11 @@ function auditShippedFiles(manifestDir, items) {
|
|
|
3242
3278
|
const failures = [];
|
|
3243
3279
|
for (const item of items) {
|
|
3244
3280
|
if (!item.path) continue;
|
|
3245
|
-
const absPath =
|
|
3281
|
+
const absPath = path25.join(manifestDir, item.path);
|
|
3246
3282
|
if (item.type === "skill") {
|
|
3247
|
-
const skillMd =
|
|
3283
|
+
const skillMd = path25.join(absPath, "SKILL.md");
|
|
3248
3284
|
if (!fs16.existsSync(skillMd)) {
|
|
3249
|
-
failures.push(`${item.id}: missing ${
|
|
3285
|
+
failures.push(`${item.id}: missing ${path25.relative(manifestDir, skillMd)}`);
|
|
3250
3286
|
continue;
|
|
3251
3287
|
}
|
|
3252
3288
|
const text = fs16.readFileSync(skillMd, "utf8");
|
|
@@ -3279,11 +3315,11 @@ function auditMarkdownContent(manifestDir) {
|
|
|
3279
3315
|
const failures = [];
|
|
3280
3316
|
const dirs = ["skills", "agents"];
|
|
3281
3317
|
for (const dir of dirs) {
|
|
3282
|
-
const abs =
|
|
3318
|
+
const abs = path25.join(manifestDir, dir);
|
|
3283
3319
|
if (!fs16.existsSync(abs)) continue;
|
|
3284
3320
|
walkMd(abs, (file) => {
|
|
3285
3321
|
const text = fs16.readFileSync(file, "utf8");
|
|
3286
|
-
const rel =
|
|
3322
|
+
const rel = path25.relative(manifestDir, file);
|
|
3287
3323
|
const lines = text.split(/\r?\n/);
|
|
3288
3324
|
for (let i = 0; i < lines.length; i++) {
|
|
3289
3325
|
const line = lines[i] ?? "";
|
|
@@ -3303,7 +3339,7 @@ function auditMarkdownContent(manifestDir) {
|
|
|
3303
3339
|
}
|
|
3304
3340
|
function walkMd(dir, fn) {
|
|
3305
3341
|
for (const entry of fs16.readdirSync(dir, { withFileTypes: true })) {
|
|
3306
|
-
const full =
|
|
3342
|
+
const full = path25.join(dir, entry.name);
|
|
3307
3343
|
if (entry.isDirectory()) walkMd(full, fn);
|
|
3308
3344
|
else if (entry.name.endsWith(".md")) fn(full);
|
|
3309
3345
|
}
|
|
@@ -3314,8 +3350,8 @@ async function runValidateCatalog(manifestPath) {
|
|
|
3314
3350
|
process.exitCode = 1;
|
|
3315
3351
|
return;
|
|
3316
3352
|
}
|
|
3317
|
-
const abs =
|
|
3318
|
-
const manifestDir =
|
|
3353
|
+
const abs = path25.resolve(process.cwd(), manifestPath);
|
|
3354
|
+
const manifestDir = path25.dirname(abs);
|
|
3319
3355
|
const data = await readJson(abs);
|
|
3320
3356
|
if (!data?.items) {
|
|
3321
3357
|
error(`Could not read catalog manifest at ${abs}`);
|
|
@@ -3348,7 +3384,7 @@ async function runValidateCatalog(manifestPath) {
|
|
|
3348
3384
|
}
|
|
3349
3385
|
|
|
3350
3386
|
// src/commands/workspace.ts
|
|
3351
|
-
import
|
|
3387
|
+
import path26 from "path";
|
|
3352
3388
|
import YAML from "yaml";
|
|
3353
3389
|
async function runWorkspace(action) {
|
|
3354
3390
|
if (action === "init") {
|
|
@@ -3371,8 +3407,8 @@ relationships: []
|
|
|
3371
3407
|
process.exitCode = 1;
|
|
3372
3408
|
return;
|
|
3373
3409
|
}
|
|
3374
|
-
const
|
|
3375
|
-
const repos =
|
|
3410
|
+
const config2 = YAML.parse(configText);
|
|
3411
|
+
const repos = config2.repos ?? [];
|
|
3376
3412
|
if (repos.length === 0) {
|
|
3377
3413
|
error("No repos configured in haus.workspace.yaml.");
|
|
3378
3414
|
process.exitCode = 1;
|
|
@@ -3381,7 +3417,7 @@ relationships: []
|
|
|
3381
3417
|
const summaries = [];
|
|
3382
3418
|
const ownership = {};
|
|
3383
3419
|
for (const repo of repos) {
|
|
3384
|
-
const repoRoot =
|
|
3420
|
+
const repoRoot = path26.resolve(process.cwd(), repo.path);
|
|
3385
3421
|
const result = await scanProject(repoRoot, "fast");
|
|
3386
3422
|
summaries.push({
|
|
3387
3423
|
name: repo.name,
|
|
@@ -3417,7 +3453,7 @@ ${summaries.map(
|
|
|
3417
3453
|
// src/cli.ts
|
|
3418
3454
|
function cliVersion() {
|
|
3419
3455
|
try {
|
|
3420
|
-
const pkgPath =
|
|
3456
|
+
const pkgPath = path27.join(packageRoot(), "package.json");
|
|
3421
3457
|
const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
|
|
3422
3458
|
return pkg.version ?? "0.0.0";
|
|
3423
3459
|
} catch {
|
|
@@ -3427,7 +3463,7 @@ function cliVersion() {
|
|
|
3427
3463
|
var program = new Command();
|
|
3428
3464
|
function validateRuntimeNodeVersion() {
|
|
3429
3465
|
try {
|
|
3430
|
-
const pkgPath =
|
|
3466
|
+
const pkgPath = path27.join(packageRoot(), "package.json");
|
|
3431
3467
|
const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
|
|
3432
3468
|
const requiredRange = pkg.engines?.node;
|
|
3433
3469
|
if (requiredRange && !satisfiesVersion(process.version, requiredRange)) {
|
|
@@ -3464,6 +3500,10 @@ memory.command("promote").action(() => runMemory("promote", {}));
|
|
|
3464
3500
|
var guard = program.command("guard");
|
|
3465
3501
|
guard.command("file-access").option("--from-hook").action((opts) => runGuard("file-access", opts));
|
|
3466
3502
|
guard.command("bash").option("--from-hook").action((opts) => runGuard("bash", opts));
|
|
3503
|
+
var config = program.command("config");
|
|
3504
|
+
config.command("enable <key>").description("Enable a hook (hook.context, hook.memory)").action((key) => runConfig(key, "enable"));
|
|
3505
|
+
config.command("disable <key>").description("Disable a hook (hook.context, hook.memory)").action((key) => runConfig(key, "disable"));
|
|
3506
|
+
config.command("status <key>").description("Show current state of a hook (hook.context, hook.memory)").action((key) => runConfig(key, "status"));
|
|
3467
3507
|
var workspace = program.command("workspace");
|
|
3468
3508
|
workspace.command("init").action(() => runWorkspace("init"));
|
|
3469
3509
|
workspace.command("scan").action(() => runWorkspace("scan"));
|
|
@@ -467,6 +467,127 @@
|
|
|
467
467
|
"tokenEstimate": 1000,
|
|
468
468
|
"installMode": "copy-selected"
|
|
469
469
|
},
|
|
470
|
+
{
|
|
471
|
+
"id": "haus.stripe-patterns",
|
|
472
|
+
"version": "1.0.0",
|
|
473
|
+
"source": "haus",
|
|
474
|
+
"type": "skill",
|
|
475
|
+
"path": "skills/stripe-patterns",
|
|
476
|
+
"title": "Haus Stripe patterns",
|
|
477
|
+
"purpose": "Guide Stripe Elements, Checkout, webhook handling, and PCI-safe integration changes.",
|
|
478
|
+
"whenToUse": "Use for `@stripe/stripe-js` / `@stripe/react-stripe-js` integrations, payment intents, webhooks, and Checkout.",
|
|
479
|
+
"whenNotToUse": "Do not use for non-Stripe payment providers (Qliro, Klarna, etc.).",
|
|
480
|
+
"references": [
|
|
481
|
+
"references/conventions.md",
|
|
482
|
+
"references/scope.md",
|
|
483
|
+
"references/workflow.md",
|
|
484
|
+
"https://docs.stripe.com/"
|
|
485
|
+
],
|
|
486
|
+
"tokenBudget": 1200,
|
|
487
|
+
"tags": [
|
|
488
|
+
"stripe",
|
|
489
|
+
"payments",
|
|
490
|
+
"tooling"
|
|
491
|
+
],
|
|
492
|
+
"repoRoles": [
|
|
493
|
+
"next-app",
|
|
494
|
+
"react-app",
|
|
495
|
+
"vendure-plugin"
|
|
496
|
+
],
|
|
497
|
+
"requiresAny": [
|
|
498
|
+
{
|
|
499
|
+
"stack": "stripe"
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
"dependency": "@stripe/stripe-js"
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
"dependency": "@stripe/react-stripe-js"
|
|
506
|
+
}
|
|
507
|
+
],
|
|
508
|
+
"ecosystem": "payments",
|
|
509
|
+
"tokenEstimate": 2000,
|
|
510
|
+
"installMode": "copy-selected"
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
"id": "haus.qliro-patterns",
|
|
514
|
+
"version": "1.0.0",
|
|
515
|
+
"source": "haus",
|
|
516
|
+
"type": "skill",
|
|
517
|
+
"path": "skills/qliro-patterns",
|
|
518
|
+
"title": "Haus Qliro patterns",
|
|
519
|
+
"purpose": "Guide Qliro Checkout integration via @haus-tech/qliro-plugin in Vendure storefronts.",
|
|
520
|
+
"whenToUse": "Use for Qliro Checkout flow, order callbacks, refund handling, and merchant API config.",
|
|
521
|
+
"whenNotToUse": "Do not use for non-Nordic payment providers or Stripe-only flows.",
|
|
522
|
+
"references": [
|
|
523
|
+
"references/conventions.md",
|
|
524
|
+
"references/scope.md",
|
|
525
|
+
"references/workflow.md"
|
|
526
|
+
],
|
|
527
|
+
"tokenBudget": 1200,
|
|
528
|
+
"tags": [
|
|
529
|
+
"qliro",
|
|
530
|
+
"payments",
|
|
531
|
+
"tooling"
|
|
532
|
+
],
|
|
533
|
+
"repoRoles": [
|
|
534
|
+
"vendure-app",
|
|
535
|
+
"vendure-plugin",
|
|
536
|
+
"next-app"
|
|
537
|
+
],
|
|
538
|
+
"requiresAny": [
|
|
539
|
+
{
|
|
540
|
+
"stack": "qliro"
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
"dependency": "@haus-tech/qliro-plugin"
|
|
544
|
+
}
|
|
545
|
+
],
|
|
546
|
+
"ecosystem": "payments",
|
|
547
|
+
"tokenEstimate": 1800,
|
|
548
|
+
"installMode": "copy-selected"
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
"id": "haus.supabase-patterns",
|
|
552
|
+
"version": "1.0.0",
|
|
553
|
+
"source": "haus",
|
|
554
|
+
"type": "skill",
|
|
555
|
+
"path": "skills/supabase-patterns",
|
|
556
|
+
"title": "Haus Supabase patterns",
|
|
557
|
+
"purpose": "Guide Supabase client wiring, Row-Level Security, edge functions, and auth integration.",
|
|
558
|
+
"whenToUse": "Use for `@supabase/supabase-js` queries, RLS policies, edge functions, and Supabase Auth flows.",
|
|
559
|
+
"whenNotToUse": "Do not use for non-Supabase BaaS (Firebase, AWS Amplify) or self-hosted Postgres without Supabase.",
|
|
560
|
+
"references": [
|
|
561
|
+
"references/conventions.md",
|
|
562
|
+
"references/scope.md",
|
|
563
|
+
"references/workflow.md",
|
|
564
|
+
"https://supabase.com/docs"
|
|
565
|
+
],
|
|
566
|
+
"tokenBudget": 1200,
|
|
567
|
+
"tags": [
|
|
568
|
+
"supabase",
|
|
569
|
+
"database",
|
|
570
|
+
"backend"
|
|
571
|
+
],
|
|
572
|
+
"repoRoles": [
|
|
573
|
+
"next-app",
|
|
574
|
+
"react-app"
|
|
575
|
+
],
|
|
576
|
+
"requiresAny": [
|
|
577
|
+
{
|
|
578
|
+
"stack": "supabase"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
"dependency": "@supabase/supabase-js"
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
"packageNamePattern": "@supabase/*"
|
|
585
|
+
}
|
|
586
|
+
],
|
|
587
|
+
"ecosystem": "database",
|
|
588
|
+
"tokenEstimate": 2000,
|
|
589
|
+
"installMode": "copy-selected"
|
|
590
|
+
},
|
|
470
591
|
{
|
|
471
592
|
"id": "haus.sanity-patterns",
|
|
472
593
|
"version": "1.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haus-tech/haus-workflow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Haus AI workflow CLI for Claude Code.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"library/global",
|
|
17
17
|
"library/catalog",
|
|
18
18
|
"tests/fixtures/catalog",
|
|
19
|
-
"docs/user-guide.md",
|
|
20
19
|
"README.md",
|
|
21
20
|
"CHANGELOG.md",
|
|
22
21
|
"LICENSE",
|
|
@@ -258,6 +258,71 @@
|
|
|
258
258
|
"tokenEstimate": 1000,
|
|
259
259
|
"installMode": "copy-selected"
|
|
260
260
|
},
|
|
261
|
+
{
|
|
262
|
+
"id": "haus.stripe-patterns",
|
|
263
|
+
"source": "haus",
|
|
264
|
+
"type": "skill",
|
|
265
|
+
"path": "skills/stripe-patterns",
|
|
266
|
+
"title": "Haus Stripe patterns",
|
|
267
|
+
"purpose": "Guide Stripe Elements, Checkout, webhook handling, and PCI-safe integration changes.",
|
|
268
|
+
"whenToUse": "Use for `@stripe/stripe-js` / `@stripe/react-stripe-js` integrations, payment intents, webhooks, and Checkout.",
|
|
269
|
+
"whenNotToUse": "Do not use for non-Stripe payment providers (Qliro, Klarna, etc.).",
|
|
270
|
+
"references": ["references/scope.md", "references/workflow.md", "https://docs.stripe.com/"],
|
|
271
|
+
"tokenBudget": 1200,
|
|
272
|
+
"tags": ["stripe", "payments", "tooling"],
|
|
273
|
+
"repoRoles": ["next-app", "react-app", "vendure-plugin"],
|
|
274
|
+
"requiresAny": [
|
|
275
|
+
{ "stack": "stripe" },
|
|
276
|
+
{ "dependency": "@stripe/stripe-js" },
|
|
277
|
+
{ "dependency": "@stripe/react-stripe-js" }
|
|
278
|
+
],
|
|
279
|
+
"ecosystem": "payments",
|
|
280
|
+
"tokenEstimate": 2000,
|
|
281
|
+
"installMode": "copy-selected"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"id": "haus.qliro-patterns",
|
|
285
|
+
"source": "haus",
|
|
286
|
+
"type": "skill",
|
|
287
|
+
"path": "skills/qliro-patterns",
|
|
288
|
+
"title": "Haus Qliro patterns",
|
|
289
|
+
"purpose": "Guide Qliro Checkout integration via @haus-tech/qliro-plugin in Vendure storefronts.",
|
|
290
|
+
"whenToUse": "Use for Qliro Checkout flow, order callbacks, refund handling, and merchant API config.",
|
|
291
|
+
"whenNotToUse": "Do not use for non-Nordic payment providers or Stripe-only flows.",
|
|
292
|
+
"references": ["references/scope.md", "references/workflow.md"],
|
|
293
|
+
"tokenBudget": 1200,
|
|
294
|
+
"tags": ["qliro", "payments", "tooling"],
|
|
295
|
+
"repoRoles": ["vendure-app", "vendure-plugin", "next-app"],
|
|
296
|
+
"requiresAny": [
|
|
297
|
+
{ "stack": "qliro" },
|
|
298
|
+
{ "dependency": "@haus-tech/qliro-plugin" }
|
|
299
|
+
],
|
|
300
|
+
"ecosystem": "payments",
|
|
301
|
+
"tokenEstimate": 1800,
|
|
302
|
+
"installMode": "copy-selected"
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"id": "haus.supabase-patterns",
|
|
306
|
+
"source": "haus",
|
|
307
|
+
"type": "skill",
|
|
308
|
+
"path": "skills/supabase-patterns",
|
|
309
|
+
"title": "Haus Supabase patterns",
|
|
310
|
+
"purpose": "Guide Supabase client wiring, Row-Level Security, edge functions, and auth integration.",
|
|
311
|
+
"whenToUse": "Use for `@supabase/supabase-js` queries, RLS policies, edge functions, and Supabase Auth flows.",
|
|
312
|
+
"whenNotToUse": "Do not use for non-Supabase BaaS (Firebase, AWS Amplify) or self-hosted Postgres without Supabase.",
|
|
313
|
+
"references": ["references/scope.md", "references/workflow.md", "https://supabase.com/docs"],
|
|
314
|
+
"tokenBudget": 1200,
|
|
315
|
+
"tags": ["supabase", "database", "backend"],
|
|
316
|
+
"repoRoles": ["next-app", "react-app"],
|
|
317
|
+
"requiresAny": [
|
|
318
|
+
{ "stack": "supabase" },
|
|
319
|
+
{ "dependency": "@supabase/supabase-js" },
|
|
320
|
+
{ "packageNamePattern": "@supabase/*" }
|
|
321
|
+
],
|
|
322
|
+
"ecosystem": "database",
|
|
323
|
+
"tokenEstimate": 2000,
|
|
324
|
+
"installMode": "copy-selected"
|
|
325
|
+
},
|
|
261
326
|
{
|
|
262
327
|
"id": "haus.sanity-patterns",
|
|
263
328
|
"source": "haus",
|
package/docs/user-guide.md
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
# Haus AI User Guide
|
|
2
|
-
|
|
3
|
-
This guide shows how to use `haus` in a real project, even if you are not a developer.
|
|
4
|
-
|
|
5
|
-
## What Haus AI does
|
|
6
|
-
|
|
7
|
-
Haus AI scans your project, recommends context files/rules, then writes controlled files so Claude works with safer, stack-aware guidance.
|
|
8
|
-
|
|
9
|
-
Main output folders:
|
|
10
|
-
|
|
11
|
-
- `./.claude` (Claude settings/rules/commands)
|
|
12
|
-
- `./.haus-workflow` (scan/recommendation/lock/memory metadata)
|
|
13
|
-
|
|
14
|
-
## Before you start
|
|
15
|
-
|
|
16
|
-
You need:
|
|
17
|
-
|
|
18
|
-
- a project folder on your machine
|
|
19
|
-
- Node.js 22+ (`node --version`)
|
|
20
|
-
- terminal access
|
|
21
|
-
|
|
22
|
-
Check Node:
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
node --version
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
If version is below 22, install/update Node first.
|
|
29
|
-
|
|
30
|
-
## Install Haus AI
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npm install -g @haus-tech/haus-workflow
|
|
34
|
-
haus --help
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Seed `~/.claude/` with Haus skills, agents, and hooks (once per machine):
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
haus install
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### If you switch Node versions often (nvm, Herd, Volta…)
|
|
44
|
-
|
|
45
|
-
`npm install -g` binds to the currently active Node version. Switch Node → `haus` disappears. Two options:
|
|
46
|
-
|
|
47
|
-
1. **Re-install per version.** When you change Node, carry globals forward:
|
|
48
|
-
```bash
|
|
49
|
-
nvm install <new-version> --reinstall-packages-from=current
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
2. **Use a shell alias.** No per-version install needed:
|
|
53
|
-
```bash
|
|
54
|
-
echo 'alias haus="node $(npm root -g)/@haus-tech/haus-workflow/dist/cli.js"' >> ~/.zshrc
|
|
55
|
-
source ~/.zshrc
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
## Use Haus in a project
|
|
59
|
-
|
|
60
|
-
Move terminal to project root (folder that contains your app code), then run:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
haus setup-project
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Setup modes:
|
|
67
|
-
|
|
68
|
-
- guided: asks simple onboarding questions
|
|
69
|
-
- fast: minimal prompts, default flow
|
|
70
|
-
|
|
71
|
-
## Typical daily workflow
|
|
72
|
-
|
|
73
|
-
### 1) Scan project
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
haus scan --json
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
Writes project detection outputs to `./.haus-workflow/*`.
|
|
80
|
-
|
|
81
|
-
### 2) Generate recommendations
|
|
82
|
-
|
|
83
|
-
```bash
|
|
84
|
-
haus recommend --json
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Creates `./.haus-workflow/recommendation.json` with selected and skipped items, confidence, and reasons.
|
|
88
|
-
|
|
89
|
-
### 3) Preview generated changes
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
haus apply --dry-run
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Shows planned files without writing.
|
|
96
|
-
|
|
97
|
-
### 4) Apply generated files
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
haus apply --write
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Writes generated files and reports overwrite summaries with concise diff counts.
|
|
104
|
-
|
|
105
|
-
### 5) Verify setup health
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
haus doctor
|
|
109
|
-
haus doctor --hooks
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
`--hooks` checks that project hook settings still match the hook contract.
|
|
113
|
-
|
|
114
|
-
## Update flow
|
|
115
|
-
|
|
116
|
-
Check update state:
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
haus update --check
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Apply lock refresh:
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
haus update
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Update behavior:
|
|
129
|
-
|
|
130
|
-
- preserves local `.claude` overrides
|
|
131
|
-
- backs up lockfile under `./.haus-workflow/backups`
|
|
132
|
-
- prints unified lockfile diff summary
|
|
133
|
-
|
|
134
|
-
## Memory commands
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
haus memory status
|
|
138
|
-
haus memory add "Use explicit transaction boundaries in checkout service"
|
|
139
|
-
haus memory inject --task "review checkout flow"
|
|
140
|
-
haus memory promote
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
Memory is local-only in `./.haus-workflow/memory`.
|
|
144
|
-
|
|
145
|
-
## Explain/context commands
|
|
146
|
-
|
|
147
|
-
Use when you need to understand why rules were selected:
|
|
148
|
-
|
|
149
|
-
```bash
|
|
150
|
-
haus explain-recommendation --json
|
|
151
|
-
haus context --task "build shipping plugin" --json
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Claude slash-command usage
|
|
155
|
-
|
|
156
|
-
After `haus apply --write`, command docs are generated in:
|
|
157
|
-
|
|
158
|
-
- `./.claude/commands/haus-doctor.md`
|
|
159
|
-
- `./.claude/commands/haus-review.md`
|
|
160
|
-
|
|
161
|
-
Some environments expose these as slash commands. If not, run the CLI commands directly.
|
|
162
|
-
|
|
163
|
-
## If something fails
|
|
164
|
-
|
|
165
|
-
- `haus: command not found` -> run `npm install -g @haus-tech/haus-workflow` or check Node version
|
|
166
|
-
- Node engine error -> switch to Node 22+
|
|
167
|
-
- hook mismatch in doctor -> run `haus apply --write` again
|
|
168
|
-
- wrong project scanned -> `cd` into correct project root, rerun
|
|
169
|
-
|
|
170
|
-
## Remove generated setup
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
haus undo --yes
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Removes `./.claude` and `./.haus-workflow` in current project.
|