@mrclrchtr/supi-flow 0.6.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.
- package/README.md +175 -0
- package/package.json +32 -0
- package/prompts/supi-coding-retro.md +18 -0
- package/skills/supi-flow-apply/SKILL.md +119 -0
- package/skills/supi-flow-archive/SKILL.md +101 -0
- package/skills/supi-flow-brainstorm/SKILL.md +117 -0
- package/skills/supi-flow-debug/SKILL.md +151 -0
- package/skills/supi-flow-plan/SKILL.md +117 -0
- package/skills/supi-flow-slop-detect/SKILL.md +393 -0
- package/skills/supi-flow-slop-detect/references/vocabulary.json +161 -0
- package/skills/supi-flow-slop-detect/scripts/slop-helpers.ts +301 -0
- package/skills/supi-flow-slop-detect/scripts/slop-scan-structural.ts +269 -0
- package/skills/supi-flow-slop-detect/scripts/slop-scan-vocab.ts +161 -0
- package/skills/supi-flow-slop-detect/scripts/slop-scan.ts +209 -0
- package/src/cli.ts +80 -0
- package/src/index.ts +167 -0
- package/src/tools/flow-tools.ts +337 -0
- package/src/tools/tndm-cli.ts +246 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { type Static, Type } from "typebox";
|
|
2
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
3
|
+
import { tndm, tndmJson } from "../cli.js";
|
|
4
|
+
|
|
5
|
+
export const actionEnum = StringEnum([
|
|
6
|
+
"create",
|
|
7
|
+
"update",
|
|
8
|
+
"show",
|
|
9
|
+
"list",
|
|
10
|
+
"awareness",
|
|
11
|
+
"doc_create",
|
|
12
|
+
"sync",
|
|
13
|
+
] as const);
|
|
14
|
+
|
|
15
|
+
export const supi_tndm_cli_params = Type.Object({
|
|
16
|
+
action: actionEnum,
|
|
17
|
+
// Common identifiers
|
|
18
|
+
id: Type.Optional(Type.String({ description: "Ticket ID (required for update/show)" })),
|
|
19
|
+
|
|
20
|
+
// Create / Update params
|
|
21
|
+
title: Type.Optional(Type.String({ description: "Ticket title (required for create)" })),
|
|
22
|
+
status: Type.Optional(
|
|
23
|
+
StringEnum(["todo", "in_progress", "blocked", "done"] as const, {
|
|
24
|
+
description: "Ticket status",
|
|
25
|
+
}),
|
|
26
|
+
),
|
|
27
|
+
priority: Type.Optional(
|
|
28
|
+
StringEnum(["p0", "p1", "p2", "p3", "p4"] as const, {
|
|
29
|
+
description: "Priority (p0=critical)",
|
|
30
|
+
}),
|
|
31
|
+
),
|
|
32
|
+
type: Type.Optional(
|
|
33
|
+
StringEnum(["task", "bug", "feature", "chore", "epic"] as const, {
|
|
34
|
+
description: "Ticket type",
|
|
35
|
+
}),
|
|
36
|
+
),
|
|
37
|
+
tags: Type.Optional(
|
|
38
|
+
Type.String({
|
|
39
|
+
description: "Comma-separated tags (replaces existing list; e.g. 'auth,security,flow:brainstorm')",
|
|
40
|
+
}),
|
|
41
|
+
),
|
|
42
|
+
add_tags: Type.Optional(
|
|
43
|
+
Type.String({
|
|
44
|
+
description: "Comma-separated tags to add (preserves existing tags)",
|
|
45
|
+
}),
|
|
46
|
+
),
|
|
47
|
+
remove_tags: Type.Optional(
|
|
48
|
+
Type.String({
|
|
49
|
+
description: "Comma-separated tags to remove from existing list",
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
depends_on: Type.Optional(
|
|
53
|
+
Type.String({ description: "Comma-separated ticket IDs this ticket depends on" }),
|
|
54
|
+
),
|
|
55
|
+
effort: Type.Optional(
|
|
56
|
+
StringEnum(["xs", "s", "m", "l", "xl"] as const, {
|
|
57
|
+
description: "Effort estimate",
|
|
58
|
+
}),
|
|
59
|
+
),
|
|
60
|
+
content: Type.Optional(
|
|
61
|
+
Type.String({ description: "Markdown content body for the ticket" }),
|
|
62
|
+
),
|
|
63
|
+
|
|
64
|
+
// Document params
|
|
65
|
+
name: Type.Optional(
|
|
66
|
+
Type.String({ description: "Document name for doc_create (e.g. 'plan', 'archive')" }),
|
|
67
|
+
),
|
|
68
|
+
|
|
69
|
+
// List params
|
|
70
|
+
all: Type.Optional(Type.Boolean({ description: "Include done tickets in list" })),
|
|
71
|
+
definition: Type.Optional(
|
|
72
|
+
StringEnum(["ready", "questions", "unknown"] as const, {
|
|
73
|
+
description: "Filter by definition state",
|
|
74
|
+
}),
|
|
75
|
+
),
|
|
76
|
+
|
|
77
|
+
// Awareness params
|
|
78
|
+
against: Type.Optional(
|
|
79
|
+
Type.String({ description: "Git ref to run awareness against (required for awareness)" }),
|
|
80
|
+
),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* supi_tndm_cli — thin wrapper around the tndm CLI.
|
|
85
|
+
*
|
|
86
|
+
* Actions map to tndm subcommands:
|
|
87
|
+
* create → tndm ticket create <title> [--status] [--priority] [--type] [--tags] [--depends-on] [--effort] [--content]
|
|
88
|
+
* update → tndm ticket update <id> [--title] [--status] [--priority] [--type] [--tags] [--add-tags] [--remove-tags] [--depends-on] [--effort] [--content]
|
|
89
|
+
* show → tndm ticket show <id> --json
|
|
90
|
+
* list → tndm ticket list [--all] [--definition <state>] --json
|
|
91
|
+
* awareness → tndm awareness --against <ref> --json
|
|
92
|
+
*/
|
|
93
|
+
export type TndmCliParams = Static<typeof supi_tndm_cli_params>;
|
|
94
|
+
|
|
95
|
+
export async function executeTndmCli(params: TndmCliParams) {
|
|
96
|
+
const { action } = params;
|
|
97
|
+
|
|
98
|
+
switch (action) {
|
|
99
|
+
case "create": {
|
|
100
|
+
if (!params.title) {
|
|
101
|
+
throw new Error("supi_tndm_cli: title is required for create");
|
|
102
|
+
}
|
|
103
|
+
const args: string[] = ["ticket", "create", params.title];
|
|
104
|
+
addOptionalFlags(args, params, [
|
|
105
|
+
"status",
|
|
106
|
+
"priority",
|
|
107
|
+
"type",
|
|
108
|
+
"tags",
|
|
109
|
+
"depends_on",
|
|
110
|
+
"effort",
|
|
111
|
+
"content",
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
const result = await tndm(args);
|
|
115
|
+
return {
|
|
116
|
+
content: [{ type: "text" as const, text: result.stdout || result.stderr }],
|
|
117
|
+
details: { action: "create", ticketId: result.stdout.trim() },
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
case "update": {
|
|
122
|
+
if (!params.id) {
|
|
123
|
+
throw new Error("supi_tndm_cli: id is required for update");
|
|
124
|
+
}
|
|
125
|
+
const args: string[] = ["ticket", "update", params.id];
|
|
126
|
+
addOptionalFlags(args, params, [
|
|
127
|
+
"title",
|
|
128
|
+
"status",
|
|
129
|
+
"priority",
|
|
130
|
+
"type",
|
|
131
|
+
"tags",
|
|
132
|
+
"add_tags",
|
|
133
|
+
"remove_tags",
|
|
134
|
+
"depends_on",
|
|
135
|
+
"effort",
|
|
136
|
+
"content",
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
const result = await tndm(args);
|
|
140
|
+
return {
|
|
141
|
+
content: [{ type: "text" as const, text: result.stdout || "Ticket updated" }],
|
|
142
|
+
details: { action: "update", ticketId: params.id },
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
case "show": {
|
|
147
|
+
if (!params.id) {
|
|
148
|
+
throw new Error("supi_tndm_cli: id is required for show");
|
|
149
|
+
}
|
|
150
|
+
const result = await tndmJson<Record<string, unknown>>([
|
|
151
|
+
"ticket",
|
|
152
|
+
"show",
|
|
153
|
+
params.id,
|
|
154
|
+
]);
|
|
155
|
+
return {
|
|
156
|
+
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
|
157
|
+
details: { action: "show", ticket: result },
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
case "list": {
|
|
162
|
+
const args: string[] = ["ticket", "list"];
|
|
163
|
+
if (params.all) args.push("--all");
|
|
164
|
+
if (params.definition) args.push("--definition", params.definition);
|
|
165
|
+
|
|
166
|
+
const result = await tndmJson<Record<string, unknown>[]>(args);
|
|
167
|
+
return {
|
|
168
|
+
content: [
|
|
169
|
+
{
|
|
170
|
+
type: "text" as const,
|
|
171
|
+
text:
|
|
172
|
+
result.length > 0
|
|
173
|
+
? JSON.stringify(result, null, 2)
|
|
174
|
+
: "No tickets found.",
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
details: { action: "list", tickets: result },
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
case "awareness": {
|
|
182
|
+
if (!params.against) {
|
|
183
|
+
throw new Error("supi_tndm_cli: --against is required for awareness");
|
|
184
|
+
}
|
|
185
|
+
const result = await tndmJson<Record<string, unknown>>([
|
|
186
|
+
"awareness",
|
|
187
|
+
"--against",
|
|
188
|
+
params.against,
|
|
189
|
+
]);
|
|
190
|
+
return {
|
|
191
|
+
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
|
192
|
+
details: { action: "awareness", awareness: result },
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
case "doc_create": {
|
|
197
|
+
if (!params.id) {
|
|
198
|
+
throw new Error("supi_tndm_cli: id is required for doc_create");
|
|
199
|
+
}
|
|
200
|
+
if (!params.name) {
|
|
201
|
+
throw new Error("supi_tndm_cli: name is required for doc_create");
|
|
202
|
+
}
|
|
203
|
+
const result = await tndm(["ticket", "doc", "create", params.id, params.name]);
|
|
204
|
+
return {
|
|
205
|
+
content: [
|
|
206
|
+
{
|
|
207
|
+
type: "text" as const,
|
|
208
|
+
text: result.stdout || `Document '${params.name}' created for ${params.id}`,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
details: {
|
|
212
|
+
action: "doc_create",
|
|
213
|
+
ticketId: params.id,
|
|
214
|
+
name: params.name,
|
|
215
|
+
path: result.stdout.trim(),
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
case "sync": {
|
|
221
|
+
if (!params.id) {
|
|
222
|
+
throw new Error("supi_tndm_cli: id is required for sync");
|
|
223
|
+
}
|
|
224
|
+
const result = await tndm(["ticket", "sync", params.id]);
|
|
225
|
+
return {
|
|
226
|
+
content: [
|
|
227
|
+
{ type: "text" as const, text: result.stdout || `Ticket ${params.id} synced` },
|
|
228
|
+
],
|
|
229
|
+
details: { action: "sync", ticketId: params.id },
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function addOptionalFlags(
|
|
236
|
+
args: string[],
|
|
237
|
+
params: TndmCliParams,
|
|
238
|
+
flags: Array<keyof TndmCliParams>,
|
|
239
|
+
): void {
|
|
240
|
+
for (const flag of flags) {
|
|
241
|
+
const value = params[flag];
|
|
242
|
+
if (value === undefined || value === null || value === false) continue;
|
|
243
|
+
const flagName = String(flag).replace(/_/g, "-");
|
|
244
|
+
args.push(`--${flagName}`, String(value));
|
|
245
|
+
}
|
|
246
|
+
}
|