@akanjs/cli 3.0.0-alpha.52 → 3.0.0-alpha.54
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/.build-stamp +1 -1
- package/{cloud.command-eqafzn1a.js → cloud.command-v5g0c7nm.js} +2 -2
- package/guidelines/agentRule/agentRule.instruction.md +40 -19
- package/guidelines/conventions/conventions.instruction.md +24 -9
- package/{index-wmwcwn1f.js → index-d3vvvzsd.js} +2 -2
- package/{index-ssrz6xr1.js → index-rrcdemys.js} +1 -1
- package/index.js +3 -3
- package/{localRegistry.command-jewr722x.js → localRegistry.command-5hp7am6e.js} +1 -1
- package/package.json +2 -2
- package/{workspace.command-zg8me6fg.js → workspace.command-s2g36sgr.js} +2 -2
package/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
7d8513ec7462aef8977ff2afed56276153d80f296d00bd1133297ba30fa2d84b
|
|
@@ -41,6 +41,9 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
41
41
|
replacement for `AgentBubble` carries its own `memo`, since the transcript re-renders on every delta. Only then
|
|
42
42
|
`AgentChat`, which replaces the panel whole: it also replaces the slash commands, the compaction notice and the
|
|
43
43
|
approval gate, so reach for it when the *layout* differs, not when the look does.
|
|
44
|
+
A manifest reaches a layout-mounted chat like any other component: the override provider wraps the whole layout
|
|
45
|
+
stack, root layouts included. `UiOverrideProvider` is also a public `akanjs/ui` export, for giving one mount site
|
|
46
|
+
a different slot map than its route would.
|
|
44
47
|
- **The composer is a textarea**: Enter sends, Shift+Enter writes a newline, and it grows to a few lines before
|
|
45
48
|
it scrolls. The vertical arrows still walk what was sent, but only from the first or last line — anywhere else
|
|
46
49
|
the caret is the textarea's own. On a phone the panel is the whole screen (it is a card from `sm:` up) and it
|
|
@@ -116,12 +119,19 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
116
119
|
|
|
117
120
|
## Publishing A Tool
|
|
118
121
|
- **Declare the tool beside the control that already does it.**
|
|
119
|
-
`st.tool("x"
|
|
120
|
-
`onClick` — one handler for the person and the agent, which is the point: a button wired to
|
|
121
|
-
be clicked by a person and by nobody else. `.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
`st.tool("x").desc("…").arg("id", ID).opt("force", Boolean).exec(fn)` publishes one action and returns the
|
|
123
|
+
callable to hand to `onClick` — one handler for the person and the agent, which is the point: a button wired to
|
|
124
|
+
an inline arrow can be clicked by a person and by nobody else. `.desc()` is required and comes first, `.arg()`
|
|
125
|
+
is an argument the caller must pass and `.opt()` one it may leave out (the `exec` parameter is then `| null`),
|
|
126
|
+
and `.exec()` is the only hook, so the chain completes in one unconditional statement and the callable carries
|
|
127
|
+
`data-akan-action` like a store setter does. A `remove*` name defaults to a confirm gate. Reach a store action
|
|
128
|
+
from the body — `.exec((id) => st.do.removeX(id))` — which is how an agent gets CRUD; `st.do` on its own
|
|
129
|
+
reaches nobody.
|
|
130
|
+
- **The second argument is how the call behaves, never what it is.** `{ settle, confirm, guard }`:
|
|
131
|
+
`settle: false` marks a read that returns what is already there and skips the wait; every other tool is waited
|
|
132
|
+
out before its effect on the screen is reported, because a write may still be landing when `exec` resolves. A
|
|
133
|
+
tool that reads and one that writes are otherwise the same declaration — there is no taxonomy field, because
|
|
134
|
+
nothing downstream would read one.
|
|
125
135
|
- **A falsy name declares the tool without publishing it** — the callable still drives the click a person makes,
|
|
126
136
|
and the agent never learns the tool exists. That is the only way a conditional surface stays legal, because
|
|
127
137
|
`.exec()` is a hook and the declaration can never be skipped: withhold the name, not the call. `st.useState`
|
|
@@ -132,7 +142,7 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
132
142
|
- **An `enumOf` class is a complete argument type on its own**: `.arg("mode", TaskStatus)` publishes the values as
|
|
133
143
|
the argument's `enum`, refuses anything off them by name at call time, and narrows the `.exec` parameter to the
|
|
134
144
|
value union — nothing else to write, and the scalar (`string` / `integer` / `number`) comes from the values.
|
|
135
|
-
**A value set the *render* decides takes `.arg(name, type, { oneOf })`** instead, because `enumOf` registers
|
|
145
|
+
**A value set the *render* decides takes `.arg(name, type, { oneOf })`** (or `.opt`) instead, because `enumOf` registers
|
|
136
146
|
globally and a component cannot build one per render: pass the list it has — a slice's sort keys, the options a
|
|
137
147
|
prop carried — and it is published and enforced the same way. Neither reaches a set that fills in *after* the
|
|
138
148
|
first render, since a declaration is mount-static; put that in the tool's `guard`, which is re-read per call and
|
|
@@ -145,10 +155,13 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
145
155
|
tool that captured its own row would be fifty registrations of one name, forty-nine of them shadowed, and the
|
|
146
156
|
survivor would remove whichever row happened to mount last. Take the id instead — `removeTask(taskId)`, never
|
|
147
157
|
fifty `removeTask` — and every row's registration is then interchangeable, so a row component may publish after
|
|
148
|
-
all
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
all. **Nothing is written to say so: the description says it.** Fifty rows of one component produce one name
|
|
159
|
+
and one `.desc()`, which is the same declaration fifty times over and warns about nothing; a second description
|
|
160
|
+
arriving under a name already taken is two components that happened to pick one word, and that warns. So the
|
|
161
|
+
rule an app follows is about the sentence, not about a flag — two tools that would do different things
|
|
162
|
+
(a different `modal`, a different redirect) cannot honestly share a description, and that pair wants a
|
|
163
|
+
`namespace` or nothing at all. The ids come from the `<slice>.items` resource `Load.Units` and
|
|
164
|
+
`Data.ListContainer` already expose.
|
|
152
165
|
|
|
153
166
|
## What `akanjs/ui` Publishes For You
|
|
154
167
|
- **`akanjs/ui` publishes its own controls, so an app writes nothing for them.** `Data.ListContainer` (and every
|
|
@@ -199,9 +212,9 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
199
212
|
**The patch writes each plain field through that control's own published tool, not the setter underneath it** —
|
|
200
213
|
which is what carries the control's `transform`, so a field cannot normalize one way for `setPhoneOnBizAccount`
|
|
201
214
|
and another way for `fillBizAccountForm`. Only a composite, having no control, dispatches its setter directly.
|
|
202
|
-
The patch tool
|
|
203
|
-
|
|
204
|
-
not a clash
|
|
215
|
+
The patch tool's entry is a pure function of the model, so a form put on screen by a shell that subscribes it
|
|
216
|
+
(`Model.EditModal`) *and* by the `Template` inside it registers the same name with the same description twice
|
|
217
|
+
over — one declaration, not a clash. Neither has to suppress the other, and neither is asked to.
|
|
205
218
|
- **A `disabled` control publishes nothing, so the agent never gets a lever the person cannot pull.** Every value
|
|
206
219
|
control reads it — `Field.*`, `Input.*`, `Select`, `Switch`, and the four relation pickers — and disabling a
|
|
207
220
|
mounted control withdraws its tool for as long as it stays disabled. One gate covers both writers: with no
|
|
@@ -271,18 +284,26 @@ apps and libs never import it directly (`no-import-external-library`) — everyt
|
|
|
271
284
|
`Field.TextList` does, and the only place an inline arrow is the right answer rather than a bug.
|
|
272
285
|
|
|
273
286
|
## Reading State
|
|
274
|
-
- **Reading is per key, not per store.** `st.
|
|
275
|
-
|
|
287
|
+
- **Reading is per key, not per store.** `st.expose(name, Type).desc("…").value(v)` publishes a derived value and
|
|
288
|
+
`st.useState(name, Type, { set: true }).desc("…").init(initial)` local state, read-only without `set`. Both
|
|
289
|
+
end in a hook, so a conditional surface withholds the name rather than skipping the chain. A subscribed store key is listed in
|
|
276
290
|
the state context block by name and pulled with `readState(key)`, masked by the model that key declares — while
|
|
277
291
|
a key the screen does not read stays unreadable even when a sibling key of the same store is live. **There is no
|
|
278
292
|
store-level exposure declaration**: a store class says nothing about agents, and `st.use.x({ agent: false })` is
|
|
279
293
|
how the component that subscribes a value keeps it off the surface. Base-store plumbing does the same at the
|
|
280
294
|
call site — `st.use.path({ agent: false })`, `st.use.tryJwt({ agent: false })` — so routing and the caller's
|
|
281
295
|
credential stay off the surface unless a component opts a key in, as ThemeToggle does for `theme`.
|
|
282
|
-
- **Model-facing text is English, always** —
|
|
296
|
+
- **Model-facing text is English, always** — every `.desc()`, `instructions`, Guide text. The `l()` rule covers
|
|
283
297
|
strings a *user* reads: Chat's own buttons go through `l("base.*")`, the model's text never does.
|
|
284
|
-
-
|
|
285
|
-
|
|
298
|
+
- **The declared type is the mask.** `st.expose`/`st.useState` take a scalar, an enum, a model class, a one-level
|
|
299
|
+
array of any of those, or `Any`. A model class both typechecks what the component hands over — the state object,
|
|
300
|
+
so a hydrated document and a plain copy of one are equally accepted — and strips the model's `hidden`, `secret`,
|
|
301
|
+
and `visual` fields on the way out, by the model that was *named* rather than by whatever class the value still
|
|
302
|
+
happens to carry. A `Date` leaves as an ISO string. `Any` is the escape hatch and passes the value untouched, so
|
|
303
|
+
a payload nobody modeled stays publishable and its owner keeps the tokens it costs. A type nothing can read (a
|
|
304
|
+
`Map`, an `Upload`, a class that is not a model) is reported on the console and left unpublished, never thrown —
|
|
305
|
+
the same degradation an undescribable `.arg` gets. `.value()` also takes a thunk, which is read when the agent
|
|
306
|
+
reads: that is the shape for a value assembled out of a ref the children fill in after the render.
|
|
286
307
|
- **A field the page needs and an agent does not is `field.visual`.** `abstractData: field.visual(String)` — a blur
|
|
287
308
|
placeholder, a rendered HTML body, a serialized geometry: real data the screen renders, and hundreds of tokens per
|
|
288
309
|
record no question is answered from. It is stripped by `mask`, so it leaves every agent-facing read and every MCP
|
|
@@ -529,14 +529,21 @@ package; apps and libs never import it directly (`no-import-external-library`)
|
|
|
529
529
|
`option.setAgentAccess(SignedIn)`.
|
|
530
530
|
- **The LLM is configured in `option.ts`, never through the environment.** `option.setLlm({ apiKey, model, host })`
|
|
531
531
|
fills whichever adaptor holds `LlmAdaptorRole`; swap providers with `option.applyAdaptor(LlmAdaptorRole, X)`.
|
|
532
|
-
- **Declare the tool beside the control that already does it.**
|
|
533
|
-
publishes one action and returns the
|
|
534
|
-
|
|
532
|
+
- **Declare the tool beside the control that already does it.**
|
|
533
|
+
`st.tool("x").desc("…").arg("id", ID).opt("force", Boolean).exec(fn)` publishes one action and returns the
|
|
534
|
+
callable to hand to `onClick` — one handler for the person and the agent. `.desc()` is required and comes first,
|
|
535
|
+
`.arg()` is an argument the caller must pass and `.opt()` one it may, and `.exec()` is the only hook. Reach a
|
|
536
|
+
store action from the body (`.exec((id) => st.do.removeX(id))`); `st.do` on its own reaches nobody.
|
|
535
537
|
- **Publish a tool only where the screen already renders the control.** A falsy name declares the tool without
|
|
536
538
|
publishing it, which is how a conditional surface stays legal — `.exec()` is a hook, so withhold the name, not
|
|
537
539
|
the call. A `disabled` control publishes nothing.
|
|
538
540
|
- **A component that renders once per row takes the row id as an argument**, never a closure over it — fifty
|
|
539
|
-
registrations of one name leave forty-nine shadowed.
|
|
541
|
+
registrations of one name leave forty-nine shadowed. The description is what makes the repeats one declaration:
|
|
542
|
+
same name and same `.desc()` is the same tool registered fifty times, and only a second description under that
|
|
543
|
+
name warns.
|
|
544
|
+
- **`{ settle: false }` is the read that returns what is already there.** Every other tool is waited out before
|
|
545
|
+
what it did to the screen is reported, because a write may still be landing when `exec` resolves. `{ confirm }`
|
|
546
|
+
and `{ guard }` ride the same object; a `remove*` name confirms by default.
|
|
540
547
|
- **A component that can render twice on one screen takes a `namespace` prop** (`Tab`, `Dialog`, `Dropdown`,
|
|
541
548
|
`ScreenNavigator`) and publishes nothing without one.
|
|
542
549
|
- **Forms publish themselves**: a `Field.*` / `Input.*` / `Select` / `Switch` handed `onChange={st.do.setXOnY}`
|
|
@@ -544,6 +551,11 @@ package; apps and libs never import it directly (`no-import-external-library`)
|
|
|
544
551
|
nothing — normalize with the control's `transform` prop, and multi-write with a `_postSet<Field>` store method.
|
|
545
552
|
- **Reading is per key, not per store.** `st.use.x({ agent: false })` keeps a subscribed key off the surface;
|
|
546
553
|
a key no component reads is unreadable. A value with populated `hidden`/`secret` fields is refused at read.
|
|
554
|
+
- **A component's own value is published by its declared type.** `st.expose("taskId", ID).desc("…").value(v)` for
|
|
555
|
+
a derived value and `st.useState("tab", String, { set: true }).desc("…").init("all")` for local state — the type
|
|
556
|
+
typechecks what is handed over and decides how it is read, so a model class masks by that model. `Any` is the
|
|
557
|
+
escape hatch: no typecheck, and the value passes untouched. `.value()` also takes a thunk, read when the agent
|
|
558
|
+
reads.
|
|
547
559
|
- **Return what answers the question, not the record.** A tool's value is capped at 20,000 characters before it
|
|
548
560
|
enters the transcript and clipped with a note the model reads (`ToolOutput.limit`), because a store key is sized
|
|
549
561
|
for a screen and not for a model's window: one `readState` of a list whose rows carry inlined bytes is megabytes,
|
|
@@ -562,7 +574,7 @@ package; apps and libs never import it directly (`no-import-external-library`)
|
|
|
562
574
|
- The framework publishes five built-ins on every store surface — `navigate`, `goBack`, `readScreen`,
|
|
563
575
|
`readState(key)`, `highlight(target)` — plus `askUser`, which the session owns. **There is no general-purpose
|
|
564
576
|
wait**: publish an `st.tool` beside the control that starts the work and let it await the work.
|
|
565
|
-
- **Model-facing text is English, always** —
|
|
577
|
+
- **Model-facing text is English, always** — every `.desc()`, `instructions`, Guide text. The `l()` rule covers
|
|
566
578
|
strings a *user* reads.
|
|
567
579
|
|
|
568
580
|
Full contract — the chat's own options, attachments and speech, zones, what `akanjs/ui` publishes for you, slash
|
|
@@ -595,10 +607,13 @@ commands, transcript compaction, and the built-in tool semantics: `get_guideline
|
|
|
595
607
|
`filter` 0): `title` is the one line a human scans for, `desc` is prose, `tag` is a keyword list, `filter` is a
|
|
596
608
|
scoping value (status, owner, role) that must be matchable but must never outrank a real title hit.
|
|
597
609
|
- `thumb` is mirrored for rendering a hit and is **not** indexed — never expect it to match.
|
|
598
|
-
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
610
|
+
- **`field.secret`, `field.hidden` and `resolve()` take no `text` role — it is a compile error**, because the
|
|
611
|
+
mirror is plaintext and an indexed secret would leak through search. The class build throws the same refusal as
|
|
612
|
+
a backstop, for an option object the excess-property check cannot see through, and names the way out: drop the
|
|
613
|
+
role, or leave the field unmasked. Do not work around either. The throw also covers a `text` field *underneath*
|
|
614
|
+
one of those — a scalar's own field is reachable through its parent, so `f.secret(Noti)` where `Noti.label`
|
|
615
|
+
carries a role is rejected at the parent, not silently indexed; that one is only reachable at runtime, since the
|
|
616
|
+
pairing spans two files.
|
|
602
617
|
- The role works on a relation too (`image: field(File, { text: "thumb" })`) and on an array (`playing: field([String],
|
|
603
618
|
{ text: "tag" })`); an array of objects indexes by leaf key, including an array leaf (`works[*].tags`). A field
|
|
604
619
|
inside a `Map` indexes nothing: there is no fixed path to extract it from.
|
|
@@ -283,7 +283,7 @@ ${chalk.green("\u27A4")} Authentication Required`));
|
|
|
283
283
|
return;
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
-
|
|
286
|
+
for (const library of akanPkgs) {
|
|
287
287
|
Logger.info(`Publishing ${library}@${nextVersion} to ${registry ?? "npm"}...`);
|
|
288
288
|
await workspace.spawn("npm", ["publish", "--tag", tag, ...this.#getRegistryArgs(registry), ...this.#getLocalRegistryAuthArgs(registry)], {
|
|
289
289
|
cwd: path.join(workspace.workspaceRoot, "dist/pkgs", library),
|
|
@@ -291,7 +291,7 @@ ${chalk.green("\u27A4")} Authentication Required`));
|
|
|
291
291
|
stdio: "inherit"
|
|
292
292
|
});
|
|
293
293
|
Logger.info(`${library}@${nextVersion} is published to ${registry ?? "npm"}`);
|
|
294
|
-
}
|
|
294
|
+
}
|
|
295
295
|
Logger.info(`All libraries are published to ${registry ?? "npm"}`);
|
|
296
296
|
}
|
|
297
297
|
async update(workspace, tag = "latest", { registryUrl } = {}) {
|
package/index.js
CHANGED
|
@@ -19,16 +19,16 @@ import path from "path";
|
|
|
19
19
|
|
|
20
20
|
// pkgs/@akanjs/cli/commandModules.ts
|
|
21
21
|
var commandModules = {
|
|
22
|
-
workspace: async () => (await import("./workspace.command-
|
|
22
|
+
workspace: async () => (await import("./workspace.command-s2g36sgr.js")).WorkspaceCommand,
|
|
23
23
|
agent: async () => (await import("./agent.command-tsx1nsdj.js")).AgentCommand,
|
|
24
24
|
application: async () => (await import("./application.command-n8ex8g9h.js")).ApplicationCommand,
|
|
25
25
|
library: async () => (await import("./library.command-p3jgf5h2.js")).LibraryCommand,
|
|
26
|
-
localRegistry: async () => (await import("./localRegistry.command-
|
|
26
|
+
localRegistry: async () => (await import("./localRegistry.command-5hp7am6e.js")).LocalRegistryCommand,
|
|
27
27
|
package: async () => (await import("./package.command-fbpa6r4w.js")).PackageCommand,
|
|
28
28
|
module: async () => (await import("./module.command-hb3xm07r.js")).ModuleCommand,
|
|
29
29
|
page: async () => (await import("./page.command-92vrmw96.js")).PageCommand,
|
|
30
30
|
context: async () => (await import("./context.command-s7a41qjs.js")).ContextCommand,
|
|
31
|
-
cloud: async () => (await import("./cloud.command-
|
|
31
|
+
cloud: async () => (await import("./cloud.command-v5g0c7nm.js")).CloudCommand,
|
|
32
32
|
guideline: async () => (await import("./guideline.command-qrw84ttb.js")).GuidelineCommand,
|
|
33
33
|
scalar: async () => (await import("./scalar.command-zcsjgvax.js")).ScalarCommand,
|
|
34
34
|
primitive: async () => (await import("./primitive.command-k6b9jfxr.js")).PrimitiveCommand,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.54",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@langchain/openai": "^1.4.6",
|
|
35
35
|
"@tailwindcss/node": "^4.3.0",
|
|
36
36
|
"@trapezedev/project": "^7.1.4",
|
|
37
|
-
"akanjs": "3.0.0-alpha.
|
|
37
|
+
"akanjs": "3.0.0-alpha.54",
|
|
38
38
|
"chalk": "^5.6.2",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
40
|
"dayjs": "^1.11.20",
|
|
@@ -11,11 +11,11 @@ import"./index-ss469dec.js";
|
|
|
11
11
|
import"./index-kkya2ped.js";
|
|
12
12
|
import {
|
|
13
13
|
CloudScript
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-rrcdemys.js";
|
|
15
15
|
import {
|
|
16
16
|
getLatestPackageVersion,
|
|
17
17
|
getNpmRegistryUrl
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-d3vvvzsd.js";
|
|
19
19
|
import {
|
|
20
20
|
PackageScript
|
|
21
21
|
} from "./index-x1dbhd80.js";
|