@archme/cli 0.4.5 → 0.5.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/README.md +26 -0
- package/dist/cli.js +667 -87
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,6 +42,28 @@ Watching a repo needs the ArchMe GitHub App installed on it; `archme init` tells
|
|
|
42
42
|
you if that step is still missing. Not up for hosting the agent yourself? Get in
|
|
43
43
|
touch at [archme.io](https://archme.io) and we'll run it for you.
|
|
44
44
|
|
|
45
|
+
## 3. Remove it
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
archme uninstall
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
From inside the repo. It lists everything it will change and asks before doing
|
|
52
|
+
any of it:
|
|
53
|
+
|
|
54
|
+
- **The agent config** — the `archme` MCP server from `.mcp.json`, the ArchMe
|
|
55
|
+
block from `CLAUDE.md`, the architecture skill. Your own config in those files
|
|
56
|
+
is left alone, and a file we created is deleted rather than left empty.
|
|
57
|
+
- **Watching** — so commits stop updating the architecture. Removing the config
|
|
58
|
+
on its own would leave the board moving while looking uninstalled, which is
|
|
59
|
+
why this is the default; `--keep-watching` opts out.
|
|
60
|
+
- **The daemon** — but only if no other repo is left for it to serve. It's one
|
|
61
|
+
service per account, so uninstalling one repo never stops the others.
|
|
62
|
+
|
|
63
|
+
Your architecture on archme.io and this machine's login survive (`archme logout`
|
|
64
|
+
for the latter). The ArchMe GitHub App installation is org-wide, so it stays —
|
|
65
|
+
remove it on github.com if you want it gone.
|
|
66
|
+
|
|
45
67
|
---
|
|
46
68
|
|
|
47
69
|
## Other commands
|
|
@@ -49,6 +71,7 @@ touch at [archme.io](https://archme.io) and we'll run it for you.
|
|
|
49
71
|
| Command | Purpose |
|
|
50
72
|
|---|---|
|
|
51
73
|
| `archme init [repo-path]` | Guided setup + scan (see above); `--force` re-scans |
|
|
74
|
+
| `archme uninstall [repo-path]` | Stop ArchMe for a repo: agent config, watching, and the daemon if idle |
|
|
52
75
|
| `archme scan <repo-path>` | Low-level single-pass scan into an explicit `--project` / `--branch` |
|
|
53
76
|
| `archme daemon <sub>` | `start` · `stop` · `restart` · `status` · `logs` · `enable` · `disable` · `install` · `uninstall` · `run` |
|
|
54
77
|
| `archme analyze <sub>` | Tree-sitter code analysis: `outline` · `repo` · `diff` · `match` · `prefetch` |
|
|
@@ -61,6 +84,9 @@ Every command takes `--help`. Useful flags:
|
|
|
61
84
|
(scans are interactive by default, so you can steer them as they go).
|
|
62
85
|
- `archme daemon status` — is it installed, running, and signed in?
|
|
63
86
|
`archme daemon logs -f` follows the log.
|
|
87
|
+
- `archme uninstall --keep-watching` — removes the agent config but leaves the
|
|
88
|
+
repo watched and the daemon running (see above); `--yes` skips the
|
|
89
|
+
confirmation. Run it from anywhere in the repo — it resolves the repo root.
|
|
64
90
|
- `archme analyze outline <file>` — JSON symbol outline for one file; `repo`
|
|
65
91
|
outlines everything, `diff <range>` only what a git range touched. Supports
|
|
66
92
|
TypeScript, TSX, JavaScript, Python, Go, Java, Ruby, Rust, Swift, and Kotlin.
|
package/dist/cli.js
CHANGED
|
@@ -1091,12 +1091,12 @@ var require_isexe = __commonJS({
|
|
|
1091
1091
|
if (typeof Promise !== "function") {
|
|
1092
1092
|
throw new TypeError("callback not provided");
|
|
1093
1093
|
}
|
|
1094
|
-
return new Promise(function(
|
|
1094
|
+
return new Promise(function(resolve6, reject) {
|
|
1095
1095
|
isexe(path7, options || {}, function(er, is) {
|
|
1096
1096
|
if (er) {
|
|
1097
1097
|
reject(er);
|
|
1098
1098
|
} else {
|
|
1099
|
-
|
|
1099
|
+
resolve6(is);
|
|
1100
1100
|
}
|
|
1101
1101
|
});
|
|
1102
1102
|
});
|
|
@@ -1162,27 +1162,27 @@ var require_which = __commonJS({
|
|
|
1162
1162
|
opt = {};
|
|
1163
1163
|
const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt);
|
|
1164
1164
|
const found = [];
|
|
1165
|
-
const step = (i2) => new Promise((
|
|
1165
|
+
const step = (i2) => new Promise((resolve6, reject) => {
|
|
1166
1166
|
if (i2 === pathEnv.length)
|
|
1167
|
-
return opt.all && found.length ?
|
|
1167
|
+
return opt.all && found.length ? resolve6(found) : reject(getNotFoundError(cmd));
|
|
1168
1168
|
const ppRaw = pathEnv[i2];
|
|
1169
1169
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
1170
1170
|
const pCmd = path7.join(pathPart, cmd);
|
|
1171
1171
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
1172
|
-
|
|
1172
|
+
resolve6(subStep(p, i2, 0));
|
|
1173
1173
|
});
|
|
1174
|
-
const subStep = (p, i2, ii) => new Promise((
|
|
1174
|
+
const subStep = (p, i2, ii) => new Promise((resolve6, reject) => {
|
|
1175
1175
|
if (ii === pathExt.length)
|
|
1176
|
-
return
|
|
1176
|
+
return resolve6(step(i2 + 1));
|
|
1177
1177
|
const ext2 = pathExt[ii];
|
|
1178
1178
|
isexe(p + ext2, { pathExt: pathExtExe }, (er, is) => {
|
|
1179
1179
|
if (!er && is) {
|
|
1180
1180
|
if (opt.all)
|
|
1181
1181
|
found.push(p + ext2);
|
|
1182
1182
|
else
|
|
1183
|
-
return
|
|
1183
|
+
return resolve6(p + ext2);
|
|
1184
1184
|
}
|
|
1185
|
-
return
|
|
1185
|
+
return resolve6(subStep(p, i2, ii + 1));
|
|
1186
1186
|
});
|
|
1187
1187
|
});
|
|
1188
1188
|
return cb ? step(0).then((res) => cb(null, res), cb) : step(0);
|
|
@@ -2262,8 +2262,8 @@ var init_deferred = __esm({
|
|
|
2262
2262
|
"node_modules/execa/lib/utils/deferred.js"() {
|
|
2263
2263
|
createDeferred = () => {
|
|
2264
2264
|
const methods = {};
|
|
2265
|
-
const promise = new Promise((
|
|
2266
|
-
Object.assign(methods, { resolve:
|
|
2265
|
+
const promise = new Promise((resolve6, reject) => {
|
|
2266
|
+
Object.assign(methods, { resolve: resolve6, reject });
|
|
2267
2267
|
});
|
|
2268
2268
|
return Object.assign(promise, methods);
|
|
2269
2269
|
};
|
|
@@ -7560,11 +7560,11 @@ var init_concurrent = __esm({
|
|
|
7560
7560
|
const promises = weakMap.get(stream);
|
|
7561
7561
|
const promise = createDeferred();
|
|
7562
7562
|
promises.push(promise);
|
|
7563
|
-
const
|
|
7564
|
-
return { resolve:
|
|
7563
|
+
const resolve6 = promise.resolve.bind(promise);
|
|
7564
|
+
return { resolve: resolve6, promises };
|
|
7565
7565
|
};
|
|
7566
|
-
waitForConcurrentStreams = async ({ resolve:
|
|
7567
|
-
|
|
7566
|
+
waitForConcurrentStreams = async ({ resolve: resolve6, promises }, subprocess) => {
|
|
7567
|
+
resolve6();
|
|
7568
7568
|
const [isSubprocessExit] = await Promise.race([
|
|
7569
7569
|
Promise.allSettled([true, subprocess]),
|
|
7570
7570
|
Promise.all([false, ...promises])
|
|
@@ -8636,7 +8636,7 @@ async function runLogin() {
|
|
|
8636
8636
|
const expiresAt = Date.now() + data.expires_in * 1e3;
|
|
8637
8637
|
let intervalMs = Math.max(data.interval, 1) * 1e3;
|
|
8638
8638
|
while (Date.now() < expiresAt) {
|
|
8639
|
-
await new Promise((
|
|
8639
|
+
await new Promise((resolve6) => setTimeout(resolve6, intervalMs));
|
|
8640
8640
|
const poll = await postJson("/poll", {
|
|
8641
8641
|
device_code: data.device_code
|
|
8642
8642
|
});
|
|
@@ -8877,7 +8877,7 @@ var init_launchctl = __esm({
|
|
|
8877
8877
|
if (r.exitCode === 0 || r.exitCode === 17) return;
|
|
8878
8878
|
if (r.exitCode === 5) {
|
|
8879
8879
|
await this.opts.spawner.run("launchctl", ["bootout", SERVICE_TARGET(this.opts.uid)]);
|
|
8880
|
-
await new Promise((
|
|
8880
|
+
await new Promise((resolve6) => setTimeout(resolve6, 500));
|
|
8881
8881
|
r = await bootstrap();
|
|
8882
8882
|
if (r.exitCode === 0 || r.exitCode === 17) return;
|
|
8883
8883
|
}
|
|
@@ -9022,6 +9022,13 @@ var init_systemctl = __esm({
|
|
|
9022
9022
|
});
|
|
9023
9023
|
|
|
9024
9024
|
// src/daemon/service/serviceController.ts
|
|
9025
|
+
var serviceController_exports = {};
|
|
9026
|
+
__export(serviceController_exports, {
|
|
9027
|
+
orchestrateStart: () => orchestrateStart,
|
|
9028
|
+
orchestrateStop: () => orchestrateStop,
|
|
9029
|
+
pickController: () => pickController,
|
|
9030
|
+
reinstallIfChanged: () => reinstallIfChanged
|
|
9031
|
+
});
|
|
9025
9032
|
import { userInfo } from "node:os";
|
|
9026
9033
|
function pickController(platform3 = process.platform, env = process.env, spawner = execaSpawner) {
|
|
9027
9034
|
const unitPath = resolveUnitPath(platform3, env);
|
|
@@ -9256,7 +9263,7 @@ async function tailLog(opts = {}) {
|
|
|
9256
9263
|
}
|
|
9257
9264
|
}
|
|
9258
9265
|
if (!opts.follow) return;
|
|
9259
|
-
await new Promise((
|
|
9266
|
+
await new Promise((resolve6, reject) => {
|
|
9260
9267
|
let fileWatcher = null;
|
|
9261
9268
|
let dirWatcher = null;
|
|
9262
9269
|
const dir = dirname6(path7);
|
|
@@ -9320,7 +9327,7 @@ async function tailLog(opts = {}) {
|
|
|
9320
9327
|
const handleSigint = () => {
|
|
9321
9328
|
fileWatcher?.close();
|
|
9322
9329
|
dirWatcher?.close();
|
|
9323
|
-
|
|
9330
|
+
resolve6();
|
|
9324
9331
|
};
|
|
9325
9332
|
process.once("SIGINT", handleSigint);
|
|
9326
9333
|
});
|
|
@@ -14258,7 +14265,7 @@ var init_semaphore = __esm({
|
|
|
14258
14265
|
acquire() {
|
|
14259
14266
|
const slot = this.tryAcquire();
|
|
14260
14267
|
if (slot) return Promise.resolve(slot);
|
|
14261
|
-
return new Promise((
|
|
14268
|
+
return new Promise((resolve6) => this.waiters.push(resolve6));
|
|
14262
14269
|
}
|
|
14263
14270
|
makeRelease() {
|
|
14264
14271
|
let released = false;
|
|
@@ -16118,12 +16125,12 @@ async function rpcHeartbeat(ctx, jobId, deviceId) {
|
|
|
16118
16125
|
);
|
|
16119
16126
|
if (error) console.error(`[${jobId}] heartbeat error:`, error.message);
|
|
16120
16127
|
}
|
|
16121
|
-
async function rpcComplete(ctx, jobId, deviceId,
|
|
16128
|
+
async function rpcComplete(ctx, jobId, deviceId, summary2, kind, changed, metrics, logs, advanceMarker = true, affected) {
|
|
16122
16129
|
const extra = {
|
|
16123
16130
|
// `kind` drives lifecycle (squash, marker, retry). `changed` is the
|
|
16124
16131
|
// decoupled what-changed verdict the Check Run / sticky comment render
|
|
16125
16132
|
// (migration 083): a structural-only job is kind=applied but changed=false.
|
|
16126
|
-
result: { summary, kind, changed },
|
|
16133
|
+
result: { summary: summary2, kind, changed },
|
|
16127
16134
|
metrics,
|
|
16128
16135
|
logs,
|
|
16129
16136
|
advance_marker: advanceMarker
|
|
@@ -16657,8 +16664,8 @@ async function runJob(ctx, job, claimDeviceId) {
|
|
|
16657
16664
|
if (runResult.isError) {
|
|
16658
16665
|
throw new Error(`claude reported is_error in its JSON result (no successful completion)`);
|
|
16659
16666
|
}
|
|
16660
|
-
const
|
|
16661
|
-
if (
|
|
16667
|
+
const summary2 = runResult.summary.trim().slice(0, 4e3);
|
|
16668
|
+
if (summary2) log(`claude> ${summary2.slice(0, 500).replace(/\n/g, " ")}`);
|
|
16662
16669
|
metrics.model_served = runResult.modelServed;
|
|
16663
16670
|
metrics.usage = runResult.usage;
|
|
16664
16671
|
metrics.cost_usd = runResult.costUsd;
|
|
@@ -16685,7 +16692,7 @@ async function runJob(ctx, job, claimDeviceId) {
|
|
|
16685
16692
|
throw new Error(outcome.reason);
|
|
16686
16693
|
}
|
|
16687
16694
|
const changed = outcome.changed;
|
|
16688
|
-
log(`done (${outcome.kind}, changed=${changed}, ${writes} commit(s) [${diffWrites} diff / ${structuralWrites} structural], ${reads ?? "?"} mcp tool call(s)): ${
|
|
16695
|
+
log(`done (${outcome.kind}, changed=${changed}, ${writes} commit(s) [${diffWrites} diff / ${structuralWrites} structural], ${reads ?? "?"} mcp tool call(s)): ${summary2.slice(0, 200).replace(/\n/g, " ")}`);
|
|
16689
16696
|
const totalMs = Date.now() - startedAt;
|
|
16690
16697
|
markPhase("complete", { kind: outcome.kind, changed, total_ms: totalMs });
|
|
16691
16698
|
metrics.total_ms = totalMs;
|
|
@@ -16693,7 +16700,7 @@ async function runJob(ctx, job, claimDeviceId) {
|
|
|
16693
16700
|
ctx,
|
|
16694
16701
|
job.id,
|
|
16695
16702
|
deviceId,
|
|
16696
|
-
|
|
16703
|
+
summary2,
|
|
16697
16704
|
outcome.kind,
|
|
16698
16705
|
changed,
|
|
16699
16706
|
metrics,
|
|
@@ -18164,14 +18171,14 @@ async function runScan(opts, deps = {}) {
|
|
|
18164
18171
|
log(` supabase: ${supabaseUrl}`);
|
|
18165
18172
|
log(` mcp base: ${mcpUrl}`);
|
|
18166
18173
|
log(` repo: ${opts.repoPath}`);
|
|
18167
|
-
const
|
|
18174
|
+
const authed2 = await createAuthedClient({
|
|
18168
18175
|
supabaseUrl,
|
|
18169
18176
|
supabaseAnonKey: supabaseAnon,
|
|
18170
18177
|
clientId,
|
|
18171
18178
|
preSpawnMinLifetimeMs: SCAN_PRE_SPAWN_TOKEN_MIN_LIFETIME_MS
|
|
18172
18179
|
});
|
|
18173
|
-
log(` signed in as ${
|
|
18174
|
-
const { supabase, userId } =
|
|
18180
|
+
log(` signed in as ${authed2.identity}`);
|
|
18181
|
+
const { supabase, userId } = authed2;
|
|
18175
18182
|
let headSha;
|
|
18176
18183
|
try {
|
|
18177
18184
|
headSha = await repoHeadSha(opts.repoPath);
|
|
@@ -18270,7 +18277,7 @@ async function runScan(opts, deps = {}) {
|
|
|
18270
18277
|
const result = await proc;
|
|
18271
18278
|
const exitCode = result.exitCode ?? 0;
|
|
18272
18279
|
const signal = result.signal ?? null;
|
|
18273
|
-
const
|
|
18280
|
+
const summary2 = (finalResultText || (result.stdout ? String(result.stdout) : "")).trim().slice(-4e3);
|
|
18274
18281
|
if (signal) {
|
|
18275
18282
|
log(`claude exited ${exitCode} (signal: ${signal})`);
|
|
18276
18283
|
} else {
|
|
@@ -18281,8 +18288,8 @@ async function runScan(opts, deps = {}) {
|
|
|
18281
18288
|
log(` stories: ${counts.stories}`);
|
|
18282
18289
|
let signals2 = null;
|
|
18283
18290
|
try {
|
|
18284
|
-
await new Promise((
|
|
18285
|
-
transcriptStream.end((err2) => err2 ? reject(err2) :
|
|
18291
|
+
await new Promise((resolve6, reject) => {
|
|
18292
|
+
transcriptStream.end((err2) => err2 ? reject(err2) : resolve6());
|
|
18286
18293
|
});
|
|
18287
18294
|
const reportPath = join19(transcriptDir, "report.json");
|
|
18288
18295
|
const orphansTxtPath = join19(transcriptDir, "orphans.txt");
|
|
@@ -18348,7 +18355,7 @@ async function runScan(opts, deps = {}) {
|
|
|
18348
18355
|
orgSlug: resolved2.orgSlug,
|
|
18349
18356
|
projectSlug: resolved2.projectSlug,
|
|
18350
18357
|
webUrl,
|
|
18351
|
-
summary
|
|
18358
|
+
summary: summary2
|
|
18352
18359
|
};
|
|
18353
18360
|
} finally {
|
|
18354
18361
|
await cleanupMcp().catch(() => {
|
|
@@ -18959,6 +18966,9 @@ var init_githubAppSetup = __esm({
|
|
|
18959
18966
|
|
|
18960
18967
|
// src/daemon/prompt.ts
|
|
18961
18968
|
import { createInterface } from "node:readline/promises";
|
|
18969
|
+
function stdinIsInteractive() {
|
|
18970
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
18971
|
+
}
|
|
18962
18972
|
async function withReadline(fn) {
|
|
18963
18973
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
18964
18974
|
try {
|
|
@@ -19241,9 +19251,9 @@ async function runLocalWatch(opts) {
|
|
|
19241
19251
|
log(` repo: ${target.repo}`);
|
|
19242
19252
|
log(` branch: ${target.repoBranch} \u2192 archme branch ${target.archmeBranch}`);
|
|
19243
19253
|
const cfg = resolveConfig();
|
|
19244
|
-
let
|
|
19254
|
+
let authed2;
|
|
19245
19255
|
try {
|
|
19246
|
-
|
|
19256
|
+
authed2 = await createAuthedClient({
|
|
19247
19257
|
supabaseUrl: cfg.supabaseUrl,
|
|
19248
19258
|
supabaseAnonKey: cfg.supabaseAnonKey,
|
|
19249
19259
|
clientId: cfg.archmeDaemonClientId,
|
|
@@ -19253,8 +19263,8 @@ async function runLocalWatch(opts) {
|
|
|
19253
19263
|
console.error(`archme local-watch: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
19254
19264
|
return 3;
|
|
19255
19265
|
}
|
|
19256
|
-
const { supabase, userId } =
|
|
19257
|
-
const orgId =
|
|
19266
|
+
const { supabase, userId } = authed2;
|
|
19267
|
+
const orgId = authed2.orgId;
|
|
19258
19268
|
if (!orgId) {
|
|
19259
19269
|
console.error("archme local-watch: this login is not paired with an org \u2014 run `archme login`");
|
|
19260
19270
|
return 3;
|
|
@@ -19494,22 +19504,22 @@ async function runInit(opts, deps = {}) {
|
|
|
19494
19504
|
} else {
|
|
19495
19505
|
log("\u2713 already signed in");
|
|
19496
19506
|
}
|
|
19497
|
-
const
|
|
19507
|
+
const authed2 = await authenticate({
|
|
19498
19508
|
supabaseUrl: cfg.supabaseUrl,
|
|
19499
19509
|
supabaseAnonKey: cfg.supabaseAnonKey,
|
|
19500
19510
|
clientId: cfg.archmeDaemonClientId,
|
|
19501
19511
|
preSpawnMinLifetimeMs: SCAN_PRE_SPAWN_TOKEN_MIN_LIFETIME_MS
|
|
19502
19512
|
});
|
|
19503
|
-
log(` signed in as ${
|
|
19513
|
+
log(` signed in as ${authed2.identity}`);
|
|
19504
19514
|
let fullSlug = opts.project;
|
|
19505
19515
|
if (!fullSlug) {
|
|
19506
|
-
const orgId =
|
|
19516
|
+
const orgId = authed2.orgId;
|
|
19507
19517
|
if (!orgId) {
|
|
19508
19518
|
throw new Error(
|
|
19509
19519
|
"could not resolve your organization \u2014 run `archme login` again, or pass --project <org>/<slug>"
|
|
19510
19520
|
);
|
|
19511
19521
|
}
|
|
19512
|
-
const orgSlug = await resolveOrgSlug(
|
|
19522
|
+
const orgSlug = await resolveOrgSlug(authed2.supabase, orgId);
|
|
19513
19523
|
if (!orgSlug) {
|
|
19514
19524
|
throw new Error(
|
|
19515
19525
|
`paired org ${orgId} no longer exists \u2014 run \`archme login\` again, or pass --project <org>/<slug>`
|
|
@@ -19518,8 +19528,8 @@ async function runInit(opts, deps = {}) {
|
|
|
19518
19528
|
fullSlug = defaultProjectSlug(orgSlug, repoPath);
|
|
19519
19529
|
}
|
|
19520
19530
|
const project = await resolveProject(
|
|
19521
|
-
|
|
19522
|
-
|
|
19531
|
+
authed2.supabase,
|
|
19532
|
+
authed2.userId,
|
|
19523
19533
|
fullSlug
|
|
19524
19534
|
);
|
|
19525
19535
|
log(
|
|
@@ -19527,7 +19537,7 @@ async function runInit(opts, deps = {}) {
|
|
|
19527
19537
|
);
|
|
19528
19538
|
const scanBranch = initScanBranch(project.defaultBranch);
|
|
19529
19539
|
const detail = await fetchArchitecture(
|
|
19530
|
-
|
|
19540
|
+
authed2.supabase,
|
|
19531
19541
|
project.projectId,
|
|
19532
19542
|
scanBranch
|
|
19533
19543
|
);
|
|
@@ -19541,8 +19551,8 @@ async function runInit(opts, deps = {}) {
|
|
|
19541
19551
|
await runInstallScript(script, repoPath);
|
|
19542
19552
|
ran.push("agent-config");
|
|
19543
19553
|
const watched = await watchRepo({
|
|
19544
|
-
supabase:
|
|
19545
|
-
orgId:
|
|
19554
|
+
supabase: authed2.supabase,
|
|
19555
|
+
orgId: authed2.orgId,
|
|
19546
19556
|
projectId: project.projectId,
|
|
19547
19557
|
repoSlug: git4.repoSlug,
|
|
19548
19558
|
branch: git4.branch,
|
|
@@ -19609,18 +19619,18 @@ Your board: ${webUrl}`);
|
|
|
19609
19619
|
startGitHubAppInstall: () => (deps.buildInstallUrl ?? buildInstallUrl)({
|
|
19610
19620
|
supabaseUrl: cfg.supabaseUrl,
|
|
19611
19621
|
accessToken: getCurrentAccessToken() ?? "",
|
|
19612
|
-
orgId:
|
|
19622
|
+
orgId: authed2.orgId
|
|
19613
19623
|
}),
|
|
19614
19624
|
waitForInstallation: () => (deps.waitForInstallation ?? waitForInstallation)({
|
|
19615
|
-
hasInstallation: () => (deps.orgHasInstallation ?? orgHasInstallation)(
|
|
19625
|
+
hasInstallation: () => (deps.orgHasInstallation ?? orgHasInstallation)(authed2.supabase, authed2.orgId),
|
|
19616
19626
|
sleep: (ms) => new Promise((r) => setTimeout(r, ms)),
|
|
19617
19627
|
now: () => Date.now(),
|
|
19618
19628
|
log
|
|
19619
19629
|
}),
|
|
19620
19630
|
watchRepo: async () => {
|
|
19621
19631
|
const w = await watchRepo({
|
|
19622
|
-
supabase:
|
|
19623
|
-
orgId:
|
|
19632
|
+
supabase: authed2.supabase,
|
|
19633
|
+
orgId: authed2.orgId,
|
|
19624
19634
|
projectId: project.projectId,
|
|
19625
19635
|
repoSlug: git4.repoSlug,
|
|
19626
19636
|
branch: git4.branch,
|
|
@@ -19642,8 +19652,8 @@ Your board: ${webUrl}`);
|
|
|
19642
19652
|
});
|
|
19643
19653
|
if (!target.ok) return { ok: false, reason: target.error };
|
|
19644
19654
|
const res = await (deps.registerLocalWatch ?? registerLocalWatch)({
|
|
19645
|
-
supabase:
|
|
19646
|
-
orgId:
|
|
19655
|
+
supabase: authed2.supabase,
|
|
19656
|
+
orgId: authed2.orgId,
|
|
19647
19657
|
projectId: project.projectId,
|
|
19648
19658
|
repoPath: git4.root,
|
|
19649
19659
|
originUrl: git4.originUrl,
|
|
@@ -19852,12 +19862,575 @@ var init_dispatch4 = __esm({
|
|
|
19852
19862
|
}
|
|
19853
19863
|
});
|
|
19854
19864
|
|
|
19865
|
+
// src/uninstall/edits.ts
|
|
19866
|
+
function stripArchmeClaudeBlock(content) {
|
|
19867
|
+
const start = content.indexOf(START_MARKER);
|
|
19868
|
+
if (start === -1) return UNCHANGED;
|
|
19869
|
+
const end = content.indexOf(END_MARKER, start);
|
|
19870
|
+
if (end === -1) return UNCHANGED;
|
|
19871
|
+
const before = content.slice(0, start).replace(/\n+$/, "");
|
|
19872
|
+
const after = content.slice(end + END_MARKER.length).replace(/^\n+/, "");
|
|
19873
|
+
if (!before.trim() && !after.trim()) return { kind: "delete" };
|
|
19874
|
+
if (!before.trim()) return { kind: "rewrite", content: `${after.replace(/\n*$/, "")}
|
|
19875
|
+
` };
|
|
19876
|
+
if (!after.trim()) return { kind: "rewrite", content: `${before}
|
|
19877
|
+
` };
|
|
19878
|
+
return { kind: "rewrite", content: `${before}
|
|
19879
|
+
|
|
19880
|
+
${after.replace(/\n*$/, "")}
|
|
19881
|
+
` };
|
|
19882
|
+
}
|
|
19883
|
+
function parseObject(raw) {
|
|
19884
|
+
try {
|
|
19885
|
+
const parsed = JSON.parse(raw);
|
|
19886
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
19887
|
+
return parsed;
|
|
19888
|
+
} catch {
|
|
19889
|
+
return null;
|
|
19890
|
+
}
|
|
19891
|
+
}
|
|
19892
|
+
function render(value) {
|
|
19893
|
+
return `${JSON.stringify(value, null, 2)}
|
|
19894
|
+
`;
|
|
19895
|
+
}
|
|
19896
|
+
function stripArchmeMcpServer(content) {
|
|
19897
|
+
const root = parseObject(content);
|
|
19898
|
+
if (!root) return UNCHANGED;
|
|
19899
|
+
const servers = root.mcpServers;
|
|
19900
|
+
if (!servers || typeof servers !== "object" || Array.isArray(servers)) return UNCHANGED;
|
|
19901
|
+
const entries = servers;
|
|
19902
|
+
if (!(MCP_SERVER_NAME2 in entries)) return UNCHANGED;
|
|
19903
|
+
delete entries[MCP_SERVER_NAME2];
|
|
19904
|
+
const onlyKey = Object.keys(root).length === 1 && "mcpServers" in root;
|
|
19905
|
+
if (onlyKey && Object.keys(entries).length === 0) return { kind: "delete" };
|
|
19906
|
+
return { kind: "rewrite", content: render(root) };
|
|
19907
|
+
}
|
|
19908
|
+
function isArchmeHook(hook) {
|
|
19909
|
+
const command = hook?.command;
|
|
19910
|
+
return typeof command === "string" && command.includes(NUDGE_HOOK_FILE);
|
|
19911
|
+
}
|
|
19912
|
+
function stripArchmeHooks(content) {
|
|
19913
|
+
const root = parseObject(content);
|
|
19914
|
+
if (!root) return UNCHANGED;
|
|
19915
|
+
const hooks = root.hooks;
|
|
19916
|
+
if (!hooks || typeof hooks !== "object" || Array.isArray(hooks)) return UNCHANGED;
|
|
19917
|
+
const byEvent = hooks;
|
|
19918
|
+
const pre = byEvent.PreToolUse;
|
|
19919
|
+
if (!Array.isArray(pre)) return UNCHANGED;
|
|
19920
|
+
let changed = false;
|
|
19921
|
+
const kept = [];
|
|
19922
|
+
for (const entry of pre) {
|
|
19923
|
+
const inner = entry?.hooks;
|
|
19924
|
+
if (!Array.isArray(inner) || !inner.some(isArchmeHook)) {
|
|
19925
|
+
kept.push(entry);
|
|
19926
|
+
continue;
|
|
19927
|
+
}
|
|
19928
|
+
changed = true;
|
|
19929
|
+
const survivors = inner.filter((h2) => !isArchmeHook(h2));
|
|
19930
|
+
if (survivors.length > 0) kept.push({ ...entry, hooks: survivors });
|
|
19931
|
+
}
|
|
19932
|
+
if (!changed) return UNCHANGED;
|
|
19933
|
+
if (kept.length > 0) byEvent.PreToolUse = kept;
|
|
19934
|
+
else delete byEvent.PreToolUse;
|
|
19935
|
+
if (Object.keys(byEvent).length === 0) delete root.hooks;
|
|
19936
|
+
if (Object.keys(root).length === 0) return { kind: "delete" };
|
|
19937
|
+
return { kind: "rewrite", content: render(root) };
|
|
19938
|
+
}
|
|
19939
|
+
var UNCHANGED, START_MARKER, END_MARKER, NUDGE_HOOK_FILE, MCP_SERVER_NAME2;
|
|
19940
|
+
var init_edits = __esm({
|
|
19941
|
+
"src/uninstall/edits.ts"() {
|
|
19942
|
+
"use strict";
|
|
19943
|
+
UNCHANGED = { kind: "unchanged" };
|
|
19944
|
+
START_MARKER = "<!-- ARCHME-START -->";
|
|
19945
|
+
END_MARKER = "<!-- ARCHME-END -->";
|
|
19946
|
+
NUDGE_HOOK_FILE = "archme-search-nudge.mjs";
|
|
19947
|
+
MCP_SERVER_NAME2 = "archme";
|
|
19948
|
+
}
|
|
19949
|
+
});
|
|
19950
|
+
|
|
19951
|
+
// src/uninstall/watching.ts
|
|
19952
|
+
function decideDaemonRemoval(input) {
|
|
19953
|
+
if (!input.installed) {
|
|
19954
|
+
return { action: "keep", reason: "no daemon service is installed on this machine" };
|
|
19955
|
+
}
|
|
19956
|
+
if (input.keepWatching) {
|
|
19957
|
+
return { action: "keep", reason: "--keep-watching: this repo is still being watched" };
|
|
19958
|
+
}
|
|
19959
|
+
if (input.remaining.watchedRepos === null) {
|
|
19960
|
+
return {
|
|
19961
|
+
action: "keep",
|
|
19962
|
+
reason: "could not check what else it serves, so it was left running"
|
|
19963
|
+
};
|
|
19964
|
+
}
|
|
19965
|
+
const { watchedRepos, localEntries } = input.remaining;
|
|
19966
|
+
if (watchedRepos > 0 || localEntries > 0) {
|
|
19967
|
+
const parts = [];
|
|
19968
|
+
if (watchedRepos > 0) parts.push(`${watchedRepos} watched repo${watchedRepos === 1 ? "" : "s"}`);
|
|
19969
|
+
if (localEntries > 0) {
|
|
19970
|
+
parts.push(`${localEntries} locally-polled repo${localEntries === 1 ? "" : "s"}`);
|
|
19971
|
+
}
|
|
19972
|
+
return { action: "keep", reason: `still serving ${parts.join(" and ")}` };
|
|
19973
|
+
}
|
|
19974
|
+
return { action: "remove", reason: "nothing is left for it to do" };
|
|
19975
|
+
}
|
|
19976
|
+
function rowsForSlug(rows, slug) {
|
|
19977
|
+
const wanted = slug.toLowerCase();
|
|
19978
|
+
return rows.filter((r) => r.slug.toLowerCase() === wanted);
|
|
19979
|
+
}
|
|
19980
|
+
var init_watching = __esm({
|
|
19981
|
+
"src/uninstall/watching.ts"() {
|
|
19982
|
+
"use strict";
|
|
19983
|
+
}
|
|
19984
|
+
});
|
|
19985
|
+
|
|
19986
|
+
// src/uninstall/backend.ts
|
|
19987
|
+
var backend_exports = {};
|
|
19988
|
+
__export(backend_exports, {
|
|
19989
|
+
createDefaultBackend: () => createDefaultBackend
|
|
19990
|
+
});
|
|
19991
|
+
function labelFor(row) {
|
|
19992
|
+
const org = row.projects?.organizations?.slug;
|
|
19993
|
+
const project = row.projects?.slug;
|
|
19994
|
+
const where = org && project ? `${org}/${project}` : `${row.repo_owner}/${row.repo_name}`;
|
|
19995
|
+
return `${where} @ ${row.branch}`;
|
|
19996
|
+
}
|
|
19997
|
+
function createDefaultBackend() {
|
|
19998
|
+
let clientPromise = null;
|
|
19999
|
+
const supabase = () => clientPromise ??= authed();
|
|
20000
|
+
return {
|
|
20001
|
+
async gitRoot(dir) {
|
|
20002
|
+
try {
|
|
20003
|
+
const { stdout } = await execa("git", ["rev-parse", "--show-toplevel"], { cwd: dir });
|
|
20004
|
+
return stdout.trim() || null;
|
|
20005
|
+
} catch {
|
|
20006
|
+
return null;
|
|
20007
|
+
}
|
|
20008
|
+
},
|
|
20009
|
+
async repoSlug(repoPath) {
|
|
20010
|
+
const origin = await readOriginUrl(repoPath);
|
|
20011
|
+
return origin ? parseGitHubRepo(origin) : null;
|
|
20012
|
+
},
|
|
20013
|
+
async listWatchedRepos() {
|
|
20014
|
+
const { supabase: client } = await supabase();
|
|
20015
|
+
const { data, error } = await client.from("watched_repos").select("id, repo_owner, repo_name, branch, projects(slug, organizations(slug))").is("deleted_at", null);
|
|
20016
|
+
if (error) throw new Error(error.message);
|
|
20017
|
+
return (data ?? []).map((row) => ({
|
|
20018
|
+
id: row.id,
|
|
20019
|
+
slug: `${row.repo_owner}/${row.repo_name}`,
|
|
20020
|
+
label: labelFor(row)
|
|
20021
|
+
}));
|
|
20022
|
+
},
|
|
20023
|
+
async unwatch(ids) {
|
|
20024
|
+
if (ids.length === 0) return;
|
|
20025
|
+
const { supabase: client } = await supabase();
|
|
20026
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20027
|
+
const { error } = await client.from("watched_repos").update({ deleted_at: now, updated_at: now }).in("id", ids);
|
|
20028
|
+
if (error) throw new Error(error.message);
|
|
20029
|
+
},
|
|
20030
|
+
async localRepoSlugs() {
|
|
20031
|
+
return (await loadLocalRepos()).map((e) => slugOf(e));
|
|
20032
|
+
},
|
|
20033
|
+
async removeLocalRepos(slug) {
|
|
20034
|
+
const repos = await loadLocalRepos();
|
|
20035
|
+
const kept = removeLocalReposBySlug(repos, slug);
|
|
20036
|
+
const dropped = repos.length - kept.length;
|
|
20037
|
+
if (dropped > 0) await saveLocalRepos(kept);
|
|
20038
|
+
return dropped;
|
|
20039
|
+
},
|
|
20040
|
+
async daemonInstalled() {
|
|
20041
|
+
const { pickController: pickController2 } = await Promise.resolve().then(() => (init_serviceController(), serviceController_exports));
|
|
20042
|
+
return await pickController2().unitExists();
|
|
20043
|
+
},
|
|
20044
|
+
async removeDaemon() {
|
|
20045
|
+
const { pickController: pickController2, orchestrateStop: orchestrateStop2 } = await Promise.resolve().then(() => (init_serviceController(), serviceController_exports));
|
|
20046
|
+
const controller = pickController2();
|
|
20047
|
+
await orchestrateStop2(controller);
|
|
20048
|
+
await controller.uninstall();
|
|
20049
|
+
}
|
|
20050
|
+
};
|
|
20051
|
+
}
|
|
20052
|
+
async function authed() {
|
|
20053
|
+
const cfg = resolveConfig();
|
|
20054
|
+
return await createAuthedClient({
|
|
20055
|
+
supabaseUrl: cfg.supabaseUrl,
|
|
20056
|
+
supabaseAnonKey: cfg.supabaseAnonKey,
|
|
20057
|
+
clientId: cfg.archmeDaemonClientId,
|
|
20058
|
+
preSpawnMinLifetimeMs: SCAN_PRE_SPAWN_TOKEN_MIN_LIFETIME_MS
|
|
20059
|
+
});
|
|
20060
|
+
}
|
|
20061
|
+
var init_backend = __esm({
|
|
20062
|
+
"src/uninstall/backend.ts"() {
|
|
20063
|
+
"use strict";
|
|
20064
|
+
init_execa();
|
|
20065
|
+
init_authedClient();
|
|
20066
|
+
init_defaults();
|
|
20067
|
+
init_localGit();
|
|
20068
|
+
init_github_remote();
|
|
20069
|
+
init_localRepos();
|
|
20070
|
+
}
|
|
20071
|
+
});
|
|
20072
|
+
|
|
20073
|
+
// src/uninstall/run-uninstall.ts
|
|
20074
|
+
var run_uninstall_exports = {};
|
|
20075
|
+
__export(run_uninstall_exports, {
|
|
20076
|
+
EX_OK: () => EX_OK3,
|
|
20077
|
+
EX_USAGE: () => EX_USAGE3,
|
|
20078
|
+
runUninstall: () => runUninstall
|
|
20079
|
+
});
|
|
20080
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
20081
|
+
import { join as join23 } from "node:path";
|
|
20082
|
+
function readIfExists(path7) {
|
|
20083
|
+
try {
|
|
20084
|
+
return existsSync3(path7) ? readFileSync5(path7, "utf8") : null;
|
|
20085
|
+
} catch {
|
|
20086
|
+
return null;
|
|
20087
|
+
}
|
|
20088
|
+
}
|
|
20089
|
+
function isParseableObject(raw) {
|
|
20090
|
+
try {
|
|
20091
|
+
const parsed = JSON.parse(raw);
|
|
20092
|
+
return Boolean(parsed) && typeof parsed === "object" && !Array.isArray(parsed);
|
|
20093
|
+
} catch {
|
|
20094
|
+
return false;
|
|
20095
|
+
}
|
|
20096
|
+
}
|
|
20097
|
+
function applyEdit(absolute, edit) {
|
|
20098
|
+
if (edit.kind === "rewrite") writeFileSync3(absolute, edit.content);
|
|
20099
|
+
else if (edit.kind === "delete") rmSync(absolute, { force: true });
|
|
20100
|
+
}
|
|
20101
|
+
async function runUninstall(opts) {
|
|
20102
|
+
const log = opts.log ?? ((m) => console.log(m));
|
|
20103
|
+
const error = opts.error ?? ((m) => console.error(m));
|
|
20104
|
+
const backend = opts.backend ?? await defaultBackend();
|
|
20105
|
+
const repoPath = await backend.gitRoot(opts.repoPath).catch(() => null) ?? opts.repoPath;
|
|
20106
|
+
if (repoPath !== opts.repoPath) log(`Using the repo root: ${repoPath}`);
|
|
20107
|
+
const abs = (rel) => join23(repoPath, rel);
|
|
20108
|
+
const actions = [];
|
|
20109
|
+
const skipped = [];
|
|
20110
|
+
const mcpRaw = readIfExists(abs(".mcp.json"));
|
|
20111
|
+
if (mcpRaw !== null) {
|
|
20112
|
+
const edit = stripArchmeMcpServer(mcpRaw);
|
|
20113
|
+
if (edit.kind !== "unchanged") {
|
|
20114
|
+
actions.push({
|
|
20115
|
+
path: ".mcp.json",
|
|
20116
|
+
label: edit.kind === "delete" ? `delete (it holds only the ${MCP_SERVER_NAME2} server)` : `remove the ${MCP_SERVER_NAME2} server`,
|
|
20117
|
+
apply: () => applyEdit(abs(".mcp.json"), edit)
|
|
20118
|
+
});
|
|
20119
|
+
} else if (!isParseableObject(mcpRaw)) {
|
|
20120
|
+
skipped.push(
|
|
20121
|
+
`.mcp.json \u2014 could not be parsed as JSON; if it has an ${MCP_SERVER_NAME2} server, remove it by hand`
|
|
20122
|
+
);
|
|
20123
|
+
}
|
|
20124
|
+
}
|
|
20125
|
+
const claudeRaw = readIfExists(abs("CLAUDE.md"));
|
|
20126
|
+
if (claudeRaw !== null) {
|
|
20127
|
+
const edit = stripArchmeClaudeBlock(claudeRaw);
|
|
20128
|
+
if (edit.kind !== "unchanged") {
|
|
20129
|
+
actions.push({
|
|
20130
|
+
path: "CLAUDE.md",
|
|
20131
|
+
label: edit.kind === "delete" ? "delete (it holds only the ArchitectMe block)" : "remove the ArchitectMe block",
|
|
20132
|
+
apply: () => applyEdit(abs("CLAUDE.md"), edit)
|
|
20133
|
+
});
|
|
20134
|
+
} else if (claudeRaw.includes(START_MARKER)) {
|
|
20135
|
+
skipped.push("CLAUDE.md \u2014 the ArchitectMe block has no closing marker; remove it by hand");
|
|
20136
|
+
}
|
|
20137
|
+
}
|
|
20138
|
+
const skillPath = ".claude/skills/architecture/SKILL.md";
|
|
20139
|
+
if (existsSync3(abs(skillPath))) {
|
|
20140
|
+
actions.push({
|
|
20141
|
+
path: skillPath,
|
|
20142
|
+
label: "delete the architecture skill",
|
|
20143
|
+
apply: () => rmSync(abs(skillPath), { force: true })
|
|
20144
|
+
});
|
|
20145
|
+
}
|
|
20146
|
+
const hookPath = `.claude/hooks/${NUDGE_HOOK_FILE}`;
|
|
20147
|
+
if (existsSync3(abs(hookPath))) {
|
|
20148
|
+
actions.push({
|
|
20149
|
+
path: hookPath,
|
|
20150
|
+
label: "delete the retired search-nudge hook",
|
|
20151
|
+
apply: () => rmSync(abs(hookPath), { force: true })
|
|
20152
|
+
});
|
|
20153
|
+
}
|
|
20154
|
+
const settingsRaw = readIfExists(abs(".claude/settings.json"));
|
|
20155
|
+
if (settingsRaw !== null) {
|
|
20156
|
+
const edit = stripArchmeHooks(settingsRaw);
|
|
20157
|
+
if (edit.kind !== "unchanged") {
|
|
20158
|
+
actions.push({
|
|
20159
|
+
path: ".claude/settings.json",
|
|
20160
|
+
label: edit.kind === "delete" ? "delete (it holds only the ArchitectMe hook)" : "unregister the search-nudge hook",
|
|
20161
|
+
apply: () => applyEdit(abs(".claude/settings.json"), edit)
|
|
20162
|
+
});
|
|
20163
|
+
} else if (!isParseableObject(settingsRaw)) {
|
|
20164
|
+
skipped.push(
|
|
20165
|
+
".claude/settings.json \u2014 could not be parsed as JSON; if it registers the ArchMe hook, remove that entry by hand"
|
|
20166
|
+
);
|
|
20167
|
+
}
|
|
20168
|
+
}
|
|
20169
|
+
for (const note of skipped) log(`Left alone: ${note}`);
|
|
20170
|
+
const keepWatching = opts.keepWatching ?? false;
|
|
20171
|
+
const slug = keepWatching ? null : await backend.repoSlug(repoPath).catch(() => null);
|
|
20172
|
+
let allWatched = null;
|
|
20173
|
+
let watchLookupError = null;
|
|
20174
|
+
if (slug) {
|
|
20175
|
+
try {
|
|
20176
|
+
allWatched = await backend.listWatchedRepos();
|
|
20177
|
+
} catch (err2) {
|
|
20178
|
+
watchLookupError = err2 instanceof Error ? err2.message : String(err2);
|
|
20179
|
+
}
|
|
20180
|
+
}
|
|
20181
|
+
const toUnwatch = allWatched ? rowsForSlug(allWatched, slug) : [];
|
|
20182
|
+
let localForSlug = 0;
|
|
20183
|
+
let allLocal = [];
|
|
20184
|
+
if (slug) {
|
|
20185
|
+
allLocal = await backend.localRepoSlugs().catch(() => []);
|
|
20186
|
+
localForSlug = allLocal.filter((s) => s.toLowerCase() === slug.toLowerCase()).length;
|
|
20187
|
+
}
|
|
20188
|
+
if (actions.length === 0 && toUnwatch.length === 0 && localForSlug === 0) {
|
|
20189
|
+
log(`No ArchitectMe agent config found in ${repoPath}. Nothing to remove.`);
|
|
20190
|
+
if (watchLookupError) log(watchingUnknownNote(watchLookupError));
|
|
20191
|
+
return { exitCode: EX_OK3, removed: [], unwatched: [] };
|
|
20192
|
+
}
|
|
20193
|
+
const daemonInstalled = await backend.daemonInstalled().catch(() => false);
|
|
20194
|
+
const plan = [
|
|
20195
|
+
...actions.map((a2) => ` ${a2.path} \u2014 ${a2.label}`),
|
|
20196
|
+
...toUnwatch.map((r) => ` ${r.label} \u2014 stop watching (no more commits will update it)`),
|
|
20197
|
+
...localForSlug > 0 ? [` ${slug} \u2014 stop polling from this machine (${localForSlug} registry entry)`] : [],
|
|
20198
|
+
// Stated up front, not just reported after: a repo-scoped command removing
|
|
20199
|
+
// a machine-wide service is the most surprising thing here. Whether it
|
|
20200
|
+
// actually goes is still decided afterwards, against what really happened.
|
|
20201
|
+
...daemonInstalled && !keepWatching ? [" the daemon service \u2014 removed only if this leaves it nothing to serve"] : []
|
|
20202
|
+
];
|
|
20203
|
+
if (!opts.yes) {
|
|
20204
|
+
const interactive = opts.interactive ?? stdinIsInteractive();
|
|
20205
|
+
if (!interactive && !opts.confirm) {
|
|
20206
|
+
error(
|
|
20207
|
+
"archme uninstall: refusing to edit files without confirmation.\nThere is nobody to ask (stdin is not a terminal). Re-run with --yes."
|
|
20208
|
+
);
|
|
20209
|
+
return { exitCode: EX_USAGE3, removed: [], unwatched: [] };
|
|
20210
|
+
}
|
|
20211
|
+
const confirm = opts.confirm ?? (async (lines) => {
|
|
20212
|
+
log(`This will change, in ${repoPath} and on your ArchMe account:
|
|
20213
|
+
`);
|
|
20214
|
+
lines.forEach((l) => log(l));
|
|
20215
|
+
log("");
|
|
20216
|
+
return await promptYesNo("Uninstall ArchitectMe for this repo?");
|
|
20217
|
+
});
|
|
20218
|
+
if (!await confirm(plan)) {
|
|
20219
|
+
log("Cancelled \u2014 nothing was changed.");
|
|
20220
|
+
return { exitCode: EX_OK3, removed: [], unwatched: [] };
|
|
20221
|
+
}
|
|
20222
|
+
}
|
|
20223
|
+
const removed = [];
|
|
20224
|
+
for (const action of actions) {
|
|
20225
|
+
action.apply();
|
|
20226
|
+
removed.push(action.path);
|
|
20227
|
+
log(`Removed: ${action.path} \u2014 ${action.label}`);
|
|
20228
|
+
}
|
|
20229
|
+
for (const dir of PRUNABLE_DIRS) {
|
|
20230
|
+
const path7 = abs(dir);
|
|
20231
|
+
try {
|
|
20232
|
+
if (existsSync3(path7) && readdirSync(path7).length === 0) rmSync(path7, { recursive: true });
|
|
20233
|
+
} catch {
|
|
20234
|
+
}
|
|
20235
|
+
}
|
|
20236
|
+
const unwatched = [];
|
|
20237
|
+
if (toUnwatch.length > 0) {
|
|
20238
|
+
try {
|
|
20239
|
+
await backend.unwatch(toUnwatch.map((r) => r.id));
|
|
20240
|
+
unwatched.push(...toUnwatch.map((r) => r.id));
|
|
20241
|
+
for (const row of toUnwatch) log(`Stopped watching: ${row.label}`);
|
|
20242
|
+
} catch (err2) {
|
|
20243
|
+
watchLookupError = err2 instanceof Error ? err2.message : String(err2);
|
|
20244
|
+
}
|
|
20245
|
+
}
|
|
20246
|
+
let localRemoved = 0;
|
|
20247
|
+
if (slug && localForSlug > 0) {
|
|
20248
|
+
try {
|
|
20249
|
+
localRemoved = await backend.removeLocalRepos(slug);
|
|
20250
|
+
if (localRemoved > 0) log(`Stopped local polling: ${slug}`);
|
|
20251
|
+
} catch {
|
|
20252
|
+
log(`Could not update the local watch registry \u2014 \`archme local-watch --stop ${slug}\` will.`);
|
|
20253
|
+
}
|
|
20254
|
+
}
|
|
20255
|
+
const unwatchFailed = watchLookupError !== null;
|
|
20256
|
+
const remaining = {
|
|
20257
|
+
watchedRepos: allWatched && !unwatchFailed ? allWatched.length - unwatched.length : null,
|
|
20258
|
+
localEntries: allLocal.length - localRemoved
|
|
20259
|
+
};
|
|
20260
|
+
const daemonPlan = decideDaemonRemoval({ installed: daemonInstalled, keepWatching, remaining });
|
|
20261
|
+
if (daemonPlan.action === "remove") {
|
|
20262
|
+
try {
|
|
20263
|
+
await backend.removeDaemon();
|
|
20264
|
+
log(`Removed the daemon service \u2014 ${daemonPlan.reason}.`);
|
|
20265
|
+
} catch (err2) {
|
|
20266
|
+
log(
|
|
20267
|
+
`Could not remove the daemon service (${err2 instanceof Error ? err2.message : String(err2)}).
|
|
20268
|
+
archme daemon uninstall`
|
|
20269
|
+
);
|
|
20270
|
+
}
|
|
20271
|
+
} else {
|
|
20272
|
+
log(`Daemon left in place \u2014 ${daemonPlan.reason}.`);
|
|
20273
|
+
}
|
|
20274
|
+
log(
|
|
20275
|
+
summary({
|
|
20276
|
+
keepWatching,
|
|
20277
|
+
slug,
|
|
20278
|
+
unwatchFailed,
|
|
20279
|
+
watchLookupError,
|
|
20280
|
+
removedConfig: removed.length > 0,
|
|
20281
|
+
checkedAndNotWatched: Boolean(slug) && !unwatchFailed && toUnwatch.length === 0
|
|
20282
|
+
})
|
|
20283
|
+
);
|
|
20284
|
+
return { exitCode: EX_OK3, removed, unwatched };
|
|
20285
|
+
}
|
|
20286
|
+
function watchingUnknownNote(reason) {
|
|
20287
|
+
return `
|
|
20288
|
+
Could not check whether this repo is still being watched (${reason}).
|
|
20289
|
+
If it is, commits will keep updating the architecture. Sign in and re-run to stop it:
|
|
20290
|
+
archme login && archme uninstall`;
|
|
20291
|
+
}
|
|
20292
|
+
function summary(input) {
|
|
20293
|
+
const lines = input.removedConfig ? ["\nArchitectMe agent config removed from this repo."] : ["\nNo ArchitectMe agent config was here to remove."];
|
|
20294
|
+
if (input.keepWatching) {
|
|
20295
|
+
lines.push(
|
|
20296
|
+
"This repo is STILL being watched (--keep-watching), so commits keep updating its architecture."
|
|
20297
|
+
);
|
|
20298
|
+
} else if (input.unwatchFailed && input.watchLookupError) {
|
|
20299
|
+
lines.push(watchingUnknownNote(input.watchLookupError).trimStart());
|
|
20300
|
+
} else if (!input.slug) {
|
|
20301
|
+
lines.push(
|
|
20302
|
+
"No github.com remote here, so there was no watched repo to stop. Check archme.io \u2192 Settings \u2192 Integrations if you expected one."
|
|
20303
|
+
);
|
|
20304
|
+
} else if (input.checkedAndNotWatched) {
|
|
20305
|
+
lines.push(`${input.slug} was not being watched, so no commits were updating it.`);
|
|
20306
|
+
}
|
|
20307
|
+
lines.push(
|
|
20308
|
+
"\nYour architecture on archme.io and this machine's login are untouched:",
|
|
20309
|
+
" archme logout sign this machine out",
|
|
20310
|
+
"\nThe GitHub App installation is org-wide, so it stays. Remove it on github.com if you want it gone."
|
|
20311
|
+
);
|
|
20312
|
+
return lines.join("\n");
|
|
20313
|
+
}
|
|
20314
|
+
async function defaultBackend() {
|
|
20315
|
+
const { createDefaultBackend: createDefaultBackend2 } = await Promise.resolve().then(() => (init_backend(), backend_exports));
|
|
20316
|
+
return createDefaultBackend2();
|
|
20317
|
+
}
|
|
20318
|
+
var EX_OK3, EX_USAGE3, PRUNABLE_DIRS;
|
|
20319
|
+
var init_run_uninstall = __esm({
|
|
20320
|
+
"src/uninstall/run-uninstall.ts"() {
|
|
20321
|
+
"use strict";
|
|
20322
|
+
init_prompt();
|
|
20323
|
+
init_edits();
|
|
20324
|
+
init_watching();
|
|
20325
|
+
EX_OK3 = 0;
|
|
20326
|
+
EX_USAGE3 = 78;
|
|
20327
|
+
PRUNABLE_DIRS = [
|
|
20328
|
+
".claude/skills/architecture",
|
|
20329
|
+
".claude/skills",
|
|
20330
|
+
".claude/hooks",
|
|
20331
|
+
".claude"
|
|
20332
|
+
];
|
|
20333
|
+
}
|
|
20334
|
+
});
|
|
20335
|
+
|
|
20336
|
+
// src/uninstall/dispatch.ts
|
|
20337
|
+
var dispatch_exports5 = {};
|
|
20338
|
+
__export(dispatch_exports5, {
|
|
20339
|
+
EX_INFRA: () => EX_INFRA3,
|
|
20340
|
+
EX_OK: () => EX_OK4,
|
|
20341
|
+
EX_USAGE: () => EX_USAGE4,
|
|
20342
|
+
dispatchUninstall: () => dispatchUninstall,
|
|
20343
|
+
parseUninstallArgs: () => parseUninstallArgs,
|
|
20344
|
+
printUninstallHelp: () => printUninstallHelp
|
|
20345
|
+
});
|
|
20346
|
+
import { resolve as resolve5 } from "node:path";
|
|
20347
|
+
function parseUninstallArgs(argv) {
|
|
20348
|
+
if (argv.some((a2) => a2 === "--help" || a2 === "-h" || a2 === "help")) return { kind: "help" };
|
|
20349
|
+
let repoPath;
|
|
20350
|
+
let yes = false;
|
|
20351
|
+
let keepWatching = false;
|
|
20352
|
+
for (const arg of argv) {
|
|
20353
|
+
if (arg === "--yes" || arg === "-y") {
|
|
20354
|
+
yes = true;
|
|
20355
|
+
} else if (arg === "--keep-watching") {
|
|
20356
|
+
keepWatching = true;
|
|
20357
|
+
} else if (arg.startsWith("-")) {
|
|
20358
|
+
return { kind: "error", message: `unknown option '${arg}'` };
|
|
20359
|
+
} else if (repoPath === void 0) {
|
|
20360
|
+
repoPath = arg;
|
|
20361
|
+
} else {
|
|
20362
|
+
return { kind: "error", message: `unexpected argument '${arg}'` };
|
|
20363
|
+
}
|
|
20364
|
+
}
|
|
20365
|
+
return { kind: "run", repoPath, yes, keepWatching };
|
|
20366
|
+
}
|
|
20367
|
+
function printUninstallHelp() {
|
|
20368
|
+
console.log(`archme uninstall \u2014 stop ArchitectMe for a repo
|
|
20369
|
+
|
|
20370
|
+
Undoes what \`archme init\` set up, and shows you the whole list before it does:
|
|
20371
|
+
|
|
20372
|
+
1. Agent config \u2014 the archme MCP server in .mcp.json, the ArchitectMe block in
|
|
20373
|
+
CLAUDE.md, the architecture skill, and the retired search-nudge hook older
|
|
20374
|
+
versions registered in .claude/settings.json.
|
|
20375
|
+
2. Watching \u2014 stops this repo producing jobs, both via the GitHub App and via
|
|
20376
|
+
local polling. Without this the board keeps updating while looking removed.
|
|
20377
|
+
3. The daemon \u2014 removed only if it has no other repo left to serve. It is one
|
|
20378
|
+
service per account, so uninstalling one repo never stops the others.
|
|
20379
|
+
|
|
20380
|
+
Your architecture on archme.io and this machine's login survive. The GitHub App
|
|
20381
|
+
installation is org-wide and stays; remove it on github.com if you want it gone.
|
|
20382
|
+
|
|
20383
|
+
Usage:
|
|
20384
|
+
archme uninstall [repo-path] [options]
|
|
20385
|
+
|
|
20386
|
+
repo-path Defaults to the current directory.
|
|
20387
|
+
|
|
20388
|
+
Options:
|
|
20389
|
+
-y, --yes Do not ask for confirmation
|
|
20390
|
+
--keep-watching Remove the agent config only; leave the repo watched
|
|
20391
|
+
and the daemon running
|
|
20392
|
+
-h, --help Show this help`);
|
|
20393
|
+
}
|
|
20394
|
+
async function dispatchUninstall(argv) {
|
|
20395
|
+
const parsed = parseUninstallArgs(argv);
|
|
20396
|
+
if (parsed.kind === "help") {
|
|
20397
|
+
printUninstallHelp();
|
|
20398
|
+
return EX_OK4;
|
|
20399
|
+
}
|
|
20400
|
+
if (parsed.kind === "error") {
|
|
20401
|
+
console.error(`archme uninstall: ${parsed.message}`);
|
|
20402
|
+
printUninstallHelp();
|
|
20403
|
+
return EX_USAGE4;
|
|
20404
|
+
}
|
|
20405
|
+
try {
|
|
20406
|
+
const { runUninstall: runUninstall2 } = await Promise.resolve().then(() => (init_run_uninstall(), run_uninstall_exports));
|
|
20407
|
+
const result = await runUninstall2({
|
|
20408
|
+
repoPath: resolve5(parsed.repoPath ?? process.cwd()),
|
|
20409
|
+
yes: parsed.yes,
|
|
20410
|
+
keepWatching: parsed.keepWatching
|
|
20411
|
+
});
|
|
20412
|
+
return result.exitCode;
|
|
20413
|
+
} catch (err2) {
|
|
20414
|
+
console.error(`archme uninstall: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
20415
|
+
return EX_INFRA3;
|
|
20416
|
+
}
|
|
20417
|
+
}
|
|
20418
|
+
var EX_OK4, EX_INFRA3, EX_USAGE4;
|
|
20419
|
+
var init_dispatch5 = __esm({
|
|
20420
|
+
"src/uninstall/dispatch.ts"() {
|
|
20421
|
+
"use strict";
|
|
20422
|
+
EX_OK4 = 0;
|
|
20423
|
+
EX_INFRA3 = 3;
|
|
20424
|
+
EX_USAGE4 = 78;
|
|
20425
|
+
}
|
|
20426
|
+
});
|
|
20427
|
+
|
|
19855
20428
|
// src/daemon/localWatchDispatch.ts
|
|
19856
20429
|
var localWatchDispatch_exports = {};
|
|
19857
20430
|
__export(localWatchDispatch_exports, {
|
|
19858
|
-
EX_INFRA: () =>
|
|
19859
|
-
EX_OK: () =>
|
|
19860
|
-
EX_USAGE: () =>
|
|
20431
|
+
EX_INFRA: () => EX_INFRA4,
|
|
20432
|
+
EX_OK: () => EX_OK5,
|
|
20433
|
+
EX_USAGE: () => EX_USAGE5,
|
|
19861
20434
|
dispatchLocalWatch: () => dispatchLocalWatch,
|
|
19862
20435
|
parseLocalWatchArgs: () => parseLocalWatchArgs,
|
|
19863
20436
|
printLocalWatchHelp: () => printLocalWatchHelp
|
|
@@ -19947,12 +20520,12 @@ async function dispatchLocalWatch(argv) {
|
|
|
19947
20520
|
const parsed = parseLocalWatchArgs(argv);
|
|
19948
20521
|
if (parsed.kind === "help") {
|
|
19949
20522
|
printLocalWatchHelp();
|
|
19950
|
-
return
|
|
20523
|
+
return EX_OK5;
|
|
19951
20524
|
}
|
|
19952
20525
|
if (parsed.kind === "error") {
|
|
19953
20526
|
console.error(parsed.message);
|
|
19954
20527
|
printLocalWatchHelp();
|
|
19955
|
-
return
|
|
20528
|
+
return EX_USAGE5;
|
|
19956
20529
|
}
|
|
19957
20530
|
const mod = await Promise.resolve().then(() => (init_localWatch(), localWatch_exports));
|
|
19958
20531
|
switch (parsed.kind) {
|
|
@@ -19966,13 +20539,13 @@ async function dispatchLocalWatch(argv) {
|
|
|
19966
20539
|
return await mod.runLocalWatch(parsed);
|
|
19967
20540
|
}
|
|
19968
20541
|
}
|
|
19969
|
-
var
|
|
20542
|
+
var EX_OK5, EX_INFRA4, EX_USAGE5, PROJECT_SLUG_RE3, REPO_SLUG_RE;
|
|
19970
20543
|
var init_localWatchDispatch = __esm({
|
|
19971
20544
|
"src/daemon/localWatchDispatch.ts"() {
|
|
19972
20545
|
"use strict";
|
|
19973
|
-
|
|
19974
|
-
|
|
19975
|
-
|
|
20546
|
+
EX_OK5 = 0;
|
|
20547
|
+
EX_INFRA4 = 3;
|
|
20548
|
+
EX_USAGE5 = 78;
|
|
19976
20549
|
PROJECT_SLUG_RE3 = /^[^/\s]+\/[^/\s]+$/;
|
|
19977
20550
|
REPO_SLUG_RE = /^[^/\s]+\/[^/\s]+$/;
|
|
19978
20551
|
}
|
|
@@ -19980,7 +20553,7 @@ var init_localWatchDispatch = __esm({
|
|
|
19980
20553
|
|
|
19981
20554
|
// src/doctor/collect.ts
|
|
19982
20555
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
19983
|
-
import { join as
|
|
20556
|
+
import { join as join24 } from "node:path";
|
|
19984
20557
|
function binaryPresent(name) {
|
|
19985
20558
|
try {
|
|
19986
20559
|
requireBinary(name);
|
|
@@ -19996,7 +20569,7 @@ async function git3(cwd, args) {
|
|
|
19996
20569
|
async function readMcpPin(repoRoot) {
|
|
19997
20570
|
const empty = { installed: false, pin: { project: null, branch: null } };
|
|
19998
20571
|
try {
|
|
19999
|
-
const raw = await readFile13(
|
|
20572
|
+
const raw = await readFile13(join24(repoRoot, ".mcp.json"), "utf8");
|
|
20000
20573
|
const parsed = JSON.parse(raw);
|
|
20001
20574
|
const servers = parsed.mcpServers ?? {};
|
|
20002
20575
|
const entry = Object.entries(servers).find(([name]) => /archme/i.test(name));
|
|
@@ -20064,17 +20637,17 @@ async function collectFacts(cwd) {
|
|
|
20064
20637
|
);
|
|
20065
20638
|
try {
|
|
20066
20639
|
const cfg = resolveConfig();
|
|
20067
|
-
const
|
|
20640
|
+
const authed2 = await createAuthedClient({
|
|
20068
20641
|
supabaseUrl: cfg.supabaseUrl,
|
|
20069
20642
|
supabaseAnonKey: cfg.supabaseAnonKey,
|
|
20070
20643
|
clientId: cfg.archmeDaemonClientId,
|
|
20071
20644
|
preSpawnMinLifetimeMs: SCAN_PRE_SPAWN_TOKEN_MIN_LIFETIME_MS
|
|
20072
20645
|
});
|
|
20073
|
-
if (!
|
|
20646
|
+
if (!authed2.orgId) return facts;
|
|
20074
20647
|
facts.signedIn = true;
|
|
20075
|
-
facts.orgId =
|
|
20076
|
-
facts.identity =
|
|
20077
|
-
const supabase =
|
|
20648
|
+
facts.orgId = authed2.orgId;
|
|
20649
|
+
facts.identity = authed2.identity;
|
|
20650
|
+
const supabase = authed2.supabase;
|
|
20078
20651
|
const slug = facts.pinnedProject;
|
|
20079
20652
|
if (slug) {
|
|
20080
20653
|
const [orgSlug, projectSlug] = slug.split("/");
|
|
@@ -20360,18 +20933,18 @@ optional.`;
|
|
|
20360
20933
|
});
|
|
20361
20934
|
|
|
20362
20935
|
// src/doctor/dispatch.ts
|
|
20363
|
-
var
|
|
20364
|
-
__export(
|
|
20365
|
-
EX_INFRA: () =>
|
|
20366
|
-
EX_OK: () =>
|
|
20367
|
-
EX_USAGE: () =>
|
|
20936
|
+
var dispatch_exports6 = {};
|
|
20937
|
+
__export(dispatch_exports6, {
|
|
20938
|
+
EX_INFRA: () => EX_INFRA5,
|
|
20939
|
+
EX_OK: () => EX_OK6,
|
|
20940
|
+
EX_USAGE: () => EX_USAGE6,
|
|
20368
20941
|
dispatchDoctor: () => dispatchDoctor,
|
|
20369
20942
|
parseDoctorArgs: () => parseDoctorArgs,
|
|
20370
20943
|
printDoctorHelp: () => printDoctorHelp
|
|
20371
20944
|
});
|
|
20372
20945
|
import { mkdtemp as mkdtemp5, rm as rm9, writeFile as writeFile13 } from "node:fs/promises";
|
|
20373
20946
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
20374
|
-
import { join as
|
|
20947
|
+
import { join as join25 } from "node:path";
|
|
20375
20948
|
function parseDoctorArgs(argv) {
|
|
20376
20949
|
const args = { plain: false, help: false };
|
|
20377
20950
|
for (const a2 of argv) {
|
|
@@ -20403,11 +20976,11 @@ async function dispatchDoctor(argv) {
|
|
|
20403
20976
|
if (!parsed.ok) {
|
|
20404
20977
|
console.error(parsed.message);
|
|
20405
20978
|
printDoctorHelp();
|
|
20406
|
-
return
|
|
20979
|
+
return EX_USAGE6;
|
|
20407
20980
|
}
|
|
20408
20981
|
if (parsed.args.help) {
|
|
20409
20982
|
printDoctorHelp();
|
|
20410
|
-
return
|
|
20983
|
+
return EX_OK6;
|
|
20411
20984
|
}
|
|
20412
20985
|
const facts = await collectFacts(process.cwd());
|
|
20413
20986
|
const issues = diagnose(facts);
|
|
@@ -20423,17 +20996,17 @@ async function dispatchDoctor(argv) {
|
|
|
20423
20996
|
console.log("");
|
|
20424
20997
|
}
|
|
20425
20998
|
}
|
|
20426
|
-
if (parsed.args.plain) return
|
|
20999
|
+
if (parsed.args.plain) return EX_OK6;
|
|
20427
21000
|
try {
|
|
20428
21001
|
requireBinary("claude");
|
|
20429
21002
|
} catch {
|
|
20430
21003
|
console.log(
|
|
20431
21004
|
"Install Claude Code to ask about any of this: npm i -g @anthropic-ai/claude-code"
|
|
20432
21005
|
);
|
|
20433
|
-
return
|
|
21006
|
+
return EX_OK6;
|
|
20434
21007
|
}
|
|
20435
|
-
const dir = await mkdtemp5(
|
|
20436
|
-
const mcpConfigPath =
|
|
21008
|
+
const dir = await mkdtemp5(join25(tmpdir5(), "archme-doctor-"));
|
|
21009
|
+
const mcpConfigPath = join25(dir, "mcp.json");
|
|
20437
21010
|
await writeFile13(mcpConfigPath, JSON.stringify({ mcpServers: {} }), { mode: 384 });
|
|
20438
21011
|
try {
|
|
20439
21012
|
const result = await spawnClaude({
|
|
@@ -20446,13 +21019,13 @@ async function dispatchDoctor(argv) {
|
|
|
20446
21019
|
// is a much larger surface than the problem justifies.
|
|
20447
21020
|
allowedTools: ["Read", "Glob", "Grep"]
|
|
20448
21021
|
});
|
|
20449
|
-
return result.exitCode === 0 ?
|
|
21022
|
+
return result.exitCode === 0 ? EX_OK6 : EX_INFRA5;
|
|
20450
21023
|
} finally {
|
|
20451
21024
|
await rm9(dir, { recursive: true, force: true });
|
|
20452
21025
|
}
|
|
20453
21026
|
}
|
|
20454
|
-
var
|
|
20455
|
-
var
|
|
21027
|
+
var EX_OK6, EX_INFRA5, EX_USAGE6;
|
|
21028
|
+
var init_dispatch6 = __esm({
|
|
20456
21029
|
"src/doctor/dispatch.ts"() {
|
|
20457
21030
|
"use strict";
|
|
20458
21031
|
init_collect();
|
|
@@ -20460,9 +21033,9 @@ var init_dispatch5 = __esm({
|
|
|
20460
21033
|
init_prompt2();
|
|
20461
21034
|
init_spawn_claude();
|
|
20462
21035
|
init_preflight();
|
|
20463
|
-
|
|
20464
|
-
|
|
20465
|
-
|
|
21036
|
+
EX_OK6 = 0;
|
|
21037
|
+
EX_INFRA5 = 3;
|
|
21038
|
+
EX_USAGE6 = 78;
|
|
20466
21039
|
}
|
|
20467
21040
|
});
|
|
20468
21041
|
|
|
@@ -20520,6 +21093,7 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
20520
21093
|
"keys",
|
|
20521
21094
|
"scan",
|
|
20522
21095
|
"init",
|
|
21096
|
+
"uninstall",
|
|
20523
21097
|
"local-watch",
|
|
20524
21098
|
"doctor"
|
|
20525
21099
|
]);
|
|
@@ -20618,12 +21192,16 @@ async function main(argv) {
|
|
|
20618
21192
|
const { dispatchInit: dispatchInit2 } = await Promise.resolve().then(() => (init_dispatch4(), dispatch_exports4));
|
|
20619
21193
|
return await dispatchInit2(r.args);
|
|
20620
21194
|
}
|
|
21195
|
+
case "uninstall": {
|
|
21196
|
+
const { dispatchUninstall: dispatchUninstall2 } = await Promise.resolve().then(() => (init_dispatch5(), dispatch_exports5));
|
|
21197
|
+
return await dispatchUninstall2(r.args);
|
|
21198
|
+
}
|
|
20621
21199
|
case "local-watch": {
|
|
20622
21200
|
const { dispatchLocalWatch: dispatchLocalWatch2 } = await Promise.resolve().then(() => (init_localWatchDispatch(), localWatchDispatch_exports));
|
|
20623
21201
|
return await dispatchLocalWatch2(r.args);
|
|
20624
21202
|
}
|
|
20625
21203
|
case "doctor": {
|
|
20626
|
-
const { dispatchDoctor: dispatchDoctor2 } = await Promise.resolve().then(() => (
|
|
21204
|
+
const { dispatchDoctor: dispatchDoctor2 } = await Promise.resolve().then(() => (init_dispatch6(), dispatch_exports6));
|
|
20627
21205
|
return await dispatchDoctor2(r.args);
|
|
20628
21206
|
}
|
|
20629
21207
|
case "logout": {
|
|
@@ -20650,6 +21228,8 @@ Other commands:
|
|
|
20650
21228
|
archme scan <repo-path> Single-pass scan into an explicit project/branch
|
|
20651
21229
|
archme local-watch [repo-path] Watch a repo without a GitHub App
|
|
20652
21230
|
(--list|--stop|--resume)
|
|
21231
|
+
archme uninstall [repo-path] Stop ArchitectMe for a repo: config,
|
|
21232
|
+
watching, and the daemon if it's now idle
|
|
20653
21233
|
archme doctor Check this machine's setup and explain it
|
|
20654
21234
|
archme login | logout Authenticate with archme
|
|
20655
21235
|
archme keys list|create|revoke Manage MCP API keys
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@archme/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Map your codebase into a living architecture diagram: run `archme init` in a repo to scan it, `archme daemon start` to keep it current on every commit.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|