@otto-assistant/otto 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +257 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +264 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +202 -0
- package/dist/config.test.js.map +1 -0
- package/dist/detect.d.ts +9 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/detect.js +40 -0
- package/dist/detect.js.map +1 -0
- package/dist/detect.test.d.ts +2 -0
- package/dist/detect.test.d.ts.map +1 -0
- package/dist/detect.test.js +25 -0
- package/dist/detect.test.js.map +1 -0
- package/dist/health.d.ts +27 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +78 -0
- package/dist/health.js.map +1 -0
- package/dist/health.test.d.ts +2 -0
- package/dist/health.test.d.ts.map +1 -0
- package/dist/health.test.js +33 -0
- package/dist/health.test.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +8 -0
- package/dist/index.test.js.map +1 -0
- package/dist/installer.d.ts +10 -0
- package/dist/installer.d.ts.map +1 -0
- package/dist/installer.js +50 -0
- package/dist/installer.js.map +1 -0
- package/dist/installer.test.d.ts +2 -0
- package/dist/installer.test.d.ts.map +1 -0
- package/dist/installer.test.js +43 -0
- package/dist/installer.test.js.map +1 -0
- package/dist/lifecycle.d.ts +4 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +30 -0
- package/dist/lifecycle.js.map +1 -0
- package/dist/lifecycle.test.d.ts +2 -0
- package/dist/lifecycle.test.d.ts.map +1 -0
- package/dist/lifecycle.test.js +19 -0
- package/dist/lifecycle.test.js.map +1 -0
- package/dist/manifest.d.ts +18 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +30 -0
- package/dist/manifest.js.map +1 -0
- package/dist/sync.d.ts +10 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +39 -0
- package/dist/sync.js.map +1 -0
- package/package.json +41 -0
- package/src/cli.ts +291 -0
- package/src/config.test.ts +237 -0
- package/src/config.ts +362 -0
- package/src/detect.test.ts +28 -0
- package/src/detect.ts +52 -0
- package/src/health.test.ts +39 -0
- package/src/health.ts +114 -0
- package/src/index.test.ts +8 -0
- package/src/index.ts +28 -0
- package/src/installer.test.ts +52 -0
- package/src/installer.ts +62 -0
- package/src/lifecycle.test.ts +22 -0
- package/src/lifecycle.ts +30 -0
- package/src/manifest.ts +42 -0
- package/src/sync.ts +53 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { installMissingPackages, planStableUpgrades } from "./installer.js";
|
|
3
|
+
describe("installer", () => {
|
|
4
|
+
it("installMissingPackages returns empty when all installed", () => {
|
|
5
|
+
const getInstalled = (_name) => "1.0.0";
|
|
6
|
+
const installed = installMissingPackages(getInstalled, () => "ok");
|
|
7
|
+
expect(installed).toEqual([]);
|
|
8
|
+
});
|
|
9
|
+
it("installMissingPackages returns missing package names", () => {
|
|
10
|
+
const getInstalled = (name) => name === "kimaki" ? "1.0.0" : null;
|
|
11
|
+
const installedNames = [];
|
|
12
|
+
const install = (name) => { installedNames.push(name); return name; };
|
|
13
|
+
const result = installMissingPackages(getInstalled, install);
|
|
14
|
+
expect(result).toContain("opencode-ai");
|
|
15
|
+
expect(result).not.toContain("kimaki");
|
|
16
|
+
// opencode-agent-memory is a plugin, not a global npm package
|
|
17
|
+
expect(result).not.toContain("opencode-agent-memory");
|
|
18
|
+
});
|
|
19
|
+
it("planStableUpgrades returns empty when all packages match pinned", () => {
|
|
20
|
+
const pinned = { a: "1.0.0", b: "2.0.0" };
|
|
21
|
+
const getInstalled = (name) => pinned[name] ?? null;
|
|
22
|
+
const plan = planStableUpgrades(["a", "b"], getInstalled, pinned);
|
|
23
|
+
expect(plan).toEqual([]);
|
|
24
|
+
});
|
|
25
|
+
it("planStableUpgrades lists packages whose version differs from pinned", () => {
|
|
26
|
+
const pinned = { "opencode-ai": "1.2.20", kimaki: "0.4.90" };
|
|
27
|
+
const getInstalled = (name) => (name === "kimaki" ? "0.4.90" : "1.0.0");
|
|
28
|
+
const plan = planStableUpgrades(["opencode-ai", "kimaki"], getInstalled, pinned);
|
|
29
|
+
expect(plan).toEqual([
|
|
30
|
+
{ name: "opencode-ai", current: "1.0.0", target: "1.2.20" },
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
it("planStableUpgrades includes not installed packages", () => {
|
|
34
|
+
const pinned = { x: "1.0.0" };
|
|
35
|
+
const getInstalled = () => null;
|
|
36
|
+
const plan = planStableUpgrades(["x"], getInstalled, pinned);
|
|
37
|
+
expect(plan).toEqual([{ name: "x", current: null, target: "1.0.0" }]);
|
|
38
|
+
});
|
|
39
|
+
it("planStableUpgrades throws when pinned entry missing for a package", () => {
|
|
40
|
+
expect(() => planStableUpgrades(["missing"], () => "1.0.0", {})).toThrow("No pinned version for missing in manifest");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=installer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.test.js","sourceRoot":"","sources":["../src/installer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED,wBAAgB,aAAa,IAAI,IAAI,CASpC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
export function hasKimakiBinary() {
|
|
3
|
+
try {
|
|
4
|
+
execSync("which kimaki", { encoding: "utf-8", stdio: "pipe" });
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function isKimakiRunning() {
|
|
12
|
+
try {
|
|
13
|
+
const output = execSync("pgrep -f kimaki", { encoding: "utf-8", stdio: "pipe" });
|
|
14
|
+
return output.trim().length > 0;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function restartKimaki() {
|
|
21
|
+
if (!hasKimakiBinary()) {
|
|
22
|
+
throw new Error("kimaki is not installed. Install it first with: npm install -g kimaki");
|
|
23
|
+
}
|
|
24
|
+
execSync("kimaki restart", {
|
|
25
|
+
encoding: "utf-8",
|
|
26
|
+
stdio: "pipe",
|
|
27
|
+
timeout: 30_000,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE7C,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,QAAQ,CAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;QAC9D,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,iBAAiB,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;QAChF,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAA;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAA;IAC1F,CAAC;IACD,QAAQ,CAAC,gBAAgB,EAAE;QACzB,QAAQ,EAAE,OAAO;QACjB,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,MAAM;KAChB,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.test.d.ts","sourceRoot":"","sources":["../src/lifecycle.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { hasKimakiBinary, isKimakiRunning, restartKimaki } from "./lifecycle.js";
|
|
3
|
+
describe("lifecycle", () => {
|
|
4
|
+
it("hasKimakiBinary returns boolean", () => {
|
|
5
|
+
const result = hasKimakiBinary();
|
|
6
|
+
expect(typeof result).toBe("boolean");
|
|
7
|
+
});
|
|
8
|
+
it("isKimakiRunning returns boolean", () => {
|
|
9
|
+
const result = isKimakiRunning();
|
|
10
|
+
expect(typeof result).toBe("boolean");
|
|
11
|
+
});
|
|
12
|
+
it("restartKimaki is a function", () => {
|
|
13
|
+
expect(typeof restartKimaki).toBe("function");
|
|
14
|
+
});
|
|
15
|
+
it("restartKimaki throws descriptive error when kimaki not found", () => {
|
|
16
|
+
expect(typeof restartKimaki).toBe("function");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
//# sourceMappingURL=lifecycle.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.test.js","sourceRoot":"","sources":["../src/lifecycle.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEhF,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAA;QAChC,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iCAAiC,EAAE,GAAG,EAAE;QACzC,MAAM,MAAM,GAAG,eAAe,EAAE,CAAA;QAChC,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACvC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface Manifest {
|
|
2
|
+
version: string;
|
|
3
|
+
/** Packages installed globally via npm (CLI tools) */
|
|
4
|
+
packages: Record<string, string>;
|
|
5
|
+
/** Pinned versions for `otto upgrade stable` (global npm packages only) */
|
|
6
|
+
pinned: Record<string, string>;
|
|
7
|
+
/** Plugins enabled via opencode.json plugin[] — opencode resolves them itself */
|
|
8
|
+
plugins: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare const MANIFEST: Manifest;
|
|
11
|
+
/** Upstream repositories for sync tracking */
|
|
12
|
+
export declare const UPSTREAM_REPOS: Record<string, {
|
|
13
|
+
repo: string;
|
|
14
|
+
upstream: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare const OPENCODE_CONFIG_DIR: () => string;
|
|
17
|
+
export declare const KIMAKI_DATA_DIR: () => string;
|
|
18
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,iFAAiF;IACjF,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,eAAO,MAAM,QAAQ,EAAE,QAatB,CAAA;AAED,8CAA8C;AAC9C,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAK7E,CAAA;AAED,eAAO,MAAM,mBAAmB,QAAO,MAGtC,CAAA;AAED,eAAO,MAAM,eAAe,QAAO,MAGlC,CAAA"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const MANIFEST = {
|
|
2
|
+
version: "0.1.0",
|
|
3
|
+
packages: {
|
|
4
|
+
"opencode-ai": ">=1.0.115",
|
|
5
|
+
"@otto-assistant/bridge": ">=0.4.90",
|
|
6
|
+
},
|
|
7
|
+
pinned: {
|
|
8
|
+
"opencode-ai": "1.2.20",
|
|
9
|
+
"@otto-assistant/bridge": "0.4.90",
|
|
10
|
+
},
|
|
11
|
+
plugins: [
|
|
12
|
+
"opencode-agent-memory",
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
/** Upstream repositories for sync tracking */
|
|
16
|
+
export const UPSTREAM_REPOS = {
|
|
17
|
+
"@otto-assistant/bridge": {
|
|
18
|
+
repo: "otto-assistant/bridge",
|
|
19
|
+
upstream: "remorses/kimaki",
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export const OPENCODE_CONFIG_DIR = () => {
|
|
23
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/root";
|
|
24
|
+
return `${home}/.config/opencode`;
|
|
25
|
+
};
|
|
26
|
+
export const KIMAKI_DATA_DIR = () => {
|
|
27
|
+
const home = process.env.HOME || process.env.USERPROFILE || "/root";
|
|
28
|
+
return `${home}/.kimaki`;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,QAAQ,GAAa;IAChC,OAAO,EAAE,OAAO;IAChB,QAAQ,EAAE;QACR,aAAa,EAAE,WAAW;QAC1B,wBAAwB,EAAE,UAAU;KACrC;IACD,MAAM,EAAE;QACN,aAAa,EAAE,QAAQ;QACvB,wBAAwB,EAAE,QAAQ;KACnC;IACD,OAAO,EAAE;QACP,uBAAuB;KACxB;CACF,CAAA;AAED,8CAA8C;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAuD;IAChF,wBAAwB,EAAE;QACxB,IAAI,EAAE,uBAAuB;QAC7B,QAAQ,EAAE,iBAAiB;KAC5B;CACF,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAW,EAAE;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAA;IACnE,OAAO,GAAG,IAAI,mBAAmB,CAAA;AACnC,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,GAAW,EAAE;IAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAA;IACnE,OAAO,GAAG,IAAI,UAAU,CAAA;AAC1B,CAAC,CAAA"}
|
package/dist/sync.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { UPSTREAM_REPOS } from "./manifest.js";
|
|
2
|
+
interface SyncTarget {
|
|
3
|
+
repo: string;
|
|
4
|
+
upstream: string;
|
|
5
|
+
branch: string;
|
|
6
|
+
}
|
|
7
|
+
declare function getSyncTargets(): SyncTarget[];
|
|
8
|
+
export declare function syncUpstreams(): Promise<void>;
|
|
9
|
+
export { getSyncTargets, UPSTREAM_REPOS };
|
|
10
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,iBAAS,cAAc,IAAI,UAAU,EAAE,CAMtC;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAiCnD;AAED,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAA"}
|
package/dist/sync.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { UPSTREAM_REPOS } from "./manifest.js";
|
|
3
|
+
function getSyncTargets() {
|
|
4
|
+
return Object.entries(UPSTREAM_REPOS).map(([_pkgName, info]) => ({
|
|
5
|
+
repo: info.repo,
|
|
6
|
+
upstream: info.upstream,
|
|
7
|
+
branch: "main",
|
|
8
|
+
}));
|
|
9
|
+
}
|
|
10
|
+
export async function syncUpstreams() {
|
|
11
|
+
const targets = getSyncTargets();
|
|
12
|
+
if (targets.length === 0) {
|
|
13
|
+
console.log("No upstream repos configured for sync.");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// Check gh CLI is available
|
|
17
|
+
try {
|
|
18
|
+
execSync("gh --version", { stdio: "pipe" });
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
console.error("Error: gh CLI is required for sync. Install: https://cli.github.com/");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
console.log("Triggering upstream sync for all forked repos:\n");
|
|
25
|
+
for (const target of targets) {
|
|
26
|
+
console.log(` ${target.repo} ← ${target.upstream}`);
|
|
27
|
+
try {
|
|
28
|
+
execSync(`gh workflow run sync-upstream.yml --repo ${target.repo} --ref ${target.branch}`, { stdio: "pipe" });
|
|
29
|
+
console.log(` ✓ Sync triggered`);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
33
|
+
console.error(` ✗ Failed: ${msg}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
console.log("\nSync workflows triggered. Check status with: gh run list --repo <repo>");
|
|
37
|
+
}
|
|
38
|
+
export { getSyncTargets, UPSTREAM_REPOS };
|
|
39
|
+
//# sourceMappingURL=sync.js.map
|
package/dist/sync.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAQ9C,SAAS,cAAc;IACrB,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QAC/D,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,MAAM;KACf,CAAC,CAAC,CAAA;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,OAAO,GAAG,cAAc,EAAE,CAAA;IAEhC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAA;QACrD,OAAM;IACR,CAAC;IAED,4BAA4B;IAC5B,IAAI,CAAC;QACH,QAAQ,CAAC,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAA;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,sEAAsE,CAAC,CAAA;QACrF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAA;IAE/D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAA;QACpD,IAAI,CAAC;YACH,QAAQ,CACN,4CAA4C,MAAM,CAAC,IAAI,UAAU,MAAM,CAAC,MAAM,EAAE,EAChF,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB,CAAA;YACD,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;QACrC,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAC5D,OAAO,CAAC,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,0EAA0E,CAAC,CAAA;AACzF,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@otto-assistant/otto",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Otto — terminal UI distribution wrapper for opencode + kimaki + opencode-agent-memory",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"otto": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
"./package.json": "./package.json",
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": ["src", "dist"],
|
|
19
|
+
"keywords": ["otto", "opencode", "kimaki", "ai-agent", "distribution"],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/otto-assistant/otto.git"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "rm -rf dist *.tsbuildinfo && tsc && chmod +x dist/cli.js",
|
|
27
|
+
"dev": "tsc --watch",
|
|
28
|
+
"prepublishOnly": "pnpm build",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@clack/prompts": "^0.9.1",
|
|
34
|
+
"picocolors": "^1.1.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.10.0",
|
|
38
|
+
"typescript": "^5.7.3",
|
|
39
|
+
"vitest": "^3.2.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { MANIFEST } from "./manifest.js"
|
|
4
|
+
import { getInstalledVersion } from "./detect.js"
|
|
5
|
+
import {
|
|
6
|
+
readOpenCodeConfigState,
|
|
7
|
+
writeOpenCodeConfig,
|
|
8
|
+
ensureAgentMemoryConfig,
|
|
9
|
+
ensureSubagentThreadSkill,
|
|
10
|
+
mergePlugins,
|
|
11
|
+
readOttoConfig,
|
|
12
|
+
writeOttoConfig,
|
|
13
|
+
buildSubagentThreadPolicy,
|
|
14
|
+
mergeAgentPrompts,
|
|
15
|
+
type OpenCodeConfig,
|
|
16
|
+
} from "./config.js"
|
|
17
|
+
import { installMissingPackages, upgradePackage, planStableUpgrades } from "./installer.js"
|
|
18
|
+
import { hasKimakiBinary, restartKimaki } from "./lifecycle.js"
|
|
19
|
+
import { checkPackagePresence, checkConfigHealth, checkDirectoryHealth } from "./health.js"
|
|
20
|
+
import { syncUpstreams } from "./sync.js"
|
|
21
|
+
|
|
22
|
+
const args = process.argv.slice(2)
|
|
23
|
+
const command = args[0] ?? ""
|
|
24
|
+
const subCommand = args[1] ?? ""
|
|
25
|
+
|
|
26
|
+
function mergeOttoManagedConfig(config: OpenCodeConfig): OpenCodeConfig {
|
|
27
|
+
let merged = config
|
|
28
|
+
|
|
29
|
+
for (const plugin of MANIFEST.plugins) {
|
|
30
|
+
merged = mergePlugins(merged, plugin)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Read Otto's own config from otto.json (NOT from opencode.json)
|
|
34
|
+
const ottoConfig = readOttoConfig()
|
|
35
|
+
merged = mergeAgentPrompts(merged, buildSubagentThreadPolicy(ottoConfig))
|
|
36
|
+
return merged
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function cmdInstall(): Promise<void> {
|
|
40
|
+
console.log("Otto install — conservative mode\n")
|
|
41
|
+
|
|
42
|
+
// 1. Install missing global npm packages (CLI tools only)
|
|
43
|
+
const installed = installMissingPackages(getInstalledVersion)
|
|
44
|
+
if (installed.length > 0) {
|
|
45
|
+
console.log(`Installed: ${installed.join(", ")}`)
|
|
46
|
+
} else {
|
|
47
|
+
console.log("All packages already installed.")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. Merge plugins + Otto policy into opencode.json
|
|
51
|
+
let configChanged = false
|
|
52
|
+
const { config, status } = readOpenCodeConfigState()
|
|
53
|
+
if (status === "invalid") {
|
|
54
|
+
console.error("Error: opencode.json exists but is not valid JSON. Fix the file, then run otto again.")
|
|
55
|
+
process.exit(1)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 2a. Ensure otto.json exists with defaults
|
|
59
|
+
const ottoConfig = readOttoConfig()
|
|
60
|
+
const ottoConfigChanged = writeOttoConfig(ottoConfig)
|
|
61
|
+
|
|
62
|
+
// 2b. Merge plugins + policy into opencode.json (NO otto key!)
|
|
63
|
+
const merged = mergeOttoManagedConfig(config)
|
|
64
|
+
configChanged = writeOpenCodeConfig(merged) || configChanged
|
|
65
|
+
|
|
66
|
+
if (configChanged) {
|
|
67
|
+
console.log(`Updated opencode.json — added plugins: ${MANIFEST.plugins.join(", ")} + otto subagent policy`)
|
|
68
|
+
}
|
|
69
|
+
if (ottoConfigChanged) {
|
|
70
|
+
console.log("Created otto.json with defaults")
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 3. Ensure agent-memory.json exists
|
|
74
|
+
const created = ensureAgentMemoryConfig()
|
|
75
|
+
if (created) {
|
|
76
|
+
console.log("Created agent-memory.json with defaults")
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 4. Ensure otto-subagent-threads skill exists
|
|
80
|
+
const skillCreated = ensureSubagentThreadSkill()
|
|
81
|
+
if (skillCreated) {
|
|
82
|
+
console.log("Created otto-subagent-threads skill")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 5. Restart kimaki if needed — but NOT if running inside kimaki
|
|
86
|
+
// (kimaki restart kills the current opencode session)
|
|
87
|
+
const runningInsideKimaki = !!process.env.KIMAKI
|
|
88
|
+
if (configChanged || installed.length > 0) {
|
|
89
|
+
if (runningInsideKimaki) {
|
|
90
|
+
console.log("\n⚠ Changes require kimaki restart. Run `kimaki restart` manually when ready.")
|
|
91
|
+
} else if (hasKimakiBinary()) {
|
|
92
|
+
console.log("Restarting kimaki...")
|
|
93
|
+
try {
|
|
94
|
+
restartKimaki()
|
|
95
|
+
console.log("Kimaki restarted.")
|
|
96
|
+
} catch (err: unknown) {
|
|
97
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
98
|
+
console.error(`Warning: could not restart kimaki: ${msg}`)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log("\nDone!")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function cmdUpgrade(mode: "stable" | "latest"): Promise<void> {
|
|
107
|
+
console.log(`Otto upgrade — mode: ${mode}\n`)
|
|
108
|
+
|
|
109
|
+
const packageNames = Object.keys(MANIFEST.packages)
|
|
110
|
+
|
|
111
|
+
let didUpgradePackages = false
|
|
112
|
+
|
|
113
|
+
if (mode === "stable") {
|
|
114
|
+
const upgradePlan = planStableUpgrades(packageNames, getInstalledVersion, MANIFEST.pinned)
|
|
115
|
+
if (upgradePlan.length === 0) {
|
|
116
|
+
console.log("Nothing to upgrade — already at pinned stable versions.")
|
|
117
|
+
} else {
|
|
118
|
+
console.log("Will upgrade:")
|
|
119
|
+
for (const { name, current, target } of upgradePlan) {
|
|
120
|
+
console.log(` ${name}: ${current ?? "not installed"} → ${target}`)
|
|
121
|
+
}
|
|
122
|
+
for (const { name } of upgradePlan) {
|
|
123
|
+
console.log(`Upgrading ${name}...`)
|
|
124
|
+
upgradePackage(name, mode)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
didUpgradePackages = upgradePlan.length > 0
|
|
128
|
+
} else {
|
|
129
|
+
console.log("Will upgrade:")
|
|
130
|
+
for (const name of packageNames) {
|
|
131
|
+
const current = getInstalledVersion(name)
|
|
132
|
+
console.log(` ${name}: ${current ?? "not installed"} → latest`)
|
|
133
|
+
}
|
|
134
|
+
for (const name of packageNames) {
|
|
135
|
+
console.log(`Upgrading ${name}...`)
|
|
136
|
+
upgradePackage(name, mode)
|
|
137
|
+
}
|
|
138
|
+
didUpgradePackages = packageNames.length > 0
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Ensure plugins + Otto policy are in config
|
|
142
|
+
const { config, status } = readOpenCodeConfigState()
|
|
143
|
+
if (status === "invalid") {
|
|
144
|
+
console.error("Error: opencode.json exists but is not valid JSON. Fix the file, then run otto again.")
|
|
145
|
+
process.exit(1)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Ensure otto.json exists
|
|
149
|
+
const ottoConfig = readOttoConfig()
|
|
150
|
+
writeOttoConfig(ottoConfig)
|
|
151
|
+
|
|
152
|
+
const merged = mergeOttoManagedConfig(config)
|
|
153
|
+
const configChanged = writeOpenCodeConfig(merged)
|
|
154
|
+
|
|
155
|
+
// Ensure skill file is up to date
|
|
156
|
+
ensureSubagentThreadSkill()
|
|
157
|
+
|
|
158
|
+
// Restart — but NOT inside kimaki session
|
|
159
|
+
// Only restart if anything actually changed.
|
|
160
|
+
const runningInsideKimaki = !!process.env.KIMAKI
|
|
161
|
+
if (didUpgradePackages || configChanged) {
|
|
162
|
+
if (runningInsideKimaki) {
|
|
163
|
+
console.log("\n⚠ Changes require kimaki restart. Run `kimaki restart` manually when ready.")
|
|
164
|
+
} else if (hasKimakiBinary()) {
|
|
165
|
+
console.log("Restarting kimaki...")
|
|
166
|
+
try {
|
|
167
|
+
restartKimaki()
|
|
168
|
+
console.log("Kimaki restarted.")
|
|
169
|
+
} catch (err: unknown) {
|
|
170
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
171
|
+
console.error(`Warning: could not restart kimaki: ${msg}`)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log("\nDone!")
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function cmdStatus(): Promise<void> {
|
|
180
|
+
console.log("Otto status\n")
|
|
181
|
+
console.log(`Otto version: ${MANIFEST.version}\n`)
|
|
182
|
+
|
|
183
|
+
console.log("Packages:")
|
|
184
|
+
const packages = checkPackagePresence()
|
|
185
|
+
for (const pkg of packages) {
|
|
186
|
+
const icon = pkg.status === "ok" ? "✓" : "✗"
|
|
187
|
+
console.log(` ${icon} ${pkg.name}: ${pkg.installed ?? "not installed"} (requires ${pkg.required})`)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log("\nConfig:")
|
|
191
|
+
const configHealth = checkConfigHealth()
|
|
192
|
+
console.log(` opencode.json: ${configHealth.opencodeJson}`)
|
|
193
|
+
console.log(` agent-memory.json: ${configHealth.agentMemoryJson}`)
|
|
194
|
+
console.log(` otto.json: ${configHealth.ottoJson}`)
|
|
195
|
+
console.log(` plugins: ${configHealth.plugins.length > 0 ? configHealth.plugins.join(", ") : "(none)"}`)
|
|
196
|
+
console.log(` memory plugin: ${configHealth.memoryPluginEnabled ? "enabled" : "NOT enabled"}`)
|
|
197
|
+
console.log(` subagent threads: ${configHealth.subagentThreadsEnabled ? "enabled" : "disabled"}`)
|
|
198
|
+
console.log(` ask before thread delete: ${configHealth.subagentThreadsAskBeforeDelete ? "yes" : "no"}`)
|
|
199
|
+
console.log(` auto delete thread on complete: ${configHealth.subagentThreadsAutoDelete ? "yes" : "no"}`)
|
|
200
|
+
console.log(` kimaki process: ${configHealth.kimakiRunning ? "running" : "not running"}`)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function cmdDoctor(): Promise<void> {
|
|
204
|
+
console.log("Otto doctor\n")
|
|
205
|
+
|
|
206
|
+
let hasErrors = false
|
|
207
|
+
|
|
208
|
+
console.log("Checking packages...")
|
|
209
|
+
const packages = checkPackagePresence()
|
|
210
|
+
for (const pkg of packages) {
|
|
211
|
+
if (pkg.status === "missing") {
|
|
212
|
+
console.log(` ✗ ${pkg.name} — not installed (requires ${pkg.required})`)
|
|
213
|
+
hasErrors = true
|
|
214
|
+
} else {
|
|
215
|
+
console.log(` ✓ ${pkg.name}: ${pkg.installed}`)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
console.log("\nChecking config...")
|
|
220
|
+
const configHealth = checkConfigHealth()
|
|
221
|
+
if (configHealth.opencodeJson === "error") {
|
|
222
|
+
console.log(" ✗ opencode.json is not valid JSON — fix syntax, then run `otto install`")
|
|
223
|
+
hasErrors = true
|
|
224
|
+
}
|
|
225
|
+
if (configHealth.memoryPluginEnabled) {
|
|
226
|
+
console.log(" ✓ opencode-agent-memory plugin enabled")
|
|
227
|
+
} else {
|
|
228
|
+
console.log(" ✗ opencode-agent-memory plugin NOT enabled — run `otto install`")
|
|
229
|
+
hasErrors = true
|
|
230
|
+
}
|
|
231
|
+
if (configHealth.subagentPolicyInjected) {
|
|
232
|
+
console.log(" ✓ otto subagent thread policy injected")
|
|
233
|
+
} else {
|
|
234
|
+
console.log(" ✗ otto subagent thread policy missing — run `otto install`")
|
|
235
|
+
hasErrors = true
|
|
236
|
+
}
|
|
237
|
+
if (configHealth.kimakiRunning) {
|
|
238
|
+
console.log(" ✓ kimaki is running")
|
|
239
|
+
} else {
|
|
240
|
+
console.log(" ⚠ kimaki is not running")
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
console.log("\nChecking directories...")
|
|
244
|
+
const dirs = checkDirectoryHealth()
|
|
245
|
+
for (const d of dirs) {
|
|
246
|
+
const icon = d.status === "ok" ? "✓" : d.status === "warn" ? "⚠" : "✗"
|
|
247
|
+
console.log(` ${icon} ${d.name}: ${d.message}`)
|
|
248
|
+
if (d.status === "error") hasErrors = true
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
console.log(hasErrors ? "\n✗ Issues found. Run `otto install` to fix." : "\n✓ All checks passed!")
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function main(): Promise<void> {
|
|
255
|
+
switch (command) {
|
|
256
|
+
case "install":
|
|
257
|
+
await cmdInstall()
|
|
258
|
+
break
|
|
259
|
+
case "upgrade":
|
|
260
|
+
await cmdUpgrade(subCommand === "latest" ? "latest" : "stable")
|
|
261
|
+
break
|
|
262
|
+
case "status":
|
|
263
|
+
await cmdStatus()
|
|
264
|
+
break
|
|
265
|
+
case "doctor":
|
|
266
|
+
await cmdDoctor()
|
|
267
|
+
break
|
|
268
|
+
case "sync":
|
|
269
|
+
await syncUpstreams()
|
|
270
|
+
break
|
|
271
|
+
default:
|
|
272
|
+
console.log(`Otto — terminal UI distribution for opencode + kimaki + opencode-agent-memory
|
|
273
|
+
|
|
274
|
+
Usage:
|
|
275
|
+
otto install Install missing packages + configure
|
|
276
|
+
otto upgrade Upgrade to stable (manifest-pinned) versions
|
|
277
|
+
otto upgrade stable Upgrade to manifest-pinned versions
|
|
278
|
+
otto upgrade latest Upgrade to npm latest versions
|
|
279
|
+
otto status Show installed versions + config health
|
|
280
|
+
otto doctor Validate all integration points
|
|
281
|
+
otto sync Trigger upstream sync for all forked repos
|
|
282
|
+
`)
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
main().catch((err: unknown) => {
|
|
288
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
289
|
+
console.error(`Error: ${msg}`)
|
|
290
|
+
process.exit(1)
|
|
291
|
+
})
|