@grainulation/grainulation 1.0.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/LICENSE +21 -0
- package/README.md +108 -0
- package/bin/grainulation.js +52 -0
- package/lib/doctor.js +245 -0
- package/lib/ecosystem.js +122 -0
- package/lib/pm.js +237 -0
- package/lib/router.js +586 -0
- package/lib/server.mjs +522 -0
- package/lib/setup.js +130 -0
- package/package.json +44 -0
- package/public/grainulation-tokens.css +321 -0
- package/public/index.html +1139 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 grainulation contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# grainulation
|
|
2
|
+
|
|
3
|
+
**Structured research for decisions that satisfice.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Slow down and trust the process
|
|
8
|
+
|
|
9
|
+
Most decisions fail not because the team lacked data, but because they lacked a process for turning data into evidence and evidence into conviction.
|
|
10
|
+
|
|
11
|
+
Grainulation is that process.
|
|
12
|
+
|
|
13
|
+
You start with a question. Not an answer, not a hypothesis -- a question. Then you grow evidence: claims with types, confidence levels, and evidence tiers. You challenge what you find. You look for blind spots. You corroborate with external sources. And only when the evidence compiles -- when conflicts are resolved and gaps are acknowledged -- do you write the brief.
|
|
14
|
+
|
|
15
|
+
The brief is not the goal. The brief is the receipt. The goal is the thinking that got you there.
|
|
16
|
+
|
|
17
|
+
## The journey
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
flowchart LR
|
|
21
|
+
Q["Question"] -->|"/init"| S["Seed Claims"]
|
|
22
|
+
S -->|"/research"| C["Grow Evidence"]
|
|
23
|
+
C -->|"/compile"| B["Compile Brief"]
|
|
24
|
+
C -->|"/challenge /blind-spot"| A["Adversarial Pressure"]
|
|
25
|
+
A -->|"/witness /feedback"| C
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Every step is tracked. Every claim has provenance. Every decision is reproducible.
|
|
29
|
+
|
|
30
|
+
## The ecosystem
|
|
31
|
+
|
|
32
|
+
Eight tools. Each does one thing. Use what you need.
|
|
33
|
+
|
|
34
|
+
| Tool | What it does | Install |
|
|
35
|
+
|------|-------------|---------|
|
|
36
|
+
| **wheat** | Grows evidence. Research sprint engine. | `npx @grainulation/wheat init` |
|
|
37
|
+
| **farmer** | Permission dashboard. Approve AI actions in real time. | `npm i -g @grainulation/farmer` |
|
|
38
|
+
| **barn** | Shared tools. Claim schemas, templates, validators. | `npm i -g @grainulation/barn` |
|
|
39
|
+
| **mill** | Processes output. Export to PDF, slides, wiki. | `npm i -g @grainulation/mill` |
|
|
40
|
+
| **silo** | Stores knowledge. Reusable claim libraries. | `npm i -g @grainulation/silo` |
|
|
41
|
+
| **harvest** | Analytics. Cross-sprint learning and prediction scoring. | `npm i -g @grainulation/harvest` |
|
|
42
|
+
| **orchard** | Orchestration. Multi-sprint coordination. | `npm i -g @grainulation/orchard` |
|
|
43
|
+
| **grainulation** | The machine. Unified CLI and brand. | `npm i -g grainulation` |
|
|
44
|
+
|
|
45
|
+
**You don't need all eight.** Start with wheat. That's it. One command:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npx @grainulation/wheat init
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Everything else is optional. Add tools when you feel the friction.
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Start a research sprint
|
|
57
|
+
npx @grainulation/wheat init
|
|
58
|
+
|
|
59
|
+
# Or install the unified CLI first
|
|
60
|
+
npm install -g @grainulation/grainulation
|
|
61
|
+
|
|
62
|
+
# See what's installed
|
|
63
|
+
grainulation doctor
|
|
64
|
+
|
|
65
|
+
# Interactive setup based on your role
|
|
66
|
+
grainulation setup
|
|
67
|
+
|
|
68
|
+
# Delegate to any tool
|
|
69
|
+
grainulation wheat init
|
|
70
|
+
grainulation farmer start
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## The unified CLI
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
grainulation # Ecosystem overview
|
|
77
|
+
grainulation doctor # Health check: which tools, which versions
|
|
78
|
+
grainulation setup # Install the right tools for your role
|
|
79
|
+
grainulation <tool> ... # Delegate to any grainulation tool
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The CLI is the wayfinder. It doesn't do the work -- it points you to the tool that does.
|
|
83
|
+
|
|
84
|
+
## Philosophy
|
|
85
|
+
|
|
86
|
+
**Satisficing over maximizing.** You will never have perfect information. The goal is enough evidence to make a defensible decision, not a perfect one.
|
|
87
|
+
|
|
88
|
+
**Claims over opinions.** Every finding is a typed claim with an evidence tier. "I think" becomes "r003: factual, tested -- measured 340ms p95 latency under load."
|
|
89
|
+
|
|
90
|
+
**Adversarial pressure over consensus.** The `/challenge` command exists because comfortable agreement is the enemy of good decisions. If nobody is stress-testing the claims, the research isn't done.
|
|
91
|
+
|
|
92
|
+
**Process over heroics.** A reproducible sprint that anyone can pick up beats a brilliant analysis that lives in one person's head.
|
|
93
|
+
|
|
94
|
+
## Zero dependencies
|
|
95
|
+
|
|
96
|
+
Every grainulation tool runs on Node built-ins only. No npm install waterfall. No left-pad. No supply chain anxiety. Just `node`, `fs`, `http`, and `crypto`.
|
|
97
|
+
|
|
98
|
+
## The name
|
|
99
|
+
|
|
100
|
+
The name comes last.
|
|
101
|
+
|
|
102
|
+
You build the crop (wheat), the steward (farmer), the barn, the mill, the silo, the harvest, the orchard -- and only then do you name the machine that connects them all.
|
|
103
|
+
|
|
104
|
+
Grainulation: the machine that processes the grain.
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* grainulation
|
|
7
|
+
*
|
|
8
|
+
* The unified entry point for the grainulation ecosystem.
|
|
9
|
+
* Routes to the right tool based on what you need.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* grainulation Show ecosystem overview
|
|
13
|
+
* grainulation doctor Check which tools are installed
|
|
14
|
+
* grainulation setup Interactive setup wizard
|
|
15
|
+
* grainulation serve Start the ecosystem control center
|
|
16
|
+
* grainulation <tool> [args] Delegate to a grainulation tool
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const verbose = process.argv.includes('--verbose') || process.argv.includes('-v');
|
|
20
|
+
const jsonMode = process.argv.includes('--json');
|
|
21
|
+
function vlog(...a) {
|
|
22
|
+
if (!verbose) return;
|
|
23
|
+
const ts = new Date().toISOString();
|
|
24
|
+
process.stderr.write(`[${ts}] grainulation: ${a.join(' ')}\n`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const command = process.argv[2];
|
|
28
|
+
vlog('startup', `command=${command || '(none)'}`, `cwd=${process.cwd()}`);
|
|
29
|
+
|
|
30
|
+
// Serve command — start the HTTP server (ESM module)
|
|
31
|
+
if (command === 'serve') {
|
|
32
|
+
const path = require('node:path');
|
|
33
|
+
const { spawn } = require('node:child_process');
|
|
34
|
+
const serverPath = path.join(__dirname, '..', 'lib', 'server.mjs');
|
|
35
|
+
|
|
36
|
+
// Forward remaining args to the server
|
|
37
|
+
const serverArgs = process.argv.slice(3);
|
|
38
|
+
const child = spawn(process.execPath, [serverPath, ...serverArgs], {
|
|
39
|
+
stdio: 'inherit',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
child.on('close', (code) => process.exit(code ?? 0));
|
|
43
|
+
child.on('error', (err) => {
|
|
44
|
+
console.error(`grainulation: failed to start server: ${err.message}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
});
|
|
47
|
+
} else {
|
|
48
|
+
const { route } = require('../lib/router');
|
|
49
|
+
// Strip --json from args before routing (it's handled as a mode flag)
|
|
50
|
+
const args = process.argv.slice(2).filter((a) => a !== '--json');
|
|
51
|
+
route(args, { json: jsonMode });
|
|
52
|
+
}
|
package/lib/doctor.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { execSync } = require('node:child_process');
|
|
4
|
+
const { existsSync } = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { getInstallable } = require('./ecosystem');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Health check.
|
|
10
|
+
*
|
|
11
|
+
* Scans the system for installed grainulation tools using multiple
|
|
12
|
+
* detection methods: global npm, npx cache, local node_modules,
|
|
13
|
+
* source checkout, and npx --no-install availability.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Try to detect a package via global npm install.
|
|
18
|
+
* Returns { version, method } or null.
|
|
19
|
+
*/
|
|
20
|
+
function checkGlobal(packageName) {
|
|
21
|
+
try {
|
|
22
|
+
const out = execSync(`npm list -g ${packageName} --depth=0 2>/dev/null`, {
|
|
23
|
+
stdio: 'pipe',
|
|
24
|
+
encoding: 'utf-8',
|
|
25
|
+
});
|
|
26
|
+
const match = out.match(new RegExp(`${escapeRegex(packageName)}@(\\S+)`));
|
|
27
|
+
return match ? { version: match[1], method: 'global' } : null;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check if the package exists in the npx cache (_npx directories).
|
|
35
|
+
* Returns { version, method } or null.
|
|
36
|
+
*/
|
|
37
|
+
function checkNpxCache(packageName) {
|
|
38
|
+
try {
|
|
39
|
+
const prefix = execSync('npm config get cache', {
|
|
40
|
+
stdio: 'pipe',
|
|
41
|
+
encoding: 'utf-8',
|
|
42
|
+
}).trim();
|
|
43
|
+
const npxDir = path.join(prefix, '_npx');
|
|
44
|
+
if (!existsSync(npxDir)) return null;
|
|
45
|
+
|
|
46
|
+
// npx cache has hash-named directories, each with node_modules
|
|
47
|
+
const { readdirSync } = require('node:fs');
|
|
48
|
+
const entries = readdirSync(npxDir, { withFileTypes: true });
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (!entry.isDirectory()) continue;
|
|
51
|
+
const pkgJson = path.join(npxDir, entry.name, 'node_modules', packageName, 'package.json');
|
|
52
|
+
if (existsSync(pkgJson)) {
|
|
53
|
+
try {
|
|
54
|
+
const pkg = JSON.parse(require('node:fs').readFileSync(pkgJson, 'utf-8'));
|
|
55
|
+
return { version: pkg.version || 'installed', method: 'npx cache' };
|
|
56
|
+
} catch {
|
|
57
|
+
return { version: 'installed', method: 'npx cache' };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check if the package exists in local node_modules (project-level install).
|
|
69
|
+
* Returns { version, method } or null.
|
|
70
|
+
*/
|
|
71
|
+
function checkLocal(packageName) {
|
|
72
|
+
try {
|
|
73
|
+
const pkgJson = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
|
|
74
|
+
if (existsSync(pkgJson)) {
|
|
75
|
+
const pkg = JSON.parse(require('node:fs').readFileSync(pkgJson, 'utf-8'));
|
|
76
|
+
return { version: pkg.version || 'installed', method: 'local' };
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if the package is being run from a source/git checkout.
|
|
86
|
+
* Looks for package.json in common source locations.
|
|
87
|
+
* Returns { version, method } or null.
|
|
88
|
+
*/
|
|
89
|
+
function checkSource(packageName) {
|
|
90
|
+
const candidates = [
|
|
91
|
+
// Sibling directory (monorepo or co-located checkouts)
|
|
92
|
+
path.join(process.cwd(), '..', packageName.replace(/^@[^/]+\//, '')),
|
|
93
|
+
path.join(process.cwd(), '..', packageName.replace(/^@[^/]+\//, ''), 'package.json'),
|
|
94
|
+
// Packages dir (monorepo)
|
|
95
|
+
path.join(process.cwd(), 'packages', packageName.replace(/^@[^/]+\//, '')),
|
|
96
|
+
];
|
|
97
|
+
for (const candidate of candidates) {
|
|
98
|
+
const pkgJson = candidate.endsWith('package.json')
|
|
99
|
+
? candidate
|
|
100
|
+
: path.join(candidate, 'package.json');
|
|
101
|
+
if (existsSync(pkgJson)) {
|
|
102
|
+
try {
|
|
103
|
+
const pkg = JSON.parse(require('node:fs').readFileSync(pkgJson, 'utf-8'));
|
|
104
|
+
if (pkg.name === packageName) {
|
|
105
|
+
return { version: pkg.version || 'installed', method: 'source' };
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
// not a match, continue
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Try `npx --no-install <package> --version` to check availability
|
|
117
|
+
* without downloading anything.
|
|
118
|
+
* Returns { version, method } or null.
|
|
119
|
+
*/
|
|
120
|
+
function checkNpxNoInstall(packageName) {
|
|
121
|
+
try {
|
|
122
|
+
const out = execSync(`npx --no-install ${packageName} --version 2>/dev/null`, {
|
|
123
|
+
stdio: 'pipe',
|
|
124
|
+
encoding: 'utf-8',
|
|
125
|
+
timeout: 5000,
|
|
126
|
+
}).trim();
|
|
127
|
+
// Expect a version-like string
|
|
128
|
+
const match = out.match(/v?(\d+\.\d+\.\d+\S*)/);
|
|
129
|
+
return match ? { version: match[1], method: 'npx' } : null;
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function escapeRegex(str) {
|
|
136
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Detect a package using all available methods, in priority order.
|
|
141
|
+
* Returns { version, method } or null.
|
|
142
|
+
*/
|
|
143
|
+
function detect(packageName) {
|
|
144
|
+
return (
|
|
145
|
+
checkGlobal(packageName) ||
|
|
146
|
+
checkNpxCache(packageName) ||
|
|
147
|
+
checkLocal(packageName) ||
|
|
148
|
+
checkSource(packageName) ||
|
|
149
|
+
checkNpxNoInstall(packageName)
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Legacy API: returns version string or null.
|
|
155
|
+
* Kept for backward compatibility with tests.
|
|
156
|
+
*/
|
|
157
|
+
function getVersion(packageName) {
|
|
158
|
+
const result = detect(packageName);
|
|
159
|
+
return result ? result.version : null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getNodeVersion() {
|
|
163
|
+
return process.version;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function getNpmVersion() {
|
|
167
|
+
try {
|
|
168
|
+
return execSync('npm --version', { stdio: 'pipe', encoding: 'utf-8' }).trim();
|
|
169
|
+
} catch {
|
|
170
|
+
return 'not found';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function run(opts) {
|
|
175
|
+
const json = opts && opts.json;
|
|
176
|
+
const tools = getInstallable();
|
|
177
|
+
|
|
178
|
+
if (json) {
|
|
179
|
+
const toolResults = [];
|
|
180
|
+
for (const tool of tools) {
|
|
181
|
+
const result = detect(tool.package);
|
|
182
|
+
toolResults.push({
|
|
183
|
+
name: tool.name,
|
|
184
|
+
package: tool.package,
|
|
185
|
+
installed: !!result,
|
|
186
|
+
version: result ? result.version : null,
|
|
187
|
+
method: result ? result.method : null,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
console.log(JSON.stringify({
|
|
191
|
+
environment: { node: getNodeVersion(), npm: getNpmVersion() },
|
|
192
|
+
tools: toolResults,
|
|
193
|
+
installed: toolResults.filter((t) => t.installed).length,
|
|
194
|
+
missing: toolResults.filter((t) => !t.installed).length,
|
|
195
|
+
}));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let installed = 0;
|
|
200
|
+
let missing = 0;
|
|
201
|
+
|
|
202
|
+
console.log('');
|
|
203
|
+
console.log(' \x1b[1;33mgrainulation doctor\x1b[0m');
|
|
204
|
+
console.log(' Checking ecosystem health...');
|
|
205
|
+
console.log('');
|
|
206
|
+
|
|
207
|
+
// Environment
|
|
208
|
+
console.log(' \x1b[2mEnvironment:\x1b[0m');
|
|
209
|
+
console.log(` Node ${getNodeVersion()}`);
|
|
210
|
+
console.log(` npm v${getNpmVersion()}`);
|
|
211
|
+
console.log('');
|
|
212
|
+
|
|
213
|
+
// Tools
|
|
214
|
+
console.log(' \x1b[2mTools:\x1b[0m');
|
|
215
|
+
for (const tool of tools) {
|
|
216
|
+
const result = detect(tool.package);
|
|
217
|
+
if (result) {
|
|
218
|
+
installed++;
|
|
219
|
+
const ver = `v${result.version}`.padEnd(10);
|
|
220
|
+
console.log(
|
|
221
|
+
` \x1b[32m\u2713\x1b[0m ${tool.name.padEnd(12)} ${ver} \x1b[2m(${result.method})\x1b[0m`
|
|
222
|
+
);
|
|
223
|
+
} else {
|
|
224
|
+
missing++;
|
|
225
|
+
console.log(` \x1b[2m\u2717 ${tool.name.padEnd(12)} -- (not found)\x1b[0m`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
console.log('');
|
|
230
|
+
|
|
231
|
+
// Summary
|
|
232
|
+
if (missing === tools.length) {
|
|
233
|
+
console.log(' \x1b[33mNo grainulation tools found.\x1b[0m');
|
|
234
|
+
console.log(' Start with: npx @grainulation/wheat init');
|
|
235
|
+
} else if (missing > 0) {
|
|
236
|
+
console.log(` \x1b[32m${installed} found\x1b[0m, \x1b[2m${missing} not found\x1b[0m`);
|
|
237
|
+
console.log(' Run \x1b[1mgrainulation setup\x1b[0m to install what you need.');
|
|
238
|
+
} else {
|
|
239
|
+
console.log(' \x1b[32mAll tools found. Full ecosystem ready.\x1b[0m');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log('');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
module.exports = { run, getVersion, detect };
|
package/lib/ecosystem.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The grainulation ecosystem registry.
|
|
5
|
+
*
|
|
6
|
+
* Eight tools. Each grows from the same soil:
|
|
7
|
+
* question -> evidence -> decision.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const TOOLS = [
|
|
11
|
+
{
|
|
12
|
+
name: 'wheat',
|
|
13
|
+
package: '@grainulation/wheat',
|
|
14
|
+
icon: 'W',
|
|
15
|
+
role: 'Grows evidence',
|
|
16
|
+
description: 'Research sprint engine. Ask a question, grow claims, compile a brief.',
|
|
17
|
+
category: 'core',
|
|
18
|
+
port: 9091,
|
|
19
|
+
serveCmd: ['serve'],
|
|
20
|
+
entryPoint: true,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'farmer',
|
|
24
|
+
package: '@grainulation/farmer',
|
|
25
|
+
icon: 'F',
|
|
26
|
+
role: 'Permission dashboard',
|
|
27
|
+
description: 'Permission dashboard. Approve tool calls, review AI actions in real time.',
|
|
28
|
+
category: 'core',
|
|
29
|
+
port: 9090,
|
|
30
|
+
serveCmd: ['start'],
|
|
31
|
+
entryPoint: false,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: 'barn',
|
|
35
|
+
package: '@grainulation/barn',
|
|
36
|
+
icon: 'B',
|
|
37
|
+
role: 'Shared tools',
|
|
38
|
+
description: 'Public utilities. Claim schemas, HTML templates, shared validators.',
|
|
39
|
+
category: 'foundation',
|
|
40
|
+
port: 9093,
|
|
41
|
+
serveCmd: ['serve'],
|
|
42
|
+
entryPoint: false,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'mill',
|
|
46
|
+
package: '@grainulation/mill',
|
|
47
|
+
icon: 'M',
|
|
48
|
+
role: 'Processes output',
|
|
49
|
+
description: 'Export and publish. Turn compiled research into PDFs, slides, wikis.',
|
|
50
|
+
category: 'output',
|
|
51
|
+
port: 9094,
|
|
52
|
+
serveCmd: ['serve'],
|
|
53
|
+
entryPoint: false,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'silo',
|
|
57
|
+
package: '@grainulation/silo',
|
|
58
|
+
icon: 'S',
|
|
59
|
+
role: 'Stores knowledge',
|
|
60
|
+
description: 'Reusable claim libraries. Share vetted claims across sprints and teams.',
|
|
61
|
+
category: 'storage',
|
|
62
|
+
port: 9095,
|
|
63
|
+
serveCmd: ['serve'],
|
|
64
|
+
entryPoint: false,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'harvest',
|
|
68
|
+
package: '@grainulation/harvest',
|
|
69
|
+
icon: 'H',
|
|
70
|
+
role: 'Analytics & retrospectives',
|
|
71
|
+
description: 'Cross-sprint learning. Track prediction accuracy, find blind spots over time.',
|
|
72
|
+
category: 'analytics',
|
|
73
|
+
port: 9096,
|
|
74
|
+
serveCmd: ['serve'],
|
|
75
|
+
entryPoint: false,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'orchard',
|
|
79
|
+
package: '@grainulation/orchard',
|
|
80
|
+
icon: 'O',
|
|
81
|
+
role: 'Orchestration',
|
|
82
|
+
description: 'Multi-sprint coordination. Run parallel research tracks, merge results.',
|
|
83
|
+
category: 'orchestration',
|
|
84
|
+
port: 9097,
|
|
85
|
+
serveCmd: ['serve'],
|
|
86
|
+
entryPoint: false,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'grainulation',
|
|
90
|
+
package: '@grainulation/grainulation',
|
|
91
|
+
icon: 'G',
|
|
92
|
+
role: 'The machine',
|
|
93
|
+
description: 'Process manager and ecosystem hub. Start, stop, and monitor all tools.',
|
|
94
|
+
category: 'meta',
|
|
95
|
+
port: 9098,
|
|
96
|
+
serveCmd: ['serve'],
|
|
97
|
+
entryPoint: false,
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
function getAll() {
|
|
102
|
+
return TOOLS;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getByName(name) {
|
|
106
|
+
return TOOLS.find((t) => t.name === name);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function getInstallable() {
|
|
110
|
+
return TOOLS.filter((t) => t.name !== 'grainulation');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getCategories() {
|
|
114
|
+
const cats = {};
|
|
115
|
+
for (const tool of TOOLS) {
|
|
116
|
+
if (!cats[tool.category]) cats[tool.category] = [];
|
|
117
|
+
cats[tool.category].push(tool);
|
|
118
|
+
}
|
|
119
|
+
return cats;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
module.exports = { TOOLS, getAll, getByName, getInstallable, getCategories };
|