@pi-unipi/command-enchantment 2.0.1 → 2.0.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/package.json
CHANGED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { existsSync, globSync, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
function findRepoRoot(start: string): string {
|
|
6
|
+
let dir = start;
|
|
7
|
+
while (dir !== dirname(dir)) {
|
|
8
|
+
if (existsSync(join(dir, "packages", "autocomplete", "src", "constants.ts"))) return dir;
|
|
9
|
+
dir = dirname(dir);
|
|
10
|
+
}
|
|
11
|
+
return start;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const root = findRepoRoot(process.cwd());
|
|
15
|
+
|
|
16
|
+
function read(path: string): string {
|
|
17
|
+
return readFileSync(join(root, path), "utf-8");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function collectConstants(): Map<string, string> {
|
|
21
|
+
const constants = new Map<string, string>([["UNIPI_PREFIX", "unipi:"]]);
|
|
22
|
+
|
|
23
|
+
for (const path of globSync("packages/**/*.ts", { cwd: root })) {
|
|
24
|
+
const text = read(path);
|
|
25
|
+
for (const obj of text.matchAll(/(?:export\s+)?const\s+(\w+)\s*=\s*\{([\s\S]*?)\}\s*as\s+const/g)) {
|
|
26
|
+
const [, name, body] = obj;
|
|
27
|
+
for (const item of body.matchAll(/(\w+):\s*"([^"]+)"/g)) {
|
|
28
|
+
constants.set(`${name}.${item[1]}`, item[2]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return constants;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function evaluateCommandExpression(expr: string, constants: Map<string, string>): string | null {
|
|
37
|
+
const trimmed = expr.trim();
|
|
38
|
+
|
|
39
|
+
if (trimmed.startsWith('"') || trimmed.startsWith("'")) {
|
|
40
|
+
const quote = trimmed[0];
|
|
41
|
+
const end = trimmed.indexOf(quote, 1);
|
|
42
|
+
return end > 0 ? trimmed.slice(1, end) : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (trimmed.startsWith("`")) {
|
|
46
|
+
const end = trimmed.lastIndexOf("`");
|
|
47
|
+
if (end <= 0) return null;
|
|
48
|
+
return trimmed.slice(1, end).replace(/\$\{([^}]+)\}/g, (_match, key: string) => {
|
|
49
|
+
const resolved = constants.get(key.trim());
|
|
50
|
+
return resolved ?? `\${${key}}`;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return constants.get(trimmed) ?? null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function registeredCommands(): { commands: Set<string>; nonUnipi: string[]; unresolved: string[] } {
|
|
58
|
+
const constants = collectConstants();
|
|
59
|
+
const commands = new Set<string>();
|
|
60
|
+
const nonUnipi: string[] = [];
|
|
61
|
+
const unresolved: string[] = [];
|
|
62
|
+
|
|
63
|
+
for (const path of globSync("packages/**/*.ts", { cwd: root }).sort()) {
|
|
64
|
+
const text = read(path);
|
|
65
|
+
if (!text.includes("registerCommand")) continue;
|
|
66
|
+
|
|
67
|
+
for (const match of text.matchAll(/\.registerCommand\(\s*([^,\n]+)/g)) {
|
|
68
|
+
const expr = match[1].trim();
|
|
69
|
+
|
|
70
|
+
// Workflow registers a loop over WORKFLOW_COMMANDS via local `fullCommand`.
|
|
71
|
+
if (path === "packages/workflow/commands.ts" && expr === "fullCommand") {
|
|
72
|
+
for (const [key, value] of constants) {
|
|
73
|
+
if (key.startsWith("WORKFLOW_COMMANDS.")) commands.add(`unipi:${value}`);
|
|
74
|
+
}
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const command = evaluateCommandExpression(expr, constants);
|
|
79
|
+
if (!command || command.includes("${")) {
|
|
80
|
+
unresolved.push(`${path}: ${expr}`);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (command.startsWith("unipi:")) {
|
|
85
|
+
commands.add(command);
|
|
86
|
+
} else {
|
|
87
|
+
nonUnipi.push(`${command} (${path})`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return { commands, nonUnipi, unresolved };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function autocompleteRegistry(): { registry: Set<string>; descriptions: Set<string>; registryPackages: Set<string>; labels: Set<string> } {
|
|
96
|
+
const text = read("packages/autocomplete/src/constants.ts");
|
|
97
|
+
const registryBody = text.match(/export const COMMAND_REGISTRY[^=]*= \{([\s\S]*?)\n\};/)?.[1] ?? "";
|
|
98
|
+
const descriptionsBody = text.match(/export const COMMAND_DESCRIPTIONS[^=]*= \{([\s\S]*?)\n\};/)?.[1] ?? "";
|
|
99
|
+
const labelsBody = text.match(/export const PACKAGE_LABELS[^=]*= \{([\s\S]*?)\n\};/)?.[1] ?? "";
|
|
100
|
+
|
|
101
|
+
const registry = new Set([...registryBody.matchAll(/"(unipi:[^"]+)"\s*:/g)].map((m) => m[1]));
|
|
102
|
+
const descriptions = new Set([...descriptionsBody.matchAll(/"(unipi:[^"]+)"\s*:/g)].map((m) => m[1]));
|
|
103
|
+
const registryPackages = new Set([...registryBody.matchAll(/"unipi:[^"]+"\s*:\s*"([^"]+)"/g)].map((m) => m[1]));
|
|
104
|
+
const labels = new Set([...labelsBody.matchAll(/^\s*"?([a-z][a-z0-9-]*)"?:\s*"/gm)].map((m) => m[1]));
|
|
105
|
+
|
|
106
|
+
return { registry, descriptions, registryPackages, labels };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
describe("autocomplete command registry audit", () => {
|
|
110
|
+
it("mirrors every registered /unipi:* command and has no non-unipi package commands", () => {
|
|
111
|
+
const registered = registeredCommands();
|
|
112
|
+
const autocomplete = autocompleteRegistry();
|
|
113
|
+
|
|
114
|
+
expect(registered.unresolved, "unresolved registerCommand expressions").toEqual([]);
|
|
115
|
+
expect(registered.nonUnipi, "package commands must use the unipi: prefix").toEqual([]);
|
|
116
|
+
expect([...registered.commands].sort()).toEqual([...autocomplete.registry].sort());
|
|
117
|
+
expect([...autocomplete.registry].sort()).toEqual([...autocomplete.descriptions].sort());
|
|
118
|
+
expect([...autocomplete.registryPackages].filter((pkg) => !autocomplete.labels.has(pkg)).sort()).toEqual([]);
|
|
119
|
+
});
|
|
120
|
+
});
|
package/src/constants.ts
CHANGED
|
@@ -20,6 +20,7 @@ export const PACKAGE_ORDER: string[] = [
|
|
|
20
20
|
"workflow",
|
|
21
21
|
"ralph",
|
|
22
22
|
"memory",
|
|
23
|
+
"btw",
|
|
23
24
|
"milestone",
|
|
24
25
|
"mcp",
|
|
25
26
|
"utility",
|
|
@@ -41,6 +42,7 @@ export const PACKAGE_COLORS: Record<string, string> = {
|
|
|
41
42
|
workflow: `${ESC}[91m`, // Bright Red
|
|
42
43
|
ralph: `${ESC}[33m`, // Yellow/Orange
|
|
43
44
|
memory: `${ESC}[93m`, // Bright Yellow
|
|
45
|
+
btw: `${ESC}[95m`, // Bright Magenta
|
|
44
46
|
milestone: `${ESC}[32m`, // Green
|
|
45
47
|
mcp: `${ESC}[32m`, // Green
|
|
46
48
|
utility: `${ESC}[36m`, // Cyan
|
|
@@ -57,7 +59,7 @@ export const PACKAGE_COLORS: Record<string, string> = {
|
|
|
57
59
|
};
|
|
58
60
|
|
|
59
61
|
// ─── Command Registry ────────────────────────────────────────────────
|
|
60
|
-
/** Mapping of full command name → package name (
|
|
62
|
+
/** Mapping of full command name → package name (88 verified commands) */
|
|
61
63
|
export const COMMAND_REGISTRY: Record<string, string> = {
|
|
62
64
|
// workflow (20 commands)
|
|
63
65
|
"unipi:brainstorm": "workflow",
|
|
@@ -95,6 +97,14 @@ export const COMMAND_REGISTRY: Record<string, string> = {
|
|
|
95
97
|
"unipi:global-memory-list": "memory",
|
|
96
98
|
"unipi:memory-settings": "memory",
|
|
97
99
|
|
|
100
|
+
// btw (6 commands)
|
|
101
|
+
"unipi:btw": "btw",
|
|
102
|
+
"unipi:btw-tangent": "btw",
|
|
103
|
+
"unipi:btw-new": "btw",
|
|
104
|
+
"unipi:btw-clear": "btw",
|
|
105
|
+
"unipi:btw-inject": "btw",
|
|
106
|
+
"unipi:btw-summarize": "btw",
|
|
107
|
+
|
|
98
108
|
// mcp (5 commands)
|
|
99
109
|
"unipi:mcp-status": "mcp",
|
|
100
110
|
"unipi:mcp-sync": "mcp",
|
|
@@ -126,14 +136,16 @@ export const COMMAND_REGISTRY: Record<string, string> = {
|
|
|
126
136
|
"unipi:web-settings": "web-api",
|
|
127
137
|
"unipi:web-cache-clear": "web-api",
|
|
128
138
|
|
|
129
|
-
// compact (
|
|
139
|
+
// compact (9 commands)
|
|
130
140
|
"unipi:lossless-compact": "compact",
|
|
131
141
|
"unipi:compact": "compact",
|
|
142
|
+
"unipi:session-recall": "compact",
|
|
132
143
|
"unipi:compact-recall": "compact",
|
|
133
144
|
"unipi:compact-stats": "compact",
|
|
134
145
|
"unipi:compact-doctor": "compact",
|
|
135
146
|
"unipi:compact-settings": "compact",
|
|
136
147
|
"unipi:compact-preset": "compact",
|
|
148
|
+
"unipi:compact-help": "compact",
|
|
137
149
|
|
|
138
150
|
// cocoindex (5 commands)
|
|
139
151
|
"unipi:cocoindex-update": "cocoindex",
|
|
@@ -208,6 +220,13 @@ export const COMMAND_DESCRIPTIONS: Record<string, string> = {
|
|
|
208
220
|
"unipi:global-memory-list": "List all project memories",
|
|
209
221
|
"unipi:memory-settings": "Configure memory settings",
|
|
210
222
|
|
|
223
|
+
"unipi:btw": "Run a parallel side conversation",
|
|
224
|
+
"unipi:btw-tangent": "Start a contextless BTW tangent thread",
|
|
225
|
+
"unipi:btw-new": "Start a fresh BTW thread with session context",
|
|
226
|
+
"unipi:btw-clear": "Dismiss and clear the BTW thread",
|
|
227
|
+
"unipi:btw-inject": "Inject the BTW thread into the main agent",
|
|
228
|
+
"unipi:btw-summarize": "Summarize and inject the BTW thread",
|
|
229
|
+
|
|
211
230
|
"unipi:mcp-status": "Show MCP server status",
|
|
212
231
|
"unipi:mcp-sync": "Sync MCP server connections",
|
|
213
232
|
"unipi:mcp-add": "Add a new MCP server",
|
|
@@ -238,11 +257,13 @@ export const COMMAND_DESCRIPTIONS: Record<string, string> = {
|
|
|
238
257
|
|
|
239
258
|
"unipi:lossless-compact": "Immediate zero-LLM compaction",
|
|
240
259
|
"unipi:compact": "(DEPRECATED) Use /unipi:lossless-compact instead",
|
|
241
|
-
"unipi:
|
|
260
|
+
"unipi:session-recall": "Search session history, including compacted-away messages",
|
|
261
|
+
"unipi:compact-recall": "(DEPRECATED) Use /unipi:session-recall instead",
|
|
242
262
|
"unipi:compact-stats": "Show compaction statistics",
|
|
243
263
|
"unipi:compact-doctor": "Diagnose compaction issues",
|
|
244
264
|
"unipi:compact-settings": "Configure compaction settings",
|
|
245
265
|
"unipi:compact-preset": "Manage compaction presets",
|
|
266
|
+
"unipi:compact-help": "Show compactor command help",
|
|
246
267
|
"unipi:cocoindex-update": "Run CocoIndex update to index project",
|
|
247
268
|
"unipi:cocoindex-status": "Show CocoIndex indexing status",
|
|
248
269
|
"unipi:cocoindex-init": "Initialize CocoIndex pipeline",
|
|
@@ -276,6 +297,7 @@ export const PACKAGE_LABELS: Record<string, string> = {
|
|
|
276
297
|
workflow: "workflow",
|
|
277
298
|
ralph: "ralph",
|
|
278
299
|
memory: "memory",
|
|
300
|
+
btw: "btw",
|
|
279
301
|
milestone: "milestone",
|
|
280
302
|
mcp: "mcp",
|
|
281
303
|
utility: "utility",
|
|
@@ -288,4 +310,5 @@ export const PACKAGE_LABELS: Record<string, string> = {
|
|
|
288
310
|
footer: "footer",
|
|
289
311
|
updater: "updater",
|
|
290
312
|
"input-shortcuts": "input-shortcuts",
|
|
313
|
+
cocoindex: "cocoindex",
|
|
291
314
|
};
|