@opvs-ai/cli 0.22.0 → 0.24.1

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 (46) hide show
  1. package/README.md +73 -0
  2. package/claude-plugin/.claude-plugin/marketplace.json +14 -0
  3. package/claude-plugin/.claude-plugin/plugin.json +9 -0
  4. package/claude-plugin/.mcp.json +8 -0
  5. package/claude-plugin/README.md +24 -0
  6. package/claude-plugin/hooks/hooks.json +50 -0
  7. package/claude-plugin/skills/remember/SKILL.md +31 -0
  8. package/claude-plugin/skills/remember/scripts/remember.mjs +121 -0
  9. package/dist/.build-stamp.json +5 -5
  10. package/dist/commands/code-graph.d.ts +17 -0
  11. package/dist/commands/code-graph.js +64 -0
  12. package/dist/commands/code-graph.js.map +1 -0
  13. package/dist/commands/code.d.ts +13 -3
  14. package/dist/commands/code.js +191 -3
  15. package/dist/commands/code.js.map +1 -1
  16. package/dist/commands/memory-capture.d.ts +76 -0
  17. package/dist/commands/memory-capture.js +562 -0
  18. package/dist/commands/memory-capture.js.map +1 -0
  19. package/dist/commands/memory-hook.d.ts +21 -0
  20. package/dist/commands/memory-hook.js +138 -0
  21. package/dist/commands/memory-hook.js.map +1 -0
  22. package/dist/commands/memory-install.d.ts +17 -0
  23. package/dist/commands/memory-install.js +349 -0
  24. package/dist/commands/memory-install.js.map +1 -0
  25. package/dist/commands/memory-lessons.d.ts +8 -0
  26. package/dist/commands/memory-lessons.js +135 -0
  27. package/dist/commands/memory-lessons.js.map +1 -0
  28. package/dist/commands/memory.d.ts +24 -0
  29. package/dist/commands/memory.js +239 -0
  30. package/dist/commands/memory.js.map +1 -0
  31. package/dist/commands/myMail.d.ts +3 -0
  32. package/dist/commands/myMail.js +119 -0
  33. package/dist/commands/myMail.js.map +1 -0
  34. package/dist/commands/orgChart.d.ts +3 -0
  35. package/dist/commands/orgChart.js +88 -0
  36. package/dist/commands/orgChart.js.map +1 -0
  37. package/dist/commands/tasks.js +45 -3
  38. package/dist/commands/tasks.js.map +1 -1
  39. package/dist/index.js +8 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/lib/redact.d.ts +16 -0
  42. package/dist/lib/redact.js +92 -0
  43. package/dist/lib/redact.js.map +1 -0
  44. package/package.json +3 -1
  45. package/runtime-plugins/cline/opvs-memory.ts +99 -0
  46. package/runtime-plugins/opencode/opvs-memory.ts +94 -0
package/README.md CHANGED
@@ -288,6 +288,79 @@ Needs the `code:read` scope, which is in the default ceiling. There is no
288
288
  `opvs code index` command on purpose — writing an index is `code:write`, which
289
289
  a worker PAT does not carry, and the ingest caller is the sidecar on the desk.
290
290
 
291
+ ### Skill lessons
292
+
293
+ Pull and contribute the lessons a skill's collectors left in the typed marketplace
294
+ (AS-590). The reference is `<vendor>/<name>` (a leading `@` on the vendor is
295
+ optional); the CLI validates it locally before any request.
296
+
297
+ ```bash
298
+ opvs memory lessons get opvs-ai/opvs-vibe # Pull a skill's lessons
299
+ opvs memory lessons get opvs-ai/opvs-vibe --query retry # FTS over body + tags
300
+ opvs memory lessons get opvs-ai/opvs-vibe --limit 50 --since cur-1
301
+
302
+ opvs memory lessons add opvs-ai/opvs-vibe \
303
+ --kind learning \
304
+ --body "Retry on 429 with jitter; never under 200ms"
305
+
306
+ opvs memory lessons add opvs-ai/opvs-vibe \
307
+ --kind pattern --body-file ./note.md --tags "retry,http"
308
+ ```
309
+
310
+ `add` ships five keys exactly — `kind`, `body`, `tags`, `source_engagement_hash`,
311
+ `craft_id` — because the server-side `CraftContribute` schema sets
312
+ `extra="forbid"` (AS-445 sealed anything that looked like a credential). The
313
+ provenance hash is `sha256("cli:" + workspace_slug + ":" + body)` and NEVER
314
+ carries a brand id.
315
+
316
+ ### Runtime memory hooks
317
+
318
+ Install the SessionStart memory hook for Codex, the complete checked-in plugin
319
+ hook set for Claude Code, the OPVS MCP read path and capture hooks for Cursor,
320
+ or a managed capture plugin for OpenCode or Cline. Existing JSON entries are
321
+ merged and repeated installs are no-ops.
322
+
323
+ ```bash
324
+ opvs memory install codex
325
+ opvs memory install claude-code
326
+ opvs memory install cursor
327
+ opvs memory install opencode
328
+ opvs memory install cline
329
+ opvs memory install codex --project # writes ./.codex/hooks.json
330
+ opvs memory install cursor --project # writes ./.cursor/{mcp,hooks}.json
331
+ opvs memory install opencode --project # writes ./.opencode/plugins/opvs-memory.ts
332
+ opvs memory uninstall codex # removes only OPVS memory hooks
333
+ opvs memory uninstall cursor # removes only OPVS MCP/hooks entries
334
+ opvs memory uninstall cline # removes only the managed OPVS plugin
335
+ opvs memory uninstall codex --project
336
+ ```
337
+
338
+ User-level Codex hooks use `$CODEX_HOME/hooks.json` (default
339
+ `~/.codex/hooks.json`). User-level Claude Code hooks use
340
+ `$CLAUDE_CONFIG_DIR/settings.json` (default `~/.claude/settings.json`), with
341
+ the plugin hook set merged under its `hooks` key. `--project` uses
342
+ `./.codex/hooks.json` or `./.claude/settings.json` in the current project.
343
+ Installing the bundled Claude Code plugin through Claude Code's plugin
344
+ installer is an alternative to `opvs memory install claude-code`; plugins load
345
+ their own `hooks/hooks.json` as part of plugin installation.
346
+
347
+ Cursor uses `~/.cursor/mcp.json` and `~/.cursor/hooks.json`, or the matching
348
+ project files with `--project`. The MCP entry starts `opvs-mcp`, which provides
349
+ the OPVS memory read tools. The installed `postToolUse`, `stop`, and
350
+ `sessionEnd` hooks feed the same local capture pipeline as Claude Code.
351
+
352
+ OpenCode installs `opvs-memory.ts` under
353
+ `~/.config/opencode/plugins/` (or `./.opencode/plugins/` with `--project`).
354
+ Cline installs it under `~/.cline/plugins/` (or `./.cline/plugins/` with
355
+ `--project`). Each file is marked as OPVS-managed, so uninstall will not remove
356
+ an unmarked file or any other plugin in the directory. The plugins forward
357
+ tool-after and idle/run-end events to `opvs memory hook` with a two-second
358
+ timeout and fail open. They only upload capture data after the user explicitly
359
+ runs `opvs memory capture on`.
360
+ Installing hooks does **not** enable capture: run `opvs memory capture on` with
361
+ a dedicated PAT whose only scope is `memory:ingest`, and use
362
+ `opvs memory capture off` to remove that local PAT again.
363
+
291
364
  ### Session
292
365
 
293
366
  ```bash
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "opvs",
3
+ "description": "OPVS plugins for agent workflows",
4
+ "owner": {
5
+ "name": "OPVS"
6
+ },
7
+ "plugins": [
8
+ {
9
+ "name": "opvs-memory",
10
+ "source": "./",
11
+ "description": "Read relevant typed memories and record new lessons from Claude Code."
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "opvs-memory",
3
+ "displayName": "OPVS Memory",
4
+ "version": "0.1.0",
5
+ "description": "Loads relevant typed memories at session start and provides memory tools and a remember workflow.",
6
+ "author": {
7
+ "name": "OPVS"
8
+ }
9
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "opvs": {
4
+ "command": "npx",
5
+ "args": ["-y", "@opvs-ai/mcp"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,24 @@
1
+ # OPVS Memory for Claude Code
2
+
3
+ This plugin loads relevant typed memories when a Claude Code session starts, exposes the OPVS memory MCP tools, and provides the `/opvs-memory:remember` skill for recording a validated lesson in the current repository.
4
+
5
+ ## Prerequisites
6
+
7
+ - Install and authenticate the OPVS CLI (`opvs auth request ...`).
8
+ - Use a PAT with `memory:read` scope.
9
+ - Have Node.js available so Claude Code can launch `@opvs-ai/mcp`.
10
+
11
+ ## Install from this checkout
12
+
13
+ From the repository root:
14
+
15
+ ```sh
16
+ claude plugin marketplace add ./packages/cli/claude-plugin
17
+ claude plugin install opvs-memory@opvs
18
+ ```
19
+
20
+ Start a new Claude Code session after installation. The SessionStart hook calls `opvs memory hook session-start`; when memory is unavailable or the CLI is not logged in, it stays silent and does not block startup.
21
+
22
+ Use `/opvs-memory:remember` to record a lesson. The skill follows `memory/SPEC.md`, writes with exclusive-create semantics, and refuses to overwrite an existing entry.
23
+
24
+ The plugin starts the published MCP server with `npx -y @opvs-ai/mcp`. The server reads the same local OPVS authentication as the CLI; no credential is stored in this plugin.
@@ -0,0 +1,50 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume|clear|compact|fork",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "opvs memory hook session-start",
10
+ "timeout": 10
11
+ }
12
+ ]
13
+ }
14
+ ],
15
+ "PostToolUse": [
16
+ {
17
+ "matcher": "*",
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "opvs memory hook post-tool",
22
+ "timeout": 10
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "Stop": [
28
+ {
29
+ "hooks": [
30
+ {
31
+ "type": "command",
32
+ "command": "opvs memory hook stop",
33
+ "timeout": 10
34
+ }
35
+ ]
36
+ }
37
+ ],
38
+ "SessionEnd": [
39
+ {
40
+ "hooks": [
41
+ {
42
+ "type": "command",
43
+ "command": "opvs memory hook session-end",
44
+ "timeout": 10
45
+ }
46
+ ]
47
+ }
48
+ ]
49
+ }
50
+ }
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: remember
3
+ description: Record a durable, typed lesson in the repository's registered service planning folder.
4
+ argument-hint: "<lesson to remember>"
5
+ disable-model-invocation: true
6
+ allowed-tools: Read, Grep, Bash
7
+ ---
8
+
9
+ # Remember a lesson
10
+
11
+ Use this workflow only for a durable lesson supported by evidence. Do not use it for a preference, guess, secret, credential, or temporary task state.
12
+
13
+ 1. Read `memory/SPEC.md` sections 2, 3, and 5 and `memory/services.yaml` in the current repository.
14
+ 2. Identify the registered service whose `planning_dir` owns the lesson. Use that service id and planning directory; do not invent either value.
15
+ 3. Turn the lesson into one standalone core line. Collect the required `evidence`, runnable `verify`, expected result, and a control that makes verification fail. Ask the user for any required value that cannot be established from the session.
16
+ 4. Run the writer below from the repository root. Pass the active card ref as `--card`; if there is no card, explain that this writer requires one and stop. Quote every value as a separate shell argument.
17
+
18
+ ```sh
19
+ node "${CLAUDE_PLUGIN_ROOT}/skills/remember/scripts/remember.mjs" \
20
+ --service "<service-id>" \
21
+ --planning-dir "<registered-planning-dir>" \
22
+ --card "<CARD-123>" \
23
+ --core "<one-line lesson>" \
24
+ --topic "<short topic>" \
25
+ --evidence "<observed evidence>" \
26
+ --verify "<SPEC-compliant command>" \
27
+ --expect "<SPEC-compliant expectation>" \
28
+ --control "<change that makes verification fail>"
29
+ ```
30
+
31
+ The writer derives the UTC date and branch slug, creates one file with exclusive-create semantics, and prints its relative path. Read the generated file and check its header, routing line, and core line before reporting success. Never overwrite, move, or delete an existing memory entry.
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from "node:child_process";
3
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
4
+ import { isAbsolute, relative, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ function required(value, name) {
8
+ if (typeof value !== "string" || value.trim() === "") {
9
+ throw new Error(`${name} is required`);
10
+ }
11
+ return value.trim();
12
+ }
13
+
14
+ function oneLine(value, name) {
15
+ const checked = required(value, name);
16
+ if (/\r|\n/.test(checked)) throw new Error(`${name} must be one line`);
17
+ return checked;
18
+ }
19
+
20
+ function slug(value) {
21
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
22
+ }
23
+
24
+ function yamlString(value) {
25
+ return JSON.stringify(value);
26
+ }
27
+
28
+ export function buildLesson(input) {
29
+ const date = required(input.date, "date");
30
+ const card = required(input.card, "card");
31
+ const service = required(input.service, "service");
32
+ const branchSlug = slug(required(input.branch, "branch"));
33
+ const core = oneLine(input.core, "core");
34
+ const topic = slug(required(input.topic, "topic"));
35
+ const evidence = oneLine(input.evidence, "evidence");
36
+ const verify = oneLine(input.verify, "verify");
37
+ const expect = oneLine(input.expect, "expect");
38
+ const control = oneLine(input.control, "control");
39
+ const confidence = input.confidence ?? 0.9;
40
+
41
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) throw new Error("date must be YYYY-MM-DD");
42
+ if (!/^[A-Z][A-Z0-9]*-\d+$/.test(card)) throw new Error("card must look like ABC-123");
43
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(service)) {
44
+ throw new Error("service must be a lower-case service id");
45
+ }
46
+ if (!branchSlug) throw new Error("branch must produce a slug");
47
+ if (typeof confidence !== "number" || confidence < 0 || confidence > 1) {
48
+ throw new Error("confidence must be between 0 and 1");
49
+ }
50
+
51
+ const stem = `${date}-${card}-${branchSlug}`;
52
+ const filename = `${stem}.md`;
53
+ const detail = typeof input.detail === "string" && input.detail.trim()
54
+ ? `\n\n${input.detail.trim()}\n`
55
+ : "\n";
56
+ const text = `---
57
+ id: ${service}/lesson/${stem}
58
+ type: lesson
59
+ author: session:${card}
60
+ card: ${card}
61
+ evidence: ${yamlString(evidence)}
62
+ verify: ${yamlString(verify)}
63
+ expect: ${yamlString(expect)}
64
+ control: ${yamlString(control)}
65
+ ---
66
+ @${service}>${service} . #lrn #${topic} $f:${confidence} $t:${date}
67
+ ${core}${detail}`;
68
+
69
+ return { filename, text };
70
+ }
71
+
72
+ export function writeLesson(input, options = {}) {
73
+ const cwd = resolve(options.cwd ?? process.cwd());
74
+ const planningDir = required(input.planningDir, "planning-dir");
75
+ if (isAbsolute(planningDir) || planningDir.split(/[\\/]+/).includes("..")) {
76
+ throw new Error("planning-dir must be a repository-relative path without '..'");
77
+ }
78
+ if (!planningDir.startsWith("docs/planning/")) {
79
+ throw new Error("planning-dir must name a registered docs/planning service folder");
80
+ }
81
+ const planningRoot = resolve(cwd, planningDir);
82
+ if (!existsSync(planningRoot) || relative(cwd, planningRoot).startsWith("..")) {
83
+ throw new Error("planning-dir must be an existing directory inside the repository");
84
+ }
85
+
86
+ const date = options.date ?? new Date().toISOString().slice(0, 10);
87
+ const branch = options.branch ?? execFileSync(
88
+ "git",
89
+ ["branch", "--show-current"],
90
+ { cwd, encoding: "utf8" },
91
+ ).trim();
92
+ const rendered = buildLesson({ ...input, date, branch });
93
+ const lessonsDir = resolve(planningRoot, "learnings");
94
+ mkdirSync(lessonsDir, { recursive: true });
95
+ const target = resolve(lessonsDir, rendered.filename);
96
+ writeFileSync(target, rendered.text, { encoding: "utf8", flag: "wx" });
97
+ return relative(cwd, target);
98
+ }
99
+
100
+ function parseArgs(argv) {
101
+ const values = {};
102
+ for (let index = 0; index < argv.length; index += 2) {
103
+ const flag = argv[index];
104
+ const value = argv[index + 1];
105
+ if (!flag?.startsWith("--") || value === undefined) {
106
+ throw new Error("arguments must be --name value pairs");
107
+ }
108
+ values[flag.slice(2).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())] = value;
109
+ }
110
+ return values;
111
+ }
112
+
113
+ if (process.argv[1] && resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url))) {
114
+ try {
115
+ const output = writeLesson(parseArgs(process.argv.slice(2)));
116
+ process.stdout.write(`${output}\n`);
117
+ } catch (error) {
118
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
119
+ process.exitCode = 1;
120
+ }
121
+ }
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "stamp_version": 1,
3
3
  "package": "@opvs-ai/cli",
4
- "version": "0.22.0",
5
- "source_digest": "688f710405153fa8948da5155d608cc39119da7c91d1647a34b8665f7df5b3ce",
6
- "source_files": 45,
7
- "built_at": "2026-09-30T19:14:41.962Z",
8
- "node": "v22.23.2"
4
+ "version": "0.24.1",
5
+ "source_digest": "60073109ec4e93c4fdd6612c101b4ff3599bfec06d9959c2b51cf95882888409",
6
+ "source_files": 54,
7
+ "built_at": "2026-10-03T05:52:09.611Z",
8
+ "node": "v22.23.3"
9
9
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * opvs code graph — a node's import neighbourhood, built against the agreed route shape
3
+ * (TCI-113 · design §7).
4
+ *
5
+ * The CLI does not interpret the answer. It builds the URL the API contract describes
6
+ * (`GET /api/v1/code/{project_id}/graph`) and lets `printResponse` write the server's
7
+ * bytes — including a 422 when the server rejects `depth` or `limit`. That parity is
8
+ * what makes the "CLI and HTTP return the same YAML" claim testable rather than
9
+ * aspirational, the same property the `context` and `summary` subcommands have.
10
+ *
11
+ * ⚠️ Registration precondition. The `code` parent command must already be attached to
12
+ * the program. A silent no-op would ship a CLI without the command — the whole point
13
+ * of this file is to register `graph`, so a missing parent is a programmer error and
14
+ * we throw.
15
+ */
16
+ import { Command } from "commander";
17
+ export declare function registerCodeGraphCommand(program: Command): void;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * opvs code graph — a node's import neighbourhood, built against the agreed route shape
3
+ * (TCI-113 · design §7).
4
+ *
5
+ * The CLI does not interpret the answer. It builds the URL the API contract describes
6
+ * (`GET /api/v1/code/{project_id}/graph`) and lets `printResponse` write the server's
7
+ * bytes — including a 422 when the server rejects `depth` or `limit`. That parity is
8
+ * what makes the "CLI and HTTP return the same YAML" claim testable rather than
9
+ * aspirational, the same property the `context` and `summary` subcommands have.
10
+ *
11
+ * ⚠️ Registration precondition. The `code` parent command must already be attached to
12
+ * the program. A silent no-op would ship a CLI without the command — the whole point
13
+ * of this file is to register `graph`, so a missing parent is a programmer error and
14
+ * we throw.
15
+ */
16
+ import { getEffectiveConfig, createClient } from "@opvs-ai/core";
17
+ import { buildUrl, printResponse } from "../lib/format.js";
18
+ export function registerCodeGraphCommand(program) {
19
+ const code = program.commands.find((c) => c.name() === "code");
20
+ if (!code) {
21
+ throw new Error("registerCodeGraphCommand: 'code' command is not registered — call registerCodeCommands(program) first");
22
+ }
23
+ code
24
+ .command("graph <node_id>")
25
+ .description("A node's import neighbourhood — who imports it, what it imports, one or two hops")
26
+ .requiredOption("--project <id>", "Project id (board_groups.id where kind=project)")
27
+ .option("--direction <dir>", "in | out | both — which side of <node_id> to walk from")
28
+ .option("--depth <n>", "1 | 2 — server rejects 3+ with a 422")
29
+ .option("--limit <n>", "Max neighbours (server default 100, clamps at 500)")
30
+ .option("--branch <name>", "Answer from a specific indexed branch")
31
+ .option("--format <fmt>", "yaml | md | json — overrides the configured default")
32
+ .action(async (nodeId, opts) => {
33
+ const wsSlug = program.opts().workspace;
34
+ const cfg = getEffectiveConfig(wsSlug);
35
+ try {
36
+ const client = createClient(wsSlug);
37
+ // Send ONLY the options the user set. A `direction: undefined` would become
38
+ // `direction=undefined` on the wire and the server would read it as a real
39
+ // filter — the same `code context` defect (TCI-12) this card inherits.
40
+ const params = { node_id: nodeId };
41
+ if (opts.direction)
42
+ params.direction = opts.direction;
43
+ if (opts.depth)
44
+ params.depth = opts.depth;
45
+ if (opts.limit)
46
+ params.limit = opts.limit;
47
+ if (opts.branch)
48
+ params.branch = opts.branch;
49
+ // The server is the judge of `depth` (1/2 → 200, 3+ → 422) and `limit`
50
+ // (default 100, clamps at 500). Forwarding both verbatim is what lets a
51
+ // client discover the cap from the response, instead of having the CLI
52
+ // pre-mangle a request the API was built to refuse with a precise code.
53
+ const url = buildUrl(`/api/v1/code/${opts.project}/graph`, params, opts.format || cfg.format);
54
+ const res = await client.get(url);
55
+ await printResponse(res);
56
+ }
57
+ catch (err) {
58
+ const e = err;
59
+ console.error(`Error: ${e.detail || "Failed to query code graph"}`);
60
+ process.exit(1);
61
+ }
62
+ });
63
+ }
64
+ //# sourceMappingURL=code-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-graph.js","sourceRoot":"","sources":["../../src/commands/code-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAE3D,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC,CAAC;IAC/D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,uGAAuG,CACxG,CAAC;IACJ,CAAC;IAED,IAAI;SACD,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,kFAAkF,CAAC;SAC/F,cAAc,CAAC,gBAAgB,EAAE,iDAAiD,CAAC;SACnF,MAAM,CAAC,mBAAmB,EAAE,wDAAwD,CAAC;SACrF,MAAM,CAAC,aAAa,EAAE,sCAAsC,CAAC;SAC7D,MAAM,CAAC,aAAa,EAAE,oDAAoD,CAAC;SAC3E,MAAM,CAAC,iBAAiB,EAAE,uCAAuC,CAAC;SAClE,MAAM,CAAC,gBAAgB,EAAE,qDAAqD,CAAC;SAC/E,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,IAAI,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC;QACxC,MAAM,GAAG,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YACpC,4EAA4E;YAC5E,2EAA2E;YAC3E,uEAAuE;YACvE,MAAM,MAAM,GAA2B,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC3D,IAAI,IAAI,CAAC,SAAS;gBAAE,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;YACtD,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAC1C,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAC1C,IAAI,IAAI,CAAC,MAAM;gBAAE,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC7C,uEAAuE;YACvE,wEAAwE;YACxE,uEAAuE;YACvE,wEAAwE;YACxE,MAAM,GAAG,GAAG,QAAQ,CAClB,gBAAgB,IAAI,CAAC,OAAO,QAAQ,EACpC,MAAM,EACN,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAC1B,CAAC;YACF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,CAAC,GAAG,GAA0B,CAAC;YACrC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,IAAI,4BAA4B,EAAE,CAAC,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -1,9 +1,10 @@
1
1
  /**
2
- * opvs code — the tenant code index query door (TCI · 2.2)
2
+ * opvs code — the tenant code index query door (TCI · 2.2 + 2.4)
3
3
  *
4
4
  * The question this answers is "does this already exist in the repo?", asked by
5
- * an agent that is about to write something. It is the CLI face of
6
- * `GET /api/v1/code/{project_id}/context` and `.../summary`.
5
+ * an agent that is about to write something. It is the CLI face of every read
6
+ * the API serves under `/api/v1/code/{project_id}/`: context, summary, tree,
7
+ * files, symbols, and the stable-node reads (resolve, changes).
7
8
  *
8
9
  * ⚠️ THE VERDICT IS NOT THE WHOLE ANSWER.
9
10
  * `indexed: false` and `verdict: "none"` are DIFFERENT facts and the API returns
@@ -18,6 +19,15 @@
18
19
  * scope ceiling. `code:write` (the ingest lane) is deliberately not, and there is
19
20
  * no CLI command for it: an index pass is a 50,000-row bulk load and a worker
20
21
  * session must not be able to spend one (D5).
22
+ *
23
+ * SEND ONLY WHAT THE USER SET (TCI-112, P2.4)
24
+ * ---------------------------------------------
25
+ * Every option here is optional except `--project`. The brief is explicit: do
26
+ * not invent a default the server did not ask for. A `branch=undefined` on the
27
+ * wire is a real filter reading as "the branch named 'undefined'"; a
28
+ * `directory=` left out of `/files` lets the API apply its own `.` default; a
29
+ * `format=undefined` is a literal string on the URL. Each `if (opts.x) ...`
30
+ * gate exists so the request the server sees is the request the user asked for.
21
31
  */
22
32
  import { Command } from "commander";
23
33
  export declare function registerCodeCommands(program: Command): void;