@use-aistack/cli 0.10.2 → 0.12.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 +5 -3
- package/dist/index.js +184 -47
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -20,17 +20,19 @@ npx @use-aistack/cli sync
|
|
|
20
20
|
|
|
21
21
|
On an unlinked machine, `sync` starts the login flow inline - your browser opens to approve the machine, then the sync continues. One command is the whole onboarding.
|
|
22
22
|
|
|
23
|
+
After a manual sync, the CLI offers auto-sync with three choices: Enable, Maybe later, and Never ask again. Maybe later asks again after your next manual sync. Existing declines from older CLI versions are treated as Maybe later.
|
|
24
|
+
|
|
23
25
|
### `npx @use-aistack/cli sync --auto on` / `off`
|
|
24
26
|
|
|
25
|
-
Optional: keep your stack fresh without manual syncs. `on` asks your stack for the permission, then writes a `SessionStart` hook into the harnesses you actually use - `~/.claude/settings.json` for Claude Code, `~/.codex/hooks.json` for Codex. The hook runs a silent sync at most once
|
|
27
|
+
Optional: keep your stack fresh without manual syncs. `on` asks your stack for the permission, then writes a `SessionStart` hook into the harnesses you actually use - `~/.claude/settings.json` for Claude Code, `~/.codex/hooks.json` for Codex. The hook runs a silent sync at most once every 6 hours when a session starts. `off` removes the hooks and takes the permission back.
|
|
26
28
|
|
|
27
29
|
```sh
|
|
28
|
-
npx @use-aistack/cli sync --auto on # enable, default every
|
|
30
|
+
npx @use-aistack/cli sync --auto on # enable, default every 6h
|
|
29
31
|
npx @use-aistack/cli sync --auto on --every 12 # custom frequency in hours
|
|
30
32
|
npx @use-aistack/cli sync --auto off # revoke
|
|
31
33
|
```
|
|
32
34
|
|
|
33
|
-
**Your stack owns the permission, not this machine.** The silent run asks aistack.to before it publishes anything, so the switch on your stack page is a complete revoke: it stops every machine, even one whose hooks are still installed.
|
|
35
|
+
**Your stack owns the permission, not this machine.** The silent run asks aistack.to before it publishes anything, so the switch above the first section on your stack page is a complete revoke: it stops every machine, even one whose hooks are still installed. The switch defaults to every 6 hours. Run the enable command shown beside it on each machine that should install triggers. A harness you adopt months later gets its trigger the same way.
|
|
34
36
|
|
|
35
37
|
The silent run (`sync --auto`) never prompts and never installs a hook. Each run appends one line to `~/.config/aistack/sync.log` (capped at 200 lines). The next interactive `sync` reports the last result. After 3 failures in a row, one visible message appears in Claude Code and names the fix. No email, no dialogs.
|
|
36
38
|
|
package/dist/index.js
CHANGED
|
@@ -348,7 +348,7 @@ function apiEquivalentCost(modelKey, t, atMs) {
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
// src/version.ts
|
|
351
|
-
var CLI_VERSION = true ? "0.
|
|
351
|
+
var CLI_VERSION = true ? "0.12.0" : "0.0.0-dev";
|
|
352
352
|
|
|
353
353
|
// src/api.ts
|
|
354
354
|
var BASE_URL = process.env.AISTACK_URL || "https://aistack.to";
|
|
@@ -6202,7 +6202,7 @@ ${e instanceof Error ? e.message : String(e)}`
|
|
|
6202
6202
|
}
|
|
6203
6203
|
return {
|
|
6204
6204
|
ok: true,
|
|
6205
|
-
message: `Installed. Say ${limeBold('"sync my stack"')} in any Claude Code session
|
|
6205
|
+
message: `Installed the user-scoped aistack MCP server and Skill. Say ${limeBold('"sync my stack"')} in any Claude Code session. Every send still requires your confirmation. Remove it with: claude mcp remove --scope user aistack`
|
|
6206
6206
|
};
|
|
6207
6207
|
}
|
|
6208
6208
|
async function connectCommand(harness) {
|
|
@@ -6238,17 +6238,17 @@ async function offerConnectUpsell(deps = {}) {
|
|
|
6238
6238
|
if (!await (deps.claudeActiveImpl ?? claudeRecentlyActive)()) return;
|
|
6239
6239
|
if (!(deps.claudeOnPathImpl ?? claudeOnPath)()) return;
|
|
6240
6240
|
const answer = await p3.select({
|
|
6241
|
-
message: "
|
|
6241
|
+
message: "Add AI Stack commands to Claude Code? Every send still asks first.",
|
|
6242
6242
|
options: [
|
|
6243
6243
|
{
|
|
6244
6244
|
value: "later",
|
|
6245
|
-
label: "
|
|
6246
|
-
hint: "
|
|
6245
|
+
label: "No, don't ask again",
|
|
6246
|
+
hint: "you can install later with aistack connect claude"
|
|
6247
6247
|
},
|
|
6248
6248
|
{
|
|
6249
6249
|
value: "install",
|
|
6250
|
-
label: "Install",
|
|
6251
|
-
hint: "
|
|
6250
|
+
label: "Install MCP + Skill",
|
|
6251
|
+
hint: "user scope; enables preview and confirmed send commands"
|
|
6252
6252
|
}
|
|
6253
6253
|
],
|
|
6254
6254
|
initialValue: "later"
|
|
@@ -6781,7 +6781,8 @@ async function settleAutoSync(permission, deps = {}) {
|
|
|
6781
6781
|
return offerAutoSyncOptIn(deps);
|
|
6782
6782
|
}
|
|
6783
6783
|
async function offerAutoSyncOptIn(deps = {}) {
|
|
6784
|
-
if (getSettings(deps.settingsFile).
|
|
6784
|
+
if (getSettings(deps.settingsFile).autoSyncNeverAskAgain === true)
|
|
6785
|
+
return false;
|
|
6785
6786
|
const detected = await (deps.detectedImpl ?? detectedAdapters)();
|
|
6786
6787
|
if (detected.length === 0) return false;
|
|
6787
6788
|
const answer = await p6.select({
|
|
@@ -6794,8 +6795,13 @@ async function offerAutoSyncOptIn(deps = {}) {
|
|
|
6794
6795
|
},
|
|
6795
6796
|
{
|
|
6796
6797
|
value: "later",
|
|
6797
|
-
label: "
|
|
6798
|
-
hint: "
|
|
6798
|
+
label: "Maybe later",
|
|
6799
|
+
hint: "ask again after your next manual sync"
|
|
6800
|
+
},
|
|
6801
|
+
{
|
|
6802
|
+
value: "never",
|
|
6803
|
+
label: "Never ask again",
|
|
6804
|
+
hint: "you can still enable it with sync --auto on"
|
|
6799
6805
|
}
|
|
6800
6806
|
],
|
|
6801
6807
|
initialValue: "enable"
|
|
@@ -6813,7 +6819,9 @@ async function offerAutoSyncOptIn(deps = {}) {
|
|
|
6813
6819
|
}
|
|
6814
6820
|
return true;
|
|
6815
6821
|
}
|
|
6816
|
-
|
|
6822
|
+
if (answer === "never") {
|
|
6823
|
+
saveSettings({ autoSyncNeverAskAgain: true }, deps.settingsFile);
|
|
6824
|
+
}
|
|
6817
6825
|
p6.log.message(
|
|
6818
6826
|
`If you change your mind: ${limeBold("npx @use-aistack/cli sync --auto on")} ${dim(
|
|
6819
6827
|
"(and --auto off to revoke)"
|
|
@@ -6982,7 +6990,7 @@ function selectDaysToPublish(input) {
|
|
|
6982
6990
|
}
|
|
6983
6991
|
|
|
6984
6992
|
// src/workflow/git.ts
|
|
6985
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
6993
|
+
import { execFile, execFileSync as execFileSync2 } from "child_process";
|
|
6986
6994
|
import path6 from "path";
|
|
6987
6995
|
var TEST_FILE_RULE_VERSION = "test-files/v2";
|
|
6988
6996
|
var FILE_TYPE_RULE_VERSION = "file-types/v2";
|
|
@@ -6999,6 +7007,18 @@ var defaultRunner = (cwd, args) => {
|
|
|
6999
7007
|
return null;
|
|
7000
7008
|
}
|
|
7001
7009
|
};
|
|
7010
|
+
var defaultAsyncRunner = (cwd, args) => new Promise((resolve) => {
|
|
7011
|
+
execFile(
|
|
7012
|
+
"git",
|
|
7013
|
+
[...args],
|
|
7014
|
+
{
|
|
7015
|
+
cwd,
|
|
7016
|
+
encoding: "utf8",
|
|
7017
|
+
maxBuffer: 64 * 1024 * 1024
|
|
7018
|
+
},
|
|
7019
|
+
(error, stdout) => resolve(error ? null : stdout)
|
|
7020
|
+
);
|
|
7021
|
+
});
|
|
7002
7022
|
var emptyGitDay = () => ({
|
|
7003
7023
|
testFileRuleVersion: TEST_FILE_RULE_VERSION,
|
|
7004
7024
|
fileTypeRuleVersion: FILE_TYPE_RULE_VERSION,
|
|
@@ -7153,6 +7173,39 @@ function extractGitWorkflow(options) {
|
|
|
7153
7173
|
const root = run(directory, ["rev-parse", "--show-toplevel"])?.trim();
|
|
7154
7174
|
if (root) roots.add(root);
|
|
7155
7175
|
}
|
|
7176
|
+
const histories = [];
|
|
7177
|
+
for (const root of roots) {
|
|
7178
|
+
const history = run(root, gitLogArgs());
|
|
7179
|
+
if (history) histories.push(history);
|
|
7180
|
+
}
|
|
7181
|
+
return reduceGitHistories(histories, options);
|
|
7182
|
+
}
|
|
7183
|
+
async function extractGitWorkflowAsync(options) {
|
|
7184
|
+
const run = options.run ?? defaultAsyncRunner;
|
|
7185
|
+
const roots = /* @__PURE__ */ new Set();
|
|
7186
|
+
for (const directory of options.workingDirectories) {
|
|
7187
|
+
const root = (await run(directory, ["rev-parse", "--show-toplevel"]))?.trim();
|
|
7188
|
+
if (root) roots.add(root);
|
|
7189
|
+
}
|
|
7190
|
+
const histories = await Promise.all(
|
|
7191
|
+
[...roots].map((root) => run(root, gitLogArgs()))
|
|
7192
|
+
);
|
|
7193
|
+
return reduceGitHistories(
|
|
7194
|
+
histories.filter((history) => history !== null),
|
|
7195
|
+
options
|
|
7196
|
+
);
|
|
7197
|
+
}
|
|
7198
|
+
function gitLogArgs() {
|
|
7199
|
+
return [
|
|
7200
|
+
"log",
|
|
7201
|
+
"--all",
|
|
7202
|
+
"--no-merges",
|
|
7203
|
+
`--format=%x00${COMMIT_MARKER}%x00%H%x00%aI%x00`,
|
|
7204
|
+
"--numstat",
|
|
7205
|
+
"-z"
|
|
7206
|
+
];
|
|
7207
|
+
}
|
|
7208
|
+
function reduceGitHistories(histories, options) {
|
|
7156
7209
|
const days = /* @__PURE__ */ new Map();
|
|
7157
7210
|
const dayOf = (date) => {
|
|
7158
7211
|
let day = days.get(date);
|
|
@@ -7173,16 +7226,7 @@ function extractGitWorkflow(options) {
|
|
|
7173
7226
|
return day;
|
|
7174
7227
|
};
|
|
7175
7228
|
const seenCommits = /* @__PURE__ */ new Set();
|
|
7176
|
-
for (const
|
|
7177
|
-
const history = run(root, [
|
|
7178
|
-
"log",
|
|
7179
|
-
"--all",
|
|
7180
|
-
"--no-merges",
|
|
7181
|
-
`--format=%x00${COMMIT_MARKER}%x00%H%x00%aI%x00`,
|
|
7182
|
-
"--numstat",
|
|
7183
|
-
"-z"
|
|
7184
|
-
]);
|
|
7185
|
-
if (!history) continue;
|
|
7229
|
+
for (const history of histories) {
|
|
7186
7230
|
let current;
|
|
7187
7231
|
const finishCommit = () => {
|
|
7188
7232
|
if (!current?.included || !current.authored) return;
|
|
@@ -7292,6 +7336,18 @@ function extractLocalWorkflow(options) {
|
|
|
7292
7336
|
});
|
|
7293
7337
|
return buildWorkflowExtraction(options.harnesses, git, utcOffsetMinutes);
|
|
7294
7338
|
}
|
|
7339
|
+
async function extractLocalWorkflowAsync(options) {
|
|
7340
|
+
const utcOffsetMinutes = options.utcOffsetMinutes ?? machineUtcOffsetMinutes();
|
|
7341
|
+
const git = await extractGitWorkflowAsync({
|
|
7342
|
+
workingDirectories: options.harnesses.flatMap(({ local }) => [
|
|
7343
|
+
...local.projectWorkspaces
|
|
7344
|
+
]),
|
|
7345
|
+
fromMs: options.fromMs,
|
|
7346
|
+
toMs: options.toMs,
|
|
7347
|
+
utcOffsetMinutes
|
|
7348
|
+
});
|
|
7349
|
+
return buildWorkflowExtraction(options.harnesses, git, utcOffsetMinutes);
|
|
7350
|
+
}
|
|
7295
7351
|
function machineUtcOffsetMinutes(now = /* @__PURE__ */ new Date()) {
|
|
7296
7352
|
return -now.getTimezoneOffset();
|
|
7297
7353
|
}
|
|
@@ -7378,20 +7434,21 @@ function buildGateDialog(ctx) {
|
|
|
7378
7434
|
const days = payloads[0]?.window.days ?? 0;
|
|
7379
7435
|
const facts = [
|
|
7380
7436
|
`${fmtTokens(tokens)} tokens`,
|
|
7381
|
-
`${days}
|
|
7437
|
+
`${days}-day profile`,
|
|
7438
|
+
...ctx.body.measuredDays && ctx.body.measuredDays.days.length !== days ? [`${ctx.body.measuredDays.days.length} historical days`] : [],
|
|
7382
7439
|
...usd === null ? [] : [fmtUSD(usd)]
|
|
7383
7440
|
].join(" \xB7 ");
|
|
7384
7441
|
const n = payloads.reduce((a, p8) => a + withheldCount(p8), 0);
|
|
7385
7442
|
const lines2 = [`Publish to aistack? ${facts}`];
|
|
7386
7443
|
if (n > 0) {
|
|
7387
7444
|
lines2.push(
|
|
7388
|
-
keptPrivate === void 0 ? `${n} name${n === 1 ? "" : "s"} stay${n === 1 ? "s" : ""} on this machine` : `${n} name${n === 1 ? "" : "s"}
|
|
7445
|
+
keptPrivate === void 0 ? `${n} name${n === 1 ? "" : "s"} stay${n === 1 ? "s" : ""} on this machine` : `${n} private review name${n === 1 ? "" : "s"} will be stored`
|
|
7389
7446
|
);
|
|
7390
7447
|
}
|
|
7391
7448
|
return lines2.join("\n");
|
|
7392
7449
|
}
|
|
7393
7450
|
var CATEGORY_LABEL = {
|
|
7394
|
-
builtinTools: "
|
|
7451
|
+
builtinTools: "actions",
|
|
7395
7452
|
mcpServers: "mcp",
|
|
7396
7453
|
skills: "skills",
|
|
7397
7454
|
subagents: "agents",
|
|
@@ -7421,6 +7478,24 @@ function wrapRow(head, continuation, text, width) {
|
|
|
7421
7478
|
if (line !== "") lines2.push(line);
|
|
7422
7479
|
return lines2.map((l, i) => (i === 0 ? head : continuation) + l);
|
|
7423
7480
|
}
|
|
7481
|
+
function wrapEntries(head, continuation, entries, width) {
|
|
7482
|
+
const limit = Math.max(24, width - continuation.length);
|
|
7483
|
+
const lines2 = [];
|
|
7484
|
+
let line = "";
|
|
7485
|
+
for (const entry of entries) {
|
|
7486
|
+
const next = line ? `${line} \xB7 ${entry}` : entry;
|
|
7487
|
+
if (line && next.length > limit) {
|
|
7488
|
+
lines2.push(line);
|
|
7489
|
+
line = entry;
|
|
7490
|
+
} else {
|
|
7491
|
+
line = next;
|
|
7492
|
+
}
|
|
7493
|
+
}
|
|
7494
|
+
if (line) lines2.push(line);
|
|
7495
|
+
return lines2.map(
|
|
7496
|
+
(line2, index) => `${index === 0 ? head : continuation}${line2}`
|
|
7497
|
+
);
|
|
7498
|
+
}
|
|
7424
7499
|
function keptPrivateRows(keptPrivate) {
|
|
7425
7500
|
const groups = /* @__PURE__ */ new Map();
|
|
7426
7501
|
const singles = [];
|
|
@@ -7516,23 +7591,20 @@ function payloadBlock(payload, width, ownWindow, stats) {
|
|
|
7516
7591
|
}
|
|
7517
7592
|
if (entries.length > 0) {
|
|
7518
7593
|
out.push(
|
|
7519
|
-
...
|
|
7520
|
-
"models ",
|
|
7521
|
-
" ".repeat(LABEL_WIDTH),
|
|
7522
|
-
entries.join(" \xB7 "),
|
|
7523
|
-
width
|
|
7524
|
-
)
|
|
7594
|
+
...wrapEntries("models ", " ".repeat(LABEL_WIDTH), entries, width)
|
|
7525
7595
|
);
|
|
7526
7596
|
}
|
|
7527
7597
|
const filled = NAME_CATEGORIES.filter(
|
|
7528
7598
|
(category) => payload.inventory[category].length > 0
|
|
7529
7599
|
);
|
|
7530
7600
|
if (filled.length === 0) {
|
|
7531
|
-
out.push(
|
|
7601
|
+
out.push(
|
|
7602
|
+
`${"sends".padEnd(LABEL_WIDTH)}no inventory names from this harness`
|
|
7603
|
+
);
|
|
7532
7604
|
return out;
|
|
7533
7605
|
}
|
|
7534
7606
|
out.push(
|
|
7535
|
-
`${"
|
|
7607
|
+
`${"sends".padEnd(LABEL_WIDTH)}${filled.map(
|
|
7536
7608
|
(category) => `${payload.inventory[category].length} ${CATEGORY_LABEL[category]}`
|
|
7537
7609
|
).join(" \xB7 ")}`
|
|
7538
7610
|
);
|
|
@@ -7648,6 +7720,14 @@ function buildGateSummary(ctx) {
|
|
|
7648
7720
|
out.push(`prices ${origin} \xB7 cites ${cited.join(", ")}`);
|
|
7649
7721
|
}
|
|
7650
7722
|
const width = wrapWidth(ctx.width);
|
|
7723
|
+
out.push(
|
|
7724
|
+
...wrapRow(
|
|
7725
|
+
"privacy ",
|
|
7726
|
+
" ".repeat(LABEL_WIDTH),
|
|
7727
|
+
"raw conversation text, paths, repo names, and command arguments stay local",
|
|
7728
|
+
width
|
|
7729
|
+
)
|
|
7730
|
+
);
|
|
7651
7731
|
for (const payload of payloads) {
|
|
7652
7732
|
const stats = ctx.scanStats?.[payload.harness.name];
|
|
7653
7733
|
out.push("", DIVIDER, "");
|
|
@@ -7675,10 +7755,12 @@ function buildGateSummary(ctx) {
|
|
|
7675
7755
|
const shown = rows.slice(0, KEPT_PRIVATE_ROWS_SHOWN);
|
|
7676
7756
|
const examples = shown.map((r) => r.names > 1 ? `${r.label} \xD7${r.names}` : r.label).join(", ");
|
|
7677
7757
|
const more = rows.length > shown.length ? `, ...${rows.length - shown.length} more` : "";
|
|
7678
|
-
out.push(
|
|
7758
|
+
out.push(
|
|
7759
|
+
`private ${n} review name${n === 1 ? "" : "s"} \xB7 ${examples}${more}`
|
|
7760
|
+
);
|
|
7679
7761
|
if (body.keptPrivate !== void 0 && config.stack !== null) {
|
|
7680
7762
|
out.push(
|
|
7681
|
-
`
|
|
7763
|
+
` stored privately for your review at ${host}/stacks/${config.stack.slug}/changes`
|
|
7682
7764
|
);
|
|
7683
7765
|
out.push(
|
|
7684
7766
|
" (turn off: Review kept-private names, on your stack)"
|
|
@@ -7718,10 +7800,13 @@ async function stageSync(deps) {
|
|
|
7718
7800
|
const projectWorkspaceId = deps.getProjectWorkspaceIdImpl ?? getProjectWorkspaceId;
|
|
7719
7801
|
const fetchManifest = deps.fetchManifestImpl ?? fetchDayManifest;
|
|
7720
7802
|
const fetchPrices = deps.fetchPricesImpl ?? fetchPriceTable;
|
|
7803
|
+
const progress = deps.onProgress ?? (() => {
|
|
7804
|
+
});
|
|
7721
7805
|
let prices = {
|
|
7722
7806
|
id: BUNDLED_PRICE_TABLE_ID,
|
|
7723
7807
|
origin: "bundled"
|
|
7724
7808
|
};
|
|
7809
|
+
progress("Checking prices and stack settings");
|
|
7725
7810
|
try {
|
|
7726
7811
|
const table = await fetchPrices(deps.baseUrl);
|
|
7727
7812
|
if (table) {
|
|
@@ -7757,7 +7842,11 @@ async function stageSync(deps) {
|
|
|
7757
7842
|
const daysSinceMs = windowStartMs(now, retentionDays);
|
|
7758
7843
|
const active2 = await adapters(sinceMs);
|
|
7759
7844
|
for (const adapter of active2) {
|
|
7760
|
-
|
|
7845
|
+
progress(`Scanning recent ${adapter.name} usage`);
|
|
7846
|
+
const { aggregate, stats } = await adapter.scan({
|
|
7847
|
+
sinceMs,
|
|
7848
|
+
onProgress: (files) => progress(`Scanning recent ${adapter.name} usage \xB7 ${files} files`)
|
|
7849
|
+
});
|
|
7761
7850
|
scanStats[adapter.name] = stats;
|
|
7762
7851
|
built.push(
|
|
7763
7852
|
buildPayload({
|
|
@@ -7773,8 +7862,10 @@ async function stageSync(deps) {
|
|
|
7773
7862
|
);
|
|
7774
7863
|
}
|
|
7775
7864
|
for (const adapter of active2) {
|
|
7865
|
+
progress(`Reading historical ${adapter.name} days`);
|
|
7776
7866
|
const { aggregate, workflow: workflow2, workflowLocal } = await adapter.scan({
|
|
7777
|
-
sinceMs: daysSinceMs
|
|
7867
|
+
sinceMs: daysSinceMs,
|
|
7868
|
+
onProgress: (files) => progress(`Reading historical ${adapter.name} days \xB7 ${files} files`)
|
|
7778
7869
|
});
|
|
7779
7870
|
workflowScans.push({ aggregate: workflow2, local: workflowLocal });
|
|
7780
7871
|
usageScans.push(
|
|
@@ -7787,12 +7878,20 @@ async function stageSync(deps) {
|
|
|
7787
7878
|
);
|
|
7788
7879
|
}
|
|
7789
7880
|
const settings = (deps.getSettingsImpl ?? getSettings)();
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
|
|
7881
|
+
let workflow;
|
|
7882
|
+
if (workflowScans.length > 0 && config.publishWorkflow) {
|
|
7883
|
+
progress("Reading Git history");
|
|
7884
|
+
workflow = deps.gitRunnerImpl ? extractLocalWorkflow({
|
|
7885
|
+
harnesses: workflowScans,
|
|
7886
|
+
fromMs: daysSinceMs,
|
|
7887
|
+
toMs: now,
|
|
7888
|
+
run: deps.gitRunnerImpl
|
|
7889
|
+
}) : await extractLocalWorkflowAsync({
|
|
7890
|
+
harnesses: workflowScans,
|
|
7891
|
+
fromMs: daysSinceMs,
|
|
7892
|
+
toMs: now
|
|
7893
|
+
});
|
|
7894
|
+
}
|
|
7796
7895
|
const localDays = applyDayConsent(
|
|
7797
7896
|
buildMeasuredDays({
|
|
7798
7897
|
usage: mergeUsageDays(usageScans),
|
|
@@ -7819,6 +7918,7 @@ async function stageSync(deps) {
|
|
|
7819
7918
|
} : void 0,
|
|
7820
7919
|
CLI_VERSION
|
|
7821
7920
|
);
|
|
7921
|
+
progress("Preparing review");
|
|
7822
7922
|
const bodyJson = JSON.stringify(body);
|
|
7823
7923
|
const keptPrivate = mergeKeptPrivate(built.map((b) => b.keptPrivate));
|
|
7824
7924
|
const ctx = {
|
|
@@ -8090,7 +8190,8 @@ async function syncCommand(options = {}) {
|
|
|
8090
8190
|
staged = await stageSync({
|
|
8091
8191
|
baseUrl: BASE_URL,
|
|
8092
8192
|
getTokenImpl: () => destinationToken,
|
|
8093
|
-
loadConfigImpl: async () => destinationConfig
|
|
8193
|
+
loadConfigImpl: async () => destinationConfig,
|
|
8194
|
+
onProgress: (message) => s.message(message)
|
|
8094
8195
|
});
|
|
8095
8196
|
} catch (e) {
|
|
8096
8197
|
s.stop("Scan failed");
|
|
@@ -8133,7 +8234,12 @@ async function syncCommand(options = {}) {
|
|
|
8133
8234
|
);
|
|
8134
8235
|
} else if (res.keptPrivate.stored > 0) {
|
|
8135
8236
|
lines2.push(
|
|
8136
|
-
`${res.keptPrivate.stored}
|
|
8237
|
+
`${res.keptPrivate.stored} private review name${res.keptPrivate.stored === 1 ? "" : "s"} stored at ${res.url}/changes`
|
|
8238
|
+
);
|
|
8239
|
+
}
|
|
8240
|
+
if (res.keptPrivate.machineStored > 0) {
|
|
8241
|
+
lines2.push(
|
|
8242
|
+
"This machine's private label was stored for the same review."
|
|
8137
8243
|
);
|
|
8138
8244
|
}
|
|
8139
8245
|
p7.log.message(lines2.join("\n"));
|
|
@@ -8162,6 +8268,26 @@ function styleSummaryLine(line) {
|
|
|
8162
8268
|
return line;
|
|
8163
8269
|
}
|
|
8164
8270
|
|
|
8271
|
+
// src/node-version.ts
|
|
8272
|
+
var MINIMUM_NODE_VERSION = "22.5.0";
|
|
8273
|
+
function versionParts(version) {
|
|
8274
|
+
const match = /^(?:v)?(\d+)\.(\d+)\.(\d+)/.exec(version);
|
|
8275
|
+
if (!match) return null;
|
|
8276
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
8277
|
+
}
|
|
8278
|
+
function supportsNodeVersion(version) {
|
|
8279
|
+
const actual = versionParts(version);
|
|
8280
|
+
const minimum = versionParts(MINIMUM_NODE_VERSION);
|
|
8281
|
+
if (!actual || !minimum) return false;
|
|
8282
|
+
for (let i = 0; i < actual.length; i++) {
|
|
8283
|
+
if (actual[i] !== minimum[i]) return actual[i] > minimum[i];
|
|
8284
|
+
}
|
|
8285
|
+
return true;
|
|
8286
|
+
}
|
|
8287
|
+
function unsupportedNodeMessage(version) {
|
|
8288
|
+
return `aistack requires Node.js ${MINIMUM_NODE_VERSION} or newer. You are running ${version}. Upgrade Node.js so sync can read OpenCode's SQLite usage database.`;
|
|
8289
|
+
}
|
|
8290
|
+
|
|
8165
8291
|
// src/sync/server.ts
|
|
8166
8292
|
var SERVER_NAME = "aistack";
|
|
8167
8293
|
var SERVER_VERSION = "0.3.0";
|
|
@@ -8332,7 +8458,12 @@ function createSyncServer(deps, send) {
|
|
|
8332
8458
|
);
|
|
8333
8459
|
} else if (res.keptPrivate.stored > 0) {
|
|
8334
8460
|
lines2.push(
|
|
8335
|
-
`${res.keptPrivate.stored}
|
|
8461
|
+
`${res.keptPrivate.stored} private review name${res.keptPrivate.stored === 1 ? "" : "s"} stored at ${res.url}/changes`
|
|
8462
|
+
);
|
|
8463
|
+
}
|
|
8464
|
+
if (res.keptPrivate.machineStored > 0) {
|
|
8465
|
+
lines2.push(
|
|
8466
|
+
"This machine's private label was stored for the same review."
|
|
8336
8467
|
);
|
|
8337
8468
|
}
|
|
8338
8469
|
ok(id, textResult(lines2.join("\n")));
|
|
@@ -8436,5 +8567,11 @@ program.command("sync").description("Scan, preview, and publish measured usage (
|
|
|
8436
8567
|
"with --auto on: hours between auto-syncs (default 6)"
|
|
8437
8568
|
).action((options) => syncCommand(options));
|
|
8438
8569
|
program.command("connect").description("Install the in-session sync surface (MCP server + Skill)").argument("<harness>", 'the harness to connect ("claude")').action(connectCommand);
|
|
8439
|
-
|
|
8570
|
+
if (!supportsNodeVersion(process.versions.node)) {
|
|
8571
|
+
process.stderr.write(`${unsupportedNodeMessage(process.versions.node)}
|
|
8572
|
+
`);
|
|
8573
|
+
process.exitCode = 1;
|
|
8574
|
+
} else {
|
|
8575
|
+
program.parse();
|
|
8576
|
+
}
|
|
8440
8577
|
//# sourceMappingURL=index.js.map
|