outfitter 0.2.4 → 0.2.6
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 +8 -5
- package/dist/actions.d.ts +2 -0
- package/dist/actions.js +34 -0
- package/dist/cli.js +1 -1
- package/dist/commands/add.d.ts +54 -0
- package/dist/commands/add.js +16 -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 +13 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.js +31 -0
- package/dist/commands/migrate-kit.d.ts +2 -0
- package/dist/commands/migrate-kit.js +15 -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 +31 -0
- package/dist/commands/shared-deps.d.ts +36 -0
- package/dist/commands/shared-deps.js +10 -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 +76 -0
- package/dist/commands/upgrade-workspace.js +16 -0
- package/dist/commands/upgrade.d.ts +214 -0
- package/dist/commands/upgrade.js +25 -0
- package/dist/create/index.d.ts +5 -0
- package/dist/create/index.js +29 -0
- package/dist/create/planner.d.ts +3 -0
- package/dist/create/planner.js +21 -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 +12 -0
- package/dist/engine/executor.d.ts +3 -0
- package/dist/engine/executor.js +16 -0
- package/dist/engine/index.d.ts +8 -0
- package/dist/engine/index.js +59 -0
- package/dist/engine/names.d.ts +2 -0
- package/dist/engine/names.js +16 -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 +13 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -1
- 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-k59f60cp.js} +858 -396
- package/dist/shared/outfitter-193jvzg4.d.ts +5 -0
- package/dist/shared/outfitter-1dd0k853.js +194 -0
- package/dist/shared/outfitter-1dvma85c.js +322 -0
- package/dist/shared/outfitter-1h7k8xxt.js +29 -0
- package/dist/shared/outfitter-2ngep1h2.d.ts +5 -0
- package/dist/shared/outfitter-2np85etz.js +95 -0
- package/dist/shared/outfitter-33w361tc.d.ts +18 -0
- package/dist/shared/outfitter-344t1r38.js +1 -0
- package/dist/shared/outfitter-3weh61w7.d.ts +25 -0
- package/dist/shared/outfitter-4s9meh3j.js +221 -0
- package/dist/shared/outfitter-66b25bj8.js +125 -0
- package/dist/shared/outfitter-6bkqjk86.d.ts +3 -0
- package/dist/shared/outfitter-79vfxt6y.js +269 -0
- package/dist/shared/outfitter-7ha7p61k.d.ts +6 -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-9zqc2njf.js +859 -0
- package/dist/shared/outfitter-a79xrm12.d.ts +17 -0
- package/dist/shared/outfitter-amc4jbs1.d.ts +50 -0
- package/dist/shared/outfitter-ara3djt0.js +73 -0
- package/dist/shared/outfitter-avhm5z6w.js +82 -0
- package/dist/shared/outfitter-bkwpbkr9.d.ts +63 -0
- package/dist/shared/outfitter-bn9c8p2e.js +204 -0
- package/dist/shared/outfitter-bpr28y54.js +70 -0
- package/dist/shared/outfitter-cwq39bv4.d.ts +48 -0
- package/dist/shared/outfitter-d7pq7d0k.js +196 -0
- package/dist/shared/outfitter-dd0btgec.d.ts +40 -0
- package/dist/shared/outfitter-e2zz5wv7.d.ts +51 -0
- package/dist/shared/outfitter-ehp18x1n.js +1 -0
- package/dist/shared/outfitter-gdvm5c0b.d.ts +4 -0
- package/dist/shared/outfitter-h1mnzzd1.d.ts +14 -0
- package/dist/shared/outfitter-hvsaxgcp.js +1 -0
- package/dist/shared/outfitter-hws10ze7.js +532 -0
- package/dist/shared/outfitter-j833sxws.js +61 -0
- package/dist/shared/outfitter-j8yc7294.d.ts +22 -0
- package/dist/shared/outfitter-k112c427.js +21 -0
- package/dist/shared/outfitter-k56rmt24.d.ts +30 -0
- package/dist/shared/outfitter-ksa1pp4t.d.ts +4 -0
- package/dist/shared/outfitter-ksyvwmb5.js +191 -0
- package/dist/shared/outfitter-mdt37hqm.js +4 -0
- package/dist/shared/outfitter-mtbpabf3.js +91 -0
- package/dist/shared/outfitter-mxz69pgy.js +713 -0
- package/dist/shared/outfitter-npemy7ta.d.ts +53 -0
- package/dist/shared/outfitter-npyfbdmc.d.ts +6 -0
- package/dist/shared/outfitter-pyy1zkfh.d.ts +133 -0
- package/dist/shared/outfitter-q9agarmb.js +42 -0
- package/dist/shared/outfitter-qfh36ddg.d.ts +66 -0
- package/dist/shared/outfitter-qn864k6h.js +581 -0
- package/dist/shared/outfitter-rdc5v5ms.js +746 -0
- package/dist/shared/outfitter-sgtq57qr.d.ts +5 -0
- package/dist/shared/outfitter-ttjr95y9.js +98 -0
- package/dist/shared/outfitter-vh4xgb93.js +35 -0
- package/dist/shared/outfitter-yvksv5qb.js +322 -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 +21 -14
|
@@ -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,73 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
SHARED_DEV_DEPS,
|
|
4
|
+
SHARED_SCRIPTS
|
|
5
|
+
} from "./outfitter-k112c427.js";
|
|
6
|
+
import {
|
|
7
|
+
ScaffoldError
|
|
8
|
+
} from "./outfitter-8y2dfx6n.js";
|
|
9
|
+
|
|
10
|
+
// apps/outfitter/src/engine/config.ts
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
import { Result } from "@outfitter/contracts";
|
|
14
|
+
var DEPENDENCY_SECTIONS = [
|
|
15
|
+
"dependencies",
|
|
16
|
+
"devDependencies",
|
|
17
|
+
"peerDependencies",
|
|
18
|
+
"optionalDependencies"
|
|
19
|
+
];
|
|
20
|
+
function injectSharedConfig(targetDir) {
|
|
21
|
+
const packageJsonPath = join(targetDir, "package.json");
|
|
22
|
+
if (!existsSync(packageJsonPath)) {
|
|
23
|
+
return Result.ok(undefined);
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
27
|
+
const parsed = JSON.parse(content);
|
|
28
|
+
const existingDevDeps = parsed["devDependencies"] ?? {};
|
|
29
|
+
parsed["devDependencies"] = { ...SHARED_DEV_DEPS, ...existingDevDeps };
|
|
30
|
+
const existingScripts = parsed["scripts"] ?? {};
|
|
31
|
+
parsed["scripts"] = { ...SHARED_SCRIPTS, ...existingScripts };
|
|
32
|
+
writeFileSync(packageJsonPath, `${JSON.stringify(parsed, null, 2)}
|
|
33
|
+
`, "utf-8");
|
|
34
|
+
return Result.ok(undefined);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
37
|
+
return Result.err(new ScaffoldError(`Failed to inject shared config: ${message}`));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function rewriteLocalDependencies(targetDir) {
|
|
41
|
+
const packageJsonPath = join(targetDir, "package.json");
|
|
42
|
+
if (!existsSync(packageJsonPath)) {
|
|
43
|
+
return Result.ok(undefined);
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const content = readFileSync(packageJsonPath, "utf-8");
|
|
47
|
+
const parsed = JSON.parse(content);
|
|
48
|
+
let updated = false;
|
|
49
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
50
|
+
const deps = parsed[section];
|
|
51
|
+
if (!deps || typeof deps !== "object" || Array.isArray(deps)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const entries = deps;
|
|
55
|
+
for (const [name, version] of Object.entries(entries)) {
|
|
56
|
+
if (typeof version === "string" && name.startsWith("@outfitter/") && version !== "workspace:*") {
|
|
57
|
+
entries[name] = "workspace:*";
|
|
58
|
+
updated = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (updated) {
|
|
63
|
+
writeFileSync(packageJsonPath, `${JSON.stringify(parsed, null, 2)}
|
|
64
|
+
`, "utf-8");
|
|
65
|
+
}
|
|
66
|
+
return Result.ok(undefined);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
69
|
+
return Result.err(new ScaffoldError(`Failed to update local dependencies: ${message}`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { injectSharedConfig, rewriteLocalDependencies };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
getCreatePreset
|
|
4
|
+
} from "./outfitter-vh4xgb93.js";
|
|
5
|
+
import {
|
|
6
|
+
deriveBinName,
|
|
7
|
+
deriveProjectName
|
|
8
|
+
} from "./outfitter-q9agarmb.js";
|
|
9
|
+
|
|
10
|
+
// apps/outfitter/src/create/planner.ts
|
|
11
|
+
import { Result, ValidationError } from "@outfitter/contracts";
|
|
12
|
+
function derivePackageName(input) {
|
|
13
|
+
return (input.packageName ?? input.name).trim();
|
|
14
|
+
}
|
|
15
|
+
function planCreateProject(input) {
|
|
16
|
+
const packageName = derivePackageName(input);
|
|
17
|
+
if (packageName.length === 0) {
|
|
18
|
+
return Result.err(new ValidationError({
|
|
19
|
+
message: "Project name must not be empty",
|
|
20
|
+
field: "name"
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
const targetDir = input.targetDir.trim();
|
|
24
|
+
if (targetDir.length === 0) {
|
|
25
|
+
return Result.err(new ValidationError({
|
|
26
|
+
message: "Target directory must not be empty",
|
|
27
|
+
field: "targetDir"
|
|
28
|
+
}));
|
|
29
|
+
}
|
|
30
|
+
if (packageName.startsWith("@") && !packageName.includes("/")) {
|
|
31
|
+
return Result.err(new ValidationError({
|
|
32
|
+
message: "Could not derive a project name from package name",
|
|
33
|
+
field: "packageName"
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
const projectName = deriveProjectName(packageName);
|
|
37
|
+
if (projectName.length === 0) {
|
|
38
|
+
return Result.err(new ValidationError({
|
|
39
|
+
message: "Could not derive a project name from package name",
|
|
40
|
+
field: "packageName"
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
const preset = getCreatePreset(input.preset);
|
|
44
|
+
if (!preset) {
|
|
45
|
+
return Result.err(new ValidationError({
|
|
46
|
+
message: `Unknown create preset '${input.preset}'`,
|
|
47
|
+
field: "preset"
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
const includeTooling = input.includeTooling ?? true;
|
|
51
|
+
const defaultBlocks = includeTooling ? [...preset.defaultBlocks] : [];
|
|
52
|
+
const changes = [
|
|
53
|
+
{
|
|
54
|
+
type: "copy-template",
|
|
55
|
+
template: preset.template,
|
|
56
|
+
targetDir,
|
|
57
|
+
overlayBaseTemplate: true
|
|
58
|
+
},
|
|
59
|
+
{ type: "inject-shared-config" }
|
|
60
|
+
];
|
|
61
|
+
if (input.local) {
|
|
62
|
+
changes.push({ type: "rewrite-local-dependencies", mode: "workspace" });
|
|
63
|
+
}
|
|
64
|
+
if (defaultBlocks.length > 0) {
|
|
65
|
+
changes.push({ type: "add-blocks", blocks: defaultBlocks });
|
|
66
|
+
}
|
|
67
|
+
const plan = {
|
|
68
|
+
preset,
|
|
69
|
+
values: {
|
|
70
|
+
packageName,
|
|
71
|
+
projectName,
|
|
72
|
+
version: input.version?.trim() || "0.1.0",
|
|
73
|
+
description: input.description?.trim() || "A new project created with Outfitter",
|
|
74
|
+
binName: deriveBinName(projectName),
|
|
75
|
+
year: input.year ?? String(new Date().getFullYear())
|
|
76
|
+
},
|
|
77
|
+
changes
|
|
78
|
+
};
|
|
79
|
+
return Result.ok(plan);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { planCreateProject };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { TargetId } from "./outfitter-k56rmt24";
|
|
2
|
+
import { PostScaffoldResult } from "./outfitter-3weh61w7";
|
|
3
|
+
import { OutputMode } from "@outfitter/cli/types";
|
|
4
|
+
import { Result } from "@outfitter/contracts";
|
|
5
|
+
import { AddBlockResult } from "@outfitter/tooling";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
type InitStructure = "single" | "workspace";
|
|
8
|
+
type InitPresetId = Extract<TargetId, "minimal" | "cli" | "mcp" | "daemon">;
|
|
9
|
+
/**
|
|
10
|
+
* Options for the init command.
|
|
11
|
+
*/
|
|
12
|
+
interface InitOptions {
|
|
13
|
+
readonly targetDir: string;
|
|
14
|
+
readonly name: string | undefined;
|
|
15
|
+
readonly bin?: string | undefined;
|
|
16
|
+
readonly preset?: InitPresetId | undefined;
|
|
17
|
+
/** @deprecated Use `preset` instead. */
|
|
18
|
+
readonly template?: string | undefined;
|
|
19
|
+
readonly structure?: InitStructure | undefined;
|
|
20
|
+
readonly workspaceName?: string | undefined;
|
|
21
|
+
readonly local?: boolean | undefined;
|
|
22
|
+
readonly force: boolean;
|
|
23
|
+
readonly with?: string | undefined;
|
|
24
|
+
readonly noTooling?: boolean | undefined;
|
|
25
|
+
readonly yes?: boolean | undefined;
|
|
26
|
+
readonly dryRun?: boolean | undefined;
|
|
27
|
+
readonly skipInstall?: boolean | undefined;
|
|
28
|
+
readonly skipGit?: boolean | undefined;
|
|
29
|
+
readonly skipCommit?: boolean | undefined;
|
|
30
|
+
readonly installTimeout?: number | undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Result of running init.
|
|
34
|
+
*/
|
|
35
|
+
interface InitResult {
|
|
36
|
+
readonly structure: InitStructure;
|
|
37
|
+
readonly rootDir: string;
|
|
38
|
+
readonly projectDir: string;
|
|
39
|
+
readonly preset: InitPresetId;
|
|
40
|
+
readonly packageName: string;
|
|
41
|
+
readonly blocksAdded?: AddBlockResult | undefined;
|
|
42
|
+
readonly postScaffold: PostScaffoldResult;
|
|
43
|
+
readonly dryRunPlan?: {
|
|
44
|
+
readonly operations: readonly unknown[];
|
|
45
|
+
readonly summary: Record<string, number>;
|
|
46
|
+
} | undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Error returned when initialization fails.
|
|
50
|
+
*/
|
|
51
|
+
declare class InitError extends Error {
|
|
52
|
+
readonly _tag: "InitError";
|
|
53
|
+
constructor(message: string);
|
|
54
|
+
}
|
|
55
|
+
declare function runInit(options: InitOptions, presetOverride?: InitPresetId): Promise<Result<InitResult, InitError>>;
|
|
56
|
+
declare function printInitResults(result: InitResult, options?: {
|
|
57
|
+
mode?: OutputMode;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* @deprecated Use action-registry CLI wiring via `buildCliCommands(outfitterActions, ...)`.
|
|
61
|
+
*/
|
|
62
|
+
declare function initCommand(program: Command): void;
|
|
63
|
+
export { InitStructure, InitPresetId, InitOptions, InitResult, InitError, runInit, printInitResults, initCommand };
|
|
@@ -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,48 @@
|
|
|
1
|
+
import { PostScaffoldResult } from "./outfitter-3weh61w7";
|
|
2
|
+
import { OutputMode } from "@outfitter/cli/types";
|
|
3
|
+
import { Result } from "@outfitter/contracts";
|
|
4
|
+
import { AddBlockResult } from "@outfitter/tooling";
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
interface ScaffoldOptions {
|
|
7
|
+
readonly target: string;
|
|
8
|
+
readonly name?: string | undefined;
|
|
9
|
+
readonly force: boolean;
|
|
10
|
+
readonly skipInstall: boolean;
|
|
11
|
+
readonly dryRun: boolean;
|
|
12
|
+
readonly with?: string | undefined;
|
|
13
|
+
readonly noTooling?: boolean | undefined;
|
|
14
|
+
readonly local?: boolean | undefined;
|
|
15
|
+
readonly cwd: string;
|
|
16
|
+
readonly installTimeout?: number | undefined;
|
|
17
|
+
}
|
|
18
|
+
interface ScaffoldCommandResult {
|
|
19
|
+
readonly target: string;
|
|
20
|
+
readonly rootDir: string;
|
|
21
|
+
readonly targetDir: string;
|
|
22
|
+
readonly converted: boolean;
|
|
23
|
+
readonly movedExisting?: {
|
|
24
|
+
readonly from: string;
|
|
25
|
+
readonly to: string;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
} | undefined;
|
|
28
|
+
readonly workspacePatternsUpdated: boolean;
|
|
29
|
+
readonly blocksAdded?: AddBlockResult | undefined;
|
|
30
|
+
readonly postScaffold: PostScaffoldResult;
|
|
31
|
+
readonly dryRunPlan?: {
|
|
32
|
+
readonly operations: readonly unknown[];
|
|
33
|
+
readonly summary: Record<string, number>;
|
|
34
|
+
} | undefined;
|
|
35
|
+
}
|
|
36
|
+
declare class ScaffoldCommandError extends Error {
|
|
37
|
+
readonly _tag: "ScaffoldCommandError";
|
|
38
|
+
constructor(message: string);
|
|
39
|
+
}
|
|
40
|
+
declare function runScaffold(options: ScaffoldOptions): Promise<Result<ScaffoldCommandResult, ScaffoldCommandError>>;
|
|
41
|
+
declare function printScaffoldResults(result: ScaffoldCommandResult, options?: {
|
|
42
|
+
readonly mode?: OutputMode;
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated Use action-registry CLI wiring via `buildCliCommands(outfitterActions, ...)`.
|
|
46
|
+
*/
|
|
47
|
+
declare function scaffoldCommand(program: Command): void;
|
|
48
|
+
export { ScaffoldOptions, ScaffoldCommandResult, ScaffoldCommandError, runScaffold, printScaffoldResults, scaffoldCommand };
|