@aslomon/effectum 0.4.0 → 0.5.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/bin/install.js +46 -7
- package/bin/lib/cli-tools.js +201 -118
- package/bin/lib/tool-loader.js +243 -0
- package/bin/lib/ui.js +138 -75
- package/package.json +1 -1
- package/system/tools/_schema.json +112 -0
- package/system/tools/foundation.json +56 -0
- package/system/tools/generic.json +20 -0
- package/system/tools/nextjs-supabase.json +56 -0
- package/system/tools/python-fastapi.json +47 -0
- package/system/tools/swift-ios.json +33 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic tool loader — loads JSON-based tool definitions from system/tools/,
|
|
3
|
+
* merges foundation + stack + community definitions, and deduplicates by key.
|
|
4
|
+
*
|
|
5
|
+
* New stacks require only a new JSON file in system/tools/ — zero code changes.
|
|
6
|
+
* Community/local overrides are loaded from .effectum/tools/ and ~/.effectum/tools/.
|
|
7
|
+
*/
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const os = require("os");
|
|
13
|
+
|
|
14
|
+
// ─── JSON loading helpers ────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Load tools from a JSON file. Returns empty array if file doesn't exist or is invalid.
|
|
18
|
+
* @param {string} filePath - absolute path to JSON file
|
|
19
|
+
* @returns {Array<object>}
|
|
20
|
+
*/
|
|
21
|
+
function loadJsonTools(filePath) {
|
|
22
|
+
try {
|
|
23
|
+
if (!fs.existsSync(filePath)) return [];
|
|
24
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
if (Array.isArray(parsed.tools)) return parsed.tools;
|
|
27
|
+
if (Array.isArray(parsed)) return parsed;
|
|
28
|
+
return [];
|
|
29
|
+
} catch (_) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Load all JSON files from a directory.
|
|
36
|
+
* Skips files starting with _ (e.g., _schema.json).
|
|
37
|
+
* @param {string} dirPath - directory to scan
|
|
38
|
+
* @returns {Array<object>}
|
|
39
|
+
*/
|
|
40
|
+
function loadToolsFromDir(dirPath) {
|
|
41
|
+
const tools = [];
|
|
42
|
+
try {
|
|
43
|
+
if (!fs.existsSync(dirPath)) return tools;
|
|
44
|
+
const files = fs
|
|
45
|
+
.readdirSync(dirPath)
|
|
46
|
+
.filter((f) => f.endsWith(".json") && !f.startsWith("_"));
|
|
47
|
+
for (const file of files) {
|
|
48
|
+
tools.push(...loadJsonTools(path.join(dirPath, file)));
|
|
49
|
+
}
|
|
50
|
+
} catch (_) {
|
|
51
|
+
// Directory doesn't exist or isn't readable
|
|
52
|
+
}
|
|
53
|
+
return tools;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Tool resolution ─────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Find the system/tools/ directory relative to this module (which lives in bin/lib/).
|
|
60
|
+
* @returns {string}
|
|
61
|
+
*/
|
|
62
|
+
function getSystemToolsDir() {
|
|
63
|
+
return path.resolve(__dirname, "..", "..", "system", "tools");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the JSON filename for a stack key.
|
|
68
|
+
* @param {string} stack - e.g., "nextjs-supabase"
|
|
69
|
+
* @returns {string} - e.g., "nextjs-supabase.json"
|
|
70
|
+
*/
|
|
71
|
+
function stackToFilename(stack) {
|
|
72
|
+
return `${stack}.json`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ─── System basics (pre-config) ──────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get system-level basics that must be checked before any configuration.
|
|
79
|
+
* These are Homebrew (macOS), Git, Node.js, and Claude Code.
|
|
80
|
+
* @returns {Array<object>}
|
|
81
|
+
*/
|
|
82
|
+
function getSystemBasics() {
|
|
83
|
+
const platform = os.platform() === "darwin" ? "darwin" : "linux";
|
|
84
|
+
const basics = [];
|
|
85
|
+
|
|
86
|
+
// Homebrew (macOS only)
|
|
87
|
+
if (platform === "darwin") {
|
|
88
|
+
basics.push({
|
|
89
|
+
key: "brew",
|
|
90
|
+
bin: "brew",
|
|
91
|
+
displayName: "Homebrew",
|
|
92
|
+
category: "system",
|
|
93
|
+
why: "Package manager for macOS — needed to install other tools",
|
|
94
|
+
priority: 0,
|
|
95
|
+
autoInstall: true,
|
|
96
|
+
install: {
|
|
97
|
+
darwin:
|
|
98
|
+
'/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
|
|
99
|
+
},
|
|
100
|
+
check: "brew --version",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Git
|
|
105
|
+
basics.push({
|
|
106
|
+
key: "git",
|
|
107
|
+
bin: "git",
|
|
108
|
+
displayName: "Git",
|
|
109
|
+
category: "system",
|
|
110
|
+
why: "Version control — required for all projects",
|
|
111
|
+
priority: 0,
|
|
112
|
+
autoInstall: true,
|
|
113
|
+
install: {
|
|
114
|
+
darwin: "xcode-select --install",
|
|
115
|
+
linux: "sudo apt install -y git",
|
|
116
|
+
},
|
|
117
|
+
check: "git --version",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Node.js
|
|
121
|
+
basics.push({
|
|
122
|
+
key: "node",
|
|
123
|
+
bin: "node",
|
|
124
|
+
displayName: "Node.js",
|
|
125
|
+
category: "system",
|
|
126
|
+
why: "JavaScript runtime — required for Claude Code and npm tools",
|
|
127
|
+
priority: 0,
|
|
128
|
+
autoInstall: true,
|
|
129
|
+
install: {
|
|
130
|
+
darwin: "brew install node",
|
|
131
|
+
linux: "sudo apt install -y nodejs npm",
|
|
132
|
+
},
|
|
133
|
+
check: "node --version",
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Claude Code
|
|
137
|
+
basics.push({
|
|
138
|
+
key: "claude",
|
|
139
|
+
bin: "claude",
|
|
140
|
+
displayName: "Claude Code",
|
|
141
|
+
category: "system",
|
|
142
|
+
why: "AI coding agent — the core of the autonomous workflow",
|
|
143
|
+
priority: 0,
|
|
144
|
+
autoInstall: true,
|
|
145
|
+
install: {
|
|
146
|
+
all: "npm i -g @anthropic-ai/claude-code",
|
|
147
|
+
},
|
|
148
|
+
check: "claude --version",
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return basics;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─── Main loader ─────────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Load and merge tool definitions for a given stack.
|
|
158
|
+
*
|
|
159
|
+
* Merge order (last wins for duplicate keys):
|
|
160
|
+
* 1. foundation.json (always)
|
|
161
|
+
* 2. stack-specific.json (if exists)
|
|
162
|
+
* 3. Community: <targetDir>/.effectum/tools/*.json
|
|
163
|
+
* 4. Community: ~/.effectum/tools/*.json
|
|
164
|
+
*
|
|
165
|
+
* @param {string} stack - stack key (e.g., "nextjs-supabase", "generic")
|
|
166
|
+
* @param {string} [targetDir] - project directory for local community tools
|
|
167
|
+
* @returns {Array<object>} - deduplicated, priority-sorted tool list
|
|
168
|
+
*/
|
|
169
|
+
function loadToolDefinitions(stack, targetDir) {
|
|
170
|
+
const systemDir = getSystemToolsDir();
|
|
171
|
+
const tools = [];
|
|
172
|
+
|
|
173
|
+
// 1. Foundation (always loaded)
|
|
174
|
+
const foundationPath = path.join(systemDir, "foundation.json");
|
|
175
|
+
tools.push(...loadJsonTools(foundationPath));
|
|
176
|
+
|
|
177
|
+
// 2. Stack-specific
|
|
178
|
+
if (stack && stack !== "foundation") {
|
|
179
|
+
const stackPath = path.join(systemDir, stackToFilename(stack));
|
|
180
|
+
tools.push(...loadJsonTools(stackPath));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 3. Community: local project overrides
|
|
184
|
+
if (targetDir) {
|
|
185
|
+
const localToolsDir = path.join(targetDir, ".effectum", "tools");
|
|
186
|
+
tools.push(...loadToolsFromDir(localToolsDir));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 4. Community: global user overrides
|
|
190
|
+
const globalToolsDir = path.join(os.homedir(), ".effectum", "tools");
|
|
191
|
+
tools.push(...loadToolsFromDir(globalToolsDir));
|
|
192
|
+
|
|
193
|
+
return deduplicateByKey(tools);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ─── Deduplication ───────────────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Deduplicate tools by key. Last occurrence wins (community overrides bundled).
|
|
200
|
+
* Result is sorted by priority (ascending).
|
|
201
|
+
* @param {Array<object>} tools
|
|
202
|
+
* @returns {Array<object>}
|
|
203
|
+
*/
|
|
204
|
+
function deduplicateByKey(tools) {
|
|
205
|
+
const map = new Map();
|
|
206
|
+
for (const tool of tools) {
|
|
207
|
+
map.set(tool.key, tool);
|
|
208
|
+
}
|
|
209
|
+
return Array.from(map.values()).sort(
|
|
210
|
+
(a, b) => (a.priority ?? 5) - (b.priority ?? 5),
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ─── List available stacks ───────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* List all available stack JSON files (excluding foundation and _schema).
|
|
218
|
+
* @returns {Array<string>} - stack keys (e.g., ["nextjs-supabase", "python-fastapi"])
|
|
219
|
+
*/
|
|
220
|
+
function listAvailableStacks() {
|
|
221
|
+
const systemDir = getSystemToolsDir();
|
|
222
|
+
try {
|
|
223
|
+
return fs
|
|
224
|
+
.readdirSync(systemDir)
|
|
225
|
+
.filter(
|
|
226
|
+
(f) =>
|
|
227
|
+
f.endsWith(".json") && !f.startsWith("_") && f !== "foundation.json",
|
|
228
|
+
)
|
|
229
|
+
.map((f) => f.replace(".json", ""));
|
|
230
|
+
} catch (_) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = {
|
|
236
|
+
loadJsonTools,
|
|
237
|
+
loadToolsFromDir,
|
|
238
|
+
getSystemToolsDir,
|
|
239
|
+
getSystemBasics,
|
|
240
|
+
loadToolDefinitions,
|
|
241
|
+
deduplicateByKey,
|
|
242
|
+
listAvailableStacks,
|
|
243
|
+
};
|
package/bin/lib/ui.js
CHANGED
|
@@ -21,10 +21,14 @@ const {
|
|
|
21
21
|
} = require("./recommendation");
|
|
22
22
|
const {
|
|
23
23
|
checkAllTools,
|
|
24
|
+
checkSystemBasics,
|
|
24
25
|
formatToolStatus,
|
|
25
26
|
formatInstallInstructions,
|
|
27
|
+
formatInstallPlan,
|
|
28
|
+
formatAuthStatus,
|
|
26
29
|
installTool,
|
|
27
|
-
|
|
30
|
+
categorizeForInstall,
|
|
31
|
+
checkAllAuth,
|
|
28
32
|
} = require("./cli-tools");
|
|
29
33
|
|
|
30
34
|
/** @type {import("@clack/prompts")} */
|
|
@@ -502,103 +506,159 @@ async function askGitBranch() {
|
|
|
502
506
|
return { create: true, name };
|
|
503
507
|
}
|
|
504
508
|
|
|
505
|
-
// ───
|
|
509
|
+
// ─── System Basics Check (Phase 1) ──────────────────────────────────────────
|
|
506
510
|
|
|
507
511
|
/**
|
|
508
|
-
*
|
|
512
|
+
* Check system basics (Homebrew, Git, Node.js, Claude Code) before config.
|
|
513
|
+
* Offers to install missing basics.
|
|
514
|
+
* @returns {Promise<void>}
|
|
515
|
+
*/
|
|
516
|
+
async function showSystemCheck() {
|
|
517
|
+
const result = checkSystemBasics();
|
|
518
|
+
|
|
519
|
+
p.note(formatToolStatus(result.tools), "System Basics");
|
|
520
|
+
|
|
521
|
+
if (result.missing.length === 0) {
|
|
522
|
+
p.log.success("All system basics are installed.");
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
p.log.warn(`${result.missing.length} system tool(s) not found.`);
|
|
527
|
+
|
|
528
|
+
for (const tool of result.missing) {
|
|
529
|
+
const name = tool.displayName || tool.key;
|
|
530
|
+
const wantInstall = await p.confirm({
|
|
531
|
+
message: `Install ${name}? (${tool.why})`,
|
|
532
|
+
initialValue: true,
|
|
533
|
+
});
|
|
534
|
+
handleCancel(wantInstall);
|
|
535
|
+
|
|
536
|
+
if (wantInstall) {
|
|
537
|
+
const s = p.spinner();
|
|
538
|
+
s.start(`Installing ${name}...`);
|
|
539
|
+
const installResult = installTool(tool);
|
|
540
|
+
if (installResult.ok) {
|
|
541
|
+
tool.installed = true;
|
|
542
|
+
s.stop(`${name} installed`);
|
|
543
|
+
} else {
|
|
544
|
+
s.stop(`${name} failed: ${installResult.error || "unknown error"}`);
|
|
545
|
+
p.log.warn(`You can install ${name} manually later.`);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// ─── Consolidated Installation Plan (Phase 3) ──────────────────────────────
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Show consolidated installation plan for stack tools and install with one confirmation.
|
|
509
555
|
* @param {string} stack - selected stack key
|
|
556
|
+
* @param {string} [targetDir] - project directory for community overrides
|
|
510
557
|
* @returns {Promise<{ tools: Array<object>, missing: Array<object>, installed: Array<object> }>}
|
|
511
558
|
*/
|
|
512
|
-
async function
|
|
513
|
-
const result = checkAllTools(stack);
|
|
559
|
+
async function showInstallPlan(stack, targetDir) {
|
|
560
|
+
const result = checkAllTools(stack, targetDir);
|
|
514
561
|
|
|
515
|
-
p.note(formatToolStatus(result.tools), "
|
|
562
|
+
p.note(formatToolStatus(result.tools), "Stack Tools");
|
|
516
563
|
|
|
517
564
|
if (result.missing.length === 0) {
|
|
518
|
-
p.log.success("All
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
value: "skip",
|
|
537
|
-
label: "Skip",
|
|
538
|
-
hint: "Continue without installing",
|
|
539
|
-
},
|
|
540
|
-
],
|
|
541
|
-
initialValue: "show",
|
|
565
|
+
p.log.success("All stack tools are installed.");
|
|
566
|
+
return result;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Categorize missing tools
|
|
570
|
+
const plan = categorizeForInstall(result.tools);
|
|
571
|
+
|
|
572
|
+
// Show the consolidated plan
|
|
573
|
+
p.note(formatInstallPlan(plan), "Installation Plan");
|
|
574
|
+
|
|
575
|
+
// Auto-install with one confirmation
|
|
576
|
+
if (plan.autoInstall.length > 0) {
|
|
577
|
+
const names = plan.autoInstall
|
|
578
|
+
.map((t) => t.displayName || t.key)
|
|
579
|
+
.join(", ");
|
|
580
|
+
const confirm = await p.confirm({
|
|
581
|
+
message: `Install ${plan.autoInstall.length} tool(s)? (${names})`,
|
|
582
|
+
initialValue: true,
|
|
542
583
|
});
|
|
543
|
-
handleCancel(
|
|
584
|
+
handleCancel(confirm);
|
|
544
585
|
|
|
545
|
-
if (
|
|
546
|
-
for (const tool of
|
|
586
|
+
if (confirm) {
|
|
587
|
+
for (const tool of plan.autoInstall) {
|
|
588
|
+
const name = tool.displayName || tool.key;
|
|
547
589
|
const s = p.spinner();
|
|
548
|
-
s.start(`Installing ${
|
|
590
|
+
s.start(`Installing ${name}...`);
|
|
549
591
|
const installResult = installTool(tool);
|
|
550
592
|
if (installResult.ok) {
|
|
551
|
-
|
|
552
|
-
|
|
593
|
+
// Update in result too
|
|
594
|
+
const match = result.tools.find((t) => t.key === tool.key);
|
|
595
|
+
if (match) match.installed = true;
|
|
596
|
+
s.stop(`${name} installed`);
|
|
553
597
|
} else {
|
|
554
|
-
s.stop(
|
|
555
|
-
`${tool.key} failed: ${installResult.error || "unknown error"}`,
|
|
556
|
-
);
|
|
598
|
+
s.stop(`${name} failed: ${installResult.error || "unknown error"}`);
|
|
557
599
|
}
|
|
558
600
|
}
|
|
559
|
-
} else if (action === "show") {
|
|
560
|
-
p.note(formatInstallInstructions(result.missing), "Install Commands");
|
|
561
601
|
}
|
|
562
602
|
}
|
|
563
603
|
|
|
564
|
-
|
|
565
|
-
|
|
604
|
+
if (plan.manual.length > 0) {
|
|
605
|
+
p.log.info("Manual setup required for the tools listed above.");
|
|
606
|
+
}
|
|
566
607
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const authStatus = checkAuth(t);
|
|
570
|
-
return { ...t, ...authStatus };
|
|
571
|
-
});
|
|
608
|
+
return result;
|
|
609
|
+
}
|
|
572
610
|
|
|
573
|
-
|
|
574
|
-
(t) => t.needsAuth && !t.authenticated,
|
|
575
|
-
);
|
|
611
|
+
// ─── Auth Flow (Phase 4) ────────────────────────────────────────────────────
|
|
576
612
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
613
|
+
/**
|
|
614
|
+
* Check auth status for installed tools and guide through authentication.
|
|
615
|
+
* @param {Array<object>} tools - tools with `installed` status
|
|
616
|
+
* @returns {Promise<void>}
|
|
617
|
+
*/
|
|
618
|
+
async function showAuthCheck(tools) {
|
|
619
|
+
const authResults = checkAllAuth(tools);
|
|
620
|
+
|
|
621
|
+
if (authResults.length === 0) return;
|
|
622
|
+
|
|
623
|
+
p.note(formatAuthStatus(authResults), "Auth Status");
|
|
624
|
+
|
|
625
|
+
const unauthenticated = authResults.filter((t) => !t.authenticated);
|
|
626
|
+
|
|
627
|
+
if (unauthenticated.length === 0) {
|
|
628
|
+
p.log.success("All tools are authenticated.");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
p.log.warn(`${unauthenticated.length} tool(s) need authentication.`);
|
|
633
|
+
|
|
634
|
+
const runAuth = await p.confirm({
|
|
635
|
+
message: "Show auth commands for unauthenticated tools?",
|
|
636
|
+
initialValue: true,
|
|
637
|
+
});
|
|
638
|
+
handleCancel(runAuth);
|
|
639
|
+
|
|
640
|
+
if (runAuth) {
|
|
641
|
+
const lines = unauthenticated.map((t) => {
|
|
642
|
+
const name = t.displayName || t.key;
|
|
643
|
+
let line = ` ${name}: ${t.authSetupCmd}`;
|
|
644
|
+
if (t.authUrl) line += `\n Token: ${t.authUrl}`;
|
|
645
|
+
return line;
|
|
646
|
+
});
|
|
647
|
+
p.note(lines.join("\n"), "Run these commands manually");
|
|
600
648
|
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// ─── Legacy CLI Tool Check (kept for backward compat) ───────────────────────
|
|
601
652
|
|
|
653
|
+
/**
|
|
654
|
+
* Run CLI tool check and offer installation/auth for missing tools.
|
|
655
|
+
* @deprecated Use showSystemCheck + showInstallPlan + showAuthCheck instead.
|
|
656
|
+
* @param {string} stack - selected stack key
|
|
657
|
+
* @returns {Promise<{ tools: Array<object>, missing: Array<object>, installed: Array<object> }>}
|
|
658
|
+
*/
|
|
659
|
+
async function showCliToolCheck(stack) {
|
|
660
|
+
const result = await showInstallPlan(stack);
|
|
661
|
+
await showAuthCheck(result.tools);
|
|
602
662
|
return result;
|
|
603
663
|
}
|
|
604
664
|
|
|
@@ -668,7 +728,10 @@ module.exports = {
|
|
|
668
728
|
askSetupMode,
|
|
669
729
|
askCustomize,
|
|
670
730
|
askManual,
|
|
671
|
-
//
|
|
731
|
+
// Tool check flow
|
|
732
|
+
showSystemCheck,
|
|
733
|
+
showInstallPlan,
|
|
734
|
+
showAuthCheck,
|
|
672
735
|
showCliToolCheck,
|
|
673
736
|
// Legacy / utility prompts
|
|
674
737
|
askMcpServers,
|
package/package.json
CHANGED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "Effectum Tool Definition",
|
|
4
|
+
"description": "Schema for tool definition files in system/tools/. Each file defines tools for a specific stack or category.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["tools"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"name": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Human-readable name for this tool set (e.g., 'Foundation Tools')"
|
|
11
|
+
},
|
|
12
|
+
"description": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Brief description of when these tools are used"
|
|
15
|
+
},
|
|
16
|
+
"tools": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"required": [
|
|
21
|
+
"key",
|
|
22
|
+
"bin",
|
|
23
|
+
"displayName",
|
|
24
|
+
"category",
|
|
25
|
+
"why",
|
|
26
|
+
"priority"
|
|
27
|
+
],
|
|
28
|
+
"properties": {
|
|
29
|
+
"key": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"description": "Unique identifier for the tool (e.g., 'git', 'supabase')"
|
|
32
|
+
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Binary name to check in PATH (e.g., 'git', 'supabase')"
|
|
36
|
+
},
|
|
37
|
+
"displayName": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"description": "Human-readable name (e.g., 'Supabase CLI')"
|
|
40
|
+
},
|
|
41
|
+
"category": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"enum": ["system", "foundation", "stack", "optional"],
|
|
44
|
+
"description": "Tool category: system (OS-level), foundation (always needed), stack (stack-specific), optional (nice-to-have)"
|
|
45
|
+
},
|
|
46
|
+
"why": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "Human-readable reason why this tool is needed"
|
|
49
|
+
},
|
|
50
|
+
"priority": {
|
|
51
|
+
"type": "integer",
|
|
52
|
+
"minimum": 0,
|
|
53
|
+
"maximum": 10,
|
|
54
|
+
"description": "Installation priority (0 = highest, 10 = lowest). Determines install order."
|
|
55
|
+
},
|
|
56
|
+
"autoInstall": {
|
|
57
|
+
"type": "boolean",
|
|
58
|
+
"default": true,
|
|
59
|
+
"description": "If false, tool is recommended with a link but not auto-installed (e.g., Docker, Xcode)"
|
|
60
|
+
},
|
|
61
|
+
"install": {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"properties": {
|
|
64
|
+
"darwin": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"description": "macOS install command"
|
|
67
|
+
},
|
|
68
|
+
"linux": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "Linux install command"
|
|
71
|
+
},
|
|
72
|
+
"all": {
|
|
73
|
+
"type": "string",
|
|
74
|
+
"description": "Cross-platform install command"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"description": "Platform-specific install commands. 'all' is used as fallback."
|
|
78
|
+
},
|
|
79
|
+
"check": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"description": "Command to verify the tool is working (e.g., 'git --version'). Falls back to 'which <bin>'."
|
|
82
|
+
},
|
|
83
|
+
"manualUrl": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"format": "uri",
|
|
86
|
+
"description": "URL for manual installation instructions (used when autoInstall is false)"
|
|
87
|
+
},
|
|
88
|
+
"auth": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"properties": {
|
|
91
|
+
"check": {
|
|
92
|
+
"type": "string",
|
|
93
|
+
"description": "Command to check auth status (exit 0 = authenticated)"
|
|
94
|
+
},
|
|
95
|
+
"setup": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"description": "Command to set up authentication"
|
|
98
|
+
},
|
|
99
|
+
"url": {
|
|
100
|
+
"type": "string",
|
|
101
|
+
"format": "uri",
|
|
102
|
+
"description": "URL where user can create tokens/credentials"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"required": ["check", "setup"],
|
|
106
|
+
"description": "Authentication configuration. Omit if tool needs no auth."
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Foundation Tools",
|
|
3
|
+
"description": "Always-required tools regardless of stack. Loaded for every project.",
|
|
4
|
+
"tools": [
|
|
5
|
+
{
|
|
6
|
+
"key": "git",
|
|
7
|
+
"bin": "git",
|
|
8
|
+
"displayName": "Git",
|
|
9
|
+
"category": "foundation",
|
|
10
|
+
"why": "Version control — required for all projects",
|
|
11
|
+
"priority": 0,
|
|
12
|
+
"autoInstall": true,
|
|
13
|
+
"install": {
|
|
14
|
+
"darwin": "xcode-select --install",
|
|
15
|
+
"linux": "sudo apt install -y git"
|
|
16
|
+
},
|
|
17
|
+
"check": "git --version",
|
|
18
|
+
"auth": {
|
|
19
|
+
"check": "git config user.name && git config user.email",
|
|
20
|
+
"setup": "git config --global user.name \"Your Name\" && git config --global user.email \"you@example.com\""
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"key": "gh",
|
|
25
|
+
"bin": "gh",
|
|
26
|
+
"displayName": "GitHub CLI",
|
|
27
|
+
"category": "foundation",
|
|
28
|
+
"why": "GitHub: Issues, PRs, Code Search, CI status",
|
|
29
|
+
"priority": 1,
|
|
30
|
+
"autoInstall": true,
|
|
31
|
+
"install": {
|
|
32
|
+
"darwin": "brew install gh",
|
|
33
|
+
"linux": "sudo apt install -y gh"
|
|
34
|
+
},
|
|
35
|
+
"check": "gh --version",
|
|
36
|
+
"auth": {
|
|
37
|
+
"check": "gh auth status",
|
|
38
|
+
"setup": "gh auth login",
|
|
39
|
+
"url": "https://github.com/settings/tokens"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"key": "claude",
|
|
44
|
+
"bin": "claude",
|
|
45
|
+
"displayName": "Claude Code",
|
|
46
|
+
"category": "foundation",
|
|
47
|
+
"why": "AI coding agent — the core of the autonomous workflow",
|
|
48
|
+
"priority": 0,
|
|
49
|
+
"autoInstall": true,
|
|
50
|
+
"install": {
|
|
51
|
+
"all": "npm i -g @anthropic-ai/claude-code"
|
|
52
|
+
},
|
|
53
|
+
"check": "claude --version"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|