@hireobi/cli 0.1.9 → 0.1.10
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 +2 -0
- package/dist/obi.js +156 -23
- package/package.json +1 -1
- package/skills/obi/SKILL.md +2 -0
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@ npm i -g @hireobi/cli && obi
|
|
|
12
12
|
|
|
13
13
|
Windows PowerShell 5.1 has no `&&`: run `npm i -g @hireobi/cli`, then `obi`.
|
|
14
14
|
|
|
15
|
+
To update, run the same line again: `obi` refreshes the Skill links. At most once a day, any command asks npm for the newest version; when yours is older, a JSON envelope carries `update` and a human run prints one line on stderr, and the Skill offers to update. Offline or slow, the check stays silent. `obi doctor` shows the installed and newest versions. `OBI_NO_UPDATE_CHECK=1` turns the check off, and so does `CI`. A version the service no longer accepts fails with `CLI_UPDATE_REQUIRED` and the update command.
|
|
16
|
+
|
|
15
17
|
The first `obi` registers the Obi Skill for Claude Code, Codex, Cursor and OpenCode, and every later command keeps it registered: two personal links, `~/.claude/skills/obi` and `~/.agents/skills/obi`. A conflicting file or link is never replaced: the command still runs, and bare `obi` says what is in the way and how to fix it. `obi skill install` registers it again after a fix and removes the links an earlier alpha of this package made under its former name; `obi skill path` locates the bundled Skill. `OBI_SKILL_LINKS=off` leaves the links to `obi skill install`. A cloud harness needs its own supported installation path.
|
|
16
18
|
|
|
17
19
|
Run by a coding agent, bare `obi` prints where the Skill's guide is and the next command. In an interactive terminal it shows the Obi mascot and a short welcome, then exits: the conversation happens in the assistant. The greeting is English or French; `OBI_LANGUAGE` overrides `LC_ALL`, `LC_MESSAGES`, then `LANG`.
|
package/dist/obi.js
CHANGED
|
@@ -16664,7 +16664,43 @@ var SUPPORTED_MARKET_NAMES = SUPPORTED_MARKETS.map(
|
|
|
16664
16664
|
(market) => market.name
|
|
16665
16665
|
);
|
|
16666
16666
|
|
|
16667
|
+
// ../shared/lib/cli-version.ts
|
|
16668
|
+
var CLI_VERSION_HEADER = "x-obi-cli-version";
|
|
16669
|
+
var CLI_UPDATE_COMMAND = "npm i -g @hireobi/cli";
|
|
16670
|
+
var release = /^(0|[1-9][0-9]{0,8})\.(0|[1-9][0-9]{0,8})\.(0|[1-9][0-9]{0,8})$/;
|
|
16671
|
+
function parseRelease(version3) {
|
|
16672
|
+
const match = release.exec(version3);
|
|
16673
|
+
return match === null ? null : [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
16674
|
+
}
|
|
16675
|
+
function isOlderRelease(version3, other) {
|
|
16676
|
+
const left = parseRelease(version3);
|
|
16677
|
+
const right = parseRelease(other);
|
|
16678
|
+
if (left === null || right === null) return false;
|
|
16679
|
+
for (let index = 0; index < 3; index += 1) {
|
|
16680
|
+
if (left[index] !== right[index]) return left[index] < right[index];
|
|
16681
|
+
}
|
|
16682
|
+
return false;
|
|
16683
|
+
}
|
|
16684
|
+
|
|
16685
|
+
// src/version.ts
|
|
16686
|
+
var cliVersion = "0.1.10";
|
|
16687
|
+
|
|
16667
16688
|
// src/product-api-failures.ts
|
|
16689
|
+
var versionHeaders = { [CLI_VERSION_HEADER]: cliVersion };
|
|
16690
|
+
function updateRequiredError() {
|
|
16691
|
+
return new CliError({
|
|
16692
|
+
code: "CLI_UPDATE_REQUIRED",
|
|
16693
|
+
message: `This Obi CLI (${cliVersion}) is too old for the Obi service.`,
|
|
16694
|
+
exitCode: 7,
|
|
16695
|
+
retryable: false,
|
|
16696
|
+
nextAction: `Run ${CLI_UPDATE_COMMAND}, then run obi once to refresh the Skill, and try again.`,
|
|
16697
|
+
mode: "product"
|
|
16698
|
+
});
|
|
16699
|
+
}
|
|
16700
|
+
function apiFailure(code, httpStatus, retryAfterSeconds) {
|
|
16701
|
+
if (code === "CLI_UPDATE_REQUIRED") throw updateRequiredError();
|
|
16702
|
+
return { ok: false, failure: { code, httpStatus, retryAfterSeconds } };
|
|
16703
|
+
}
|
|
16668
16704
|
function transportError(code, message, exitCode, retryable) {
|
|
16669
16705
|
return new CliError({
|
|
16670
16706
|
code,
|
|
@@ -17294,7 +17330,9 @@ var pendingAuthorizationViewSchema = external_exports.object({
|
|
|
17294
17330
|
var originMarker = {
|
|
17295
17331
|
origin: external_exports.string().min(1).max(200).optional(),
|
|
17296
17332
|
development: external_exports.literal(true).optional(),
|
|
17297
|
-
evaluationCaseId: external_exports.uuid().optional()
|
|
17333
|
+
evaluationCaseId: external_exports.uuid().optional(),
|
|
17334
|
+
// Set when npm holds a newer CLI than this one (cli/src/update-check.ts).
|
|
17335
|
+
update: external_exports.object({ installed: external_exports.string().max(32), latest: external_exports.string().max(32), command: external_exports.string().max(80) }).optional()
|
|
17298
17336
|
};
|
|
17299
17337
|
var helpDataSchema = external_exports.object({
|
|
17300
17338
|
usage: external_exports.string(),
|
|
@@ -17339,6 +17377,10 @@ var capabilitiesDataSchema = external_exports.object({
|
|
|
17339
17377
|
var localDoctorDataSchema = external_exports.object({
|
|
17340
17378
|
node: external_exports.string(),
|
|
17341
17379
|
cliVersion: external_exports.string(),
|
|
17380
|
+
latestVersion: external_exports.string().nullable(),
|
|
17381
|
+
// Set only when npm could not be reached: the last version it named.
|
|
17382
|
+
lastKnownVersion: external_exports.string().nullable(),
|
|
17383
|
+
updateCheck: external_exports.enum(["on", "off"]),
|
|
17342
17384
|
origin: external_exports.string(),
|
|
17343
17385
|
credentialStorage: external_exports.union([backendKindSchema, external_exports.literal("none")]),
|
|
17344
17386
|
videoTools: external_exports.object({ ytDlp: external_exports.boolean(), ffmpeg: external_exports.boolean() }),
|
|
@@ -17780,7 +17822,7 @@ function retryAfter(response) {
|
|
|
17780
17822
|
return typeof value === "string" && /^[0-9]{1,5}$/.test(value) ? Number(value) : null;
|
|
17781
17823
|
}
|
|
17782
17824
|
async function send(context, request) {
|
|
17783
|
-
const headers2 = { accept: "application/json" };
|
|
17825
|
+
const headers2 = { accept: "application/json", ...versionHeaders };
|
|
17784
17826
|
if (request.body !== void 0) {
|
|
17785
17827
|
headers2["content-type"] = "application/json";
|
|
17786
17828
|
}
|
|
@@ -17990,12 +18032,7 @@ function interpret(response, schema) {
|
|
|
17990
18032
|
return { ok: true, data: parsed.data.data };
|
|
17991
18033
|
}
|
|
17992
18034
|
const failure2 = ProductApiErrorResponseSchema.safeParse(body);
|
|
17993
|
-
if (failure2.success)
|
|
17994
|
-
return {
|
|
17995
|
-
ok: false,
|
|
17996
|
-
failure: { code: failure2.data.error.code, httpStatus: response.status, retryAfterSeconds: retryAfter(response) }
|
|
17997
|
-
};
|
|
17998
|
-
}
|
|
18035
|
+
if (failure2.success) return apiFailure(failure2.data.error.code, response.status, retryAfter(response));
|
|
17999
18036
|
if (response.status >= 500) {
|
|
18000
18037
|
throw transportError("SERVICE_UNAVAILABLE", "The Obi API is unavailable.", 6, true);
|
|
18001
18038
|
}
|
|
@@ -20376,7 +20413,7 @@ function memoryText(data) {
|
|
|
20376
20413
|
|
|
20377
20414
|
// src/support-commands.ts
|
|
20378
20415
|
import { createHash as createHash2 } from "node:crypto";
|
|
20379
|
-
import { release } from "node:os";
|
|
20416
|
+
import { release as release2 } from "node:os";
|
|
20380
20417
|
|
|
20381
20418
|
// src/last-error.ts
|
|
20382
20419
|
import { readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
|
|
@@ -20400,9 +20437,6 @@ async function readLastError(stateDirectory) {
|
|
|
20400
20437
|
}
|
|
20401
20438
|
}
|
|
20402
20439
|
|
|
20403
|
-
// src/version.ts
|
|
20404
|
-
var cliVersion = "0.1.9";
|
|
20405
|
-
|
|
20406
20440
|
// src/support-commands.ts
|
|
20407
20441
|
async function probeServer(origin, timeoutMs) {
|
|
20408
20442
|
try {
|
|
@@ -20419,7 +20453,7 @@ async function probeServer(origin, timeoutMs) {
|
|
|
20419
20453
|
}
|
|
20420
20454
|
async function sendSupport(context, input) {
|
|
20421
20455
|
const lastError = await readLastError(context.stateDirectory);
|
|
20422
|
-
const diagnostic = { cliVersion, node: process.version, os: `${process.platform} ${
|
|
20456
|
+
const diagnostic = { cliVersion, node: process.version, os: `${process.platform} ${release2()} ${process.arch}`, lastError };
|
|
20423
20457
|
let account2 = null;
|
|
20424
20458
|
let reason = null;
|
|
20425
20459
|
try {
|
|
@@ -20695,7 +20729,9 @@ Next: ${sanitizeHumanText(result.data.nextStep)}
|
|
|
20695
20729
|
degraded: `Obi server degraded: try again later, and write to ${SUPPORT_EMAIL} if it lasts.`,
|
|
20696
20730
|
unreachable: `Obi server unreachable: check the network connection, and write to ${SUPPORT_EMAIL} if it lasts.`
|
|
20697
20731
|
}[result.data.server];
|
|
20698
|
-
|
|
20732
|
+
const { cliVersion: installed, latestVersion: latest } = result.data;
|
|
20733
|
+
const version3 = result.data.updateCheck === "off" ? `CLI ${sanitizeHumanText(installed)} (update check off)` : latest === null ? `CLI ${sanitizeHumanText(installed)} (latest version unknown: npm could not be reached${result.data.lastKnownVersion === null ? "" : `, last known ${sanitizeHumanText(result.data.lastKnownVersion)}`})` : isOlderRelease(installed, latest) ? `CLI ${sanitizeHumanText(installed)}, latest ${sanitizeHumanText(latest)}: update with ${CLI_UPDATE_COMMAND}` : `CLI ${sanitizeHumanText(installed)}, the latest`;
|
|
20734
|
+
return `Local diagnostic completed for ${sanitizeHumanText(result.data.origin)}: ${version3}; ${storage}; ${video}. ${server}
|
|
20699
20735
|
`;
|
|
20700
20736
|
}
|
|
20701
20737
|
if (result.command === "login") {
|
|
@@ -21240,7 +21276,7 @@ function accountMismatch(account2, revoked, expected) {
|
|
|
21240
21276
|
}
|
|
21241
21277
|
async function confirmAndStore(context, store, pair, receivedAt, expectedEmail) {
|
|
21242
21278
|
const api = context;
|
|
21243
|
-
const
|
|
21279
|
+
const release4 = guardNewFamily(api, pair);
|
|
21244
21280
|
let stored = false;
|
|
21245
21281
|
try {
|
|
21246
21282
|
const status = await readAccountStatus(api, pair.accessToken);
|
|
@@ -21271,7 +21307,7 @@ async function confirmAndStore(context, store, pair, receivedAt, expectedEmail)
|
|
|
21271
21307
|
}
|
|
21272
21308
|
await store.writeCredential(credentialFromPair(context.origin.origin, pair, confirmed, receivedAt));
|
|
21273
21309
|
stored = true;
|
|
21274
|
-
|
|
21310
|
+
release4();
|
|
21275
21311
|
});
|
|
21276
21312
|
try {
|
|
21277
21313
|
await store.removePending();
|
|
@@ -21291,7 +21327,7 @@ async function confirmAndStore(context, store, pair, receivedAt, expectedEmail)
|
|
|
21291
21327
|
}
|
|
21292
21328
|
throw error51;
|
|
21293
21329
|
} finally {
|
|
21294
|
-
|
|
21330
|
+
release4();
|
|
21295
21331
|
}
|
|
21296
21332
|
}
|
|
21297
21333
|
async function login(session, options) {
|
|
@@ -21606,7 +21642,7 @@ async function safeFetch(url3, opts = {}) {
|
|
|
21606
21642
|
if (!REDIRECT_STATUSES.has(res.status)) return res;
|
|
21607
21643
|
const location = res.headers.get("location");
|
|
21608
21644
|
if (!location) return res;
|
|
21609
|
-
await
|
|
21645
|
+
await release3(res);
|
|
21610
21646
|
let next;
|
|
21611
21647
|
try {
|
|
21612
21648
|
next = new URL(location, current).href;
|
|
@@ -21617,7 +21653,7 @@ async function safeFetch(url3, opts = {}) {
|
|
|
21617
21653
|
}
|
|
21618
21654
|
throw new TooManyRedirectsError(url3, maxRedirects);
|
|
21619
21655
|
}
|
|
21620
|
-
async function
|
|
21656
|
+
async function release3(res) {
|
|
21621
21657
|
await res.body?.cancel().catch((err) => {
|
|
21622
21658
|
console.warn("[safe-fetch] cancelling an unread body failed:", err);
|
|
21623
21659
|
});
|
|
@@ -21625,7 +21661,7 @@ async function release2(res) {
|
|
|
21625
21661
|
async function readCapped(res, maxBytes = MAX_RESPONSE_BYTES) {
|
|
21626
21662
|
const declared = Number(res.headers.get("content-length"));
|
|
21627
21663
|
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
21628
|
-
await
|
|
21664
|
+
await release3(res);
|
|
21629
21665
|
return "";
|
|
21630
21666
|
}
|
|
21631
21667
|
if (!res.body) return (await res.text()).slice(0, maxBytes);
|
|
@@ -33601,9 +33637,13 @@ function statusResult(status) {
|
|
|
33601
33637
|
}
|
|
33602
33638
|
async function localDoctor(runtime, timeoutMs) {
|
|
33603
33639
|
const located = await locateCredential(runtime);
|
|
33640
|
+
const updates = await runtime.updates;
|
|
33604
33641
|
return success2("doctor", "local", {
|
|
33605
33642
|
node: process.version,
|
|
33606
33643
|
cliVersion,
|
|
33644
|
+
latestVersion: updates?.state === "on" && updates.reached ? updates.latest : null,
|
|
33645
|
+
lastKnownVersion: updates?.state === "on" && !updates.reached ? updates.latest : null,
|
|
33646
|
+
updateCheck: updates?.state ?? "off",
|
|
33607
33647
|
origin: runtime.origin.origin,
|
|
33608
33648
|
credentialStorage: located === null ? "none" : located.store.backend.kind,
|
|
33609
33649
|
videoTools: await videoTools(),
|
|
@@ -33818,6 +33858,81 @@ function renderStart(input) {
|
|
|
33818
33858
|
].join("\n") + "\n";
|
|
33819
33859
|
}
|
|
33820
33860
|
|
|
33861
|
+
// src/update-check.ts
|
|
33862
|
+
import { readFile as readFile9, writeFile as writeFile4 } from "node:fs/promises";
|
|
33863
|
+
import { join as join8 } from "node:path";
|
|
33864
|
+
var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
33865
|
+
var UPDATE_CHECK_TIMEOUT_MS = 1500;
|
|
33866
|
+
var releaseSchema = external_exports.string().max(32).refine((value) => parseRelease(value) !== null);
|
|
33867
|
+
var cacheSchema = external_exports.object({
|
|
33868
|
+
checkedAt: external_exports.iso.datetime(),
|
|
33869
|
+
latest: releaseSchema.nullable()
|
|
33870
|
+
});
|
|
33871
|
+
var cacheFile = (stateDirectory) => join8(stateDirectory, "update-check.json");
|
|
33872
|
+
var npmRegistry = "https://registry.npmjs.org";
|
|
33873
|
+
var loopbackRegistry = /^http:\/\/(127\.0\.0\.1|\[::1\]):[1-9][0-9]{0,4}$/;
|
|
33874
|
+
function updateCheckDisabled(env) {
|
|
33875
|
+
const on = (value) => value !== void 0 && value !== "" && value !== "0" && value.toLowerCase() !== "false";
|
|
33876
|
+
return on(env.OBI_NO_UPDATE_CHECK) || on(env.CI);
|
|
33877
|
+
}
|
|
33878
|
+
async function fetchLatestVersion(env, timeoutMs) {
|
|
33879
|
+
const override = env.OBI_UPDATE_REGISTRY;
|
|
33880
|
+
const registry2 = override !== void 0 && loopbackRegistry.test(override) ? override : npmRegistry;
|
|
33881
|
+
try {
|
|
33882
|
+
const response = await exchange({
|
|
33883
|
+
url: new URL("/-/package/@hireobi%2fcli/dist-tags", registry2),
|
|
33884
|
+
method: "GET",
|
|
33885
|
+
headers: { accept: "application/json" },
|
|
33886
|
+
timeoutMs,
|
|
33887
|
+
responseLimitBytes: 16 * 1024
|
|
33888
|
+
});
|
|
33889
|
+
if (response.status !== 200) return null;
|
|
33890
|
+
const parsed = external_exports.object({ latest: releaseSchema }).safeParse(JSON.parse(response.body));
|
|
33891
|
+
return parsed.success ? parsed.data.latest : null;
|
|
33892
|
+
} catch {
|
|
33893
|
+
return null;
|
|
33894
|
+
}
|
|
33895
|
+
}
|
|
33896
|
+
async function readCache(stateDirectory) {
|
|
33897
|
+
try {
|
|
33898
|
+
const parsed = cacheSchema.safeParse(JSON.parse(await readFile9(cacheFile(stateDirectory), "utf8")));
|
|
33899
|
+
return parsed.success ? parsed.data : null;
|
|
33900
|
+
} catch {
|
|
33901
|
+
return null;
|
|
33902
|
+
}
|
|
33903
|
+
}
|
|
33904
|
+
async function writeCache(stateDirectory, cache) {
|
|
33905
|
+
try {
|
|
33906
|
+
await ensureStateDirectory(stateDirectory);
|
|
33907
|
+
await writeFile4(cacheFile(stateDirectory), JSON.stringify(cache), { mode: 384 });
|
|
33908
|
+
} catch {
|
|
33909
|
+
}
|
|
33910
|
+
}
|
|
33911
|
+
async function checkForUpdate(input) {
|
|
33912
|
+
if (updateCheckDisabled(input.env) || parseRelease(input.installed) === null) return { state: "off" };
|
|
33913
|
+
try {
|
|
33914
|
+
const now = (input.now ?? Date.now)();
|
|
33915
|
+
const cache = await readCache(input.stateDirectory);
|
|
33916
|
+
const age = cache === null ? Infinity : now - Date.parse(cache.checkedAt);
|
|
33917
|
+
let latest = cache?.latest ?? null;
|
|
33918
|
+
let reached = true;
|
|
33919
|
+
if (input.fresh === true || !(age >= 0 && age < UPDATE_CHECK_INTERVAL_MS)) {
|
|
33920
|
+
const fetched = await (input.fetchLatest ?? ((timeoutMs) => fetchLatestVersion(input.env, timeoutMs)))(UPDATE_CHECK_TIMEOUT_MS);
|
|
33921
|
+
reached = fetched !== null;
|
|
33922
|
+
latest = fetched ?? latest;
|
|
33923
|
+
await writeCache(input.stateDirectory, { checkedAt: new Date(now).toISOString(), latest });
|
|
33924
|
+
}
|
|
33925
|
+
const notice = latest !== null && isOlderRelease(input.installed, latest) ? { installed: input.installed, latest, command: CLI_UPDATE_COMMAND } : null;
|
|
33926
|
+
return { state: "on", latest, reached, notice };
|
|
33927
|
+
} catch {
|
|
33928
|
+
return { state: "on", latest: null, reached: false, notice: null };
|
|
33929
|
+
}
|
|
33930
|
+
}
|
|
33931
|
+
function updateNoticeLine(notice) {
|
|
33932
|
+
return `Obi CLI ${notice.latest} is available (you have ${notice.installed}). Update with: ${notice.command}
|
|
33933
|
+
`;
|
|
33934
|
+
}
|
|
33935
|
+
|
|
33821
33936
|
// src/welcome.ts
|
|
33822
33937
|
var copy = {
|
|
33823
33938
|
en: {
|
|
@@ -33941,6 +34056,17 @@ function failureResult(error51, command, mode) {
|
|
|
33941
34056
|
function markOrigin(result, origin) {
|
|
33942
34057
|
return origin?.development === true ? cliResultSchema.parse({ ...result, origin: origin.origin, development: true }) : result;
|
|
33943
34058
|
}
|
|
34059
|
+
async function updateNotice(updates) {
|
|
34060
|
+
const check3 = await updates;
|
|
34061
|
+
return check3?.state === "on" ? check3.notice : null;
|
|
34062
|
+
}
|
|
34063
|
+
function withUpdate(result, notice) {
|
|
34064
|
+
return notice === null ? result : cliResultSchema.parse({ ...result, update: notice });
|
|
34065
|
+
}
|
|
34066
|
+
async function announceUpdate(updates) {
|
|
34067
|
+
const notice = await updateNotice(updates);
|
|
34068
|
+
if (notice !== null) process.stderr.write(updateNoticeLine(notice));
|
|
34069
|
+
}
|
|
33944
34070
|
async function runCli() {
|
|
33945
34071
|
const argv = process.argv.slice(2);
|
|
33946
34072
|
const json2 = wantsJson(argv);
|
|
@@ -33953,6 +34079,7 @@ async function runCli() {
|
|
|
33953
34079
|
argv.some((value) => value === "--test-server" || value.startsWith("--test-server="))
|
|
33954
34080
|
);
|
|
33955
34081
|
let origin;
|
|
34082
|
+
let updates;
|
|
33956
34083
|
let warned = false;
|
|
33957
34084
|
const activeOrigin = () => {
|
|
33958
34085
|
const resolved = resolveOrigin(process.env);
|
|
@@ -33968,7 +34095,9 @@ async function runCli() {
|
|
|
33968
34095
|
command = args.command;
|
|
33969
34096
|
mode = commandMode(args.command, args.testServer !== void 0);
|
|
33970
34097
|
origin = activeOrigin();
|
|
33971
|
-
const
|
|
34098
|
+
const stateDirectory = await prepareStateDirectory(process.env);
|
|
34099
|
+
updates = checkForUpdate({ env: process.env, stateDirectory, installed: cliVersion, fresh: command === "doctor" });
|
|
34100
|
+
const runtime = { origin, stateDirectory, updates };
|
|
33972
34101
|
if (start) {
|
|
33973
34102
|
const located = await locateCredential(runtime, "skip");
|
|
33974
34103
|
process.stdout.write(renderStart({
|
|
@@ -33976,6 +34105,7 @@ async function runCli() {
|
|
|
33976
34105
|
skillDirectory: packagedSkillDirectory(),
|
|
33977
34106
|
accountEmail: located === null ? null : located.credential.account.email
|
|
33978
34107
|
}));
|
|
34108
|
+
await announceUpdate(updates);
|
|
33979
34109
|
return;
|
|
33980
34110
|
}
|
|
33981
34111
|
const result = await executeCommand(args, runtime);
|
|
@@ -33991,8 +34121,10 @@ async function runCli() {
|
|
|
33991
34121
|
}) : result;
|
|
33992
34122
|
if (scoped.ok && welcome) {
|
|
33993
34123
|
process.stdout.write(renderWelcome(process.env, process.stdout.columns, process.stdout.getColorDepth(), registration));
|
|
34124
|
+
await announceUpdate(updates);
|
|
33994
34125
|
} else {
|
|
33995
|
-
writeResult(markOrigin(scoped, origin), args.json);
|
|
34126
|
+
writeResult(withUpdate(markOrigin(scoped, origin), args.json ? await updateNotice(updates) : null), args.json);
|
|
34127
|
+
if (!args.json) await announceUpdate(updates);
|
|
33996
34128
|
}
|
|
33997
34129
|
} catch (error51) {
|
|
33998
34130
|
if (origin === void 0) {
|
|
@@ -34015,7 +34147,8 @@ async function runCli() {
|
|
|
34015
34147
|
{ code: knownError.code, command: failure2.command }
|
|
34016
34148
|
), () => void 0);
|
|
34017
34149
|
}
|
|
34018
|
-
writeResult(markOrigin(failure2, origin), json2);
|
|
34150
|
+
writeResult(withUpdate(markOrigin(failure2, origin), json2 ? await updateNotice(updates) : null), json2);
|
|
34151
|
+
if (!json2) await announceUpdate(updates);
|
|
34019
34152
|
process.exitCode = knownError.exitCode;
|
|
34020
34153
|
} finally {
|
|
34021
34154
|
closeIdleConnections();
|
package/package.json
CHANGED
package/skills/obi/SKILL.md
CHANGED
|
@@ -53,6 +53,8 @@ Lead with the outcome, material uncertainty and one useful next action. On an in
|
|
|
53
53
|
|
|
54
54
|
When a failure cannot be recovered in this conversation (the same error after the retry its `nextAction` names, or `obi doctor --json` reporting `server` as `degraded` or `unreachable`), say so and offer once to send it to Obi support. On the customer's yes, run `obi support "<what happened, in their words>" --json`: it adds the CLI version, the system and the last error code itself. When it fails with `SUPPORT_NOT_SENT`, nothing was sent: give them support@hireobi.com and the `support.diagnosticText` to paste. Never put a token or a credential file in the message.
|
|
55
55
|
|
|
56
|
+
When a CLI result carries `update` (or stderr says a newer Obi CLI is available), tell the customer in one sentence, once per conversation, and offer to update it for them. Run `update.command` (`npm i -g @hireobi/cli`) only on their yes, then run `obi` once so the Skill links refresh, and ask them to open a new chat if this one keeps the old Skill. Without a yes, carry on with the current version. `CLI_UPDATE_REQUIRED` means the service refuses this version: say so, offer the same update, and retry the command after it.
|
|
57
|
+
|
|
56
58
|
Separate site observations, customer confirmations, host inferences and independently measured answers. Request only the private extracts needed for the agreed scope; keep secrets and unrelated workspace material local. A local draft or patch is a reviewable suggestion until the customer accepts it. The Skill runs when invoked; while the harness is closed, saved server measurements can continue but host research and reasoning wait for resumption.
|
|
57
59
|
|
|
58
60
|
Stay in customer mode even inside the Obi source repository. A product-use failure is explained and recovered in this conversation. Open a development task, branch or pull request only after an explicit separate software-development request.
|