@agentbridge1/cli 0.0.1

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.
Files changed (69) hide show
  1. package/bin/agentbridge.js +11 -0
  2. package/dist/acceptance-block.js +21 -0
  3. package/dist/acceptance-preflight.js +91 -0
  4. package/dist/api-client.js +6 -0
  5. package/dist/authority-request.js +25 -0
  6. package/dist/briefing.js +26 -0
  7. package/dist/bug-registry.js +350 -0
  8. package/dist/build-info.json +6 -0
  9. package/dist/canonical-state.js +11 -0
  10. package/dist/claimed-paths.js +42 -0
  11. package/dist/cli-failure-log.js +34 -0
  12. package/dist/commands/accept.js +241 -0
  13. package/dist/commands/attention.js +85 -0
  14. package/dist/commands/autopilot.js +93 -0
  15. package/dist/commands/bug.js +106 -0
  16. package/dist/commands/check.js +283 -0
  17. package/dist/commands/connect.js +159 -0
  18. package/dist/commands/dist-freshness.js +105 -0
  19. package/dist/commands/doctor.js +300 -0
  20. package/dist/commands/done.js +292 -0
  21. package/dist/commands/handoff.js +189 -0
  22. package/dist/commands/handshake.js +78 -0
  23. package/dist/commands/health.js +154 -0
  24. package/dist/commands/identity.js +57 -0
  25. package/dist/commands/init.js +5 -0
  26. package/dist/commands/memory.js +400 -0
  27. package/dist/commands/next.js +21 -0
  28. package/dist/commands/precommit-check.js +17 -0
  29. package/dist/commands/recover.js +116 -0
  30. package/dist/commands/session.js +229 -0
  31. package/dist/commands/setup-mcp.js +56 -0
  32. package/dist/commands/start.js +626 -0
  33. package/dist/commands/status.js +486 -0
  34. package/dist/commands/use.js +13 -0
  35. package/dist/commands/verify.js +264 -0
  36. package/dist/commands/version.js +32 -0
  37. package/dist/commands/watch.js +1718 -0
  38. package/dist/config.js +55 -0
  39. package/dist/domain-resolution.js +63 -0
  40. package/dist/error-catalog.js +494 -0
  41. package/dist/errors.js +276 -0
  42. package/dist/file-fingerprints.js +45 -0
  43. package/dist/gates.js +200 -0
  44. package/dist/git-evidence.js +285 -0
  45. package/dist/git-status.js +81 -0
  46. package/dist/http.js +151 -0
  47. package/dist/index.js +622 -0
  48. package/dist/init.js +458 -0
  49. package/dist/memory-context-render.js +51 -0
  50. package/dist/operator-snapshot.js +99 -0
  51. package/dist/precommit.js +72 -0
  52. package/dist/preflight-changed-files.js +109 -0
  53. package/dist/proof-guidance.js +110 -0
  54. package/dist/redact-secrets.js +15 -0
  55. package/dist/revert-crossing.js +73 -0
  56. package/dist/server-sync.js +433 -0
  57. package/dist/session-state.js +138 -0
  58. package/dist/session.js +89 -0
  59. package/dist/supervision.js +212 -0
  60. package/dist/terminal-ui.js +18 -0
  61. package/dist/test-runner.js +62 -0
  62. package/dist/types.js +2 -0
  63. package/dist/verification-conditions.js +185 -0
  64. package/dist/watch-core.js +208 -0
  65. package/dist/watch-packet-handshake.js +71 -0
  66. package/dist/watcher.js +62 -0
  67. package/dist/work-context-resolver.js +412 -0
  68. package/dist/work-contract.js +110 -0
  69. package/package.json +44 -0
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.repoDirtySnapshot = repoDirtySnapshot;
4
+ exports.preflightSyncChangedFiles = preflightSyncChangedFiles;
5
+ const claimed_paths_1 = require("./claimed-paths");
6
+ const file_fingerprints_1 = require("./file-fingerprints");
7
+ const git_status_1 = require("./git-status");
8
+ const session_state_1 = require("./session-state");
9
+ const server_sync_1 = require("./server-sync");
10
+ function normalizeChangedFiles(files) {
11
+ return [...new Set(files.map((file) => file.trim()).filter((file) => file.length > 0))].sort();
12
+ }
13
+ function buildChangedFileEntries(input) {
14
+ const { files, claimedPaths, activeLaneDomain, dirtyAtStart, fingerprintsByPath } = input;
15
+ return files.map((path) => {
16
+ const fingerprint = fingerprintsByPath.get(path);
17
+ const status = fingerprint?.exists === false ? "deleted" : dirtyAtStart.has(path) ? "modified" : "added";
18
+ const crossing = claimedPaths.length > 0 && !(0, claimed_paths_1.fileWithinClaimedPaths)(path, claimedPaths);
19
+ return {
20
+ path,
21
+ status,
22
+ owningDomain: null,
23
+ activeLaneDomain,
24
+ crossing,
25
+ severity: "tier_b",
26
+ };
27
+ });
28
+ }
29
+ /** Git working-tree paths (repo-relative), excluding .agentbridge noise. */
30
+ function repoDirtySnapshot() {
31
+ return normalizeChangedFiles((0, git_status_1.repoDirtySnapshotFromGitStatus)());
32
+ }
33
+ /**
34
+ * Sync changed files to the server before acceptance decisions.
35
+ * Merges watch-tracked files with a git dirty snapshot so check/done/handoff
36
+ * do not depend on a long-running watch process.
37
+ */
38
+ async function preflightSyncChangedFiles(ctx) {
39
+ const state = (0, session_state_1.readSessionState)();
40
+ if (!state?.serverSessionId) {
41
+ return { syncedFiles: [], observedFiles: [] };
42
+ }
43
+ const watchFiles = normalizeChangedFiles(state.changedFiles ?? []);
44
+ const gitFiles = repoDirtySnapshot();
45
+ const gitFileSet = new Set(gitFiles);
46
+ const claimedPaths = normalizeChangedFiles(state.claimedPaths ?? []);
47
+ // Phase 2/3: Filter out files that were already dirty when this session started
48
+ // and have NOT changed since (contamination control). Only include them once they
49
+ // acquire new content within the session.
50
+ const dirtyAtStart = new Set(state.dirtyFilesAtStart ?? []);
51
+ const startFingerprints = state.fingerprintsAtStart ?? {};
52
+ /** True when a git-dirty file is genuinely new work within this session. */
53
+ function isNewWorkFile(file) {
54
+ if (!dirtyAtStart.has(file))
55
+ return true; // wasn't dirty at start → always new work
56
+ // Was dirty at start – only include if content changed since start
57
+ const fps = (0, file_fingerprints_1.computeFileFingerprints)([file]);
58
+ const current = fps[0];
59
+ if (!current?.exists) {
60
+ // File now missing. Treat as new work only when it previously existed at start.
61
+ return startFingerprints[file] !== undefined;
62
+ }
63
+ const currentFp = current.sha256 ?? current.mtimeMs?.toString() ?? "";
64
+ const baseFp = startFingerprints[file];
65
+ return baseFp === undefined || currentFp !== baseFp;
66
+ }
67
+ const watchOnlyFiles = watchFiles.filter((file) => !gitFileSet.has(file));
68
+ const watchOnlyFingerprints = (0, file_fingerprints_1.computeFileFingerprints)(watchOnlyFiles);
69
+ const activeWatchOnlyFiles = watchOnlyFingerprints
70
+ .filter((fingerprint) => fingerprint.exists)
71
+ .map((fingerprint) => fingerprint.path);
72
+ const merged = normalizeChangedFiles([
73
+ ...watchFiles.filter((file) => gitFileSet.has(file)),
74
+ ...activeWatchOnlyFiles,
75
+ ...gitFiles.filter((file) => isNewWorkFile(file) &&
76
+ (claimedPaths.length === 0 || (0, claimed_paths_1.fileWithinClaimedPaths)(file, claimedPaths))),
77
+ ...gitFiles.filter((file) => isNewWorkFile(file) &&
78
+ claimedPaths.length > 0 &&
79
+ !(0, claimed_paths_1.fileWithinClaimedPaths)(file, claimedPaths)),
80
+ ]);
81
+ // Phase 4: Include content fingerprints in the hash so that changing the *content*
82
+ // of a file (without changing the file list) correctly triggers a new server sync.
83
+ // Also allow transitioning to an empty set to clear current server-side drift.
84
+ const fingerprints = (0, file_fingerprints_1.computeFileFingerprints)(merged);
85
+ const fpMap = new Map(fingerprints.map((fp) => [fp.path, fp.sha256 ?? fp.mtimeMs?.toString() ?? ""]));
86
+ const nextHash = merged.length === 0 ? "" : merged.map((f) => `${f}:${fpMap.get(f) ?? ""}`).join("\n");
87
+ if (state.lastSyncedChangedFilesHash === nextHash) {
88
+ return { syncedFiles: [], observedFiles: merged };
89
+ }
90
+ const fingerprintByPath = new Map(fingerprints.map((fp) => [fp.path, fp]));
91
+ const changedFileEntries = buildChangedFileEntries({
92
+ files: merged,
93
+ claimedPaths,
94
+ activeLaneDomain: state.laneDomain ?? null,
95
+ dirtyAtStart,
96
+ fingerprintsByPath: fingerprintByPath,
97
+ });
98
+ await (0, server_sync_1.postObservedDiff)(ctx, { workSessionId: state.serverSessionId }, {
99
+ activeLaneDomain: state.laneDomain ?? null,
100
+ changedFiles: merged,
101
+ changedFileEntries,
102
+ boundaryCrossings: [],
103
+ fileFingerprints: fingerprints,
104
+ });
105
+ state.changedFiles = merged;
106
+ state.lastSyncedChangedFilesHash = nextHash;
107
+ (0, session_state_1.writeSessionState)(state);
108
+ return { syncedFiles: merged, observedFiles: merged };
109
+ }
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.suggestVerifyCommands = suggestVerifyCommands;
4
+ exports.explainMissingProofItem = explainMissingProofItem;
5
+ exports.getMissingProofDetails = getMissingProofDetails;
6
+ function quoteForShell(input) {
7
+ return input.replace(/(["\\$`])/g, "\\$1");
8
+ }
9
+ function suggestVerifyCommands(changedFiles, detectedWorkType) {
10
+ const commands = [];
11
+ const unique = [...new Set(changedFiles.map((file) => file.trim()).filter((file) => file.length > 0))];
12
+ const docsFile = unique.find((file) => file.startsWith("docs/"));
13
+ const hasCli = unique.some((file) => file.startsWith("cli/"));
14
+ const hasBackend = unique.some((file) => file.startsWith("src/"));
15
+ if (detectedWorkType === "cli_git_enforcement") {
16
+ commands.push("agentbridge verify -- npm --prefix cli test");
17
+ commands.push("agentbridge verify -- npm --prefix cli run typecheck");
18
+ }
19
+ if (detectedWorkType === "documentation" && docsFile) {
20
+ commands.push(`agentbridge verify -- "test -f ${quoteForShell(docsFile)}"`);
21
+ commands.push('agentbridge verify -- "rg -n \\"TODO|TBD\\" docs/"');
22
+ }
23
+ if (detectedWorkType === "general") {
24
+ commands.push("agentbridge verify -- npm test");
25
+ }
26
+ if (hasCli) {
27
+ commands.push("agentbridge verify -- npm --prefix cli test");
28
+ }
29
+ if (hasBackend || detectedWorkType === "general") {
30
+ commands.push("agentbridge verify -- npm test");
31
+ }
32
+ if (docsFile) {
33
+ commands.push(`agentbridge verify -- "test -f ${quoteForShell(docsFile)}"`);
34
+ }
35
+ if (commands.length === 0 && unique.length > 0) {
36
+ commands.push(`agentbridge verify -- "test -f ${quoteForShell(unique[0])}"`);
37
+ }
38
+ if (commands.length === 0) {
39
+ commands.push("agentbridge verify -- <command>");
40
+ }
41
+ return [...new Set(commands)];
42
+ }
43
+ function explainMissingProofItem(item) {
44
+ const lower = item.toLowerCase();
45
+ if (lower.includes("verification")) {
46
+ return "Acceptance requires a fresh verification run that covers the changed files.";
47
+ }
48
+ if (lower.includes("handoff")) {
49
+ return "Acceptance requires a posted handoff before the session can be closed.";
50
+ }
51
+ if (lower.includes("scope") || lower.includes("boundary")) {
52
+ return "Acceptance requires proof that work stayed within the declared lane and boundaries.";
53
+ }
54
+ return "Acceptance is blocked until this required proof artifact is provided.";
55
+ }
56
+ function codeFromMissingProof(item) {
57
+ const lower = item.toLowerCase();
58
+ if (lower.includes("verification"))
59
+ return "verification_required";
60
+ if (lower.includes("handoff"))
61
+ return "handoff_required";
62
+ if (lower.includes("scope") || lower.includes("boundary"))
63
+ return "scope_boundary_required";
64
+ return "required_proof_missing";
65
+ }
66
+ function sourceRuleFromMissingProof(item) {
67
+ const lower = item.toLowerCase();
68
+ if (lower.includes("verification"))
69
+ return "acceptance.required_proof.verification";
70
+ if (lower.includes("handoff"))
71
+ return "acceptance.required_proof.handoff";
72
+ if (lower.includes("scope") || lower.includes("boundary")) {
73
+ return "acceptance.required_proof.scope-boundary";
74
+ }
75
+ return "acceptance.required_proof.general";
76
+ }
77
+ function proofRecipeFor(workType, changedFiles) {
78
+ if (workType === "documentation") {
79
+ return "Validate changed docs exist and are consistent, then record that command output.";
80
+ }
81
+ if (workType === "cli_git_enforcement") {
82
+ return "Run CLI tests + typecheck, then rerun the highest-signal CLI verify command.";
83
+ }
84
+ if (changedFiles.some((file) => file.startsWith("src/"))) {
85
+ return "Run focused module tests and then a broader project test command.";
86
+ }
87
+ return "Run the smallest command proving the changed files behave as intended.";
88
+ }
89
+ function getMissingProofDetails(report) {
90
+ if (report.evidence_missing_details && report.evidence_missing_details.length > 0) {
91
+ return report.evidence_missing_details.map((detail) => ({
92
+ code: detail.code,
93
+ message: detail.message,
94
+ why: detail.why,
95
+ source_rule: detail.source_rule,
96
+ proof_recipe: detail.proof_recipe,
97
+ suggested_verify: detail.suggested_verify,
98
+ }));
99
+ }
100
+ const fallbackSuggested = suggestVerifyCommands(report.changed_files, report.detected_work_type)[0];
101
+ const proofRecipe = proofRecipeFor(report.detected_work_type, report.changed_files);
102
+ return report.evidence_missing.map((item) => ({
103
+ code: codeFromMissingProof(item),
104
+ message: item,
105
+ why: explainMissingProofItem(item),
106
+ source_rule: sourceRuleFromMissingProof(item),
107
+ proof_recipe: proofRecipe,
108
+ ...(fallbackSuggested ? { suggested_verify: fallbackSuggested } : {}),
109
+ }));
110
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.redactSecrets = redactSecrets;
4
+ const SECRET_PATTERNS = [
5
+ /(?:api[_-]?key|token|secret|password|authorization|bearer)\s*[:=]\s*\S+/gi,
6
+ /sk-[a-zA-Z0-9]{20,}/g,
7
+ /eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+/g,
8
+ ];
9
+ function redactSecrets(value) {
10
+ let out = value;
11
+ for (const pattern of SECRET_PATTERNS) {
12
+ out = out.replace(pattern, "[REDACTED]");
13
+ }
14
+ return out;
15
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.revertCrossingFile = revertCrossingFile;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ function run(command) {
8
+ return (0, node_child_process_1.execSync)(command, {
9
+ encoding: "utf8",
10
+ stdio: ["ignore", "pipe", "pipe"],
11
+ });
12
+ }
13
+ function shellQuote(path) {
14
+ return `'${path.replace(/'/g, `'\\''`)}'`;
15
+ }
16
+ function inHead(path) {
17
+ try {
18
+ (0, node_child_process_1.execSync)(`git cat-file -e HEAD:${shellQuote(path)}`, {
19
+ stdio: ["ignore", "ignore", "ignore"],
20
+ });
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ function statusCode(path) {
28
+ const output = run(`git status --porcelain -- ${shellQuote(path)}`);
29
+ const line = output
30
+ .split("\n")
31
+ .map((v) => v.trimEnd())
32
+ .find((v) => v.trim().length > 0);
33
+ if (!line)
34
+ return " ";
35
+ return line.slice(0, 2);
36
+ }
37
+ function removeEmptyParents(path, repoRoot) {
38
+ let cursor = (0, node_path_1.dirname)((0, node_path_1.resolve)(repoRoot, path));
39
+ const root = (0, node_path_1.resolve)(repoRoot);
40
+ while (cursor.startsWith(root) && cursor !== root) {
41
+ try {
42
+ (0, node_fs_1.rmSync)(cursor);
43
+ cursor = (0, node_path_1.dirname)(cursor);
44
+ }
45
+ catch {
46
+ break;
47
+ }
48
+ }
49
+ }
50
+ function revertCrossingFile(path, repoRoot = process.cwd()) {
51
+ const code = statusCode(path);
52
+ const trackedInHead = inHead(path);
53
+ const absolute = (0, node_path_1.resolve)(repoRoot, path);
54
+ const x = code[0] ?? " ";
55
+ const isUntracked = code === "??";
56
+ const isStaged = x !== " " && x !== "?";
57
+ if (trackedInHead) {
58
+ run(`git checkout HEAD -- ${shellQuote(path)}`);
59
+ return;
60
+ }
61
+ if (isStaged) {
62
+ try {
63
+ run(`git rm -f --cached -- ${shellQuote(path)}`);
64
+ }
65
+ catch {
66
+ // Continue with filesystem cleanup.
67
+ }
68
+ }
69
+ if (isUntracked || (0, node_fs_1.existsSync)(absolute)) {
70
+ (0, node_fs_1.rmSync)(absolute, { force: true });
71
+ removeEmptyParents(path, repoRoot);
72
+ }
73
+ }