@zenera/cli 1.1.22 → 1.1.24

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.
Files changed (73) hide show
  1. package/README.md +72 -2
  2. package/dist/archive.d.ts +139 -0
  3. package/dist/archive.js +376 -0
  4. package/dist/ask.d.ts +51 -0
  5. package/dist/ask.js +143 -0
  6. package/dist/batch.d.ts +77 -0
  7. package/dist/batch.js +338 -0
  8. package/dist/command.d.ts +6 -0
  9. package/dist/commands/export.d.ts +3 -0
  10. package/dist/commands/export.js +255 -0
  11. package/dist/commands/import.d.ts +3 -0
  12. package/dist/commands/import.js +173 -0
  13. package/dist/commands/index.d.ts +5 -1
  14. package/dist/commands/index.js +13 -1
  15. package/dist/commands/inspect.d.ts +11 -0
  16. package/dist/commands/inspect.js +388 -89
  17. package/dist/commands/list.js +128 -19
  18. package/dist/commands/memory.js +453 -16
  19. package/dist/commands/models.js +14 -2
  20. package/dist/commands/run.js +139 -34
  21. package/dist/commands/sandbox.js +72 -1
  22. package/dist/engine.d.ts +19 -0
  23. package/dist/engine.js +60 -8
  24. package/dist/liveness.js +6 -3
  25. package/dist/main.js +5 -2
  26. package/dist/podman.d.ts +16 -0
  27. package/dist/podman.js +115 -2
  28. package/dist/progress.d.ts +33 -0
  29. package/dist/progress.js +402 -0
  30. package/dist/request.d.ts +45 -0
  31. package/dist/request.js +300 -0
  32. package/dist/sandbox.js +10 -0
  33. package/dist/scaffold.d.ts +8 -0
  34. package/dist/scaffold.js +4 -1
  35. package/dist/session.d.ts +26 -1
  36. package/dist/session.js +42 -3
  37. package/dist/term.d.ts +3 -0
  38. package/dist/term.js +17 -1
  39. package/dist/trace.d.ts +84 -0
  40. package/dist/trace.js +686 -0
  41. package/dist/validate.js +93 -4
  42. package/package.json +12 -6
  43. package/templates/editor/.github/copilot-instructions.md +11 -6
  44. package/templates/editor/.github/prompts/finetune.prompt.md +251 -0
  45. package/templates/editor/.github/prompts/spec-sync-project.prompt.md +25 -2
  46. package/templates/editor/.github/skills/zen-cli/SKILL.md +14 -3
  47. package/templates/editor/.github/skills/zen-cli/references/check.md +13 -5
  48. package/templates/editor/.github/skills/zen-cli/references/export.md +107 -0
  49. package/templates/editor/.github/skills/zen-cli/references/inspect.md +262 -21
  50. package/templates/editor/.github/skills/zen-cli/references/memory.md +123 -15
  51. package/templates/editor/.github/skills/zen-cli/references/models.md +1 -1
  52. package/templates/editor/.github/skills/zen-cli/references/projects.md +12 -5
  53. package/templates/editor/.github/skills/zen-cli/references/rag.md +6 -0
  54. package/templates/editor/.github/skills/zen-cli/references/run.md +201 -4
  55. package/templates/editor/.github/skills/zen-finetune/SKILL.md +1000 -0
  56. package/templates/editor/.github/skills/zen-finetune/scripts/collect.sh +432 -0
  57. package/templates/editor/.github/skills/zen-finetune/scripts/sample.sh +199 -0
  58. package/templates/editor/.github/skills/zen-inspect/SKILL.md +396 -0
  59. package/templates/editor/.github/skills/zen-memory/SKILL.md +74 -28
  60. package/templates/editor/.github/skills/zen-memory-warmup/SKILL.md +370 -408
  61. package/templates/editor/.github/skills/zen-sandbox/SKILL.md +19 -0
  62. package/templates/editor/.github/skills/zen-sandbox-capacity/SKILL.md +334 -0
  63. package/templates/editor/.github/skills/zen-sandbox-capacity/references/loadtest.md +181 -0
  64. package/templates/editor/.github/skills/zen-sandbox-capacity/scripts/preflight_sandbox.sh +457 -0
  65. package/templates/editor/.github/skills/zen-spec-sync/SKILL.md +6 -5
  66. package/templates/editor/.vscode/agents.schema.json +1 -1
  67. package/templates/editor/.vscode/settings.json +1 -0
  68. package/templates/project/SPECIFICATION.md.tmpl +2 -0
  69. package/templates/project/agents/files-instructions.md +13 -0
  70. package/templates/project/agents/fork-instructions.md +27 -0
  71. package/templates/project/agents/memory-instructions.md +80 -7
  72. package/templates/project/gitignore +3 -0
  73. package/templates/project/scripts/_setup.sh +49 -7
@@ -0,0 +1,255 @@
1
+ import { lockHolder, memoryDir, readProjectConfig } from '@zenera/neo';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { isAbsolute, join, resolve } from 'node:path';
4
+ import { ARCHIVE_FORMAT, collect, ENV_EXAMPLE, envExample, MANIFEST_FILE, safeName, writeArchive, } from "../archive.js";
5
+ import { one, parse } from "../args.js";
6
+ import { ENV_FILE } from "../env.js";
7
+ import { stamp } from "../ids.js";
8
+ import { isBusy, Registry, sessionIds, sessionsDir } from "../projects.js";
9
+ import { project as resolveProject } from "../resolve.js";
10
+ import { ownVersion } from "../scaffold.js";
11
+ import { bold, count, cyan, dim, green, bytes as human, invalidError, json, note, progress, table, usageError, warn, writeAll, } from "../term.js";
12
+ import { validateProject } from "../validate.js";
13
+ const USAGE = 'zen export [project] [--out <file>] [--no-vectors] [--no-memory] [--force]';
14
+ // ---------------------------------------------------------------------------
15
+ // zen export
16
+ //
17
+ // A project is a directory, and the reason this is not `zip -r` is that half
18
+ // of the directory is this machine rather than the project: the sessions, the
19
+ // scratch, the `.env`. What is left is the thing somebody else can run, and
20
+ // this writes exactly that — plus a manifest saying what it is, so `zen
21
+ // import` can register it under the right name and tell the reader what is
22
+ // still missing.
23
+ //
24
+ // The vectors travel by default, and that is the decision this command is
25
+ // built around. Memory has no other source: a graph without its vectors
26
+ // recalls by term overlap until every node is written again, and a rag index
27
+ // without its `lance/` tree looks built and cannot search. `--no-vectors`
28
+ // makes a small archive for someone who will run the restore step; the default
29
+ // makes one that works when it is opened.
30
+ //
31
+ // What never travels, behind no flag: the values in `.env`. The names do, as
32
+ // `.env.example`, because "which credentials does this need" is the first
33
+ // question on the other end and the answer is not a secret.
34
+ // ---------------------------------------------------------------------------
35
+ export const pack = {
36
+ summary: 'Write a project to a shareable zip archive.',
37
+ usage: USAGE,
38
+ banner: { head: 'Zenera', accent: 'Export', subtitle: 'Project Archive', hue: 'emerald' },
39
+ details: [
40
+ 'Carries agents.yaml, the agents/ tree, the specification and its',
41
+ 'feedback, assets, memory, the sandbox and scripts, and the editor',
42
+ 'files — everything that is the project.',
43
+ '',
44
+ 'Leaves behind what belongs to this machine: sessions/, .tmp/, .git/,',
45
+ 'node_modules/, lock files, any *.zip at the top of the project, and',
46
+ `.env. The names in .env become a ${ENV_EXAMPLE} with no values, so the`,
47
+ 'other end knows what to fill in.',
48
+ '',
49
+ 'Vectors travel by default — memory has no other source, and a rag',
50
+ 'index without its lance/ tree looks built and cannot search.',
51
+ '--no-vectors leaves both out for a much smaller archive that needs',
52
+ '`zen rag <subject> restore` on arrival. --no-memory drops memory/',
53
+ 'entirely, for an archive that shares the project and not what the',
54
+ 'agents learned.',
55
+ '',
56
+ 'The default name is <project>-<stamp>.zip in the current directory, so',
57
+ 'two exports never overwrite one another. --out takes any path.',
58
+ '',
59
+ 'A project that fails `zen check` is still exported, with a warning:',
60
+ 'sending someone a broken project to ask for help is the point.',
61
+ 'A memory held by a live run is refused — half a graph is not an',
62
+ 'archive — and --force overrides that.',
63
+ ],
64
+ run: async (ctx) => {
65
+ const { values, positionals } = parse(ctx.args, {
66
+ out: { type: 'string' },
67
+ 'no-vectors': { type: 'boolean' },
68
+ 'no-memory': { type: 'boolean' },
69
+ force: { type: 'boolean' },
70
+ }, USAGE);
71
+ const found = await resolveProject({
72
+ cwd: ctx.cwd,
73
+ project: one(positionals, 'project', USAGE),
74
+ });
75
+ const dir = found.dir;
76
+ const force = values.force === true;
77
+ const vectors = values['no-vectors'] !== true;
78
+ const memory = values['no-memory'] !== true;
79
+ settled(dir, force, memory);
80
+ const out = destination(ctx.cwd, found.name, values.out, force);
81
+ const collected = collect(dir, { vectors, memory });
82
+ const extra = new Map();
83
+ const env = join(dir, ENV_FILE);
84
+ if (existsSync(env)) {
85
+ extra.set(ENV_EXAMPLE, envExample(readFileSync(env, 'utf8')));
86
+ }
87
+ const healthy = await sound(dir, found.name, ctx.json);
88
+ const manifest = {
89
+ format: ARCHIVE_FORMAT,
90
+ name: found.name,
91
+ root: safeName(found.name) ?? 'project',
92
+ exportedAt: new Date().toISOString(),
93
+ cli: ownVersion(),
94
+ vectors,
95
+ memory,
96
+ files: collected.files.length + extra.size,
97
+ bytes: collected.bytes,
98
+ };
99
+ const bar = ctx.json ? undefined : progress();
100
+ let done = 0;
101
+ const size = await writeArchive({
102
+ out,
103
+ root: manifest.root,
104
+ entries: collected.files,
105
+ extra,
106
+ manifest,
107
+ onFile: (rel) => {
108
+ done += 1;
109
+ bar?.update(`packing ${done}/${collected.files.length} ${dim(rel)}`);
110
+ },
111
+ });
112
+ bar?.done();
113
+ if (ctx.json) {
114
+ json({
115
+ file: out,
116
+ bytes: size,
117
+ files: manifest.files,
118
+ project: found.name,
119
+ root: manifest.root,
120
+ vectors,
121
+ memory,
122
+ skipped: collected.skipped,
123
+ ok: healthy,
124
+ });
125
+ return;
126
+ }
127
+ report(out, size, collected, extra.size, vectors, memory);
128
+ },
129
+ };
130
+ // ---------------------------------------------------------------------------
131
+ // Refusing at the right moment
132
+ // ---------------------------------------------------------------------------
133
+ /**
134
+ * Nothing may be written while something is writing to it.
135
+ *
136
+ * Memory is the hard case: the store is a graph, a manifest and a vector file
137
+ * that only mean anything together, and a copy taken mid-write is an archive
138
+ * that opens and is wrong — which is worse than one that does not open. A run
139
+ * holding a session is milder; the session is not in the archive at all, and
140
+ * the only thing it can disturb is an asset the agent is editing.
141
+ */
142
+ function settled(dir, force, memory) {
143
+ const at = memory ? memoryAt(dir) : undefined;
144
+ const held = at ? lockHolder(at) : undefined;
145
+ if (held && !force) {
146
+ throw invalidError(`this project's memory is in use (pid ${held.pid}, since ${held.startedAt})`, 'wait for that run to finish, or: zen export --no-memory');
147
+ }
148
+ const busy = sessionIds(dir).filter((id) => isBusy(join(sessionsDir(dir), id)));
149
+ if (busy.length > 0 && !force) {
150
+ warn(`${count(busy.length, 'session')} still running — the archive is a snapshot`);
151
+ }
152
+ }
153
+ /**
154
+ * The configured memory directory, or nothing when the project will not load —
155
+ * which is a project this command exports anyway, so it cannot be an error here.
156
+ */
157
+ function memoryAt(dir) {
158
+ try {
159
+ return memoryDir(dir, readProjectConfig(dir).config);
160
+ }
161
+ catch {
162
+ return undefined;
163
+ }
164
+ }
165
+ /**
166
+ * Whether the project is sound — asked, reported, and never obeyed.
167
+ *
168
+ * Exporting a project that does not load is a legitimate thing to want: it is
169
+ * how you send it to somebody who can tell you why. So this warns and returns,
170
+ * and the archive is written either way. Nothing is built and no model is
171
+ * asked; this is a reading of files and costs nothing.
172
+ */
173
+ async function sound(dir, name, quiet) {
174
+ const registered = (await Registry.open()).findPath(dir) !== undefined;
175
+ const report = await validateProject({
176
+ dir,
177
+ name,
178
+ registered,
179
+ sandbox: { enabled: false },
180
+ models: { enabled: false },
181
+ });
182
+ if (!report.ok && !quiet) {
183
+ warn(`this project has ${count(report.counts.errors, 'error')} — ` +
184
+ `exporting it anyway (see: zen check)`);
185
+ }
186
+ return report.ok;
187
+ }
188
+ /**
189
+ * Where the archive goes. The stamp is in the default name because an export
190
+ * is a point in time, and because two of them in one afternoon should be two
191
+ * files rather than one silently replaced.
192
+ */
193
+ function destination(cwd, name, out, force) {
194
+ const at = out
195
+ ? resolve(isAbsolute(out) ? out : join(cwd, out))
196
+ : join(cwd, `${slug(name)}-${stamp()}.zip`);
197
+ if (existsSync(at) && !force) {
198
+ throw usageError(`${at} already exists`, 'overwrite it with --force, or name another --out');
199
+ }
200
+ return at;
201
+ }
202
+ /** A project name as a file name: what a shell and a browser both accept. */
203
+ function slug(name) {
204
+ const cleaned = name.replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^[-.]+/, '');
205
+ return cleaned.length > 0 ? cleaned : 'project';
206
+ }
207
+ // ---------------------------------------------------------------------------
208
+ // The report
209
+ // ---------------------------------------------------------------------------
210
+ /** What the skip reasons are called when somebody reads them. */
211
+ const SKIPPED = {
212
+ sessions: 'session directories',
213
+ scratch: '.tmp/',
214
+ git: 'git history',
215
+ modules: 'node_modules/',
216
+ env: `${ENV_FILE} (replaced by ${ENV_EXAMPLE})`,
217
+ locks: 'lock files',
218
+ noise: 'editor noise',
219
+ archives: 'zip archives beside the project',
220
+ vectors: 'vector stores (--no-vectors)',
221
+ memory: 'memory/ (--no-memory)',
222
+ links: 'symbolic links',
223
+ };
224
+ function report(out, size, collected, extra, vectors, memory) {
225
+ const groups = new Map();
226
+ for (const file of collected.files) {
227
+ const at = file.rel.indexOf('/');
228
+ const key = at < 0 ? '(root)' : `${file.rel.slice(0, at)}/`;
229
+ const group = groups.get(key) ?? { files: 0, bytes: 0 };
230
+ group.files += 1;
231
+ group.bytes += file.bytes;
232
+ groups.set(key, group);
233
+ }
234
+ const rows = [...groups.entries()]
235
+ .sort((a, b) => b[1].bytes - a[1].bytes)
236
+ .map(([key, g]) => [cyan(key), count(g.files, 'file'), dim(human(g.bytes))]);
237
+ writeAll(table(rows));
238
+ note();
239
+ note(`${green('wrote')} ${bold(out)} ${dim(human(size))}`);
240
+ note(dim(`${count(collected.files.length + extra, 'file')} inside, ` +
241
+ `described by ${MANIFEST_FILE}`));
242
+ for (const [reason, n] of Object.entries(collected.skipped)) {
243
+ note(dim(` left out ${count(n, 'path')}: ${SKIPPED[reason] ?? reason}`));
244
+ }
245
+ if (!vectors) {
246
+ note(dim(' no vectors: the other end needs `zen rag <subject> restore`, ' +
247
+ 'and memory will recall by term overlap'));
248
+ }
249
+ if (!memory) {
250
+ note(dim(' no memory: the project travels, what the agents learned does not'));
251
+ }
252
+ note();
253
+ note(`open it with ${bold('zen import')} ${dim(out)}`);
254
+ }
255
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1,3 @@
1
+ import type { Command } from '../command.ts';
2
+ export declare const unpack: Command;
3
+ //# sourceMappingURL=import.d.ts.map
@@ -0,0 +1,173 @@
1
+ import { existsSync, readdirSync } from 'node:fs';
2
+ import { isAbsolute, join, resolve } from 'node:path';
3
+ import { ENV_EXAMPLE, extractArchive, readManifest, safeName, } from "../archive.js";
4
+ import { parse } from "../args.js";
5
+ import { ENV_FILE } from "../env.js";
6
+ import { isProjectDir, Registry } from "../projects.js";
7
+ import { bold, count, cyan, dim, green, bytes as human, invalidError, json, note, progress, usageError, warn, } from "../term.js";
8
+ const USAGE = 'zen import <file.zip> [dir] [--name <name>] [--force] [--no-register]';
9
+ // ---------------------------------------------------------------------------
10
+ // zen import
11
+ //
12
+ // The other half of `zen export`, and the half that has to be suspicious. An
13
+ // archive is a file somebody sent, and everything in it is a claim: the paths
14
+ // it says to write, the sizes it says they unpack to, the modes it says they
15
+ // carry. The checks for all three live in archive.ts, next to the code that
16
+ // writes them, because a guard that lives apart from the thing it guards is a
17
+ // guard that drifts.
18
+ //
19
+ // The rule this command is built on: nothing in the archive is executed. It
20
+ // carries a `scripts/` tree, a Dockerfile and a `.github/` tree, and every one
21
+ // of those is something a machine could be talked into running on arrival.
22
+ // So this writes files, registers a name, and prints the commands you would
23
+ // type next — which you can read first, because they are on your screen and
24
+ // not in somebody else's zip.
25
+ // ---------------------------------------------------------------------------
26
+ export const unpack = {
27
+ summary: 'Unpack a project archive written by zen export.',
28
+ usage: USAGE,
29
+ banner: { head: 'Zenera', accent: 'Import', subtitle: 'Project Archive', hue: 'violet' },
30
+ details: [
31
+ 'Unpacks into ./<project> unless a directory is named. A directory',
32
+ 'that already has something in it is refused; --force writes over it.',
33
+ '',
34
+ 'The project is registered under the name in the archive, so `zen list`',
35
+ 'and `zen open` find it straight away. --name registers it under a',
36
+ 'different one, which is also what to use when that name is taken.',
37
+ '--no-register unpacks and leaves the registry alone.',
38
+ '',
39
+ `Credentials are not in the archive. ${ENV_EXAMPLE} names what the`,
40
+ `project needs: copy it to ${ENV_FILE} and fill it in.`,
41
+ '',
42
+ 'Nothing in the archive is run — not the setup script, not the',
43
+ 'Dockerfile, not a single line of the .github/ tree. The commands to',
44
+ 'run next are printed for you to read and type.',
45
+ ],
46
+ run: async (ctx) => {
47
+ const { values, positionals } = parse(ctx.args, {
48
+ name: { type: 'string' },
49
+ force: { type: 'boolean' },
50
+ 'no-register': { type: 'boolean' },
51
+ }, USAGE);
52
+ if (positionals.length === 0) {
53
+ throw usageError('no archive named', USAGE);
54
+ }
55
+ if (positionals.length > 2) {
56
+ throw usageError(`expected an archive and a directory, got ${positionals.length}`, USAGE);
57
+ }
58
+ const file = at(ctx.cwd, positionals[0]);
59
+ if (!existsSync(file)) {
60
+ throw usageError(`no such file: ${file}`);
61
+ }
62
+ const manifest = await readManifest(file);
63
+ const name = named(values.name, manifest);
64
+ const into = positionals[1] ? at(ctx.cwd, positionals[1]) : join(ctx.cwd, name);
65
+ empty(into, values.force === true);
66
+ const bar = ctx.json ? undefined : progress();
67
+ let done = 0;
68
+ const taken = await extractArchive({
69
+ file,
70
+ into,
71
+ root: manifest.root,
72
+ onFile: (rel) => {
73
+ done += 1;
74
+ bar?.update(`unpacking ${done}/${manifest.files} ${dim(rel)}`);
75
+ },
76
+ });
77
+ bar?.done();
78
+ if (!isProjectDir(into)) {
79
+ warn(`${into} has no agents.yaml — the archive did not hold a whole project`);
80
+ }
81
+ const registered = values['no-register'] !== true && (await register(name, into, ctx.json));
82
+ if (ctx.json) {
83
+ json({
84
+ dir: into,
85
+ name,
86
+ files: taken.files,
87
+ bytes: taken.bytes,
88
+ registered,
89
+ from: {
90
+ project: manifest.name,
91
+ exportedAt: manifest.exportedAt,
92
+ cli: manifest.cli,
93
+ },
94
+ vectors: manifest.vectors,
95
+ memory: manifest.memory,
96
+ });
97
+ return;
98
+ }
99
+ report(name, into, taken.files, taken.bytes, registered, manifest);
100
+ },
101
+ };
102
+ // ---------------------------------------------------------------------------
103
+ // Where it lands
104
+ // ---------------------------------------------------------------------------
105
+ const at = (cwd, path) => resolve(isAbsolute(path) ? path : join(cwd, path));
106
+ /**
107
+ * The name it is registered under. `--name` wins, because the archive's own
108
+ * may already belong to something on this machine — and because it arrived
109
+ * from elsewhere, it is checked before it becomes a directory or a key.
110
+ */
111
+ function named(wanted, manifest) {
112
+ const name = safeName(wanted ?? manifest.name);
113
+ if (!name) {
114
+ throw usageError(`"${wanted}" is not a usable project name`, USAGE);
115
+ }
116
+ return name;
117
+ }
118
+ /**
119
+ * A target with files in it is refused rather than merged. Unpacking over a
120
+ * project leaves a tree that is neither archive nor original — every file the
121
+ * archive holds replaced, every file it does not still there — and there is no
122
+ * reading of that which is useful. `--force` is the way to say you meant it.
123
+ */
124
+ function empty(into, force) {
125
+ if (!existsSync(into)) {
126
+ return;
127
+ }
128
+ const held = readdirSync(into).filter((entry) => entry !== '.DS_Store');
129
+ if (held.length === 0 || force) {
130
+ return;
131
+ }
132
+ throw invalidError(`${into} is not empty`, 'name another directory, or overwrite what is there with --force');
133
+ }
134
+ /** Registration is a convenience; a name already taken is not a failed import. */
135
+ async function register(name, dir, quiet) {
136
+ try {
137
+ const registry = await Registry.open();
138
+ registry.add(name, dir);
139
+ registry.save();
140
+ return true;
141
+ }
142
+ catch (err) {
143
+ if (!quiet) {
144
+ warn(`${err.message} — the project is on disk, ` +
145
+ `register it with: zen init ${dir} --name <other>`);
146
+ }
147
+ return false;
148
+ }
149
+ }
150
+ // ---------------------------------------------------------------------------
151
+ // What to do next
152
+ // ---------------------------------------------------------------------------
153
+ function report(name, into, files, size, registered, manifest) {
154
+ note(`${green('unpacked')} ${bold(name)} ${dim(into)}`);
155
+ note(dim(`${count(files, 'file')}, ${human(size)}, exported by zen ${manifest.cli}`));
156
+ if (!registered) {
157
+ note(dim('not registered — reach it by path'));
158
+ }
159
+ note();
160
+ note(bold('next'));
161
+ if (existsSync(join(into, ENV_EXAMPLE))) {
162
+ note(` ${cyan(`cp ${ENV_EXAMPLE} ${ENV_FILE}`)} ${dim('and fill in the credentials')}`);
163
+ }
164
+ if (!manifest.vectors) {
165
+ note(` ${cyan('zen rag <subject> restore')} ` +
166
+ dim('— the vectors did not travel, and nothing searches until they are back'));
167
+ }
168
+ note(` ${cyan(`zen check ${name}`)} ${dim('— what this machine is still missing')}`);
169
+ note(` ${cyan(`zen open ${name}`)}`);
170
+ note();
171
+ note(dim('nothing in the archive has been run. scripts/ and .github/ are worth a read.'));
172
+ }
173
+ //# sourceMappingURL=import.js.map
@@ -3,7 +3,11 @@ import type { Command } from '../command.ts';
3
3
  /**
4
4
  * Insertion order is the order help prints in, and it is deliberate: the four
5
5
  * a new user needs first, then the credentials-and-models pair, then the two
6
- * reports on a project, then the two that are only ever run on purpose.
6
+ * reports on a project, then the pair that moves a project between machines,
7
+ * then the ones that are only ever run on purpose.
8
+ *
9
+ * `export` and `import` are reserved words, so the two commands are declared
10
+ * under names a module can export and given their real ones here.
7
11
  */
8
12
  export declare const COMMANDS: Record<string, Command>;
9
13
  /** Names that are not listed in help but still work. */
@@ -1,5 +1,7 @@
1
1
  import { cache } from "./cache.js";
2
2
  import { check } from "./check.js";
3
+ import { pack } from "./export.js";
4
+ import { unpack } from "./import.js";
3
5
  import { init } from "./init.js";
4
6
  import { inspect } from "./inspect.js";
5
7
  import { key } from "./key.js";
@@ -14,7 +16,11 @@ import { version } from "./version.js";
14
16
  /**
15
17
  * Insertion order is the order help prints in, and it is deliberate: the four
16
18
  * a new user needs first, then the credentials-and-models pair, then the two
17
- * reports on a project, then the two that are only ever run on purpose.
19
+ * reports on a project, then the pair that moves a project between machines,
20
+ * then the ones that are only ever run on purpose.
21
+ *
22
+ * `export` and `import` are reserved words, so the two commands are declared
23
+ * under names a module can export and given their real ones here.
18
24
  */
19
25
  export const COMMANDS = {
20
26
  init,
@@ -26,6 +32,8 @@ export const COMMANDS = {
26
32
  models,
27
33
  check,
28
34
  inspect,
35
+ export: pack,
36
+ import: unpack,
29
37
  memory,
30
38
  sandbox,
31
39
  cache,
@@ -45,6 +53,10 @@ export const ALIASES = {
45
53
  edit: 'open',
46
54
  code: 'open',
47
55
  mock: 'faker',
56
+ pack: 'export',
57
+ archive: 'export',
58
+ unpack: 'import',
59
+ share: 'export',
48
60
  };
49
61
  export const EXTERNAL = {
50
62
  faker: {
@@ -1,3 +1,14 @@
1
1
  import type { Command } from '../command.ts';
2
+ import { type NodeDetail } from '../trace.ts';
2
3
  export declare const inspect: Command;
4
+ /**
5
+ * Which parts print in full: everything `--part` names, or everything but
6
+ * `request`. A prefix matches, so `--part call` catches `call run_command (…)`.
7
+ */
8
+ export declare function wholeParts(found: readonly NodeDetail[], values: {
9
+ part?: string[];
10
+ full?: boolean;
11
+ }): (name: string) => boolean;
12
+ /** One node, framed so a payload cannot be mistaken for the next node. */
13
+ export declare function renderNode(node: NodeDetail, whole?: (name: string) => boolean): string;
3
14
  //# sourceMappingURL=inspect.d.ts.map