great-cto 2.20.0 → 2.21.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/dist/bootstrap.js +10 -2
- package/dist/flow.js +188 -0
- package/dist/main.js +17 -13
- package/package.json +1 -1
package/dist/bootstrap.js
CHANGED
|
@@ -4,12 +4,13 @@ import { existsSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { dim, success, warn } from "./ui.js";
|
|
6
6
|
import { suggestJurisdictions } from "./jurisdictions.js";
|
|
7
|
+
import { compileFlow, renderFlowMd } from "./flow.js";
|
|
7
8
|
export function bootstrap(dir, detection, archetype, compliance, detectionMeta) {
|
|
8
9
|
const greatCtoDir = join(dir, ".great_cto");
|
|
9
10
|
const projectMd = join(greatCtoDir, "PROJECT.md");
|
|
10
11
|
if (existsSync(projectMd)) {
|
|
11
12
|
warn(`.great_cto/PROJECT.md already exists — not overwriting.`);
|
|
12
|
-
return { projectMdPath: projectMd, created: false, skippedReason: "already exists" };
|
|
13
|
+
return { projectMdPath: projectMd, created: false, skippedReason: "already exists", flow: null };
|
|
13
14
|
}
|
|
14
15
|
mkdirSync(greatCtoDir, { recursive: true });
|
|
15
16
|
const title = inferProjectTitle(dir);
|
|
@@ -120,7 +121,14 @@ when you actually start work:
|
|
|
120
121
|
`;
|
|
121
122
|
writeFileSync(projectMd, content, "utf-8");
|
|
122
123
|
success(`created .great_cto/PROJECT.md ${dim(`(archetype: ${archetype})`)}`);
|
|
123
|
-
|
|
124
|
+
// Write FLOW.md — compiled delivery flow for agents and user
|
|
125
|
+
const confidence = detectionMeta?.confidence ?? "medium";
|
|
126
|
+
const size = (detection.projectSize ?? "medium");
|
|
127
|
+
const flow = compileFlow(archetype, size, detection, compliance, confidence);
|
|
128
|
+
const flowMdPath = join(greatCtoDir, "FLOW.md");
|
|
129
|
+
const generatedAt = new Date().toISOString().slice(0, 10);
|
|
130
|
+
writeFileSync(flowMdPath, renderFlowMd(flow, generatedAt), "utf-8");
|
|
131
|
+
return { projectMdPath: projectMd, created: true, skippedReason: null, flow };
|
|
124
132
|
}
|
|
125
133
|
/**
|
|
126
134
|
* Slugify a project title into a tenant-id safe for HTTP headers and audit
|
package/dist/flow.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// flow.ts — compiles all detection outputs into a single user-facing FlowResult.
|
|
2
|
+
// Pure function: no I/O, no side effects.
|
|
3
|
+
// Called by bootstrap.ts (writes FLOW.md) and main.ts (prints summary).
|
|
4
|
+
import { reviewersFor, gatesFor } from "./archetypes.js";
|
|
5
|
+
import { suggestPackReviewers, suggestPackGates, suggestPacks } from "./packs.js";
|
|
6
|
+
import { suggestJurisdictions, suggestJurisdictionReviewers, suggestJurisdictionGates } from "./jurisdictions.js";
|
|
7
|
+
// ── Human-readable titles ─────────────────────────────────────────────────
|
|
8
|
+
const ARCHETYPE_TITLE = {
|
|
9
|
+
"fintech": "Fintech",
|
|
10
|
+
"healthcare": "Healthcare",
|
|
11
|
+
"enterprise-saas": "Enterprise SaaS",
|
|
12
|
+
"agent-product": "AI agent",
|
|
13
|
+
"ai-system": "AI system",
|
|
14
|
+
"mlops": "MLOps pipeline",
|
|
15
|
+
"commerce": "E-commerce",
|
|
16
|
+
"marketplace": "Marketplace",
|
|
17
|
+
"mobile-app": "Mobile app",
|
|
18
|
+
"web-service": "Web service",
|
|
19
|
+
"library": "Library / SDK",
|
|
20
|
+
"cli-tool": "CLI tool",
|
|
21
|
+
"data-platform": "Data platform",
|
|
22
|
+
"streaming": "Streaming system",
|
|
23
|
+
"infra": "Infrastructure",
|
|
24
|
+
"devtools": "Developer tool",
|
|
25
|
+
"browser-extension": "Browser extension",
|
|
26
|
+
"game": "Game",
|
|
27
|
+
"web3": "Web3 / DeFi",
|
|
28
|
+
"iot-embedded": "IoT / embedded",
|
|
29
|
+
"cms": "CMS",
|
|
30
|
+
"edtech": "EdTech",
|
|
31
|
+
"gov-public": "Government",
|
|
32
|
+
"insurance": "Insurance",
|
|
33
|
+
"regulated": "Regulated system",
|
|
34
|
+
"greenfield": "New project",
|
|
35
|
+
};
|
|
36
|
+
// Gate id (StandardGate) → user label
|
|
37
|
+
const GATE_LABEL = {
|
|
38
|
+
"plan": "gate:plan",
|
|
39
|
+
"arch": "gate:arch",
|
|
40
|
+
"code": "gate:code",
|
|
41
|
+
"qa": "gate:qa",
|
|
42
|
+
"security": "gate:security",
|
|
43
|
+
"compliance": "gate:compliance",
|
|
44
|
+
"ship": "gate:ship",
|
|
45
|
+
"cost": "gate:cost-forecast",
|
|
46
|
+
"oracle-review": "gate:oracle-review",
|
|
47
|
+
"edtech-review": "gate:edtech-review",
|
|
48
|
+
"gov-review": "gate:gov-review",
|
|
49
|
+
"insurance-review": "gate:insurance-review",
|
|
50
|
+
};
|
|
51
|
+
// Cost (low, high) per feature cycle by archetype tier
|
|
52
|
+
const ARCHETYPE_COST = {
|
|
53
|
+
"fintech": [8, 18],
|
|
54
|
+
"healthcare": [8, 18],
|
|
55
|
+
"agent-product": [8, 18],
|
|
56
|
+
"mlops": [8, 18],
|
|
57
|
+
"marketplace": [8, 18],
|
|
58
|
+
"enterprise-saas": [8, 18],
|
|
59
|
+
"regulated": [8, 18],
|
|
60
|
+
"edtech": [8, 18],
|
|
61
|
+
"gov-public": [8, 18],
|
|
62
|
+
"insurance": [8, 18],
|
|
63
|
+
"web3": [8, 18],
|
|
64
|
+
"commerce": [3, 8],
|
|
65
|
+
"mobile-app": [3, 8],
|
|
66
|
+
"web-service": [3, 8],
|
|
67
|
+
"data-platform": [3, 8],
|
|
68
|
+
"streaming": [3, 8],
|
|
69
|
+
"devtools": [3, 8],
|
|
70
|
+
"browser-extension": [3, 8],
|
|
71
|
+
"game": [3, 8],
|
|
72
|
+
"cms": [3, 8],
|
|
73
|
+
"ai-system": [3, 8],
|
|
74
|
+
"iot-embedded": [3, 8],
|
|
75
|
+
"infra": [3, 8],
|
|
76
|
+
"library": [0.5, 3],
|
|
77
|
+
"cli-tool": [0.5, 3],
|
|
78
|
+
"greenfield": [0.5, 3],
|
|
79
|
+
};
|
|
80
|
+
// ── Main export ───────────────────────────────────────────────────────────
|
|
81
|
+
/**
|
|
82
|
+
* Compile all detection outputs into a single FlowResult.
|
|
83
|
+
*
|
|
84
|
+
* Pure function — no file I/O. Called by bootstrap.ts (FLOW.md) and
|
|
85
|
+
* main.ts (summary output).
|
|
86
|
+
*/
|
|
87
|
+
export function compileFlow(archetype, size, detection, compliance, confidence) {
|
|
88
|
+
// ── Agents ──────────────────────────────────────────────────────────────
|
|
89
|
+
const agentSet = new Set(reviewersFor(archetype));
|
|
90
|
+
for (const r of suggestPackReviewers(detection))
|
|
91
|
+
agentSet.add(r);
|
|
92
|
+
for (const r of suggestJurisdictionReviewers(detection))
|
|
93
|
+
agentSet.add(r);
|
|
94
|
+
// Always include base orchestration agents
|
|
95
|
+
agentSet.add("architect");
|
|
96
|
+
agentSet.add("senior-dev");
|
|
97
|
+
agentSet.add("qa-engineer");
|
|
98
|
+
// ── Gates ────────────────────────────────────────────────────────────────
|
|
99
|
+
const gateSet = new Set(gatesFor(archetype, size).map((g) => GATE_LABEL[g] ?? `gate:${g}`));
|
|
100
|
+
for (const g of suggestPackGates(detection))
|
|
101
|
+
gateSet.add(g);
|
|
102
|
+
for (const g of suggestJurisdictionGates(detection))
|
|
103
|
+
gateSet.add(g);
|
|
104
|
+
// ── Packs + jurisdictions for routing block ──────────────────────────────
|
|
105
|
+
const packs = suggestPacks(detection);
|
|
106
|
+
const jurisdictions = suggestJurisdictions(detection);
|
|
107
|
+
// ── Title ────────────────────────────────────────────────────────────────
|
|
108
|
+
const productLabel = ARCHETYPE_TITLE[archetype] ?? archetype;
|
|
109
|
+
const jCodes = jurisdictions
|
|
110
|
+
.slice(0, 3)
|
|
111
|
+
.map((j) => j.jurisdiction.toUpperCase())
|
|
112
|
+
.join(" + ");
|
|
113
|
+
const title = jCodes ? `${productLabel} · ${jCodes}` : productLabel;
|
|
114
|
+
// ── ID ───────────────────────────────────────────────────────────────────
|
|
115
|
+
const id = [archetype, ...jurisdictions.map((j) => j.jurisdiction)]
|
|
116
|
+
.join("-")
|
|
117
|
+
.toLowerCase();
|
|
118
|
+
// ── Cost range ────────────────────────────────────────────────────────────
|
|
119
|
+
const costEntry = ARCHETYPE_COST[archetype] ?? [3, 8];
|
|
120
|
+
const [low, high] = costEntry;
|
|
121
|
+
return {
|
|
122
|
+
id,
|
|
123
|
+
title,
|
|
124
|
+
agents: Array.from(agentSet).sort(),
|
|
125
|
+
gates: Array.from(gateSet).sort(),
|
|
126
|
+
compliance: [...new Set(compliance)].sort(),
|
|
127
|
+
costRange: { low, high },
|
|
128
|
+
routing: {
|
|
129
|
+
archetype,
|
|
130
|
+
packs: packs.map((p) => p.pack),
|
|
131
|
+
jurisdictions: jurisdictions.map((j) => j.jurisdiction),
|
|
132
|
+
confidence,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Render FLOW.md content from a FlowResult.
|
|
138
|
+
* Exported separately so bootstrap.ts can call it without depending on main.ts.
|
|
139
|
+
*/
|
|
140
|
+
export function renderFlowMd(flow, generatedAt) {
|
|
141
|
+
const agentLines = flow.agents.map((a) => `- ${a}`).join("\n");
|
|
142
|
+
const gateLines = flow.gates.map((g) => `- ${g}`).join("\n");
|
|
143
|
+
const complianceLines = flow.compliance.length > 0
|
|
144
|
+
? flow.compliance.map((c) => `- ${c}`).join("\n")
|
|
145
|
+
: "- none";
|
|
146
|
+
const packLines = flow.routing.packs.length > 0
|
|
147
|
+
? flow.routing.packs.join(", ")
|
|
148
|
+
: "none";
|
|
149
|
+
const jLines = flow.routing.jurisdictions.length > 0
|
|
150
|
+
? flow.routing.jurisdictions.join(", ")
|
|
151
|
+
: "none";
|
|
152
|
+
return `# Delivery Flow
|
|
153
|
+
|
|
154
|
+
> Auto-generated by \`great-cto init\` on ${generatedAt}.
|
|
155
|
+
> This file tells agents how to orchestrate your SDLC.
|
|
156
|
+
> Regenerates on \`npx great-cto init --force\`. Edit \`_routing:\` to tune.
|
|
157
|
+
|
|
158
|
+
## Detected
|
|
159
|
+
|
|
160
|
+
${flow.title}
|
|
161
|
+
|
|
162
|
+
## Agents
|
|
163
|
+
|
|
164
|
+
${agentLines}
|
|
165
|
+
|
|
166
|
+
## Human gates
|
|
167
|
+
|
|
168
|
+
${gateLines}
|
|
169
|
+
|
|
170
|
+
## Compliance
|
|
171
|
+
|
|
172
|
+
${complianceLines}
|
|
173
|
+
|
|
174
|
+
## Cost estimate
|
|
175
|
+
|
|
176
|
+
$${flow.costRange.low}–$${flow.costRange.high} per feature cycle
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
<!-- Internal routing — view with: great-cto flow explain -->
|
|
181
|
+
_routing:
|
|
182
|
+
id: ${flow.id}
|
|
183
|
+
archetype: ${flow.routing.archetype}
|
|
184
|
+
packs: [${packLines}]
|
|
185
|
+
jurisdictions: [${jLines}]
|
|
186
|
+
confidence: ${flow.routing.confidence}
|
|
187
|
+
`;
|
|
188
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -18,6 +18,7 @@ import { install, findInstalledVersions } from "./installer.js";
|
|
|
18
18
|
import { enableGreatCto } from "./settings.js";
|
|
19
19
|
import { installAllCompanions } from "./companion.js";
|
|
20
20
|
import { bootstrap } from "./bootstrap.js";
|
|
21
|
+
import { compileFlow } from "./flow.js";
|
|
21
22
|
import { shouldUseLlmFallback, suggestArchetypeFromLlm } from "./llm-fallback.js";
|
|
22
23
|
import { readFileSync, copyFileSync, chmodSync, existsSync as fsExistsSync } from "node:fs";
|
|
23
24
|
import { dirname, join } from "node:path";
|
|
@@ -576,23 +577,26 @@ async function runInit(args) {
|
|
|
576
577
|
}
|
|
577
578
|
}
|
|
578
579
|
const compliance = suggestCompliance(detection, archetype);
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
580
|
+
// Compile flow — used for user-facing summary AND written to FLOW.md by bootstrap()
|
|
581
|
+
const compiledFlow = compileFlow(archetype, (detection.projectSize ?? "medium"), detection, compliance, confidence);
|
|
582
|
+
// ── User-facing "Compiled flow" summary ──────────────────────────────────
|
|
583
|
+
log("");
|
|
584
|
+
log(`${bold("Compiled flow:")} ${cyan(compiledFlow.title)}`);
|
|
585
|
+
log(` ${dim("Agents:")} ${compiledFlow.agents.join(" · ")}`);
|
|
586
|
+
log(` ${dim("Gates:")} ${compiledFlow.gates.join(" · ")}`);
|
|
587
|
+
if (compiledFlow.compliance.length > 0) {
|
|
588
|
+
log(` ${dim("Compliance:")} ${compiledFlow.compliance.join(", ")}`);
|
|
583
589
|
}
|
|
584
|
-
log(` ${dim("
|
|
585
|
-
|
|
586
|
-
//
|
|
590
|
+
log(` ${dim("Cost:")} ~$${compiledFlow.costRange.low}–$${compiledFlow.costRange.high} per feature cycle`);
|
|
591
|
+
log("");
|
|
592
|
+
// Low-confidence notice — show only when actionable
|
|
587
593
|
if (!args.yes && !args.archetype && (confidence === "low" || (confidence === "medium" && alternatives.length >= 2))) {
|
|
588
|
-
log("");
|
|
589
|
-
log(`${bold("⚠ Archetype detection confidence:")} ${cyan(confidence)}`);
|
|
590
|
-
log(` Top candidate: ${cyan(archetype)} — ${dim(rationale)}`);
|
|
594
|
+
log(` ${yellow("⚠")} ${dim(`Detected as ${cyan(archetype)} (${confidence} confidence).`)}`);
|
|
591
595
|
if (alternatives.length > 0) {
|
|
592
|
-
log(`
|
|
596
|
+
log(` ${dim("Alternatives: " + alternatives.join(", "))}`);
|
|
593
597
|
}
|
|
594
|
-
log(` ${dim("
|
|
595
|
-
log(
|
|
598
|
+
log(` ${dim("Override: npx great-cto init --archetype <name>")}`);
|
|
599
|
+
log("");
|
|
596
600
|
}
|
|
597
601
|
// Confirmation
|
|
598
602
|
if (!args.yes) {
|
package/package.json
CHANGED