outfitter 0.2.5 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -31
- package/dist/actions.d.ts +2 -0
- package/dist/actions.js +35 -0
- package/dist/cli.js +3 -2
- package/dist/commands/add.d.ts +54 -0
- package/dist/commands/add.js +16 -0
- package/dist/commands/check-tsdoc.d.ts +22 -0
- package/dist/commands/check-tsdoc.js +8 -0
- package/dist/commands/check.d.ts +91 -0
- package/dist/commands/check.js +14 -0
- package/dist/commands/demo.d.ts +21 -0
- package/dist/commands/demo.js +8 -0
- package/dist/commands/docs-module-loader.d.ts +2 -0
- package/dist/commands/docs-module-loader.js +8 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +25 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +32 -0
- package/dist/commands/repo.d.ts +3 -0
- package/dist/commands/repo.js +9 -0
- package/dist/commands/scaffold.d.ts +4 -0
- package/dist/commands/scaffold.js +32 -0
- package/dist/commands/shared-deps.d.ts +21 -0
- package/dist/commands/shared-deps.js +11 -0
- package/dist/commands/upgrade-codemods.d.ts +42 -0
- package/dist/commands/upgrade-codemods.js +15 -0
- package/dist/commands/upgrade-planner.d.ts +58 -0
- package/dist/commands/upgrade-planner.js +8 -0
- package/dist/commands/upgrade-workspace.d.ts +2 -0
- package/dist/commands/upgrade-workspace.js +16 -0
- package/dist/commands/upgrade.d.ts +221 -0
- package/dist/commands/upgrade.js +25 -0
- package/dist/create/index.d.ts +5 -0
- package/dist/create/index.js +30 -0
- package/dist/create/planner.d.ts +3 -0
- package/dist/create/planner.js +22 -0
- package/dist/create/presets.d.ts +3 -0
- package/dist/create/presets.js +12 -0
- package/dist/create/types.d.ts +2 -0
- package/dist/create/types.js +1 -0
- package/dist/engine/blocks.d.ts +3 -0
- package/dist/engine/blocks.js +12 -0
- package/dist/engine/collector.d.ts +2 -0
- package/dist/engine/collector.js +8 -0
- package/dist/engine/config.d.ts +3 -0
- package/dist/engine/config.js +15 -0
- package/dist/engine/dependency-versions.d.ts +12 -0
- package/dist/engine/dependency-versions.js +12 -0
- package/dist/engine/executor.d.ts +3 -0
- package/dist/engine/executor.js +19 -0
- package/dist/engine/index.d.ts +8 -0
- package/dist/engine/index.js +68 -0
- package/dist/engine/names.d.ts +2 -0
- package/dist/engine/names.js +24 -0
- package/dist/engine/post-scaffold.d.ts +3 -0
- package/dist/engine/post-scaffold.js +8 -0
- package/dist/engine/render-plan.d.ts +7 -0
- package/dist/engine/render-plan.js +9 -0
- package/dist/engine/template.d.ts +3 -0
- package/dist/engine/template.js +17 -0
- package/dist/engine/types.d.ts +2 -0
- package/dist/engine/types.js +8 -0
- package/dist/engine/workspace.d.ts +3 -0
- package/dist/engine/workspace.js +20 -0
- package/dist/index.d.ts +100 -129
- package/dist/index.js +1 -9
- package/dist/manifest.d.ts +71 -0
- package/dist/manifest.js +16 -0
- package/dist/output-mode.d.ts +2 -0
- package/dist/output-mode.js +10 -0
- package/dist/shared/{chunk-tpwtpa74.js → chunk-x6644tk8.js} +3840 -2881
- package/dist/shared/outfitter-109s75x0.d.ts +76 -0
- package/dist/shared/outfitter-1fy7byz5.js +170 -0
- package/dist/shared/outfitter-1h7k8xxt.js +29 -0
- package/dist/shared/outfitter-20f6a2n4.js +35 -0
- package/dist/shared/outfitter-344t1r38.js +1 -0
- package/dist/shared/outfitter-4q1zfmvc.js +154 -0
- package/dist/shared/outfitter-4s9meh3j.js +221 -0
- package/dist/shared/outfitter-5akzvppx.js +125 -0
- package/dist/shared/outfitter-5y646xzk.js +301 -0
- package/dist/shared/outfitter-5yjr404v.d.ts +22 -0
- package/dist/shared/outfitter-63gse8fv.js +316 -0
- package/dist/shared/outfitter-6bkqjk86.d.ts +3 -0
- package/dist/shared/outfitter-6fgk6adm.d.ts +40 -0
- package/dist/shared/outfitter-79vfxt6y.js +269 -0
- package/dist/shared/outfitter-7ch26yq8.js +885 -0
- package/dist/shared/outfitter-7r12fj7f.js +30 -0
- package/dist/shared/outfitter-8y2dfx6n.js +11 -0
- package/dist/shared/outfitter-9x1brcmq.js +184 -0
- package/dist/shared/outfitter-a79xrm12.d.ts +17 -0
- package/dist/shared/outfitter-amc4jbs1.d.ts +50 -0
- package/dist/shared/outfitter-bn9c8p2e.js +204 -0
- package/dist/shared/outfitter-bpr28y54.js +70 -0
- package/dist/shared/outfitter-dpj9erew.d.ts +4 -0
- package/dist/shared/outfitter-e9rrfekb.d.ts +51 -0
- package/dist/shared/outfitter-ehp18x1n.js +1 -0
- package/dist/shared/outfitter-f9znfhkn.d.ts +5 -0
- package/dist/shared/outfitter-fhnjpjwc.d.ts +18 -0
- package/dist/shared/outfitter-fn20r49x.d.ts +5 -0
- package/dist/shared/outfitter-h3q6ae6d.d.ts +48 -0
- package/dist/shared/outfitter-ha89qf8q.js +132 -0
- package/dist/shared/outfitter-hsp8vy5m.d.ts +146 -0
- package/dist/shared/outfitter-hvsaxgcp.js +1 -0
- package/dist/shared/outfitter-j833sxws.js +61 -0
- package/dist/shared/outfitter-ksyvwmb5.js +191 -0
- package/dist/shared/outfitter-m3ehh37q.d.ts +22 -0
- package/dist/shared/outfitter-m44n0qzw.js +161 -0
- package/dist/shared/outfitter-mdt37hqm.js +4 -0
- package/dist/shared/outfitter-mt7d1ek2.js +698 -0
- package/dist/shared/outfitter-mtbpabf3.js +91 -0
- package/dist/shared/outfitter-n9g1zk4x.d.ts +66 -0
- package/dist/shared/outfitter-p71qb0f0.js +82 -0
- package/dist/shared/outfitter-pcj9gg2g.js +909 -0
- package/dist/shared/outfitter-pj9vp00r.js +601 -0
- package/dist/shared/outfitter-qakwgrrh.d.ts +4 -0
- package/dist/shared/outfitter-r419zfgs.d.ts +30 -0
- package/dist/shared/outfitter-s7jetkge.d.ts +18 -0
- package/dist/shared/outfitter-ttjr95y9.js +98 -0
- package/dist/shared/outfitter-vh4xgb93.js +35 -0
- package/dist/shared/outfitter-w1j80j1r.js +326 -0
- package/dist/shared/outfitter-xe5mzgdc.js +208 -0
- package/dist/shared/outfitter-ybbazsxq.d.ts +14 -0
- package/dist/shared/outfitter-yraebrmw.d.ts +5 -0
- package/dist/shared/outfitter-yvksv5qb.js +322 -0
- package/dist/shared/outfitter-z0we32cp.d.ts +63 -0
- package/dist/shared/outfitter-z5sx06qe.d.ts +25 -0
- package/dist/shared/outfitter-zwyvewr1.js +36 -0
- package/dist/targets/index.d.ts +4 -0
- package/dist/targets/index.js +29 -0
- package/dist/targets/registry.d.ts +3 -0
- package/dist/targets/registry.js +28 -0
- package/dist/targets/types.d.ts +2 -0
- package/dist/targets/types.js +1 -0
- package/package.json +45 -28
- package/template-versions.json +22 -0
- package/templates/basic/package.json.template +6 -6
- package/templates/cli/biome.json.template +1 -1
- package/templates/cli/package.json.template +17 -9
- package/templates/daemon/biome.json.template +1 -1
- package/templates/daemon/package.json.template +18 -10
- package/templates/full-stack/.gitignore.template +30 -0
- package/templates/full-stack/README.md.template +30 -0
- package/templates/full-stack/apps/cli/package.json.template +39 -0
- package/templates/full-stack/apps/cli/src/cli.ts.template +24 -0
- package/templates/full-stack/apps/cli/src/index.test.ts.template +18 -0
- package/templates/full-stack/apps/cli/src/index.ts.template +5 -0
- package/templates/full-stack/apps/cli/tsconfig.json.template +37 -0
- package/templates/full-stack/apps/mcp/package.json.template +40 -0
- package/templates/full-stack/apps/mcp/src/index.test.ts.template +18 -0
- package/templates/full-stack/apps/mcp/src/index.ts.template +6 -0
- package/templates/full-stack/apps/mcp/src/mcp.ts.template +22 -0
- package/templates/full-stack/apps/mcp/src/server.ts.template +10 -0
- package/templates/full-stack/apps/mcp/tsconfig.json.template +37 -0
- package/templates/full-stack/package.json.template +16 -0
- package/templates/full-stack/packages/core/package.json.template +36 -0
- package/templates/full-stack/packages/core/src/handlers.ts.template +31 -0
- package/templates/full-stack/packages/core/src/index.test.ts.template +30 -0
- package/templates/full-stack/packages/core/src/index.ts.template +8 -0
- package/templates/full-stack/packages/core/src/types.ts.template +13 -0
- package/templates/full-stack/packages/core/tsconfig.json.template +34 -0
- package/templates/library/.gitignore.template +30 -0
- package/templates/library/README.md.template +29 -0
- package/templates/library/bunup.config.ts.template +20 -0
- package/templates/library/package.json.template +55 -0
- package/templates/library/src/handlers.ts.template +31 -0
- package/templates/library/src/index.test.ts.template +35 -0
- package/templates/library/src/index.ts.template +8 -0
- package/templates/library/src/types.ts.template +13 -0
- package/templates/library/tsconfig.json.template +34 -0
- package/templates/mcp/biome.json.template +1 -1
- package/templates/mcp/package.json.template +17 -9
- package/templates/mcp/src/index.ts.template +1 -1
- package/templates/mcp/src/mcp.ts.template +28 -74
- package/templates/mcp/src/server.ts.template +2 -9
- package/templates/minimal/package.json.template +13 -6
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// apps/outfitter/src/output-mode.ts
|
|
3
|
+
function resolveOutputModeFromContext(flags) {
|
|
4
|
+
if (flags["json"])
|
|
5
|
+
return "json";
|
|
6
|
+
if (flags["jsonl"])
|
|
7
|
+
return "jsonl";
|
|
8
|
+
if (process.env["OUTFITTER_JSONL"] === "1")
|
|
9
|
+
return "jsonl";
|
|
10
|
+
if (process.env["OUTFITTER_JSON"] === "1")
|
|
11
|
+
return "json";
|
|
12
|
+
return "human";
|
|
13
|
+
}
|
|
14
|
+
function resolveStructuredOutputMode(mode) {
|
|
15
|
+
if (mode !== undefined) {
|
|
16
|
+
if (mode === "json" || mode === "jsonl") {
|
|
17
|
+
return mode;
|
|
18
|
+
}
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (process.env["OUTFITTER_JSONL"] === "1") {
|
|
22
|
+
return "jsonl";
|
|
23
|
+
}
|
|
24
|
+
if (process.env["OUTFITTER_JSON"] === "1") {
|
|
25
|
+
return "json";
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { resolveOutputModeFromContext, resolveStructuredOutputMode };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
ScaffoldError
|
|
4
|
+
} from "./outfitter-8y2dfx6n.js";
|
|
5
|
+
|
|
6
|
+
// apps/outfitter/src/engine/template.ts
|
|
7
|
+
import {
|
|
8
|
+
existsSync,
|
|
9
|
+
mkdirSync,
|
|
10
|
+
readdirSync,
|
|
11
|
+
readFileSync,
|
|
12
|
+
statSync,
|
|
13
|
+
writeFileSync
|
|
14
|
+
} from "fs";
|
|
15
|
+
import { dirname, extname, join } from "path";
|
|
16
|
+
import { fileURLToPath } from "url";
|
|
17
|
+
import { Result } from "@outfitter/contracts";
|
|
18
|
+
var BINARY_EXTENSIONS = new Set([
|
|
19
|
+
".png",
|
|
20
|
+
".jpg",
|
|
21
|
+
".jpeg",
|
|
22
|
+
".gif",
|
|
23
|
+
".ico",
|
|
24
|
+
".webp",
|
|
25
|
+
".bmp",
|
|
26
|
+
".tiff",
|
|
27
|
+
".svg",
|
|
28
|
+
".woff",
|
|
29
|
+
".woff2",
|
|
30
|
+
".ttf",
|
|
31
|
+
".otf",
|
|
32
|
+
".eot",
|
|
33
|
+
".mp3",
|
|
34
|
+
".mp4",
|
|
35
|
+
".wav",
|
|
36
|
+
".ogg",
|
|
37
|
+
".webm",
|
|
38
|
+
".zip",
|
|
39
|
+
".tar",
|
|
40
|
+
".gz",
|
|
41
|
+
".bz2",
|
|
42
|
+
".7z",
|
|
43
|
+
".pdf",
|
|
44
|
+
".exe",
|
|
45
|
+
".dll",
|
|
46
|
+
".so",
|
|
47
|
+
".dylib",
|
|
48
|
+
".node",
|
|
49
|
+
".wasm",
|
|
50
|
+
".bin",
|
|
51
|
+
".dat",
|
|
52
|
+
".db",
|
|
53
|
+
".sqlite",
|
|
54
|
+
".sqlite3"
|
|
55
|
+
]);
|
|
56
|
+
function hasOutfitterPackage(dir) {
|
|
57
|
+
const packageJsonPath = join(dir, "package.json");
|
|
58
|
+
if (!existsSync(packageJsonPath)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
63
|
+
const parsed = JSON.parse(content);
|
|
64
|
+
return parsed.name === "outfitter";
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function getTemplatesDir() {
|
|
70
|
+
let currentDir = dirname(fileURLToPath(import.meta.url));
|
|
71
|
+
for (let i = 0;i < 10; i++) {
|
|
72
|
+
const templatesPath = join(currentDir, "templates");
|
|
73
|
+
if (existsSync(templatesPath) && hasOutfitterPackage(currentDir)) {
|
|
74
|
+
return templatesPath;
|
|
75
|
+
}
|
|
76
|
+
currentDir = dirname(currentDir);
|
|
77
|
+
}
|
|
78
|
+
const fallback = join(process.cwd(), "apps/outfitter/templates");
|
|
79
|
+
if (existsSync(fallback)) {
|
|
80
|
+
return fallback;
|
|
81
|
+
}
|
|
82
|
+
return join(process.cwd(), "templates");
|
|
83
|
+
}
|
|
84
|
+
function getOutputFilename(templateFilename) {
|
|
85
|
+
return templateFilename.endsWith(".template") ? templateFilename.slice(0, -".template".length) : templateFilename;
|
|
86
|
+
}
|
|
87
|
+
function isBinaryFile(filename) {
|
|
88
|
+
return BINARY_EXTENSIONS.has(extname(filename).toLowerCase());
|
|
89
|
+
}
|
|
90
|
+
function replacePlaceholders(content, values) {
|
|
91
|
+
return content.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
92
|
+
if (Object.hasOwn(values, key)) {
|
|
93
|
+
return values[key];
|
|
94
|
+
}
|
|
95
|
+
return match;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function copyTemplateFiles(templateDir, targetDir, values, options, copyOptions) {
|
|
99
|
+
const allowOverwrite = copyOptions?.allowOverwrite ?? false;
|
|
100
|
+
const relativePrefix = copyOptions?.relativePrefix ?? "";
|
|
101
|
+
try {
|
|
102
|
+
if (!existsSync(targetDir)) {
|
|
103
|
+
if (options.collector) {
|
|
104
|
+
options.collector.add({
|
|
105
|
+
type: "dir-create",
|
|
106
|
+
path: targetDir
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
mkdirSync(targetDir, { recursive: true });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const entries = readdirSync(templateDir);
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
const sourcePath = join(templateDir, entry);
|
|
115
|
+
const sourceStat = statSync(sourcePath);
|
|
116
|
+
const relativePath = relativePrefix ? `${relativePrefix}/${entry}` : entry;
|
|
117
|
+
if (copyOptions?.skipFilter?.(relativePath)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (sourceStat.isDirectory()) {
|
|
121
|
+
const targetSubDir = join(targetDir, entry);
|
|
122
|
+
const nestedResult = copyTemplateFiles(sourcePath, targetSubDir, values, options, {
|
|
123
|
+
...copyOptions,
|
|
124
|
+
relativePrefix: relativePath
|
|
125
|
+
});
|
|
126
|
+
if (nestedResult.isErr()) {
|
|
127
|
+
return nestedResult;
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!sourceStat.isFile()) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const outputFilename = getOutputFilename(entry);
|
|
135
|
+
const targetPath = join(targetDir, outputFilename);
|
|
136
|
+
const targetExists = existsSync(targetPath);
|
|
137
|
+
const canOverlay = allowOverwrite && (!targetExists || Boolean(copyOptions?.overwritablePaths?.has(targetPath)));
|
|
138
|
+
if (targetExists && !options.force && !canOverlay) {
|
|
139
|
+
if (options.collector) {
|
|
140
|
+
options.collector.add({
|
|
141
|
+
type: "file-skip",
|
|
142
|
+
path: targetPath,
|
|
143
|
+
reason: "exists"
|
|
144
|
+
});
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
return Result.err(new ScaffoldError(`File '${targetPath}' already exists. Use --force to overwrite.`));
|
|
148
|
+
}
|
|
149
|
+
if (options.collector) {
|
|
150
|
+
if (targetExists) {
|
|
151
|
+
options.collector.add({
|
|
152
|
+
type: "file-overwrite",
|
|
153
|
+
path: targetPath,
|
|
154
|
+
source: "template"
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
options.collector.add({
|
|
158
|
+
type: "file-create",
|
|
159
|
+
path: targetPath,
|
|
160
|
+
source: "template"
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
copyOptions?.writtenPaths?.add(targetPath);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (isBinaryFile(outputFilename)) {
|
|
167
|
+
const buffer = readFileSync(sourcePath);
|
|
168
|
+
writeFileSync(targetPath, buffer);
|
|
169
|
+
copyOptions?.writtenPaths?.add(targetPath);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const content = readFileSync(sourcePath, "utf-8");
|
|
173
|
+
const processedContent = replacePlaceholders(content, values);
|
|
174
|
+
writeFileSync(targetPath, processedContent, "utf-8");
|
|
175
|
+
copyOptions?.writtenPaths?.add(targetPath);
|
|
176
|
+
}
|
|
177
|
+
return Result.ok(undefined);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
180
|
+
return Result.err(new ScaffoldError(`Failed to copy template files: ${message}`));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export { getTemplatesDir, getOutputFilename, isBinaryFile, replacePlaceholders, copyTemplateFiles };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { OutputMode } from "@outfitter/cli/types";
|
|
2
|
+
type StructuredOutputMode = Extract<OutputMode, "json" | "jsonl">;
|
|
3
|
+
/** Output modes resolvable from CLI flags and env vars. */
|
|
4
|
+
type CliOutputMode = "human" | "json" | "jsonl";
|
|
5
|
+
/**
|
|
6
|
+
* Resolve output mode from CLI context (flags + env vars).
|
|
7
|
+
*
|
|
8
|
+
* Precedence: explicit flag > OUTFITTER_JSONL env > OUTFITTER_JSON env > "human"
|
|
9
|
+
*
|
|
10
|
+
* This function is pure -- no env var side effects.
|
|
11
|
+
*/
|
|
12
|
+
declare function resolveOutputModeFromContext(flags: Record<string, unknown>): CliOutputMode;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve machine-readable output mode from explicit options first, then env.
|
|
15
|
+
*/
|
|
16
|
+
declare function resolveStructuredOutputMode(mode?: OutputMode): StructuredOutputMode | undefined;
|
|
17
|
+
export { StructuredOutputMode, CliOutputMode, resolveOutputModeFromContext, resolveStructuredOutputMode };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type Operation = {
|
|
2
|
+
readonly type: "file-create";
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly source: "template" | "block" | "generated";
|
|
5
|
+
} | {
|
|
6
|
+
readonly type: "file-overwrite";
|
|
7
|
+
readonly path: string;
|
|
8
|
+
readonly source: "template" | "block" | "generated";
|
|
9
|
+
} | {
|
|
10
|
+
readonly type: "file-skip";
|
|
11
|
+
readonly path: string;
|
|
12
|
+
readonly reason: string;
|
|
13
|
+
} | {
|
|
14
|
+
readonly type: "dir-create";
|
|
15
|
+
readonly path: string;
|
|
16
|
+
} | {
|
|
17
|
+
readonly type: "dependency-add";
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly version: string;
|
|
20
|
+
readonly section: "dependencies" | "devDependencies" | "peerDependencies";
|
|
21
|
+
} | {
|
|
22
|
+
readonly type: "block-add";
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly files: readonly string[];
|
|
25
|
+
} | {
|
|
26
|
+
readonly type: "config-inject";
|
|
27
|
+
readonly target: string;
|
|
28
|
+
readonly description: string;
|
|
29
|
+
} | {
|
|
30
|
+
readonly type: "git";
|
|
31
|
+
readonly action: "init" | "add-all" | "commit";
|
|
32
|
+
readonly cwd: string;
|
|
33
|
+
readonly message?: string | undefined;
|
|
34
|
+
} | {
|
|
35
|
+
readonly type: "install";
|
|
36
|
+
readonly command: string;
|
|
37
|
+
readonly cwd: string;
|
|
38
|
+
};
|
|
39
|
+
declare class OperationCollector {
|
|
40
|
+
private readonly operations;
|
|
41
|
+
add(op: Operation): void;
|
|
42
|
+
getOperations(): readonly Operation[];
|
|
43
|
+
countByType(): Record<string, number>;
|
|
44
|
+
isEmpty(): boolean;
|
|
45
|
+
toJSON(): {
|
|
46
|
+
operations: readonly Operation[];
|
|
47
|
+
summary: Record<string, number>;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export { Operation, OperationCollector };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
loadDocsModule
|
|
4
|
+
} from "./outfitter-zwyvewr1.js";
|
|
5
|
+
|
|
6
|
+
// apps/outfitter/src/commands/repo.ts
|
|
7
|
+
import { existsSync } from "fs";
|
|
8
|
+
import { createRequire } from "module";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
import {
|
|
11
|
+
booleanFlagPreset,
|
|
12
|
+
composePresets,
|
|
13
|
+
cwdPreset,
|
|
14
|
+
stringListFlagPreset
|
|
15
|
+
} from "@outfitter/cli/flags";
|
|
16
|
+
import {
|
|
17
|
+
resolveDocsCliOptions,
|
|
18
|
+
withDocsCommonOptions,
|
|
19
|
+
withDocsExportOptions
|
|
20
|
+
} from "@outfitter/docs";
|
|
21
|
+
import { Command } from "commander";
|
|
22
|
+
var require2 = createRequire(import.meta.url);
|
|
23
|
+
function getIo(options) {
|
|
24
|
+
return {
|
|
25
|
+
out: options?.io?.out ?? ((line) => process.stdout.write(`${line}
|
|
26
|
+
`)),
|
|
27
|
+
err: options?.io?.err ?? ((line) => process.stderr.write(`${line}
|
|
28
|
+
`))
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function resolveToolingCliEntrypoint() {
|
|
32
|
+
const packageJsonPath = require2.resolve("@outfitter/tooling/package.json");
|
|
33
|
+
const packageRoot = dirname(packageJsonPath);
|
|
34
|
+
const srcEntrypoint = join(packageRoot, "src", "cli", "index.ts");
|
|
35
|
+
if (existsSync(srcEntrypoint)) {
|
|
36
|
+
return srcEntrypoint;
|
|
37
|
+
}
|
|
38
|
+
const distEntrypoint = join(packageRoot, "dist", "cli", "index.js");
|
|
39
|
+
if (existsSync(distEntrypoint)) {
|
|
40
|
+
return distEntrypoint;
|
|
41
|
+
}
|
|
42
|
+
throw new Error("Unable to resolve @outfitter/tooling CLI entrypoint (expected dist/cli/index.js or src/cli/index.ts).");
|
|
43
|
+
}
|
|
44
|
+
async function runDocsCheckDefault(options, io) {
|
|
45
|
+
const docsModule = await loadDocsModule();
|
|
46
|
+
return await docsModule.executeCheckCommand(options, io);
|
|
47
|
+
}
|
|
48
|
+
async function runDocsSyncDefault(options, io) {
|
|
49
|
+
const docsModule = await loadDocsModule();
|
|
50
|
+
return await docsModule.executeSyncCommand(options, io);
|
|
51
|
+
}
|
|
52
|
+
async function runDocsExportDefault(options, io) {
|
|
53
|
+
const docsModule = await loadDocsModule();
|
|
54
|
+
return await docsModule.executeExportCommand(options, io);
|
|
55
|
+
}
|
|
56
|
+
async function runToolingCommandDefault(input) {
|
|
57
|
+
const toolingCliEntrypoint = resolveToolingCliEntrypoint();
|
|
58
|
+
const child = Bun.spawn([process.execPath, toolingCliEntrypoint, input.command, ...input.args], {
|
|
59
|
+
cwd: input.cwd,
|
|
60
|
+
stdin: "inherit",
|
|
61
|
+
stdout: "inherit",
|
|
62
|
+
stderr: "inherit"
|
|
63
|
+
});
|
|
64
|
+
return await child.exited;
|
|
65
|
+
}
|
|
66
|
+
function applyExitCode(code) {
|
|
67
|
+
if (code !== 0) {
|
|
68
|
+
process.exitCode = code;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function applyPresetOptions(command, preset) {
|
|
72
|
+
for (const option of preset.options) {
|
|
73
|
+
command.option(option.flags, option.description, option.defaultValue);
|
|
74
|
+
}
|
|
75
|
+
return command;
|
|
76
|
+
}
|
|
77
|
+
function addDocsCheckSubcommand(command, options) {
|
|
78
|
+
const docsCheckCommand = command.command("docs").description("Check whether assembled package docs are in sync");
|
|
79
|
+
withDocsCommonOptions(docsCheckCommand).action(async (cmdOptions) => {
|
|
80
|
+
const code = await options.runDocsCheck(resolveDocsCliOptions(cmdOptions), options.io);
|
|
81
|
+
applyExitCode(code);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function addDocsSyncSubcommand(command, options) {
|
|
85
|
+
const docsSyncCommand = command.command("docs").description("Assemble package docs into docs/packages");
|
|
86
|
+
withDocsCommonOptions(docsSyncCommand).action(async (cmdOptions) => {
|
|
87
|
+
const code = await options.runDocsSync(resolveDocsCliOptions(cmdOptions), options.io);
|
|
88
|
+
applyExitCode(code);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function addDocsExportSubcommand(command, options) {
|
|
92
|
+
const docsExportCommand = command.command("docs").description("Export docs artifacts for packages and LLM targets");
|
|
93
|
+
withDocsExportOptions(docsExportCommand).action(async (cmdOptions) => {
|
|
94
|
+
const code = await options.runDocsExport(resolveDocsCliOptions(cmdOptions), options.io);
|
|
95
|
+
applyExitCode(code);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
function addToolingCheckSubcommands(command, runToolingCommand) {
|
|
99
|
+
const cwdFlag = cwdPreset();
|
|
100
|
+
const jsonFlag = booleanFlagPreset({
|
|
101
|
+
id: "repo-check-json",
|
|
102
|
+
key: "json",
|
|
103
|
+
flags: "--json",
|
|
104
|
+
description: "Output results as JSON"
|
|
105
|
+
});
|
|
106
|
+
const skipFlag = booleanFlagPreset({
|
|
107
|
+
id: "repo-check-skip",
|
|
108
|
+
key: "skip",
|
|
109
|
+
flags: "-s, --skip",
|
|
110
|
+
description: "Skip changeset check"
|
|
111
|
+
});
|
|
112
|
+
const pathsFlag = stringListFlagPreset({
|
|
113
|
+
id: "repo-check-paths",
|
|
114
|
+
key: "paths",
|
|
115
|
+
flags: "--paths <paths...>",
|
|
116
|
+
description: "Limit check to specific paths"
|
|
117
|
+
});
|
|
118
|
+
const toolingWithCwd = composePresets(cwdFlag);
|
|
119
|
+
const toolingWithJsonAndCwd = composePresets(jsonFlag, cwdFlag);
|
|
120
|
+
const toolingWithSkipAndCwd = composePresets(skipFlag, cwdFlag);
|
|
121
|
+
const toolingWithPathsAndCwd = composePresets(pathsFlag, cwdFlag);
|
|
122
|
+
function registerToolingCheckSubcommand(config) {
|
|
123
|
+
const subcommand = command.command(config.name).description(config.description);
|
|
124
|
+
applyPresetOptions(subcommand, config.preset);
|
|
125
|
+
subcommand.action(async (cmdOptions) => {
|
|
126
|
+
const resolved = config.preset.resolve(cmdOptions);
|
|
127
|
+
const cwd = resolveDocsCliOptions({ cwd: resolved.cwd }).cwd || process.cwd();
|
|
128
|
+
const code = await runToolingCommand({
|
|
129
|
+
command: config.toolingCommand,
|
|
130
|
+
args: config.buildArgs(resolved),
|
|
131
|
+
cwd
|
|
132
|
+
});
|
|
133
|
+
applyExitCode(code);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
registerToolingCheckSubcommand({
|
|
137
|
+
name: "exports",
|
|
138
|
+
description: "Validate package.json exports match source entry points",
|
|
139
|
+
toolingCommand: "check-exports",
|
|
140
|
+
preset: toolingWithJsonAndCwd,
|
|
141
|
+
buildArgs: (resolved) => resolved["json"] ? ["--json"] : []
|
|
142
|
+
});
|
|
143
|
+
registerToolingCheckSubcommand({
|
|
144
|
+
name: "readme",
|
|
145
|
+
description: "Validate README import examples match package exports",
|
|
146
|
+
toolingCommand: "check-readme-imports",
|
|
147
|
+
preset: toolingWithJsonAndCwd,
|
|
148
|
+
buildArgs: (resolved) => resolved["json"] ? ["--json"] : []
|
|
149
|
+
});
|
|
150
|
+
registerToolingCheckSubcommand({
|
|
151
|
+
name: "registry",
|
|
152
|
+
description: "Validate packages with bunup --filter are registered in bunup.config.ts",
|
|
153
|
+
toolingCommand: "check-bunup-registry",
|
|
154
|
+
preset: toolingWithCwd,
|
|
155
|
+
buildArgs: () => []
|
|
156
|
+
});
|
|
157
|
+
registerToolingCheckSubcommand({
|
|
158
|
+
name: "changeset",
|
|
159
|
+
description: "Validate PRs touching package source include a changeset",
|
|
160
|
+
toolingCommand: "check-changeset",
|
|
161
|
+
preset: toolingWithSkipAndCwd,
|
|
162
|
+
buildArgs: (resolved) => resolved["skip"] ? ["--skip"] : []
|
|
163
|
+
});
|
|
164
|
+
registerToolingCheckSubcommand({
|
|
165
|
+
name: "tree",
|
|
166
|
+
description: "Assert working tree is clean (no modified or untracked files)",
|
|
167
|
+
toolingCommand: "check-clean-tree",
|
|
168
|
+
preset: toolingWithPathsAndCwd,
|
|
169
|
+
buildArgs: (resolved) => Array.isArray(resolved["paths"]) && resolved["paths"].length > 0 ? ["--paths", ...resolved["paths"]] : []
|
|
170
|
+
});
|
|
171
|
+
registerToolingCheckSubcommand({
|
|
172
|
+
name: "boundary-invocations",
|
|
173
|
+
description: "Validate root/app scripts do not execute packages/*/src entrypoints directly",
|
|
174
|
+
toolingCommand: "check-boundary-invocations",
|
|
175
|
+
preset: toolingWithCwd,
|
|
176
|
+
buildArgs: () => []
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function createRepoCommand(options) {
|
|
180
|
+
const io = getIo(options);
|
|
181
|
+
const runDocsCheck = options?.runDocsCheck ?? runDocsCheckDefault;
|
|
182
|
+
const runDocsSync = options?.runDocsSync ?? runDocsSyncDefault;
|
|
183
|
+
const runDocsExport = options?.runDocsExport ?? runDocsExportDefault;
|
|
184
|
+
const runToolingCommand = options?.runToolingCommand ?? runToolingCommandDefault;
|
|
185
|
+
const command = new Command(options?.commandName ?? "repo");
|
|
186
|
+
command.description("Repository maintenance commands");
|
|
187
|
+
const checkCommand = command.command("check").description("Run repository checks by subject");
|
|
188
|
+
addDocsCheckSubcommand(checkCommand, { io, runDocsCheck });
|
|
189
|
+
addToolingCheckSubcommands(checkCommand, runToolingCommand);
|
|
190
|
+
const syncCommand = command.command("sync").description("Synchronize repository artifacts by subject");
|
|
191
|
+
addDocsSyncSubcommand(syncCommand, { io, runDocsSync });
|
|
192
|
+
const exportCommand = command.command("export").description("Export repository artifacts by subject");
|
|
193
|
+
addDocsExportSubcommand(exportCommand, { io, runDocsExport });
|
|
194
|
+
return command;
|
|
195
|
+
}
|
|
196
|
+
async function main() {
|
|
197
|
+
const command = createRepoCommand({ commandName: "repo" });
|
|
198
|
+
await command.parseAsync(process.argv, { from: "node" });
|
|
199
|
+
}
|
|
200
|
+
if (import.meta.main) {
|
|
201
|
+
main();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export { createRepoCommand };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// apps/outfitter/src/commands/upgrade-planner.ts
|
|
3
|
+
function getMajor(version) {
|
|
4
|
+
const parts = version.split(".");
|
|
5
|
+
return Number.parseInt(parts[0] ?? "0", 10);
|
|
6
|
+
}
|
|
7
|
+
function getMinor(version) {
|
|
8
|
+
const parts = version.split(".");
|
|
9
|
+
return Number.parseInt(parts[1] ?? "0", 10);
|
|
10
|
+
}
|
|
11
|
+
function isBreaking(currentVersion, latestVersion, breakingFlag) {
|
|
12
|
+
if (breakingFlag === true)
|
|
13
|
+
return true;
|
|
14
|
+
if (breakingFlag === false)
|
|
15
|
+
return false;
|
|
16
|
+
const currentMajor = getMajor(currentVersion);
|
|
17
|
+
const latestMajor = getMajor(latestVersion);
|
|
18
|
+
if (latestMajor > currentMajor) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (currentMajor === 0) {
|
|
22
|
+
const currentMinor = getMinor(currentVersion);
|
|
23
|
+
const latestMinor = getMinor(latestVersion);
|
|
24
|
+
if (latestMinor > currentMinor) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
function classify(currentVersion, latestVersion, breakingFlag) {
|
|
31
|
+
if (Bun.semver.order(latestVersion, currentVersion) <= 0) {
|
|
32
|
+
return "upToDate";
|
|
33
|
+
}
|
|
34
|
+
if (isBreaking(currentVersion, latestVersion, breakingFlag)) {
|
|
35
|
+
return "upgradableBreaking";
|
|
36
|
+
}
|
|
37
|
+
return "upgradableNonBreaking";
|
|
38
|
+
}
|
|
39
|
+
function analyzeUpgrades(installed, latest, migrationDocs) {
|
|
40
|
+
const packages = [];
|
|
41
|
+
for (const [name, currentVersion] of installed) {
|
|
42
|
+
const latestInfo = latest.get(name);
|
|
43
|
+
const latestVersion = latestInfo?.version ?? currentVersion;
|
|
44
|
+
const breakingFlag = latestInfo?.breaking;
|
|
45
|
+
const classification = classify(currentVersion, latestVersion, breakingFlag);
|
|
46
|
+
const breaking = classification === "upgradableBreaking";
|
|
47
|
+
const migrationDoc = migrationDocs?.get(name);
|
|
48
|
+
packages.push({
|
|
49
|
+
name,
|
|
50
|
+
currentVersion,
|
|
51
|
+
latestVersion,
|
|
52
|
+
classification,
|
|
53
|
+
breaking,
|
|
54
|
+
...migrationDoc !== undefined ? { migrationDoc } : {}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
packages.sort((a, b) => a.name.localeCompare(b.name));
|
|
58
|
+
const summary = {
|
|
59
|
+
upToDate: 0,
|
|
60
|
+
upgradableNonBreaking: 0,
|
|
61
|
+
upgradableBreaking: 0,
|
|
62
|
+
blocked: 0
|
|
63
|
+
};
|
|
64
|
+
for (const pkg of packages) {
|
|
65
|
+
summary[pkg.classification]++;
|
|
66
|
+
}
|
|
67
|
+
return { packages, summary };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { analyzeUpgrades };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CreateProjectInput, CreateProjectPlan } from "./outfitter-e9rrfekb.js";
|
|
2
|
+
import { Result, ValidationError } from "@outfitter/contracts";
|
|
3
|
+
declare function planCreateProject(input: CreateProjectInput): Result<CreateProjectPlan, ValidationError>;
|
|
4
|
+
export { planCreateProject };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create planner types for kit-first project scaffolding.
|
|
3
|
+
*
|
|
4
|
+
* These types model preset intent separately from execution so future flows can
|
|
5
|
+
* consume a deterministic plan before mutating the filesystem.
|
|
6
|
+
*/
|
|
7
|
+
type CreatePresetId = "basic" | "cli" | "daemon" | "mcp";
|
|
8
|
+
interface CreatePresetDefinition {
|
|
9
|
+
readonly defaultBlocks: readonly string[];
|
|
10
|
+
readonly id: CreatePresetId;
|
|
11
|
+
readonly summary: string;
|
|
12
|
+
readonly template: CreatePresetId;
|
|
13
|
+
}
|
|
14
|
+
interface CreateProjectInput {
|
|
15
|
+
readonly description?: string;
|
|
16
|
+
readonly includeTooling?: boolean;
|
|
17
|
+
readonly local?: boolean;
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly packageName?: string;
|
|
20
|
+
readonly preset: CreatePresetId;
|
|
21
|
+
readonly targetDir: string;
|
|
22
|
+
readonly version?: string;
|
|
23
|
+
readonly year?: string;
|
|
24
|
+
}
|
|
25
|
+
type CreatePlanChange = {
|
|
26
|
+
readonly type: "copy-template";
|
|
27
|
+
readonly template: string;
|
|
28
|
+
readonly targetDir: string;
|
|
29
|
+
readonly overlayBaseTemplate: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
readonly type: "inject-shared-config";
|
|
32
|
+
} | {
|
|
33
|
+
readonly type: "rewrite-local-dependencies";
|
|
34
|
+
readonly mode: "workspace";
|
|
35
|
+
} | {
|
|
36
|
+
readonly type: "add-blocks";
|
|
37
|
+
readonly blocks: readonly string[];
|
|
38
|
+
};
|
|
39
|
+
interface CreateProjectPlan {
|
|
40
|
+
readonly changes: readonly CreatePlanChange[];
|
|
41
|
+
readonly preset: CreatePresetDefinition;
|
|
42
|
+
readonly values: {
|
|
43
|
+
readonly packageName: string;
|
|
44
|
+
readonly projectName: string;
|
|
45
|
+
readonly version: string;
|
|
46
|
+
readonly description: string;
|
|
47
|
+
readonly binName: string;
|
|
48
|
+
readonly year: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export { CreatePresetId, CreatePresetDefinition, CreateProjectInput, CreatePlanChange, CreateProjectPlan };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ScaffoldError } from "./outfitter-n9g1zk4x.js";
|
|
2
|
+
import { Result } from "@outfitter/contracts";
|
|
3
|
+
declare function injectSharedConfig(targetDir: string): Result<void, ScaffoldError>;
|
|
4
|
+
declare function rewriteLocalDependencies(targetDir: string): Result<void, ScaffoldError>;
|
|
5
|
+
export { injectSharedConfig, rewriteLocalDependencies };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ScaffoldError } from "./outfitter-n9g1zk4x.js";
|
|
2
|
+
import { Result } from "@outfitter/contracts";
|
|
3
|
+
declare function buildWorkspaceRootReadme(workspaceName: string): string;
|
|
4
|
+
declare function buildWorkspaceRootPackageJson(workspaceName: string): string;
|
|
5
|
+
declare function scaffoldWorkspaceRoot(rootDir: string, workspaceName: string, force: boolean): Result<void, ScaffoldError>;
|
|
6
|
+
/**
|
|
7
|
+
* Extracts workspace glob patterns from a package.json `workspaces` field.
|
|
8
|
+
*
|
|
9
|
+
* Handles both array form (`["apps/*"]`) and object form (`{ packages: ["apps/*"] }`).
|
|
10
|
+
*/
|
|
11
|
+
declare function getWorkspacePatterns(workspaces: unknown): readonly string[];
|
|
12
|
+
interface PackageDeps {
|
|
13
|
+
workspaces?: unknown;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
declare function hasWorkspacesField(pkg: PackageDeps): boolean;
|
|
17
|
+
declare function detectWorkspaceRoot(cwd: string): Result<string | null, ScaffoldError>;
|
|
18
|
+
export { buildWorkspaceRootReadme, buildWorkspaceRootPackageJson, scaffoldWorkspaceRoot, getWorkspacePatterns, hasWorkspacesField, detectWorkspaceRoot };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { EngineOptions, ScaffoldError } from "./outfitter-n9g1zk4x.js";
|
|
2
|
+
import { Result } from "@outfitter/contracts";
|
|
3
|
+
import { AddBlockResult } from "@outfitter/tooling";
|
|
4
|
+
declare function addBlocks(targetDir: string, blocks: readonly string[], options: EngineOptions): Promise<Result<AddBlockResult, ScaffoldError>>;
|
|
5
|
+
export { addBlocks };
|