mindweave 3.4.1 → 3.4.2
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 +26 -3
- package/dist/cli/App.js +52 -8
- package/dist/cli/App.js.map +1 -1
- package/dist/cli/commands.js +1 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/components/ToolLine.js +5 -3
- package/dist/cli/components/ToolLine.js.map +1 -1
- package/dist/cli/components/WorkGroup.js +82 -64
- package/dist/cli/components/WorkGroup.js.map +1 -1
- package/dist/cli/startupArgs.js +24 -4
- package/dist/cli/startupArgs.js.map +1 -1
- package/dist/cli/transcript.js +14 -0
- package/dist/cli/transcript.js.map +1 -1
- package/dist/cli/workLines.js +40 -0
- package/dist/cli/workLines.js.map +1 -1
- package/dist/core/turnRunner.js +35 -23
- package/dist/core/turnRunner.js.map +1 -1
- package/dist/dynamo/contextWindow.js +28 -36
- package/dist/dynamo/contextWindow.js.map +1 -1
- package/dist/dynamo/engine.js +51 -9
- package/dist/dynamo/engine.js.map +1 -1
- package/dist/dynamo/guard.js +1 -1
- package/dist/dynamo/guard.js.map +1 -1
- package/dist/dynamo/prompt.js +1 -1
- package/dist/headless/args.js +82 -0
- package/dist/headless/args.js.map +1 -0
- package/dist/headless/main.js +87 -0
- package/dist/headless/main.js.map +1 -0
- package/dist/headless/output.js +150 -0
- package/dist/headless/output.js.map +1 -0
- package/dist/headless/run.js +188 -0
- package/dist/headless/run.js.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/main.js +5 -0
- package/dist/main.js.map +1 -1
- package/dist/memory/compaction.js +94 -2
- package/dist/memory/compaction.js.map +1 -1
- package/dist/memory/rewind.js +10 -6
- package/dist/memory/rewind.js.map +1 -1
- package/dist/memory/session.js +2 -2
- package/dist/memory/session.js.map +1 -1
- package/dist/memory/sessionMemory.js +270 -69
- package/dist/memory/sessionMemory.js.map +1 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/project/context.js +34 -6
- package/dist/project/context.js.map +1 -1
- package/dist/tools/coauthor.js +154 -0
- package/dist/tools/coauthor.js.map +1 -0
- package/dist/tools/codeIntel.js +38 -10
- package/dist/tools/codeIntel.js.map +1 -1
- package/dist/tools/docOutline.js +250 -0
- package/dist/tools/docOutline.js.map +1 -0
- package/dist/tools/editRipple.js +29 -2
- package/dist/tools/editRipple.js.map +1 -1
- package/dist/tools/emptyResult.js +24 -0
- package/dist/tools/emptyResult.js.map +1 -0
- package/dist/tools/glob.js +11 -2
- package/dist/tools/glob.js.map +1 -1
- package/dist/tools/grep.js +4 -3
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/missingPath.js +90 -0
- package/dist/tools/missingPath.js.map +1 -0
- package/dist/tools/readFile.js +3 -1
- package/dist/tools/readFile.js.map +1 -1
- package/dist/tools/rebuildDetail.js +9 -5
- package/dist/tools/rebuildDetail.js.map +1 -1
- package/dist/tools/runCommand.js +5 -1
- package/dist/tools/runCommand.js.map +1 -1
- package/dist/tools/webSearch.js +3 -1
- package/dist/tools/webSearch.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,8 +21,9 @@ ever go to the model provider you picked.
|
|
|
21
21
|
Prompts are kept thin on purpose, so the context goes on your code rather than on
|
|
22
22
|
scaffolding.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
desktop app
|
|
24
|
+
**Names.** Mindweave is the program and the name of this repository and of the npm package (`mindweave`, or `mw` for
|
|
25
|
+
short). [mwcode](https://github.com/mindweave-cli/mwcode) is the desktop app: a window around the same engine, for
|
|
26
|
+
Windows, macOS and Linux, with its own repository and its own download files. The two share sessions and settings.
|
|
26
27
|
|
|
27
28
|
## Install
|
|
28
29
|
|
|
@@ -54,7 +55,10 @@ npm link
|
|
|
54
55
|
Qwen, Kimi, GLM, Meta, MiniMax, Tencent, OpenRouter, and local models through Ollama
|
|
55
56
|
with no key at all. [PROVIDERS.md](src/drivers/PROVIDERS.md)
|
|
56
57
|
- **Real tools** — file reads and edits, search, a shell with background jobs,
|
|
57
|
-
sub-agents, web search, and a tool that tests your app by using it
|
|
58
|
+
sub-agents, web search, and a tool that tests your app by using it: it opens a web page or
|
|
59
|
+
dev server in a browser, clicks through it and looks at the result. On Windows it can also
|
|
60
|
+
drive a desktop window and take a screenshot of that one window, never the whole screen. It
|
|
61
|
+
asks first unless you chose auto-accept.
|
|
58
62
|
- **Code intelligence** — tree-sitter and language servers index the repo in the
|
|
59
63
|
background, at no token cost, and every edit comes back with that file's errors.
|
|
60
64
|
- **Goals that run on their own** — `/marathon` takes a goal and keeps going until it is
|
|
@@ -68,6 +72,25 @@ npm link
|
|
|
68
72
|
`/help` lists every command. `shift+tab` switches modes. Type while it works and your
|
|
69
73
|
message queues.
|
|
70
74
|
|
|
75
|
+
## Run it from a script
|
|
76
|
+
|
|
77
|
+
`mindweave -p "task"` does one task with no screen and prints the answer, so it works in a shell script or a CI job.
|
|
78
|
+
Anything piped in is added as context, and `--output-format json` gives one result object with the session, the
|
|
79
|
+
tokens and the cost. The exit code says whether it finished (0), failed (1), had no key (3) or stopped before it was
|
|
80
|
+
done (4). Nobody can answer a question in this mode, so anything that would ask is refused. [docs/HEADLESS.md](docs/HEADLESS.md)
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git diff | mindweave -p "review this change"
|
|
84
|
+
mindweave -p "list the TODOs" --output-format json | jq -r .result
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Commits
|
|
88
|
+
|
|
89
|
+
When the agent makes a commit it can add one line to the message, `Co-authored-by: mwcode <...>`, so the commit
|
|
90
|
+
shows the agent beside you on GitHub. It asks once per project the first time, remembers the answer, and never adds
|
|
91
|
+
the line to a commit you make yourself. `/coauthor` shows or changes the answer, and `/coauthor off all` turns it off
|
|
92
|
+
everywhere.
|
|
93
|
+
|
|
71
94
|
## Your own rules and hooks
|
|
72
95
|
|
|
73
96
|
Both live in your state folder (`~/.mindweave`, or `projects/<name>` inside it for one
|
package/dist/cli/App.js
CHANGED
|
@@ -29,6 +29,7 @@ import { contextPressure, sharpContextWindow } from "../dynamo/contextWindow.js"
|
|
|
29
29
|
import { contextBudget, formatBudget } from "../dynamo/contextBudget.js";
|
|
30
30
|
import { contextView, resetContextOverride, setContextOverride } from "../core/contextSettings.js";
|
|
31
31
|
import { createSession, resumeSession } from "../memory/session.js";
|
|
32
|
+
import { coauthorSetting, setCoauthor } from "../tools/coauthor.js";
|
|
32
33
|
import { saveSession, listSessions } from "../memory/store.js";
|
|
33
34
|
import { stopChassis } from "../alternator/lane.js";
|
|
34
35
|
import { loadSkillBody, substituteSkillArgs } from "../governor/skills.js";
|
|
@@ -78,6 +79,7 @@ import { applyScreenMode } from "./screenShell.js";
|
|
|
78
79
|
import { saveScreenMode } from "./screenStore.js";
|
|
79
80
|
import { FULL_TAIL_BLOCKS, needsMeasure, pruneHeights, seedHeights } from "./blockHeights.js";
|
|
80
81
|
import { expandableAt, hitBlock, hitItem, itemHit, revealNode } from "./expandHits.js";
|
|
82
|
+
import { FILE_HIT_BASE, fileRowAt } from "./workLines.js";
|
|
81
83
|
import { isGrouped, nameAt } from "./editGroup.js";
|
|
82
84
|
import { shellNote } from "./shellNotes.js";
|
|
83
85
|
import { pressBackground } from "./commandBackground.js";
|
|
@@ -85,7 +87,7 @@ import { readFileSync, statSync } from "node:fs";
|
|
|
85
87
|
import { compactSearchDetail, compactUiDetail, rebuildFull, recoverReadText, recoverSavedText, searchDetailFromContent, writtenFromDisk } from "../tools/rebuildDetail.js";
|
|
86
88
|
import { isUserShell, runUserCommand } from "../tools/userCommand.js";
|
|
87
89
|
import { isUserCommandText, parseUserCommand } from "../memory/localCommand.js";
|
|
88
|
-
import { shownText } from "../memory/compaction.js";
|
|
90
|
+
import { shownText, splitExpiredImages, userAsSent } from "../memory/compaction.js";
|
|
89
91
|
import { BASE_COMMANDS } from "./commands.js";
|
|
90
92
|
import { attachmentBudget, dismissMarathon } from "../core/turnRunner.js";
|
|
91
93
|
import { describeMarathonEvent, resumeMarathon, startMarathon } from "../dynamo/marathon.js";
|
|
@@ -728,8 +730,9 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
728
730
|
// but they are ours, not the person's. Replaying one draws it as a `>` prompt
|
|
729
731
|
// the user never typed — seen live as "> That was 3 sentences between tool
|
|
730
732
|
// calls…" sitting in their own chat history.
|
|
733
|
+
// The note compaction adds for the model when it drops an image is not something the person typed.
|
|
731
734
|
if (!e.synthetic)
|
|
732
|
-
dispatch({ type: "user", text: collapsePastes(stripAttachments(e.content)) });
|
|
735
|
+
dispatch({ type: "user", text: collapsePastes(splitExpiredImages(stripAttachments(userAsSent(e).content)).text) });
|
|
733
736
|
// A command the person ran with `!` comes back as the row it was: what they ran, how it ended,
|
|
734
737
|
// and the output behind the click. The screen's record is kept whole even when the model's copy
|
|
735
738
|
// of the output was cleared to save context.
|
|
@@ -2069,7 +2072,19 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
2069
2072
|
if (block && block.kind === "work" && !block.open && block.items.length === 1)
|
|
2070
2073
|
fetchRead(block.items[0]);
|
|
2071
2074
|
}
|
|
2072
|
-
if (hitItem(hit) >=
|
|
2075
|
+
if (hitItem(hit) >= FILE_HIT_BASE) {
|
|
2076
|
+
// One file of a read that took several: its text is fetched with the first click on any of them.
|
|
2077
|
+
const st = stateRef.current;
|
|
2078
|
+
const block = [...st.committed, ...st.tail].find((b) => b.id === hitBlock(hit));
|
|
2079
|
+
const at = block && block.kind === "work" ? fileRowAt(block.items, hitItem(hit) - FILE_HIT_BASE) : undefined;
|
|
2080
|
+
if (block && block.kind === "work" && at) {
|
|
2081
|
+
const item = block.items[at.item];
|
|
2082
|
+
if (!item.openFiles?.includes(at.file))
|
|
2083
|
+
fetchRead(item);
|
|
2084
|
+
dispatch({ type: "toggleFile", id: block.id, item: at.item, file: at.file });
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
else if (hitItem(hit) >= 0) {
|
|
2073
2088
|
// Before the toggle: the state is updated the moment it is dispatched, and a read already open is not fetched for.
|
|
2074
2089
|
fetchReadText(hitBlock(hit), hitItem(hit));
|
|
2075
2090
|
dispatch({ type: "toggleItem", id: hitBlock(hit), index: hitItem(hit) });
|
|
@@ -3127,12 +3142,14 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3127
3142
|
// Hand a free-text directive to the model with an instruction wrapper, as its own
|
|
3128
3143
|
// turn — used by the manual /rules and /skills commands (the model does the
|
|
3129
3144
|
// "rewrite it well and save it" work via remember_rule / the skill tool).
|
|
3130
|
-
|
|
3145
|
+
// `typed` is the command line the person wrote. `instruction` is the engine's wording for the model, which
|
|
3146
|
+
// must never be drawn as if the person had said it, so the message keeps what was typed as its original.
|
|
3147
|
+
async function runDirective(instruction, activity, typed) {
|
|
3131
3148
|
const s = session.current;
|
|
3132
3149
|
if (!s)
|
|
3133
3150
|
return;
|
|
3134
3151
|
note(activity);
|
|
3135
|
-
s.transcript.push({ role: "user", content: instruction });
|
|
3152
|
+
s.transcript.push({ role: "user", content: instruction, ...(typed ? { original: { content: typed } } : {}) });
|
|
3136
3153
|
await streamRespond(s);
|
|
3137
3154
|
}
|
|
3138
3155
|
async function handleCommand(raw) {
|
|
@@ -3308,7 +3325,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3308
3325
|
`pull it in with an @-import line rather than making the root file long; if a ` +
|
|
3309
3326
|
`convention is true of one folder only, write a MINDWEAVE.md inside that folder ` +
|
|
3310
3327
|
`instead, which costs nothing when work is happening elsewhere. ` +
|
|
3311
|
-
`changed. Finish by confirming in one line what you wrote and why.`, "writing MINDWEAVE.md…");
|
|
3328
|
+
`changed. Finish by confirming in one line what you wrote and why.`, "writing MINDWEAVE.md…", raw);
|
|
3312
3329
|
return;
|
|
3313
3330
|
}
|
|
3314
3331
|
if (name === "/compact") {
|
|
@@ -3724,7 +3741,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3724
3741
|
}
|
|
3725
3742
|
await runDirective(`The user wants this saved as a standing rule for this project. Rewrite it as a clear, ` +
|
|
3726
3743
|
`imperative, self-contained rule and save it with remember_rule (add globs only if it ` +
|
|
3727
|
-
`clearly applies to specific files). Then confirm in one line. Directive: "${arg}"`, "saving a rule…");
|
|
3744
|
+
`clearly applies to specific files). Then confirm in one line. Directive: "${arg}"`, "saving a rule…", raw);
|
|
3728
3745
|
return;
|
|
3729
3746
|
}
|
|
3730
3747
|
// /forbidden — list forbidden paths, or (with a path) forbid one outright.
|
|
@@ -3756,6 +3773,33 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3756
3773
|
}
|
|
3757
3774
|
return;
|
|
3758
3775
|
}
|
|
3776
|
+
// /coauthor — whether commits the agent makes carry the "Co-authored-by: mwcode" line.
|
|
3777
|
+
if (name === "/coauthor") {
|
|
3778
|
+
const root = s.governance.forbidden.root;
|
|
3779
|
+
const words = arg.trim().toLowerCase().split(/\s+/).filter(Boolean);
|
|
3780
|
+
const everywhere = words.includes("all");
|
|
3781
|
+
const verb = words.find((w) => w !== "all");
|
|
3782
|
+
if (verb === "on" || verb === "off") {
|
|
3783
|
+
await setCoauthor(root, everywhere ? "global" : "project", verb === "on");
|
|
3784
|
+
const where = everywhere ? "in every project that has no answer of its own" : "in this project";
|
|
3785
|
+
note(verb === "on" ? `commits I make will carry the co-author line ${where}.` : `commits I make will not carry the co-author line ${where}.`);
|
|
3786
|
+
return;
|
|
3787
|
+
}
|
|
3788
|
+
if (verb === "reset") {
|
|
3789
|
+
await setCoauthor(root, "project", null);
|
|
3790
|
+
note("forgot this project's answer. I will ask at the next commit.");
|
|
3791
|
+
return;
|
|
3792
|
+
}
|
|
3793
|
+
if (verb) {
|
|
3794
|
+
say("Use /coauthor to see it, /coauthor on or /coauthor off to set it here, and add 'all' for every project. /coauthor reset forgets this project's answer.");
|
|
3795
|
+
return;
|
|
3796
|
+
}
|
|
3797
|
+
const current = await coauthorSetting(root);
|
|
3798
|
+
say(current
|
|
3799
|
+
? `The co-author line is ${current.credit ? "on" : "off"}, set for ${current.from === "project" ? "this project" : "all projects"}.`
|
|
3800
|
+
: "Not set yet. I will ask the first time I commit here.");
|
|
3801
|
+
return;
|
|
3802
|
+
}
|
|
3759
3803
|
// /forbid-command — list forbidden commands, or (with an argument) forbid one.
|
|
3760
3804
|
if (name === "/forbid-command") {
|
|
3761
3805
|
if (!arg) {
|
|
@@ -3790,7 +3834,7 @@ export function App({ resumeSessionId, initialScreen }) {
|
|
|
3790
3834
|
if (arg) {
|
|
3791
3835
|
await runDirective(`The user wants a new reusable skill for this project: "${arg}". Design it and save it ` +
|
|
3792
3836
|
`with the skill tool — a short invocation name, a one-line description, and the procedure ` +
|
|
3793
|
-
`as a clear markdown checklist (use $ARGUMENTS/$1 if it should take input). Then confirm.`, "creating a skill…");
|
|
3837
|
+
`as a clear markdown checklist (use $ARGUMENTS/$1 if it should take input). Then confirm.`, "creating a skill…", raw);
|
|
3794
3838
|
return;
|
|
3795
3839
|
}
|
|
3796
3840
|
const skills = s.governance.skills;
|