@autopilot-harness/cli 0.1.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.
Files changed (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -0
  3. package/assets/autopilot-harness-hook.mjs +193 -0
  4. package/assets/vendor/migrations/001_initial.sql +62 -0
  5. package/assets/vendor/migrations/002_pending_followup.sql +4 -0
  6. package/assets/vendor/migrations/003_reviewing_item.sql +1 -0
  7. package/assets/vendor/runtime.mjs +5524 -0
  8. package/dist/assets/autopilot-harness-hook.mjs +193 -0
  9. package/dist/assets/vendor/migrations/001_initial.sql +62 -0
  10. package/dist/assets/vendor/migrations/002_pending_followup.sql +4 -0
  11. package/dist/assets/vendor/migrations/003_reviewing_item.sql +1 -0
  12. package/dist/assets/vendor/runtime.mjs +5524 -0
  13. package/dist/bin.d.ts +3 -0
  14. package/dist/bin.d.ts.map +1 -0
  15. package/dist/bin.js +300 -0
  16. package/dist/bin.js.map +1 -0
  17. package/dist/index.d.ts +23 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +13 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/init/config-merge.d.ts +27 -0
  22. package/dist/init/config-merge.d.ts.map +1 -0
  23. package/dist/init/config-merge.js +115 -0
  24. package/dist/init/config-merge.js.map +1 -0
  25. package/dist/init/default-config.d.ts +17 -0
  26. package/dist/init/default-config.d.ts.map +1 -0
  27. package/dist/init/default-config.js +101 -0
  28. package/dist/init/default-config.js.map +1 -0
  29. package/dist/init/hooks-merge.d.ts +25 -0
  30. package/dist/init/hooks-merge.d.ts.map +1 -0
  31. package/dist/init/hooks-merge.js +134 -0
  32. package/dist/init/hooks-merge.js.map +1 -0
  33. package/dist/init/install.d.ts +31 -0
  34. package/dist/init/install.d.ts.map +1 -0
  35. package/dist/init/install.js +829 -0
  36. package/dist/init/install.js.map +1 -0
  37. package/dist/init/platforms.d.ts +60 -0
  38. package/dist/init/platforms.d.ts.map +1 -0
  39. package/dist/init/platforms.js +275 -0
  40. package/dist/init/platforms.js.map +1 -0
  41. package/dist/init/tui.d.ts +65 -0
  42. package/dist/init/tui.d.ts.map +1 -0
  43. package/dist/init/tui.js +549 -0
  44. package/dist/init/tui.js.map +1 -0
  45. package/dist/init/types.d.ts +75 -0
  46. package/dist/init/types.d.ts.map +1 -0
  47. package/dist/init/types.js +9 -0
  48. package/dist/init/types.js.map +1 -0
  49. package/dist/init/wizard-copy.d.ts +74 -0
  50. package/dist/init/wizard-copy.d.ts.map +1 -0
  51. package/dist/init/wizard-copy.js +173 -0
  52. package/dist/init/wizard-copy.js.map +1 -0
  53. package/dist/init/wizard-helpers.d.ts +103 -0
  54. package/dist/init/wizard-helpers.d.ts.map +1 -0
  55. package/dist/init/wizard-helpers.js +724 -0
  56. package/dist/init/wizard-helpers.js.map +1 -0
  57. package/dist/locale-set.d.ts +24 -0
  58. package/dist/locale-set.d.ts.map +1 -0
  59. package/dist/locale-set.js +286 -0
  60. package/dist/locale-set.js.map +1 -0
  61. package/dist/names.d.ts +5 -0
  62. package/dist/names.d.ts.map +1 -0
  63. package/dist/names.js +5 -0
  64. package/dist/names.js.map +1 -0
  65. package/dist/project-fs.d.ts +48 -0
  66. package/dist/project-fs.d.ts.map +1 -0
  67. package/dist/project-fs.js +210 -0
  68. package/dist/project-fs.js.map +1 -0
  69. package/dist/read-untrusted-file.d.ts +41 -0
  70. package/dist/read-untrusted-file.d.ts.map +1 -0
  71. package/dist/read-untrusted-file.js +362 -0
  72. package/dist/read-untrusted-file.js.map +1 -0
  73. package/dist/session.d.ts +38 -0
  74. package/dist/session.d.ts.map +1 -0
  75. package/dist/session.js +242 -0
  76. package/dist/session.js.map +1 -0
  77. package/dist/status-doctor.d.ts +22 -0
  78. package/dist/status-doctor.d.ts.map +1 -0
  79. package/dist/status-doctor.js +712 -0
  80. package/dist/status-doctor.js.map +1 -0
  81. package/dist/uninstall.d.ts +31 -0
  82. package/dist/uninstall.d.ts.map +1 -0
  83. package/dist/uninstall.js +333 -0
  84. package/dist/uninstall.js.map +1 -0
  85. package/dist/upgrade.d.ts +29 -0
  86. package/dist/upgrade.d.ts.map +1 -0
  87. package/dist/upgrade.js +290 -0
  88. package/dist/upgrade.js.map +1 -0
  89. package/dist/vendor-entry.d.ts +6 -0
  90. package/dist/vendor-entry.d.ts.map +1 -0
  91. package/dist/vendor-entry.js +16 -0
  92. package/dist/vendor-entry.js.map +1 -0
  93. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Autopilot Harness contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # @autopilot-harness/cli
2
+
3
+ CLI for **Autopilot** — a vibecoding harness that turns open-ended agent chat into **structured planning → checklist execution → multi-lens self-review**.
4
+
5
+ Bin name: `autopilot-harness`. Requires **Node.js 22+**. **v0.1 is Cursor-first.**
6
+
7
+ Autopilot does **not** guarantee bug-free software. It raises confidence that work was planned, checklist-scoped, and pressure-tested under several review lenses.
8
+
9
+ ## Install & init
10
+
11
+ From the app you want to instrument (`cwd` = that project):
12
+
13
+ ```bash
14
+ npx @autopilot-harness/cli init --platform cursor --yes
15
+ ```
16
+
17
+ Interactive TUI (platform still defaults to cursor):
18
+
19
+ ```bash
20
+ npx @autopilot-harness/cli init
21
+ ```
22
+
23
+ Then reload the Cursor window (or start a new Agent chat) and run:
24
+
25
+ 1. `/autopilot-on` — plan (grill → `plans/<slug>/`)
26
+ 2. `/autopilot-run` — execute the checklist
27
+
28
+ ## Useful commands
29
+
30
+ ```bash
31
+ npx @autopilot-harness/cli status
32
+ npx @autopilot-harness/cli doctor
33
+ npx @autopilot-harness/cli upgrade --dry-run
34
+ npx @autopilot-harness/cli --help
35
+ ```
36
+
37
+ Global install (optional):
38
+
39
+ ```bash
40
+ npm i -g @autopilot-harness/cli
41
+ autopilot-harness init --platform cursor --yes
42
+ ```
43
+
44
+ Use the scoped package name (`@autopilot-harness/cli`). There is no bare npm package named `autopilot-harness`.
45
+
46
+ ## Docs
47
+
48
+ - [GitHub README](https://github.com/mt2007/autopilot-harness#readme) — product overview
49
+ - [Quickstart](https://github.com/mt2007/autopilot-harness/blob/main/docs/autopilot/quickstart.md)
50
+ - [Config](https://github.com/mt2007/autopilot-harness/blob/main/docs/config.md)
51
+ - [Troubleshooting](https://github.com/mt2007/autopilot-harness/blob/main/docs/troubleshooting.md)
52
+
53
+ ## License
54
+
55
+ MIT — see [LICENSE](https://github.com/mt2007/autopilot-harness/blob/main/LICENSE).
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Cursor hook entry — marker: autopilot-harness
3
+ * Installed at .autopilot/bin/autopilot-harness-hook.mjs (copy, not symlink).
4
+ *
5
+ * Prefers bundled vendor/runtime.mjs (shipped by init/upgrade) so empty
6
+ * consumer projects work without @autopilot-harness/* in node_modules.
7
+ * Falls back to project-local packages, then fail-open.
8
+ */
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+ import { createRequire } from "node:module";
12
+ import { fileURLToPath, pathToFileURL } from "node:url";
13
+
14
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
+ const projectRoot = path.resolve(__dirname, "..", "..");
16
+
17
+ function parseArgs(argv) {
18
+ const allowed = new Set([
19
+ "beforeSubmitPrompt",
20
+ "afterFileEdit",
21
+ "stop",
22
+ ]);
23
+ const out = { event: "beforeSubmitPrompt" };
24
+ for (let i = 0; i < argv.length; i++) {
25
+ if (argv[i] === "--event" && argv[i + 1]) {
26
+ const ev = argv[++i];
27
+ out.event = allowed.has(ev) ? ev : "beforeSubmitPrompt";
28
+ }
29
+ }
30
+ return out;
31
+ }
32
+
33
+ async function readStdin() {
34
+ const chunks = [];
35
+ for await (const chunk of process.stdin) chunks.push(chunk);
36
+ const raw = Buffer.concat(chunks).toString("utf8").trim();
37
+ if (!raw) return {};
38
+ try {
39
+ return JSON.parse(raw);
40
+ } catch {
41
+ return {};
42
+ }
43
+ }
44
+
45
+ async function tryImport(specifier) {
46
+ try {
47
+ return await import(specifier);
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function isSymlinkOrUnreadable(filePath) {
54
+ try {
55
+ return fs.lstatSync(filePath).isSymbolicLink();
56
+ } catch {
57
+ // Cannot verify — refuse vendor load (fail-closed, match CLI policy).
58
+ return true;
59
+ }
60
+ }
61
+
62
+ /** Refuse vendor paths whose realpath escapes the project root. */
63
+ function realpathEscapesProject(filePath) {
64
+ try {
65
+ const realRoot = fs.realpathSync(projectRoot);
66
+ const real = fs.realpathSync(filePath);
67
+ return real !== realRoot && !real.startsWith(realRoot + path.sep);
68
+ } catch {
69
+ return true;
70
+ }
71
+ }
72
+
73
+ async function loadVendorRuntime() {
74
+ const vendorDir = path.join(__dirname, "vendor");
75
+ const vendor = path.join(vendorDir, "runtime.mjs");
76
+ const migDir = path.join(vendorDir, "migrations");
77
+ const mig = path.join(migDir, "001_initial.sql");
78
+ if (!fs.existsSync(vendor) || !fs.existsSync(mig)) return null;
79
+ // Refuse symlink escape / unreadable lstat (same policy as CLI).
80
+ if (
81
+ isSymlinkOrUnreadable(vendorDir) ||
82
+ isSymlinkOrUnreadable(vendor) ||
83
+ isSymlinkOrUnreadable(migDir) ||
84
+ isSymlinkOrUnreadable(mig)
85
+ ) {
86
+ return null;
87
+ }
88
+ if (realpathEscapesProject(vendor) || realpathEscapesProject(mig)) {
89
+ return null;
90
+ }
91
+ return tryImport(pathToFileURL(vendor).href);
92
+ }
93
+
94
+ async function loadPortFromNodeModules() {
95
+ try {
96
+ const require = createRequire(path.join(projectRoot, "package.json"));
97
+ const resolved = require.resolve("@autopilot-harness/port-cursor");
98
+ return tryImport(pathToFileURL(resolved).href);
99
+ } catch {
100
+ return null;
101
+ }
102
+ }
103
+
104
+ async function loadCoreFromNodeModules() {
105
+ try {
106
+ const require = createRequire(path.join(projectRoot, "package.json"));
107
+ const resolved = require.resolve("@autopilot-harness/core");
108
+ return tryImport(pathToFileURL(resolved).href);
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+
114
+ function failOpen(event) {
115
+ if (event === "beforeSubmitPrompt") {
116
+ writeReply(JSON.stringify({ continue: true }));
117
+ } else {
118
+ writeReply("{}");
119
+ }
120
+ }
121
+
122
+ /** At-most-once stdout so fail-open cannot append a second JSON blob. */
123
+ let replied = false;
124
+ function writeReply(text) {
125
+ if (replied) return;
126
+ process.stdout.write(text);
127
+ // Set only after a successful write so failOpen can still retry on throw.
128
+ replied = true;
129
+ }
130
+
131
+ async function main() {
132
+ const { event } = parseArgs(process.argv.slice(2));
133
+ try {
134
+ const payload = await readStdin();
135
+
136
+ const vendor = await loadVendorRuntime();
137
+ const port = vendor ?? (await loadPortFromNodeModules());
138
+ const coreMod = vendor ?? (await loadCoreFromNodeModules());
139
+ if (!port?.handleBeforeSubmitPrompt || !coreMod?.StateStore) {
140
+ failOpen(event);
141
+ return;
142
+ }
143
+
144
+ const store = new coreMod.StateStore(projectRoot);
145
+ try {
146
+ if (event === "beforeSubmitPrompt") {
147
+ const result = port.handleBeforeSubmitPrompt(store, payload, projectRoot);
148
+ writeReply(JSON.stringify(result));
149
+ return;
150
+ }
151
+ if (event === "afterFileEdit") {
152
+ port.handleAfterFileEdit?.(store, payload, projectRoot);
153
+ writeReply("{}");
154
+ return;
155
+ }
156
+ if (event === "stop") {
157
+ // Vendor injects locale; core still applies config.yml. Fall back if
158
+ // an older vendor/runtime lacks the factory (upgrade mid-flight).
159
+ const engine =
160
+ typeof coreMod.createConfiguredReviewEngine === "function"
161
+ ? coreMod.createConfiguredReviewEngine(store, projectRoot)
162
+ : new coreMod.ReviewEngine(store, {
163
+ confirmRounds: 5,
164
+ reviewScope: "executing_only",
165
+ verifyEnabled: false,
166
+ verifyCommands: [],
167
+ maxIdleStops: 5,
168
+ maxErrorsBeforePause: 0,
169
+ projectRoot,
170
+ });
171
+ const result = port.handleStop(engine, payload);
172
+ writeReply(JSON.stringify(result ?? {}));
173
+ return;
174
+ }
175
+ writeReply("{}");
176
+ } finally {
177
+ try {
178
+ store.close();
179
+ } catch {
180
+ /* ignore */
181
+ }
182
+ }
183
+ } catch (err) {
184
+ console.error("[autopilot-harness] hook error:", err?.message ?? err);
185
+ failOpen(event);
186
+ }
187
+ }
188
+
189
+ main().catch((err) => {
190
+ console.error("[autopilot-harness] hook error:", err?.message ?? err);
191
+ failOpen("beforeSubmitPrompt");
192
+ process.exitCode = 0;
193
+ });
@@ -0,0 +1,62 @@
1
+ CREATE TABLE IF NOT EXISTS _schema_meta (
2
+ key TEXT PRIMARY KEY,
3
+ value TEXT NOT NULL
4
+ );
5
+
6
+ CREATE TABLE IF NOT EXISTS workspace (
7
+ root_path TEXT PRIMARY KEY,
8
+ token_hash TEXT,
9
+ platform TEXT NOT NULL,
10
+ surface TEXT NOT NULL DEFAULT 'ide',
11
+ integration TEXT NOT NULL DEFAULT 'hook',
12
+ locale TEXT NOT NULL DEFAULT 'en',
13
+ created_at TEXT NOT NULL
14
+ );
15
+
16
+ CREATE TABLE IF NOT EXISTS sessions (
17
+ conversation_id TEXT PRIMARY KEY,
18
+ platform TEXT NOT NULL,
19
+ session_title TEXT,
20
+ session_title_source TEXT,
21
+ title_updated_at TEXT,
22
+ track_id TEXT NOT NULL DEFAULT '_pending',
23
+ track_title TEXT,
24
+ checklist_path TEXT NOT NULL DEFAULT '',
25
+ phase TEXT NOT NULL DEFAULT 'idle',
26
+ armed INTEGER NOT NULL DEFAULT 0,
27
+ paused INTEGER NOT NULL DEFAULT 0,
28
+ paused_reason TEXT,
29
+ pending_action TEXT,
30
+ track_candidates_json TEXT,
31
+ project_root TEXT NOT NULL,
32
+ code_root TEXT NOT NULL,
33
+ worktree_path TEXT,
34
+ worktree_branch TEXT,
35
+ error_count INTEGER NOT NULL DEFAULT 0,
36
+ last_error TEXT,
37
+ idle_stop_count INTEGER NOT NULL DEFAULT 0,
38
+ cli_bound_at TEXT,
39
+ last_active_at TEXT NOT NULL,
40
+ updated_at TEXT NOT NULL
41
+ );
42
+
43
+ CREATE TABLE IF NOT EXISTS tracks (
44
+ track_id TEXT PRIMARY KEY,
45
+ slug TEXT NOT NULL,
46
+ checklist_path TEXT NOT NULL,
47
+ plan_path TEXT,
48
+ brief_path TEXT,
49
+ updated_at TEXT NOT NULL
50
+ );
51
+
52
+ CREATE TABLE IF NOT EXISTS review_chains (
53
+ conversation_id TEXT PRIMARY KEY,
54
+ fix_round INTEGER NOT NULL DEFAULT 0,
55
+ confirm_left INTEGER,
56
+ chain_pending INTEGER NOT NULL DEFAULT 0,
57
+ code_edited INTEGER NOT NULL DEFAULT 0,
58
+ item_confirm_complete INTEGER NOT NULL DEFAULT 0,
59
+ updated_at TEXT NOT NULL
60
+ );
61
+
62
+ INSERT OR IGNORE INTO _schema_meta (key, value) VALUES ('schema_version', '1');
@@ -0,0 +1,4 @@
1
+ -- Pending followup redelivery (Cursor may drop stop followups).
2
+ ALTER TABLE review_chains ADD COLUMN pending_followup TEXT;
3
+ ALTER TABLE review_chains ADD COLUMN pending_followup_at TEXT;
4
+ ALTER TABLE review_chains ADD COLUMN pending_redeliver_at TEXT;
@@ -0,0 +1 @@
1
+ ALTER TABLE review_chains ADD COLUMN reviewing_item_id TEXT;