@yargote/artifacts-cli 0.1.0-beta.8 → 0.1.0-beta.9

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/README.md CHANGED
@@ -11,9 +11,9 @@ Use Node 24 or later. The beta targets macOS and Linux. Windows has not been ver
11
11
  Choose one persistent installation:
12
12
 
13
13
  ```sh
14
- npm install --global @yargote/artifacts-cli@0.1.0-beta.8
14
+ npm install --global @yargote/artifacts-cli@0.1.0-beta.9
15
15
  # Or, with pnpm and an existing global bin directory on PATH:
16
- pnpm add --global @yargote/artifacts-cli@0.1.0-beta.8
16
+ pnpm add --global @yargote/artifacts-cli@0.1.0-beta.9
17
17
  artifacts --version
18
18
  ```
19
19
 
@@ -22,8 +22,8 @@ If pnpm reports that no global bin directory exists, run `pnpm setup` and reopen
22
22
  For one-off execution, pin the same version:
23
23
 
24
24
  ```sh
25
- npx --yes @yargote/artifacts-cli@0.1.0-beta.8 --help
26
- pnpm dlx @yargote/artifacts-cli@0.1.0-beta.8 --help
25
+ npx --yes @yargote/artifacts-cli@0.1.0-beta.9 --help
26
+ pnpm dlx @yargote/artifacts-cli@0.1.0-beta.9 --help
27
27
  ```
28
28
 
29
29
  `@beta` follows future prereleases. Exact versions remain reproducible. No stable `latest` release is part of this beta. An organization's pnpm minimum release age or trust policy can delay a new release. Follow that policy rather than disabling it globally.
@@ -141,8 +141,7 @@ artifacts history ID [--limit N] [--before-sequence N] [--summary]
141
141
  artifacts publish ID --revision REV --expected-version N [--request-id ID]
142
142
  artifacts publish [ID] --command FILE
143
143
  artifacts export ID --revision REV --out FILE
144
- artifacts move ID --project NAME [--request-id ID]
145
- artifacts move ID --no-project [--request-id ID]
144
+ artifacts move ID [--project NAME | --no-project] [--slug SLUG | --no-slug] [--request-id ID]
146
145
  artifacts share ID --email EMAIL [--notify] [--request-id ID]
147
146
  artifacts shares ID
148
147
  artifacts unshare ID SHARE_ID [--request-id ID]
@@ -215,7 +214,7 @@ artifacts move ARTIFACT_ID --no-project
215
214
 
216
215
  Every artifact read and list item includes `project`, a name or null. `projects` prints `{ "items": [{ "name": "Alpha", "artifactCount": 1 }] }`, ordered by name without regard to letter case. A project exists only while it contains an artifact.
217
216
 
218
- `move ID` requires exactly one of `--project NAME` or `--no-project`. It prints `{ "artifactId": "...", "project": "Beta" }` with the stored spelling, or `null` after `--no-project`. Move never creates a revision or changes the publication. Moving to the current project succeeds without change. Move is a retained mutation like the others, so `retry` recovers a lost response.
217
+ `move ID` requires at least one of `--project NAME`, `--no-project`, `--slug SLUG`, or `--no-slug`, and accepts at most one from each pair. An option left out keeps that value. It prints `{ "artifactId": "...", "project": "Beta" }` with the stored spelling, or `null` after `--no-project`. Move never creates a revision or changes the publication. Moving to the current project succeeds without change. Move is a retained mutation like the others, so `retry` recovers a lost response.
219
218
 
220
219
  Version `0.1.0-beta.4` and earlier parse artifact responses strictly and fail with `invalid_response` against a server that returns `project`. Upgrade the CLI together with the server.
221
220
 
@@ -227,10 +226,14 @@ A slug names a page within its project, so `read` can find it without its ID. `c
227
226
  artifacts create --snapshot snapshot.json --project Mi-Wiki --slug concepts/rag
228
227
  artifacts read --project mi-wiki --slug concepts/rag
229
228
  artifacts read --slug index
229
+ artifacts move ARTIFACT_ID --slug concepts/retrieval
230
+ artifacts move ARTIFACT_ID --no-slug
230
231
  ```
231
232
 
232
233
  A slug is unique within the owner's project, matched by the project's stored spelling. Artifacts without a project share one unfiled namespace, which `read --slug` reads without `--project`. `read` takes exactly one of an ID or `--slug`, and `--project` applies only with `--slug`. A used slug fails `create` with `slug_conflict`, status 409, and the holder's `artifactId`. A locator with no artifact fails with `not_found`. Every artifact read and list item includes `slug`, a name or null. Version `0.1.0-beta.7` and earlier reject responses that include `slug` with `invalid_response`.
233
234
 
235
+ `move ID --slug SLUG` renames a page in its project, and `move ID --no-slug` clears the slug. A move to another project keeps the slug. A move fails with `slug_conflict`, status 409, and the holder's `artifactId` when the target project already uses the target slug, and then changes nothing. A slug change creates no revision. Verify the new slug with `read`. The move result is unchanged, so published CLI versions keep working against this server.
236
+
234
237
  ## Share publication access
235
238
 
236
239
  These commands are available from `0.1.0-beta.6`. Check installed help for support.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yargote/artifacts-cli",
3
- "version": "0.1.0-beta.8",
3
+ "version": "0.1.0-beta.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Command-line client for the Artifacts REST API.",
@@ -27,8 +27,8 @@
27
27
  "skills"
28
28
  ],
29
29
  "dependencies": {
30
- "@yargote/artifacts-contracts": "0.1.0-beta.8",
31
- "@yargote/artifacts-client": "0.1.0-beta.8",
30
+ "@yargote/artifacts-contracts": "0.1.0-beta.9",
31
+ "@yargote/artifacts-client": "0.1.0-beta.9",
32
32
  "openid-client": "6.8.8",
33
33
  "zod": "4.6.5"
34
34
  },
@@ -35,7 +35,7 @@ Use `edit` for partial changes that preserve omitted fields. Use `update` for a
35
35
 
36
36
  When the user names a project for new content, add `--project NAME` to `create`. Run `artifacts projects` first when an existing project may already fit. A name matching an existing project regardless of letter case joins it and keeps the stored spelling. Do not invent a project the user did not ask for. Use `list --project NAME` to find artifacts in a project.
37
37
 
38
- When the user names a page, add `--slug SLUG` such as `concepts/rag` to `create`, and read it later with `read --project NAME --slug SLUG`, or `read --slug SLUG` for an unfiled page, instead of listing for its ID. Omit the `.md` suffix. A `slug_conflict` error names the `artifactId` that already holds the slug in that project.
38
+ When the user names a page, add `--slug SLUG` such as `concepts/rag` to `create`, and read it later with `read --project NAME --slug SLUG`, or `read --slug SLUG` for an unfiled page, instead of listing for its ID. Omit the `.md` suffix. A `slug_conflict` error names the `artifactId` that already holds the slug in that project. Rename a page with `move ID --slug SLUG` and clear its slug with `move ID --no-slug`. A move to another project keeps the slug, and a move into a used slug fails with `slug_conflict` without changing anything.
39
39
 
40
40
  Use `move ID --project NAME` to file an existing artifact under a project and `move ID --no-project` to remove it from one. A move changes only the project. It creates no revision and leaves the publication unchanged. It is a retained mutation, so recover an uncertain move with `retry`. Verify it with `read` or `list --project NAME`. A project disappears when its last artifact leaves it.
41
41
 
@@ -104,7 +104,7 @@ artifacts move ARTIFACT_ID --no-project
104
104
 
105
105
  A project name contains 1 to 80 characters after trimming and NFC normalization, without control characters. A name that matches an existing project regardless of letter case joins it and keeps its stored spelling. Every read and list item includes `project`, a name or null. `projects` prints `{ "items": [{ "name": "Client onboarding", "artifactCount": 1 }] }`, ordered by name without regard to letter case. A project exists only while it contains an artifact.
106
106
 
107
- `move` takes exactly one of `--project NAME` or `--no-project` and prints `{ "artifactId": "...", "project": "Archive" }`, or `null` for the project after `--no-project`. It creates no revision and leaves the publication unchanged. Moving to the current project succeeds without change. A lost move response is recovered with `retry REQUEST_ID` like any other retained mutation.
107
+ `move` takes at least one of `--project NAME`, `--no-project`, `--slug SLUG`, or `--no-slug`, and at most one from each pair. It prints `{ "artifactId": "...", "project": "Archive" }`, or `null` for the project after `--no-project`. An option left out keeps that value. It creates no revision and leaves the publication unchanged. Moving to the current project succeeds without change. A lost move response is recovered with `retry REQUEST_ID` like any other retained mutation.
108
108
 
109
109
  ## Name pages with slugs
110
110
 
@@ -114,11 +114,13 @@ Check that installed help lists `--slug` before using it.
114
114
  artifacts create --snapshot snapshot.json --project "Mi wiki" --slug concepts/rag
115
115
  artifacts read --project "mi wiki" --slug concepts/rag
116
116
  artifacts read --slug index
117
+ artifacts move ARTIFACT_ID --slug concepts/retrieval
118
+ artifacts move ARTIFACT_ID --no-slug
117
119
  ```
118
120
 
119
121
  A slug contains 1 to 160 characters of lowercase ASCII kebab-case segments separated by `/`, such as `concepts/rag` or `index`. It is never normalized, and a trailing `.md` is rejected with a hint to remove it. A slug is unique within the owner's project, matched by the project's stored spelling. Artifacts without a project share one unfiled namespace, which `read --slug` without `--project` reads. Every read and list item includes `slug`, a name or null.
120
122
 
121
- `read` takes exactly one of an ID or `--slug`. `--project` applies only with `--slug`. A used slug fails `create` with `slug_conflict`, status 409, and the holder's `artifactId` in `details`. A locator with no artifact fails with `not_found`. A move keeps the slug and fails with `slug_conflict` when the destination project already uses it.
123
+ `read` takes exactly one of an ID or `--slug`. `--project` applies only with `--slug`. A used slug fails `create` with `slug_conflict`, status 409, and the holder's `artifactId` in `details`. A locator with no artifact fails with `not_found`. `move ID --slug SLUG` renames a page in its project, and `move ID --no-slug` clears the slug. A move to another project keeps the slug unless `--slug` or `--no-slug` changes it. A move fails with `slug_conflict` and changes nothing when the target project already uses the target slug. A slug change creates no revision.
122
124
 
123
125
  ## Edit without losing retained data
124
126
 
package/src/index.js CHANGED
@@ -48,8 +48,8 @@ Commands:
48
48
  publish ID --revision REV --expected-version N [--request-id ID]
49
49
  publish [ID] --command FILE
50
50
  export ID --revision REV --out FILE
51
- move ID --project NAME [--request-id ID]
52
- move ID --no-project [--request-id ID]
51
+ move ID [--project NAME | --no-project] [--slug SLUG | --no-slug]
52
+ [--request-id ID]
53
53
  share ID --email EMAIL [--notify] [--request-id ID]
54
54
  shares ID
55
55
  unshare ID SHARE_ID [--request-id ID]
@@ -59,10 +59,14 @@ Mutation command files contain the complete create, update, or publish JSON.
59
59
  Mutation request IDs default to a random UUID. Commands are retained before
60
60
  submission. Retry sends the exact retained command for the same server and owner.
61
61
  Projects group artifacts by name without regard to letter case. A project
62
- exists while it contains an artifact. Move changes the project of an artifact
63
- without creating a revision; --no-project removes it from its project.
62
+ exists while it contains an artifact. Move changes the project or the slug of
63
+ an artifact without creating a revision. It needs at least one of its four
64
+ options. --no-project removes the artifact from its project, and --no-slug
65
+ clears its slug. An option left out keeps that value, so a move to another
66
+ project keeps the slug.
64
67
  A slug such as concepts/rag names an artifact within its project, or within
65
68
  the unfiled artifacts without --project. Read --slug finds it without its ID.
69
+ Create or move fails with slug_conflict when the project already uses the slug.
66
70
  Read --out saves the returned artifact JSON. Export --out saves the ZIP.
67
71
  Read --quiet requires --out and suppresses successful JSON stdout.
68
72
  Output files are created exclusively and never overwrite an existing path.
@@ -96,7 +100,7 @@ const commandOptions = {
96
100
  edit: ['changes', 'request-id'], history: ['limit', 'before-sequence', 'summary'],
97
101
  publish: ['revision', 'expected-version', 'request-id', 'command'],
98
102
  share: ['email', 'notify', 'request-id'], shares: [], unshare: ['request-id'],
99
- export: ['revision', 'out'], move: ['project', 'no-project', 'request-id'], retry: [],
103
+ export: ['revision', 'out'], move: ['project', 'no-project', 'slug', 'no-slug', 'request-id'], retry: [],
100
104
  skills: ['full', 'all', 'agent', 'global', 'force'],
101
105
  };
102
106
  async function jsonFile(path) {
@@ -133,7 +137,7 @@ async function main() {
133
137
  summary: { type: 'boolean' }, out: { type: 'string' }, quiet: { type: 'boolean' },
134
138
  full: { type: 'boolean' }, all: { type: 'boolean' }, agent: { type: 'string' },
135
139
  global: { type: 'boolean' }, force: { type: 'boolean' },
136
- project: { type: 'string' }, 'no-project': { type: 'boolean' }, slug: { type: 'string' }, email: { type: 'string' }, notify: { type: 'boolean' },
140
+ project: { type: 'string' }, 'no-project': { type: 'boolean' }, slug: { type: 'string' }, 'no-slug': { type: 'boolean' }, email: { type: 'string' }, notify: { type: 'boolean' },
137
141
  },
138
142
  });
139
143
  const seen = new Set();
@@ -198,6 +202,11 @@ async function main() {
198
202
  throw new UsageError(`--${name} requires a nonnegative safe integer`);
199
203
  return Number(value);
200
204
  }
205
+ function cleared(name, value, clear) {
206
+ if (value !== undefined && clear)
207
+ throw new UsageError(`Specify at most one of --${name} or --no-${name}`);
208
+ return clear ? null : value;
209
+ }
201
210
  const response = values.response;
202
211
  if (response !== undefined && response !== 'full' && response !== 'compact')
203
212
  throw new UsageError('--response requires full or compact');
@@ -291,9 +300,11 @@ async function main() {
291
300
  }
292
301
  case 'move': {
293
302
  const artifactId = argument();
294
- if ((values.project !== undefined) === (values['no-project'] ?? false))
295
- throw new UsageError('Specify exactly one of --project NAME or --no-project');
296
- return mutate({ kind: 'move', command: moveSchema.parse({ artifactId, requestId: values['request-id'] ?? randomUUID(), project: values.project ?? null }) });
303
+ const project = cleared('project', values.project, values['no-project']);
304
+ const slug = cleared('slug', values.slug, values['no-slug']);
305
+ if (project === undefined && slug === undefined)
306
+ throw new UsageError('move requires --project NAME, --no-project, --slug SLUG, or --no-slug');
307
+ return mutate({ kind: 'move', command: moveSchema.parse({ artifactId, requestId: values['request-id'] ?? randomUUID(), ...(project === undefined ? {} : { project }), ...(slug === undefined ? {} : { slug }) }) });
297
308
  }
298
309
  case 'share': return mutate({ kind: 'share', command: shareSchema.parse({ artifactId: argument(), requestId: values['request-id'] ?? randomUUID(), email: required('email'), ...(values.notify === undefined ? {} : { notify: values.notify }) }) });
299
310
  case 'shares': return client.listShares(listSharesSchema.parse({ artifactId: argument() }));
@@ -95,7 +95,8 @@ export declare const operationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
95
95
  command: z.ZodObject<{
96
96
  requestId: z.ZodString;
97
97
  artifactId: z.ZodString;
98
- project: z.ZodNullable<z.ZodString>;
98
+ project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
99
+ slug: z.ZodOptional<z.ZodNullable<z.ZodString>>;
99
100
  }, z.core.$strict>;
100
101
  version: z.ZodLiteral<1>;
101
102
  resource: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;