@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 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 name becomes `persona.name` in the config, the role `persona/identity.md` ("You are {{assistant}}, the platform team's release assistant.", the core putting the name in place of `{{assistant}}`), next to a `persona/tone.md` to edit;
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.1",
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.1",
45
- "@mercury-fw/utils": "0.53.1",
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.1",
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 persona.name, the persona (persona/identity.md, persona/tone.md, which refer to the name as {{assistant}}), 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).",
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, the persona and the
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 { DEFAULT_PERSONA_IDENTITY, DEFAULT_PERSONA_TONE, assistantNameOf } from "@mercury-fw/core";
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
- ["markdown.d.ts", markdownDts],
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
- /** `mercury.config.ts`: imports, the formatter helpers of the plugins that have
173
- * any, then the config with each plugin (wrapped in the formatter when it has
174
- * starting rules) and channel. */
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
- ` persona: { name: ${JSON.stringify(assistantName)}, identity, tone },`,
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
 
@@ -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 markdown.d.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
@@ -1,5 +0,0 @@
1
- /** Markdown imported as text (`with { type: "text" }`), as the persona files are. */
2
- declare module "*.md" {
3
- const text: string;
4
- export default text;
5
- }