@mercury-fw/cli 0.53.1 → 0.53.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/CHANGELOG.md +15 -0
- package/README.md +1 -1
- package/package.json +4 -4
- package/src/program.ts +1 -1
- package/src/render.ts +42 -17
- package/template/Dockerfile.tpl +1 -2
- package/template/markdown.d.ts.tpl +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# @mercury-fw/cli
|
|
2
2
|
|
|
3
|
+
## 0.53.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4a9a528: `mfw create` writes the whole persona inside `mercury.config.ts`: the assistant's name once, as a `const`, referred to by the identity and by the tone, which is written out in full, one string per line. There are no `persona/identity.md` and `persona/tone.md` files anymore, no `markdown.d.ts`, and the Dockerfile no longer copies them.
|
|
8
|
+
|
|
9
|
+
An existing app keeps working as it is. To move it to the new layout:
|
|
10
|
+
|
|
11
|
+
1. In `mercury.config.ts`, drop the two `import … from "./persona/…md"` lines, add `const name = "<the assistant's name>";` and set `persona: { name, identity: "…", tone: "…" }` with the text of the two files (`${name}` in a template literal where they say `{{assistant}}`, or keep `{{assistant}}`: the core still replaces it).
|
|
12
|
+
2. Delete `persona/` and `markdown.d.ts`.
|
|
13
|
+
3. In the `Dockerfile`, change `COPY --chown=mercury:mercury mercury.config.ts markdown.d.ts ./` to `COPY --chown=mercury:mercury mercury.config.ts ./` and delete `COPY --chown=mercury:mercury persona ./persona`.
|
|
14
|
+
|
|
15
|
+
- @mercury-fw/core@0.53.2
|
|
16
|
+
- @mercury-fw/utils@0.53.2
|
|
17
|
+
|
|
3
18
|
## 0.53.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Without a global install: `bun create mercury-agent my-agent` creates an app, an
|
|
|
47
47
|
Writes a new Mercury app into `<folder>`, which has to be missing or empty; the folder's own name is turned into kebab case (`My Agent` becomes `my-agent`, the path above it stays as typed). Without flags it asks:
|
|
48
48
|
|
|
49
49
|
- the app name (the `name` in `package.json`, defaulting to the folder's);
|
|
50
|
-
- the assistant's name and role: the
|
|
50
|
+
- the assistant's name and role: the whole persona goes in the config, the name written once as a `const` and referred to by the identity (`` `You are ${name}, the platform team's release assistant.` ``) and by the tone, written out in full to edit;
|
|
51
51
|
- which channels and which tool plugins to include (none is fine: the REPL always works);
|
|
52
52
|
- the git remote for `origin`, which you can leave empty (most of the time it doesn't exist yet when you scaffold).
|
|
53
53
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mercury-fw/cli",
|
|
3
|
-
"version": "0.53.
|
|
3
|
+
"version": "0.53.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"comment:shape": "The Mercury CLI (`mfw`). `mfw create <dir>` writes a new Mercury app from the template. The framework packages move in lockstep with it, so a new app gets them at the CLI's own version; plugins and channels at the registry's latest. The plugin and channel packages are devDependencies only for the catalog test. `main(argv)` is exported for create-mercury-agent.",
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@clack/prompts": "^1.8.1",
|
|
44
|
-
"@mercury-fw/core": "0.53.
|
|
45
|
-
"@mercury-fw/utils": "0.53.
|
|
44
|
+
"@mercury-fw/core": "0.53.2",
|
|
45
|
+
"@mercury-fw/utils": "0.53.2",
|
|
46
46
|
"commander": "^15.0.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@mercury-fw/channel-google-chat": "0.8.2",
|
|
52
52
|
"@mercury-fw/channel-http": "0.10.1",
|
|
53
53
|
"@mercury-fw/directory-static": "0.1.1",
|
|
54
|
-
"@mercury-fw/formatter": "0.53.
|
|
54
|
+
"@mercury-fw/formatter": "0.53.2",
|
|
55
55
|
"@mercury-fw/plugin-atlassian-admin": "0.3.2",
|
|
56
56
|
"@mercury-fw/plugin-bitbucket": "0.4.1",
|
|
57
57
|
"@mercury-fw/plugin-jira": "0.6.5",
|
package/src/program.ts
CHANGED
|
@@ -52,7 +52,7 @@ function buildProgram(handlers: ProgramHandlers, result: { code: number }): Comm
|
|
|
52
52
|
.command("create")
|
|
53
53
|
.summary("writes a new Mercury app")
|
|
54
54
|
.description(
|
|
55
|
-
"Writes a new Mercury app into <folder>, which has to be missing or empty (its own name is turned into kebab case). Without options it asks for the app name, the assistant's name and role, and which channels (with the HTTP channel's auth provider) and tool plugins to include; then it writes mercury.config.ts for that selection, with the assistant's name as
|
|
55
|
+
"Writes a new Mercury app into <folder>, which has to be missing or empty (its own name is turned into kebab case). Without options it asks for the app name, the assistant's name and role, and which channels (with the HTTP channel's auth provider) and tool plugins to include; then it writes mercury.config.ts for that selection, with the whole persona in it (the assistant's name, its role as the identity, and the default tone written out to edit), the service and REPL entrypoints, a Dockerfile, a compose file with Qdrant, and an env example listing every variable the app reads. Then it runs bun install, and creates a git repository on main with a first commit, adding the origin when given (nothing is pushed).",
|
|
56
56
|
)
|
|
57
57
|
.argument("<folder>", "where to write the app")
|
|
58
58
|
.option("--name <name>", "app name, as in package.json (default: the folder's name)")
|
package/src/render.ts
CHANGED
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
* Turns the wizard's answers into the new app's files, as a `path → content`
|
|
4
4
|
* map, without touching the disk (`write.ts` does that). The static files come
|
|
5
5
|
* from `template/` imported as text, so they are bundled with the CLI; the
|
|
6
|
-
* config, the manifest, the env example, the compose file
|
|
7
|
-
* README are generated from the selection. Channels and plugins are always
|
|
6
|
+
* config (with the persona), the manifest, the env example, the compose file
|
|
7
|
+
* and the README are generated from the selection. Channels and plugins are always
|
|
8
8
|
* written in catalog order, whatever order they were chosen in; the auth
|
|
9
9
|
* provider, when there is one, after the channels.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { DEFAULT_PERSONA_TONE, assistantNameOf } from "@mercury-fw/core";
|
|
12
12
|
import { CATALOG, type CatalogEntry, type EnvVar } from "./catalog.ts";
|
|
13
13
|
import indexTs from "../template/src/index.ts.tpl" with { type: "text" };
|
|
14
14
|
import replTs from "../template/src/repl.ts.tpl" with { type: "text" };
|
|
15
|
-
import markdownDts from "../template/markdown.d.ts.tpl" with { type: "text" };
|
|
16
15
|
import tsconfigJson from "../template/tsconfig.json.tpl" with { type: "text" };
|
|
17
16
|
import gitignore from "../template/gitignore.tpl" with { type: "text" };
|
|
18
17
|
import dockerignore from "../template/dockerignore.tpl" with { type: "text" };
|
|
@@ -86,12 +85,8 @@ export function renderApp(input: RenderInput): Map<string, string> {
|
|
|
86
85
|
["Dockerfile", dockerfile],
|
|
87
86
|
["README.md", renderReadme(input.name, channels, auth, tools, directory)],
|
|
88
87
|
["docker-compose.yml", renderCompose(input.name, channels.some((c) => c.id === "http"), services)],
|
|
89
|
-
["
|
|
90
|
-
["mercury.config.ts", renderConfig(assistantName, channels, auth, tools, directory)],
|
|
88
|
+
["mercury.config.ts", renderConfig(assistantName, input.role, channels, auth, tools, directory)],
|
|
91
89
|
["package.json", renderPackageJson(input.name, channels, auth, tools, input.versions, directory)],
|
|
92
|
-
// The name is persona.name in the config; the persona files refer to it.
|
|
93
|
-
["persona/identity.md", `You are {{assistant}}, ${input.role.trim().replace(/\.+$/, "")}.\n`],
|
|
94
|
-
["persona/tone.md", `${DEFAULT_PERSONA_TONE}\n`],
|
|
95
90
|
["src/index.ts", indexTs],
|
|
96
91
|
["src/repl.ts", replTs],
|
|
97
92
|
["tsconfig.json", tsconfigJson],
|
|
@@ -169,11 +164,41 @@ function selected(kind: CatalogEntry["kind"], ids: string[]): CatalogEntry[] {
|
|
|
169
164
|
return CATALOG.filter((e) => e.kind === kind && ids.includes(e.id));
|
|
170
165
|
}
|
|
171
166
|
|
|
172
|
-
/**
|
|
173
|
-
|
|
174
|
-
|
|
167
|
+
/** What the core's persona texts write where the assistant's name goes. */
|
|
168
|
+
const ASSISTANT = "{{assistant}}";
|
|
169
|
+
|
|
170
|
+
/** `text` as the inside of a template literal: backslashes, backticks and `${` stay text. */
|
|
171
|
+
function templateText(text: string): string {
|
|
172
|
+
return text.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** One line of the persona as config source: a template literal referring to
|
|
176
|
+
* the config's `name` where the line names the assistant, a string otherwise. */
|
|
177
|
+
function personaLine(line: string): string {
|
|
178
|
+
if (!line.includes(ASSISTANT)) return JSON.stringify(line);
|
|
179
|
+
return `\`${line.split(ASSISTANT).map(templateText).join("${name}")}\``;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** The config's `persona`: the name, the role as the identity, and the core's
|
|
183
|
+
* default tone written out line by line, so the whole persona is there to edit. */
|
|
184
|
+
function renderPersona(role: string): string[] {
|
|
185
|
+
return [
|
|
186
|
+
" persona: {",
|
|
187
|
+
" name,",
|
|
188
|
+
` identity: ${personaLine(`You are ${ASSISTANT}, ${role.trim().replace(/\.+$/, "")}.`)},`,
|
|
189
|
+
" tone: [",
|
|
190
|
+
...DEFAULT_PERSONA_TONE.split("\n").map((line) => ` ${personaLine(line)},`),
|
|
191
|
+
' ].join("\\n"),',
|
|
192
|
+
" },",
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** `mercury.config.ts`: imports, the assistant's name, the formatter helpers of
|
|
197
|
+
* the plugins that have any, then the config with the persona, each plugin
|
|
198
|
+
* (wrapped in the formatter when it has starting rules) and channel. */
|
|
175
199
|
function renderConfig(
|
|
176
200
|
assistantName: string,
|
|
201
|
+
role: string,
|
|
177
202
|
channels: CatalogEntry[],
|
|
178
203
|
auth: CatalogEntry | undefined,
|
|
179
204
|
tools: CatalogEntry[],
|
|
@@ -192,8 +217,6 @@ function renderConfig(
|
|
|
192
217
|
for (const c of [...channels, ...(auth ? [auth] : []), ...(directory ? [directory] : [])]) {
|
|
193
218
|
imports.push(`import { ${c.exportName} } from "${c.package}";`);
|
|
194
219
|
}
|
|
195
|
-
imports.push('import identity from "./persona/identity.md" with { type: "text" };');
|
|
196
|
-
imports.push('import tone from "./persona/tone.md" with { type: "text" };');
|
|
197
220
|
|
|
198
221
|
const helpers = withRules.map((t) => `${t.formatter?.helpers}\n\n`).join("");
|
|
199
222
|
|
|
@@ -207,8 +230,11 @@ function renderConfig(
|
|
|
207
230
|
CONFIG_HEADER,
|
|
208
231
|
...imports,
|
|
209
232
|
"",
|
|
233
|
+
"/** The assistant's name: what the persona calls it, and every text the model reads. */",
|
|
234
|
+
`const name = ${JSON.stringify(assistantName)};`,
|
|
235
|
+
"",
|
|
210
236
|
`${helpers}export default defineMercuryConfig({`,
|
|
211
|
-
|
|
237
|
+
...renderPersona(role),
|
|
212
238
|
plugins,
|
|
213
239
|
channelList,
|
|
214
240
|
...(auth ? [` auth: ${auth.exportName},`] : []),
|
|
@@ -379,8 +405,7 @@ ${auth ? `- Auth: ${auth.id}\n` : ""}- Tool plugins: ${list(tools)}
|
|
|
379
405
|
${directory ? `- Directory: ${directory.id}\n` : ""}
|
|
380
406
|
## Layout
|
|
381
407
|
|
|
382
|
-
- \`mercury.config.ts\`: what the app is made of (tool plugins, channels, how their lists read) and the assistant's persona.
|
|
383
|
-
- \`persona/identity.md\`, \`persona/tone.md\`: who the assistant is and how it answers. Edit them freely.
|
|
408
|
+
- \`mercury.config.ts\`: what the app is made of (tool plugins, channels, how their lists read) and the assistant's persona: its name, who it is and how it answers. Edit them freely.
|
|
384
409
|
- \`src/index.ts\`: the service (channels, crons). \`src/repl.ts\`: an interactive terminal for trying things out.
|
|
385
410
|
- \`.env.example\`: every variable the app reads. Copy it to \`.env\` and fill it in.
|
|
386
411
|
|
package/template/Dockerfile.tpl
CHANGED
|
@@ -20,8 +20,7 @@ COPY --chown=mercury:mercury .pack[s] ./.packs/
|
|
|
20
20
|
RUN bun install --production && chown -R mercury:mercury node_modules
|
|
21
21
|
RUN find /app/node_modules -path '*/@mercury-fw/*/bin/*' -type f -exec ln -sf {} /usr/local/bin/ \;
|
|
22
22
|
|
|
23
|
-
COPY --chown=mercury:mercury mercury.config.ts
|
|
24
|
-
COPY --chown=mercury:mercury persona ./persona
|
|
23
|
+
COPY --chown=mercury:mercury mercury.config.ts ./
|
|
25
24
|
COPY --chown=mercury:mercury src ./src
|
|
26
25
|
|
|
27
26
|
# Mount points of the named volumes, owned by the runtime user before a fresh
|