import type { z } from 'zod'; import type { Result } from '../../domain/result.js'; import type { GraphClient } from '../../infra/graph-client.js'; type CommandSchema = z.ZodType; type CommandExecute = (graph: GraphClient, params: Record) => Promise>; type CommandCategory = 'drive' | 'excel' | 'sharepoint' | 'tasks' | 'mail' | 'notes' | 'user' | 'calendar' | 'chats' | 'teams' | 'meta' | 'lifecycle'; type CommandHttpMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE'; type CommandOptionAlias = { readonly name: string; readonly key: string; }; /** * Structured type-hint for a CLI flag value. Surfaces in `help-json` so an * LLM can avoid the trial-and-error of "is this an ID or a name?" prose * reading. Optional — populate only where the hint is non-obvious from the * flag name itself. */ type ArgumentHint = { readonly kind: 'idOrName'; } | { readonly kind: 'magicValue'; readonly values: ReadonlyArray; } | { readonly kind: 'a1Address'; } | { readonly kind: 'iso8601'; } | { readonly kind: 'graphSubpath'; }; type CommandOptionMeta = { readonly name: string; readonly key: string; readonly description: string; /** * `true` for required flags (the historical default; commander rejects the * invocation if the flag is missing). `false` for optional flags such as the * OData passthrough query parameters (`--top`, `--filter`, …) which * commands accept but do not demand. */ readonly required: boolean; /** * Optional secondary spellings of the same flag. Both the canonical * `name` and every alias name are accepted on the command line; values * passed under an alias are normalized to the canonical `key` before * the schema runs. The canonical name is what `--help` shows first. */ readonly aliases?: ReadonlyArray; /** * Structured value-type hint for LLM consumers. Optional. */ readonly argumentHint?: ArgumentHint; }; /** * A positional argument (i.e. NOT a `--flag`). Used today only for the * `docs` lifecycle command (`ask-marcel docs `) but kept as its * own field so the manifest never claims a positional is a flag. An LLM * consumer reading `help-json` can branch on the presence of * `positionalArguments` to know to skip the `--` prefix. */ type CommandPositionalArgumentMeta = { readonly name: string; readonly required: boolean; readonly description: string; }; /** * How a paginated command produces subsequent pages. Optional — populate * for any command that has `pagination: true`. Lets an LLM tell which * cursor field to feed back to `next-page` (or whether `next-page` is even * applicable, vs `deltaLink`, vs the header-translation case). */ type PaginationStrategy = /** Standard: `?$top=N&$skip=K` + `@odata.nextLink` cursor. */ 'nextLink' /** `?$top=N` + `nextLink` (Graph rejects `$skip` on this endpoint). */ | 'nextLinkNoSkip' /** Delta endpoints — `nextLink` while paging, `deltaLink` on final page. */ | 'deltaLink' /** `--top` translated to `Prefer: odata.maxpagesize` header; `$top` rejected as query. */ | 'preferMaxPageSize'; type CommandMeta = { readonly summary: string; readonly category: CommandCategory; readonly graphMethod: CommandHttpMethod; readonly graphPathTemplate: string; readonly graphDocsUrl: string; readonly options: ReadonlyArray; readonly positionalArguments?: ReadonlyArray; readonly example: string; readonly responseShape?: string; readonly bodyTemplate?: string; readonly pagination?: true; readonly paginationStrategy?: PaginationStrategy; /** * Graph permission scopes the endpoint requires. The basic Teams web-client * token grants ~30 scopes (run `ask-marcel scopes-check` to see). Commands * with unmet scopes return `403 Forbidden: Missing scope` at the wire. Use * this for pre-flight checks rather than failing on-the-wire. Optional — * populated only on commands where the audit confirmed a scope-failure * path; absent means "should work with the basic Teams token". */ readonly scopesRequired?: ReadonlyArray; /** * `true` if the command needs the M365ChatClient elevated token (captured * at login from `m365.cloud.microsoft`, ODSP allow-list). Only 3 commands * today — the historical-version downloads. An LLM should check this * field before invoking; if the elevated capture failed at login, these * commands will time out. */ readonly needsElevatedToken?: true; /** * `true` if the command returns inlined bytes (`{contentType, size, base64}` * or `{contentType, size, text}`) and is therefore a valid target for the * global `--output-path` flag. Used by the CLI composition to derive the * rejection-message whitelist from the manifest rather than hand-keeping it * as a string literal. Audit round-8 Wave E2. */ readonly producesBytes?: true; /** * Stability tier of the command. Omitted from manifest entries when the * command is `'stable'` (the implicit default), surfaced only on * `'experimental'` commands so an LLM can prefer stable siblings when they * exist. `'experimental'` today means the command rides a Microsoft-internal * substrate (chatsvcagg / IC3) that is not in the public Graph API and can * break on a Teams web-client update — the docstring "Best-effort, may break * on Microsoft client updates" warnings now have a structured pair. * Audit Hervé-session §6. */ readonly stability?: 'experimental'; }; type Command = { readonly schema: CommandSchema; readonly execute: CommandExecute; readonly meta: CommandMeta; }; export type { ArgumentHint, Command, CommandCategory, CommandExecute, CommandHttpMethod, CommandMeta, CommandOptionAlias, CommandOptionMeta, CommandPositionalArgumentMeta, CommandSchema, PaginationStrategy, };