@hiveai/cli 0.12.9 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +193 -31
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -2789,6 +2789,111 @@ Use \`select_related\` (FK / one-to-one, SQL JOIN) and \`prefetch_related\` (M2M
|
|
|
2789
2789
|
\`\`\`py
|
|
2790
2790
|
for order in Order.objects.select_related("customer").all():
|
|
2791
2791
|
order.customer.name # no extra query
|
|
2792
|
+
\`\`\``
|
|
2793
|
+
}
|
|
2794
|
+
],
|
|
2795
|
+
flask: [
|
|
2796
|
+
{
|
|
2797
|
+
slug: "flask-no-debug-in-prod",
|
|
2798
|
+
type: "gotcha",
|
|
2799
|
+
tags: ["flask", "python", "security", "deployment"],
|
|
2800
|
+
body: `\`app.run(debug=True)\` enables the Werkzeug debugger \u2014 remote code execution if exposed.
|
|
2801
|
+
|
|
2802
|
+
Never ship debug mode. Run behind a real WSGI server (gunicorn/uwsgi) in production and
|
|
2803
|
+
drive debug from the environment for local dev only.`,
|
|
2804
|
+
sensor: {
|
|
2805
|
+
pattern: "app\\.run\\([^)]*debug\\s*=\\s*True",
|
|
2806
|
+
message: "Flask debug=True exposes the Werkzeug console (RCE) \u2014 never run it in production."
|
|
2807
|
+
}
|
|
2808
|
+
},
|
|
2809
|
+
{
|
|
2810
|
+
slug: "flask-secret-key-from-env",
|
|
2811
|
+
type: "convention",
|
|
2812
|
+
tags: ["flask", "python", "security"],
|
|
2813
|
+
body: `Load \`SECRET_KEY\` from the environment \u2014 never commit a literal.
|
|
2814
|
+
|
|
2815
|
+
\`\`\`py
|
|
2816
|
+
app.config["SECRET_KEY"] = os.environ["SECRET_KEY"]
|
|
2817
|
+
\`\`\`
|
|
2818
|
+
A committed key lets anyone forge sessions and CSRF tokens.`
|
|
2819
|
+
},
|
|
2820
|
+
{
|
|
2821
|
+
slug: "flask-no-sql-string-interpolation",
|
|
2822
|
+
type: "gotcha",
|
|
2823
|
+
tags: ["flask", "python", "security", "sql-injection"],
|
|
2824
|
+
body: `Never build SQL with f-strings/%-formatting \u2014 use parameterized queries.
|
|
2825
|
+
|
|
2826
|
+
\`\`\`py
|
|
2827
|
+
# \u274C SQL injection
|
|
2828
|
+
db.execute(f"SELECT * FROM users WHERE id = {uid}")
|
|
2829
|
+
# \u2705
|
|
2830
|
+
db.execute("SELECT * FROM users WHERE id = %s", (uid,))
|
|
2831
|
+
\`\`\``
|
|
2832
|
+
}
|
|
2833
|
+
],
|
|
2834
|
+
vue: [
|
|
2835
|
+
{
|
|
2836
|
+
slug: "vue-v-html-xss",
|
|
2837
|
+
type: "gotcha",
|
|
2838
|
+
tags: ["vue", "security", "xss"],
|
|
2839
|
+
body: `\`v-html\` renders raw HTML and bypasses Vue's escaping \u2014 an XSS sink for user content.
|
|
2840
|
+
|
|
2841
|
+
Only use it on trusted/sanitized content. Prefer text interpolation ({{ }}) or sanitize
|
|
2842
|
+
with DOMPurify before binding.`,
|
|
2843
|
+
sensor: {
|
|
2844
|
+
pattern: "v-html",
|
|
2845
|
+
message: "v-html renders unescaped HTML (XSS risk) \u2014 sanitize the value or use text interpolation."
|
|
2846
|
+
}
|
|
2847
|
+
},
|
|
2848
|
+
{
|
|
2849
|
+
slug: "vue-key-in-v-for",
|
|
2850
|
+
type: "convention",
|
|
2851
|
+
tags: ["vue", "performance"],
|
|
2852
|
+
body: `Always bind a stable \`:key\` on \`v-for\` \u2014 and never the loop index.
|
|
2853
|
+
|
|
2854
|
+
Index keys corrupt component state on reorder/insert, exactly like React. Use a stable id.`
|
|
2855
|
+
},
|
|
2856
|
+
{
|
|
2857
|
+
slug: "vue-props-are-readonly",
|
|
2858
|
+
type: "gotcha",
|
|
2859
|
+
tags: ["vue", "reactivity"],
|
|
2860
|
+
body: `Never mutate a prop inside a child component \u2014 props are one-way (parent \u2192 child).
|
|
2861
|
+
|
|
2862
|
+
Mutating a prop breaks the data flow and warns in dev. Emit an event (\`update:modelValue\`)
|
|
2863
|
+
or copy the prop into local state, depending on intent.`
|
|
2864
|
+
}
|
|
2865
|
+
],
|
|
2866
|
+
spring: [
|
|
2867
|
+
{
|
|
2868
|
+
slug: "spring-constructor-injection",
|
|
2869
|
+
type: "convention",
|
|
2870
|
+
tags: ["spring", "java", "di", "testing"],
|
|
2871
|
+
body: `Prefer constructor injection over \`@Autowired\` field injection.
|
|
2872
|
+
|
|
2873
|
+
Constructor injection makes dependencies explicit, allows \`final\` fields, and lets you
|
|
2874
|
+
instantiate the class in tests without a Spring context. Field injection hides dependencies
|
|
2875
|
+
and forces reflection-based test setup.`
|
|
2876
|
+
},
|
|
2877
|
+
{
|
|
2878
|
+
slug: "spring-no-cors-wildcard",
|
|
2879
|
+
type: "gotcha",
|
|
2880
|
+
tags: ["spring", "java", "security", "cors"],
|
|
2881
|
+
body: `\`@CrossOrigin(origins = "*")\` (or wildcard CORS config) allows any site to call your API.
|
|
2882
|
+
|
|
2883
|
+
Combined with credentials it leaks authenticated data cross-origin. Whitelist explicit origins.`,
|
|
2884
|
+
sensor: {
|
|
2885
|
+
pattern: "@CrossOrigin\\([^)]*\\*",
|
|
2886
|
+
message: 'Wildcard CORS (@CrossOrigin origins="*") lets any site call your API \u2014 whitelist explicit origins.'
|
|
2887
|
+
}
|
|
2888
|
+
},
|
|
2889
|
+
{
|
|
2890
|
+
slug: "spring-no-field-secrets",
|
|
2891
|
+
type: "convention",
|
|
2892
|
+
tags: ["spring", "java", "security", "config"],
|
|
2893
|
+
body: `Keep secrets in externalized config (env / vault / application.yml placeholders), not in source.
|
|
2894
|
+
|
|
2895
|
+
\`\`\`java
|
|
2896
|
+
@Value("\${app.api-key}") private String apiKey; // resolved from env, not hardcoded
|
|
2792
2897
|
\`\`\``
|
|
2793
2898
|
}
|
|
2794
2899
|
],
|
|
@@ -2907,7 +3012,7 @@ ${SEED_FOOTER(stack)}` });
|
|
|
2907
3012
|
}
|
|
2908
3013
|
|
|
2909
3014
|
// src/commands/init.ts
|
|
2910
|
-
var HAIVE_GITHUB_ACTION_REF = `v${"0.
|
|
3015
|
+
var HAIVE_GITHUB_ACTION_REF = `v${"0.13.0"}`;
|
|
2911
3016
|
var PROJECT_CONTEXT_TEMPLATE = `# Project context
|
|
2912
3017
|
|
|
2913
3018
|
> Generated by \`haive init\`. Run \`haive init --bootstrap\` to auto-fill from your codebase,
|
|
@@ -7786,7 +7891,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
|
|
|
7786
7891
|
};
|
|
7787
7892
|
}
|
|
7788
7893
|
var SERVER_NAME = "haive";
|
|
7789
|
-
var SERVER_VERSION = "0.
|
|
7894
|
+
var SERVER_VERSION = "0.13.0";
|
|
7790
7895
|
function jsonResult(data) {
|
|
7791
7896
|
return {
|
|
7792
7897
|
content: [
|
|
@@ -12546,7 +12651,7 @@ import {
|
|
|
12546
12651
|
function registerEval(program2) {
|
|
12547
12652
|
program2.command("eval").description(
|
|
12548
12653
|
"Rigorous, repeatable quality eval: do the right memories surface (retrieval) and do the right sensors fire (catch-rate)? Emits a chiffr\xE9 0\u2013100 score. Uses .ai/eval cases via --spec, or auto-synthesizes cases from anchored memories."
|
|
12549
|
-
).option("--spec <file>", "JSON eval spec ({ retrieval: [...], sensors: [...] })").option("--semantic-only", "self-eval probes by title alone (no anchor files) \u2014 harder retrieval", false).option("-k, --top <n>", "briefing top-k considered a hit", "8").option("--json", "emit JSON", false).option("--out <file>", "write a Markdown report").option("--fail-under <score>", "exit non-zero if the overall score is below this (0\u2013100) \u2014 for CI gates").option("--baseline", "save this run as the baseline (.ai/eval/baseline.json) for future --compare", false).option("--compare", "diff this run against the saved baseline and print the delta", false).option("--baseline-file <path>", "baseline file to read/write (default: .ai/eval/baseline.json)").option("--fail-on-regression", "with --compare, exit non-zero if the score dropped vs the baseline", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12654
|
+
).option("--spec <file>", "JSON eval spec ({ retrieval: [...], sensors: [...] })").option("--semantic-only", "self-eval probes by title alone (no anchor files) \u2014 harder retrieval", false).option("-k, --top <n>", "briefing top-k considered a hit", "8").option("--json", "emit JSON", false).option("--out <file>", "write a Markdown report").option("--fail-under <score>", "exit non-zero if the overall score is below this (0\u2013100) \u2014 for CI gates").option("--baseline", "save this run as the baseline (.ai/eval/baseline.json) for future --compare", false).option("--compare", "diff this run against the saved baseline and print the delta", false).option("--baseline-file <path>", "baseline file to read/write (default: .ai/eval/baseline.json)").option("--fail-on-regression", "with --compare, exit non-zero if the score dropped vs the baseline", false).option("--regression-gate", "CI-safe gate: compare against the baseline IF one exists (fail on regression), else no-op", false).option("-d, --dir <dir>", "project root").action(async (opts) => {
|
|
12550
12655
|
const root = findProjectRoot42(opts.dir);
|
|
12551
12656
|
const paths = resolveHaivePaths38(root);
|
|
12552
12657
|
if (!existsSync65(paths.memoriesDir)) {
|
|
@@ -12594,14 +12699,19 @@ function registerEval(program2) {
|
|
|
12594
12699
|
if (!opts.json) ui.success(`Saved baseline (score ${report.score}/100) \u2192 ${path43.relative(root, baselineFile)}`);
|
|
12595
12700
|
}
|
|
12596
12701
|
let delta = null;
|
|
12597
|
-
if (opts.compare) {
|
|
12702
|
+
if (opts.compare || opts.regressionGate) {
|
|
12598
12703
|
if (!existsSync65(baselineFile)) {
|
|
12599
|
-
|
|
12600
|
-
|
|
12601
|
-
|
|
12704
|
+
if (opts.regressionGate) {
|
|
12705
|
+
if (!opts.json) ui.info(`No baseline at ${path43.relative(root, baselineFile)} \u2014 regression gate skipped. Run \`haive eval --baseline\` to enable it.`);
|
|
12706
|
+
} else {
|
|
12707
|
+
ui.error(`No baseline at ${path43.relative(root, baselineFile)}. Run \`haive eval --baseline\` first.`);
|
|
12708
|
+
process.exitCode = 1;
|
|
12709
|
+
return;
|
|
12710
|
+
}
|
|
12711
|
+
} else {
|
|
12712
|
+
const snapshot = JSON.parse(await readFile20(baselineFile, "utf8"));
|
|
12713
|
+
delta = compareEvalReports(snapshot.report, report);
|
|
12602
12714
|
}
|
|
12603
|
-
const snapshot = JSON.parse(await readFile20(baselineFile, "utf8"));
|
|
12604
|
-
delta = compareEvalReports(snapshot.report, report);
|
|
12605
12715
|
}
|
|
12606
12716
|
if (opts.json) {
|
|
12607
12717
|
console.log(JSON.stringify({ root, k, spec_source: resolvedSpec.source, report, ...delta ? { delta } : {} }, null, 2));
|
|
@@ -12633,7 +12743,7 @@ function applyExitGates(opts, report, delta) {
|
|
|
12633
12743
|
process.exitCode = 1;
|
|
12634
12744
|
}
|
|
12635
12745
|
}
|
|
12636
|
-
if (opts.failOnRegression && delta?.regressed) {
|
|
12746
|
+
if ((opts.failOnRegression || opts.regressionGate) && delta?.regressed) {
|
|
12637
12747
|
ui.error(`eval score regressed ${delta.score.baseline} \u2192 ${delta.score.current} (\u0394 ${delta.score.delta}) vs baseline`);
|
|
12638
12748
|
process.exitCode = 1;
|
|
12639
12749
|
}
|
|
@@ -13350,7 +13460,7 @@ function registerDoctor(program2) {
|
|
|
13350
13460
|
fix: "Edit .ai/haive.config.json: set autoSessionEnd: true (or re-run `haive init` without --manual)."
|
|
13351
13461
|
});
|
|
13352
13462
|
}
|
|
13353
|
-
findings.push(...await collectInstallFindings(root, "0.
|
|
13463
|
+
findings.push(...await collectInstallFindings(root, "0.13.0"));
|
|
13354
13464
|
findings.push(...await collectToolchainFindings(root));
|
|
13355
13465
|
try {
|
|
13356
13466
|
const legacyRaw = execSync3("haive-mcp --version", {
|
|
@@ -13358,7 +13468,7 @@ function registerDoctor(program2) {
|
|
|
13358
13468
|
timeout: 3e3,
|
|
13359
13469
|
stdio: ["ignore", "pipe", "ignore"]
|
|
13360
13470
|
}).trim();
|
|
13361
|
-
const cliVersion = "0.
|
|
13471
|
+
const cliVersion = "0.13.0";
|
|
13362
13472
|
if (legacyRaw && legacyRaw !== cliVersion) {
|
|
13363
13473
|
findings.push({
|
|
13364
13474
|
severity: "warn",
|
|
@@ -14951,7 +15061,7 @@ async function buildEnforcementReport(dir, stage, sessionId) {
|
|
|
14951
15061
|
findings: [{ severity: "info", code: "enforcement-off", message: "hAIve enforcement is disabled." }]
|
|
14952
15062
|
});
|
|
14953
15063
|
}
|
|
14954
|
-
findings.push(...await inspectIntegrationVersions(root, "0.
|
|
15064
|
+
findings.push(...await inspectIntegrationVersions(root, "0.13.0"));
|
|
14955
15065
|
if (config.enforcement?.requireBriefingFirst !== false && stage !== "ci") {
|
|
14956
15066
|
const hasBriefing = await hasRecentBriefingMarker2(paths, sessionId);
|
|
14957
15067
|
findings.push(hasBriefing ? { severity: "ok", code: "briefing-loaded", message: "A recent hAIve briefing marker exists." } : {
|
|
@@ -16077,11 +16187,11 @@ import {
|
|
|
16077
16187
|
var SEVERITIES = ["info", "minor", "major", "critical", "blocker"];
|
|
16078
16188
|
function registerIngest(program2) {
|
|
16079
16189
|
program2.command("ingest").description(
|
|
16080
|
-
"Ingest scanner findings (SonarQube / SARIF) as proposed, anchored memories with sensors.\n\n Closes the review\u2194memory loop: a real defect a scanner found becomes a `gotcha`/`convention`\n memory anchored to the file, pre-filled with a conservative `warn` sensor, so the next agent\n is steered away from it. Drafts are status=proposed; a human validates/promotes them.\n\n Example:\n haive ingest --from sarif eslint.sarif --dry-run\n haive ingest --from sonar sonar-issues.json --scope team --min-severity major\n"
|
|
16081
|
-
).argument("
|
|
16190
|
+
"Ingest scanner findings (SonarQube / SARIF) as proposed, anchored memories with sensors.\n\n Closes the review\u2194memory loop: a real defect a scanner found becomes a `gotcha`/`convention`\n memory anchored to the file, pre-filled with a conservative `warn` sensor, so the next agent\n is steered away from it. Drafts are status=proposed; a human validates/promotes them.\n\n `sonar-api` fetches issues live over plain HTTPS from any SonarQube/SonarCloud instance \u2014\n no MCP or special setup required, just a URL + token you provide (or SONAR_HOST_URL /\n SONAR_TOKEN env). If you don't use it, file-based ingest works exactly the same.\n\n Example:\n haive ingest --from sarif eslint.sarif --dry-run\n haive ingest --from sonar sonar-issues.json --scope team --min-severity major\n haive ingest --from sonar-api --sonar-component my_project --min-severity major\n"
|
|
16191
|
+
).argument("[file]", "path to the findings report JSON (required for --from sarif|sonar)").requiredOption("--from <format>", "report format: sarif | sonar | sonar-api").option("--dry-run", "show what would be created without writing", false).option("--scope <scope>", "memory scope: personal | team | module", "team").option("--module <name>", "module name (required when scope=module)").option("--type <type>", "memory type: gotcha | convention", "gotcha").option("--min-severity <severity>", "ignore findings below this severity (info|minor|major|critical|blocker)").option("--limit <n>", "cap the number of memories created").option("--author <author>", "author email or handle").option("--json", "emit JSON", false).option("--sonar-url <url>", "SonarQube base URL for --from sonar-api (or env SONAR_HOST_URL)").option("--sonar-token <token>", "SonarQube token for --from sonar-api (or env SONAR_TOKEN)").option("--sonar-component <key>", "SonarQube project/component key for --from sonar-api").option("--sonar-branch <branch>", "optional SonarQube branch for --from sonar-api").option("-d, --dir <dir>", "project root").action(async (file, opts) => {
|
|
16082
16192
|
const format = opts.from;
|
|
16083
|
-
if (format !== "sarif" && format !== "sonar") {
|
|
16084
|
-
ui.error("--from must be sarif or sonar");
|
|
16193
|
+
if (format !== "sarif" && format !== "sonar" && format !== "sonar-api") {
|
|
16194
|
+
ui.error("--from must be sarif, sonar, or sonar-api");
|
|
16085
16195
|
process.exitCode = 1;
|
|
16086
16196
|
return;
|
|
16087
16197
|
}
|
|
@@ -16102,23 +16212,39 @@ function registerIngest(program2) {
|
|
|
16102
16212
|
process.exitCode = 1;
|
|
16103
16213
|
return;
|
|
16104
16214
|
}
|
|
16105
|
-
const
|
|
16106
|
-
if (!existsSync77(reportPath)) {
|
|
16107
|
-
ui.error(`Report file not found: ${reportPath}`);
|
|
16108
|
-
process.exitCode = 1;
|
|
16109
|
-
return;
|
|
16110
|
-
}
|
|
16215
|
+
const parseFormat = format === "sarif" ? "sarif" : "sonar";
|
|
16111
16216
|
let raw;
|
|
16112
|
-
|
|
16113
|
-
|
|
16114
|
-
|
|
16115
|
-
|
|
16116
|
-
|
|
16117
|
-
|
|
16217
|
+
if (format === "sonar-api") {
|
|
16218
|
+
const fetched = await fetchSonarIssues(opts);
|
|
16219
|
+
if (!fetched.ok) {
|
|
16220
|
+
ui.error(fetched.error);
|
|
16221
|
+
process.exitCode = 1;
|
|
16222
|
+
return;
|
|
16223
|
+
}
|
|
16224
|
+
raw = fetched.json;
|
|
16225
|
+
} else {
|
|
16226
|
+
if (!file) {
|
|
16227
|
+
ui.error(`--from ${format} needs a report file argument, e.g. \`haive ingest --from ${format} report.json\`.`);
|
|
16228
|
+
process.exitCode = 1;
|
|
16229
|
+
return;
|
|
16230
|
+
}
|
|
16231
|
+
const reportPath = path54.resolve(root, file);
|
|
16232
|
+
if (!existsSync77(reportPath)) {
|
|
16233
|
+
ui.error(`Report file not found: ${reportPath}`);
|
|
16234
|
+
process.exitCode = 1;
|
|
16235
|
+
return;
|
|
16236
|
+
}
|
|
16237
|
+
try {
|
|
16238
|
+
raw = await readFile25(reportPath, "utf8");
|
|
16239
|
+
} catch (err) {
|
|
16240
|
+
ui.error(`Could not read ${reportPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
16241
|
+
process.exitCode = 1;
|
|
16242
|
+
return;
|
|
16243
|
+
}
|
|
16118
16244
|
}
|
|
16119
16245
|
let drafts;
|
|
16120
16246
|
try {
|
|
16121
|
-
const findings = parseFindings2(
|
|
16247
|
+
const findings = parseFindings2(parseFormat, raw);
|
|
16122
16248
|
drafts = draftsFromFindings2(findings, {
|
|
16123
16249
|
type: opts.type ?? "gotcha",
|
|
16124
16250
|
scope: opts.scope ?? "team",
|
|
@@ -16201,6 +16327,42 @@ async function writeDraft2(paths, draft) {
|
|
|
16201
16327
|
await writeFile37(file, serializeMemory28({ frontmatter: draft.frontmatter, body: draft.body }), "utf8");
|
|
16202
16328
|
return file;
|
|
16203
16329
|
}
|
|
16330
|
+
async function fetchSonarIssues(opts) {
|
|
16331
|
+
const baseUrl = (opts.sonarUrl ?? process.env.SONAR_HOST_URL ?? "").trim().replace(/\/+$/, "");
|
|
16332
|
+
const token = (opts.sonarToken ?? process.env.SONAR_TOKEN ?? "").trim();
|
|
16333
|
+
const component = (opts.sonarComponent ?? "").trim();
|
|
16334
|
+
if (!baseUrl) {
|
|
16335
|
+
return { ok: false, error: "--from sonar-api needs --sonar-url (or env SONAR_HOST_URL)." };
|
|
16336
|
+
}
|
|
16337
|
+
if (!token) {
|
|
16338
|
+
return { ok: false, error: "--from sonar-api needs --sonar-token (or env SONAR_TOKEN)." };
|
|
16339
|
+
}
|
|
16340
|
+
if (!component) {
|
|
16341
|
+
return { ok: false, error: "--from sonar-api needs --sonar-component <projectKey>." };
|
|
16342
|
+
}
|
|
16343
|
+
if (typeof fetch !== "function") {
|
|
16344
|
+
return { ok: false, error: "global fetch is unavailable \u2014 Node 18+ is required for --from sonar-api." };
|
|
16345
|
+
}
|
|
16346
|
+
const params = new URLSearchParams({ componentKeys: component, resolved: "false", ps: "500" });
|
|
16347
|
+
if (opts.sonarBranch) params.set("branch", opts.sonarBranch);
|
|
16348
|
+
const url = `${baseUrl}/api/issues/search?${params.toString()}`;
|
|
16349
|
+
try {
|
|
16350
|
+
const res = await fetch(url, {
|
|
16351
|
+
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" }
|
|
16352
|
+
});
|
|
16353
|
+
if (!res.ok) {
|
|
16354
|
+
const hint = res.status === 401 || res.status === 403 ? " (check the token and its permissions)" : "";
|
|
16355
|
+
return { ok: false, error: `SonarQube API returned ${res.status} ${res.statusText}${hint}.` };
|
|
16356
|
+
}
|
|
16357
|
+
const json = await res.text();
|
|
16358
|
+
return { ok: true, json };
|
|
16359
|
+
} catch (err) {
|
|
16360
|
+
return {
|
|
16361
|
+
ok: false,
|
|
16362
|
+
error: `Could not reach SonarQube at ${baseUrl}: ${err instanceof Error ? err.message : String(err)}. File-based ingest (--from sonar) still works.`
|
|
16363
|
+
};
|
|
16364
|
+
}
|
|
16365
|
+
}
|
|
16204
16366
|
|
|
16205
16367
|
// src/commands/dashboard.ts
|
|
16206
16368
|
import { existsSync as existsSync78 } from "fs";
|
|
@@ -16305,7 +16467,7 @@ function warnNum(n) {
|
|
|
16305
16467
|
|
|
16306
16468
|
// src/index.ts
|
|
16307
16469
|
var program = new Command58();
|
|
16308
|
-
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.
|
|
16470
|
+
program.name("haive").description("hAIve - repo-native memory and context policy for coding-agent harnesses").version("0.13.0").option("--advanced", "show maintenance and experimental commands in help");
|
|
16309
16471
|
registerInit(program);
|
|
16310
16472
|
registerWelcome(program);
|
|
16311
16473
|
registerResolveProject(program);
|