@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/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 readFileSync7 } from "node:fs";
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) ? readFileSync7(pub, "utf8").trim() : "(public key missing)";
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 decisions that the repository's own decision log does not already record. Concise, variable length. |
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: when it disagrees with
71
- the source, the source is right and the map is wrong.
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 one file in the directory the plugin maintains rather than
142
- the cartographer. The cartographer records what happened — work in flight, and a
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
- edit of it and once when the session starts, so a hand edit is grouped and
150
- pruned whether the cartographer ran or not, and the file is rewritten only when
151
- its canonical form differs from what is on disk, so the write cannot loop.
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 idle/edit trigger: `src/plugins/context-autoupdate.ts`.
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`, `version`,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawaty/lacode",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Ultimate opencode setup for Go users — cost-controlled pooled subagents, hang recovery, and notifications for opencode.",
5
5
  "license": "MIT",
6
6
  "type": "module",