@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
@@ -0,0 +1,135 @@
1
+ /**
2
+ * opvs memory lessons — pull and contribute a skill's shared lessons (AS-590).
3
+ *
4
+ * Skills that are installed on a brand can contribute their own anonymized
5
+ * lessons and pull the ones other brands left behind. The CLI is the terminal
6
+ * face of `/api/v1/skill-lessons/{vendor}/{name}`; the route validates the
7
+ * `<vendor>/<name>` reference server-side, but we validate it here too so a
8
+ * typo never hits the wire.
9
+ *
10
+ * The provenance field is one-way: `source_engagement_hash` is `sha256("cli:" +
11
+ * workspace_slug + ":" + body)`. It must NEVER carry a brand id (AS-445 sealed
12
+ * anything credential-shaped at curation, but the CLI never gets to find out the
13
+ * hard way).
14
+ */
15
+ import { createHash } from "node:crypto";
16
+ import { readFileSync } from "node:fs";
17
+ import { createClient } from "@opvs-ai/core";
18
+ import { buildUrl, printJson } from "../lib/format.js";
19
+ const SEGMENT_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
20
+ const REF_RE = /^@?([a-z0-9][a-z0-9-]{0,63})\/([a-z0-9][a-z0-9-]{0,63})$/;
21
+ export function parseSkillRef(ref) {
22
+ const match = REF_RE.exec(ref);
23
+ if (!match)
24
+ return null;
25
+ if (!SEGMENT_RE.test(match[1]) || !SEGMENT_RE.test(match[2]))
26
+ return null;
27
+ return { vendor: match[1], name: match[2] };
28
+ }
29
+ /** sha256("cli:" + workspace_slug + ":" + body) — never a brand id (AS-590). */
30
+ export function lessonProvenanceHash(workspace, body) {
31
+ return createHash("sha256").update(`cli:${workspace ?? ""}:${body}`).digest("hex");
32
+ }
33
+ function readBody(body, bodyFile) {
34
+ if (body && bodyFile) {
35
+ throw new Error("provide exactly one of --body or --body-file");
36
+ }
37
+ if (bodyFile)
38
+ return readFileSync(bodyFile, "utf8");
39
+ if (!body)
40
+ throw new Error("--body or --body-file is required");
41
+ return body;
42
+ }
43
+ function fail(message) {
44
+ console.error(`Error: ${message}`);
45
+ process.exit(1);
46
+ }
47
+ function workspaceOf(command) {
48
+ let current = command;
49
+ while (current) {
50
+ const opts = current.opts();
51
+ if (opts.workspace)
52
+ return opts.workspace;
53
+ current = current.parent;
54
+ }
55
+ return undefined;
56
+ }
57
+ export function registerLessonCommands(memory) {
58
+ const lessons = memory.command("lessons").description("Pull and contribute a skill's shared lessons");
59
+ lessons
60
+ .command("get <vendor>/<name>")
61
+ .description("Pull a skill's shared lessons")
62
+ .option("--query <text>", "FTS search over body + tags")
63
+ .option("--limit <n>", "Max items", "25")
64
+ .option("--since <cursor>", "Forward cursor from a prior pull")
65
+ .action(async (ref, opts) => {
66
+ const parsed = parseSkillRef(ref);
67
+ if (!parsed) {
68
+ fail(`invalid vendor/name: ${ref} (use <vendor>/<name>, e.g. opvs-ai/opvs-vibe)`);
69
+ }
70
+ const wsSlug = workspaceOf(memory);
71
+ try {
72
+ const client = createClient(wsSlug);
73
+ const params = {};
74
+ if (opts.query)
75
+ params.query = opts.query;
76
+ if (opts.limit)
77
+ params.limit = opts.limit;
78
+ if (opts.since)
79
+ params.since = opts.since;
80
+ const url = buildUrl(`/api/v1/skill-lessons/${parsed.vendor}/${parsed.name}`, params);
81
+ const res = await client.get(url);
82
+ printJson(await res.json());
83
+ }
84
+ catch (err) {
85
+ const detail = err?.detail;
86
+ fail(detail || "Failed to pull skill lessons");
87
+ }
88
+ });
89
+ lessons
90
+ .command("add <vendor>/<name>")
91
+ .description("Contribute an anonymized lesson to a skill (→ pending)")
92
+ .requiredOption("--kind <kind>", "learning | pattern")
93
+ .option("--body <text>", "The lesson text (NO brand data)")
94
+ .option("-f, --body-file <path>", "Read the lesson text from a file")
95
+ .option("--tags <a,b>", "Comma-separated tags")
96
+ .action(async (ref, opts) => {
97
+ const parsed = parseSkillRef(ref);
98
+ if (!parsed) {
99
+ fail(`invalid vendor/name: ${ref} (use <vendor>/<name>, e.g. opvs-ai/opvs-vibe)`);
100
+ }
101
+ if (opts.kind !== "learning" && opts.kind !== "pattern") {
102
+ fail(`invalid --kind: ${opts.kind} (expected learning | pattern)`);
103
+ }
104
+ const wsSlug = workspaceOf(memory);
105
+ let body;
106
+ try {
107
+ body = readBody(opts.body, opts.bodyFile);
108
+ }
109
+ catch (err) {
110
+ fail(err instanceof Error ? err.message : "Failed to read lesson body");
111
+ }
112
+ const payload = {
113
+ kind: opts.kind,
114
+ body,
115
+ tags: opts.tags
116
+ ? opts.tags
117
+ .split(",")
118
+ .map((tag) => tag.trim())
119
+ .filter(Boolean)
120
+ : null,
121
+ source_engagement_hash: lessonProvenanceHash(wsSlug, body),
122
+ craft_id: null,
123
+ };
124
+ try {
125
+ const client = createClient(wsSlug);
126
+ const res = await client.post(`/api/v1/skill-lessons/${parsed.vendor}/${parsed.name}`, payload);
127
+ printJson(await res.json());
128
+ }
129
+ catch (err) {
130
+ const detail = err?.detail;
131
+ fail(detail || "Failed to contribute skill lesson");
132
+ }
133
+ });
134
+ }
135
+ //# sourceMappingURL=memory-lessons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-lessons.js","sourceRoot":"","sources":["../../src/commands/memory-lessons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * opvs memory — search typed repository memory and propose human curation.
3
+ */
4
+ import { Command } from "commander";
5
+ interface MemoryEntry {
6
+ id: string;
7
+ card?: string | null;
8
+ scopes?: string[];
9
+ anchors?: string[];
10
+ pinned?: boolean;
11
+ [key: string]: unknown;
12
+ }
13
+ interface ServicePattern {
14
+ service: string;
15
+ pattern: string;
16
+ }
17
+ /** Keep the API's relative order within the pinned and unpinned groups. */
18
+ export declare function pinnedFirst(entries: MemoryEntry[]): MemoryEntry[];
19
+ /** Read the service names and path patterns from the canonical registry. */
20
+ export declare function parseServicePatterns(registry: string): ServicePattern[];
21
+ /** Apply services.yaml's global "most specific wins" rule to a repository path. */
22
+ export declare function serviceForPath(path: string, patterns: ServicePattern[]): string | undefined;
23
+ export declare function registerMemoryCommands(program: Command): void;
24
+ export {};
@@ -0,0 +1,239 @@
1
+ /**
2
+ * opvs memory — search typed repository memory and propose human curation.
3
+ */
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { createClient } from "@opvs-ai/core";
7
+ import { buildUrl, printJson } from "../lib/format.js";
8
+ import { registerCaptureHooks } from "./memory-capture.js";
9
+ import { registerMemoryHooks } from "./memory-hook.js";
10
+ import { registerMemoryInstallCommands } from "./memory-install.js";
11
+ import { registerLessonCommands } from "./memory-lessons.js";
12
+ const SEARCH_PATH = "/api/v1/memory/entries/search";
13
+ /** Keep the API's relative order within the pinned and unpinned groups. */
14
+ export function pinnedFirst(entries) {
15
+ return entries
16
+ .map((entry, index) => ({ entry, index }))
17
+ .sort((a, b) => Number(Boolean(b.entry.pinned)) - Number(Boolean(a.entry.pinned)) || a.index - b.index)
18
+ .map(({ entry }) => entry);
19
+ }
20
+ function unquote(value) {
21
+ const trimmed = value.trim();
22
+ if (trimmed.length >= 2 &&
23
+ ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
24
+ (trimmed.startsWith("'") && trimmed.endsWith("'")))) {
25
+ return trimmed.slice(1, -1);
26
+ }
27
+ return trimmed;
28
+ }
29
+ /** Read the service names and path patterns from the canonical registry. */
30
+ export function parseServicePatterns(registry) {
31
+ const patterns = [];
32
+ let service;
33
+ let inCodePaths = false;
34
+ for (const line of registry.split(/\r?\n/)) {
35
+ const serviceMatch = line.match(/^ {2}- name:\s*(\S+)\s*$/);
36
+ if (serviceMatch) {
37
+ service = unquote(serviceMatch[1]);
38
+ inCodePaths = false;
39
+ continue;
40
+ }
41
+ if (!service)
42
+ continue;
43
+ const planningMatch = line.match(/^ {4}planning_dir:\s*(.+?)\s*$/);
44
+ if (planningMatch) {
45
+ const planningDir = unquote(planningMatch[1].replace(/\s+#.*$/, ""));
46
+ if (planningDir !== "null")
47
+ patterns.push({ service, pattern: planningDir });
48
+ continue;
49
+ }
50
+ if (/^ {4}code_paths:\s*$/.test(line)) {
51
+ inCodePaths = true;
52
+ continue;
53
+ }
54
+ if (/^ {4}\S/.test(line)) {
55
+ inCodePaths = false;
56
+ continue;
57
+ }
58
+ if (inCodePaths) {
59
+ const pathMatch = line.match(/^ {6}-\s+(.+?)\s*$/);
60
+ if (pathMatch) {
61
+ const pattern = unquote(pathMatch[1].replace(/\s+#.*$/, ""));
62
+ if (pattern)
63
+ patterns.push({ service, pattern });
64
+ }
65
+ }
66
+ }
67
+ return patterns;
68
+ }
69
+ function globRegex(pattern) {
70
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*+/g, ".*");
71
+ return new RegExp(`^${escaped}${pattern.endsWith("/") ? "" : "$"}`);
72
+ }
73
+ function matchesPattern(path, pattern) {
74
+ if (pattern.includes("*"))
75
+ return globRegex(pattern).test(path);
76
+ return pattern.endsWith("/") ? path.startsWith(pattern) : path === pattern;
77
+ }
78
+ function specificity(pattern) {
79
+ const wildcard = pattern.indexOf("*");
80
+ return [wildcard === -1 ? pattern.length : wildcard, pattern.length];
81
+ }
82
+ /** Apply services.yaml's global "most specific wins" rule to a repository path. */
83
+ export function serviceForPath(path, patterns) {
84
+ const normalized = path.replace(/\\/g, "/").replace(/^\.\//, "");
85
+ let best;
86
+ for (const candidate of patterns) {
87
+ if (!matchesPattern(normalized, candidate.pattern))
88
+ continue;
89
+ if (!best) {
90
+ best = candidate;
91
+ continue;
92
+ }
93
+ const [candidatePrefix, candidateLength] = specificity(candidate.pattern);
94
+ const [bestPrefix, bestLength] = specificity(best.pattern);
95
+ if (candidatePrefix > bestPrefix || (candidatePrefix === bestPrefix && candidateLength > bestLength)) {
96
+ best = candidate;
97
+ }
98
+ }
99
+ return best?.service;
100
+ }
101
+ function findServicesRegistry(start) {
102
+ let directory = resolve(start);
103
+ while (true) {
104
+ const candidate = join(directory, "memory", "services.yaml");
105
+ if (existsSync(candidate))
106
+ return candidate;
107
+ const parent = dirname(directory);
108
+ if (parent === directory)
109
+ throw new Error("memory/services.yaml was not found in this repository");
110
+ directory = parent;
111
+ }
112
+ }
113
+ function entriesFrom(payload) {
114
+ if (!payload || typeof payload !== "object")
115
+ return [];
116
+ const items = payload.items;
117
+ return Array.isArray(items) ? items : [];
118
+ }
119
+ function uniqueEntries(entries) {
120
+ const seen = new Set();
121
+ return entries.filter((entry) => {
122
+ if (seen.has(entry.id))
123
+ return false;
124
+ seen.add(entry.id);
125
+ return true;
126
+ });
127
+ }
128
+ async function searchEntries(client, params) {
129
+ const response = await client.get(buildUrl(SEARCH_PATH, params));
130
+ return (await response.json());
131
+ }
132
+ function contextResult(entries) {
133
+ const sorted = pinnedFirst(uniqueEntries(entries));
134
+ return { total: sorted.length, items: sorted };
135
+ }
136
+ function fail(error, fallback) {
137
+ const detail = error?.detail;
138
+ console.error(`Error: ${detail || (error instanceof Error ? error.message : fallback)}`);
139
+ process.exit(1);
140
+ }
141
+ export function registerMemoryCommands(program) {
142
+ const memory = program.command("memory").description("Search typed repository memory and request curation");
143
+ registerMemoryHooks(memory);
144
+ registerCaptureHooks(memory);
145
+ registerMemoryInstallCommands(memory);
146
+ registerLessonCommands(memory);
147
+ memory
148
+ .command("search <query>")
149
+ .description("Search typed memory, with pinned entries first")
150
+ .option("--service <name>", "Restrict to the service scope from memory/services.yaml")
151
+ .option("--type <type>", "Restrict to an entry type")
152
+ .action(async (query, opts) => {
153
+ const wsSlug = program.opts().workspace;
154
+ try {
155
+ const client = createClient(wsSlug);
156
+ const params = { q: query, pinned_first: "true" };
157
+ if (opts.service)
158
+ params.scope = `service:${opts.service}`;
159
+ if (opts.type)
160
+ params.type = opts.type;
161
+ const result = await searchEntries(client, params);
162
+ const items = pinnedFirst(entriesFrom(result));
163
+ printJson({ ...result, total: items.length, items });
164
+ }
165
+ catch (error) {
166
+ fail(error, "Failed to search memory");
167
+ }
168
+ });
169
+ memory
170
+ .command("context")
171
+ .description("Show memory relevant to repository files or a card, with pinned entries first")
172
+ .option("--files <paths>", "Comma-separated repository paths")
173
+ .option("--card <ref>", "Card ref whose memory should be shown")
174
+ .action(async (opts) => {
175
+ if (Boolean(opts.files) === Boolean(opts.card)) {
176
+ console.error("Error: provide exactly one of --files <paths> or --card <ref>");
177
+ process.exit(1);
178
+ }
179
+ const wsSlug = program.opts().workspace;
180
+ try {
181
+ const client = createClient(wsSlug);
182
+ if (opts.card) {
183
+ const result = await searchEntries(client, {
184
+ card: opts.card,
185
+ pinned_first: "true",
186
+ });
187
+ printJson(contextResult(entriesFrom(result).filter((entry) => entry.card === opts.card)));
188
+ return;
189
+ }
190
+ const files = (opts.files ?? "")
191
+ .split(",")
192
+ .map((path) => path.trim().replace(/\\/g, "/").replace(/^\.\//, ""))
193
+ .filter(Boolean);
194
+ const registry = readFileSync(findServicesRegistry(process.cwd()), "utf8");
195
+ const patterns = parseServicePatterns(registry);
196
+ const services = new Set(files.map((path) => serviceForPath(path, patterns)).filter(Boolean));
197
+ const entries = [];
198
+ for (const anchor of files) {
199
+ const result = await searchEntries(client, { anchor, pinned_first: "true" });
200
+ entries.push(...entriesFrom(result));
201
+ }
202
+ for (const service of services) {
203
+ const result = await searchEntries(client, {
204
+ scope: `service:${service}`,
205
+ pinned_first: "true",
206
+ });
207
+ entries.push(...entriesFrom(result));
208
+ }
209
+ printJson(contextResult(entries));
210
+ }
211
+ catch (error) {
212
+ fail(error, "Failed to load memory context");
213
+ }
214
+ });
215
+ memory
216
+ .command("pin-request <id>")
217
+ .description("Propose a pin by commenting on the entry's card; this never pins the entry")
218
+ .requiredOption("--reason <text>", "Why a human should pin this entry")
219
+ .action(async (id, opts) => {
220
+ const wsSlug = program.opts().workspace;
221
+ try {
222
+ const client = createClient(wsSlug);
223
+ const response = await client.get(`/api/v1/memory/entries/${encodeURIComponent(id)}`);
224
+ const entry = (await response.json());
225
+ if (!entry.card) {
226
+ console.error("Error: this memory entry has no card on which to record a pin request");
227
+ process.exit(1);
228
+ }
229
+ const comment = await client.post(`/api/v1/board/tasks/${encodeURIComponent(entry.card)}/comments`, {
230
+ content: `Pin request for memory entry \`${entry.id}\`: ${opts.reason}`,
231
+ });
232
+ printJson(await comment.json());
233
+ }
234
+ catch (error) {
235
+ fail(error, "Failed to request a memory pin");
236
+ }
237
+ });
238
+ }
239
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/commands/memory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ /** Register commands for the current person's PAT-backed OPVS mailbox. */
3
+ export declare function registerMyMailCommands(program: Command): void;
@@ -0,0 +1,119 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { createClient, getEffectiveConfig } from "@opvs-ai/core";
3
+ import { Option } from "commander";
4
+ import { buildUrl, printJson, printResponse } from "../lib/format.js";
5
+ function fail(err, fallback) {
6
+ const error = err;
7
+ console.error(`Error: ${error.detail || error.message || fallback}`);
8
+ return process.exit(1);
9
+ }
10
+ function isNotFound(err) {
11
+ const error = err;
12
+ return error.status === 404 || error.statusCode === 404 || error.detail === "Not found";
13
+ }
14
+ function rejectRefused(result) {
15
+ if (result.status === "rejected") {
16
+ console.error(`Error: ${result.reason || "Message rejected"}`);
17
+ process.exit(1);
18
+ return true;
19
+ }
20
+ return false;
21
+ }
22
+ /** Register commands for the current person's PAT-backed OPVS mailbox. */
23
+ export function registerMyMailCommands(program) {
24
+ const myMail = program
25
+ .command("my-mail")
26
+ .description("Read and write your OPVS mailbox");
27
+ myMail
28
+ .command("inbox")
29
+ .description("List messages in your OPVS mailbox")
30
+ .option("--status <status>", "Filter by message status")
31
+ .option("--limit <n>", "Maximum messages to return", "50")
32
+ .action(async (opts) => {
33
+ const workspace = program.opts().workspace;
34
+ const config = getEffectiveConfig(workspace);
35
+ const params = {};
36
+ if (opts.status)
37
+ params.status = opts.status;
38
+ if (opts.limit)
39
+ params.limit = opts.limit;
40
+ try {
41
+ const response = await createClient(workspace).get(buildUrl("/api/v1/me/mail", params, config.format));
42
+ await printResponse(response);
43
+ }
44
+ catch (err) {
45
+ if (isNotFound(err)) {
46
+ console.error("You have no OPVS mailbox yet.");
47
+ process.exit(1);
48
+ return;
49
+ }
50
+ fail(err, "Failed to fetch your mailbox");
51
+ }
52
+ });
53
+ myMail
54
+ .command("thread <thread_id>")
55
+ .description("Read a thread in your OPVS mailbox")
56
+ .action(async (threadId) => {
57
+ const workspace = program.opts().workspace;
58
+ const config = getEffectiveConfig(workspace);
59
+ try {
60
+ const response = await createClient(workspace).get(buildUrl(`/api/v1/me/mail/threads/${encodeURIComponent(threadId)}`, {}, config.format));
61
+ await printResponse(response);
62
+ }
63
+ catch (err) {
64
+ fail(err, "Failed to read thread");
65
+ }
66
+ });
67
+ myMail
68
+ .command("send <to> <body>")
69
+ .description("Send a message from your OPVS mailbox")
70
+ .addOption(new Option("--type <type>", "Message type: ? ! ~ ^ . > <")
71
+ .choices(["?", "!", "~", "^", ".", ">", "<"])
72
+ .default("."))
73
+ .action(async (to, body, opts) => {
74
+ const workspace = program.opts().workspace;
75
+ // Create this once per command invocation. createClient may retry the
76
+ // request, and each attempt must carry the same idempotency key.
77
+ const idempotencyKey = randomUUID();
78
+ try {
79
+ const response = await createClient(workspace).post("/api/v1/me/mail/send", { to, body, type: opts.type }, { "Idempotency-Key": idempotencyKey });
80
+ const result = (await response.json());
81
+ if (rejectRefused(result))
82
+ return;
83
+ printJson(result);
84
+ }
85
+ catch (err) {
86
+ fail(err, "Failed to send message");
87
+ }
88
+ });
89
+ myMail
90
+ .command("reply <message_id> <body>")
91
+ .description("Reply to a message in your OPVS mailbox")
92
+ .action(async (messageId, body) => {
93
+ const workspace = program.opts().workspace;
94
+ try {
95
+ const response = await createClient(workspace).post(`/api/v1/me/mail/messages/${encodeURIComponent(messageId)}/reply`, { body });
96
+ const result = (await response.json());
97
+ if (rejectRefused(result))
98
+ return;
99
+ printJson(result);
100
+ }
101
+ catch (err) {
102
+ fail(err, "Failed to reply");
103
+ }
104
+ });
105
+ myMail
106
+ .command("status <message_id> <status>")
107
+ .description("Update a message status in your OPVS mailbox")
108
+ .action(async (messageId, status) => {
109
+ const workspace = program.opts().workspace;
110
+ try {
111
+ const response = await createClient(workspace).patch(`/api/v1/me/mail/messages/${encodeURIComponent(messageId)}/status`, { status });
112
+ await printResponse(response);
113
+ }
114
+ catch (err) {
115
+ fail(err, "Failed to update message status");
116
+ }
117
+ });
118
+ }
119
+ //# sourceMappingURL=myMail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"myMail.js","sourceRoot":"","sources":["../../src/commands/myMail.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ /** Register commands for the brand's AI Office org chart. */
3
+ export declare function registerOrgChartCommands(program: Command): void;
@@ -0,0 +1,88 @@
1
+ import { createClient } from "@opvs-ai/core";
2
+ import { Option } from "commander";
3
+ import { buildUrl, printResponse } from "../lib/format.js";
4
+ function fail(err, fallback) {
5
+ const e = err;
6
+ const status = e.status ?? e.statusCode;
7
+ if (status === 404) {
8
+ console.error("Not found");
9
+ }
10
+ else if (status === 422) {
11
+ console.error(e.detail || e.message || fallback);
12
+ }
13
+ else {
14
+ console.error(`Error: ${e.detail || e.message || fallback}`);
15
+ }
16
+ return process.exit(1);
17
+ }
18
+ /** Repeatable commander argParser that turns `--manager <id>` into an array. */
19
+ const collectManagers = (value, acc) => {
20
+ acc.push(value);
21
+ return acc;
22
+ };
23
+ /** Register commands for the brand's AI Office org chart. */
24
+ export function registerOrgChartCommands(program) {
25
+ const orgChart = program
26
+ .command("org-chart")
27
+ .description("Read and write the brand's AI Office org chart");
28
+ orgChart
29
+ .command("show")
30
+ .description("Show the brand's AI Office org chart")
31
+ .addOption(new Option("--format <format>", "Output format (default text; json|yaml pass through)")
32
+ .choices(["text", "json", "yaml"])
33
+ .default("text"))
34
+ .action(async (opts) => {
35
+ const workspace = program.opts().workspace;
36
+ try {
37
+ // text → no hint, CLI parses the JSON and prints lines.
38
+ // json|yaml → pass the format to the server, print whatever comes back.
39
+ const requestFormat = opts.format === "text" ? undefined : opts.format;
40
+ const response = await createClient(workspace).get(buildUrl("/api/v1/org-chart", {}, requestFormat));
41
+ if (opts.format === "text") {
42
+ const data = (await response.json());
43
+ const namesByHire = new Map((data.nodes ?? []).map((n) => [n.hire_id, n.name]));
44
+ for (const node of data.nodes ?? []) {
45
+ const managerNames = node.manager_hire_ids.map((id) => namesByHire.get(id) ?? id);
46
+ const tail = managerNames.length ? managerNames.join(", ") : "nobody";
47
+ console.log(`${node.name} (${node.role}) → reports to: ${tail}`);
48
+ }
49
+ }
50
+ else {
51
+ await printResponse(response);
52
+ }
53
+ }
54
+ catch (err) {
55
+ fail(err, "Failed to fetch org chart");
56
+ }
57
+ });
58
+ orgChart
59
+ .command("set-manager <hire_id>")
60
+ .description("Replace the manager list for a hire (--none clears it)")
61
+ .option("--manager <hire_id>", "Manager hire id (repeatable)", collectManagers, [])
62
+ .option("--none", "Clear all managers")
63
+ .action(async (hireId, opts) => {
64
+ // Bare `set-manager <hire>` would silently send `manager_hire_ids: []`
65
+ // and clear the hire's managers — refuse it before any request.
66
+ if (!opts.none && opts.manager.length === 0) {
67
+ console.error("Error: give --manager <hire_id> (repeatable) or --none");
68
+ process.exit(1);
69
+ return;
70
+ }
71
+ // Both intents are about the same wire field — picking one silently would
72
+ // send a bundle the operator did not ask for.
73
+ if (opts.none && opts.manager.length > 0) {
74
+ console.error("Error: --none cannot be combined with --manager");
75
+ process.exit(1);
76
+ return;
77
+ }
78
+ const workspace = program.opts().workspace;
79
+ try {
80
+ const response = await createClient(workspace).put(`/api/v1/org-chart/agents/${encodeURIComponent(hireId)}/managers`, { manager_hire_ids: opts.none ? [] : opts.manager });
81
+ await printResponse(response);
82
+ }
83
+ catch (err) {
84
+ fail(err, "Failed to set managers");
85
+ }
86
+ });
87
+ }
88
+ //# sourceMappingURL=orgChart.js.map