@opvs-ai/cli 0.23.0 → 0.25.0
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 +73 -0
- package/claude-plugin/.claude-plugin/marketplace.json +14 -0
- package/claude-plugin/.claude-plugin/plugin.json +9 -0
- package/claude-plugin/.mcp.json +8 -0
- package/claude-plugin/README.md +24 -0
- package/claude-plugin/hooks/hooks.json +50 -0
- package/claude-plugin/skills/remember/SKILL.md +31 -0
- package/claude-plugin/skills/remember/scripts/remember.mjs +121 -0
- package/dist/.build-stamp.json +4 -4
- package/dist/commands/code-graph.d.ts +17 -0
- package/dist/commands/code-graph.js +64 -0
- package/dist/commands/code-graph.js.map +1 -0
- package/dist/commands/code.d.ts +13 -3
- package/dist/commands/code.js +191 -3
- package/dist/commands/code.js.map +1 -1
- package/dist/commands/memory-capture.d.ts +76 -0
- package/dist/commands/memory-capture.js +592 -0
- package/dist/commands/memory-capture.js.map +1 -0
- package/dist/commands/memory-hook.d.ts +1 -0
- package/dist/commands/memory-hook.js +35 -11
- package/dist/commands/memory-hook.js.map +1 -1
- package/dist/commands/memory-install.d.ts +17 -0
- package/dist/commands/memory-install.js +349 -0
- package/dist/commands/memory-install.js.map +1 -0
- package/dist/commands/memory-lessons.d.ts +8 -0
- package/dist/commands/memory-lessons.js +135 -0
- package/dist/commands/memory-lessons.js.map +1 -0
- package/dist/commands/memory.d.ts +1 -9
- package/dist/commands/memory.js +17 -85
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/orgChart.d.ts +3 -0
- package/dist/commands/orgChart.js +88 -0
- package/dist/commands/orgChart.js.map +1 -0
- package/dist/commands/tasks.js +45 -3
- package/dist/commands/tasks.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/redact.d.ts +16 -0
- package/dist/lib/redact.js +120 -0
- package/dist/lib/redact.js.map +1 -0
- package/memory-services.d.ts +9 -0
- package/memory-services.js +118 -0
- package/package.json +5 -1
- package/runtime-plugins/cline/opvs-memory.ts +99 -0
- 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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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* opvs memory — search typed repository memory and propose human curation.
|
|
3
3
|
*/
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
+
export { OPVS_MEMORY_REPOSITORY, parseServicePatterns, repositoryNameFromRemote, serviceForPath, } from "../../memory-services.js";
|
|
5
6
|
interface MemoryEntry {
|
|
6
7
|
id: string;
|
|
7
8
|
card?: string | null;
|
|
@@ -10,15 +11,6 @@ interface MemoryEntry {
|
|
|
10
11
|
pinned?: boolean;
|
|
11
12
|
[key: string]: unknown;
|
|
12
13
|
}
|
|
13
|
-
interface ServicePattern {
|
|
14
|
-
service: string;
|
|
15
|
-
pattern: string;
|
|
16
|
-
}
|
|
17
14
|
/** Keep the API's relative order within the pinned and unpinned groups. */
|
|
18
15
|
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
16
|
export declare function registerMemoryCommands(program: Command): void;
|
|
24
|
-
export {};
|
package/dist/commands/memory.js
CHANGED
|
@@ -5,7 +5,12 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { createClient } from "@opvs-ai/core";
|
|
7
7
|
import { buildUrl, printJson } from "../lib/format.js";
|
|
8
|
+
import { registerCaptureHooks } from "./memory-capture.js";
|
|
8
9
|
import { registerMemoryHooks } from "./memory-hook.js";
|
|
10
|
+
import { registerMemoryInstallCommands } from "./memory-install.js";
|
|
11
|
+
import { registerLessonCommands } from "./memory-lessons.js";
|
|
12
|
+
import { parseServicePatterns, serviceForPath } from "../../memory-services.js";
|
|
13
|
+
export { OPVS_MEMORY_REPOSITORY, parseServicePatterns, repositoryNameFromRemote, serviceForPath, } from "../../memory-services.js";
|
|
9
14
|
const SEARCH_PATH = "/api/v1/memory/entries/search";
|
|
10
15
|
/** Keep the API's relative order within the pinned and unpinned groups. */
|
|
11
16
|
export function pinnedFirst(entries) {
|
|
@@ -14,87 +19,6 @@ export function pinnedFirst(entries) {
|
|
|
14
19
|
.sort((a, b) => Number(Boolean(b.entry.pinned)) - Number(Boolean(a.entry.pinned)) || a.index - b.index)
|
|
15
20
|
.map(({ entry }) => entry);
|
|
16
21
|
}
|
|
17
|
-
function unquote(value) {
|
|
18
|
-
const trimmed = value.trim();
|
|
19
|
-
if (trimmed.length >= 2 &&
|
|
20
|
-
((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
21
|
-
(trimmed.startsWith("'") && trimmed.endsWith("'")))) {
|
|
22
|
-
return trimmed.slice(1, -1);
|
|
23
|
-
}
|
|
24
|
-
return trimmed;
|
|
25
|
-
}
|
|
26
|
-
/** Read the service names and path patterns from the canonical registry. */
|
|
27
|
-
export function parseServicePatterns(registry) {
|
|
28
|
-
const patterns = [];
|
|
29
|
-
let service;
|
|
30
|
-
let inCodePaths = false;
|
|
31
|
-
for (const line of registry.split(/\r?\n/)) {
|
|
32
|
-
const serviceMatch = line.match(/^ {2}- name:\s*(\S+)\s*$/);
|
|
33
|
-
if (serviceMatch) {
|
|
34
|
-
service = unquote(serviceMatch[1]);
|
|
35
|
-
inCodePaths = false;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (!service)
|
|
39
|
-
continue;
|
|
40
|
-
const planningMatch = line.match(/^ {4}planning_dir:\s*(.+?)\s*$/);
|
|
41
|
-
if (planningMatch) {
|
|
42
|
-
const planningDir = unquote(planningMatch[1].replace(/\s+#.*$/, ""));
|
|
43
|
-
if (planningDir !== "null")
|
|
44
|
-
patterns.push({ service, pattern: planningDir });
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (/^ {4}code_paths:\s*$/.test(line)) {
|
|
48
|
-
inCodePaths = true;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (/^ {4}\S/.test(line)) {
|
|
52
|
-
inCodePaths = false;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (inCodePaths) {
|
|
56
|
-
const pathMatch = line.match(/^ {6}-\s+(.+?)\s*$/);
|
|
57
|
-
if (pathMatch) {
|
|
58
|
-
const pattern = unquote(pathMatch[1].replace(/\s+#.*$/, ""));
|
|
59
|
-
if (pattern)
|
|
60
|
-
patterns.push({ service, pattern });
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return patterns;
|
|
65
|
-
}
|
|
66
|
-
function globRegex(pattern) {
|
|
67
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*+/g, ".*");
|
|
68
|
-
return new RegExp(`^${escaped}${pattern.endsWith("/") ? "" : "$"}`);
|
|
69
|
-
}
|
|
70
|
-
function matchesPattern(path, pattern) {
|
|
71
|
-
if (pattern.includes("*"))
|
|
72
|
-
return globRegex(pattern).test(path);
|
|
73
|
-
return pattern.endsWith("/") ? path.startsWith(pattern) : path === pattern;
|
|
74
|
-
}
|
|
75
|
-
function specificity(pattern) {
|
|
76
|
-
const wildcard = pattern.indexOf("*");
|
|
77
|
-
return [wildcard === -1 ? pattern.length : wildcard, pattern.length];
|
|
78
|
-
}
|
|
79
|
-
/** Apply services.yaml's global "most specific wins" rule to a repository path. */
|
|
80
|
-
export function serviceForPath(path, patterns) {
|
|
81
|
-
const normalized = path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
82
|
-
let best;
|
|
83
|
-
for (const candidate of patterns) {
|
|
84
|
-
if (!matchesPattern(normalized, candidate.pattern))
|
|
85
|
-
continue;
|
|
86
|
-
if (!best) {
|
|
87
|
-
best = candidate;
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
const [candidatePrefix, candidateLength] = specificity(candidate.pattern);
|
|
91
|
-
const [bestPrefix, bestLength] = specificity(best.pattern);
|
|
92
|
-
if (candidatePrefix > bestPrefix || (candidatePrefix === bestPrefix && candidateLength > bestLength)) {
|
|
93
|
-
best = candidate;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return best?.service;
|
|
97
|
-
}
|
|
98
22
|
function findServicesRegistry(start) {
|
|
99
23
|
let directory = resolve(start);
|
|
100
24
|
while (true) {
|
|
@@ -126,9 +50,9 @@ async function searchEntries(client, params) {
|
|
|
126
50
|
const response = await client.get(buildUrl(SEARCH_PATH, params));
|
|
127
51
|
return (await response.json());
|
|
128
52
|
}
|
|
129
|
-
function contextResult(entries) {
|
|
53
|
+
function contextResult(entries, warnings) {
|
|
130
54
|
const sorted = pinnedFirst(uniqueEntries(entries));
|
|
131
|
-
return { total: sorted.length, items: sorted };
|
|
55
|
+
return { total: sorted.length, items: sorted, warnings };
|
|
132
56
|
}
|
|
133
57
|
function fail(error, fallback) {
|
|
134
58
|
const detail = error?.detail;
|
|
@@ -138,6 +62,9 @@ function fail(error, fallback) {
|
|
|
138
62
|
export function registerMemoryCommands(program) {
|
|
139
63
|
const memory = program.command("memory").description("Search typed repository memory and request curation");
|
|
140
64
|
registerMemoryHooks(memory);
|
|
65
|
+
registerCaptureHooks(memory);
|
|
66
|
+
registerMemoryInstallCommands(memory);
|
|
67
|
+
registerLessonCommands(memory);
|
|
141
68
|
memory
|
|
142
69
|
.command("search <query>")
|
|
143
70
|
.description("Search typed memory, with pinned entries first")
|
|
@@ -178,7 +105,7 @@ export function registerMemoryCommands(program) {
|
|
|
178
105
|
card: opts.card,
|
|
179
106
|
pinned_first: "true",
|
|
180
107
|
});
|
|
181
|
-
printJson(contextResult(entriesFrom(result).filter((entry) => entry.card === opts.card)));
|
|
108
|
+
printJson(contextResult(entriesFrom(result).filter((entry) => entry.card === opts.card), Array.isArray(result.warnings) ? result.warnings : []));
|
|
182
109
|
return;
|
|
183
110
|
}
|
|
184
111
|
const files = (opts.files ?? "")
|
|
@@ -189,9 +116,12 @@ export function registerMemoryCommands(program) {
|
|
|
189
116
|
const patterns = parseServicePatterns(registry);
|
|
190
117
|
const services = new Set(files.map((path) => serviceForPath(path, patterns)).filter(Boolean));
|
|
191
118
|
const entries = [];
|
|
119
|
+
const warnings = [];
|
|
192
120
|
for (const anchor of files) {
|
|
193
121
|
const result = await searchEntries(client, { anchor, pinned_first: "true" });
|
|
194
122
|
entries.push(...entriesFrom(result));
|
|
123
|
+
if (Array.isArray(result.warnings))
|
|
124
|
+
warnings.push(...result.warnings);
|
|
195
125
|
}
|
|
196
126
|
for (const service of services) {
|
|
197
127
|
const result = await searchEntries(client, {
|
|
@@ -199,8 +129,10 @@ export function registerMemoryCommands(program) {
|
|
|
199
129
|
pinned_first: "true",
|
|
200
130
|
});
|
|
201
131
|
entries.push(...entriesFrom(result));
|
|
132
|
+
if (Array.isArray(result.warnings))
|
|
133
|
+
warnings.push(...result.warnings);
|
|
202
134
|
}
|
|
203
|
-
printJson(contextResult(entries));
|
|
135
|
+
printJson(contextResult(entries, warnings));
|
|
204
136
|
}
|
|
205
137
|
catch (error) {
|
|
206
138
|
fail(error, "Failed to load memory context");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/commands/memory.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/commands/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orgChart.js","sourceRoot":"","sources":["../../src/commands/orgChart.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/tasks.js
CHANGED
|
@@ -479,6 +479,7 @@ export function registerTaskCommands(program) {
|
|
|
479
479
|
.command("update <id>")
|
|
480
480
|
.description("Update a task (status, priority, result)")
|
|
481
481
|
.option("--status <status>", "Status: pending, assigned, in_progress, blocked, review, done, cancelled, failed (pending can only go to assigned/in_progress/cancelled)")
|
|
482
|
+
.option("--any-status", "Skip status compare-and-set and write regardless of the card's current status")
|
|
482
483
|
.option("--priority <priority>", "Priority: low, medium, high, critical")
|
|
483
484
|
.option("--title <title>", "New title")
|
|
484
485
|
.option("-d, --description <desc>", "New description (markdown)")
|
|
@@ -504,6 +505,11 @@ export function registerTaskCommands(program) {
|
|
|
504
505
|
// defect of exactly the shape AgentCLI LEARNINGS records for `--column`:
|
|
505
506
|
// an existing capability read as missing, and callers built workarounds.
|
|
506
507
|
.option("--depends-on <refs>", "Comma-separated tasks this card waits on, REPLACING the current set — short refs (OSB-108), 8+ hex UUID prefixes, or full UUIDs. Pass '' to clear every edge.")
|
|
508
|
+
// OD-500 — a dependency write, not a field on the card: it goes to the
|
|
509
|
+
// dependency route (which runs the cycle guard and the stack limits), never
|
|
510
|
+
// into the PATCH body. Sent with met_at, that route creates the edge or
|
|
511
|
+
// flips an existing one, so re-running it is harmless.
|
|
512
|
+
.option("--stack-on <parent>", "Stacked cards: make ONE dependency on <parent> that is met when the parent's PR is APPROVED (the planner writes its approved_head), not only when it merges. Creates the edge or flips an existing one. One per card; a stack is at most 3 cards tall.")
|
|
507
513
|
.option("--mutex-group <lane>", "DAG lane, or 'none' to clear it. Two cards sharing a lane on one board never run concurrently.")
|
|
508
514
|
.option("--claimable-by <class>", "Actor gate: worker | agent | human, or 'none' to CLEAR the card's own gate — it then inherits the board's, then the project's, default, and is claimable by anyone only when neither sets one.")
|
|
509
515
|
// ── Build routing pins (OSB-198 item 8) ────────────────────────────────
|
|
@@ -644,9 +650,45 @@ export function registerTaskCommands(program) {
|
|
|
644
650
|
body.custom_fields_merge = true;
|
|
645
651
|
}
|
|
646
652
|
Object.assign(body, buildDeliveryFields(opts, true));
|
|
647
|
-
|
|
648
|
-
|
|
653
|
+
const stackOn = typeof opts.stackOn === "string" ? opts.stackOn.trim() : undefined;
|
|
654
|
+
if (stackOn === "") {
|
|
655
|
+
console.error("Error: --stack-on needs the parent card (short ref or UUID).");
|
|
649
656
|
process.exit(1);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
if (Object.keys(body).length === 0 && !stackOn) {
|
|
660
|
+
console.error("Nothing to update. Provide --status, --priority, --title, --description, --result, --result-file, --agent-result, --blocked-reason, --assign, --assign-user, --labels, --due, --instructions, --instructions-file, --context, --parent, --depends-on, --stack-on, --mutex-group, --claimable-by, --task-type, --model, --provider, --harness, --subscription, --pin-env, --estimate, --workflow-doc, --workflow-skill, --workflow-rule, --pr, --pr-url, --branch, --worktree, --deploy-needs, --field, or --custom-fields");
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
// The stack edge goes FIRST: a refusal (422 — a second stacked edge, or
|
|
664
|
+
// a stack taller than 3) then leaves the card exactly as it was.
|
|
665
|
+
if (stackOn) {
|
|
666
|
+
const cardRes = await client.get(`/api/v1/board/tasks/${id}`);
|
|
667
|
+
const card = (await cardRes.json());
|
|
668
|
+
const linkRes = await client.post(`/api/v1/board/boards/${card.board_id}/tasks/${card.id}/dependencies`, { depends_on_task_id: stackOn, met_at: "approved" });
|
|
669
|
+
const link = await linkRes.json();
|
|
670
|
+
if (Object.keys(body).length === 0) {
|
|
671
|
+
if (opts.json) {
|
|
672
|
+
printJson(link);
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
console.log(`${id} stacks on ${stackOn} (met_at=approved)`);
|
|
676
|
+
}
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
// Keep the optimistic-concurrency precondition out of the printed
|
|
681
|
+
// changed-field list: it controls this write but is not persisted.
|
|
682
|
+
const changedFields = Object.keys(body);
|
|
683
|
+
if (opts.status && !opts.anyStatus) {
|
|
684
|
+
const currentRes = await client.get(`/api/v1/board/tasks/${id}`);
|
|
685
|
+
// createClient returns a Response or throws. The truthiness check
|
|
686
|
+
// also keeps lightweight injected clients that omit a response
|
|
687
|
+
// object from crashing before the PATCH.
|
|
688
|
+
if (currentRes) {
|
|
689
|
+
const current = await currentRes.json();
|
|
690
|
+
body.expected_status = current.status;
|
|
691
|
+
}
|
|
650
692
|
}
|
|
651
693
|
const res = await client.patch(`/api/v1/board/tasks/${id}`, body);
|
|
652
694
|
const data = await res.json();
|
|
@@ -654,7 +696,7 @@ export function registerTaskCommands(program) {
|
|
|
654
696
|
printJson(data);
|
|
655
697
|
}
|
|
656
698
|
else {
|
|
657
|
-
printWriteSummary(data,
|
|
699
|
+
printWriteSummary(data, changedFields);
|
|
658
700
|
}
|
|
659
701
|
}
|
|
660
702
|
catch (err) {
|