flowcat 1.4.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,122 @@
1
+ import path from "node:path";
2
+
3
+ import {
4
+ readConfigFile,
5
+ resolveGlobalConfigPath,
6
+ resolveWorkspaceConfigPath,
7
+ updateConfigFile,
8
+ } from "./config";
9
+ import { APP_DIR } from "./constants";
10
+ import { ensureWorkspaceIgnored } from "./gitignore";
11
+ import type { WorkspaceKind } from "./workspace";
12
+ import {
13
+ ensureWorkspaceLayout,
14
+ findGitRoot,
15
+ resolveGlobalWorkspaceRoot,
16
+ } from "./workspace";
17
+
18
+ type InitChoice = {
19
+ workspaceRoot: string;
20
+ kind: WorkspaceKind;
21
+ repoRoot?: string;
22
+ };
23
+
24
+ type InitFlowOptions = {
25
+ cwd: string;
26
+ store?: string;
27
+ repo?: boolean;
28
+ global?: boolean;
29
+ allowNonInteractiveDefault?: boolean;
30
+ };
31
+
32
+ export const resolveOptionalText = (value: unknown): string | null => {
33
+ if (value === null || typeof value === "undefined") {
34
+ return null;
35
+ }
36
+ const trimmed = String(value).trim();
37
+ return trimmed.length > 0 ? trimmed : null;
38
+ };
39
+
40
+ const selectWorkspace = async (
41
+ cwd: string,
42
+ force?: WorkspaceKind,
43
+ store?: string,
44
+ ): Promise<InitChoice> => {
45
+ if (store) {
46
+ return { workspaceRoot: path.resolve(store), kind: "explicit" };
47
+ }
48
+
49
+ const repoRoot = await findGitRoot(cwd);
50
+ if (force === "repo") {
51
+ if (!repoRoot) {
52
+ throw new Error("Not inside a git repository");
53
+ }
54
+ return { workspaceRoot: path.join(repoRoot, APP_DIR), kind: "repo", repoRoot };
55
+ }
56
+
57
+ if (force === "global") {
58
+ return { workspaceRoot: resolveGlobalWorkspaceRoot(), kind: "global" };
59
+ }
60
+
61
+ if (repoRoot) {
62
+ return {
63
+ workspaceRoot: path.join(repoRoot, APP_DIR),
64
+ kind: "repo",
65
+ repoRoot: repoRoot ?? undefined,
66
+ };
67
+ }
68
+
69
+ return { workspaceRoot: resolveGlobalWorkspaceRoot(), kind: "global" };
70
+ };
71
+
72
+ const resolveConfigPath = (choice: InitChoice): string => {
73
+ if (choice.kind === "global") {
74
+ return resolveGlobalConfigPath();
75
+ }
76
+
77
+ return resolveWorkspaceConfigPath(choice.workspaceRoot);
78
+ };
79
+
80
+ export const runInitFlow = async (options: InitFlowOptions): Promise<InitChoice> => {
81
+ const hasExplicitTarget = Boolean(options.store || options.repo || options.global);
82
+ const allowDefault = options.allowNonInteractiveDefault ?? false;
83
+
84
+ const forceKind = options.repo ? "repo" : options.global ? "global" : undefined;
85
+ let choice: InitChoice;
86
+
87
+ if (!hasExplicitTarget && allowDefault) {
88
+ const repoRoot = await findGitRoot(options.cwd);
89
+ if (repoRoot) {
90
+ choice = { workspaceRoot: path.join(repoRoot, APP_DIR), kind: "repo", repoRoot };
91
+ } else {
92
+ choice = { workspaceRoot: resolveGlobalWorkspaceRoot(), kind: "global" };
93
+ }
94
+ } else if (!hasExplicitTarget) {
95
+ throw new Error("Workspace not initialized. Run `flowcat init` or pass --repo/--global.");
96
+ } else {
97
+ choice = await selectWorkspace(options.cwd, forceKind, options.store);
98
+ }
99
+
100
+ const configPath = resolveConfigPath(choice);
101
+ const currentConfig = await readConfigFile(configPath);
102
+
103
+ const autoCommit = currentConfig.autoCommit ?? false;
104
+ const githubToken = currentConfig.github?.token ?? null;
105
+
106
+ await ensureWorkspaceLayout(choice.workspaceRoot);
107
+
108
+ await updateConfigFile(configPath, (current) => ({
109
+ ...current,
110
+ autoCommit,
111
+ github: {
112
+ ...current.github,
113
+ ...(githubToken ? { token: githubToken } : {}),
114
+ },
115
+ }));
116
+
117
+ if (choice.kind === "repo" && choice.repoRoot) {
118
+ await ensureWorkspaceIgnored(choice.repoRoot);
119
+ }
120
+
121
+ return choice;
122
+ };