@aihq/harness 0.2.0-rc.0 → 0.3.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/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  buildProgram
4
- } from "./chunk-S7XFTZJW.js";
4
+ } from "./chunk-7GVBGS5N.js";
5
5
 
6
6
  // src/cli.ts
7
7
  buildProgram().parseAsync(process.argv).catch((err) => {
package/dist/index.d.ts CHANGED
@@ -186,7 +186,7 @@ type Verdict = "pass" | "fail" | "skip";
186
186
  * sealed: a new failure mode means a new member here PLUS the `code` set at the
187
187
  * emitter; never derive a code by matching `detail`.
188
188
  */
189
- type CheckCode = "env.node-runtime" | "env.git-missing" | "env.dev-tool-missing" | "env.tool-install-blocked" | "cert.ca-missing" | "tls.verify-failed" | "npm.runtime-broken" | "path.missing" | "mcp.blocked" | "mcp.uv-missing" | "mcp.config-missing" | "mcp.unvendored-offline" | "mcp.policy-denied" | "mcp.hardcoded-secret" | "mcp.allowlist-drift" | "cli.not-detected" | "cli.config-only" | "cli.bootloader-missing" | "cli.bootloader-drift" | "cli.wont-load" | "canon.router-missing" | "canon.context-dir-missing" | "canon.lint-failed" | "canon.adoptable" | "canon.cli-native-unmigrated" | "secrets.plaintext-detected" | "guardrails.gitleaks-missing" | "usage.no-data" | "scale.code-review-graph-missing" | "contract.path-unportable" | "contract.stale" | "org-policy.drift" | "report.context-over-budget" | "report.low-adoption" | "report.contract-untrue" | "trust.fetch-blocked" | "trust.detector-unavailable" | "trust.hidden-unicode" | "trust.prompt-injection" | "trust.source-changed" | "trust.auto-exec-hook" | "trust.dependency-confusion" | "trust.typosquat" | "trust.malicious-code" | "trust.source-drift" | "trust.unpinned-dependency" | "trust.untrusted-publisher" | "trust.unsigned-source";
189
+ type CheckCode = "env.node-runtime" | "env.git-missing" | "env.dev-tool-missing" | "env.tool-install-blocked" | "cert.ca-missing" | "tls.verify-failed" | "npm.runtime-broken" | "path.missing" | "mcp.blocked" | "mcp.uv-missing" | "mcp.config-missing" | "mcp.unvendored-offline" | "mcp.policy-denied" | "mcp.hardcoded-secret" | "mcp.allowlist-drift" | "cli.not-detected" | "cli.config-only" | "cli.bootloader-missing" | "cli.bootloader-drift" | "cli.wont-load" | "canon.router-missing" | "canon.context-dir-missing" | "canon.lint-failed" | "canon.adoptable" | "canon.cli-native-unmigrated" | "secrets.plaintext-detected" | "guardrails.gitleaks-missing" | "usage.no-data" | "scale.code-review-graph-missing" | "contract.path-unportable" | "contract.stale" | "org-policy.drift" | "report.context-over-budget" | "report.low-adoption" | "report.contract-untrue" | "ready.blocked" | "trust.fetch-blocked" | "trust.detector-unavailable" | "trust.hidden-unicode" | "trust.prompt-injection" | "trust.source-changed" | "trust.auto-exec-hook" | "trust.dependency-confusion" | "trust.typosquat" | "trust.malicious-code" | "trust.source-drift" | "trust.unpinned-dependency" | "trust.untrusted-publisher" | "trust.unsigned-source";
190
190
  interface Check {
191
191
  name: string;
192
192
  verdict: Verdict;
@@ -246,10 +246,14 @@ declare class VerificationReport {
246
246
  * - `digest`: a read-only computed result printed verbatim (an analytics
247
247
  * report / roll-up) plus optional structured `data` echoed into
248
248
  * `--json` — mutates nothing, never contacts a remote system.
249
+ * - `remove`: delete a repo-LOCAL file aih exclusively owns (a stale per-CLI
250
+ * adapter / kiro extra when its CLI is dropped), reversibly by
251
+ * default (moved to gitignored `.aih/legacy/`). Fail-closed:
252
+ * contained, symlink-guarded, backed up before unlink. Never remote.
249
253
  * Because no action kind can mutate a remote system, an autonomous run cannot
250
254
  * "fake provisioning" — the capability simply does not exist.
251
255
  */
252
- type ActionKind = "write" | "probe" | "doc" | "exec" | "envblock" | "digest";
256
+ type ActionKind = "write" | "probe" | "doc" | "exec" | "envblock" | "digest" | "remove";
253
257
  interface WriteAction {
254
258
  kind: "write";
255
259
  path: string;
@@ -346,7 +350,23 @@ interface DigestAction {
346
350
  data?: unknown;
347
351
  };
348
352
  }
349
- type Action = WriteAction | DocAction | ProbeAction | ExecAction | EnvBlockAction | DigestAction;
353
+ /**
354
+ * Remove a repo-LOCAL file that aih exclusively owns — aih's only destructive
355
+ * action. Emitted solely by `aih prune` for artifacts its detection proved
356
+ * aih-owned (a per-CLI adapter note, a kiro steering/hook extra) once the CLI is
357
+ * dropped. The executor fails closed: mandatory {@link assertContained} on the raw
358
+ * path (no `external` field exists, so a global `~/home` file is structurally
359
+ * unreachable), a symlink guard, and a backup before unlink. It MOVES the file to
360
+ * gitignored `.aih/legacy/<path>` (reversible) — the move itself is the backup, so a
361
+ * dropped CLI's artifacts can always be restored by moving them back.
362
+ */
363
+ interface RemoveAction {
364
+ kind: "remove";
365
+ /** Repo-relative path of the file to remove. */
366
+ path: string;
367
+ describe: string;
368
+ }
369
+ type Action = WriteAction | DocAction | ProbeAction | ExecAction | EnvBlockAction | DigestAction | RemoveAction;
350
370
  interface Plan {
351
371
  capability: string;
352
372
  actions: Action[];
@@ -366,6 +386,13 @@ interface PlanContext {
366
386
  /** When true, probe actions run and contribute to the verification report. */
367
387
  verify: boolean;
368
388
  json: boolean;
389
+ /**
390
+ * Local process runner. During `plan()` (dry-run) only READ-ONLY tools on FIXED targets
391
+ * may be run, and only to DECIDE the plan (heal's node/npm/TLS checks pick the repair
392
+ * ladder; certs reads the OS trust store; report shells `git` for stats). Never shell out
393
+ * an arbitrary or interpolated command at plan time — that is the `AIH_GRAPH_CMD` class of
394
+ * bug. The read-only allowlist is pinned by `tests/internals/plan-purity.test.ts` (#35).
395
+ */
369
396
  run: Runner;
370
397
  host: HostAdapter;
371
398
  env: NodeJS.ProcessEnv;
@@ -413,6 +440,13 @@ interface CommandSpec {
413
440
  * blocking the report on a dirty tree is wrong.
414
441
  */
415
442
  skipWorktreeGate?: boolean;
443
+ /**
444
+ * Wire an interactive prompter for this command in a TTY even without `--detect`,
445
+ * so a bare run can offer a confirmation (e.g. `aih ready` asking to install the
446
+ * missing core tools). Still suppressed under `--json`/`--yes`/non-TTY, so
447
+ * automation stays non-interactive.
448
+ */
449
+ wantsInstallPrompt?: boolean;
416
450
  }
417
451
  declare function writeText(path: string, contents: string, describe: string, opts?: {
418
452
  mode?: number;
@@ -437,6 +471,7 @@ declare function exec(describe: string, argv: string[], opts?: {
437
471
  blockProbesOnFailure?: boolean;
438
472
  }): ExecAction;
439
473
  declare function envBlock(path: string, scope: string, shell: EnvShell, vars: EnvVar[], describe: string): EnvBlockAction;
474
+ declare function remove(path: string, describe: string): RemoveAction;
440
475
  declare function plan(capability: string, ...actions: Action[]): Plan;
441
476
 
442
477
  /** Capability commands (repo/workstation mutators), dry-run by default. */
@@ -520,6 +555,14 @@ interface WriteSummary {
520
555
  */
521
556
  effect: "create" | "overwrite" | "merge" | "unchanged" | "kept";
522
557
  }
558
+ interface RemoveSummary {
559
+ path: string;
560
+ describe: string;
561
+ /** `remove` = present, will move to `.aih/legacy/`; `absent` = nothing on disk. */
562
+ effect: "remove" | "absent";
563
+ /** Repo-relative `.aih/legacy/` destination, when effect is `remove`. */
564
+ to?: string;
565
+ }
523
566
  interface PlanResult {
524
567
  capability: string;
525
568
  applied: boolean;
@@ -545,6 +588,8 @@ interface PlanResult {
545
588
  data?: unknown;
546
589
  }[];
547
590
  backups: string[];
591
+ /** Files aih removed (moved to `.aih/legacy/`) or would remove (dry-run). */
592
+ removed: RemoveSummary[];
548
593
  report?: VerificationReport;
549
594
  }
550
595
  /**
@@ -601,9 +646,15 @@ interface StagedWrite {
601
646
  contents: string;
602
647
  mode?: number;
603
648
  }
649
+ interface AppliedRemoval {
650
+ path: string;
651
+ legacyPath: string;
652
+ }
604
653
  interface FsTxnResult {
605
654
  written: string[];
606
655
  backups: string[];
656
+ /** Files moved out of the tree (source → `.aih/legacy/` destination). */
657
+ removed: AppliedRemoval[];
607
658
  }
608
659
  /**
609
660
  * Stages writes in memory and commits them atomically. Each existing target is
@@ -614,7 +665,15 @@ interface FsTxnResult {
614
665
  */
615
666
  declare class FsTransaction {
616
667
  private staged;
668
+ private stagedRemovals;
617
669
  stage(path: string, contents: string, mode?: number): void;
670
+ /**
671
+ * Stage a file REMOVAL as a reversible move to `legacyPath` (under gitignored
672
+ * `.aih/legacy/`). The move IS the backup: rollback (and the user) restore by
673
+ * moving it back. Symlinks are refused at commit (moving a link then restoring it
674
+ * would recreate a regular file). No-op if the source is already gone.
675
+ */
676
+ stageRemoval(path: string, legacyPath: string): void;
618
677
  preview(): ReadonlyArray<StagedWrite>;
619
678
  commit(): FsTxnResult;
620
679
  }
@@ -699,8 +758,8 @@ declare function parseCertLines(stdout: string): CertEntry[];
699
758
  */
700
759
  declare function parsePemBlocks(stdout: string, subject?: string): CertEntry[];
701
760
 
702
- declare const VERSION = "0.1.0";
761
+ declare const VERSION = "0.3.0";
703
762
  /** Build the configured commander program. Imported by both the CLI entry and tests. */
704
763
  declare function buildProgram(): Command;
705
764
 
706
- export { ALL_COMMANDS, type AccelBackend, type Action, type ActionKind, type AdapterFactory, AihError, CAPABILITIES, type CertEntry, type Check, type CheckCode, type CommandOption, type CommandSpec, ContextDir, type DigestAction, DirtyWorktreeError, type DocAction, type EnvBlockAction, type EnvShell, type EnvVar, type ExecAction, FsTransaction, FsTxnError, type FsTxnResult, type GpuInfo, type GpuVendor, type HostAdapter, type HostAdapterOptions, MergeError, NotImplementedError, PathContainmentError, type Plan, type PlanContext, type PlanFn, type PlanResult, type Platform, PlatformError, type ProbeAction, READONLY, type RunOptions, type RunResult, type Runner, type Settings, SettingsError, VERSION, type VdiInfo, type Verdict, VerificationError, VerificationReport, type WriteAction, type WriteSummary, beginMarker, buildProgram, deepMerge, defaultRunner, derBase64ToPem, digest, doc, dynamicDigest, endMarker, ensureTrailingNewline, envBlock, exec, executePlan, fakeRunner, formatExport, frontmatter, indent, isPlainObject, jsonFile, lines, loadSettings, makeHostAdapter, managedBlock, missingToolRunner, parseCertLines, parseFirstInt, parseJsoncText, parseNvidiaSmi, parsePemBlocks, plan, probe, probeMany, readIfExists, registerCommands, removeManagedBlock, resolveContents, resolvePlatform, retryTransient, safeCaPattern, stripTrailingNewlines, summarizeResult, upsertManagedBlock, upsertTextBlock, vdiFromEnv, writeArtifact, writeJson, writeText };
765
+ export { ALL_COMMANDS, type AccelBackend, type Action, type ActionKind, type AdapterFactory, AihError, CAPABILITIES, type CertEntry, type Check, type CheckCode, type CommandOption, type CommandSpec, ContextDir, type DigestAction, DirtyWorktreeError, type DocAction, type EnvBlockAction, type EnvShell, type EnvVar, type ExecAction, FsTransaction, FsTxnError, type FsTxnResult, type GpuInfo, type GpuVendor, type HostAdapter, type HostAdapterOptions, MergeError, NotImplementedError, PathContainmentError, type Plan, type PlanContext, type PlanFn, type PlanResult, type Platform, PlatformError, type ProbeAction, READONLY, type RemoveAction, type RemoveSummary, type RunOptions, type RunResult, type Runner, type Settings, SettingsError, VERSION, type VdiInfo, type Verdict, VerificationError, VerificationReport, type WriteAction, type WriteSummary, beginMarker, buildProgram, deepMerge, defaultRunner, derBase64ToPem, digest, doc, dynamicDigest, endMarker, ensureTrailingNewline, envBlock, exec, executePlan, fakeRunner, formatExport, frontmatter, indent, isPlainObject, jsonFile, lines, loadSettings, makeHostAdapter, managedBlock, missingToolRunner, parseCertLines, parseFirstInt, parseJsoncText, parseNvidiaSmi, parsePemBlocks, plan, probe, probeMany, readIfExists, registerCommands, remove, removeManagedBlock, resolveContents, resolvePlatform, retryTransient, safeCaPattern, stripTrailingNewlines, summarizeResult, upsertManagedBlock, upsertTextBlock, vdiFromEnv, writeArtifact, writeJson, writeText };
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ import {
49
49
  probeMany,
50
50
  readIfExists,
51
51
  registerCommands,
52
+ remove,
52
53
  removeManagedBlock,
53
54
  resolveContents,
54
55
  resolvePlatform,
@@ -62,7 +63,7 @@ import {
62
63
  writeArtifact,
63
64
  writeJson,
64
65
  writeText
65
- } from "./chunk-S7XFTZJW.js";
66
+ } from "./chunk-7GVBGS5N.js";
66
67
  export {
67
68
  ALL_COMMANDS,
68
69
  AihError,
@@ -114,6 +115,7 @@ export {
114
115
  probeMany,
115
116
  readIfExists,
116
117
  registerCommands,
118
+ remove,
117
119
  removeManagedBlock,
118
120
  resolveContents,
119
121
  resolvePlatform,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aihq/harness",
3
- "version": "0.2.0-rc.0",
3
+ "version": "0.3.0",
4
4
  "description": "Enterprise AI Bootstrapping Harness — bootstraps governed, proxy-safe AI coding into workstations and repos",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,7 +16,8 @@
16
16
  },
17
17
  "files": [
18
18
  "dist",
19
- "README.md"
19
+ "README.md",
20
+ "NOTICE"
20
21
  ],
21
22
  "engines": {
22
23
  "node": ">=20"
@@ -57,6 +58,8 @@
57
58
  "@biomejs/biome": "^2.5.1",
58
59
  "@types/node": "^26.0.1",
59
60
  "@vitest/coverage-v8": "^4.1.9",
61
+ "ajv": "^8.20.0",
62
+ "ajv-formats": "^3.0.1",
60
63
  "happy-dom": "^20.10.6",
61
64
  "tsup": "^8.5.1",
62
65
  "tsx": "^4.22.4",