@o/cli 0.16.19 → 0.16.20-canary.1791848111568
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/cmd.d.ts +10 -8
- package/dist/cmd.d.ts.map +1 -1
- package/dist/cmd.js.map +1 -1
- package/package.json +31 -6
- package/src/tko/cli.ts +173 -0
- package/src/tko/commands/changed.ts +313 -0
- package/src/tko/commands/docs.ts +164 -0
- package/src/tko/commands/env-setup.ts +69 -0
- package/src/tko/commands/onboard.ts +1420 -0
- package/src/tko/commands/run-all.ts +83 -0
- package/src/tko/commands/run.ts +135 -0
- package/src/tko/commands/script.ts +431 -0
- package/src/tko/commands/skills.test.ts +193 -0
- package/src/tko/commands/skills.ts +361 -0
- package/src/tko/commands/sync.ts +319 -0
- package/src/tko/constants/ascii.ts +17 -0
- package/src/tko/index.ts +1 -0
- package/src/tko/types.ts +59 -0
- package/src/tko/utils/env-categories.ts +246 -0
- package/src/tko/utils/env-setup.ts +335 -0
- package/src/tko/utils/env.ts +127 -0
- package/src/tko/utils/files.ts +302 -0
- package/src/tko/utils/parallel-runner.ts +195 -0
- package/src/tko/utils/ports.ts +77 -0
- package/src/tko/utils/prerequisites.ts +137 -0
- package/src/tko/utils/prompts.ts +203 -0
- package/src/tko/utils/script-listing.ts +194 -0
- package/src/tko/utils/script-utils.ts +103 -0
- package/src/tko/utils/sync.ts +101 -0
- package/src/tko/withExpoModulesCoreSwift6Fix.cjs +58 -0
- package/src/tko/withOpSqliteStatic.cjs +51 -0
package/dist/cmd.d.ts
CHANGED
|
@@ -14,18 +14,20 @@ type Args<S extends string> = import('./args.js').Args<S>;
|
|
|
14
14
|
type RunFn<S extends string> = (fn: (ctx: Prettify<{
|
|
15
15
|
args: Args<S>;
|
|
16
16
|
} & CmdContext>) => Promise<void> | void) => Promise<void>;
|
|
17
|
+
type Chainable<S extends string> = {
|
|
18
|
+
env(vars: Record<string, string>): Chainable<S>;
|
|
19
|
+
run: RunFn<S>;
|
|
20
|
+
};
|
|
21
|
+
type Command = {
|
|
22
|
+
env(vars: Record<string, string>): Command;
|
|
23
|
+
args<const S extends string>(spec: S): Chainable<S>;
|
|
24
|
+
run: RunFn<''>;
|
|
25
|
+
};
|
|
17
26
|
export declare function setInterceptCmd(fn: ((info: {
|
|
18
27
|
description: string;
|
|
19
28
|
args?: string;
|
|
20
29
|
}) => void) | undefined): void;
|
|
21
|
-
declare function createCmd(description: string):
|
|
22
|
-
env(vars: Record<string, string>): /*elided*/ any;
|
|
23
|
-
args<const S extends string>(spec: S): {
|
|
24
|
-
env(vars: Record<string, string>): /*elided*/ any;
|
|
25
|
-
run: RunFn<S>;
|
|
26
|
-
};
|
|
27
|
-
run: RunFn<"">;
|
|
28
|
-
};
|
|
30
|
+
declare function createCmd(description: string): Command;
|
|
29
31
|
export declare function cmd(description: string): ReturnType<typeof createCmd>;
|
|
30
32
|
export declare function cmd(strings: TemplateStringsArray): ReturnType<typeof createCmd>;
|
|
31
33
|
export {};
|
package/dist/cmd.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cmd.d.ts","sourceRoot":"","sources":["../src/cmd.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAA;AAEhD,KAAK,UAAU,GAAG;IAChB,CAAC,EAAE,cAAc,KAAK,EAAE,CAAC,CAAA;IACzB,MAAM,EAAE,cAAc,YAAY,CAAC,CAAA;IACnC,MAAM,EAAE,cAAc,gBAAgB,CAAC,CAAA;IACvC,GAAG,EAAE,cAAc,UAAU,EAAE,GAAG,CAAA;IAClC,EAAE,EAAE,cAAc,SAAS,CAAC,CAAA;IAC5B,IAAI,EAAE,cAAc,WAAW,CAAC,CAAA;IAChC,EAAE,EAAE,cAAc,SAAS,CAAC,CAAA;CAC7B,CAAA;AAED,KAAK,IAAI,CAAC,CAAC,SAAS,MAAM,IAAI,OAAO,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;AAEzD,KAAK,KAAK,CAAC,CAAC,SAAS,MAAM,IAAI,CAC7B,EAAE,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;CAAE,GAAG,UAAU,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,KACxE,OAAO,CAAC,IAAI,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"cmd.d.ts","sourceRoot":"","sources":["../src/cmd.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAA;AAEhD,KAAK,UAAU,GAAG;IAChB,CAAC,EAAE,cAAc,KAAK,EAAE,CAAC,CAAA;IACzB,MAAM,EAAE,cAAc,YAAY,CAAC,CAAA;IACnC,MAAM,EAAE,cAAc,gBAAgB,CAAC,CAAA;IACvC,GAAG,EAAE,cAAc,UAAU,EAAE,GAAG,CAAA;IAClC,EAAE,EAAE,cAAc,SAAS,CAAC,CAAA;IAC5B,IAAI,EAAE,cAAc,WAAW,CAAC,CAAA;IAChC,EAAE,EAAE,cAAc,SAAS,CAAC,CAAA;CAC7B,CAAA;AAED,KAAK,IAAI,CAAC,CAAC,SAAS,MAAM,IAAI,OAAO,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;AAEzD,KAAK,KAAK,CAAC,CAAC,SAAS,MAAM,IAAI,CAC7B,EAAE,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAA;CAAE,GAAG,UAAU,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,KACxE,OAAO,CAAC,IAAI,CAAC,CAAA;AAElB,KAAK,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI;IACjC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;IAC/C,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;CACd,CAAA;AAED,KAAK,OAAO,GAAG;IACb,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAA;IAC1C,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAA;IACnD,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,CAAA;CACf,CAAA;AAID,wBAAgB,eAAe,CAC7B,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,IAAI,CAAC,GAAG,SAAS,QAGzE;AAED,iBAAS,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAuE/C;AAED,wBAAgB,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAA;AACtE,wBAAgB,GAAG,CAAC,OAAO,EAAE,oBAAoB,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAA"}
|
package/dist/cmd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cmd.js","sourceRoot":"","sources":["../src/cmd.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cmd.js","sourceRoot":"","sources":["../src/cmd.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@o/cli",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.20-canary.1791848111568",
|
|
4
4
|
"description": "Typed command and subprocess helpers for Orez consumers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"module": "dist/index.js",
|
|
15
15
|
"types": "./dist/index.d.ts",
|
|
16
16
|
"bin": {
|
|
17
|
-
"o": "./dist/cli.js"
|
|
17
|
+
"o": "./dist/cli.js",
|
|
18
|
+
"tko": "./src/tko/cli.ts",
|
|
19
|
+
"takeout": "./src/tko/cli.ts"
|
|
18
20
|
},
|
|
19
21
|
"exports": {
|
|
20
22
|
"./package.json": "./package.json",
|
|
@@ -22,12 +24,23 @@
|
|
|
22
24
|
"types": "./dist/index.d.ts",
|
|
23
25
|
"import": "./dist/index.js",
|
|
24
26
|
"default": "./dist/index.js"
|
|
25
|
-
}
|
|
27
|
+
},
|
|
28
|
+
"./utils/*": {
|
|
29
|
+
"types": "./src/tko/utils/*.ts",
|
|
30
|
+
"default": "./src/tko/utils/*.ts"
|
|
31
|
+
},
|
|
32
|
+
"./constants/*": {
|
|
33
|
+
"types": "./src/tko/constants/*.ts",
|
|
34
|
+
"default": "./src/tko/constants/*.ts"
|
|
35
|
+
},
|
|
36
|
+
"./withOpSqliteStatic": "./src/tko/withOpSqliteStatic.cjs",
|
|
37
|
+
"./withExpoModulesCoreSwift6Fix": "./src/tko/withExpoModulesCoreSwift6Fix.cjs"
|
|
26
38
|
},
|
|
27
39
|
"files": [
|
|
28
40
|
"dist",
|
|
29
41
|
"LICENSE",
|
|
30
|
-
"README.md"
|
|
42
|
+
"README.md",
|
|
43
|
+
"src/tko"
|
|
31
44
|
],
|
|
32
45
|
"scripts": {
|
|
33
46
|
"build": "rm -rf dist && tsc -p tsconfig.build.json && cp src/run-pty.mjs dist/run-pty.mjs && chmod +x dist/cli.js dist/run-pty.mjs",
|
|
@@ -40,7 +53,10 @@
|
|
|
40
53
|
"dependencies": {
|
|
41
54
|
"@clack/prompts": "^0.8.2",
|
|
42
55
|
"@lydell/node-pty": "^1.2.0-beta.3",
|
|
43
|
-
"picocolors": "^1.1.1"
|
|
56
|
+
"picocolors": "^1.1.1",
|
|
57
|
+
"@o/run": "0.16.20-canary.1791848111568",
|
|
58
|
+
"@o/scripts": "0.16.20-canary.1791848111568",
|
|
59
|
+
"citty": "^0.1.6"
|
|
44
60
|
},
|
|
45
61
|
"devDependencies": {
|
|
46
62
|
"@types/bun": "1.3.14",
|
|
@@ -50,5 +66,14 @@
|
|
|
50
66
|
},
|
|
51
67
|
"publishConfig": {
|
|
52
68
|
"access": "public"
|
|
53
|
-
}
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"@expo/config-plugins": "*"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"@expo/config-plugins": {
|
|
75
|
+
"optional": true
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
"releaseSourceCommit": "541e39001d511200c35f30786b84516ed2af9b5b"
|
|
54
79
|
}
|
package/src/tko/cli.ts
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Takeout CLI
|
|
5
|
+
* Interactive tools for Takeout starter kit setup and management
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from 'node:child_process'
|
|
9
|
+
import { existsSync, statSync } from 'node:fs'
|
|
10
|
+
import { join } from 'node:path'
|
|
11
|
+
|
|
12
|
+
import { defineCommand, runMain } from 'citty'
|
|
13
|
+
|
|
14
|
+
// check if a directory exists in scripts folder (lazy check)
|
|
15
|
+
function isScriptCategory(name: string): boolean {
|
|
16
|
+
const scriptsDir = join(process.cwd(), 'scripts')
|
|
17
|
+
const categoryPath = join(scriptsDir, name)
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
return existsSync(categoryPath) && statSync(categoryPath).isDirectory()
|
|
21
|
+
} catch {
|
|
22
|
+
return false
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// find a local script in ./scripts/, returns path or null
|
|
27
|
+
function findLocalScript(name: string): string | null {
|
|
28
|
+
const scriptsDir = join(process.cwd(), 'scripts')
|
|
29
|
+
const normalizedName = name.replace(/:/g, '/')
|
|
30
|
+
|
|
31
|
+
for (const ext of ['.ts', '.js', '']) {
|
|
32
|
+
const scriptPath = join(scriptsDir, `${normalizedName}${ext}`)
|
|
33
|
+
if (existsSync(scriptPath)) {
|
|
34
|
+
return scriptPath
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return null
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isLocalScript(name: string): boolean {
|
|
42
|
+
return findLocalScript(name) !== null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// find a built-in script in @o/scripts, returns path or null
|
|
46
|
+
function findBuiltInScript(name: string): string | null {
|
|
47
|
+
try {
|
|
48
|
+
const resolved = import.meta.resolve('@o/scripts/package.json')
|
|
49
|
+
const packageJsonPath = new URL(resolved).pathname
|
|
50
|
+
const packageRoot = join(packageJsonPath, '..')
|
|
51
|
+
const srcPath = join(packageRoot, 'src')
|
|
52
|
+
|
|
53
|
+
// normalize name: convert colons to slashes
|
|
54
|
+
const normalizedName = name.replace(/:/g, '/')
|
|
55
|
+
|
|
56
|
+
// check if the script file exists
|
|
57
|
+
for (const ext of ['.ts', '.js', '']) {
|
|
58
|
+
const scriptPath = join(srcPath, `${normalizedName}${ext}`)
|
|
59
|
+
if (existsSync(scriptPath)) {
|
|
60
|
+
return scriptPath
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// package not found or error resolving
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isBuiltInScript(name: string): boolean {
|
|
71
|
+
return findBuiltInScript(name) !== null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// always enable shorthand mode — both tko and takeout resolve to this file
|
|
75
|
+
const isShorthand = true
|
|
76
|
+
|
|
77
|
+
if (isShorthand) {
|
|
78
|
+
// in shorthand mode, treat first arg as potential script command
|
|
79
|
+
const firstArg = process.argv[2]
|
|
80
|
+
const builtInCommands = [
|
|
81
|
+
'docs',
|
|
82
|
+
'onboard',
|
|
83
|
+
'run',
|
|
84
|
+
'run-all',
|
|
85
|
+
'script',
|
|
86
|
+
'skills',
|
|
87
|
+
'env:setup',
|
|
88
|
+
'sync',
|
|
89
|
+
'changed',
|
|
90
|
+
'--help',
|
|
91
|
+
'-h',
|
|
92
|
+
'--version',
|
|
93
|
+
'-v',
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
if (firstArg && !builtInCommands.includes(firstArg)) {
|
|
97
|
+
// resolve the script name for category/script patterns
|
|
98
|
+
let resolvedScriptName: string | undefined
|
|
99
|
+
|
|
100
|
+
// check if it's a script category or has a slash (category/script format)
|
|
101
|
+
// Do a lazy check only on the specific arg, not scanning all directories
|
|
102
|
+
if (isScriptCategory(firstArg)) {
|
|
103
|
+
// find first non-flag arg as sub-script name
|
|
104
|
+
// e.g., "tko check types" or "tko check --default-all types"
|
|
105
|
+
const restArgs = process.argv.slice(3)
|
|
106
|
+
const subScriptIdx = restArgs.findIndex((a) => !a.startsWith('-'))
|
|
107
|
+
if (subScriptIdx !== -1) {
|
|
108
|
+
const subScript = restArgs[subScriptIdx]
|
|
109
|
+
resolvedScriptName = `${firstArg}/${subScript}`
|
|
110
|
+
// remove the sub-script from its position and replace category with combined path
|
|
111
|
+
process.argv.splice(3 + subScriptIdx, 1)
|
|
112
|
+
process.argv[2] = resolvedScriptName
|
|
113
|
+
}
|
|
114
|
+
// inject "run" into args to use the run command directly
|
|
115
|
+
process.argv.splice(2, 0, 'run')
|
|
116
|
+
} else if (firstArg?.includes('/')) {
|
|
117
|
+
resolvedScriptName = firstArg
|
|
118
|
+
// inject "run" into args to use the run command directly
|
|
119
|
+
process.argv.splice(2, 0, 'run')
|
|
120
|
+
} else if (isLocalScript(firstArg) || isBuiltInScript(firstArg)) {
|
|
121
|
+
resolvedScriptName = firstArg
|
|
122
|
+
// check if it's a local or built-in script (like update-deps, clean, etc.)
|
|
123
|
+
// use "run" to avoid citty parsing issues with hyphens
|
|
124
|
+
process.argv.splice(2, 0, 'run')
|
|
125
|
+
} else {
|
|
126
|
+
// assume it's a script command, inject "script" into args
|
|
127
|
+
process.argv.splice(2, 0, 'script')
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// bypass citty for --help on scripts so the script handles it itself
|
|
131
|
+
const hasHelp = process.argv.includes('--help') || process.argv.includes('-h')
|
|
132
|
+
if (resolvedScriptName && hasHelp) {
|
|
133
|
+
const scriptPath =
|
|
134
|
+
findLocalScript(resolvedScriptName) || findBuiltInScript(resolvedScriptName)
|
|
135
|
+
if (scriptPath) {
|
|
136
|
+
const flagArgs = process.argv.slice(3).filter((a) => a !== resolvedScriptName)
|
|
137
|
+
const result = spawnSync('bun', [scriptPath, ...flagArgs], {
|
|
138
|
+
stdio: 'inherit',
|
|
139
|
+
shell: false,
|
|
140
|
+
})
|
|
141
|
+
process.exit(result.status || 0)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const main = defineCommand({
|
|
148
|
+
meta: {
|
|
149
|
+
name: isShorthand ? 'tko' : 'takeout',
|
|
150
|
+
version: '0.0.2',
|
|
151
|
+
description: 'CLI tools for Takeout starter kit',
|
|
152
|
+
},
|
|
153
|
+
subCommands: {
|
|
154
|
+
onboard: () => import('./commands/onboard').then((m) => m.onboardCommand),
|
|
155
|
+
docs: () => import('./commands/docs').then((m) => m.docsCommand),
|
|
156
|
+
'env:setup': () => import('./commands/env-setup').then((m) => m.envSetupCommand),
|
|
157
|
+
run: () => import('./commands/run').then((m) => m.runCommand),
|
|
158
|
+
'run-all': () => import('./commands/run-all').then((m) => m.runAllCommand),
|
|
159
|
+
script: () => import('./commands/script').then((m) => m.scriptCommand),
|
|
160
|
+
skills: () => import('./commands/skills').then((m) => m.skillsCommand),
|
|
161
|
+
sync: () => import('./commands/sync').then((m) => m.syncCommand),
|
|
162
|
+
changed: () => import('./commands/changed').then((m) => m.changedCommand),
|
|
163
|
+
},
|
|
164
|
+
async run() {
|
|
165
|
+
const hasArgs = process.argv.length > 2
|
|
166
|
+
if (!hasArgs) {
|
|
167
|
+
const { listAllScripts } = await import('./utils/script-listing')
|
|
168
|
+
await listAllScripts()
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
runMain(main)
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Changed command - show what's changed in upstream Takeout since last sync
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { execSync } from 'node:child_process'
|
|
6
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
7
|
+
import { join } from 'node:path'
|
|
8
|
+
|
|
9
|
+
import { defineCommand } from 'citty'
|
|
10
|
+
import pc from 'picocolors'
|
|
11
|
+
|
|
12
|
+
const UPSTREAM_REPO = 'tamagui/takeout2'
|
|
13
|
+
const UPSTREAM_REMOTE = 'takeout-upstream'
|
|
14
|
+
const TAKEOUT_FILE = '.takeout'
|
|
15
|
+
|
|
16
|
+
const COMMIT_TYPE_ORDER = [
|
|
17
|
+
'feat',
|
|
18
|
+
'fix',
|
|
19
|
+
'perf',
|
|
20
|
+
'refactor',
|
|
21
|
+
'docs',
|
|
22
|
+
'chore',
|
|
23
|
+
'test',
|
|
24
|
+
'ci',
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
interface CommitInfo {
|
|
28
|
+
hash: string
|
|
29
|
+
type: string
|
|
30
|
+
scope?: string
|
|
31
|
+
message: string
|
|
32
|
+
breaking: boolean
|
|
33
|
+
date: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface TakeoutConfig {
|
|
37
|
+
sha?: string
|
|
38
|
+
date?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readTakeoutConfig(): TakeoutConfig | null {
|
|
42
|
+
const configPath = join(process.cwd(), TAKEOUT_FILE)
|
|
43
|
+
|
|
44
|
+
if (!existsSync(configPath)) {
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const content = readFileSync(configPath, 'utf-8')
|
|
50
|
+
const config: TakeoutConfig = {}
|
|
51
|
+
|
|
52
|
+
for (const line of content.split('\n')) {
|
|
53
|
+
const trimmed = line.trim()
|
|
54
|
+
if (trimmed.startsWith('#') || !trimmed) continue
|
|
55
|
+
|
|
56
|
+
const [key, ...valueParts] = trimmed.split('=')
|
|
57
|
+
const value = valueParts.join('=').trim()
|
|
58
|
+
|
|
59
|
+
if (key === 'sha') config.sha = value
|
|
60
|
+
if (key === 'date') config.date = value
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return config
|
|
64
|
+
} catch {
|
|
65
|
+
return null
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function parseConventionalCommit(
|
|
70
|
+
message: string
|
|
71
|
+
): { type: string; scope?: string; message: string; breaking: boolean } | null {
|
|
72
|
+
// match conventional commit format: type(scope)!: message or type!: message
|
|
73
|
+
const match = message.match(/^(\w+)(?:\(([^)]+)\))?(!)?: (.+)$/)
|
|
74
|
+
if (!match) return null
|
|
75
|
+
|
|
76
|
+
const [, type, scope, breaking, msg] = match
|
|
77
|
+
|
|
78
|
+
// include all commit types for takeout
|
|
79
|
+
const validTypes = [
|
|
80
|
+
'feat',
|
|
81
|
+
'fix',
|
|
82
|
+
'perf',
|
|
83
|
+
'refactor',
|
|
84
|
+
'docs',
|
|
85
|
+
'chore',
|
|
86
|
+
'test',
|
|
87
|
+
'ci',
|
|
88
|
+
'build',
|
|
89
|
+
'style',
|
|
90
|
+
]
|
|
91
|
+
if (!type || !validTypes.includes(type)) return null
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
type,
|
|
95
|
+
scope,
|
|
96
|
+
message: msg || message,
|
|
97
|
+
breaking: !!breaking || message.toLowerCase().includes('breaking'),
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function ensureUpstreamRemote(): boolean {
|
|
102
|
+
try {
|
|
103
|
+
// check if remote exists
|
|
104
|
+
const remotes = execSync('git remote', { encoding: 'utf-8' })
|
|
105
|
+
if (!remotes.includes(UPSTREAM_REMOTE)) {
|
|
106
|
+
console.info(pc.dim(` adding ${UPSTREAM_REMOTE} remote...`))
|
|
107
|
+
execSync(`git remote add ${UPSTREAM_REMOTE} git@github.com:${UPSTREAM_REPO}.git`, {
|
|
108
|
+
stdio: 'pipe',
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// fetch latest
|
|
113
|
+
console.info(pc.dim(` fetching from ${UPSTREAM_REMOTE}...`))
|
|
114
|
+
execSync(`git fetch ${UPSTREAM_REMOTE} --quiet`, { stdio: 'pipe' })
|
|
115
|
+
return true
|
|
116
|
+
} catch (err) {
|
|
117
|
+
console.error(pc.red(`failed to setup upstream remote: ${err}`))
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getCommitsBetween(fromSha: string, toRef: string): CommitInfo[] {
|
|
123
|
+
const commits: CommitInfo[] = []
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
// format: hash|date|message
|
|
127
|
+
const result = execSync(
|
|
128
|
+
`git log ${fromSha}..${toRef} --pretty=format:"%H|%ad|%s" --date=short 2>/dev/null`,
|
|
129
|
+
{ encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 }
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
const lines = result.trim().split('\n').filter(Boolean)
|
|
133
|
+
|
|
134
|
+
for (const line of lines) {
|
|
135
|
+
const [hash = '', date = '', ...messageParts] = line.split('|')
|
|
136
|
+
const message = messageParts.join('|')
|
|
137
|
+
|
|
138
|
+
const parsed = parseConventionalCommit(message)
|
|
139
|
+
|
|
140
|
+
// include non-conventional commits too, just categorize them as "other"
|
|
141
|
+
commits.push({
|
|
142
|
+
hash: hash.slice(0, 7),
|
|
143
|
+
type: parsed?.type || 'other',
|
|
144
|
+
scope: parsed?.scope,
|
|
145
|
+
message: parsed?.message || message,
|
|
146
|
+
breaking: parsed?.breaking || false,
|
|
147
|
+
date,
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
// no commits or error
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return commits
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function formatChangelog(commits: CommitInfo[]): void {
|
|
158
|
+
if (commits.length === 0) {
|
|
159
|
+
console.info(pc.dim(' no changes found'))
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// group by type
|
|
164
|
+
const grouped = new Map<string, CommitInfo[]>()
|
|
165
|
+
for (const commit of commits) {
|
|
166
|
+
const existing = grouped.get(commit.type) || []
|
|
167
|
+
existing.push(commit)
|
|
168
|
+
grouped.set(commit.type, existing)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// sort by type order
|
|
172
|
+
const sortedTypes = Array.from(grouped.keys()).sort((a, b) => {
|
|
173
|
+
const aIdx = COMMIT_TYPE_ORDER.indexOf(a)
|
|
174
|
+
const bIdx = COMMIT_TYPE_ORDER.indexOf(b)
|
|
175
|
+
return (aIdx === -1 ? 999 : aIdx) - (bIdx === -1 ? 999 : bIdx)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
// show breaking changes first
|
|
179
|
+
const breakingChanges = commits.filter((c) => c.breaking)
|
|
180
|
+
if (breakingChanges.length > 0) {
|
|
181
|
+
console.info()
|
|
182
|
+
console.info(pc.red(pc.bold(' BREAKING CHANGES')))
|
|
183
|
+
for (const commit of breakingChanges) {
|
|
184
|
+
const scope = commit.scope ? pc.cyan(`(${commit.scope})`) : ''
|
|
185
|
+
console.info(
|
|
186
|
+
` ${pc.red('!')} ${scope} ${commit.message} ${pc.dim(`(${commit.hash})`)}`
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const typeLabels: Record<string, string> = {
|
|
192
|
+
feat: 'Features',
|
|
193
|
+
fix: 'Bug Fixes',
|
|
194
|
+
perf: 'Performance',
|
|
195
|
+
refactor: 'Refactoring',
|
|
196
|
+
docs: 'Documentation',
|
|
197
|
+
chore: 'Maintenance',
|
|
198
|
+
test: 'Tests',
|
|
199
|
+
ci: 'CI',
|
|
200
|
+
build: 'Build',
|
|
201
|
+
style: 'Style',
|
|
202
|
+
other: 'Other',
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const typeColors: Record<string, (s: string) => string> = {
|
|
206
|
+
feat: pc.green,
|
|
207
|
+
fix: pc.yellow,
|
|
208
|
+
perf: pc.magenta,
|
|
209
|
+
refactor: pc.blue,
|
|
210
|
+
docs: pc.dim,
|
|
211
|
+
chore: pc.dim,
|
|
212
|
+
test: pc.dim,
|
|
213
|
+
ci: pc.dim,
|
|
214
|
+
build: pc.dim,
|
|
215
|
+
style: pc.dim,
|
|
216
|
+
other: pc.white,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
for (const type of sortedTypes) {
|
|
220
|
+
const typeCommits = grouped.get(type)!.filter((c) => !c.breaking)
|
|
221
|
+
if (typeCommits.length === 0) continue
|
|
222
|
+
|
|
223
|
+
const label = typeLabels[type] || type
|
|
224
|
+
const color = typeColors[type] || pc.white
|
|
225
|
+
|
|
226
|
+
console.info()
|
|
227
|
+
console.info(color(pc.bold(` ${label}`)))
|
|
228
|
+
|
|
229
|
+
for (const commit of typeCommits) {
|
|
230
|
+
const scope = commit.scope ? pc.cyan(`(${commit.scope}) `) : ''
|
|
231
|
+
console.info(
|
|
232
|
+
` ${pc.dim('-')} ${scope}${commit.message} ${pc.dim(`(${commit.hash})`)}`
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export const changedCommand = defineCommand({
|
|
239
|
+
meta: {
|
|
240
|
+
name: 'changed',
|
|
241
|
+
description: 'Show changes in upstream Takeout since last sync',
|
|
242
|
+
},
|
|
243
|
+
args: {
|
|
244
|
+
from: {
|
|
245
|
+
type: 'string',
|
|
246
|
+
description: 'Starting commit SHA (defaults to .takeout file)',
|
|
247
|
+
},
|
|
248
|
+
to: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
description: 'Ending commit ref (defaults to upstream main)',
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
async run({ args }) {
|
|
254
|
+
console.info()
|
|
255
|
+
console.info(pc.bold(pc.cyan('Takeout Changes')))
|
|
256
|
+
console.info()
|
|
257
|
+
|
|
258
|
+
// determine from SHA
|
|
259
|
+
let fromSha = args.from
|
|
260
|
+
const config = readTakeoutConfig()
|
|
261
|
+
|
|
262
|
+
if (!fromSha) {
|
|
263
|
+
if (!config?.sha) {
|
|
264
|
+
console.info(pc.yellow('No .takeout file found with last sync SHA.'))
|
|
265
|
+
console.info()
|
|
266
|
+
console.info(pc.dim('Either:'))
|
|
267
|
+
console.info(pc.dim(' 1. Create a .takeout file with: sha=<commit-sha>'))
|
|
268
|
+
console.info(pc.dim(' 2. Run with --from <sha> to specify starting point'))
|
|
269
|
+
console.info(pc.dim(' 3. Run `tko sync` to sync and create the file'))
|
|
270
|
+
console.info()
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
fromSha = config.sha
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// setup upstream remote and fetch
|
|
277
|
+
if (!ensureUpstreamRemote()) {
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const toRef = args.to || `${UPSTREAM_REMOTE}/main`
|
|
282
|
+
|
|
283
|
+
console.info(pc.dim(` from: ${fromSha.slice(0, 7)}`))
|
|
284
|
+
console.info(pc.dim(` to: ${toRef}`))
|
|
285
|
+
|
|
286
|
+
if (config?.date) {
|
|
287
|
+
console.info(pc.dim(` last sync: ${config.date}`))
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// get commits
|
|
291
|
+
const commits = getCommitsBetween(fromSha, toRef)
|
|
292
|
+
|
|
293
|
+
if (commits.length === 0) {
|
|
294
|
+
console.info()
|
|
295
|
+
console.info(pc.green('✓ Already up to date with upstream!'))
|
|
296
|
+
console.info()
|
|
297
|
+
return
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.info()
|
|
301
|
+
console.info(
|
|
302
|
+
pc.bold(
|
|
303
|
+
`${commits.length} commit${commits.length === 1 ? '' : 's'} since last sync:`
|
|
304
|
+
)
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
formatChangelog(commits)
|
|
308
|
+
|
|
309
|
+
console.info()
|
|
310
|
+
console.info(pc.dim('Run `tko sync` to sync these changes into your fork.'))
|
|
311
|
+
console.info()
|
|
312
|
+
},
|
|
313
|
+
})
|