@harms-haus/pi-subagents 0.1.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.
@@ -0,0 +1,176 @@
1
+ /**
2
+ * /profile Slash Command
3
+ *
4
+ * Command registration for managing subagent profiles.
5
+ */
6
+
7
+ import { editProfileInteractive } from "../profile-editor";
8
+ import { deleteProfile, formatProfileDetail, loadProfiles, profileSummary } from "../profiles";
9
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
10
+
11
+ /** Subset of ExtensionCommandContext used by the profile command */
12
+ type ProfileCommandContext = Pick<ExtensionCommandContext, "cwd" | "ui">;
13
+
14
+ /** Handle /profile list subcommand. */
15
+ function handleList(ctx: ProfileCommandContext): void {
16
+ const profiles = loadProfiles(ctx.cwd);
17
+ const names = Object.keys(profiles);
18
+ if (names.length === 0) {
19
+ ctx.ui.notify(
20
+ "No subagent profiles found. Add .md files to ~/.pi/agent/agent-profiles/ or use /profile create.",
21
+ "info",
22
+ );
23
+ return;
24
+ }
25
+ const lines = names
26
+ .map((n) => {
27
+ const p = profiles[n];
28
+ return p ? ` ${profileSummary(n, p)}` : null;
29
+ })
30
+ .filter((l): l is string => l !== null);
31
+ ctx.ui.notify(`Subagent profiles:\n${lines.join("\n")}`, "info");
32
+ }
33
+
34
+ /** Handle /profile show <name> subcommand. */
35
+ function handleShow(tokens: string[], ctx: ProfileCommandContext): void {
36
+ const name = tokens[1];
37
+ if (!name) {
38
+ ctx.ui.notify("Usage: /profile show <name>", "warning");
39
+ return;
40
+ }
41
+ const profiles = loadProfiles(ctx.cwd);
42
+ if (!Object.hasOwn(profiles, name)) {
43
+ ctx.ui.notify(
44
+ `Profile "${name}" not found. Available: ${Object.keys(profiles).join(", ") || "(none)"}`,
45
+ "error",
46
+ );
47
+ return;
48
+ }
49
+ const profile = profiles[name];
50
+ if (profile) {
51
+ ctx.ui.notify(formatProfileDetail(name, profile), "info");
52
+ }
53
+ }
54
+
55
+ /** Handle /profile create <name> subcommand. */
56
+ async function handleCreate(tokens: string[], ctx: ProfileCommandContext): Promise<void> {
57
+ const name = tokens[1];
58
+ if (!(name && /^[a-zA-Z0-9_-]+$/.test(name))) {
59
+ ctx.ui.notify("Usage: /profile create <name> (alphanumeric, hyphens, underscores)", "warning");
60
+ return;
61
+ }
62
+ const profiles = loadProfiles(ctx.cwd);
63
+ if (Object.hasOwn(profiles, name)) {
64
+ ctx.ui.notify(
65
+ `Profile "${name}" already exists. Use /profile edit ${name} to modify it.`,
66
+ "warning",
67
+ );
68
+ return;
69
+ }
70
+ await editProfileInteractive(name, {}, ctx);
71
+ }
72
+
73
+ /** Handle /profile edit <name> subcommand. */
74
+ async function handleEdit(tokens: string[], ctx: ProfileCommandContext): Promise<void> {
75
+ const name = tokens[1];
76
+ if (!name) {
77
+ ctx.ui.notify("Usage: /profile edit <name>", "warning");
78
+ return;
79
+ }
80
+ const profiles = loadProfiles(ctx.cwd);
81
+ if (!Object.hasOwn(profiles, name)) {
82
+ ctx.ui.notify(
83
+ `Profile "${name}" not found. Use /profile create ${name} to create it.`,
84
+ "error",
85
+ );
86
+ return;
87
+ }
88
+ await editProfileInteractive(name, { ...profiles[name] }, ctx);
89
+ }
90
+
91
+ /** Handle /profile delete <name> subcommand. */
92
+ async function handleDelete(tokens: string[], ctx: ProfileCommandContext): Promise<void> {
93
+ const name = tokens[1];
94
+ if (!name) {
95
+ ctx.ui.notify("Usage: /profile delete <name>", "warning");
96
+ return;
97
+ }
98
+ const ok = await ctx.ui.confirm("Delete profile?", `Delete subagent profile "${name}"?`);
99
+ if (!ok) {
100
+ return;
101
+ }
102
+ const deleted = await deleteProfile(name, "global");
103
+ const deletedProject = await deleteProfile(name, "project", ctx.cwd);
104
+ if (deleted || deletedProject) {
105
+ ctx.ui.notify(`Profile "${name}" deleted.`, "info");
106
+ } else {
107
+ ctx.ui.notify(`Profile "${name}" not found.`, "error");
108
+ }
109
+ }
110
+
111
+ /** Handle bare name: /profile <name> (alias for show). Returns true if handled. */
112
+ function handleBareName(sub: string, ctx: ProfileCommandContext): boolean {
113
+ const profiles = loadProfiles(ctx.cwd);
114
+ if (Object.hasOwn(profiles, sub)) {
115
+ const profile = profiles[sub];
116
+ if (profile) {
117
+ ctx.ui.notify(formatProfileDetail(sub, profile), "info");
118
+ }
119
+ return true;
120
+ }
121
+ return false;
122
+ }
123
+
124
+ const SUBCOMMAND_RE = /^(list|show|create|edit|delete|ls|new|rm|remove)$/;
125
+
126
+ /**
127
+ * Register the /profile command.
128
+ */
129
+ export function registerProfileCommand(pi: ExtensionAPI): void {
130
+ pi.registerCommand("profile", {
131
+ description: "Manage subagent profiles (list, show, create, edit, delete)",
132
+
133
+ getArgumentCompletions(prefix: string) {
134
+ const profiles = loadProfiles();
135
+ const subs = ["list", "show", "create", "edit", "delete"];
136
+ const items = [...subs, ...Object.keys(profiles)]
137
+ .filter((s) => s.startsWith(prefix))
138
+ .map((s) => ({ value: s, label: s }));
139
+ return items.length > 0 ? items : null;
140
+ },
141
+
142
+ handler: async (args: string, ctx: ProfileCommandContext) => {
143
+ const tokens = args.trim().split(/\s+/);
144
+ const sub = tokens[0] ?? "list";
145
+
146
+ if (sub === "list" || sub === "ls") {
147
+ handleList(ctx);
148
+ return;
149
+ }
150
+ if (sub === "show") {
151
+ handleShow(tokens, ctx);
152
+ return;
153
+ }
154
+ if (sub === "create" || sub === "new") {
155
+ await handleCreate(tokens, ctx);
156
+ return;
157
+ }
158
+ if (sub === "edit") {
159
+ await handleEdit(tokens, ctx);
160
+ return;
161
+ }
162
+ if (sub === "delete" || sub === "rm" || sub === "remove") {
163
+ await handleDelete(tokens, ctx);
164
+ return;
165
+ }
166
+ if (sub && !SUBCOMMAND_RE.test(sub) && handleBareName(sub, ctx)) {
167
+ return;
168
+ }
169
+
170
+ ctx.ui.notify(
171
+ "Usage: /profile [list|show <name>|create <name>|edit <name>|delete <name>]",
172
+ "warning",
173
+ );
174
+ },
175
+ });
176
+ }