@drewpayment/mink 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.
- package/README.md +347 -0
- package/package.json +32 -0
- package/src/cli.ts +176 -0
- package/src/commands/bug-search.ts +32 -0
- package/src/commands/config.ts +109 -0
- package/src/commands/cron.ts +295 -0
- package/src/commands/daemon.ts +46 -0
- package/src/commands/dashboard.ts +21 -0
- package/src/commands/designqc.ts +160 -0
- package/src/commands/detect-waste.ts +81 -0
- package/src/commands/framework-advisor.ts +52 -0
- package/src/commands/init.ts +159 -0
- package/src/commands/post-read.ts +123 -0
- package/src/commands/post-write.ts +157 -0
- package/src/commands/pre-read.ts +109 -0
- package/src/commands/pre-write.ts +136 -0
- package/src/commands/reflect.ts +39 -0
- package/src/commands/restore.ts +31 -0
- package/src/commands/scan.ts +101 -0
- package/src/commands/session-start.ts +21 -0
- package/src/commands/session-stop.ts +115 -0
- package/src/commands/status.ts +152 -0
- package/src/commands/update.ts +121 -0
- package/src/core/action-log.ts +341 -0
- package/src/core/backup.ts +122 -0
- package/src/core/bug-memory.ts +223 -0
- package/src/core/cron-parser.ts +94 -0
- package/src/core/daemon.ts +152 -0
- package/src/core/dashboard-api.ts +280 -0
- package/src/core/dashboard-server.ts +580 -0
- package/src/core/description.ts +232 -0
- package/src/core/design-eval/capture.ts +269 -0
- package/src/core/design-eval/route-detect.ts +165 -0
- package/src/core/design-eval/server-detect.ts +91 -0
- package/src/core/framework-advisor/catalog.ts +360 -0
- package/src/core/framework-advisor/decision-tree.ts +287 -0
- package/src/core/framework-advisor/generate.ts +132 -0
- package/src/core/framework-advisor/migration-prompts.ts +502 -0
- package/src/core/framework-advisor/validate.ts +137 -0
- package/src/core/fs-utils.ts +30 -0
- package/src/core/global-config.ts +74 -0
- package/src/core/index-store.ts +72 -0
- package/src/core/learning-memory.ts +120 -0
- package/src/core/paths.ts +86 -0
- package/src/core/pattern-engine.ts +108 -0
- package/src/core/project-id.ts +19 -0
- package/src/core/project-registry.ts +64 -0
- package/src/core/reflection.ts +256 -0
- package/src/core/scanner.ts +99 -0
- package/src/core/scheduler.ts +352 -0
- package/src/core/seed.ts +239 -0
- package/src/core/session.ts +128 -0
- package/src/core/stdin.ts +13 -0
- package/src/core/task-registry.ts +202 -0
- package/src/core/token-estimate.ts +36 -0
- package/src/core/token-ledger.ts +185 -0
- package/src/core/waste-detection.ts +214 -0
- package/src/core/write-exclusions.ts +24 -0
- package/src/types/action-log.ts +20 -0
- package/src/types/backup.ts +6 -0
- package/src/types/bug-memory.ts +24 -0
- package/src/types/config.ts +59 -0
- package/src/types/dashboard.ts +104 -0
- package/src/types/design-eval.ts +64 -0
- package/src/types/file-index.ts +38 -0
- package/src/types/framework-advisor.ts +97 -0
- package/src/types/hook-input.ts +27 -0
- package/src/types/learning-memory.ts +36 -0
- package/src/types/scheduler.ts +82 -0
- package/src/types/session.ts +50 -0
- package/src/types/token-ledger.ts +43 -0
- package/src/types/waste-detection.ts +21 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// ── Decision Tree for Framework Advisor (Spec 14) ────────────────────────────
|
|
2
|
+
|
|
3
|
+
import type { DecisionTreeNode, FrameworkEntry } from "../../types/framework-advisor";
|
|
4
|
+
import { getFrameworkById } from "./catalog";
|
|
5
|
+
|
|
6
|
+
// ── Tree Nodes ───────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
export const DECISION_TREE: DecisionTreeNode[] = [
|
|
9
|
+
{
|
|
10
|
+
id: "component-model",
|
|
11
|
+
question: "What component model does your project use?",
|
|
12
|
+
options: [
|
|
13
|
+
{ label: "React", value: "react", nextNodeId: "react-css" },
|
|
14
|
+
{ label: "Vue", value: "vue", nextNodeId: "vue-css" },
|
|
15
|
+
{
|
|
16
|
+
label: "Svelte",
|
|
17
|
+
value: "svelte",
|
|
18
|
+
nextNodeId: null,
|
|
19
|
+
recommends: ["skeleton-svelte"],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
label: "Angular",
|
|
23
|
+
value: "angular",
|
|
24
|
+
nextNodeId: null,
|
|
25
|
+
recommends: ["angular-material"],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
label: "Any/Framework-agnostic",
|
|
29
|
+
value: "agnostic",
|
|
30
|
+
nextNodeId: "agnostic-css",
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "react-css",
|
|
36
|
+
question: "What CSS approach do you prefer?",
|
|
37
|
+
options: [
|
|
38
|
+
{
|
|
39
|
+
label: "Utility-first (Tailwind)",
|
|
40
|
+
value: "utility-first",
|
|
41
|
+
nextNodeId: "react-utility-a11y",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: "CSS-in-JS",
|
|
45
|
+
value: "css-in-js",
|
|
46
|
+
nextNodeId: "react-cssjs-a11y",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: "CSS Modules",
|
|
50
|
+
value: "css-modules",
|
|
51
|
+
nextNodeId: null,
|
|
52
|
+
recommends: ["mantine"],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: "No preference",
|
|
56
|
+
value: "no-preference",
|
|
57
|
+
nextNodeId: "react-utility-a11y",
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: "react-utility-a11y",
|
|
63
|
+
question: "How important is accessibility compliance?",
|
|
64
|
+
options: [
|
|
65
|
+
{
|
|
66
|
+
label: "Critical (WCAG AA+)",
|
|
67
|
+
value: "critical",
|
|
68
|
+
nextNodeId: "react-utility-critical-bundle",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
label: "Important",
|
|
72
|
+
value: "important",
|
|
73
|
+
nextNodeId: null,
|
|
74
|
+
recommends: ["shadcn-ui", "tailwind-headlessui"],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: "Basic is fine",
|
|
78
|
+
value: "basic",
|
|
79
|
+
nextNodeId: null,
|
|
80
|
+
recommends: ["shadcn-ui"],
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: "react-utility-critical-bundle",
|
|
86
|
+
question: "How sensitive are you to bundle size?",
|
|
87
|
+
options: [
|
|
88
|
+
{
|
|
89
|
+
label: "Very sensitive",
|
|
90
|
+
value: "very-sensitive",
|
|
91
|
+
nextNodeId: null,
|
|
92
|
+
recommends: ["radix-ui", "tailwind-headlessui"],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
label: "Somewhat",
|
|
96
|
+
value: "somewhat",
|
|
97
|
+
nextNodeId: null,
|
|
98
|
+
recommends: ["shadcn-ui", "radix-ui"],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
label: "Not concerned",
|
|
102
|
+
value: "not-concerned",
|
|
103
|
+
nextNodeId: null,
|
|
104
|
+
recommends: ["shadcn-ui"],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "react-cssjs-a11y",
|
|
110
|
+
question: "How important is accessibility compliance?",
|
|
111
|
+
options: [
|
|
112
|
+
{
|
|
113
|
+
label: "Critical",
|
|
114
|
+
value: "critical",
|
|
115
|
+
nextNodeId: null,
|
|
116
|
+
recommends: ["chakra-ui"],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: "Important",
|
|
120
|
+
value: "important",
|
|
121
|
+
nextNodeId: "react-cssjs-maturity",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
label: "Basic is fine",
|
|
125
|
+
value: "basic",
|
|
126
|
+
nextNodeId: "react-cssjs-maturity",
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "react-cssjs-maturity",
|
|
132
|
+
question: "Do you prefer battle-tested or modern?",
|
|
133
|
+
options: [
|
|
134
|
+
{
|
|
135
|
+
label: "Battle-tested",
|
|
136
|
+
value: "battle-tested",
|
|
137
|
+
nextNodeId: null,
|
|
138
|
+
recommends: ["mui"],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
label: "Modern",
|
|
142
|
+
value: "modern",
|
|
143
|
+
nextNodeId: null,
|
|
144
|
+
recommends: ["chakra-ui"],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
label: "Enterprise-focused",
|
|
148
|
+
value: "enterprise",
|
|
149
|
+
nextNodeId: null,
|
|
150
|
+
recommends: ["ant-design"],
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "vue-css",
|
|
156
|
+
question: "What CSS approach do you prefer?",
|
|
157
|
+
options: [
|
|
158
|
+
{
|
|
159
|
+
label: "Utility-first",
|
|
160
|
+
value: "utility-first",
|
|
161
|
+
nextNodeId: null,
|
|
162
|
+
recommends: ["vuetify"],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
label: "Traditional/Scoped",
|
|
166
|
+
value: "traditional",
|
|
167
|
+
nextNodeId: "vue-scale",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
label: "No preference",
|
|
171
|
+
value: "no-preference",
|
|
172
|
+
nextNodeId: "vue-scale",
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "vue-scale",
|
|
178
|
+
question: "What scale is your application?",
|
|
179
|
+
options: [
|
|
180
|
+
{
|
|
181
|
+
label: "Enterprise / large component library needed",
|
|
182
|
+
value: "enterprise",
|
|
183
|
+
nextNodeId: null,
|
|
184
|
+
recommends: ["primevue"],
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
label: "Standard / Material Design",
|
|
188
|
+
value: "standard",
|
|
189
|
+
nextNodeId: null,
|
|
190
|
+
recommends: ["vuetify"],
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
id: "agnostic-css",
|
|
196
|
+
question: "What CSS approach do you prefer?",
|
|
197
|
+
options: [
|
|
198
|
+
{
|
|
199
|
+
label: "Utility-first (Tailwind)",
|
|
200
|
+
value: "utility-first",
|
|
201
|
+
nextNodeId: "agnostic-bundle",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
label: "Other",
|
|
205
|
+
value: "other",
|
|
206
|
+
nextNodeId: null,
|
|
207
|
+
recommends: ["park-ui"],
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
id: "agnostic-bundle",
|
|
213
|
+
question: "How sensitive are you to bundle size?",
|
|
214
|
+
options: [
|
|
215
|
+
{
|
|
216
|
+
label: "Very sensitive",
|
|
217
|
+
value: "very-sensitive",
|
|
218
|
+
nextNodeId: null,
|
|
219
|
+
recommends: ["daisyui", "flowbite"],
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
label: "Not very",
|
|
223
|
+
value: "not-very",
|
|
224
|
+
nextNodeId: null,
|
|
225
|
+
recommends: ["flowbite", "park-ui"],
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
// ── Traversal ────────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
const nodeIndex = new Map<string, DecisionTreeNode>(
|
|
234
|
+
DECISION_TREE.map((n) => [n.id, n]),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Walk the decision tree using the provided answers map.
|
|
239
|
+
* Returns an array of recommended framework IDs, or [] if the answers
|
|
240
|
+
* don't reach a terminal node.
|
|
241
|
+
*/
|
|
242
|
+
export function traverseDecisionTree(
|
|
243
|
+
answers: Record<string, string>,
|
|
244
|
+
): string[] {
|
|
245
|
+
let currentNodeId: string | null = "component-model";
|
|
246
|
+
|
|
247
|
+
while (currentNodeId !== null) {
|
|
248
|
+
const node = nodeIndex.get(currentNodeId);
|
|
249
|
+
if (!node) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const answer = answers[node.id];
|
|
254
|
+
if (answer === undefined || answer === null) {
|
|
255
|
+
// No answer for this node — tree traversal is incomplete
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const matched = node.options.find((o) => o.value === answer);
|
|
260
|
+
if (!matched) {
|
|
261
|
+
// Answer doesn't match any option — treat as incomplete
|
|
262
|
+
return [];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (matched.recommends) {
|
|
266
|
+
return matched.recommends;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
currentNodeId = matched.nextNodeId;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Reached a null nextNodeId without recommends — shouldn't happen
|
|
273
|
+
// with a well-formed tree, but handle gracefully.
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Resolve framework IDs from the decision tree into full FrameworkEntry objects.
|
|
279
|
+
*/
|
|
280
|
+
export function getRecommendations(
|
|
281
|
+
answers: Record<string, string>,
|
|
282
|
+
): FrameworkEntry[] {
|
|
283
|
+
const ids = traverseDecisionTree(answers);
|
|
284
|
+
return ids
|
|
285
|
+
.map((id) => getFrameworkById(id))
|
|
286
|
+
.filter((f): f is FrameworkEntry => f !== undefined);
|
|
287
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FrameworkAdvisorKnowledge,
|
|
3
|
+
FrameworkEntry,
|
|
4
|
+
DecisionTreeNode,
|
|
5
|
+
} from "../../types/framework-advisor";
|
|
6
|
+
import { FRAMEWORK_CATALOG } from "./catalog";
|
|
7
|
+
import { DECISION_TREE } from "./decision-tree";
|
|
8
|
+
import { MIGRATION_PROMPTS } from "./migration-prompts";
|
|
9
|
+
|
|
10
|
+
export function buildKnowledge(): FrameworkAdvisorKnowledge {
|
|
11
|
+
return {
|
|
12
|
+
version: "1.0.0",
|
|
13
|
+
generatedAt: new Date().toISOString(),
|
|
14
|
+
frameworks: FRAMEWORK_CATALOG,
|
|
15
|
+
decisionTree: DECISION_TREE,
|
|
16
|
+
migrationPrompts: MIGRATION_PROMPTS,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── Markdown Generation ───────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
export function generateKnowledgeMarkdown(
|
|
23
|
+
k: FrameworkAdvisorKnowledge
|
|
24
|
+
): string {
|
|
25
|
+
const parts: string[] = [];
|
|
26
|
+
|
|
27
|
+
parts.push(`# Framework Advisor Knowledge Base`);
|
|
28
|
+
parts.push("");
|
|
29
|
+
parts.push(
|
|
30
|
+
`> Generated: ${k.generatedAt} | Version: ${k.version} | Frameworks: ${k.frameworks.length}`
|
|
31
|
+
);
|
|
32
|
+
parts.push("");
|
|
33
|
+
|
|
34
|
+
// ── Comparison Matrix ─────────────────────────────────────────────────
|
|
35
|
+
parts.push("## Comparison Matrix");
|
|
36
|
+
parts.push("");
|
|
37
|
+
parts.push(
|
|
38
|
+
"| Framework | CSS Approach | Bundle | A11y | Dark Mode | TS | Learning Curve | Design Tokens |"
|
|
39
|
+
);
|
|
40
|
+
parts.push(
|
|
41
|
+
"|-----------|-------------|--------|------|-----------|-----|---------------|---------------|"
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
for (const fw of k.frameworks) {
|
|
45
|
+
parts.push(
|
|
46
|
+
`| ${fw.name} | ${fw.cssApproach} | ${fw.bundleSize} | ${fw.accessibilityRating} | ${fw.darkModeSupport ? "yes" : "no"} | ${fw.typescriptSupport} | ${fw.learningCurve} | ${fw.designTokens ? "yes" : "no"} |`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
parts.push("");
|
|
50
|
+
|
|
51
|
+
// ── Decision Tree ─────────────────────────────────────────────────────
|
|
52
|
+
parts.push("## Decision Tree");
|
|
53
|
+
parts.push("");
|
|
54
|
+
parts.push(
|
|
55
|
+
"Answer the following questions in order. Stop when you reach a recommendation."
|
|
56
|
+
);
|
|
57
|
+
parts.push("");
|
|
58
|
+
|
|
59
|
+
for (let i = 0; i < k.decisionTree.length; i++) {
|
|
60
|
+
const node = k.decisionTree[i];
|
|
61
|
+
parts.push(`### Q: ${node.question}`);
|
|
62
|
+
parts.push(`*(node: ${node.id})*`);
|
|
63
|
+
parts.push("");
|
|
64
|
+
for (const opt of node.options) {
|
|
65
|
+
if (opt.recommends && opt.recommends.length > 0) {
|
|
66
|
+
const names = opt.recommends
|
|
67
|
+
.map((id) => frameworkName(k.frameworks, id))
|
|
68
|
+
.join(", ");
|
|
69
|
+
parts.push(`- **${opt.label}** → Recommend: **${names}**`);
|
|
70
|
+
} else if (opt.nextNodeId) {
|
|
71
|
+
parts.push(`- **${opt.label}** → Continue to *${opt.nextNodeId}*`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
parts.push("");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Framework Details ─────────────────────────────────────────────────
|
|
78
|
+
parts.push("## Framework Details");
|
|
79
|
+
parts.push("");
|
|
80
|
+
|
|
81
|
+
for (const fw of k.frameworks) {
|
|
82
|
+
parts.push(`### ${fw.name}`);
|
|
83
|
+
parts.push("");
|
|
84
|
+
parts.push(fw.description);
|
|
85
|
+
parts.push("");
|
|
86
|
+
parts.push(`- **Ecosystem:** ${fw.ecosystem}`);
|
|
87
|
+
parts.push(
|
|
88
|
+
`- **Best for:** ${fw.bestFor.join("; ")}`
|
|
89
|
+
);
|
|
90
|
+
parts.push(
|
|
91
|
+
`- **Limitations:** ${fw.limitations.join("; ")}`
|
|
92
|
+
);
|
|
93
|
+
parts.push(`- **URL:** ${fw.officialUrl}`);
|
|
94
|
+
parts.push("");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── Migration Prompts ─────────────────────────────────────────────────
|
|
98
|
+
parts.push("## Migration Prompts");
|
|
99
|
+
parts.push("");
|
|
100
|
+
|
|
101
|
+
for (const prompt of k.migrationPrompts) {
|
|
102
|
+
const fw = k.frameworks.find((f) => f.id === prompt.frameworkId);
|
|
103
|
+
const name = fw ? fw.name : prompt.frameworkId;
|
|
104
|
+
parts.push(`### ${name} Migration`);
|
|
105
|
+
parts.push("");
|
|
106
|
+
for (const section of prompt.sections) {
|
|
107
|
+
parts.push(`**${sectionTitle(section.key)}**`);
|
|
108
|
+
parts.push("");
|
|
109
|
+
parts.push(section.content);
|
|
110
|
+
parts.push("");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return parts.join("\n");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function frameworkName(frameworks: FrameworkEntry[], id: string): string {
|
|
118
|
+
const fw = frameworks.find((f) => f.id === id);
|
|
119
|
+
return fw ? fw.name : id;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function sectionTitle(key: string): string {
|
|
123
|
+
const titles: Record<string, string> = {
|
|
124
|
+
install: "Installation",
|
|
125
|
+
configure: "Configuration",
|
|
126
|
+
"migrate-components": "Component Migration",
|
|
127
|
+
"migrate-styles": "Style Migration",
|
|
128
|
+
gotchas: "Known Gotchas",
|
|
129
|
+
verification: "Verification",
|
|
130
|
+
};
|
|
131
|
+
return titles[key] ?? key;
|
|
132
|
+
}
|