@cmetech/otto 1.2.4 → 1.2.6
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/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/coworker-memory/index.js +1 -1
- package/dist/resources/extensions/coworker-scratchpad/index.js +1 -1
- package/dist/resources/extensions/coworker-scratchpad/scratchpad-tool.js +5 -5
- package/dist/resources/extensions/coworker-vault/index.js +1 -1
- package/dist/resources/extensions/otto/commands/release-notes/_data.js +25 -2
- package/dist/resources/extensions/otto/index.js +31 -6
- package/dist/resources/extensions/slash-commands/{audit.js → audit-codebase.js} +4 -4
- package/dist/resources/extensions/slash-commands/extension-manifest.json +1 -1
- package/dist/resources/extensions/slash-commands/index.js +2 -2
- package/dist/resources/extensions/workflow/health-widget-core.js +1 -1
- package/package.json +7 -7
- package/packages/contracts/package.json +1 -1
- package/packages/coworker-memory/package.json +1 -1
- package/packages/coworker-scratchpad/dist/kernel-bindings.d.ts +5 -0
- package/packages/coworker-scratchpad/dist/kernel-bindings.js +5 -2
- package/packages/coworker-scratchpad/dist/kernel-entry.js +0 -1
- package/packages/coworker-scratchpad/src/child-process-runtime.test.ts +1 -2
- package/packages/coworker-scratchpad/src/kernel-bindings.test.ts +2 -3
- package/packages/coworker-scratchpad/src/kernel-bindings.ts +5 -2
- package/packages/coworker-scratchpad/src/kernel-entry.ts +0 -1
- package/packages/daemon/package.json +3 -3
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +2 -2
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/coworker-memory/index.ts +1 -1
- package/src/resources/extensions/coworker-scratchpad/index.ts +1 -1
- package/src/resources/extensions/coworker-scratchpad/scratchpad-tool.ts +5 -5
- package/src/resources/extensions/coworker-vault/index.ts +1 -1
- package/src/resources/extensions/otto/commands/release-notes/_data.ts +25 -2
- package/src/resources/extensions/otto/index.ts +29 -6
- package/src/resources/extensions/{_coworker-paths.test.ts → shared/coworker-paths.test.ts} +2 -2
- package/src/resources/extensions/slash-commands/{audit.ts → audit-codebase.ts} +4 -4
- package/src/resources/extensions/slash-commands/extension-manifest.json +1 -1
- package/src/resources/extensions/slash-commands/index.ts +2 -2
- package/src/resources/extensions/workflow/health-widget-core.ts +1 -1
- /package/dist/resources/extensions/{_coworker-paths.js → shared/coworker-paths.js} +0 -0
- /package/src/resources/extensions/{_coworker-paths.ts → shared/coworker-paths.ts} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4baa0132ee073a5c
|
|
@@ -6,7 +6,7 @@ import { runRecall } from './recall-tool.js';
|
|
|
6
6
|
import { runMemoryCommand } from './memory-command.js';
|
|
7
7
|
import { onSessionShutdown } from './session-hooks.js';
|
|
8
8
|
import { createCurrentScratchpadProvider } from '../coworker-scratchpad/sp-command.js';
|
|
9
|
-
import { getCoworkerGlobalDir, getScratchpadsRoot } from '../
|
|
9
|
+
import { getCoworkerGlobalDir, getScratchpadsRoot } from '../shared/coworker-paths.js';
|
|
10
10
|
export { createMemoryBundle };
|
|
11
11
|
// Cross-pillar export. Scratchpad's onDataLoad closure imports this and calls
|
|
12
12
|
// it lazily — returns null before session_start or after session_shutdown,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join, basename } from 'node:path';
|
|
3
3
|
import { ScratchpadManager } from '@otto/coworker-scratchpad';
|
|
4
|
-
import { getScratchpadsRoot } from '../
|
|
4
|
+
import { getScratchpadsRoot } from '../shared/coworker-paths.js';
|
|
5
5
|
import { getMemoryRecorder } from '../coworker-memory/index.js';
|
|
6
6
|
import { getArtifactStore } from '../coworker-artifacts/index.js';
|
|
7
7
|
import { registerSpCommand } from './sp-command.js';
|
|
@@ -44,17 +44,17 @@ export function registerScratchpadTool(pi, deps) {
|
|
|
44
44
|
pi.registerTool({
|
|
45
45
|
name: 'cw_scratchpad',
|
|
46
46
|
label: 'Co-worker scratchpad',
|
|
47
|
-
description: 'USE FOR: loading or analyzing files (CSV,
|
|
47
|
+
description: 'USE FOR: loading or analyzing files (CSV, JSON, Parquet), tabular data manipulation with polars or DuckDB, multi-step data exploration where state should persist across turns, or anything that calls otto.collectors. ' +
|
|
48
48
|
'DO NOT USE FOR: simple arithmetic, lookups answerable from reasoning alone, pure prose, code review, or one-off calculations with no file or data source involved. ' +
|
|
49
49
|
'IF the user explicitly names this tool ("use cw_scratchpad", "run this in the scratchpad", "exec a cell"), use it regardless of the rules above. ' +
|
|
50
50
|
'IF UNSURE whether a request belongs here, ASK the user once: "Do you want me to run this in cw_scratchpad so you can inspect the cells via /sp view, or answer inline?" Wait for their reply before deciding. ' +
|
|
51
51
|
'Runs TypeScript cells in a persistent JS kernel scoped to a named scratchpad. State persists across cells via globalThis.* and across Otto sessions via on-disk kernel.db + namespace.json. ' +
|
|
52
|
-
'Pre-bound libs in every cell: polars, DuckDB,
|
|
52
|
+
'Pre-bound libs in every cell: polars, DuckDB, dateFns, lodash, zod, axios. otto.collectors.{list,open} enumerates and loads data sources. ' +
|
|
53
53
|
'Actions: exec (run a TypeScript cell), view (return the last N cells). ' +
|
|
54
54
|
'Distinct from the analyst extension\'s SQL-only `scratchpad` tool — this one runs arbitrary TypeScript.',
|
|
55
|
-
promptSnippet: 'cw_scratchpad — run TypeScript cells in a persistent JS kernel. USE for files (CSV/
|
|
55
|
+
promptSnippet: 'cw_scratchpad — run TypeScript cells in a persistent JS kernel. USE for files (CSV/JSON/Parquet), polars/DuckDB analysis, otto.collectors, or multi-step data work. NOT for arithmetic or pure prose. If unsure, ASK the user first.',
|
|
56
56
|
promptGuidelines: [
|
|
57
|
-
'Trigger criteria: the request involves loading a file (CSV/
|
|
57
|
+
'Trigger criteria: the request involves loading a file (CSV/JSON/Parquet/etc.), querying tabular data via polars or DuckDB, calling otto.collectors, or building state that must survive across turns.',
|
|
58
58
|
'Skip the tool for: trivial arithmetic, lookups you can answer from reasoning, prose generation, code review, and pure-explanation tasks.',
|
|
59
59
|
'If the user names the tool explicitly (e.g. "use cw_scratchpad", "in the scratchpad", "exec a cell"), always honor that request and skip the unsure-prompt.',
|
|
60
60
|
'IF UNSURE whether a request fits the trigger criteria, ask the user once before deciding: "Do you want me to run this in cw_scratchpad (you can inspect the cells via /sp view) or answer inline?" Wait for the user\'s reply before either calling the tool or answering inline.',
|
|
@@ -62,7 +62,7 @@ export function registerScratchpadTool(pi, deps) {
|
|
|
62
62
|
'The cell body is wrapped in (async () => { ... })(). let/const/var are local to the cell. To persist, assign to globalThis.foo = ...',
|
|
63
63
|
'For DuckDB tables that survive across Otto sessions, use `await otto.duckdb.connect()`. For ephemeral in-memory, use `DuckDB.DuckDBInstance.create(":memory:")`.',
|
|
64
64
|
'For polars→DuckDB: prefer `otto.duckdb.registerDf(name, df)` over manual API discovery. If inference picks the wrong column type, pass `{ schema: { col: \'TYPE\' } }` as the third argument. Falls back to polars\' own SQL (`df.sql(...)`) for one-off aggregations.',
|
|
65
|
-
'Pre-bound libs available in every cell: polars, DuckDB,
|
|
65
|
+
'Pre-bound libs available in every cell: polars, DuckDB, dateFns, lodash, zod, axios. No imports needed.',
|
|
66
66
|
'Use otto.collectors.list() to discover data sources and otto.collectors.open(uri) to load one.',
|
|
67
67
|
'The `name` parameter defaults to the currently attached scratchpad. Omit it unless you want to operate on a different one (this does NOT switch the user attachment).',
|
|
68
68
|
'A returned string that looks markdown-shaped will appear in the response as text/markdown automatically. Return a markdown table or heading to render it.',
|
|
@@ -2,7 +2,7 @@ import { createVaultBundle } from './vault-singleton.js';
|
|
|
2
2
|
import { runConnect } from './connect-command.js';
|
|
3
3
|
import { runDatasourceList, runDatasourceRemove, runDatasourceTest, } from './datasource-command.js';
|
|
4
4
|
import { runAudit } from './audit-command.js';
|
|
5
|
-
import { getCoworkerGlobalDir } from '../
|
|
5
|
+
import { getCoworkerGlobalDir } from '../shared/coworker-paths.js';
|
|
6
6
|
export { createVaultBundle };
|
|
7
7
|
export default function coworkerVaultExtension(api) {
|
|
8
8
|
let bundle = null;
|
|
@@ -9,12 +9,35 @@
|
|
|
9
9
|
// runtime command surfaces a link when a requested version isn't bundled.
|
|
10
10
|
export const RELEASE_NOTES_MANIFEST = {
|
|
11
11
|
truncated: false,
|
|
12
|
-
total:
|
|
12
|
+
total: 15,
|
|
13
13
|
oldestBundled: '1.0.0',
|
|
14
|
-
newestBundled: '1.2.
|
|
14
|
+
newestBundled: '1.2.6',
|
|
15
15
|
historyUrl: 'https://github.com/cmetech/otto-cli/blob/main/CHANGELOG.md',
|
|
16
16
|
};
|
|
17
17
|
export const RELEASE_NOTES = [
|
|
18
|
+
{
|
|
19
|
+
version: '1.2.6',
|
|
20
|
+
date: '2026-06-03',
|
|
21
|
+
headline: 'Second Windows-install hotfix on top of 1.2.5: better-sqlite3 native install no longer requires Visual Studio on Node 24, and the entire exceljs transitive-dep chain (glob@7 / inflight / rimraf@2 / fstream / lodash.isequal) is removed at the source by dropping the library itself.',
|
|
22
|
+
fixed: [
|
|
23
|
+
'**`better-sqlite3` install fails on Windows + Node 24** with `gyp ERR! find VS / Could not find any Visual Studio installation to use`. The pinned `^11.7.0` from 1.2.5 has no prebuilt binary for `NODE_MODULE_VERSION 137` (Node 24), so npm fell through to `node-gyp rebuild`, which requires a C++ toolchain. Bumped to `^12.10.0` in both root `dependencies` and `packages/coworker-memory/package.json`; v12.10.0 ships a `v137-win32-x64` prebuild that installs cleanly.',
|
|
24
|
+
],
|
|
25
|
+
changed: [
|
|
26
|
+
'**`exceljs@4.4.0` dropped from the scratchpad data-lib bindings.** Upstream is dormant (last commit Jan 2024) and the package\'s transitive deps generate eight `npm warn deprecated` messages on every install — `glob@7.2.3` (CVE), `inflight@1.0.6` (memory leak), `rimraf@2.7.1`, `fstream@1.0.12`, `lodash.isequal@4.5.0`, plus old `uuid@8/9` chains. Removed from `package.json`, `packages/coworker-scratchpad/src/kernel-bindings.ts` import + spread, `kernel-entry.ts` binding-names list, and both test files; the LLM-facing tool description in `src/resources/extensions/coworker-scratchpad/scratchpad-tool.ts` no longer claims `ExcelJS` as a pre-bound lib. Cell code that depended on `new ExcelJS.Workbook()` now throws `ReferenceError`. xlsx-capability replacement (likely `xlsx-populate@1.21.0`) is tracked in `docs/superpowers/notes/2026-06-01-coworker-roadmap.md` § Out-of-scope.',
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
version: '1.2.5',
|
|
31
|
+
date: '2026-06-03',
|
|
32
|
+
headline: 'Windows-install hotfix: silences three startup errors that fired the first time `otto` launched after a global npm install, and stops the otto extension from racing the TUI welcome banner with raw stderr writes.',
|
|
33
|
+
fixed: [
|
|
34
|
+
'**`_coworker-paths.js` "Extension does not export a valid factory function".** The shared helper module sat at the top of `src/resources/extensions/` so the pi-coding-agent extension discovery (`discoverExtensionsInDir`) walked it as an extension candidate. Moved to `src/resources/extensions/shared/coworker-paths.{ts,test.ts}` (subdir is skipped by `resolveExtensionEntries` because it has no `index.ts`/`package.json`). The three coworker-{memory,scratchpad,vault}/index.ts importers were updated in lockstep.',
|
|
35
|
+
'**`Cannot find module \'better-sqlite3\'` on global install.** `better-sqlite3` was declared only in `packages/coworker-memory/package.json`. Because npm only resolves the root manifest for end users of a published tarball, the native module was never installed and `@otto/coworker-memory`\'s eager re-export of `local-sqlite-backend.js` blew up at first import. Hoisted `better-sqlite3@^11.7.0` into root `dependencies`.',
|
|
36
|
+
'**`/audit` slash-command conflict between coworker-vault and slash-commands.** Both extensions registered the same id; the vault command (the real Phase 2 reader) supersedes, but the conflict warning fired every launch. Renamed the slash-commands command to `/audit-codebase` (file + manifest + dispatch updated).',
|
|
37
|
+
'**Otto session_start bled raw stderr into the TUI welcome banner.** The gateway + langflow probes wrote ANSI-colored status lines directly to `process.stderr` while the pi TUI was still painting; on Windows the rendered output collided with the yellow `─` rule and overwrote the prompt area. Now routes status through `ctx.ui.setStatus("otto-gateway"|"otto-langflow", ...)` when `ctx.hasUI` is true; falls back to stderr only in headless/RPC modes.',
|
|
38
|
+
'**Duplicate "OTTO" brand on the no-project landing.** The `gsd-health` widget\'s no-project line began with `" OTTO No project loaded — …"`, which appeared alongside the ASCII OTTO logo in the welcome header — looking like the brand was loading twice. Dropped the `OTTO ` prefix; the header already brands the session.',
|
|
39
|
+
],
|
|
40
|
+
},
|
|
18
41
|
{
|
|
19
42
|
version: '1.2.4',
|
|
20
43
|
date: '2026-06-02',
|
|
@@ -108,6 +108,10 @@ export default function Otto(pi) {
|
|
|
108
108
|
const green = ANSI_BRAND_GREEN;
|
|
109
109
|
const dim = ANSI_DIM;
|
|
110
110
|
const reset = ANSI_RESET;
|
|
111
|
+
// When a TUI is rendering, raw stderr writes race with the welcome banner
|
|
112
|
+
// and overwrite the prompt area. Route status through `ctx.ui.setStatus`
|
|
113
|
+
// instead, and only fall back to stderr for headless/RPC modes.
|
|
114
|
+
const hasUI = ctx?.hasUI === true;
|
|
111
115
|
// ── Gateway connection probe (preserved from Phase 1 Task 6) ──
|
|
112
116
|
const gwUrl = process.env.OTTO_GATEWAY_URL?.trim();
|
|
113
117
|
if (gwUrl) {
|
|
@@ -118,15 +122,30 @@ export default function Otto(pi) {
|
|
|
118
122
|
clearTimeout(timer);
|
|
119
123
|
const ok = r.ok;
|
|
120
124
|
const host = new URL(gwUrl).host;
|
|
121
|
-
|
|
125
|
+
if (hasUI) {
|
|
126
|
+
ctx?.ui.setStatus("otto-gateway", ok ? `Gateway → ${host}` : `Gateway → ${host} (down)`);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
process.stderr.write(` ${yellow}gateway:${reset} ${ok ? green : dim}routed → ${host}${reset}\n`);
|
|
130
|
+
}
|
|
122
131
|
}
|
|
123
132
|
catch {
|
|
124
133
|
const host = new URL(gwUrl).host;
|
|
125
|
-
|
|
134
|
+
if (hasUI) {
|
|
135
|
+
ctx?.ui.setStatus("otto-gateway", `Gateway → ${host} (unreachable)`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
process.stderr.write(` ${yellow}gateway:${reset} ${dim}routed → ${host} (unreachable)${reset}\n`);
|
|
139
|
+
}
|
|
126
140
|
}
|
|
127
141
|
}
|
|
128
142
|
else {
|
|
129
|
-
|
|
143
|
+
if (hasUI) {
|
|
144
|
+
ctx?.ui.setStatus("otto-gateway", undefined);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
process.stderr.write(` ${yellow}gateway:${reset} ${dim}direct (no OTTO_GATEWAY_URL set)${reset}\n`);
|
|
148
|
+
}
|
|
130
149
|
}
|
|
131
150
|
// ── LangFlow connection probe ──
|
|
132
151
|
const cfg = effectiveLangFlowConfig();
|
|
@@ -135,18 +154,24 @@ export default function Otto(pi) {
|
|
|
135
154
|
const lfHost = new URL(lfUrl).host;
|
|
136
155
|
if (langflowDisabled) {
|
|
137
156
|
ctx?.ui.setStatus("otto-langflow", undefined);
|
|
138
|
-
|
|
157
|
+
if (!hasUI) {
|
|
158
|
+
process.stderr.write(` ${yellow}langflow:${reset} ${dim}disabled (${lfHost})${reset}\n`);
|
|
159
|
+
}
|
|
139
160
|
}
|
|
140
161
|
else {
|
|
141
162
|
const lfClient = getLangFlowClient();
|
|
142
163
|
const lfVersion = await lfClient.getVersion();
|
|
143
164
|
if (lfVersion) {
|
|
144
165
|
ctx?.ui.setStatus("otto-langflow", `LangFlow ok v${lfVersion.version}`);
|
|
145
|
-
|
|
166
|
+
if (!hasUI) {
|
|
167
|
+
process.stderr.write(` ${yellow}langflow:${reset} ${green}connected${reset} ${dim}(v${lfVersion.version} @ ${lfHost})${reset}\n`);
|
|
168
|
+
}
|
|
146
169
|
}
|
|
147
170
|
else {
|
|
148
171
|
ctx?.ui.setStatus("otto-langflow", "LangFlow offline");
|
|
149
|
-
|
|
172
|
+
if (!hasUI) {
|
|
173
|
+
process.stderr.write(` ${yellow}langflow:${reset} ${dim}offline (${lfHost})${reset}\n`);
|
|
174
|
+
}
|
|
150
175
|
}
|
|
151
176
|
}
|
|
152
177
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdirSync } from "node:fs";
|
|
2
|
-
export default function
|
|
3
|
-
pi.registerCommand("audit", {
|
|
2
|
+
export default function auditCodebaseCommand(pi) {
|
|
3
|
+
pi.registerCommand("audit-codebase", {
|
|
4
4
|
description: "Audit the current codebase against a specific goal and write a structured report to .gsd/audits/",
|
|
5
5
|
async handler(args, ctx) {
|
|
6
6
|
// ── Step 1: Get the audit goal ────────────────────────────────────────
|
|
@@ -8,7 +8,7 @@ export default function auditCommand(pi) {
|
|
|
8
8
|
if (!goal) {
|
|
9
9
|
const input = await ctx.ui.input("What is the audit goal?", "e.g. understand performance bottlenecks before planning a roadmap");
|
|
10
10
|
if (!input?.trim()) {
|
|
11
|
-
ctx.ui.notify("audit: No goal provided — cancelled.", "error");
|
|
11
|
+
ctx.ui.notify("audit-codebase: No goal provided — cancelled.", "error");
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
goal = input.trim();
|
|
@@ -62,7 +62,7 @@ ${goal}
|
|
|
62
62
|
|
|
63
63
|
---
|
|
64
64
|
|
|
65
|
-
*Generated by /audit — read-only recce, no code was modified.*
|
|
65
|
+
*Generated by /audit-codebase — read-only recce, no code was modified.*
|
|
66
66
|
\`\`\`
|
|
67
67
|
|
|
68
68
|
After writing the file, confirm with: "✅ Audit complete — report saved to \`${outputPath}\`"`;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import createSlashCommand from "./create-slash-command.js";
|
|
2
2
|
import createExtension from "./create-extension.js";
|
|
3
|
-
import
|
|
3
|
+
import auditCodebaseCommand from "./audit-codebase.js";
|
|
4
4
|
import clearCommand from "./clear.js";
|
|
5
5
|
export default function slashCommands(pi) {
|
|
6
6
|
createSlashCommand(pi);
|
|
7
7
|
createExtension(pi);
|
|
8
|
-
|
|
8
|
+
auditCodebaseCommand(pi);
|
|
9
9
|
clearCommand(pi);
|
|
10
10
|
}
|
|
@@ -50,7 +50,7 @@ function truncateMessage(msg, maxLen) {
|
|
|
50
50
|
*/
|
|
51
51
|
export function buildHealthLines(data, width) {
|
|
52
52
|
if (data.projectState === "none") {
|
|
53
|
-
return ["
|
|
53
|
+
return [" No project loaded — cd into a project, then run /otto init"];
|
|
54
54
|
}
|
|
55
55
|
if (data.projectState === "initialized") {
|
|
56
56
|
return [` ${BRAND} Project Initialized`];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmetech/otto",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Terminal-based developer chat assistant. Permanent hard fork of gsd-pi with LangFlow flow triggers, a flow builder, and optional gateway routing for compliance environments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -156,12 +156,12 @@
|
|
|
156
156
|
"ajv": "^8.17.1",
|
|
157
157
|
"ajv-formats": "^3.0.1",
|
|
158
158
|
"axios": "^1.16.1",
|
|
159
|
+
"better-sqlite3": "^12.10.0",
|
|
159
160
|
"chalk": "^5.6.2",
|
|
160
161
|
"chokidar": "^5.0.0",
|
|
161
162
|
"date-fns": "^4.4.0",
|
|
162
163
|
"diff": "^8.0.2",
|
|
163
164
|
"echarts": "^5.6.0",
|
|
164
|
-
"exceljs": "^4.4.0",
|
|
165
165
|
"extract-zip": "^2.0.1",
|
|
166
166
|
"file-type": "^21.1.1",
|
|
167
167
|
"glob": "^13.0.1",
|
|
@@ -196,11 +196,11 @@
|
|
|
196
196
|
},
|
|
197
197
|
"optionalDependencies": {
|
|
198
198
|
"@anthropic-ai/claude-agent-sdk": "0.2.83",
|
|
199
|
-
"@cmetech/otto-engine-darwin-arm64": "1.2.
|
|
200
|
-
"@cmetech/otto-engine-darwin-x64": "1.2.
|
|
201
|
-
"@cmetech/otto-engine-linux-arm64-gnu": "1.2.
|
|
202
|
-
"@cmetech/otto-engine-linux-x64-gnu": "1.2.
|
|
203
|
-
"@cmetech/otto-engine-win32-x64-msvc": "1.2.
|
|
199
|
+
"@cmetech/otto-engine-darwin-arm64": "1.2.6",
|
|
200
|
+
"@cmetech/otto-engine-darwin-x64": "1.2.6",
|
|
201
|
+
"@cmetech/otto-engine-linux-arm64-gnu": "1.2.6",
|
|
202
|
+
"@cmetech/otto-engine-linux-x64-gnu": "1.2.6",
|
|
203
|
+
"@cmetech/otto-engine-win32-x64-msvc": "1.2.6",
|
|
204
204
|
"fsevents": "~2.3.3",
|
|
205
205
|
"koffi": "^2.9.0"
|
|
206
206
|
},
|
|
@@ -3,6 +3,11 @@ import { type AuditLog } from '@otto/coworker-utils';
|
|
|
3
3
|
/**
|
|
4
4
|
* The data libraries pre-bound into every scratchpad cell's vm sandbox.
|
|
5
5
|
* DuckDB is bound as an in-memory-capable lib here; on-disk kernel.db wiring is 1d2.
|
|
6
|
+
*
|
|
7
|
+
* Note: ExcelJS was removed in 1.2.6 — its transitive deps (glob@7, inflight,
|
|
8
|
+
* rimraf@2, fstream, lodash.isequal) are deprecated with known vulnerabilities
|
|
9
|
+
* and exceljs is no longer maintained upstream. xlsx-populate is the candidate
|
|
10
|
+
* replacement; tracked in docs/superpowers/notes/2026-06-01-coworker-roadmap.md.
|
|
6
11
|
*/
|
|
7
12
|
export declare function buildDataLibBindings(): Record<string, unknown>;
|
|
8
13
|
/** SQL column type string passed straight into CREATE TABLE (e.g. VARCHAR, BIGINT). */
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import pl from 'nodejs-polars';
|
|
2
|
-
import ExcelJS from 'exceljs';
|
|
3
2
|
import lodash from 'lodash';
|
|
4
3
|
import axios from 'axios';
|
|
5
4
|
import { z } from 'zod';
|
|
@@ -9,12 +8,16 @@ import { SecretScanner } from '@otto/coworker-utils';
|
|
|
9
8
|
/**
|
|
10
9
|
* The data libraries pre-bound into every scratchpad cell's vm sandbox.
|
|
11
10
|
* DuckDB is bound as an in-memory-capable lib here; on-disk kernel.db wiring is 1d2.
|
|
11
|
+
*
|
|
12
|
+
* Note: ExcelJS was removed in 1.2.6 — its transitive deps (glob@7, inflight,
|
|
13
|
+
* rimraf@2, fstream, lodash.isequal) are deprecated with known vulnerabilities
|
|
14
|
+
* and exceljs is no longer maintained upstream. xlsx-populate is the candidate
|
|
15
|
+
* replacement; tracked in docs/superpowers/notes/2026-06-01-coworker-roadmap.md.
|
|
12
16
|
*/
|
|
13
17
|
export function buildDataLibBindings() {
|
|
14
18
|
return {
|
|
15
19
|
polars: pl,
|
|
16
20
|
DuckDB,
|
|
17
|
-
ExcelJS,
|
|
18
21
|
dateFns,
|
|
19
22
|
lodash,
|
|
20
23
|
zod: z,
|
|
@@ -160,14 +160,13 @@ describe('data-lib bindings inside a live kernel', () => {
|
|
|
160
160
|
await rm(ws, { recursive: true, force: true });
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
it('polars / lodash / zod / date-fns /
|
|
163
|
+
it('polars / lodash / zod / date-fns / axios / DuckDB are bound', async () => {
|
|
164
164
|
rt = new ChildProcessRuntime({ workspace: ws, inactivityTimeoutMs: 20_000, cellTimeoutMs: 20_000 });
|
|
165
165
|
await rt.start();
|
|
166
166
|
assert.equal((await rt.runCell('return polars.DataFrame({ a: [1, 2, 3] }).height;')).value, 3);
|
|
167
167
|
assert.equal((await rt.runCell('return lodash.chunk([1, 2, 3, 4], 2).length;')).value, 2);
|
|
168
168
|
assert.equal((await rt.runCell('return zod.string().parse("hi");')).value, 'hi');
|
|
169
169
|
assert.equal((await rt.runCell('return dateFns.format(new Date(1970, 0, 1), "yyyy");')).value, '1970');
|
|
170
|
-
assert.equal((await rt.runCell('const wb = new ExcelJS.Workbook(); wb.addWorksheet("s"); const buf = await wb.xlsx.writeBuffer(); return buf.byteLength > 0;')).value, true);
|
|
171
170
|
assert.equal((await rt.runCell('return typeof axios.get;')).value, 'function');
|
|
172
171
|
assert.equal((await rt.runCell('return typeof DuckDB.DuckDBInstance;')).value, 'function');
|
|
173
172
|
});
|
|
@@ -9,9 +9,9 @@ import { buildDataLibBindings, redactForJournal } from './kernel-bindings.js';
|
|
|
9
9
|
import { ChildProcessRuntime } from './child-process-runtime.js';
|
|
10
10
|
|
|
11
11
|
describe('kernel-bindings', () => {
|
|
12
|
-
it('exposes all
|
|
12
|
+
it('exposes all six pre-bound data libraries', () => {
|
|
13
13
|
const b = buildDataLibBindings();
|
|
14
|
-
for (const key of ['polars', 'DuckDB', '
|
|
14
|
+
for (const key of ['polars', 'DuckDB', 'dateFns', 'lodash', 'zod', 'axios']) {
|
|
15
15
|
assert.ok(key in b, `missing binding: ${key}`);
|
|
16
16
|
assert.notEqual(b[key], undefined, `binding is undefined: ${key}`);
|
|
17
17
|
}
|
|
@@ -24,7 +24,6 @@ describe('kernel-bindings', () => {
|
|
|
24
24
|
assert.equal(typeof b.dateFns.format, 'function');
|
|
25
25
|
assert.equal(typeof b.lodash.chunk, 'function');
|
|
26
26
|
assert.equal(typeof b.axios.get, 'function');
|
|
27
|
-
assert.equal(typeof b.ExcelJS.Workbook, 'function');
|
|
28
27
|
});
|
|
29
28
|
});
|
|
30
29
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import pl from 'nodejs-polars';
|
|
2
|
-
import ExcelJS from 'exceljs';
|
|
3
2
|
import lodash from 'lodash';
|
|
4
3
|
import axios from 'axios';
|
|
5
4
|
import { z } from 'zod';
|
|
@@ -11,12 +10,16 @@ import { SecretScanner, type AuditLog, type AuditRecord } from '@otto/coworker-u
|
|
|
11
10
|
/**
|
|
12
11
|
* The data libraries pre-bound into every scratchpad cell's vm sandbox.
|
|
13
12
|
* DuckDB is bound as an in-memory-capable lib here; on-disk kernel.db wiring is 1d2.
|
|
13
|
+
*
|
|
14
|
+
* Note: ExcelJS was removed in 1.2.6 — its transitive deps (glob@7, inflight,
|
|
15
|
+
* rimraf@2, fstream, lodash.isequal) are deprecated with known vulnerabilities
|
|
16
|
+
* and exceljs is no longer maintained upstream. xlsx-populate is the candidate
|
|
17
|
+
* replacement; tracked in docs/superpowers/notes/2026-06-01-coworker-roadmap.md.
|
|
14
18
|
*/
|
|
15
19
|
export function buildDataLibBindings(): Record<string, unknown> {
|
|
16
20
|
return {
|
|
17
21
|
polars: pl,
|
|
18
22
|
DuckDB,
|
|
19
|
-
ExcelJS,
|
|
20
23
|
dateFns,
|
|
21
24
|
lodash,
|
|
22
25
|
zod: z,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@otto-build/daemon",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "OTTO daemon — background process for project monitoring and Discord integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@anthropic-ai/sdk": "^0.52.0",
|
|
32
|
-
"@otto-build/contracts": "^1.2.
|
|
33
|
-
"@otto-build/rpc-client": "^1.2.
|
|
32
|
+
"@otto-build/contracts": "^1.2.6",
|
|
33
|
+
"@otto-build/rpc-client": "^1.2.6",
|
|
34
34
|
"discord.js": "^14.25.1",
|
|
35
35
|
"yaml": "^2.8.0",
|
|
36
36
|
"zod": "^3.24.0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@otto-build/mcp-server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "MCP server exposing OTTO orchestration tools for compatible clients",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"otto": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"test": "npm run build:test && node --test dist/mcp-server.test.js dist/remote-questions.test.js"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@otto-build/contracts": "^1.2.
|
|
38
|
-
"@otto-build/rpc-client": "^1.2.
|
|
37
|
+
"@otto-build/contracts": "^1.2.6",
|
|
38
|
+
"@otto-build/rpc-client": "^1.2.6",
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
40
40
|
"zod": "^4.0.0"
|
|
41
41
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@otto/pi-coding-agent",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Coding agent CLI (vendored from pi-mono)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"otto": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"copy-assets": "node scripts/copy-assets.cjs"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@otto-build/contracts": "^1.2.
|
|
31
|
+
"@otto-build/contracts": "^1.2.6",
|
|
32
32
|
"@mariozechner/jiti": "^2.6.2",
|
|
33
33
|
"@silvia-odwyer/photon-node": "^0.3.4",
|
|
34
34
|
"chalk": "^5.5.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@otto-build/rpc-client",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Standalone RPC client SDK for OTTO — zero internal dependencies",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"otto": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"test": "node --test dist/rpc-client.test.js"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@otto-build/contracts": "^1.2.
|
|
37
|
+
"@otto-build/contracts": "^1.2.6"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
40
|
"node": ">=22.0.0"
|
package/pkg/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loop24/client",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"piConfig": {
|
|
5
5
|
"_comment": "AUTO-SYNCED from root package.json by scripts/sync-piconfig.mjs (runs on prebuild). Do not edit this block directly — edit root package.json and re-run `npm run build` or `npm run sync-piconfig`.",
|
|
6
6
|
"name": "otto",
|
|
@@ -22,7 +22,7 @@ import { runRecall, type RecallToolArgs } from './recall-tool.js';
|
|
|
22
22
|
import { runMemoryCommand } from './memory-command.js';
|
|
23
23
|
import { onSessionShutdown } from './session-hooks.js';
|
|
24
24
|
import { createCurrentScratchpadProvider } from '../coworker-scratchpad/sp-command.js';
|
|
25
|
-
import { getCoworkerGlobalDir, getScratchpadsRoot } from '../
|
|
25
|
+
import { getCoworkerGlobalDir, getScratchpadsRoot } from '../shared/coworker-paths.js';
|
|
26
26
|
|
|
27
27
|
export { createMemoryBundle };
|
|
28
28
|
export type { MemoryBundle, MemoryBundleOptions };
|
|
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { join, basename } from 'node:path';
|
|
3
3
|
import type { ExtensionAPI, ExtensionContext } from '@otto/pi-coding-agent';
|
|
4
4
|
import { ScratchpadManager, type DataLoadDrawer, type ArtifactCreateDrawer } from '@otto/coworker-scratchpad';
|
|
5
|
-
import { getScratchpadsRoot } from '../
|
|
5
|
+
import { getScratchpadsRoot } from '../shared/coworker-paths.js';
|
|
6
6
|
import { getMemoryRecorder } from '../coworker-memory/index.js';
|
|
7
7
|
import { getArtifactStore } from '../coworker-artifacts/index.js';
|
|
8
8
|
import { registerSpCommand } from './sp-command.js';
|
|
@@ -90,18 +90,18 @@ export function registerScratchpadTool(pi: ExtensionAPI, deps: ScratchpadToolDep
|
|
|
90
90
|
name: 'cw_scratchpad',
|
|
91
91
|
label: 'Co-worker scratchpad',
|
|
92
92
|
description:
|
|
93
|
-
'USE FOR: loading or analyzing files (CSV,
|
|
93
|
+
'USE FOR: loading or analyzing files (CSV, JSON, Parquet), tabular data manipulation with polars or DuckDB, multi-step data exploration where state should persist across turns, or anything that calls otto.collectors. ' +
|
|
94
94
|
'DO NOT USE FOR: simple arithmetic, lookups answerable from reasoning alone, pure prose, code review, or one-off calculations with no file or data source involved. ' +
|
|
95
95
|
'IF the user explicitly names this tool ("use cw_scratchpad", "run this in the scratchpad", "exec a cell"), use it regardless of the rules above. ' +
|
|
96
96
|
'IF UNSURE whether a request belongs here, ASK the user once: "Do you want me to run this in cw_scratchpad so you can inspect the cells via /sp view, or answer inline?" Wait for their reply before deciding. ' +
|
|
97
97
|
'Runs TypeScript cells in a persistent JS kernel scoped to a named scratchpad. State persists across cells via globalThis.* and across Otto sessions via on-disk kernel.db + namespace.json. ' +
|
|
98
|
-
'Pre-bound libs in every cell: polars, DuckDB,
|
|
98
|
+
'Pre-bound libs in every cell: polars, DuckDB, dateFns, lodash, zod, axios. otto.collectors.{list,open} enumerates and loads data sources. ' +
|
|
99
99
|
'Actions: exec (run a TypeScript cell), view (return the last N cells). ' +
|
|
100
100
|
'Distinct from the analyst extension\'s SQL-only `scratchpad` tool — this one runs arbitrary TypeScript.',
|
|
101
101
|
promptSnippet:
|
|
102
|
-
'cw_scratchpad — run TypeScript cells in a persistent JS kernel. USE for files (CSV/
|
|
102
|
+
'cw_scratchpad — run TypeScript cells in a persistent JS kernel. USE for files (CSV/JSON/Parquet), polars/DuckDB analysis, otto.collectors, or multi-step data work. NOT for arithmetic or pure prose. If unsure, ASK the user first.',
|
|
103
103
|
promptGuidelines: [
|
|
104
|
-
'Trigger criteria: the request involves loading a file (CSV/
|
|
104
|
+
'Trigger criteria: the request involves loading a file (CSV/JSON/Parquet/etc.), querying tabular data via polars or DuckDB, calling otto.collectors, or building state that must survive across turns.',
|
|
105
105
|
'Skip the tool for: trivial arithmetic, lookups you can answer from reasoning, prose generation, code review, and pure-explanation tasks.',
|
|
106
106
|
'If the user names the tool explicitly (e.g. "use cw_scratchpad", "in the scratchpad", "exec a cell"), always honor that request and skip the unsure-prompt.',
|
|
107
107
|
'IF UNSURE whether a request fits the trigger criteria, ask the user once before deciding: "Do you want me to run this in cw_scratchpad (you can inspect the cells via /sp view) or answer inline?" Wait for the user\'s reply before either calling the tool or answering inline.',
|
|
@@ -109,7 +109,7 @@ export function registerScratchpadTool(pi: ExtensionAPI, deps: ScratchpadToolDep
|
|
|
109
109
|
'The cell body is wrapped in (async () => { ... })(). let/const/var are local to the cell. To persist, assign to globalThis.foo = ...',
|
|
110
110
|
'For DuckDB tables that survive across Otto sessions, use `await otto.duckdb.connect()`. For ephemeral in-memory, use `DuckDB.DuckDBInstance.create(":memory:")`.',
|
|
111
111
|
'For polars→DuckDB: prefer `otto.duckdb.registerDf(name, df)` over manual API discovery. If inference picks the wrong column type, pass `{ schema: { col: \'TYPE\' } }` as the third argument. Falls back to polars\' own SQL (`df.sql(...)`) for one-off aggregations.',
|
|
112
|
-
'Pre-bound libs available in every cell: polars, DuckDB,
|
|
112
|
+
'Pre-bound libs available in every cell: polars, DuckDB, dateFns, lodash, zod, axios. No imports needed.',
|
|
113
113
|
'Use otto.collectors.list() to discover data sources and otto.collectors.open(uri) to load one.',
|
|
114
114
|
'The `name` parameter defaults to the currently attached scratchpad. Omit it unless you want to operate on a different one (this does NOT switch the user attachment).',
|
|
115
115
|
'A returned string that looks markdown-shaped will appear in the response as text/markdown automatically. Return a markdown table or heading to render it.',
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
runDatasourceTest,
|
|
15
15
|
} from './datasource-command.js';
|
|
16
16
|
import { runAudit, type AuditQuery } from './audit-command.js';
|
|
17
|
-
import { getCoworkerGlobalDir } from '../
|
|
17
|
+
import { getCoworkerGlobalDir } from '../shared/coworker-paths.js';
|
|
18
18
|
|
|
19
19
|
export { createVaultBundle };
|
|
20
20
|
export type { VaultBundle, VaultBundleOptions };
|
|
@@ -33,13 +33,36 @@ export interface ReleaseNotesManifest {
|
|
|
33
33
|
|
|
34
34
|
export const RELEASE_NOTES_MANIFEST: ReleaseNotesManifest = {
|
|
35
35
|
truncated: false,
|
|
36
|
-
total:
|
|
36
|
+
total: 15,
|
|
37
37
|
oldestBundled: '1.0.0',
|
|
38
|
-
newestBundled: '1.2.
|
|
38
|
+
newestBundled: '1.2.6',
|
|
39
39
|
historyUrl: 'https://github.com/cmetech/otto-cli/blob/main/CHANGELOG.md',
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
export const RELEASE_NOTES: ReleaseNote[] = [
|
|
43
|
+
{
|
|
44
|
+
version: '1.2.6',
|
|
45
|
+
date: '2026-06-03',
|
|
46
|
+
headline: 'Second Windows-install hotfix on top of 1.2.5: better-sqlite3 native install no longer requires Visual Studio on Node 24, and the entire exceljs transitive-dep chain (glob@7 / inflight / rimraf@2 / fstream / lodash.isequal) is removed at the source by dropping the library itself.',
|
|
47
|
+
fixed: [
|
|
48
|
+
'**`better-sqlite3` install fails on Windows + Node 24** with `gyp ERR! find VS / Could not find any Visual Studio installation to use`. The pinned `^11.7.0` from 1.2.5 has no prebuilt binary for `NODE_MODULE_VERSION 137` (Node 24), so npm fell through to `node-gyp rebuild`, which requires a C++ toolchain. Bumped to `^12.10.0` in both root `dependencies` and `packages/coworker-memory/package.json`; v12.10.0 ships a `v137-win32-x64` prebuild that installs cleanly.',
|
|
49
|
+
],
|
|
50
|
+
changed: [
|
|
51
|
+
'**`exceljs@4.4.0` dropped from the scratchpad data-lib bindings.** Upstream is dormant (last commit Jan 2024) and the package\'s transitive deps generate eight `npm warn deprecated` messages on every install — `glob@7.2.3` (CVE), `inflight@1.0.6` (memory leak), `rimraf@2.7.1`, `fstream@1.0.12`, `lodash.isequal@4.5.0`, plus old `uuid@8/9` chains. Removed from `package.json`, `packages/coworker-scratchpad/src/kernel-bindings.ts` import + spread, `kernel-entry.ts` binding-names list, and both test files; the LLM-facing tool description in `src/resources/extensions/coworker-scratchpad/scratchpad-tool.ts` no longer claims `ExcelJS` as a pre-bound lib. Cell code that depended on `new ExcelJS.Workbook()` now throws `ReferenceError`. xlsx-capability replacement (likely `xlsx-populate@1.21.0`) is tracked in `docs/superpowers/notes/2026-06-01-coworker-roadmap.md` § Out-of-scope.',
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
version: '1.2.5',
|
|
56
|
+
date: '2026-06-03',
|
|
57
|
+
headline: 'Windows-install hotfix: silences three startup errors that fired the first time `otto` launched after a global npm install, and stops the otto extension from racing the TUI welcome banner with raw stderr writes.',
|
|
58
|
+
fixed: [
|
|
59
|
+
'**`_coworker-paths.js` "Extension does not export a valid factory function".** The shared helper module sat at the top of `src/resources/extensions/` so the pi-coding-agent extension discovery (`discoverExtensionsInDir`) walked it as an extension candidate. Moved to `src/resources/extensions/shared/coworker-paths.{ts,test.ts}` (subdir is skipped by `resolveExtensionEntries` because it has no `index.ts`/`package.json`). The three coworker-{memory,scratchpad,vault}/index.ts importers were updated in lockstep.',
|
|
60
|
+
'**`Cannot find module \'better-sqlite3\'` on global install.** `better-sqlite3` was declared only in `packages/coworker-memory/package.json`. Because npm only resolves the root manifest for end users of a published tarball, the native module was never installed and `@otto/coworker-memory`\'s eager re-export of `local-sqlite-backend.js` blew up at first import. Hoisted `better-sqlite3@^11.7.0` into root `dependencies`.',
|
|
61
|
+
'**`/audit` slash-command conflict between coworker-vault and slash-commands.** Both extensions registered the same id; the vault command (the real Phase 2 reader) supersedes, but the conflict warning fired every launch. Renamed the slash-commands command to `/audit-codebase` (file + manifest + dispatch updated).',
|
|
62
|
+
'**Otto session_start bled raw stderr into the TUI welcome banner.** The gateway + langflow probes wrote ANSI-colored status lines directly to `process.stderr` while the pi TUI was still painting; on Windows the rendered output collided with the yellow `─` rule and overwrote the prompt area. Now routes status through `ctx.ui.setStatus("otto-gateway"|"otto-langflow", ...)` when `ctx.hasUI` is true; falls back to stderr only in headless/RPC modes.',
|
|
63
|
+
'**Duplicate "OTTO" brand on the no-project landing.** The `gsd-health` widget\'s no-project line began with `" OTTO No project loaded — …"`, which appeared alongside the ASCII OTTO logo in the welcome header — looking like the brand was loading twice. Dropped the `OTTO ` prefix; the header already brands the session.',
|
|
64
|
+
],
|
|
65
|
+
},
|
|
43
66
|
{
|
|
44
67
|
version: '1.2.4',
|
|
45
68
|
date: '2026-06-02',
|
|
@@ -112,6 +112,11 @@ export default function Otto(pi: ExtensionAPI): void {
|
|
|
112
112
|
const dim = ANSI_DIM;
|
|
113
113
|
const reset = ANSI_RESET;
|
|
114
114
|
|
|
115
|
+
// When a TUI is rendering, raw stderr writes race with the welcome banner
|
|
116
|
+
// and overwrite the prompt area. Route status through `ctx.ui.setStatus`
|
|
117
|
+
// instead, and only fall back to stderr for headless/RPC modes.
|
|
118
|
+
const hasUI = ctx?.hasUI === true;
|
|
119
|
+
|
|
115
120
|
// ── Gateway connection probe (preserved from Phase 1 Task 6) ──
|
|
116
121
|
const gwUrl = process.env.OTTO_GATEWAY_URL?.trim();
|
|
117
122
|
if (gwUrl) {
|
|
@@ -122,13 +127,25 @@ export default function Otto(pi: ExtensionAPI): void {
|
|
|
122
127
|
clearTimeout(timer);
|
|
123
128
|
const ok = r.ok;
|
|
124
129
|
const host = new URL(gwUrl).host;
|
|
125
|
-
|
|
130
|
+
if (hasUI) {
|
|
131
|
+
ctx?.ui.setStatus("otto-gateway", ok ? `Gateway → ${host}` : `Gateway → ${host} (down)`);
|
|
132
|
+
} else {
|
|
133
|
+
process.stderr.write(` ${yellow}gateway:${reset} ${ok ? green : dim}routed → ${host}${reset}\n`);
|
|
134
|
+
}
|
|
126
135
|
} catch {
|
|
127
136
|
const host = new URL(gwUrl).host;
|
|
128
|
-
|
|
137
|
+
if (hasUI) {
|
|
138
|
+
ctx?.ui.setStatus("otto-gateway", `Gateway → ${host} (unreachable)`);
|
|
139
|
+
} else {
|
|
140
|
+
process.stderr.write(` ${yellow}gateway:${reset} ${dim}routed → ${host} (unreachable)${reset}\n`);
|
|
141
|
+
}
|
|
129
142
|
}
|
|
130
143
|
} else {
|
|
131
|
-
|
|
144
|
+
if (hasUI) {
|
|
145
|
+
ctx?.ui.setStatus("otto-gateway", undefined);
|
|
146
|
+
} else {
|
|
147
|
+
process.stderr.write(` ${yellow}gateway:${reset} ${dim}direct (no OTTO_GATEWAY_URL set)${reset}\n`);
|
|
148
|
+
}
|
|
132
149
|
}
|
|
133
150
|
|
|
134
151
|
// ── LangFlow connection probe ──
|
|
@@ -138,16 +155,22 @@ export default function Otto(pi: ExtensionAPI): void {
|
|
|
138
155
|
const lfHost = new URL(lfUrl).host;
|
|
139
156
|
if (langflowDisabled) {
|
|
140
157
|
ctx?.ui.setStatus("otto-langflow", undefined);
|
|
141
|
-
|
|
158
|
+
if (!hasUI) {
|
|
159
|
+
process.stderr.write(` ${yellow}langflow:${reset} ${dim}disabled (${lfHost})${reset}\n`);
|
|
160
|
+
}
|
|
142
161
|
} else {
|
|
143
162
|
const lfClient = getLangFlowClient();
|
|
144
163
|
const lfVersion = await lfClient.getVersion();
|
|
145
164
|
if (lfVersion) {
|
|
146
165
|
ctx?.ui.setStatus("otto-langflow", `LangFlow ok v${lfVersion.version}`);
|
|
147
|
-
|
|
166
|
+
if (!hasUI) {
|
|
167
|
+
process.stderr.write(` ${yellow}langflow:${reset} ${green}connected${reset} ${dim}(v${lfVersion.version} @ ${lfHost})${reset}\n`);
|
|
168
|
+
}
|
|
148
169
|
} else {
|
|
149
170
|
ctx?.ui.setStatus("otto-langflow", "LangFlow offline");
|
|
150
|
-
|
|
171
|
+
if (!hasUI) {
|
|
172
|
+
process.stderr.write(` ${yellow}langflow:${reset} ${dim}offline (${lfHost})${reset}\n`);
|
|
173
|
+
}
|
|
151
174
|
}
|
|
152
175
|
}
|
|
153
176
|
});
|
|
@@ -2,12 +2,12 @@ import { describe, it, before, after } from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { getCoworkerGlobalDir, getScratchpadsRoot } from './
|
|
5
|
+
import { getCoworkerGlobalDir, getScratchpadsRoot } from './coworker-paths.js';
|
|
6
6
|
|
|
7
7
|
const ORIGINAL_GLOBAL = process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
8
8
|
const ORIGINAL_SCRATCH = process.env.OTTO_SCRATCHPAD_ROOT;
|
|
9
9
|
|
|
10
|
-
describe('
|
|
10
|
+
describe('coworker-paths', () => {
|
|
11
11
|
before(() => {
|
|
12
12
|
delete process.env.OTTO_COWORKER_GLOBAL_DIR;
|
|
13
13
|
delete process.env.OTTO_SCRATCHPAD_ROOT;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionCommandContext } from "@otto/pi-coding-agent";
|
|
2
2
|
import { mkdirSync } from "node:fs";
|
|
3
3
|
|
|
4
|
-
export default function
|
|
5
|
-
pi.registerCommand("audit", {
|
|
4
|
+
export default function auditCodebaseCommand(pi: ExtensionAPI) {
|
|
5
|
+
pi.registerCommand("audit-codebase", {
|
|
6
6
|
description: "Audit the current codebase against a specific goal and write a structured report to .gsd/audits/",
|
|
7
7
|
async handler(args: string, ctx: ExtensionCommandContext) {
|
|
8
8
|
// ── Step 1: Get the audit goal ────────────────────────────────────────
|
|
@@ -15,7 +15,7 @@ export default function auditCommand(pi: ExtensionAPI) {
|
|
|
15
15
|
"e.g. understand performance bottlenecks before planning a roadmap",
|
|
16
16
|
);
|
|
17
17
|
if (!input?.trim()) {
|
|
18
|
-
ctx.ui.notify("audit: No goal provided — cancelled.", "error");
|
|
18
|
+
ctx.ui.notify("audit-codebase: No goal provided — cancelled.", "error");
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
goal = input.trim();
|
|
@@ -77,7 +77,7 @@ ${goal}
|
|
|
77
77
|
|
|
78
78
|
---
|
|
79
79
|
|
|
80
|
-
*Generated by /audit — read-only recce, no code was modified.*
|
|
80
|
+
*Generated by /audit-codebase — read-only recce, no code was modified.*
|
|
81
81
|
\`\`\`
|
|
82
82
|
|
|
83
83
|
After writing the file, confirm with: "✅ Audit complete — report saved to \`${outputPath}\`"`;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@otto/pi-coding-agent";
|
|
2
2
|
import createSlashCommand from "./create-slash-command.js";
|
|
3
3
|
import createExtension from "./create-extension.js";
|
|
4
|
-
import
|
|
4
|
+
import auditCodebaseCommand from "./audit-codebase.js";
|
|
5
5
|
import clearCommand from "./clear.js";
|
|
6
6
|
|
|
7
7
|
export default function slashCommands(pi: ExtensionAPI) {
|
|
8
8
|
createSlashCommand(pi);
|
|
9
9
|
createExtension(pi);
|
|
10
|
-
|
|
10
|
+
auditCodebaseCommand(pi);
|
|
11
11
|
clearCommand(pi);
|
|
12
12
|
}
|
|
@@ -68,7 +68,7 @@ function truncateMessage(msg: string, maxLen: number): string {
|
|
|
68
68
|
*/
|
|
69
69
|
export function buildHealthLines(data: HealthWidgetData, width?: number): string[] {
|
|
70
70
|
if (data.projectState === "none") {
|
|
71
|
-
return ["
|
|
71
|
+
return [" No project loaded — cd into a project, then run /otto init"];
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
if (data.projectState === "initialized") {
|
|
File without changes
|
|
File without changes
|