@ordewell/cli 0.4.14 → 0.4.16
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/main.js +113 -33
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -376,6 +376,14 @@ var import_http2 = __toESM(require("http"));
|
|
|
376
376
|
var import_ws = __toESM(require("ws"));
|
|
377
377
|
var import_core3 = require("@ordewell/core");
|
|
378
378
|
var DEFAULT_HTTP_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
379
|
+
var WorkspaceInitNeededError = class extends Error {
|
|
380
|
+
constructor(message, workspace) {
|
|
381
|
+
super(message);
|
|
382
|
+
this.workspace = workspace;
|
|
383
|
+
this.name = "WorkspaceInitNeededError";
|
|
384
|
+
}
|
|
385
|
+
workspace;
|
|
386
|
+
};
|
|
379
387
|
var ApiClient = class {
|
|
380
388
|
port;
|
|
381
389
|
constructor(port) {
|
|
@@ -446,13 +454,17 @@ var ApiClient = class {
|
|
|
446
454
|
* the verify toggle, so the returned plan may be a question
|
|
447
455
|
* (empty `tasks`, last word in `conversationHistory`) rather than a committed plan.
|
|
448
456
|
*/
|
|
449
|
-
async startConversation(sessionId, goal, runners2, workspace) {
|
|
457
|
+
async startConversation(sessionId, goal, runners2, workspace, allowInit) {
|
|
450
458
|
const res = await this.httpRequest("POST", `/api/plans/${sessionId}/converse/start`, {
|
|
451
459
|
goal,
|
|
452
460
|
runners: runners2 || void 0,
|
|
453
|
-
workspace
|
|
461
|
+
workspace,
|
|
462
|
+
allowInit
|
|
454
463
|
});
|
|
455
464
|
if (res.status !== 200) {
|
|
465
|
+
if (res.data?.code === "workspace_not_a_project") {
|
|
466
|
+
throw new WorkspaceInitNeededError(res.data.error || "Not a project directory", res.data.workspace || workspace || "");
|
|
467
|
+
}
|
|
456
468
|
throw new Error(res.data?.error || "Planning failed");
|
|
457
469
|
}
|
|
458
470
|
return res.data.plan;
|
|
@@ -3023,12 +3035,39 @@ function findCommand(name) {
|
|
|
3023
3035
|
const key = name.replace(/^\//, "").toLowerCase();
|
|
3024
3036
|
return BY_NAME.get(key) ?? skillByName.get(key);
|
|
3025
3037
|
}
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
const
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3038
|
+
var TRAILING_PUNCT = /[,.!?;:]+$/;
|
|
3039
|
+
function slashTokens(text) {
|
|
3040
|
+
const tokens = [];
|
|
3041
|
+
const re = /(^|\s)\/(\S*)/g;
|
|
3042
|
+
let match;
|
|
3043
|
+
while (match = re.exec(text)) {
|
|
3044
|
+
const start = match.index + match[1].length;
|
|
3045
|
+
const raw = match[2];
|
|
3046
|
+
const punct = raw.match(TRAILING_PUNCT)?.[0] ?? "";
|
|
3047
|
+
const nameLen = raw.length - punct.length;
|
|
3048
|
+
tokens.push({
|
|
3049
|
+
start,
|
|
3050
|
+
end: start + 1 + raw.length,
|
|
3051
|
+
name: raw.slice(0, nameLen).toLowerCase(),
|
|
3052
|
+
nameEnd: start + 1 + nameLen
|
|
3053
|
+
});
|
|
3054
|
+
}
|
|
3055
|
+
return tokens;
|
|
3056
|
+
}
|
|
3057
|
+
function activeToken(text, cursor) {
|
|
3058
|
+
return slashTokens(text).find((t) => t.end === cursor) ?? null;
|
|
3059
|
+
}
|
|
3060
|
+
function skillMatchKind(name) {
|
|
3061
|
+
if (name.length === 0) return null;
|
|
3062
|
+
if (skillByName.has(name)) return "exact";
|
|
3063
|
+
for (const key of skillByName.keys()) {
|
|
3064
|
+
if (key.startsWith(name)) return "prefix";
|
|
3065
|
+
}
|
|
3066
|
+
return null;
|
|
3067
|
+
}
|
|
3068
|
+
function tokenCompletions(token) {
|
|
3069
|
+
const pool = token.start === 0 ? [...SLASH_COMMANDS, ...skillCommands] : skillCommands;
|
|
3070
|
+
return pool.filter((c) => c.name.startsWith(token.name));
|
|
3032
3071
|
}
|
|
3033
3072
|
|
|
3034
3073
|
// src/tui/markdown.ts
|
|
@@ -3756,17 +3795,32 @@ function renderStatus(state, cols) {
|
|
|
3756
3795
|
const tail = thinking.length > room ? `\u2026${thinking.slice(-(room - 1)).trimStart()}` : thinking;
|
|
3757
3796
|
return truncate(`${head}${style.grey(` \xB7 ${tail}`)}`, cols);
|
|
3758
3797
|
}
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
const
|
|
3762
|
-
|
|
3763
|
-
|
|
3798
|
+
function highlightSpans(text) {
|
|
3799
|
+
const spans = [];
|
|
3800
|
+
const expandedExact = /* @__PURE__ */ new Set();
|
|
3801
|
+
for (const token of slashTokens(text)) {
|
|
3802
|
+
const kind = skillMatchKind(token.name);
|
|
3803
|
+
if (kind === null) continue;
|
|
3804
|
+
if (kind === "exact") {
|
|
3805
|
+
if (expandedExact.has(token.name)) continue;
|
|
3806
|
+
expandedExact.add(token.name);
|
|
3807
|
+
}
|
|
3808
|
+
spans.push({ start: token.start, end: token.nameEnd });
|
|
3809
|
+
}
|
|
3810
|
+
return spans;
|
|
3764
3811
|
}
|
|
3765
|
-
function
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3812
|
+
function paintSpans(text, base, spans) {
|
|
3813
|
+
if (spans.length === 0) return text;
|
|
3814
|
+
let out = "";
|
|
3815
|
+
let cursor = 0;
|
|
3816
|
+
for (const span of spans) {
|
|
3817
|
+
const localStart = Math.max(cursor, Math.min(text.length, span.start - base));
|
|
3818
|
+
const localEnd = Math.max(0, Math.min(text.length, span.end - base));
|
|
3819
|
+
if (localEnd <= localStart) continue;
|
|
3820
|
+
out += text.slice(cursor, localStart) + style.cyan(text.slice(localStart, localEnd));
|
|
3821
|
+
cursor = localEnd;
|
|
3822
|
+
}
|
|
3823
|
+
return out + text.slice(cursor);
|
|
3770
3824
|
}
|
|
3771
3825
|
function renderInput(state, cols) {
|
|
3772
3826
|
const prompt = state.focus === "plan" ? style.grey("\u276F ") : style.cyan("\u276F ");
|
|
@@ -3774,28 +3828,31 @@ function renderInput(state, cols) {
|
|
|
3774
3828
|
const room = chatEditorRoom(cols, lines !== null);
|
|
3775
3829
|
const { text, cursor } = state.editor;
|
|
3776
3830
|
const continuation = " ".repeat(width(prompt));
|
|
3777
|
-
const
|
|
3831
|
+
const spans = highlightSpans(text);
|
|
3778
3832
|
if (!lines) {
|
|
3779
3833
|
const visible = text.replace(/\n/g, "\xB6").replace(/\t/g, " ");
|
|
3780
|
-
const painted =
|
|
3834
|
+
const painted = paintSpans(visible, 0, spans);
|
|
3781
3835
|
return [prompt + painted + suggest(state, room - width(visible))];
|
|
3782
3836
|
}
|
|
3783
3837
|
const { line: lineIndex, col } = cursorInLines(lines, cursor, text.length);
|
|
3784
|
-
|
|
3785
|
-
return rawLines.map((raw, i) => {
|
|
3838
|
+
return lines.map(({ line: raw, start: base }, i) => {
|
|
3786
3839
|
const prefix = i === 0 ? prompt : continuation;
|
|
3787
3840
|
const line = raw.replace(/\t/g, " ");
|
|
3788
3841
|
const isCursorLine = i === lineIndex;
|
|
3789
|
-
|
|
3790
|
-
const
|
|
3791
|
-
const
|
|
3792
|
-
const
|
|
3793
|
-
|
|
3842
|
+
if (!isCursorLine) return prefix + paintSpans(line, base, spans);
|
|
3843
|
+
const before = paintSpans(line.slice(0, col), base, spans);
|
|
3844
|
+
const caretChar = line.slice(col, col + 1) || " ";
|
|
3845
|
+
const after = paintSpans(line.slice(col + 1), base + col + 1, spans);
|
|
3846
|
+
const used = width(line.slice(0, col)) + width(caretChar);
|
|
3847
|
+
const hint = suggest(state, room - used);
|
|
3848
|
+
return prefix + before + style.inverse(caretChar) + hint + after;
|
|
3794
3849
|
});
|
|
3795
3850
|
}
|
|
3796
3851
|
function suggest(state, room) {
|
|
3797
3852
|
if (state.focus === "plan" || room <= 2) return "";
|
|
3798
|
-
const
|
|
3853
|
+
const token = activeToken(state.editor.text, state.editor.cursor);
|
|
3854
|
+
if (!token) return "";
|
|
3855
|
+
const matches = tokenCompletions(token);
|
|
3799
3856
|
if (matches.length === 0) return "";
|
|
3800
3857
|
const names = matches.map((c) => c.name).join(" ");
|
|
3801
3858
|
return style.grey(` ${truncate(names, Math.max(0, room - 2))}`);
|
|
@@ -4133,6 +4190,19 @@ function reduce(state, action) {
|
|
|
4133
4190
|
}, ["set-planner", "set-task-runner"]));
|
|
4134
4191
|
case "failed":
|
|
4135
4192
|
return step({ ...say(state, "error", action.message), status: "idle", busyLabel: "", thinkingLine: "" });
|
|
4193
|
+
case "workspaceNeedsInit":
|
|
4194
|
+
return step({
|
|
4195
|
+
...state,
|
|
4196
|
+
status: "idle",
|
|
4197
|
+
busyLabel: "",
|
|
4198
|
+
thinkingLine: "",
|
|
4199
|
+
overlay: {
|
|
4200
|
+
kind: "confirm",
|
|
4201
|
+
title: "Initialize new workspace?",
|
|
4202
|
+
message: `"${action.workspace}" isn't a recognized project yet (no .git or manifest found). Start a new ordewell workspace here?`,
|
|
4203
|
+
action: { kind: "init-workspace", goal: action.goal, workspace: action.workspace }
|
|
4204
|
+
}
|
|
4205
|
+
});
|
|
4136
4206
|
case "notice":
|
|
4137
4207
|
return step(say(state, "system", action.message));
|
|
4138
4208
|
case "resize": {
|
|
@@ -4409,10 +4479,13 @@ function handleKey2(state, key) {
|
|
|
4409
4479
|
if (isMouseSelect(key)) return handleMouseSelect(state, key);
|
|
4410
4480
|
if (key.name === "wheelignored") return step(state);
|
|
4411
4481
|
if (key.name === "tab" && state.focus === "chat") {
|
|
4412
|
-
const
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4482
|
+
const token = activeToken(state.editor.text, state.editor.cursor);
|
|
4483
|
+
const matches = token ? tokenCompletions(token) : [];
|
|
4484
|
+
if (token && matches.length > 0) {
|
|
4485
|
+
const { text: full } = state.editor;
|
|
4486
|
+
const completed = `/${matches[0].name} `;
|
|
4487
|
+
const text = full.slice(0, token.start) + completed + full.slice(token.end);
|
|
4488
|
+
return step({ ...state, editor: { ...state.editor, text, cursor: token.start + completed.length } });
|
|
4416
4489
|
}
|
|
4417
4490
|
}
|
|
4418
4491
|
if (key.name === "tab") return step({ ...state, focus: state.focus === "chat" ? "plan" : "chat" });
|
|
@@ -4561,6 +4634,9 @@ function handleConfirmKey(state, overlay, key) {
|
|
|
4561
4634
|
if (!state.sessionId) return step(closed);
|
|
4562
4635
|
return step(closed, [{ type: "removeTask", sessionId: state.sessionId, taskId: overlay.action.taskId }]);
|
|
4563
4636
|
}
|
|
4637
|
+
if (overlay.action.kind === "init-workspace") {
|
|
4638
|
+
return step({ ...closed, status: "planning" }, [{ type: "startConversation", goal: overlay.action.goal, allowInit: true }]);
|
|
4639
|
+
}
|
|
4564
4640
|
return step(closed);
|
|
4565
4641
|
}
|
|
4566
4642
|
function handleHelpKey(state, scroll, key) {
|
|
@@ -5431,9 +5507,13 @@ async function perform(effect, deps) {
|
|
|
5431
5507
|
const sessionId = deps.newSessionId();
|
|
5432
5508
|
dispatch({ type: "sessionStarted", sessionId, goal: effect.goal });
|
|
5433
5509
|
try {
|
|
5434
|
-
await converse(deps, sessionId, () => api.startConversation(sessionId, effect.goal, void 0, workspace));
|
|
5510
|
+
await converse(deps, sessionId, () => api.startConversation(sessionId, effect.goal, void 0, workspace, effect.allowInit));
|
|
5435
5511
|
} catch (err) {
|
|
5436
5512
|
dispatch({ type: "sessionCleared" });
|
|
5513
|
+
if (err instanceof WorkspaceInitNeededError && !effect.allowInit) {
|
|
5514
|
+
dispatch({ type: "workspaceNeedsInit", goal: effect.goal, workspace: err.workspace });
|
|
5515
|
+
return;
|
|
5516
|
+
}
|
|
5437
5517
|
throw err;
|
|
5438
5518
|
}
|
|
5439
5519
|
return;
|
package/package.json
CHANGED