kiriya 0.1.1 → 0.1.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
@@ -7,6 +7,20 @@ output shapes are kiriya's public API.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.2] - 2026-09-24
11
+
12
+ ### Changed
13
+
14
+ - An option that takes one of a list of values now declares that list, so an MCP tool
15
+ offers it as a JSON Schema `enum` instead of naming it only in prose an agent would have
16
+ to read. Eleven options gained one: `files list --sort`, `files find --type`,
17
+ `files hash --algo`, `files info --hash`, `files rename --case` and `--only`,
18
+ `convert case --to`, `convert time --unit`, `gen token --format` and `net dns --type`.
19
+ Help and the generated reference are unchanged, since a declared list names itself.
20
+ - Tab completion reads the same list, rather than parsing the value name it was displayed
21
+ under. `docker logs --tail <n|all>` therefore offers nothing instead of offering `n`,
22
+ which was never a value it took.
23
+
10
24
  ## [0.1.1] - 2026-09-24
11
25
 
12
26
  ### Changed
@@ -85,7 +85,8 @@ function wordmark(context) {
85
85
  }
86
86
  /** `-i, --ignore-case` or `--to <file>`, as help and the generated docs show an option. */
87
87
  export function optionLabel(name, spec) {
88
- const value = spec.type === "string" ? ` ${spec.valueName ?? "<value>"}` : "";
88
+ const named = spec.valueName ?? (spec.choices === undefined ? "<value>" : `<${spec.choices.join("|")}>`);
89
+ const value = spec.type === "string" ? ` ${named}` : "";
89
90
  const short = spec.short === undefined ? "" : `-${spec.short}, `;
90
91
  return `${short}--${name}${value}`;
91
92
  }
@@ -65,13 +65,15 @@ export function inputSchemaOf(commandId, input, translator, access = READING) {
65
65
  }
66
66
  for (const [name, option] of toolOptions(input, access)) {
67
67
  const text = translator.text(message(option.description));
68
+ // Choices belong in the enum, where a client can act on them, rather than in prose it must read.
68
69
  const description = option.valueName === undefined ? text : `${text} ${option.valueName}`;
70
+ const choices = option.choices === undefined ? {} : { enum: [...option.choices] };
69
71
  if (option.type === "boolean")
70
72
  claim(name, { type: "boolean", description });
71
73
  else if (option.multiple === true)
72
- claim(name, { type: "array", items: { type: "string" }, description });
74
+ claim(name, { type: "array", items: { type: "string", ...choices }, description });
73
75
  else
74
- claim(name, { type: "string", description });
76
+ claim(name, { type: "string", ...choices, description });
75
77
  }
76
78
  return { type: "object", properties, ...(required.length > 0 ? { required } : {}), additionalProperties: false };
77
79
  }
@@ -1,7 +1,11 @@
1
- /** The values an option lists in its value name, such as `<hex|base64|base64url>`. */
1
+ /**
2
+ * The values an option takes, when it takes only a list of them. It reads the list the
3
+ * command declares, rather than parsing the value name it is displayed under, so an option
4
+ * whose value name merely looks like a list — `--tail <n|all>`, a number or a word — offers
5
+ * nothing instead of offering `n`.
6
+ */
2
7
  export function optionChoices(option) {
3
- const match = /^<([a-z0-9-]+(?:\|[a-z0-9-]+)+)>$/.exec(option.valueName ?? "");
4
- return match?.[1]?.split("|") ?? [];
8
+ return option.choices === undefined ? [] : [...option.choices];
5
9
  }
6
10
  function ownCommand(module) {
7
11
  return module.commands.find((command) => command.verb === "");
@@ -11,7 +11,7 @@ export const caseSpec = {
11
11
  input: {
12
12
  positionals: [valuePositional],
13
13
  options: {
14
- to: { type: "string", description: "convert.case.option.to", valueName: `<${TEXT_CASES.join("|")}>` },
14
+ to: { type: "string", description: "convert.case.option.to", choices: TEXT_CASES },
15
15
  file: fileOption,
16
16
  },
17
17
  parse(raw) {
@@ -16,7 +16,7 @@ export const timeSpec = {
16
16
  idempotent: false,
17
17
  input: {
18
18
  positionals: [{ name: "value", description: "convert.time.arg.value", required: false, variadic: false }],
19
- options: { unit: { type: "string", description: "convert.time.option.unit", valueName: "<auto|seconds|ms>" } },
19
+ options: { unit: { type: "string", description: "convert.time.option.unit", choices: EPOCH_UNITS } },
20
20
  parse(raw) {
21
21
  const reader = new RawReader(raw);
22
22
  return { value: reader.positional(0), unit: reader.choice("unit", EPOCH_UNITS, "auto") };
@@ -26,7 +26,7 @@ export const findSpec = {
26
26
  options: {
27
27
  name: { type: "string", description: "files.find.option.name", valueName: "<glob>" },
28
28
  ext: { type: "string", description: "files.find.option.ext", valueName: "<extensions>", multiple: true },
29
- type: { type: "string", description: "files.find.option.type", valueName: "<file|dir>" },
29
+ type: { type: "string", description: "files.find.option.type", choices: FIND_TYPES },
30
30
  larger: { type: "string", description: "files.find.option.larger", valueName: "<size>" },
31
31
  smaller: { type: "string", description: "files.find.option.smaller", valueName: "<size>" },
32
32
  newer: { type: "string", description: "files.find.option.newer", valueName: "<time>" },
@@ -19,7 +19,7 @@ export const hashSpec = {
19
19
  input: {
20
20
  positionals: [{ name: "paths", description: "files.hash.arg.paths", required: true, variadic: true, path: true }],
21
21
  options: {
22
- algo: { type: "string", description: "files.hash.option.algo", valueName: `<${HASH_ALGORITHMS.join("|")}>` },
22
+ algo: { type: "string", description: "files.hash.option.algo", choices: HASH_ALGORITHMS },
23
23
  check: { type: "string", description: "files.hash.option.check", valueName: "<hex>" },
24
24
  },
25
25
  parse(raw) {
@@ -16,7 +16,7 @@ export const listSpec = {
16
16
  positionals: [{ name: "path", description: "files.list.arg.path", required: false, variadic: false, path: true }],
17
17
  options: {
18
18
  all: { type: "boolean", description: "files.option.all" },
19
- sort: { type: "string", description: "files.list.option.sort", valueName: "<name|size|time>" },
19
+ sort: { type: "string", description: "files.list.option.sort", choices: LIST_SORTS },
20
20
  reverse: { type: "boolean", description: "files.list.option.reverse" },
21
21
  },
22
22
  parse(raw) {
@@ -27,11 +27,11 @@ export const renameSpec = {
27
27
  { name: "paths", description: "files.rename.arg.paths", required: false, variadic: true, path: true },
28
28
  ],
29
29
  options: {
30
- case: { type: "string", description: "files.rename.option.case", valueName: `<${CASE_STYLES.join("|")}>` },
30
+ case: { type: "string", description: "files.rename.option.case", choices: CASE_STYLES },
31
31
  find: { type: "string", description: "files.rename.option.find", valueName: "<text>" },
32
32
  with: { type: "string", description: "files.rename.option.with", valueName: "<text>" },
33
33
  recursive: { type: "boolean", description: "files.rename.option.recursive" },
34
- only: { type: "string", description: "files.rename.option.only", valueName: "<files|dirs>" },
34
+ only: { type: "string", description: "files.rename.option.only", choices: ONLY },
35
35
  apply: { type: "boolean", description: "files.rename.option.apply" },
36
36
  yes: { type: "boolean", description: "files.rename.option.yes", short: "y", terminalOnly: true },
37
37
  },
@@ -28,7 +28,7 @@ export const infoSpec = {
28
28
  input: {
29
29
  positionals: [{ name: "paths", description: "files.info.arg.paths", required: true, variadic: true, path: true }],
30
30
  options: {
31
- hash: { type: "string", description: "files.info.option.hash", valueName: `<${HASH_ALGORITHMS.join("|")}>` },
31
+ hash: { type: "string", description: "files.info.option.hash", choices: HASH_ALGORITHMS },
32
32
  },
33
33
  parse(raw) {
34
34
  const reader = new RawReader(raw);
@@ -11,7 +11,7 @@ export const tokenSpec = {
11
11
  positionals: [],
12
12
  options: {
13
13
  bytes: { type: "string", description: "gen.token.option.bytes", valueName: "<n>" },
14
- format: { type: "string", description: "gen.token.option.format", valueName: "<hex|base64|base64url>" },
14
+ format: { type: "string", description: "gen.token.option.format", choices: TOKEN_FORMATS },
15
15
  count: countOption,
16
16
  },
17
17
  parse(raw) {
@@ -16,7 +16,7 @@ export const dnsSpec = {
16
16
  input: {
17
17
  positionals: [{ name: "name", description: "net.dns.arg.name", required: true, variadic: false }],
18
18
  options: {
19
- type: { type: "string", description: "net.dns.option.type", valueName: `<${LOOKUP_TYPES.join("|")}>` },
19
+ type: { type: "string", description: "net.dns.option.type", choices: LOOKUP_TYPES },
20
20
  },
21
21
  parse(raw) {
22
22
  const reader = new RawReader(raw);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kiriya",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "One command-line toolbox for everyday developer work that behaves the same on Windows, Linux and macOS.",
5
5
  "mcpName": "io.github.SatPaingOo/kiriya",
6
6
  "license": "MIT",