@lawaty/lacode 1.0.3 → 1.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/CHANGELOG.md +55 -0
- package/README.md +25 -6
- package/dist/agents.js +368 -244
- package/dist/commands.js +29 -13
- package/dist/index.js +1170 -403
- package/dist/lacode.js +23 -2
- package/docs/CONTEXT.md +80 -10
- package/docs/INSTALL.md +2 -2
- package/package.json +1 -1
package/dist/lacode.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// bin/lacode
|
|
4
4
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
5
|
-
import { existsSync as existsSync4, readFileSync as
|
|
5
|
+
import { existsSync as existsSync4, readFileSync as readFileSync8 } from "node:fs";
|
|
6
6
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
7
7
|
|
|
8
8
|
// src/tunnel/index.ts
|
|
@@ -826,6 +826,13 @@ var formatVersion = (report) => {
|
|
|
826
826
|
return lines.map(([key, value]) => `${key.padEnd(10)} ${String(value)}`).join("\n");
|
|
827
827
|
};
|
|
828
828
|
|
|
829
|
+
// src/lib/feedback.ts
|
|
830
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync7, renameSync as renameSync4, unlinkSync as unlinkSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
831
|
+
import { homedir as homedir6 } from "node:os";
|
|
832
|
+
import { dirname as dirname3, join as join7 } from "node:path";
|
|
833
|
+
var ISSUES_URL = "https://github.com/lawaty/lacode/issues";
|
|
834
|
+
var DISCUSSIONS_URL = "https://github.com/lawaty/lacode/discussions";
|
|
835
|
+
|
|
829
836
|
// bin/lacode
|
|
830
837
|
if (!process.features?.typescript) {
|
|
831
838
|
if (process.env.LACODE_STRIP_TYPECHECKED === "1") {
|
|
@@ -858,6 +865,10 @@ var USAGE = `lacode \u2014 portable notifications and remote exec for LaCode
|
|
|
858
865
|
lacode doctor
|
|
859
866
|
Print what the environment resolves to. Diagnostics only.
|
|
860
867
|
|
|
868
|
+
lacode feedback
|
|
869
|
+
Print where to report a bug, ask a question, or say how you use LaCode.
|
|
870
|
+
Nothing is opened and nothing is sent.
|
|
871
|
+
|
|
861
872
|
lacode version
|
|
862
873
|
Report WHICH LaCode is speaking and WHICH one opencode actually loads.
|
|
863
874
|
Version, source (checkout or install), the commit it was built from,
|
|
@@ -937,6 +948,16 @@ switch (command) {
|
|
|
937
948
|
process.exit(0);
|
|
938
949
|
break;
|
|
939
950
|
}
|
|
951
|
+
case "feedback": {
|
|
952
|
+
console.log("Bugs and feature requests:");
|
|
953
|
+
console.log(` ${ISSUES_URL}`);
|
|
954
|
+
console.log('Questions and "how are you using it":');
|
|
955
|
+
console.log(` ${DISCUSSIONS_URL}`);
|
|
956
|
+
console.log("");
|
|
957
|
+
console.log("No browser is opened and nothing is sent.");
|
|
958
|
+
process.exit(0);
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
940
961
|
case "version": {
|
|
941
962
|
const report = readVersionReport();
|
|
942
963
|
console.log(formatVersion(report));
|
|
@@ -1007,7 +1028,7 @@ ${report.note}`);
|
|
|
1007
1028
|
}
|
|
1008
1029
|
console.log(`Generated ${key}`);
|
|
1009
1030
|
}
|
|
1010
|
-
const pubkey = existsSync4(pub) ?
|
|
1031
|
+
const pubkey = existsSync4(pub) ? readFileSync8(pub, "utf8").trim() : "(public key missing)";
|
|
1011
1032
|
const self = process.argv[1] ?? "lacode";
|
|
1012
1033
|
console.log("\nOn the REMOTE host (where opencode runs), export:");
|
|
1013
1034
|
console.log(` OC_LOCAL_USER=${user || "<desktop-user>"}`);
|
package/docs/CONTEXT.md
CHANGED
|
@@ -57,7 +57,7 @@ a work list rather than a map:
|
|
|
57
57
|
| `contexts.md` | The major subsystems. For each: what it is for, where to start reading it, what it depends on, what must not change while you are in there. Read this for a subsystem question. |
|
|
58
58
|
| `conventions.md` | Repository-specific rules: naming, module layout, error handling, testing, persistence. Read this before writing code, not after. |
|
|
59
59
|
| `workflows.md` | The few runtime flows worth tracing end to end — how a request becomes a mutation, how a plugin hook reaches the thing it hooks. Read this when you need to know what actually happens at runtime. |
|
|
60
|
-
| `decisions.md` | Architectural
|
|
60
|
+
| `decisions.md` | Architectural constraints that bind future work and that the repository's own decision log does not already record. Each entry states the constraint and points at the page or path that owns it. Capped at 120 non-blank lines; decision records and deliberation are not copied here. |
|
|
61
61
|
| `active.md` | The active backlog: work in flight, grouped by the ISO week each task was opened in. A task that is not marked `[x]` is open and is never removed; a task that is finished and dated is kept for a week after it was finished, then dropped. The plugin enforces that window — see [How it stays current](#how-it-stays-current). Not a navigation file: there is no question it is the answer to. |
|
|
62
62
|
|
|
63
63
|
The project's root `AGENTS.md` is the standing-instructions home: the client's
|
|
@@ -67,8 +67,51 @@ no prescribed structure. It has exactly one writer — the hidden `instructions`
|
|
|
67
67
|
agent, reachable only from the `/agent-instructions` command.
|
|
68
68
|
|
|
69
69
|
Files the user or the plan explicitly names always win over the map. The map is
|
|
70
|
-
a shortcut to the right starting point, not an authority
|
|
71
|
-
|
|
70
|
+
a shortcut to the right starting point, not an authority. When it disagrees with
|
|
71
|
+
**code**, the code is right and the map is wrong. When it disagrees with a
|
|
72
|
+
**project page**, neither side is automatically right: the map may be current
|
|
73
|
+
and the page stale (that has happened). The disagreement is reported and marked
|
|
74
|
+
`[disputed]`; the page's owner resolves it. The cartographer never silently
|
|
75
|
+
overwrites either side.
|
|
76
|
+
|
|
77
|
+
## The entry contract
|
|
78
|
+
|
|
79
|
+
Every navigation entry has one shape, in every file:
|
|
80
|
+
|
|
81
|
+
```markdown
|
|
82
|
+
- [binding] The operative rule, in at most three non-blank lines. — See `docs/DESIGN.md` §The two halves (verified 2026-10-26)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **A provenance marker.** `[binding]` is a constraint established against the
|
|
86
|
+
source and exempt from age expiry. `[provisional]` is an inference or an
|
|
87
|
+
unverified claim and expires 30 days after `verified`. `[disputed]` records a
|
|
88
|
+
map/page disagreement and is kept, reported, and exempt from age until the
|
|
89
|
+
owner resolves it. There is no second confidence dialect.
|
|
90
|
+
- **The rule, not the deliberation.** No Context / Decision / Consequences /
|
|
91
|
+
Alternatives blocks, status lines, or decision records. A reader needs the
|
|
92
|
+
constraint, not the meeting that produced it.
|
|
93
|
+
- **A pointer.** A fact whose home is a page cites the page **and** its section
|
|
94
|
+
(`docs/DESIGN.md §Retention`). A fact whose home is code cites the path, with
|
|
95
|
+
an optional line or symbol. No pointer means the content has no source in the
|
|
96
|
+
tree — it is reported as unarchived, never emitted as though it were derived.
|
|
97
|
+
- **A verification date.** `(verified YYYY-MM-DD)` drives both the lifecycle and
|
|
98
|
+
the drift check.
|
|
99
|
+
|
|
100
|
+
Every file has a hard cap, counted in **non-blank lines**: architecture 150,
|
|
101
|
+
contexts 200, conventions 150, workflows 150, decisions 120, active 60. Lines
|
|
102
|
+
are the unit because they are what a reader pays for, they bound an entry that
|
|
103
|
+
wraps to three lines, and the validator can count them exactly; an entry count
|
|
104
|
+
does not bound cost, and a token count needs a tokenizer and is model-specific.
|
|
105
|
+
The cap is enforced by a post-generation validator, not only by the prompt.
|
|
106
|
+
|
|
107
|
+
The lifecycle generalises the window `active.md` already had. A finished backlog
|
|
108
|
+
task is dropped after 7 days. A `[provisional]` entry in any navigation file is
|
|
109
|
+
dropped 30 days after its `verified` date unless it is re-verified. `[binding]`
|
|
110
|
+
and `[disputed]` never expire by age. An entry whose cited source no longer
|
|
111
|
+
exists is **never** auto-dropped: it may be the only copy, so it is reported as
|
|
112
|
+
unarchived and kept. Only provisional entries whose source still resolves are
|
|
113
|
+
eligible for the sweep. The arithmetic lives in `src/lib/map-schema.ts`; the
|
|
114
|
+
cartographer writes the shape, code decides what has aged out.
|
|
72
115
|
|
|
73
116
|
The map is **local and untracked**. It describes *your* checkout on *this*
|
|
74
117
|
machine, so committing it would churn on every session that edits a file, and
|
|
@@ -110,6 +153,18 @@ a new or moved entry point, a changed workflow, a new convention, an
|
|
|
110
153
|
architectural decision, an undocumented invariant — and to report
|
|
111
154
|
"no update needed" otherwise.
|
|
112
155
|
|
|
156
|
+
After the turn ends, the plugin applies the per-file lifecycle and then audits
|
|
157
|
+
the map. The audit is **report-only**: it never rewrites a claim and never
|
|
158
|
+
deletes an entry whose source is gone. It checks the schema, each file's line
|
|
159
|
+
budget, dangling paths and `§sections`, a numeric claim absent from its cited
|
|
160
|
+
source, a possible negation conflict, a source modified after its `verified`
|
|
161
|
+
date, tables and fenced blocks (restatements), duplicate rules across files,
|
|
162
|
+
archive-of-record language, and whether each file still answers its charter. A
|
|
163
|
+
failure is a loud `error` in the log; it is what turns the prompt's rules into a
|
|
164
|
+
gate. The deterministic half is `src/lib/map-schema.ts`; the semantic drift pass
|
|
165
|
+
is the cartographer's, and it reports a page disagreement instead of picking a
|
|
166
|
+
side.
|
|
167
|
+
|
|
113
168
|
If the map is missing entirely, nothing breaks: agents run the check, find no
|
|
114
169
|
`.opencode/context/`, and explore from scratch. What tells them to run the check
|
|
115
170
|
is an entry appended to your config's `instructions` array: your own entries are
|
|
@@ -138,17 +193,25 @@ your source.
|
|
|
138
193
|
If you have defined a command of the same name, yours wins and is left exactly
|
|
139
194
|
as you wrote it — the same merge-without-clobber rule the agents follow.
|
|
140
195
|
|
|
141
|
-
`active.md` is the
|
|
142
|
-
|
|
196
|
+
`active.md` is the file the plugin maintains as a backlog rather than as a
|
|
197
|
+
navigation map. The cartographer records what happened — work in flight, and a
|
|
143
198
|
task marked finished with the date it finished — and the plugin decides what is
|
|
144
199
|
still worth keeping: the file is regrouped by week and a task that was finished
|
|
145
200
|
more than a week ago is dropped. The arithmetic lives in
|
|
146
201
|
`src/lib/active-backlog.ts`, not in the cartographer's prompt, because a rule a
|
|
147
202
|
model applies is a rule the model applies when it feels like it, and the failure
|
|
148
|
-
is silent — the backlog just grows. The plugin normalizes the file on every
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
203
|
+
is silent — the backlog just grows. The plugin normalizes the file on every edit
|
|
204
|
+
of it and once when the session starts, so a hand edit is grouped and pruned
|
|
205
|
+
whether the cartographer ran or not, and the file is rewritten only when its
|
|
206
|
+
canonical form differs from what is on disk, so the write cannot loop.
|
|
207
|
+
|
|
208
|
+
The same code owns the navigation files' lifecycle, but it may only **drop** an
|
|
209
|
+
entry that is `[provisional]`, past its 30-day window, and whose cited source
|
|
210
|
+
still exists. It never drops a binding constraint or disputed entry, and it
|
|
211
|
+
never drops an entry with no source — that is the one case where the map may be
|
|
212
|
+
the only copy. The plugin is not a second writer of the map in the permission
|
|
213
|
+
sense: `context` is still the only **agent** that writes it. The lifecycle is
|
|
214
|
+
housekeeping by code, the same category as the backlog window.
|
|
152
215
|
|
|
153
216
|
## One writer
|
|
154
217
|
|
|
@@ -226,14 +289,21 @@ to check is that permission rule, not the map's content.
|
|
|
226
289
|
| `auto update finished` | The run completed and changed at least one map file. |
|
|
227
290
|
| `auto update finished without changing the map` | Nothing landed. Either nothing needed saying, or the cartographer could not write. |
|
|
228
291
|
| `auto update failed: …` | The run errored. The paths it was given are queued again for the next window, so the work is not lost. |
|
|
292
|
+
| `map lifecycle: dropped N expired provisional entr… from <file>` | The code sweep dropped an entry past its 30-day window. Binding and disputed entries are never dropped. |
|
|
293
|
+
| `context map validation passed` | Every file is in schema and within budget, every pointer resolves, and the drift checks found nothing. |
|
|
294
|
+
| `context map validation failed: N issue(s)` | The run's map is off schema or over budget, cites a missing path/section, restates a page, or has a possible contradiction. The map is **not** rewritten; the log names up to 20 issues and the paths to fix. |
|
|
229
295
|
| `context map is not writable: …` | Your config's one-writer rule does not match the path opencode evaluates. Nothing will update until you fix it. |
|
|
230
296
|
| `context map has more than one writer: …` | An agent other than `context` would be granted the map. The offending agent and the winning rule are named. |
|
|
231
297
|
|
|
232
298
|
## Reference
|
|
233
299
|
|
|
234
300
|
- The agent and command definitions, as injected: `src/agents.ts`, `src/commands.ts`.
|
|
235
|
-
- The
|
|
301
|
+
- The prompt the model actually runs, in one place: `src/lib/cartographer-prompt.ts`; the tracked copy is `.opencode/prompts/context.txt`, and a test pins equality.
|
|
302
|
+
- The idle/edit trigger and the post-run audit wiring: `src/plugins/context-autoupdate.ts`.
|
|
303
|
+
- The schema, budget, lifecycle and deterministic drift check: `src/lib/map-schema.ts`.
|
|
304
|
+
- The active backlog's window: `src/lib/active-backlog.ts`.
|
|
236
305
|
- The one-writer rule check: `src/lib/writer-rule.ts`.
|
|
306
|
+
- Fixtures and their report: `tests/fixtures/context-map/`, `scripts/context-map-fixture.mjs`.
|
|
237
307
|
- The behavioural contracts, with acceptance criteria a test asserts:
|
|
238
308
|
[USER-STORIES.md](USER-STORIES.md), US-19 to US-22.
|
|
239
309
|
- Tests: `tests/stories/g-context-management.test.mjs`.
|
package/docs/INSTALL.md
CHANGED
|
@@ -181,8 +181,8 @@ or no GNU `timeout` — rather than passing silently or failing confusingly.
|
|
|
181
181
|
|
|
182
182
|
## The `lacode` CLI
|
|
183
183
|
|
|
184
|
-
The package also ships the `lacode` CLI (`notify`, `forward`, `doctor`, `
|
|
185
|
-
`update`, `receive`, `tunnel setup`) — see
|
|
184
|
+
The package also ships the `lacode` CLI (`notify`, `forward`, `doctor`, `feedback`,
|
|
185
|
+
`version`, `update`, `receive`, `tunnel setup`) — see
|
|
186
186
|
[Remote hosts](../README.md#remote-hosts). The plugin install does **not** put it on your
|
|
187
187
|
`PATH`; run it from the package's `node_modules/.bin`, or install just the CLI
|
|
188
188
|
globally with `npm i -g @lawaty/lacode` if you want the bare command. (That
|
package/package.json
CHANGED