groundplan 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/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/args.d.ts +8 -0
- package/dist/args.js +30 -0
- package/dist/args.js.map +1 -0
- package/dist/commands/doctor.d.ts +7 -0
- package/dist/commands/doctor.js +123 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +33 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/new.d.ts +2 -0
- package/dist/commands/new.js +266 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/recommend.d.ts +9 -0
- package/dist/commands/recommend.js +101 -0
- package/dist/commands/recommend.js.map +1 -0
- package/dist/commands/sync-agents.d.ts +12 -0
- package/dist/commands/sync-agents.js +99 -0
- package/dist/commands/sync-agents.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Groundplan 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,20 @@
|
|
|
1
|
+
# groundplan
|
|
2
|
+
|
|
3
|
+
Prepares a repository: picks an architecture from stated priorities, assembles the repo from
|
|
4
|
+
composable templates, brands it with your solution name, and wires it for every AI coding tool.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
npm install -g groundplan
|
|
8
|
+
groundplan
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`groundplan doctor` validates the catalog it shipped with — run it first if generation ever
|
|
12
|
+
behaves unexpectedly, since it reports which home directory was resolved.
|
|
13
|
+
|
|
14
|
+
The templates and catalog live in [`@groundplan/core`](https://www.npmjs.com/package/@groundplan/core),
|
|
15
|
+
which this package depends on; installing the CLI brings them with it.
|
|
16
|
+
|
|
17
|
+
Full documentation, architecture notes, and the contributor guide are in the
|
|
18
|
+
[repository](https://github.com/groundplan/groundplan#readme).
|
|
19
|
+
|
|
20
|
+
MIT licensed.
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Minimal flag parsing — the CLI's surface is small enough that a dependency is not worth it. */
|
|
2
|
+
export interface Args {
|
|
3
|
+
positional: string[];
|
|
4
|
+
flags: Record<string, string | boolean>;
|
|
5
|
+
}
|
|
6
|
+
export declare function parseArgs(argv: string[]): Args;
|
|
7
|
+
export declare function flagString(args: Args, name: string): string | undefined;
|
|
8
|
+
export declare function flagBool(args: Args, name: string): boolean;
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function parseArgs(argv) {
|
|
2
|
+
const positional = [];
|
|
3
|
+
const flags = {};
|
|
4
|
+
for (let i = 0; i < argv.length; i++) {
|
|
5
|
+
const token = argv[i];
|
|
6
|
+
if (!token.startsWith('--')) {
|
|
7
|
+
positional.push(token);
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const [name, inline] = token.slice(2).split('=');
|
|
11
|
+
if (inline !== undefined) {
|
|
12
|
+
flags[name] = inline;
|
|
13
|
+
}
|
|
14
|
+
else if (argv[i + 1] && !argv[i + 1].startsWith('--')) {
|
|
15
|
+
flags[name] = argv[++i];
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
flags[name] = true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { positional, flags };
|
|
22
|
+
}
|
|
23
|
+
export function flagString(args, name) {
|
|
24
|
+
const value = args.flags[name];
|
|
25
|
+
return typeof value === 'string' ? value : undefined;
|
|
26
|
+
}
|
|
27
|
+
export function flagBool(args, name) {
|
|
28
|
+
return args.flags[name] === true || args.flags[name] === 'true';
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,MAAM,KAAK,GAAqC,EAAE,CAAC;IAEnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;QACvB,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACxD,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAU,EAAE,IAAY;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAU,EAAE,IAAY;IAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type GroundplanConfig } from '@groundplan/core';
|
|
2
|
+
/**
|
|
3
|
+
* Validates every YAML against the schema, every cross-reference, and the rules that keep
|
|
4
|
+
* the multi-tool layer honest. Run this in CI — a dangling id or a duplicated instruction
|
|
5
|
+
* file is invisible until it reaches a generated repo.
|
|
6
|
+
*/
|
|
7
|
+
export declare function cmdDoctor(_argv: string[], config: GroundplanConfig): Promise<void>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { checkCapabilityCoverage, checkDelegationCompliance, checkGoverningRule, loadCatalog, } from '@groundplan/core';
|
|
5
|
+
import { templatePaths } from '@groundplan/core';
|
|
6
|
+
/**
|
|
7
|
+
* A wrong GROUNDPLAN_HOME finds nothing, and "nothing" trivially satisfies every
|
|
8
|
+
* consistency check. Reporting that as healthy is the one failure mode a validator must
|
|
9
|
+
* never have, so the paths are proven real before anything is judged.
|
|
10
|
+
*/
|
|
11
|
+
async function assertHomeIsReal(config) {
|
|
12
|
+
const required = [
|
|
13
|
+
['catalog', config.paths.catalog],
|
|
14
|
+
['templates', config.paths.templates],
|
|
15
|
+
['adapters', config.paths.adapters],
|
|
16
|
+
];
|
|
17
|
+
const missing = [];
|
|
18
|
+
for (const [label, dir] of required) {
|
|
19
|
+
const stat = await fs.stat(dir).catch(() => null);
|
|
20
|
+
if (!stat?.isDirectory())
|
|
21
|
+
missing.push(`${label}/ (${dir})`);
|
|
22
|
+
}
|
|
23
|
+
if (missing.length) {
|
|
24
|
+
throw new Error(`GROUNDPLAN_HOME does not look like a Groundplan repository.\n` +
|
|
25
|
+
` home: ${config.groundplanHome}\n` +
|
|
26
|
+
` missing: ${missing.join(', ')}\n` +
|
|
27
|
+
`Set GROUNDPLAN_HOME to the repository root, or run from inside it.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** An empty catalog passes every cross-reference check, which makes a green misleading. */
|
|
31
|
+
function assertNotEmpty(catalog) {
|
|
32
|
+
const empty = Object.entries({
|
|
33
|
+
architectures: catalog.architectures.size,
|
|
34
|
+
blueprints: catalog.blueprints.size,
|
|
35
|
+
adapters: catalog.adapters.size,
|
|
36
|
+
})
|
|
37
|
+
.filter(([, count]) => count === 0)
|
|
38
|
+
.map(([label]) => label);
|
|
39
|
+
if (empty.length) {
|
|
40
|
+
throw new Error(`Catalog is empty: ${empty.join(', ')} loaded nothing.\n` +
|
|
41
|
+
`Consistency checks pass vacuously on an empty catalog, so this is a failure, not a pass.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validates every YAML against the schema, every cross-reference, and the rules that keep
|
|
46
|
+
* the multi-tool layer honest. Run this in CI — a dangling id or a duplicated instruction
|
|
47
|
+
* file is invisible until it reaches a generated repo.
|
|
48
|
+
*/
|
|
49
|
+
export async function cmdDoctor(_argv, config) {
|
|
50
|
+
console.log(pc.dim(`groundplan home: ${config.groundplanHome}\n`));
|
|
51
|
+
await assertHomeIsReal(config);
|
|
52
|
+
const catalog = await loadCatalog(config);
|
|
53
|
+
assertNotEmpty(catalog);
|
|
54
|
+
const counts = {
|
|
55
|
+
architectures: catalog.architectures.size,
|
|
56
|
+
blueprints: catalog.blueprints.size,
|
|
57
|
+
adapters: catalog.adapters.size,
|
|
58
|
+
'registry items': catalog.registry.size,
|
|
59
|
+
};
|
|
60
|
+
for (const [label, count] of Object.entries(counts)) {
|
|
61
|
+
console.log(`${count > 0 ? pc.green('✔') : pc.yellow('!')} ${String(count).padStart(4)} ${label}`);
|
|
62
|
+
}
|
|
63
|
+
const problems = [
|
|
64
|
+
...checkGoverningRule(catalog.adapters.values()),
|
|
65
|
+
...checkCapabilityCoverage(catalog.adapters.values(), catalog.registry.values()),
|
|
66
|
+
...checkDelegationCompliance(catalog.adapters.values()),
|
|
67
|
+
];
|
|
68
|
+
// An output with no template renders nothing. sync-agents used to skip those in silence,
|
|
69
|
+
// which is how every adapter came to emit no file while reporting success.
|
|
70
|
+
for (const adapter of catalog.adapters.values()) {
|
|
71
|
+
for (const output of adapter.outputs) {
|
|
72
|
+
const candidates = templatePaths(config, adapter.id, output);
|
|
73
|
+
const found = await Promise.all(candidates.map((c) => fs.stat(c).then((s) => s.isFile()).catch(() => false)));
|
|
74
|
+
if (!found.some(Boolean)) {
|
|
75
|
+
problems.push(`adapter "${adapter.id}" declares output ${output.path} but ships no template — ` +
|
|
76
|
+
`tried ${candidates.map((c) => path.relative(config.groundplanHome, c)).join(' or ')}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// A `verified:` URL says someone looked once. The date says when, so staleness is
|
|
81
|
+
// measurable instead of assumed — CLI vendors rename flags without announcing it.
|
|
82
|
+
const now = Date.now();
|
|
83
|
+
const window = config.adapterVerificationDays * 24 * 60 * 60 * 1000;
|
|
84
|
+
const stale = [...catalog.adapters.values()]
|
|
85
|
+
.filter((a) => a.invoke)
|
|
86
|
+
.map((a) => ({ id: a.id, age: now - Date.parse(a.invoke.verifiedOn) }))
|
|
87
|
+
.filter((entry) => entry.age > window)
|
|
88
|
+
.map((entry) => `${entry.id} (${Math.floor(entry.age / 86_400_000)} days)`);
|
|
89
|
+
if (stale.length) {
|
|
90
|
+
console.log(pc.yellow(`\n${stale.length} adapter(s) need their flags re-checked against vendor docs: `) +
|
|
91
|
+
pc.yellow(stale.join(', ')));
|
|
92
|
+
}
|
|
93
|
+
// The schema already refuses an architecture with neither overlay nor guidance, so anything
|
|
94
|
+
// here is a deliberate no-layout entry rather than an unfinished one.
|
|
95
|
+
const guidance = [...catalog.architectures.values()].filter((a) => a.guidance);
|
|
96
|
+
if (guidance.length) {
|
|
97
|
+
console.log(pc.dim(`\n${guidance.length} architecture(s) ship guidance rather than a layout: `) +
|
|
98
|
+
pc.dim(guidance.map((a) => a.id).join(', ')));
|
|
99
|
+
}
|
|
100
|
+
// An architecture no base supports cannot be chosen: `pickBase` filters on
|
|
101
|
+
// supportsArchitectures and throws when nothing survives. `list` and `recommend` still
|
|
102
|
+
// offer it, so the failure lands on the user at the last step of the interview.
|
|
103
|
+
const bases = [...catalog.blueprints.values()].filter((b) => b.kind === 'base');
|
|
104
|
+
for (const architecture of catalog.architectures.values()) {
|
|
105
|
+
const supporting = bases.filter((b) => b.supportsArchitectures.length === 0 || b.supportsArchitectures.includes(architecture.id));
|
|
106
|
+
if (supporting.length === 0) {
|
|
107
|
+
problems.push(`architecture "${architecture.id}" is offered by list and recommend but no base ` +
|
|
108
|
+
`declares it in supportsArchitectures, so choosing it aborts \`groundplan new\``);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (problems.length) {
|
|
112
|
+
console.log(pc.red(`\n✖ ${problems.length} problem(s):`));
|
|
113
|
+
for (const problem of problems)
|
|
114
|
+
console.log(pc.red(` - ${problem}`));
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
console.log(pc.green('\n✔ catalog is internally consistent'));
|
|
119
|
+
console.log(pc.green('✔ no adapter duplicates AGENTS.md'));
|
|
120
|
+
console.log(pc.green('✔ every declared capability has a matching output'));
|
|
121
|
+
console.log(pc.green('✔ every delegation target complies with ADR 0007'));
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { loadCatalog } from '@groundplan/core';
|
|
3
|
+
import { parseArgs } from '../args.js';
|
|
4
|
+
const KINDS = ['architectures', 'blueprints', 'adapters', 'registry'];
|
|
5
|
+
export async function cmdList(argv, config) {
|
|
6
|
+
const args = parseArgs(argv);
|
|
7
|
+
const requested = args.positional[0] ?? undefined;
|
|
8
|
+
if (requested && !KINDS.includes(requested)) {
|
|
9
|
+
throw new Error(`Unknown kind "${requested}". Expected one of: ${KINDS.join(', ')}`);
|
|
10
|
+
}
|
|
11
|
+
const catalog = await loadCatalog(config);
|
|
12
|
+
for (const kind of requested ? [requested] : KINDS) {
|
|
13
|
+
const items = [...catalog[kind].values()];
|
|
14
|
+
console.log(`\n${pc.bold(pc.cyan(kind))} ${pc.dim(`(${items.length})`)}`);
|
|
15
|
+
if (items.length === 0) {
|
|
16
|
+
console.log(pc.dim(' none yet'));
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
for (const item of items) {
|
|
20
|
+
const meta = 'family' in item ? pc.dim(`[${item.family}/${item.complexity}]`)
|
|
21
|
+
: 'kind' in item ? pc.dim(`[${item.kind}]`)
|
|
22
|
+
: 'readsAgentsMd' in item ? pc.dim(item.readsAgentsMd ? '[native AGENTS.md]' : '[wrapper]')
|
|
23
|
+
: '';
|
|
24
|
+
const summary = 'oneLine' in item ? item.oneLine : 'description' in item ? item.description : '';
|
|
25
|
+
console.log(` ${pc.green(item.id.padEnd(24))} ${meta} ${pc.dim(truncate(summary, 70))}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
console.log();
|
|
29
|
+
}
|
|
30
|
+
function truncate(text, max) {
|
|
31
|
+
return text.length <= max ? text : text.slice(0, max - 1) + '…';
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/commands/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import * as p from '@clack/prompts';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { apply, buildPlan, choiceSets, commandVars, delegationToolsJson, engineGlobals, hasRequiredFailure, loadCatalog, recommend, renderAdapterFiles, writeAdapterFiles, resolve, test as testCondition, } from '@groundplan/core';
|
|
5
|
+
import { flagBool, flagString, parseArgs } from '../args.js';
|
|
6
|
+
import { askAnswers } from './recommend.js';
|
|
7
|
+
export async function cmdNew(argv, config) {
|
|
8
|
+
const args = parseArgs(argv);
|
|
9
|
+
const dryRun = flagBool(args, 'dry-run');
|
|
10
|
+
p.intro(pc.bgCyan(pc.black(' groundplan new ')));
|
|
11
|
+
const catalog = await loadCatalog(config);
|
|
12
|
+
if (catalog.blueprints.size === 0) {
|
|
13
|
+
throw new Error('No blueprints installed. Run `groundplan doctor` and check templates/.');
|
|
14
|
+
}
|
|
15
|
+
const solutionName = args.positional[0] ?? (await askSolutionName());
|
|
16
|
+
const target = path.resolve(flagString(args, 'target') ?? solutionName);
|
|
17
|
+
const answers = await askAnswers();
|
|
18
|
+
// Rank first, then let the user override — the recommendation is advice, not a lock.
|
|
19
|
+
const ranked = recommend(catalog.architectures.values(), {
|
|
20
|
+
priorities: answers.priorities,
|
|
21
|
+
teamSize: answers.teamSize,
|
|
22
|
+
stack: answers.stack,
|
|
23
|
+
family: answers.family,
|
|
24
|
+
});
|
|
25
|
+
const architecture = await pickArchitecture(ranked);
|
|
26
|
+
const base = await pickBase(catalog, answers.stack, architecture);
|
|
27
|
+
const layers = await pickLayers(catalog, base);
|
|
28
|
+
const adapters = await pickAdapters(catalog, config);
|
|
29
|
+
const selected = resolve({ base, architecture, layers }, catalog.blueprints);
|
|
30
|
+
for (const warning of selected.warnings)
|
|
31
|
+
p.log.warn(warning);
|
|
32
|
+
const vars = {
|
|
33
|
+
...engineGlobals({
|
|
34
|
+
solutionName,
|
|
35
|
+
base,
|
|
36
|
+
stack: answers.stack,
|
|
37
|
+
teamSize: answers.teamSize,
|
|
38
|
+
architecture,
|
|
39
|
+
adapters,
|
|
40
|
+
today: new Date().toISOString().slice(0, 10),
|
|
41
|
+
delegationTools: delegationToolsJson(adapters.map((id) => catalog.adapters.get(id)).filter((a) => a !== undefined)),
|
|
42
|
+
}),
|
|
43
|
+
...commandVars(selected.order, answers.stack, config),
|
|
44
|
+
...(await askBlueprintVariables(selected.order, { base, stack: answers.stack, architecture })),
|
|
45
|
+
};
|
|
46
|
+
const plan = await buildPlan({ solution: solutionName, target, blueprints: selected.order, adapters, architecture, vars }, config);
|
|
47
|
+
printPlan(plan);
|
|
48
|
+
for (const warning of plan.warnings)
|
|
49
|
+
p.log.warn(warning);
|
|
50
|
+
if (dryRun) {
|
|
51
|
+
p.outro(pc.dim('Dry run — nothing written.'));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const approved = await p.confirm({ message: `Write ${plan.files.length} files to ${target}?` });
|
|
55
|
+
if (p.isCancel(approved) || !approved) {
|
|
56
|
+
p.cancel('Nothing written.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const spinner = p.spinner();
|
|
60
|
+
spinner.start('Generating');
|
|
61
|
+
const result = await apply(plan, config, { onProgress: (m) => spinner.message(m) });
|
|
62
|
+
spinner.stop(`Wrote ${result.written.length} files`);
|
|
63
|
+
// Compile AGENTS.md outward, one file per selected tool. Without this the repo has the
|
|
64
|
+
// source of truth and nothing pointing at it, which is the whole point of the adapters.
|
|
65
|
+
const emitted = await renderAdapterFiles({
|
|
66
|
+
adapters: adapters.map((id) => catalog.adapters.get(id)).filter((a) => a !== undefined),
|
|
67
|
+
registry: [...catalog.registry.values()],
|
|
68
|
+
vars,
|
|
69
|
+
config,
|
|
70
|
+
});
|
|
71
|
+
await writeAdapterFiles(target, emitted.files);
|
|
72
|
+
const perTool = new Map();
|
|
73
|
+
for (const file of emitted.files)
|
|
74
|
+
perTool.set(file.adapter, (perTool.get(file.adapter) ?? 0) + 1);
|
|
75
|
+
for (const [id, count] of perTool)
|
|
76
|
+
p.log.success(`${id}: ${count} file(s)`);
|
|
77
|
+
for (const reason of emitted.skipped)
|
|
78
|
+
p.log.warn(reason);
|
|
79
|
+
for (const step of result.postApply) {
|
|
80
|
+
if (step.ok)
|
|
81
|
+
p.log.success(step.command);
|
|
82
|
+
else if (step.optional)
|
|
83
|
+
p.log.warn(`${step.command} — failed, run it manually`);
|
|
84
|
+
else
|
|
85
|
+
p.log.error(`${step.command} — REQUIRED and failed; the repo is incomplete`);
|
|
86
|
+
}
|
|
87
|
+
if (hasRequiredFailure(result)) {
|
|
88
|
+
p.outro(pc.red('Generated, but a required step failed. Fix it before using this repo.'));
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
p.outro(`${pc.green('✔')} ${solutionName} ready at ${pc.cyan(target)}`);
|
|
93
|
+
}
|
|
94
|
+
async function askSolutionName() {
|
|
95
|
+
const value = await p.text({
|
|
96
|
+
message: 'Solution name',
|
|
97
|
+
placeholder: 'acme-billing',
|
|
98
|
+
validate: (v) => (v.trim().length > 0 ? undefined : 'Required'),
|
|
99
|
+
});
|
|
100
|
+
assertNotCancelled(value);
|
|
101
|
+
return value.trim();
|
|
102
|
+
}
|
|
103
|
+
async function pickArchitecture(ranked) {
|
|
104
|
+
if (ranked.length === 0)
|
|
105
|
+
return undefined;
|
|
106
|
+
const value = await p.select({
|
|
107
|
+
message: 'Architecture',
|
|
108
|
+
options: ranked.slice(0, 6).map((r, i) => ({
|
|
109
|
+
value: r.architecture.id,
|
|
110
|
+
label: `${r.architecture.name}${i === 0 ? pc.green(' ← recommended') : ''}`,
|
|
111
|
+
hint: `${Math.round(r.fit * 100)}% fit — ${r.architecture.keyTradeoff}`,
|
|
112
|
+
})),
|
|
113
|
+
});
|
|
114
|
+
assertNotCancelled(value);
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
async function pickBase(catalog, stack, architecture) {
|
|
118
|
+
const bases = [...catalog.blueprints.values()].filter((b) => b.kind === 'base' &&
|
|
119
|
+
b.stack === stack &&
|
|
120
|
+
(!architecture || b.supportsArchitectures.length === 0 || b.supportsArchitectures.includes(architecture)));
|
|
121
|
+
if (bases.length === 0) {
|
|
122
|
+
// Two very different causes; saying "no base for this stack" when eight exist sends the
|
|
123
|
+
// reader looking in the wrong place.
|
|
124
|
+
const forStack = [...catalog.blueprints.values()].filter((b) => b.kind === 'base' && b.stack === stack);
|
|
125
|
+
throw new Error(forStack.length === 0
|
|
126
|
+
? `No base template for stack "${stack}" yet.`
|
|
127
|
+
: `No ${stack} base supports the "${architecture}" architecture. ` +
|
|
128
|
+
`Bases for this stack: ${forStack.map((b) => b.id).join(', ')}. ` +
|
|
129
|
+
`A base opts in through supportsArchitectures in its blueprint.yaml.`);
|
|
130
|
+
}
|
|
131
|
+
if (bases.length === 1)
|
|
132
|
+
return bases[0].id;
|
|
133
|
+
const value = await p.select({
|
|
134
|
+
message: 'Base template',
|
|
135
|
+
options: bases.map((b) => ({ value: b.id, label: b.name, hint: b.description })),
|
|
136
|
+
});
|
|
137
|
+
assertNotCancelled(value);
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Choice sets are asked first as pick-one (database, ORM, auth provider…), then everything
|
|
142
|
+
* else as checkboxes. Presenting exclusive alternatives as checkboxes invites a selection
|
|
143
|
+
* the resolver will only reject afterwards.
|
|
144
|
+
*/
|
|
145
|
+
async function pickLayers(catalog, base) {
|
|
146
|
+
const baseStack = catalog.blueprints.get(base)?.stack;
|
|
147
|
+
const layers = [...catalog.blueprints.values()].filter((b) => b.kind === 'layer' && (!b.stack || b.stack === baseStack));
|
|
148
|
+
if (layers.length === 0)
|
|
149
|
+
return [];
|
|
150
|
+
const selected = [];
|
|
151
|
+
const sets = choiceSets(layers);
|
|
152
|
+
for (const [setName, options] of [...sets.entries()].sort()) {
|
|
153
|
+
const preselected = options.find((o) => o.choiceDefault);
|
|
154
|
+
const value = await p.select({
|
|
155
|
+
message: `${setName.replace(/-/g, ' ')}`,
|
|
156
|
+
initialValue: preselected?.id,
|
|
157
|
+
options: [
|
|
158
|
+
...options.map((o) => ({ value: o.id, label: o.name, hint: o.description })),
|
|
159
|
+
{ value: '', label: 'None', hint: 'skip this' },
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
assertNotCancelled(value);
|
|
163
|
+
if (value)
|
|
164
|
+
selected.push(value);
|
|
165
|
+
}
|
|
166
|
+
const rest = layers.filter((l) => !l.choiceSet);
|
|
167
|
+
if (rest.length === 0)
|
|
168
|
+
return selected;
|
|
169
|
+
const extras = await p.multiselect({
|
|
170
|
+
message: 'Capabilities',
|
|
171
|
+
options: rest.map((l) => ({ value: l.id, label: l.name, hint: l.description })),
|
|
172
|
+
required: false,
|
|
173
|
+
});
|
|
174
|
+
assertNotCancelled(extras);
|
|
175
|
+
return [...selected, ...extras];
|
|
176
|
+
}
|
|
177
|
+
async function pickAdapters(catalog, config) {
|
|
178
|
+
const available = [...catalog.adapters.values()];
|
|
179
|
+
if (available.length === 0)
|
|
180
|
+
return [];
|
|
181
|
+
const value = await p.multiselect({
|
|
182
|
+
message: 'Which AI coding tools will touch this repo?',
|
|
183
|
+
options: available.map((a) => ({
|
|
184
|
+
value: a.id,
|
|
185
|
+
label: a.name,
|
|
186
|
+
hint: a.readsAgentsMd ? 'reads AGENTS.md natively' : 'needs a wrapper file',
|
|
187
|
+
})),
|
|
188
|
+
initialValues: config.defaultAdapters.filter((id) => catalog.adapters.has(id)),
|
|
189
|
+
required: false,
|
|
190
|
+
});
|
|
191
|
+
assertNotCancelled(value);
|
|
192
|
+
return value;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Only asks questions the selected blueprints actually declared, and only those whose `when`
|
|
196
|
+
* holds. Skipping the condition asked a .NET user which Python package manager to use, and —
|
|
197
|
+
* worse — recorded an answer the templates then rendered as fact.
|
|
198
|
+
*
|
|
199
|
+
* A skipped variable still takes its declared default, because a template may reference it
|
|
200
|
+
* even on a stack that was never asked: the docker layer's compose file names a Dockerfile
|
|
201
|
+
* whose suffix only one stack chooses.
|
|
202
|
+
*/
|
|
203
|
+
async function askBlueprintVariables(blueprints, context) {
|
|
204
|
+
const answers = {};
|
|
205
|
+
const asked = new Set();
|
|
206
|
+
const skipped = [];
|
|
207
|
+
for (const blueprint of blueprints) {
|
|
208
|
+
for (const variable of blueprint.variables) {
|
|
209
|
+
if (asked.has(variable.name))
|
|
210
|
+
continue;
|
|
211
|
+
asked.add(variable.name);
|
|
212
|
+
if (!testCondition(variable.when, { ...context, ...answers })) {
|
|
213
|
+
skipped.push(variable);
|
|
214
|
+
asked.delete(variable.name);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const value = variable.type === 'boolean'
|
|
218
|
+
? await p.confirm({ message: variable.prompt, initialValue: Boolean(variable.default) })
|
|
219
|
+
: variable.type === 'select'
|
|
220
|
+
? await p.select({
|
|
221
|
+
message: variable.prompt,
|
|
222
|
+
options: (variable.choices ?? []).map((c) => ({ value: c.value, label: c.label })),
|
|
223
|
+
})
|
|
224
|
+
: await p.text({
|
|
225
|
+
message: variable.prompt,
|
|
226
|
+
defaultValue: variable.default === undefined ? '' : String(variable.default),
|
|
227
|
+
});
|
|
228
|
+
assertNotCancelled(value);
|
|
229
|
+
answers[variable.name] = value;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// A template may reference a variable this stack was never asked for. Its declared default
|
|
233
|
+
// is the right value; leaving it unset would fail generation on an unresolved token.
|
|
234
|
+
for (const variable of skipped) {
|
|
235
|
+
if (!(variable.name in answers) && variable.default !== undefined) {
|
|
236
|
+
answers[variable.name] = variable.default;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return answers;
|
|
240
|
+
}
|
|
241
|
+
function printPlan(plan) {
|
|
242
|
+
const byBlueprint = new Map();
|
|
243
|
+
for (const file of plan.files) {
|
|
244
|
+
byBlueprint.set(file.fromBlueprint, (byBlueprint.get(file.fromBlueprint) ?? 0) + 1);
|
|
245
|
+
}
|
|
246
|
+
console.log(`\n${pc.bold('Plan')} ${pc.dim(`— ${plan.files.length} files`)}`);
|
|
247
|
+
for (const [blueprint, count] of byBlueprint) {
|
|
248
|
+
console.log(` ${pc.cyan(blueprint.padEnd(24))} ${pc.dim(`${count} files`)}`);
|
|
249
|
+
}
|
|
250
|
+
if (plan.adapters.length)
|
|
251
|
+
console.log(` ${pc.cyan('ai-tools'.padEnd(24))} ${pc.dim(plan.adapters.join(', '))}`);
|
|
252
|
+
if (plan.postApply.length) {
|
|
253
|
+
console.log(`\n${pc.dim('After writing:')}`);
|
|
254
|
+
for (const step of plan.postApply) {
|
|
255
|
+
console.log(pc.dim(` $ ${step.run}${step.optional ? '' : ' (required)'}`));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
console.log();
|
|
259
|
+
}
|
|
260
|
+
function assertNotCancelled(value) {
|
|
261
|
+
if (p.isCancel(value)) {
|
|
262
|
+
p.cancel('Cancelled.');
|
|
263
|
+
process.exit(0);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=new.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/commands/new.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Characteristic, type GroundplanConfig, type Stack } from '@groundplan/core';
|
|
2
|
+
export interface Answers {
|
|
3
|
+
stack: Stack;
|
|
4
|
+
teamSize: number;
|
|
5
|
+
priorities: Partial<Record<Characteristic, number>>;
|
|
6
|
+
family?: 'application' | 'system' | 'frontend' | 'data' | 'ai';
|
|
7
|
+
}
|
|
8
|
+
export declare function askAnswers(): Promise<Answers>;
|
|
9
|
+
export declare function cmdRecommend(argv: string[], config: GroundplanConfig): Promise<void>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { loadCatalog, recommend, } from '@groundplan/core';
|
|
4
|
+
import { parseArgs } from '../args.js';
|
|
5
|
+
/** The interview. Kept short — every question must change the ranking or it is cut. */
|
|
6
|
+
const PRIORITY_CHOICES = [
|
|
7
|
+
{ value: 'simplicity', label: 'Simplicity', hint: 'ship fast, few moving parts' },
|
|
8
|
+
{ value: 'scalability', label: 'Scalability', hint: 'grow with load' },
|
|
9
|
+
{ value: 'deployability', label: 'Deployability', hint: 'deploy often, independently' },
|
|
10
|
+
{ value: 'testability', label: 'Testability', hint: 'fast, isolated tests' },
|
|
11
|
+
{ value: 'evolvability', label: 'Evolvability', hint: 'change direction cheaply' },
|
|
12
|
+
{ value: 'fault-tolerance', label: 'Fault tolerance', hint: 'survive partial failure' },
|
|
13
|
+
{ value: 'maintainability', label: 'Maintainability', hint: 'easy to change safely' },
|
|
14
|
+
{ value: 'elasticity', label: 'Elasticity', hint: 'absorb sudden spikes' },
|
|
15
|
+
{ value: 'responsiveness', label: 'Responsiveness', hint: 'low latency to the user' },
|
|
16
|
+
{ value: 'cost', label: 'Low cost', hint: 'minimize infrastructure spend' },
|
|
17
|
+
];
|
|
18
|
+
export async function askAnswers() {
|
|
19
|
+
const stack = (await p.select({
|
|
20
|
+
message: 'Primary stack',
|
|
21
|
+
options: [
|
|
22
|
+
{ value: 'typescript', label: 'TypeScript / Next.js / React' },
|
|
23
|
+
{ value: 'python', label: 'Python / FastAPI / AI-ML' },
|
|
24
|
+
{ value: 'dotnet', label: '.NET / C#' },
|
|
25
|
+
{ value: 'node', label: 'Node backend / NestJS / Express' },
|
|
26
|
+
{ value: 'go', label: 'Go' },
|
|
27
|
+
{ value: 'polyglot', label: 'Polyglot / monorepo' },
|
|
28
|
+
],
|
|
29
|
+
}));
|
|
30
|
+
assertNotCancelled(stack);
|
|
31
|
+
const teamSizeRaw = await p.text({
|
|
32
|
+
message: 'How many engineers will work on this?',
|
|
33
|
+
placeholder: '4',
|
|
34
|
+
defaultValue: '4',
|
|
35
|
+
validate: (v) => (Number.isFinite(Number(v)) && Number(v) > 0 ? undefined : 'Enter a positive number'),
|
|
36
|
+
});
|
|
37
|
+
assertNotCancelled(teamSizeRaw);
|
|
38
|
+
const family = (await p.select({
|
|
39
|
+
message: 'What are you deciding?',
|
|
40
|
+
options: [
|
|
41
|
+
{ value: 'application', label: 'Application/code structure', hint: 'layered, clean, vertical-slice…' },
|
|
42
|
+
{ value: 'system', label: 'System topology', hint: 'monolith, microservices, event-driven…' },
|
|
43
|
+
{ value: 'frontend', label: 'Frontend structure' },
|
|
44
|
+
{ value: 'ai', label: 'AI/agent architecture' },
|
|
45
|
+
{ value: 'data', label: 'Data architecture' },
|
|
46
|
+
],
|
|
47
|
+
}));
|
|
48
|
+
assertNotCancelled(family);
|
|
49
|
+
const chosen = (await p.multiselect({
|
|
50
|
+
message: 'What matters most? (pick 2-4)',
|
|
51
|
+
options: PRIORITY_CHOICES,
|
|
52
|
+
required: true,
|
|
53
|
+
}));
|
|
54
|
+
assertNotCancelled(chosen);
|
|
55
|
+
// Weight by selection order: the first pick counts most, so ties break meaningfully.
|
|
56
|
+
const priorities = {};
|
|
57
|
+
chosen.forEach((c, i) => {
|
|
58
|
+
priorities[c] = Math.max(5 - i, 2);
|
|
59
|
+
});
|
|
60
|
+
return { stack, teamSize: Number(teamSizeRaw), priorities, family };
|
|
61
|
+
}
|
|
62
|
+
export async function cmdRecommend(argv, config) {
|
|
63
|
+
parseArgs(argv);
|
|
64
|
+
p.intro(pc.bgCyan(pc.black(' groundplan recommend ')));
|
|
65
|
+
const catalog = await loadCatalog(config);
|
|
66
|
+
if (catalog.architectures.size === 0) {
|
|
67
|
+
throw new Error('Catalog is empty. Run `groundplan doctor` and check catalog/architectures/.');
|
|
68
|
+
}
|
|
69
|
+
const answers = await askAnswers();
|
|
70
|
+
const ranked = recommend(catalog.architectures.values(), {
|
|
71
|
+
priorities: answers.priorities,
|
|
72
|
+
teamSize: answers.teamSize,
|
|
73
|
+
stack: answers.stack,
|
|
74
|
+
family: answers.family,
|
|
75
|
+
});
|
|
76
|
+
if (ranked.length === 0) {
|
|
77
|
+
p.outro(pc.yellow('No architecture in the catalog fits those constraints.'));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
console.log();
|
|
81
|
+
for (const [rank, result] of ranked.slice(0, 5).entries()) {
|
|
82
|
+
const badge = rank === 0 ? pc.green('▸ RECOMMENDED') : pc.dim(` #${rank + 1}`);
|
|
83
|
+
console.log(`${badge} ${pc.bold(result.architecture.name)} ${pc.dim(`(${Math.round(result.fit * 100)}% fit)`)}`);
|
|
84
|
+
console.log(` ${pc.dim(result.architecture.oneLine)}`);
|
|
85
|
+
for (const reason of result.reasons.slice(0, 3))
|
|
86
|
+
console.log(` ${pc.green('+')} ${reason}`);
|
|
87
|
+
for (const concern of result.concerns.slice(0, 3))
|
|
88
|
+
console.log(` ${pc.yellow('!')} ${concern}`);
|
|
89
|
+
if (rank === 0)
|
|
90
|
+
console.log(` ${pc.dim('trade-off: ' + result.architecture.keyTradeoff)}`);
|
|
91
|
+
console.log();
|
|
92
|
+
}
|
|
93
|
+
p.outro(pc.dim('Run `groundplan new <name>` to scaffold with the recommended architecture.'));
|
|
94
|
+
}
|
|
95
|
+
function assertNotCancelled(value) {
|
|
96
|
+
if (p.isCancel(value)) {
|
|
97
|
+
p.cancel('Cancelled.');
|
|
98
|
+
process.exit(0);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=recommend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recommend.js","sourceRoot":"","sources":["../../src/commands/recommend.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type GroundplanConfig } from '@groundplan/core';
|
|
2
|
+
/**
|
|
3
|
+
* Regenerates every AI tool's wrapper file from AGENTS.md in an existing repo.
|
|
4
|
+
* Run it after editing AGENTS.md so no tool's view can drift from the source of truth.
|
|
5
|
+
*
|
|
6
|
+
* Only wrappers. A rule, command, agent or skill file is rendered once per registry item and
|
|
7
|
+
* a settings file needs the project's build commands — neither is knowable from a repo that
|
|
8
|
+
* already exists, so this command reports them rather than inventing values.
|
|
9
|
+
*/
|
|
10
|
+
export declare function cmdSyncAgents(argv: string[], config: GroundplanConfig): Promise<void>;
|
|
11
|
+
/** Templates are named after the file they produce, plus `.tmpl`. */
|
|
12
|
+
export declare function templateFor(config: GroundplanConfig, adapterId: string, outputPath: string): string;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import pc from 'picocolors';
|
|
4
|
+
import { AGENTS_FILE, collectTokens, loadCatalog, renderContent, } from '@groundplan/core';
|
|
5
|
+
import { flagBool, flagString, parseArgs } from '../args.js';
|
|
6
|
+
/**
|
|
7
|
+
* Regenerates every AI tool's wrapper file from AGENTS.md in an existing repo.
|
|
8
|
+
* Run it after editing AGENTS.md so no tool's view can drift from the source of truth.
|
|
9
|
+
*
|
|
10
|
+
* Only wrappers. A rule, command, agent or skill file is rendered once per registry item and
|
|
11
|
+
* a settings file needs the project's build commands — neither is knowable from a repo that
|
|
12
|
+
* already exists, so this command reports them rather than inventing values.
|
|
13
|
+
*/
|
|
14
|
+
export async function cmdSyncAgents(argv, config) {
|
|
15
|
+
const args = parseArgs(argv);
|
|
16
|
+
const target = path.resolve(flagString(args, 'target') ?? process.cwd());
|
|
17
|
+
const dryRun = flagBool(args, 'dry-run');
|
|
18
|
+
const agentsFile = path.join(target, AGENTS_FILE);
|
|
19
|
+
if (!(await exists(agentsFile))) {
|
|
20
|
+
throw new Error(`No ${AGENTS_FILE} in ${target}. It is the source of truth; create it first.`);
|
|
21
|
+
}
|
|
22
|
+
const catalog = await loadCatalog(config);
|
|
23
|
+
const requested = flagString(args, 'tools')?.split(',').map((s) => s.trim());
|
|
24
|
+
const adapters = [...catalog.adapters.values()].filter((a) => !requested || requested.includes(a.id));
|
|
25
|
+
if (adapters.length === 0)
|
|
26
|
+
throw new Error('No matching adapters. Run `groundplan list adapters`.');
|
|
27
|
+
/** Everything a sync of an existing repo can honestly supply. */
|
|
28
|
+
const vars = { agentsMdPath: AGENTS_FILE, solutionName: path.basename(target) };
|
|
29
|
+
const available = new Set(Object.keys(vars));
|
|
30
|
+
const written = [];
|
|
31
|
+
const deferred = [];
|
|
32
|
+
const missing = [];
|
|
33
|
+
for (const adapter of adapters) {
|
|
34
|
+
if (adapter.readsAgentsMd) {
|
|
35
|
+
console.log(`${pc.dim('skip')} ${adapter.id} ${pc.dim(`— reads ${AGENTS_FILE} natively`)}`);
|
|
36
|
+
}
|
|
37
|
+
for (const output of adapter.outputs) {
|
|
38
|
+
// Native readers get no wrapper — a duplicate file is exactly what causes drift.
|
|
39
|
+
if (adapter.readsAgentsMd && output.mode === 'wrapper')
|
|
40
|
+
continue;
|
|
41
|
+
// A `__token__` in the path means one file per registry item, which generation expands.
|
|
42
|
+
if (/__[A-Za-z]/.test(output.path)) {
|
|
43
|
+
deferred.push(`${adapter.id}: ${output.path} — one per registry item, written by \`groundplan new\``);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const templateFile = templateFor(config, adapter.id, output.path);
|
|
47
|
+
if (!(await exists(templateFile))) {
|
|
48
|
+
missing.push(`${adapter.id}: ${output.path} — no ${path.relative(config.groundplanHome, templateFile)}`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const source = await fs.readFile(templateFile, 'utf8');
|
|
52
|
+
const needs = collectTokens(source).filter((token) => !available.has(token));
|
|
53
|
+
if (needs.length > 0) {
|
|
54
|
+
// Build commands and MCP config come from the blueprint that generated the repo, not
|
|
55
|
+
// from the repo itself. Rendering a guess would write a wrong file confidently.
|
|
56
|
+
deferred.push(`${adapter.id}: ${output.path} — needs ${needs.join(', ')}, known only at generation`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
const destination = path.join(target, output.path);
|
|
60
|
+
console.log(`${dryRun ? pc.dim('would write') : pc.green('write')} ${output.path} ${pc.dim(`(${adapter.id})`)}`);
|
|
61
|
+
if (!dryRun) {
|
|
62
|
+
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
63
|
+
await fs.writeFile(destination, renderContent(source, vars, templateFile), 'utf8');
|
|
64
|
+
}
|
|
65
|
+
written.push(output.path);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
for (const entry of deferred)
|
|
69
|
+
console.log(`${pc.dim('defer')} ${pc.dim(entry)}`);
|
|
70
|
+
// A declared output with no template on disk is a catalog defect. It used to be skipped in
|
|
71
|
+
// silence, which is how every adapter came to emit nothing while the command reported success.
|
|
72
|
+
if (missing.length) {
|
|
73
|
+
console.log(pc.red(`\n✖ ${missing.length} declared output(s) have no template:`));
|
|
74
|
+
for (const entry of missing)
|
|
75
|
+
console.log(pc.red(` - ${entry}`));
|
|
76
|
+
console.log(pc.dim(' Add the template, or remove the output from the adapter. `groundplan check:catalog` also reports these.'));
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (written.length === 0) {
|
|
81
|
+
console.log(pc.yellow(`\n! nothing to sync — every selected adapter reads ${AGENTS_FILE} natively.`));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
console.log(`\n${pc.green('✔')} ${written.length} file(s) ${dryRun ? 'planned' : 'synced'} from ${AGENTS_FILE}`);
|
|
85
|
+
}
|
|
86
|
+
/** Templates are named after the file they produce, plus `.tmpl`. */
|
|
87
|
+
export function templateFor(config, adapterId, outputPath) {
|
|
88
|
+
return path.join(config.paths.adapters, adapterId, 'templates', `${path.basename(outputPath)}.tmpl`);
|
|
89
|
+
}
|
|
90
|
+
async function exists(target) {
|
|
91
|
+
try {
|
|
92
|
+
await fs.access(target);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=sync-agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-agents.js","sourceRoot":"","sources":["../../src/commands/sync-agents.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* groundplan — prepare a repository so a project starts at "write the feature"
|
|
4
|
+
* instead of "decide everything again".
|
|
5
|
+
*/
|
|
6
|
+
import pc from 'picocolors';
|
|
7
|
+
import { loadConfig } from '@groundplan/core';
|
|
8
|
+
import { cmdNew } from './commands/new.js';
|
|
9
|
+
import { cmdList } from './commands/list.js';
|
|
10
|
+
import { cmdRecommend } from './commands/recommend.js';
|
|
11
|
+
import { cmdSyncAgents } from './commands/sync-agents.js';
|
|
12
|
+
import { cmdDoctor } from './commands/doctor.js';
|
|
13
|
+
const COMMANDS = {
|
|
14
|
+
new: { run: cmdNew, help: 'Create a new repository (interactive)' },
|
|
15
|
+
list: { run: cmdList, help: 'List architectures, blueprints, adapters, registry items' },
|
|
16
|
+
recommend: { run: cmdRecommend, help: 'Rank architectures against your project priorities' },
|
|
17
|
+
'sync-agents': { run: cmdSyncAgents, help: 'Regenerate AI-tool files from agent-kit in an existing repo' },
|
|
18
|
+
doctor: { run: cmdDoctor, help: 'Validate the catalog and report dangling references' },
|
|
19
|
+
};
|
|
20
|
+
function usage() {
|
|
21
|
+
console.log(`
|
|
22
|
+
${pc.bold('groundplan')} — scalable repository scaffolding
|
|
23
|
+
|
|
24
|
+
${pc.dim('Usage:')} groundplan <command> [options]
|
|
25
|
+
|
|
26
|
+
${Object.entries(COMMANDS)
|
|
27
|
+
.map(([name, { help }]) => ` ${pc.cyan(name.padEnd(13))} ${help}`)
|
|
28
|
+
.join('\n')}
|
|
29
|
+
|
|
30
|
+
${pc.dim('Common flags:')}
|
|
31
|
+
--dry-run Plan only; write nothing
|
|
32
|
+
--yes Accept defaults, skip the interview
|
|
33
|
+
--target <dir> Output directory (default: ./<name>)
|
|
34
|
+
|
|
35
|
+
${pc.dim('Configuration is environment-driven; see docs/ARCHITECTURE.md section 8.')}
|
|
36
|
+
`);
|
|
37
|
+
}
|
|
38
|
+
async function main() {
|
|
39
|
+
const [command, ...argv] = process.argv.slice(2);
|
|
40
|
+
if (!command || command === '--help' || command === '-h') {
|
|
41
|
+
usage();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const entry = COMMANDS[command];
|
|
45
|
+
if (!entry) {
|
|
46
|
+
console.error(pc.red(`Unknown command "${command}"`));
|
|
47
|
+
usage();
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
await entry.run(argv, loadConfig());
|
|
52
|
+
}
|
|
53
|
+
main().catch((error) => {
|
|
54
|
+
console.error(pc.red('\n✖ ' + (error instanceof Error ? error.message : String(error))));
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AACH,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,MAAM,QAAQ,GAAG;IACf,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,uCAAuC,EAAE;IACnE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,0DAA0D,EAAE;IACxF,SAAS,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,oDAAoD,EAAE;IAC5F,aAAa,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,6DAA6D,EAAE;IAC1G,MAAM,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,qDAAqD,EAAE;CAC/E,CAAC;AAEX,SAAS,KAAK;IACZ,OAAO,CAAC,GAAG,CAAC;EACZ,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;;EAErB,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC;;EAEhB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;SACvB,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;SAClE,IAAI,CAAC,IAAI,CAAC;;EAEX,EAAE,CAAC,GAAG,CAAC,eAAe,CAAC;;;;;EAKvB,EAAE,CAAC,GAAG,CAAC,0EAA0E,CAAC;CACnF,CAAC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEjD,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACzD,KAAK,EAAE,CAAC;QACR,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAgC,CAAC,CAAC;IACzD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,oBAAoB,OAAO,GAAG,CAAC,CAAC,CAAC;QACtD,KAAK,EAAE,CAAC;QACR,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACzF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "groundplan",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Groundplan CLI: prepare a repository — pick an architecture, assemble it from templates, and wire it for every AI coding tool.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Groundplan contributors",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/groundplan/groundplan.git",
|
|
10
|
+
"directory": "packages/cli"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/groundplan/groundplan#readme",
|
|
13
|
+
"bugs": "https://github.com/groundplan/groundplan/issues",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"cli",
|
|
16
|
+
"scaffolding",
|
|
17
|
+
"templates",
|
|
18
|
+
"architecture",
|
|
19
|
+
"agents",
|
|
20
|
+
"agents-md"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"bin": {
|
|
24
|
+
"groundplan": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=22"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"!dist/__tests__",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"README.md"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@clack/prompts": "^0.9.0",
|
|
40
|
+
"picocolors": "^1.1.1",
|
|
41
|
+
"@groundplan/core": "0.1.0"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc -p tsconfig.json",
|
|
45
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
46
|
+
"test": "vitest run",
|
|
47
|
+
"lint": "eslint src"
|
|
48
|
+
}
|
|
49
|
+
}
|