workon 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1602 -1093
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,27 +9,313 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
// src/lib/config.ts
|
|
13
|
+
import Conf from "conf";
|
|
14
|
+
var TRANSIENT_PROPS, Config;
|
|
15
|
+
var init_config = __esm({
|
|
16
|
+
"src/lib/config.ts"() {
|
|
17
|
+
"use strict";
|
|
18
|
+
TRANSIENT_PROPS = ["pkg", "work"];
|
|
19
|
+
Config = class {
|
|
20
|
+
_transient = {};
|
|
21
|
+
_store;
|
|
22
|
+
constructor() {
|
|
23
|
+
this._store = new Conf({
|
|
24
|
+
projectName: "workon"
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
get(key, defaultValue) {
|
|
28
|
+
const rootKey = key.split(".")[0];
|
|
29
|
+
if (TRANSIENT_PROPS.includes(rootKey)) {
|
|
30
|
+
return this._transient[key] ?? defaultValue;
|
|
31
|
+
}
|
|
32
|
+
return this._store.get(key, defaultValue);
|
|
33
|
+
}
|
|
34
|
+
set(key, value) {
|
|
35
|
+
const rootKey = key.split(".")[0];
|
|
36
|
+
if (TRANSIENT_PROPS.includes(rootKey)) {
|
|
37
|
+
this._transient[key] = value;
|
|
38
|
+
} else {
|
|
39
|
+
if (value === void 0) {
|
|
40
|
+
this._store.set(key, value);
|
|
41
|
+
} else {
|
|
42
|
+
this._store.set(key, value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
has(key) {
|
|
47
|
+
const rootKey = key.split(".")[0];
|
|
48
|
+
if (TRANSIENT_PROPS.includes(rootKey)) {
|
|
49
|
+
return Object.prototype.hasOwnProperty.call(this._transient, key);
|
|
50
|
+
}
|
|
51
|
+
return this._store.has(key);
|
|
52
|
+
}
|
|
53
|
+
delete(key) {
|
|
54
|
+
const rootKey = key.split(".")[0];
|
|
55
|
+
if (TRANSIENT_PROPS.includes(rootKey)) {
|
|
56
|
+
delete this._transient[key];
|
|
57
|
+
} else {
|
|
58
|
+
this._store.delete(key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
getProjects() {
|
|
62
|
+
return this.get("projects") ?? {};
|
|
63
|
+
}
|
|
64
|
+
getProject(name) {
|
|
65
|
+
const projects = this.getProjects();
|
|
66
|
+
return projects[name];
|
|
67
|
+
}
|
|
68
|
+
setProject(name, config) {
|
|
69
|
+
const projects = this.getProjects();
|
|
70
|
+
projects[name] = config;
|
|
71
|
+
this.set("projects", projects);
|
|
72
|
+
}
|
|
73
|
+
deleteProject(name) {
|
|
74
|
+
const projects = this.getProjects();
|
|
75
|
+
delete projects[name];
|
|
76
|
+
this.set("projects", projects);
|
|
77
|
+
}
|
|
78
|
+
getDefaults() {
|
|
79
|
+
return this.get("project_defaults");
|
|
80
|
+
}
|
|
81
|
+
setDefaults(defaults) {
|
|
82
|
+
this.set("project_defaults", defaults);
|
|
83
|
+
}
|
|
84
|
+
get path() {
|
|
85
|
+
return this._store.path;
|
|
86
|
+
}
|
|
87
|
+
get store() {
|
|
88
|
+
return this._store.store;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
17
92
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
93
|
+
|
|
94
|
+
// src/lib/project.ts
|
|
95
|
+
import File from "phylo";
|
|
96
|
+
import deepAssign from "deep-assign";
|
|
97
|
+
var Project;
|
|
98
|
+
var init_project = __esm({
|
|
99
|
+
"src/lib/project.ts"() {
|
|
23
100
|
"use strict";
|
|
24
|
-
|
|
101
|
+
Project = class {
|
|
102
|
+
name;
|
|
103
|
+
_base;
|
|
104
|
+
_path;
|
|
105
|
+
_ide;
|
|
106
|
+
_events = {};
|
|
107
|
+
_branch;
|
|
108
|
+
_homepage;
|
|
109
|
+
_defaults;
|
|
110
|
+
_initialCfg;
|
|
111
|
+
constructor(name, cfg, defaults) {
|
|
112
|
+
this._defaults = defaults ?? { base: "" };
|
|
113
|
+
this._initialCfg = { path: name, events: {}, ...cfg };
|
|
114
|
+
this.name = cfg?.name ?? name;
|
|
115
|
+
const merged = deepAssign({}, this._defaults, this._initialCfg);
|
|
116
|
+
if (merged.base) {
|
|
117
|
+
this.base = merged.base;
|
|
118
|
+
}
|
|
119
|
+
if (merged.path) {
|
|
120
|
+
this.path = merged.path;
|
|
121
|
+
}
|
|
122
|
+
if (merged.ide) {
|
|
123
|
+
this._ide = merged.ide;
|
|
124
|
+
}
|
|
125
|
+
if (merged.events) {
|
|
126
|
+
this._events = merged.events;
|
|
127
|
+
}
|
|
128
|
+
if (merged.branch) {
|
|
129
|
+
this._branch = merged.branch;
|
|
130
|
+
}
|
|
131
|
+
if (merged.homepage) {
|
|
132
|
+
this._homepage = merged.homepage;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
set base(path) {
|
|
136
|
+
this._base = File.from(path).absolutify();
|
|
137
|
+
}
|
|
138
|
+
get base() {
|
|
139
|
+
return this._base;
|
|
140
|
+
}
|
|
141
|
+
set ide(cmd) {
|
|
142
|
+
this._ide = cmd;
|
|
143
|
+
}
|
|
144
|
+
get ide() {
|
|
145
|
+
return this._ide;
|
|
146
|
+
}
|
|
147
|
+
set events(eventCfg) {
|
|
148
|
+
this._events = eventCfg;
|
|
149
|
+
}
|
|
150
|
+
get events() {
|
|
151
|
+
return this._events;
|
|
152
|
+
}
|
|
153
|
+
set path(path) {
|
|
154
|
+
if (this._base) {
|
|
155
|
+
this._path = this._base.join(path);
|
|
156
|
+
} else {
|
|
157
|
+
this._path = File.from(path);
|
|
158
|
+
}
|
|
159
|
+
this._path = this._path.absolutify();
|
|
160
|
+
}
|
|
161
|
+
get path() {
|
|
162
|
+
if (!this._path) {
|
|
163
|
+
throw new Error("Project path not set");
|
|
164
|
+
}
|
|
165
|
+
return this._path;
|
|
166
|
+
}
|
|
167
|
+
set branch(branch) {
|
|
168
|
+
this._branch = branch;
|
|
169
|
+
}
|
|
170
|
+
get branch() {
|
|
171
|
+
return this._branch;
|
|
172
|
+
}
|
|
173
|
+
set homepage(url) {
|
|
174
|
+
this._homepage = url;
|
|
175
|
+
}
|
|
176
|
+
get homepage() {
|
|
177
|
+
return this._homepage;
|
|
178
|
+
}
|
|
179
|
+
static $isProject = true;
|
|
180
|
+
$isProject = true;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// src/lib/environment.ts
|
|
186
|
+
import File2 from "phylo";
|
|
187
|
+
import { simpleGit } from "simple-git";
|
|
188
|
+
var BaseEnvironment, ProjectEnvironment, EnvironmentRecognizer;
|
|
189
|
+
var init_environment = __esm({
|
|
190
|
+
"src/lib/environment.ts"() {
|
|
191
|
+
"use strict";
|
|
192
|
+
init_config();
|
|
193
|
+
init_project();
|
|
194
|
+
BaseEnvironment = class {
|
|
195
|
+
$isProjectEnvironment = false;
|
|
196
|
+
};
|
|
197
|
+
ProjectEnvironment = class _ProjectEnvironment {
|
|
198
|
+
$isProjectEnvironment = true;
|
|
199
|
+
project;
|
|
200
|
+
constructor(projectCfg) {
|
|
201
|
+
this.project = new Project(projectCfg.name, projectCfg);
|
|
202
|
+
}
|
|
203
|
+
static load(cfg, defaults) {
|
|
204
|
+
const project = new Project(cfg.name, cfg, defaults);
|
|
205
|
+
return new _ProjectEnvironment({ ...cfg, name: project.name });
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
EnvironmentRecognizer = class {
|
|
209
|
+
static config;
|
|
210
|
+
static log;
|
|
211
|
+
static projects = [];
|
|
212
|
+
static configured = false;
|
|
213
|
+
static configure(config, log) {
|
|
214
|
+
if (this.configured) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
this.config = config;
|
|
218
|
+
this.log = log;
|
|
219
|
+
this.configured = true;
|
|
220
|
+
}
|
|
221
|
+
static async recognize(dir) {
|
|
222
|
+
this.ensureConfigured();
|
|
223
|
+
const theDir = File2.from(dir).canonicalize();
|
|
224
|
+
this.log.debug("Directory to recognize is: " + theDir.canonicalPath());
|
|
225
|
+
const allProjects = this.getAllProjects();
|
|
226
|
+
const matching = allProjects.filter((p) => p.path.canonicalPath() === theDir.path);
|
|
227
|
+
if (matching.length === 0) {
|
|
228
|
+
return new BaseEnvironment();
|
|
229
|
+
}
|
|
230
|
+
this.log.debug(`Found ${matching.length} matching projects`);
|
|
231
|
+
const base = matching.find((p) => !p.name.includes("#")) ?? matching[0];
|
|
232
|
+
this.log.debug("Base project is: " + base.name);
|
|
233
|
+
const gitDir = base.path.up(".git");
|
|
234
|
+
if (gitDir) {
|
|
235
|
+
try {
|
|
236
|
+
const git = simpleGit(gitDir.path);
|
|
237
|
+
const branchSummary = await git.branchLocal();
|
|
238
|
+
base.branch = branchSummary.current;
|
|
239
|
+
} catch {
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return this.getProjectEnvironment(base, matching);
|
|
243
|
+
}
|
|
244
|
+
static getAllProjects(refresh = false) {
|
|
245
|
+
if (this.projects.length > 0 && !refresh) {
|
|
246
|
+
return this.projects;
|
|
247
|
+
}
|
|
248
|
+
const defaults = this.config.getDefaults();
|
|
249
|
+
if (!defaults?.base) {
|
|
250
|
+
this.projects = [];
|
|
251
|
+
return this.projects;
|
|
252
|
+
}
|
|
253
|
+
const baseDir = File2.from(defaults.base);
|
|
254
|
+
const projectsMap = this.config.getProjects();
|
|
255
|
+
this.projects = Object.entries(projectsMap).map(([name, project]) => ({
|
|
256
|
+
...project,
|
|
257
|
+
name,
|
|
258
|
+
path: baseDir.join(project.path)
|
|
259
|
+
}));
|
|
260
|
+
return this.projects;
|
|
261
|
+
}
|
|
262
|
+
static getProjectEnvironment(base, _matching) {
|
|
263
|
+
const exactName = `${base.name}#${base.branch}`;
|
|
264
|
+
const exactProj = this.projects.find((p) => p.name === exactName);
|
|
265
|
+
const toProjectConfig = (p) => ({
|
|
266
|
+
name: p.name,
|
|
267
|
+
path: p.path.path,
|
|
268
|
+
// Convert PhyloFile to string path
|
|
269
|
+
ide: p.ide,
|
|
270
|
+
homepage: p.homepage,
|
|
271
|
+
events: p.events,
|
|
272
|
+
branch: p.branch,
|
|
273
|
+
exactName
|
|
274
|
+
});
|
|
275
|
+
if (exactProj) {
|
|
276
|
+
return new ProjectEnvironment({ ...toProjectConfig(exactProj), branch: base.branch });
|
|
277
|
+
}
|
|
278
|
+
return new ProjectEnvironment(toProjectConfig(base));
|
|
279
|
+
}
|
|
280
|
+
static ensureConfigured() {
|
|
281
|
+
if (!this.configured) {
|
|
282
|
+
this.config = new Config();
|
|
283
|
+
this.log = {
|
|
284
|
+
debug: () => {
|
|
285
|
+
},
|
|
286
|
+
info: () => {
|
|
287
|
+
},
|
|
288
|
+
log: () => {
|
|
289
|
+
},
|
|
290
|
+
warn: () => {
|
|
291
|
+
},
|
|
292
|
+
error: () => {
|
|
293
|
+
},
|
|
294
|
+
setLogLevel: () => {
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
this.configured = true;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// src/events/core/cwd.ts
|
|
305
|
+
import { spawn as spawn2 } from "child_process";
|
|
306
|
+
var CwdEvent;
|
|
307
|
+
var init_cwd = __esm({
|
|
308
|
+
"src/events/core/cwd.ts"() {
|
|
309
|
+
"use strict";
|
|
310
|
+
CwdEvent = class {
|
|
25
311
|
static get metadata() {
|
|
26
312
|
return {
|
|
27
|
-
name: "
|
|
28
|
-
displayName: "
|
|
29
|
-
description: "
|
|
30
|
-
category: "
|
|
31
|
-
requiresTmux:
|
|
32
|
-
dependencies: [
|
|
313
|
+
name: "cwd",
|
|
314
|
+
displayName: "Change directory (cwd)",
|
|
315
|
+
description: "Change current working directory to project path",
|
|
316
|
+
category: "core",
|
|
317
|
+
requiresTmux: false,
|
|
318
|
+
dependencies: []
|
|
33
319
|
};
|
|
34
320
|
}
|
|
35
321
|
static get validation() {
|
|
@@ -38,157 +324,813 @@ var init_npm = __esm({
|
|
|
38
324
|
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
39
325
|
return true;
|
|
40
326
|
}
|
|
41
|
-
|
|
42
|
-
if (config.trim().length === 0) {
|
|
43
|
-
return "npm script name cannot be empty";
|
|
44
|
-
}
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
if (typeof config === "object" && config !== null) {
|
|
48
|
-
const cfg = config;
|
|
49
|
-
if (typeof cfg.command !== "string" || cfg.command.trim().length === 0) {
|
|
50
|
-
return "npm.command must be a non-empty string";
|
|
51
|
-
}
|
|
52
|
-
if (cfg.watch !== void 0 && typeof cfg.watch !== "boolean") {
|
|
53
|
-
return "npm.watch must be a boolean";
|
|
54
|
-
}
|
|
55
|
-
if (cfg.auto_restart !== void 0 && typeof cfg.auto_restart !== "boolean") {
|
|
56
|
-
return "npm.auto_restart must be a boolean";
|
|
57
|
-
}
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
return "npm config must be a boolean, string (script name), or object";
|
|
327
|
+
return "cwd config must be a boolean (true/false)";
|
|
61
328
|
}
|
|
62
329
|
};
|
|
63
330
|
}
|
|
64
331
|
static get configuration() {
|
|
65
332
|
return {
|
|
66
333
|
async configureInteractive() {
|
|
67
|
-
|
|
68
|
-
message: "Enter NPM script to run:",
|
|
69
|
-
default: "dev"
|
|
70
|
-
});
|
|
71
|
-
const useAdvanced = await confirm2({
|
|
72
|
-
message: "Configure advanced NPM options?",
|
|
73
|
-
default: false
|
|
74
|
-
});
|
|
75
|
-
if (!useAdvanced) {
|
|
76
|
-
return scriptName;
|
|
77
|
-
}
|
|
78
|
-
const watch = await confirm2({
|
|
79
|
-
message: "Enable watch mode?",
|
|
80
|
-
default: false
|
|
81
|
-
});
|
|
82
|
-
const autoRestart = await confirm2({
|
|
83
|
-
message: "Auto-restart on crash?",
|
|
84
|
-
default: false
|
|
85
|
-
});
|
|
86
|
-
if (!watch && !autoRestart) {
|
|
87
|
-
return scriptName;
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
command: scriptName,
|
|
91
|
-
watch,
|
|
92
|
-
auto_restart: autoRestart
|
|
93
|
-
};
|
|
334
|
+
return true;
|
|
94
335
|
},
|
|
95
336
|
getDefaultConfig() {
|
|
96
|
-
return
|
|
337
|
+
return true;
|
|
97
338
|
}
|
|
98
339
|
};
|
|
99
340
|
}
|
|
100
|
-
static getNpmCommand(config) {
|
|
101
|
-
if (typeof config === "boolean" || config === void 0) {
|
|
102
|
-
return "npm run dev";
|
|
103
|
-
}
|
|
104
|
-
if (typeof config === "string") {
|
|
105
|
-
return `npm run ${config}`;
|
|
106
|
-
}
|
|
107
|
-
return `npm run ${config.command}`;
|
|
108
|
-
}
|
|
109
341
|
static get processing() {
|
|
110
342
|
return {
|
|
111
343
|
async processEvent(context) {
|
|
112
344
|
const { project, isShellMode, shellCommands } = context;
|
|
113
|
-
const
|
|
114
|
-
const npmCommand = _NpmEvent.getNpmCommand(npmConfig);
|
|
345
|
+
const projectPath = project.path.path;
|
|
115
346
|
if (isShellMode) {
|
|
116
|
-
shellCommands.push(
|
|
347
|
+
shellCommands.push(`cd "${projectPath}"`);
|
|
117
348
|
} else {
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
cwd:
|
|
349
|
+
const shell = process.env.SHELL || "/bin/bash";
|
|
350
|
+
spawn2(shell, [], {
|
|
351
|
+
cwd: projectPath,
|
|
121
352
|
stdio: "inherit"
|
|
122
353
|
});
|
|
123
354
|
}
|
|
124
355
|
},
|
|
125
356
|
generateShellCommand(context) {
|
|
126
|
-
const
|
|
127
|
-
return [
|
|
357
|
+
const projectPath = context.project.path.path;
|
|
358
|
+
return [`cd "${projectPath}"`];
|
|
128
359
|
}
|
|
129
360
|
};
|
|
130
361
|
}
|
|
131
362
|
static get tmux() {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
static get help() {
|
|
132
366
|
return {
|
|
133
|
-
|
|
134
|
-
|
|
367
|
+
usage: "cwd: true | false",
|
|
368
|
+
description: "Change the current working directory to the project path",
|
|
369
|
+
examples: [
|
|
370
|
+
{ config: true, description: "Enable directory change" },
|
|
371
|
+
{ config: false, description: "Disable directory change" }
|
|
372
|
+
]
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// src/events/core/ide.ts
|
|
380
|
+
import { spawn as spawn3 } from "child_process";
|
|
381
|
+
var IdeEvent;
|
|
382
|
+
var init_ide = __esm({
|
|
383
|
+
"src/events/core/ide.ts"() {
|
|
384
|
+
"use strict";
|
|
385
|
+
IdeEvent = class {
|
|
386
|
+
static get metadata() {
|
|
387
|
+
return {
|
|
388
|
+
name: "ide",
|
|
389
|
+
displayName: "Open in IDE",
|
|
390
|
+
description: "Open project in configured IDE/editor",
|
|
391
|
+
category: "core",
|
|
392
|
+
requiresTmux: false,
|
|
393
|
+
dependencies: []
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
static get validation() {
|
|
397
|
+
return {
|
|
398
|
+
validateConfig(config) {
|
|
399
|
+
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
return "ide config must be a boolean (true/false)";
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
static get configuration() {
|
|
407
|
+
return {
|
|
408
|
+
async configureInteractive() {
|
|
409
|
+
return true;
|
|
135
410
|
},
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
411
|
+
getDefaultConfig() {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
static get processing() {
|
|
417
|
+
return {
|
|
418
|
+
async processEvent(context) {
|
|
419
|
+
const { project, isShellMode, shellCommands } = context;
|
|
420
|
+
const projectPath = project.path.path;
|
|
421
|
+
const ide = project.ide || "code";
|
|
422
|
+
if (isShellMode) {
|
|
423
|
+
shellCommands.push(`${ide} "${projectPath}" &`);
|
|
424
|
+
} else {
|
|
425
|
+
spawn3(ide, [projectPath], {
|
|
426
|
+
detached: true,
|
|
427
|
+
stdio: "ignore"
|
|
428
|
+
}).unref();
|
|
139
429
|
}
|
|
140
|
-
|
|
430
|
+
},
|
|
431
|
+
generateShellCommand(context) {
|
|
432
|
+
const projectPath = context.project.path.path;
|
|
433
|
+
const ide = context.project.ide || "code";
|
|
434
|
+
return [`${ide} "${projectPath}" &`];
|
|
141
435
|
}
|
|
142
436
|
};
|
|
143
437
|
}
|
|
438
|
+
static get tmux() {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
144
441
|
static get help() {
|
|
145
442
|
return {
|
|
146
|
-
usage:
|
|
147
|
-
description: "
|
|
443
|
+
usage: "ide: true | false",
|
|
444
|
+
description: "Open the project in the configured IDE",
|
|
148
445
|
examples: [
|
|
149
|
-
{ config: true, description: "
|
|
150
|
-
{ config:
|
|
151
|
-
{ config: { command: "dev", watch: true }, description: "Run dev with watch mode" }
|
|
446
|
+
{ config: true, description: "Enable IDE opening" },
|
|
447
|
+
{ config: false, description: "Disable IDE opening" }
|
|
152
448
|
]
|
|
153
449
|
};
|
|
154
450
|
}
|
|
155
451
|
};
|
|
156
|
-
npm_default = NpmEvent;
|
|
157
452
|
}
|
|
158
453
|
});
|
|
159
454
|
|
|
160
|
-
// src/
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
455
|
+
// src/events/core/web.ts
|
|
456
|
+
import { spawn as spawn4 } from "child_process";
|
|
457
|
+
import { platform } from "os";
|
|
458
|
+
var WebEvent;
|
|
459
|
+
var init_web = __esm({
|
|
460
|
+
"src/events/core/web.ts"() {
|
|
461
|
+
"use strict";
|
|
462
|
+
WebEvent = class _WebEvent {
|
|
463
|
+
static get metadata() {
|
|
464
|
+
return {
|
|
465
|
+
name: "web",
|
|
466
|
+
displayName: "Open homepage in browser",
|
|
467
|
+
description: "Open project homepage in web browser",
|
|
468
|
+
category: "core",
|
|
469
|
+
requiresTmux: false,
|
|
470
|
+
dependencies: []
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
static get validation() {
|
|
474
|
+
return {
|
|
475
|
+
validateConfig(config) {
|
|
476
|
+
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
return "web config must be a boolean (true/false)";
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
static get configuration() {
|
|
484
|
+
return {
|
|
485
|
+
async configureInteractive() {
|
|
486
|
+
return true;
|
|
487
|
+
},
|
|
488
|
+
getDefaultConfig() {
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
static getOpenCommand() {
|
|
494
|
+
const os = platform();
|
|
495
|
+
switch (os) {
|
|
496
|
+
case "darwin":
|
|
497
|
+
return "open";
|
|
498
|
+
case "win32":
|
|
499
|
+
return "start";
|
|
500
|
+
default:
|
|
501
|
+
return "xdg-open";
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
static get processing() {
|
|
505
|
+
return {
|
|
506
|
+
async processEvent(context) {
|
|
507
|
+
const { project, isShellMode, shellCommands } = context;
|
|
508
|
+
const homepage = project.homepage;
|
|
509
|
+
if (!homepage) {
|
|
510
|
+
console.warn("No homepage configured for project");
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const openCmd = _WebEvent.getOpenCommand();
|
|
514
|
+
if (isShellMode) {
|
|
515
|
+
shellCommands.push(`${openCmd} "${homepage}" &`);
|
|
516
|
+
} else {
|
|
517
|
+
spawn4(openCmd, [homepage], {
|
|
518
|
+
detached: true,
|
|
519
|
+
stdio: "ignore"
|
|
520
|
+
}).unref();
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
generateShellCommand(context) {
|
|
524
|
+
const homepage = context.project.homepage;
|
|
525
|
+
if (!homepage) return [];
|
|
526
|
+
const openCmd = _WebEvent.getOpenCommand();
|
|
527
|
+
return [`${openCmd} "${homepage}" &`];
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
static get tmux() {
|
|
532
|
+
return null;
|
|
533
|
+
}
|
|
534
|
+
static get help() {
|
|
535
|
+
return {
|
|
536
|
+
usage: "web: true | false",
|
|
537
|
+
description: "Open the project homepage in the default browser",
|
|
538
|
+
examples: [
|
|
539
|
+
{ config: true, description: "Enable browser opening" },
|
|
540
|
+
{ config: false, description: "Disable browser opening" }
|
|
541
|
+
]
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
// src/events/extensions/claude.ts
|
|
549
|
+
import { spawn as spawn5 } from "child_process";
|
|
550
|
+
import { input, confirm } from "@inquirer/prompts";
|
|
551
|
+
var ClaudeEvent;
|
|
552
|
+
var init_claude = __esm({
|
|
553
|
+
"src/events/extensions/claude.ts"() {
|
|
554
|
+
"use strict";
|
|
555
|
+
ClaudeEvent = class _ClaudeEvent {
|
|
556
|
+
static get metadata() {
|
|
557
|
+
return {
|
|
558
|
+
name: "claude",
|
|
559
|
+
displayName: "Launch Claude Code",
|
|
560
|
+
description: "Launch Claude Code with optional flags and configuration",
|
|
561
|
+
category: "development",
|
|
562
|
+
requiresTmux: true,
|
|
563
|
+
dependencies: ["claude"]
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
static get validation() {
|
|
567
|
+
return {
|
|
568
|
+
validateConfig(config) {
|
|
569
|
+
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
if (typeof config === "object" && config !== null) {
|
|
573
|
+
const cfg = config;
|
|
574
|
+
if (cfg.flags !== void 0) {
|
|
575
|
+
if (!Array.isArray(cfg.flags)) {
|
|
576
|
+
return "claude.flags must be an array of strings";
|
|
577
|
+
}
|
|
578
|
+
for (const flag of cfg.flags) {
|
|
579
|
+
if (typeof flag !== "string") {
|
|
580
|
+
return "claude.flags must contain only strings";
|
|
581
|
+
}
|
|
582
|
+
if (!flag.startsWith("-")) {
|
|
583
|
+
return `Invalid flag "${flag}": flags must start with - or --`;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
if (cfg.split_terminal !== void 0 && typeof cfg.split_terminal !== "boolean") {
|
|
588
|
+
return "claude.split_terminal must be a boolean";
|
|
589
|
+
}
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
return "claude config must be a boolean or object with flags/split_terminal";
|
|
593
|
+
}
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
static get configuration() {
|
|
597
|
+
return {
|
|
598
|
+
async configureInteractive() {
|
|
599
|
+
const useAdvanced = await confirm({
|
|
600
|
+
message: "Configure advanced Claude options?",
|
|
601
|
+
default: false
|
|
602
|
+
});
|
|
603
|
+
if (!useAdvanced) {
|
|
604
|
+
return true;
|
|
605
|
+
}
|
|
606
|
+
const flagsInput = await input({
|
|
607
|
+
message: "Enter Claude flags (comma-separated, e.g., --resume, --debug):",
|
|
608
|
+
default: ""
|
|
609
|
+
});
|
|
610
|
+
const flags = flagsInput.split(",").map((f) => f.trim()).filter((f) => f.length > 0 && f.startsWith("-"));
|
|
611
|
+
const splitTerminal = await confirm({
|
|
612
|
+
message: "Use split terminal layout (Claude + shell)?",
|
|
613
|
+
default: true
|
|
614
|
+
});
|
|
615
|
+
if (flags.length === 0 && !splitTerminal) {
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
const config = {};
|
|
619
|
+
if (flags.length > 0) config.flags = flags;
|
|
620
|
+
if (splitTerminal) config.split_terminal = splitTerminal;
|
|
621
|
+
return config;
|
|
622
|
+
},
|
|
623
|
+
getDefaultConfig() {
|
|
624
|
+
return true;
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
static getClaudeCommand(config) {
|
|
629
|
+
if (typeof config === "boolean" || config === void 0) {
|
|
630
|
+
return "claude";
|
|
631
|
+
}
|
|
632
|
+
const flags = config.flags || [];
|
|
633
|
+
return flags.length > 0 ? `claude ${flags.join(" ")}` : "claude";
|
|
634
|
+
}
|
|
635
|
+
static get processing() {
|
|
636
|
+
return {
|
|
637
|
+
async processEvent(context) {
|
|
638
|
+
const { project, isShellMode, shellCommands } = context;
|
|
639
|
+
const claudeConfig = project.events.claude;
|
|
640
|
+
const claudeCommand = _ClaudeEvent.getClaudeCommand(claudeConfig);
|
|
641
|
+
if (isShellMode) {
|
|
642
|
+
shellCommands.push(claudeCommand);
|
|
643
|
+
} else {
|
|
644
|
+
const args = claudeCommand.split(" ").slice(1);
|
|
645
|
+
spawn5("claude", args, {
|
|
646
|
+
cwd: project.path.path,
|
|
647
|
+
stdio: "inherit"
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
},
|
|
651
|
+
generateShellCommand(context) {
|
|
652
|
+
const claudeConfig = context.project.events.claude;
|
|
653
|
+
return [_ClaudeEvent.getClaudeCommand(claudeConfig)];
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
static get tmux() {
|
|
658
|
+
return {
|
|
659
|
+
getLayoutPriority() {
|
|
660
|
+
return 100;
|
|
661
|
+
},
|
|
662
|
+
contributeToLayout(enabledCommands) {
|
|
663
|
+
if (enabledCommands.includes("npm")) {
|
|
664
|
+
return "three-pane";
|
|
665
|
+
}
|
|
666
|
+
return "split";
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
static get help() {
|
|
671
|
+
return {
|
|
672
|
+
usage: "claude: true | { flags: string[], split_terminal: boolean }",
|
|
673
|
+
description: "Launch Claude Code in the project directory",
|
|
674
|
+
examples: [
|
|
675
|
+
{ config: true, description: "Launch Claude with defaults" },
|
|
676
|
+
{ config: { flags: ["--resume"] }, description: "Resume previous session" },
|
|
677
|
+
{
|
|
678
|
+
config: { flags: ["--model", "opus"], split_terminal: true },
|
|
679
|
+
description: "Use Opus model with split terminal"
|
|
680
|
+
}
|
|
681
|
+
]
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// src/events/extensions/docker.ts
|
|
689
|
+
import { spawn as spawn6 } from "child_process";
|
|
690
|
+
import { input as input2 } from "@inquirer/prompts";
|
|
691
|
+
var DockerEvent;
|
|
692
|
+
var init_docker = __esm({
|
|
693
|
+
"src/events/extensions/docker.ts"() {
|
|
694
|
+
"use strict";
|
|
695
|
+
DockerEvent = class _DockerEvent {
|
|
696
|
+
static get metadata() {
|
|
697
|
+
return {
|
|
698
|
+
name: "docker",
|
|
699
|
+
displayName: "Docker container management",
|
|
700
|
+
description: "Start/stop Docker containers for the project",
|
|
701
|
+
category: "development",
|
|
702
|
+
requiresTmux: false,
|
|
703
|
+
dependencies: ["docker"]
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
static get validation() {
|
|
707
|
+
return {
|
|
708
|
+
validateConfig(config) {
|
|
709
|
+
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
712
|
+
if (typeof config === "string") {
|
|
713
|
+
return true;
|
|
714
|
+
}
|
|
715
|
+
if (typeof config === "object" && config !== null) {
|
|
716
|
+
const cfg = config;
|
|
717
|
+
if (cfg.compose_file !== void 0 && typeof cfg.compose_file !== "string") {
|
|
718
|
+
return "docker.compose_file must be a string";
|
|
719
|
+
}
|
|
720
|
+
if (cfg.services !== void 0) {
|
|
721
|
+
if (!Array.isArray(cfg.services)) {
|
|
722
|
+
return "docker.services must be an array";
|
|
723
|
+
}
|
|
724
|
+
for (const service of cfg.services) {
|
|
725
|
+
if (typeof service !== "string") {
|
|
726
|
+
return "docker.services must contain only strings";
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return true;
|
|
731
|
+
}
|
|
732
|
+
return "docker config must be a boolean, string (compose file), or object";
|
|
733
|
+
}
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
static get configuration() {
|
|
737
|
+
return {
|
|
738
|
+
async configureInteractive() {
|
|
739
|
+
const composeFile = await input2({
|
|
740
|
+
message: "Enter docker-compose file path:",
|
|
741
|
+
default: "docker-compose.yml"
|
|
742
|
+
});
|
|
743
|
+
const servicesInput = await input2({
|
|
744
|
+
message: "Enter services to start (comma-separated, leave empty for all):",
|
|
745
|
+
default: ""
|
|
746
|
+
});
|
|
747
|
+
const services = servicesInput.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
748
|
+
if (composeFile === "docker-compose.yml" && services.length === 0) {
|
|
749
|
+
return { compose_file: "docker-compose.yml" };
|
|
750
|
+
}
|
|
751
|
+
if (services.length === 0) {
|
|
752
|
+
return composeFile;
|
|
753
|
+
}
|
|
754
|
+
return {
|
|
755
|
+
compose_file: composeFile,
|
|
756
|
+
services
|
|
757
|
+
};
|
|
758
|
+
},
|
|
759
|
+
getDefaultConfig() {
|
|
760
|
+
return { compose_file: "docker-compose.yml" };
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
}
|
|
764
|
+
static getDockerCommand(config) {
|
|
765
|
+
if (typeof config === "boolean" || config === void 0) {
|
|
766
|
+
return "docker-compose up -d";
|
|
767
|
+
}
|
|
768
|
+
if (typeof config === "string") {
|
|
769
|
+
return `docker-compose -f ${config} up -d`;
|
|
770
|
+
}
|
|
771
|
+
const composeFile = config.compose_file || "docker-compose.yml";
|
|
772
|
+
const services = config.services?.join(" ") || "";
|
|
773
|
+
return `docker-compose -f ${composeFile} up -d ${services}`.trim();
|
|
774
|
+
}
|
|
775
|
+
static get processing() {
|
|
776
|
+
return {
|
|
777
|
+
async processEvent(context) {
|
|
778
|
+
const { project, isShellMode, shellCommands } = context;
|
|
779
|
+
const dockerConfig = project.events.docker;
|
|
780
|
+
const dockerCommand = _DockerEvent.getDockerCommand(
|
|
781
|
+
dockerConfig
|
|
782
|
+
);
|
|
783
|
+
if (isShellMode) {
|
|
784
|
+
shellCommands.push(dockerCommand);
|
|
785
|
+
} else {
|
|
786
|
+
const [cmd, ...args] = dockerCommand.split(" ");
|
|
787
|
+
spawn6(cmd, args, {
|
|
788
|
+
cwd: project.path.path,
|
|
789
|
+
stdio: "inherit"
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
},
|
|
793
|
+
generateShellCommand(context) {
|
|
794
|
+
const dockerConfig = context.project.events.docker;
|
|
795
|
+
return [_DockerEvent.getDockerCommand(dockerConfig)];
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
static get tmux() {
|
|
800
|
+
return null;
|
|
801
|
+
}
|
|
802
|
+
static get help() {
|
|
803
|
+
return {
|
|
804
|
+
usage: 'docker: true | "compose-file.yml" | { compose_file: string, services?: string[] }',
|
|
805
|
+
description: "Start Docker containers for the project",
|
|
806
|
+
examples: [
|
|
807
|
+
{ config: true, description: "Use default docker-compose.yml" },
|
|
808
|
+
{ config: "docker-compose.dev.yml", description: "Use custom compose file" },
|
|
809
|
+
{
|
|
810
|
+
config: { compose_file: "docker-compose.yml", services: ["web", "db"] },
|
|
811
|
+
description: "Start specific services"
|
|
812
|
+
}
|
|
813
|
+
]
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// src/events/extensions/npm.ts
|
|
821
|
+
var npm_exports = {};
|
|
822
|
+
__export(npm_exports, {
|
|
823
|
+
NpmEvent: () => NpmEvent,
|
|
824
|
+
default: () => npm_default
|
|
825
|
+
});
|
|
826
|
+
import { spawn as spawn7 } from "child_process";
|
|
827
|
+
import { input as input3, confirm as confirm2 } from "@inquirer/prompts";
|
|
828
|
+
var NpmEvent, npm_default;
|
|
829
|
+
var init_npm = __esm({
|
|
830
|
+
"src/events/extensions/npm.ts"() {
|
|
831
|
+
"use strict";
|
|
832
|
+
NpmEvent = class _NpmEvent {
|
|
833
|
+
static get metadata() {
|
|
834
|
+
return {
|
|
835
|
+
name: "npm",
|
|
836
|
+
displayName: "Run NPM command",
|
|
837
|
+
description: "Execute NPM scripts in project directory",
|
|
838
|
+
category: "development",
|
|
839
|
+
requiresTmux: true,
|
|
840
|
+
dependencies: ["npm"]
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
static get validation() {
|
|
844
|
+
return {
|
|
845
|
+
validateConfig(config) {
|
|
846
|
+
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
847
|
+
return true;
|
|
848
|
+
}
|
|
849
|
+
if (typeof config === "string") {
|
|
850
|
+
if (config.trim().length === 0) {
|
|
851
|
+
return "npm script name cannot be empty";
|
|
852
|
+
}
|
|
853
|
+
return true;
|
|
854
|
+
}
|
|
855
|
+
if (typeof config === "object" && config !== null) {
|
|
856
|
+
const cfg = config;
|
|
857
|
+
if (typeof cfg.command !== "string" || cfg.command.trim().length === 0) {
|
|
858
|
+
return "npm.command must be a non-empty string";
|
|
859
|
+
}
|
|
860
|
+
if (cfg.watch !== void 0 && typeof cfg.watch !== "boolean") {
|
|
861
|
+
return "npm.watch must be a boolean";
|
|
862
|
+
}
|
|
863
|
+
if (cfg.auto_restart !== void 0 && typeof cfg.auto_restart !== "boolean") {
|
|
864
|
+
return "npm.auto_restart must be a boolean";
|
|
865
|
+
}
|
|
866
|
+
return true;
|
|
867
|
+
}
|
|
868
|
+
return "npm config must be a boolean, string (script name), or object";
|
|
869
|
+
}
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
static get configuration() {
|
|
873
|
+
return {
|
|
874
|
+
async configureInteractive() {
|
|
875
|
+
const scriptName = await input3({
|
|
876
|
+
message: "Enter NPM script to run:",
|
|
877
|
+
default: "dev"
|
|
878
|
+
});
|
|
879
|
+
const useAdvanced = await confirm2({
|
|
880
|
+
message: "Configure advanced NPM options?",
|
|
881
|
+
default: false
|
|
882
|
+
});
|
|
883
|
+
if (!useAdvanced) {
|
|
884
|
+
return scriptName;
|
|
885
|
+
}
|
|
886
|
+
const watch = await confirm2({
|
|
887
|
+
message: "Enable watch mode?",
|
|
888
|
+
default: false
|
|
889
|
+
});
|
|
890
|
+
const autoRestart = await confirm2({
|
|
891
|
+
message: "Auto-restart on crash?",
|
|
892
|
+
default: false
|
|
893
|
+
});
|
|
894
|
+
if (!watch && !autoRestart) {
|
|
895
|
+
return scriptName;
|
|
896
|
+
}
|
|
897
|
+
return {
|
|
898
|
+
command: scriptName,
|
|
899
|
+
watch,
|
|
900
|
+
auto_restart: autoRestart
|
|
901
|
+
};
|
|
902
|
+
},
|
|
903
|
+
getDefaultConfig() {
|
|
904
|
+
return "dev";
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
static getNpmCommand(config) {
|
|
909
|
+
if (typeof config === "boolean" || config === void 0) {
|
|
910
|
+
return "npm run dev";
|
|
911
|
+
}
|
|
912
|
+
if (typeof config === "string") {
|
|
913
|
+
return `npm run ${config}`;
|
|
914
|
+
}
|
|
915
|
+
return `npm run ${config.command}`;
|
|
916
|
+
}
|
|
917
|
+
static get processing() {
|
|
918
|
+
return {
|
|
919
|
+
async processEvent(context) {
|
|
920
|
+
const { project, isShellMode, shellCommands } = context;
|
|
921
|
+
const npmConfig = project.events.npm;
|
|
922
|
+
const npmCommand = _NpmEvent.getNpmCommand(npmConfig);
|
|
923
|
+
if (isShellMode) {
|
|
924
|
+
shellCommands.push(npmCommand);
|
|
925
|
+
} else {
|
|
926
|
+
const [cmd, ...args] = npmCommand.split(" ");
|
|
927
|
+
spawn7(cmd, args, {
|
|
928
|
+
cwd: project.path.path,
|
|
929
|
+
stdio: "inherit"
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
},
|
|
933
|
+
generateShellCommand(context) {
|
|
934
|
+
const npmConfig = context.project.events.npm;
|
|
935
|
+
return [_NpmEvent.getNpmCommand(npmConfig)];
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
static get tmux() {
|
|
940
|
+
return {
|
|
941
|
+
getLayoutPriority() {
|
|
942
|
+
return 50;
|
|
943
|
+
},
|
|
944
|
+
contributeToLayout(enabledCommands) {
|
|
945
|
+
if (enabledCommands.includes("claude")) {
|
|
946
|
+
return "three-pane";
|
|
947
|
+
}
|
|
948
|
+
return "two-pane-npm";
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
static get help() {
|
|
953
|
+
return {
|
|
954
|
+
usage: 'npm: true | "script" | { command: string, watch?: boolean, auto_restart?: boolean }',
|
|
955
|
+
description: "Run an NPM script in the project directory",
|
|
956
|
+
examples: [
|
|
957
|
+
{ config: true, description: "Run npm run dev" },
|
|
958
|
+
{ config: "test", description: "Run npm run test" },
|
|
959
|
+
{ config: { command: "dev", watch: true }, description: "Run dev with watch mode" }
|
|
960
|
+
]
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
npm_default = NpmEvent;
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
// src/events/registry.ts
|
|
969
|
+
var ALL_EVENTS, EventRegistryClass, EventRegistry;
|
|
970
|
+
var init_registry = __esm({
|
|
971
|
+
"src/events/registry.ts"() {
|
|
972
|
+
"use strict";
|
|
973
|
+
init_cwd();
|
|
974
|
+
init_ide();
|
|
975
|
+
init_web();
|
|
976
|
+
init_claude();
|
|
977
|
+
init_docker();
|
|
978
|
+
init_npm();
|
|
979
|
+
ALL_EVENTS = [CwdEvent, IdeEvent, WebEvent, ClaudeEvent, DockerEvent, NpmEvent];
|
|
980
|
+
EventRegistryClass = class {
|
|
981
|
+
_events = /* @__PURE__ */ new Map();
|
|
982
|
+
_initialized = false;
|
|
983
|
+
/**
|
|
984
|
+
* Initialize the registry by registering all events
|
|
985
|
+
*/
|
|
986
|
+
async initialize() {
|
|
987
|
+
if (this._initialized) return;
|
|
988
|
+
this.registerEvents();
|
|
989
|
+
this._initialized = true;
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Register all event classes
|
|
993
|
+
*/
|
|
994
|
+
registerEvents() {
|
|
995
|
+
for (const EventClass of ALL_EVENTS) {
|
|
996
|
+
if (this.isValidEvent(EventClass)) {
|
|
997
|
+
const metadata = EventClass.metadata;
|
|
998
|
+
this._events.set(metadata.name, EventClass);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Validate if a class is a proper event
|
|
1004
|
+
*/
|
|
1005
|
+
isValidEvent(EventClass) {
|
|
1006
|
+
try {
|
|
1007
|
+
if (typeof EventClass !== "function") return false;
|
|
1008
|
+
const metadata = EventClass.metadata;
|
|
1009
|
+
return metadata !== void 0 && typeof metadata.name === "string" && typeof metadata.displayName === "string" && typeof EventClass.validation === "object" && typeof EventClass.configuration === "object" && typeof EventClass.processing === "object";
|
|
1010
|
+
} catch {
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Get all valid event names from registered events
|
|
1016
|
+
*/
|
|
1017
|
+
getValidEventNames() {
|
|
1018
|
+
this.ensureInitialized();
|
|
1019
|
+
return Array.from(this._events.keys());
|
|
1020
|
+
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Get event by name
|
|
1023
|
+
*/
|
|
1024
|
+
getEventByName(name) {
|
|
1025
|
+
this.ensureInitialized();
|
|
1026
|
+
return this._events.get(name) ?? null;
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Get all events for management UI
|
|
1030
|
+
*/
|
|
1031
|
+
getEventsForManageUI() {
|
|
1032
|
+
this.ensureInitialized();
|
|
1033
|
+
const events = [];
|
|
1034
|
+
for (const [name, EventClass] of this._events) {
|
|
1035
|
+
const metadata = EventClass.metadata;
|
|
1036
|
+
events.push({
|
|
1037
|
+
name: metadata.displayName,
|
|
1038
|
+
value: name,
|
|
1039
|
+
description: metadata.description
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
return events.sort((a, b) => a.name.localeCompare(b.name));
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Get events that support tmux integration
|
|
1046
|
+
*/
|
|
1047
|
+
getTmuxEnabledEvents() {
|
|
1048
|
+
this.ensureInitialized();
|
|
1049
|
+
const tmuxEvents = [];
|
|
1050
|
+
for (const [name, EventClass] of this._events) {
|
|
1051
|
+
const tmux = EventClass.tmux;
|
|
1052
|
+
if (tmux) {
|
|
1053
|
+
tmuxEvents.push({
|
|
1054
|
+
name,
|
|
1055
|
+
event: EventClass,
|
|
1056
|
+
priority: tmux.getLayoutPriority ? tmux.getLayoutPriority() : 0
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
return tmuxEvents.sort((a, b) => b.priority - a.priority);
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Get all available events with their metadata
|
|
1064
|
+
*/
|
|
1065
|
+
getAllEvents() {
|
|
1066
|
+
this.ensureInitialized();
|
|
1067
|
+
const events = [];
|
|
1068
|
+
for (const [name, EventClass] of this._events) {
|
|
1069
|
+
const typedClass = EventClass;
|
|
1070
|
+
events.push({
|
|
1071
|
+
name,
|
|
1072
|
+
metadata: typedClass.metadata,
|
|
1073
|
+
hasValidation: !!typedClass.validation,
|
|
1074
|
+
hasConfiguration: !!typedClass.configuration,
|
|
1075
|
+
hasProcessing: !!typedClass.processing,
|
|
1076
|
+
hasTmux: !!typedClass.tmux,
|
|
1077
|
+
hasHelp: !!typedClass.help
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
return events;
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Ensure registry is initialized
|
|
1084
|
+
*/
|
|
1085
|
+
ensureInitialized() {
|
|
1086
|
+
if (!this._initialized) {
|
|
1087
|
+
throw new Error("EventRegistry must be initialized before use. Call initialize() first.");
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Clear the registry (useful for testing)
|
|
1092
|
+
*/
|
|
1093
|
+
clear() {
|
|
1094
|
+
this._events.clear();
|
|
1095
|
+
this._initialized = false;
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
EventRegistry = new EventRegistryClass();
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
// src/commands/interactive.ts
|
|
1103
|
+
var interactive_exports = {};
|
|
1104
|
+
__export(interactive_exports, {
|
|
1105
|
+
runInteractive: () => runInteractive
|
|
1106
|
+
});
|
|
1107
|
+
import { select, input as input4, checkbox, confirm as confirm3 } from "@inquirer/prompts";
|
|
1108
|
+
import File3 from "phylo";
|
|
1109
|
+
import deepAssign2 from "deep-assign";
|
|
1110
|
+
async function runInteractive(ctx) {
|
|
1111
|
+
const { config, log, environment, suggestedName } = ctx;
|
|
1112
|
+
showLogo(config);
|
|
1113
|
+
log.log("");
|
|
1114
|
+
const defaultName = suggestedName ?? (environment.$isProjectEnvironment ? environment.project.name : File3.cwd().name);
|
|
1115
|
+
const fromUser = !!suggestedName;
|
|
1116
|
+
await startInteractive(defaultName, fromUser, ctx);
|
|
1117
|
+
}
|
|
1118
|
+
function showLogo(config) {
|
|
1119
|
+
const version = config.get("pkg")?.version ?? "unknown";
|
|
1120
|
+
console.log(
|
|
1121
|
+
` 8\x1B[2m${" ".repeat(Math.max(15 - version.length - 1, 1)) + "v" + version}\x1B[22m
|
|
1122
|
+
Yb db dP .d8b. 8d8b 8.dP \x1B[92m.d8b. 8d8b.\x1B[0m
|
|
1123
|
+
YbdPYbdP 8' .8 8P 88b \x1B[92m8' .8 8P Y8\x1B[0m
|
|
1124
|
+
YP YP \`Y8P' 8 8 Yb \x1B[92m\`Y8P' 8 8\x1B[0m`
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
async function startInteractive(defaultName, fromUser, ctx, showMain = false) {
|
|
1128
|
+
const { log, environment } = ctx;
|
|
1129
|
+
log.debug(`Name '${defaultName}' was${fromUser ? "" : " not"} provided by the user`);
|
|
1130
|
+
const question = getFirstQuestion(defaultName, fromUser, environment, showMain);
|
|
1131
|
+
const action = await select(question);
|
|
1132
|
+
switch (action) {
|
|
1133
|
+
case "exit":
|
|
192
1134
|
return;
|
|
193
1135
|
case "more":
|
|
194
1136
|
await startInteractive(defaultName, fromUser, ctx, true);
|
|
@@ -200,17 +1142,17 @@ async function startInteractive(defaultName, fromUser, ctx, showMain = false) {
|
|
|
200
1142
|
await initBranch(defaultName, ctx);
|
|
201
1143
|
return;
|
|
202
1144
|
case "switch-project":
|
|
203
|
-
|
|
204
|
-
|
|
1145
|
+
await switchProject(ctx);
|
|
1146
|
+
return;
|
|
205
1147
|
case "switch-branch":
|
|
206
|
-
|
|
207
|
-
|
|
1148
|
+
await switchBranch(defaultName, ctx);
|
|
1149
|
+
return;
|
|
208
1150
|
case "manage-projects":
|
|
209
|
-
|
|
210
|
-
|
|
1151
|
+
await manageProjects(ctx);
|
|
1152
|
+
return;
|
|
211
1153
|
case "manage-branches":
|
|
212
|
-
|
|
213
|
-
|
|
1154
|
+
await manageBranches(defaultName, ctx);
|
|
1155
|
+
return;
|
|
214
1156
|
}
|
|
215
1157
|
}
|
|
216
1158
|
function getFirstQuestion(defaultName, fromUser, environment, showMain) {
|
|
@@ -279,358 +1221,523 @@ async function initProject(defaultName, fromUser, ctx) {
|
|
|
279
1221
|
answerFile = defaultBase.join(answerFile.path);
|
|
280
1222
|
}
|
|
281
1223
|
try {
|
|
282
|
-
const canonical = answerFile.canonicalize();
|
|
283
|
-
if (canonical) {
|
|
284
|
-
answerFile = canonical;
|
|
285
|
-
} else {
|
|
286
|
-
answerFile = answerFile.absolutify();
|
|
1224
|
+
const canonical = answerFile.canonicalize();
|
|
1225
|
+
if (canonical) {
|
|
1226
|
+
answerFile = canonical;
|
|
1227
|
+
} else {
|
|
1228
|
+
answerFile = answerFile.absolutify();
|
|
1229
|
+
}
|
|
1230
|
+
} catch {
|
|
1231
|
+
answerFile = answerFile.absolutify();
|
|
1232
|
+
}
|
|
1233
|
+
basePath = answerFile.relativize(defaultBase.path).path;
|
|
1234
|
+
}
|
|
1235
|
+
const ide = await select({
|
|
1236
|
+
message: "What is the IDE?",
|
|
1237
|
+
choices: IDE_CHOICES
|
|
1238
|
+
});
|
|
1239
|
+
const selectedEvents = await checkbox({
|
|
1240
|
+
message: "Which events should take place when opening?",
|
|
1241
|
+
choices: [
|
|
1242
|
+
{ name: "Change terminal cwd to project path", value: "cwd", checked: true },
|
|
1243
|
+
{ name: "Open project in IDE", value: "ide", checked: true }
|
|
1244
|
+
]
|
|
1245
|
+
});
|
|
1246
|
+
const events = {
|
|
1247
|
+
cwd: selectedEvents.includes("cwd"),
|
|
1248
|
+
ide: selectedEvents.includes("ide")
|
|
1249
|
+
};
|
|
1250
|
+
const projectConfig = {
|
|
1251
|
+
path: basePath,
|
|
1252
|
+
ide,
|
|
1253
|
+
events
|
|
1254
|
+
};
|
|
1255
|
+
projects[name] = projectConfig;
|
|
1256
|
+
config.set("projects", projects);
|
|
1257
|
+
log.info("Your project has been initialized.");
|
|
1258
|
+
log.info(`Use 'workon ${name}' to start working!`);
|
|
1259
|
+
}
|
|
1260
|
+
async function initBranch(defaultName, ctx) {
|
|
1261
|
+
const { config, log } = ctx;
|
|
1262
|
+
const projects = config.getProjects();
|
|
1263
|
+
const branch = await input4({
|
|
1264
|
+
message: "What is the name of the branch?",
|
|
1265
|
+
validate: (value) => {
|
|
1266
|
+
if (/\w+#\w+/.test(value)) return `Branch name can't contain the "#" sign`;
|
|
1267
|
+
if (`${defaultName}#${value}` in projects) return "Branch already exists.";
|
|
1268
|
+
return true;
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
const branchName = `${defaultName}#${branch}`;
|
|
1272
|
+
const baseProject = projects[defaultName];
|
|
1273
|
+
const branchConfig = deepAssign2({}, baseProject, { branch });
|
|
1274
|
+
delete branchConfig.name;
|
|
1275
|
+
projects[branchName] = branchConfig;
|
|
1276
|
+
config.set("projects", projects);
|
|
1277
|
+
log.info("Your branch configuration has been initialized.");
|
|
1278
|
+
log.info(`Use 'workon ${branchName}' to start working!`);
|
|
1279
|
+
}
|
|
1280
|
+
async function switchProject(ctx) {
|
|
1281
|
+
const { config, log } = ctx;
|
|
1282
|
+
const projects = config.getProjects();
|
|
1283
|
+
const baseProjects = Object.keys(projects).filter((name) => !name.includes("#"));
|
|
1284
|
+
if (baseProjects.length === 0) {
|
|
1285
|
+
log.info('No projects configured yet. Use "Start a new project" to create one.');
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
const projectName = await select({
|
|
1289
|
+
message: "Select a project to open:",
|
|
1290
|
+
choices: baseProjects.map((name) => ({
|
|
1291
|
+
name: `${name} (${projects[name].path})`,
|
|
1292
|
+
value: name
|
|
1293
|
+
}))
|
|
1294
|
+
});
|
|
1295
|
+
await openProject(projectName, ctx);
|
|
1296
|
+
}
|
|
1297
|
+
async function switchBranch(projectName, ctx) {
|
|
1298
|
+
const { config, log } = ctx;
|
|
1299
|
+
const projects = config.getProjects();
|
|
1300
|
+
const branchPrefix = `${projectName}#`;
|
|
1301
|
+
const branches = Object.keys(projects).filter((name) => name.startsWith(branchPrefix));
|
|
1302
|
+
if (branches.length === 0) {
|
|
1303
|
+
log.info(`No branch configurations found for '${projectName}'.`);
|
|
1304
|
+
log.info('Use "Start a branch" to create one.');
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
const branchConfig = await select({
|
|
1308
|
+
message: "Select a branch configuration:",
|
|
1309
|
+
choices: branches.map((name) => ({
|
|
1310
|
+
name: name.substring(branchPrefix.length),
|
|
1311
|
+
value: name
|
|
1312
|
+
}))
|
|
1313
|
+
});
|
|
1314
|
+
await openProject(branchConfig, ctx);
|
|
1315
|
+
}
|
|
1316
|
+
async function manageProjects(ctx) {
|
|
1317
|
+
const { config } = ctx;
|
|
1318
|
+
await EventRegistry.initialize();
|
|
1319
|
+
const projects = config.getProjects();
|
|
1320
|
+
const hasProjects = Object.keys(projects).length > 0;
|
|
1321
|
+
const choices = [
|
|
1322
|
+
{ name: "Create new project", value: "create" },
|
|
1323
|
+
...hasProjects ? [
|
|
1324
|
+
{ name: "Edit project", value: "edit" },
|
|
1325
|
+
{ name: "Delete project", value: "delete" },
|
|
1326
|
+
{ name: "List projects", value: "list" }
|
|
1327
|
+
] : [],
|
|
1328
|
+
{ name: "Back", value: "back" }
|
|
1329
|
+
];
|
|
1330
|
+
const action = await select({
|
|
1331
|
+
message: "Manage projects:",
|
|
1332
|
+
choices
|
|
1333
|
+
});
|
|
1334
|
+
switch (action) {
|
|
1335
|
+
case "create":
|
|
1336
|
+
await createProjectManage(ctx);
|
|
1337
|
+
break;
|
|
1338
|
+
case "edit":
|
|
1339
|
+
await editProjectManage(ctx);
|
|
1340
|
+
break;
|
|
1341
|
+
case "delete":
|
|
1342
|
+
await deleteProjectManage(ctx);
|
|
1343
|
+
break;
|
|
1344
|
+
case "list":
|
|
1345
|
+
listProjectsManage(ctx);
|
|
1346
|
+
break;
|
|
1347
|
+
case "back":
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
await manageProjects(ctx);
|
|
1351
|
+
}
|
|
1352
|
+
async function manageBranches(projectName, ctx) {
|
|
1353
|
+
const { config } = ctx;
|
|
1354
|
+
const projects = config.getProjects();
|
|
1355
|
+
const branchPrefix = `${projectName}#`;
|
|
1356
|
+
const branches = Object.keys(projects).filter((name) => name.startsWith(branchPrefix));
|
|
1357
|
+
const choices = [
|
|
1358
|
+
{ name: "Create new branch config", value: "create" },
|
|
1359
|
+
...branches.length > 0 ? [
|
|
1360
|
+
{ name: "Edit branch config", value: "edit" },
|
|
1361
|
+
{ name: "Delete branch config", value: "delete" },
|
|
1362
|
+
{ name: "List branch configs", value: "list" }
|
|
1363
|
+
] : [],
|
|
1364
|
+
{ name: "Back", value: "back" }
|
|
1365
|
+
];
|
|
1366
|
+
const action = await select({
|
|
1367
|
+
message: `Manage branches for '${projectName}':`,
|
|
1368
|
+
choices
|
|
1369
|
+
});
|
|
1370
|
+
switch (action) {
|
|
1371
|
+
case "create":
|
|
1372
|
+
await initBranch(projectName, ctx);
|
|
1373
|
+
break;
|
|
1374
|
+
case "edit":
|
|
1375
|
+
await editBranchManage(projectName, ctx);
|
|
1376
|
+
break;
|
|
1377
|
+
case "delete":
|
|
1378
|
+
await deleteBranchManage(projectName, ctx);
|
|
1379
|
+
break;
|
|
1380
|
+
case "list":
|
|
1381
|
+
listBranchesManage(projectName, ctx);
|
|
1382
|
+
break;
|
|
1383
|
+
case "back":
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
await manageBranches(projectName, ctx);
|
|
1387
|
+
}
|
|
1388
|
+
async function openProject(projectName, ctx) {
|
|
1389
|
+
const { config, log } = ctx;
|
|
1390
|
+
const projects = config.getProjects();
|
|
1391
|
+
if (!(projectName in projects)) {
|
|
1392
|
+
log.error(`Project '${projectName}' not found.`);
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
await EventRegistry.initialize();
|
|
1396
|
+
const projectConfig = projects[projectName];
|
|
1397
|
+
const projectCfg = { ...projectConfig, name: projectName };
|
|
1398
|
+
const projectEnv = ProjectEnvironment.load(projectCfg, config.getDefaults());
|
|
1399
|
+
log.info(`Opening project '${projectName}'...`);
|
|
1400
|
+
const events = Object.keys(projectConfig.events).filter(
|
|
1401
|
+
(e) => projectConfig.events[e]
|
|
1402
|
+
);
|
|
1403
|
+
for (const event of events) {
|
|
1404
|
+
const eventHandler = EventRegistry.getEventByName(event);
|
|
1405
|
+
if (eventHandler && eventHandler.processing) {
|
|
1406
|
+
await eventHandler.processing.processEvent({
|
|
1407
|
+
project: projectEnv.project,
|
|
1408
|
+
isShellMode: false,
|
|
1409
|
+
shellCommands: []
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
async function createProjectManage(ctx) {
|
|
1415
|
+
const { config, log } = ctx;
|
|
1416
|
+
const defaults = config.getDefaults();
|
|
1417
|
+
const projects = config.getProjects();
|
|
1418
|
+
const name = await input4({
|
|
1419
|
+
message: "Project name:",
|
|
1420
|
+
validate: (value) => {
|
|
1421
|
+
if (!value.trim()) return "Name is required";
|
|
1422
|
+
if (!/^[\w-]+$/.test(value))
|
|
1423
|
+
return "Name can only contain letters, numbers, underscores, and hyphens";
|
|
1424
|
+
if (value in projects) return "Project already exists";
|
|
1425
|
+
return true;
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
const defaultPath = defaults?.base ? File3.from(defaults.base).join(name).path : name;
|
|
1429
|
+
const pathInput = await input4({
|
|
1430
|
+
message: "Project path:",
|
|
1431
|
+
default: defaultPath
|
|
1432
|
+
});
|
|
1433
|
+
let relativePath = pathInput;
|
|
1434
|
+
if (defaults?.base) {
|
|
1435
|
+
const baseDir = File3.from(defaults.base);
|
|
1436
|
+
const pathFile = File3.from(pathInput);
|
|
1437
|
+
try {
|
|
1438
|
+
if (pathFile.isAbsolute()) {
|
|
1439
|
+
relativePath = pathFile.relativize(baseDir.path).path;
|
|
1440
|
+
}
|
|
1441
|
+
} catch {
|
|
1442
|
+
relativePath = pathInput;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
const ide = await select({
|
|
1446
|
+
message: "Select IDE:",
|
|
1447
|
+
choices: IDE_CHOICES
|
|
1448
|
+
});
|
|
1449
|
+
const availableEvents = EventRegistry.getEventsForManageUI();
|
|
1450
|
+
const selectedEvents = await checkbox({
|
|
1451
|
+
message: "Select events to enable:",
|
|
1452
|
+
choices: availableEvents.map((e) => ({
|
|
1453
|
+
name: `${e.name} - ${e.description}`,
|
|
1454
|
+
value: e.value,
|
|
1455
|
+
checked: e.value === "cwd" || e.value === "ide"
|
|
1456
|
+
}))
|
|
1457
|
+
});
|
|
1458
|
+
const events = {};
|
|
1459
|
+
for (const eventName of selectedEvents) {
|
|
1460
|
+
const eventHandler = EventRegistry.getEventByName(eventName);
|
|
1461
|
+
if (eventHandler) {
|
|
1462
|
+
const eventConfig = await eventHandler.configuration.configureInteractive();
|
|
1463
|
+
events[eventName] = eventConfig;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
const projectConfig = {
|
|
1467
|
+
path: relativePath,
|
|
1468
|
+
ide,
|
|
1469
|
+
events
|
|
1470
|
+
};
|
|
1471
|
+
const confirmed = await confirm3({
|
|
1472
|
+
message: "Save this project?",
|
|
1473
|
+
default: true
|
|
1474
|
+
});
|
|
1475
|
+
if (confirmed) {
|
|
1476
|
+
config.setProject(name, projectConfig);
|
|
1477
|
+
log.info(`Project '${name}' created successfully!`);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
async function editProjectManage(ctx) {
|
|
1481
|
+
const { config, log } = ctx;
|
|
1482
|
+
const projects = config.getProjects();
|
|
1483
|
+
const defaults = config.getDefaults();
|
|
1484
|
+
const baseProjects = Object.keys(projects).filter((name2) => !name2.includes("#"));
|
|
1485
|
+
if (baseProjects.length === 0) {
|
|
1486
|
+
log.info("No projects to edit.");
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
const name = await select({
|
|
1490
|
+
message: "Select project to edit:",
|
|
1491
|
+
choices: baseProjects.map((n) => ({ name: n, value: n }))
|
|
1492
|
+
});
|
|
1493
|
+
const project = projects[name];
|
|
1494
|
+
const pathInput = await input4({
|
|
1495
|
+
message: "Project path:",
|
|
1496
|
+
default: project.path
|
|
1497
|
+
});
|
|
1498
|
+
let relativePath = pathInput;
|
|
1499
|
+
if (defaults?.base) {
|
|
1500
|
+
const baseDir = File3.from(defaults.base);
|
|
1501
|
+
const pathFile = File3.from(pathInput);
|
|
1502
|
+
try {
|
|
1503
|
+
if (pathFile.isAbsolute()) {
|
|
1504
|
+
relativePath = pathFile.relativize(baseDir.path).path;
|
|
287
1505
|
}
|
|
288
1506
|
} catch {
|
|
289
|
-
|
|
1507
|
+
relativePath = pathInput;
|
|
290
1508
|
}
|
|
291
|
-
basePath = answerFile.relativize(defaultBase.path).path;
|
|
292
1509
|
}
|
|
293
1510
|
const ide = await select({
|
|
294
|
-
message: "
|
|
295
|
-
choices: IDE_CHOICES
|
|
1511
|
+
message: "Select IDE:",
|
|
1512
|
+
choices: IDE_CHOICES,
|
|
1513
|
+
default: project.ide || "vscode"
|
|
296
1514
|
});
|
|
297
|
-
const
|
|
298
|
-
message: "
|
|
299
|
-
|
|
300
|
-
{ name: "Change terminal cwd to project path", value: "cwd", checked: true },
|
|
301
|
-
{ name: "Open project in IDE", value: "ide", checked: true }
|
|
302
|
-
]
|
|
1515
|
+
const keepEvents = await confirm3({
|
|
1516
|
+
message: "Keep existing event configuration?",
|
|
1517
|
+
default: true
|
|
303
1518
|
});
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
1519
|
+
let events = project.events;
|
|
1520
|
+
if (!keepEvents) {
|
|
1521
|
+
const availableEvents = EventRegistry.getEventsForManageUI();
|
|
1522
|
+
const currentEvents = Object.keys(project.events);
|
|
1523
|
+
const selectedEvents = await checkbox({
|
|
1524
|
+
message: "Select events to enable:",
|
|
1525
|
+
choices: availableEvents.map((e) => ({
|
|
1526
|
+
name: `${e.name} - ${e.description}`,
|
|
1527
|
+
value: e.value,
|
|
1528
|
+
checked: currentEvents.includes(e.value)
|
|
1529
|
+
}))
|
|
1530
|
+
});
|
|
1531
|
+
events = {};
|
|
1532
|
+
for (const eventName of selectedEvents) {
|
|
1533
|
+
if (project.events[eventName]) {
|
|
1534
|
+
events[eventName] = project.events[eventName];
|
|
1535
|
+
} else {
|
|
1536
|
+
const eventHandler = EventRegistry.getEventByName(eventName);
|
|
1537
|
+
if (eventHandler) {
|
|
1538
|
+
const eventConfig = await eventHandler.configuration.configureInteractive();
|
|
1539
|
+
events[eventName] = eventConfig;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
const updatedConfig = {
|
|
1545
|
+
path: relativePath,
|
|
310
1546
|
ide,
|
|
311
1547
|
events
|
|
312
1548
|
};
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
1549
|
+
const confirmed = await confirm3({
|
|
1550
|
+
message: "Save changes?",
|
|
1551
|
+
default: true
|
|
1552
|
+
});
|
|
1553
|
+
if (confirmed) {
|
|
1554
|
+
config.setProject(name, updatedConfig);
|
|
1555
|
+
log.info(`Project '${name}' updated successfully!`);
|
|
1556
|
+
}
|
|
317
1557
|
}
|
|
318
|
-
async function
|
|
1558
|
+
async function deleteProjectManage(ctx) {
|
|
319
1559
|
const { config, log } = ctx;
|
|
320
1560
|
const projects = config.getProjects();
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (`${defaultName}#${value}` in projects) return "Branch already exists.";
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
const branchName = `${defaultName}#${branch}`;
|
|
330
|
-
const baseProject = projects[defaultName];
|
|
331
|
-
const branchConfig = deepAssign2({}, baseProject, { branch });
|
|
332
|
-
delete branchConfig.name;
|
|
333
|
-
projects[branchName] = branchConfig;
|
|
334
|
-
config.set("projects", projects);
|
|
335
|
-
log.info("Your branch configuration has been initialized.");
|
|
336
|
-
log.info(`Use 'workon ${branchName}' to start working!`);
|
|
337
|
-
}
|
|
338
|
-
var IDE_CHOICES;
|
|
339
|
-
var init_interactive = __esm({
|
|
340
|
-
"src/commands/interactive.ts"() {
|
|
341
|
-
"use strict";
|
|
342
|
-
IDE_CHOICES = [
|
|
343
|
-
{ name: "Visual Studio Code", value: "vscode" },
|
|
344
|
-
{ name: "IntelliJ IDEA", value: "idea" },
|
|
345
|
-
{ name: "Atom", value: "atom" }
|
|
346
|
-
];
|
|
1561
|
+
const baseProjects = Object.keys(projects).filter((name2) => !name2.includes("#"));
|
|
1562
|
+
if (baseProjects.length === 0) {
|
|
1563
|
+
log.info("No projects to delete.");
|
|
1564
|
+
return;
|
|
347
1565
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
// src/lib/config.ts
|
|
360
|
-
import Conf from "conf";
|
|
361
|
-
var TRANSIENT_PROPS = ["pkg", "work"];
|
|
362
|
-
var Config = class {
|
|
363
|
-
_transient = {};
|
|
364
|
-
_store;
|
|
365
|
-
constructor() {
|
|
366
|
-
this._store = new Conf({
|
|
367
|
-
projectName: "workon"
|
|
1566
|
+
const name = await select({
|
|
1567
|
+
message: "Select project to delete:",
|
|
1568
|
+
choices: baseProjects.map((n) => ({ name: n, value: n }))
|
|
1569
|
+
});
|
|
1570
|
+
const branchPrefix = `${name}#`;
|
|
1571
|
+
const branches = Object.keys(projects).filter((n) => n.startsWith(branchPrefix));
|
|
1572
|
+
if (branches.length > 0) {
|
|
1573
|
+
log.warn(`This project has ${branches.length} branch configuration(s).`);
|
|
1574
|
+
const deleteAll = await confirm3({
|
|
1575
|
+
message: "Delete all branch configurations as well?",
|
|
1576
|
+
default: false
|
|
368
1577
|
});
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
if (TRANSIENT_PROPS.includes(rootKey)) {
|
|
373
|
-
return this._transient[key] ?? defaultValue;
|
|
374
|
-
}
|
|
375
|
-
return this._store.get(key, defaultValue);
|
|
376
|
-
}
|
|
377
|
-
set(key, value) {
|
|
378
|
-
const rootKey = key.split(".")[0];
|
|
379
|
-
if (TRANSIENT_PROPS.includes(rootKey)) {
|
|
380
|
-
this._transient[key] = value;
|
|
381
|
-
} else {
|
|
382
|
-
if (value === void 0) {
|
|
383
|
-
this._store.set(key, value);
|
|
384
|
-
} else {
|
|
385
|
-
this._store.set(key, value);
|
|
1578
|
+
if (deleteAll) {
|
|
1579
|
+
for (const branch of branches) {
|
|
1580
|
+
config.deleteProject(branch);
|
|
386
1581
|
}
|
|
387
1582
|
}
|
|
388
1583
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
delete(key) {
|
|
397
|
-
const rootKey = key.split(".")[0];
|
|
398
|
-
if (TRANSIENT_PROPS.includes(rootKey)) {
|
|
399
|
-
delete this._transient[key];
|
|
400
|
-
} else {
|
|
401
|
-
this._store.delete(key);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
getProjects() {
|
|
405
|
-
return this.get("projects") ?? {};
|
|
406
|
-
}
|
|
407
|
-
getProject(name) {
|
|
408
|
-
const projects = this.getProjects();
|
|
409
|
-
return projects[name];
|
|
410
|
-
}
|
|
411
|
-
setProject(name, config) {
|
|
412
|
-
const projects = this.getProjects();
|
|
413
|
-
projects[name] = config;
|
|
414
|
-
this.set("projects", projects);
|
|
415
|
-
}
|
|
416
|
-
deleteProject(name) {
|
|
417
|
-
const projects = this.getProjects();
|
|
418
|
-
delete projects[name];
|
|
419
|
-
this.set("projects", projects);
|
|
420
|
-
}
|
|
421
|
-
getDefaults() {
|
|
422
|
-
return this.get("project_defaults");
|
|
423
|
-
}
|
|
424
|
-
setDefaults(defaults) {
|
|
425
|
-
this.set("project_defaults", defaults);
|
|
426
|
-
}
|
|
427
|
-
get path() {
|
|
428
|
-
return this._store.path;
|
|
429
|
-
}
|
|
430
|
-
get store() {
|
|
431
|
-
return this._store.store;
|
|
1584
|
+
const confirmed = await confirm3({
|
|
1585
|
+
message: `Are you sure you want to delete '${name}'?`,
|
|
1586
|
+
default: false
|
|
1587
|
+
});
|
|
1588
|
+
if (confirmed) {
|
|
1589
|
+
config.deleteProject(name);
|
|
1590
|
+
log.info(`Project '${name}' deleted.`);
|
|
432
1591
|
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
name;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
_initialCfg;
|
|
452
|
-
constructor(name, cfg, defaults) {
|
|
453
|
-
this._defaults = defaults ?? { base: "" };
|
|
454
|
-
this._initialCfg = { path: name, events: {}, ...cfg };
|
|
455
|
-
this.name = cfg?.name ?? name;
|
|
456
|
-
const merged = deepAssign({}, this._defaults, this._initialCfg);
|
|
457
|
-
if (merged.base) {
|
|
458
|
-
this.base = merged.base;
|
|
459
|
-
}
|
|
460
|
-
if (merged.path) {
|
|
461
|
-
this.path = merged.path;
|
|
462
|
-
}
|
|
463
|
-
if (merged.ide) {
|
|
464
|
-
this._ide = merged.ide;
|
|
465
|
-
}
|
|
466
|
-
if (merged.events) {
|
|
467
|
-
this._events = merged.events;
|
|
468
|
-
}
|
|
469
|
-
if (merged.branch) {
|
|
470
|
-
this._branch = merged.branch;
|
|
471
|
-
}
|
|
472
|
-
if (merged.homepage) {
|
|
473
|
-
this._homepage = merged.homepage;
|
|
1592
|
+
}
|
|
1593
|
+
function listProjectsManage(ctx) {
|
|
1594
|
+
const { config } = ctx;
|
|
1595
|
+
const projects = config.getProjects();
|
|
1596
|
+
const defaults = config.getDefaults();
|
|
1597
|
+
console.log("\nConfigured projects:\n");
|
|
1598
|
+
const baseProjects = Object.keys(projects).filter((name) => !name.includes("#"));
|
|
1599
|
+
for (const name of baseProjects) {
|
|
1600
|
+
const project = projects[name];
|
|
1601
|
+
const fullPath = defaults?.base ? File3.from(defaults.base).join(project.path).path : project.path;
|
|
1602
|
+
console.log(` ${name}`);
|
|
1603
|
+
console.log(` Path: ${fullPath}`);
|
|
1604
|
+
console.log(` IDE: ${project.ide || "not set"}`);
|
|
1605
|
+
console.log(` Events: ${Object.keys(project.events).join(", ") || "none"}`);
|
|
1606
|
+
const branchPrefix = `${name}#`;
|
|
1607
|
+
const branches = Object.keys(projects).filter((n) => n.startsWith(branchPrefix));
|
|
1608
|
+
if (branches.length > 0) {
|
|
1609
|
+
console.log(` Branches: ${branches.length}`);
|
|
474
1610
|
}
|
|
1611
|
+
console.log();
|
|
475
1612
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
get ide() {
|
|
486
|
-
return this._ide;
|
|
487
|
-
}
|
|
488
|
-
set events(eventCfg) {
|
|
489
|
-
this._events = eventCfg;
|
|
490
|
-
}
|
|
491
|
-
get events() {
|
|
492
|
-
return this._events;
|
|
493
|
-
}
|
|
494
|
-
set path(path) {
|
|
495
|
-
if (this._base) {
|
|
496
|
-
this._path = this._base.join(path);
|
|
497
|
-
} else {
|
|
498
|
-
this._path = File.from(path);
|
|
499
|
-
}
|
|
500
|
-
this._path = this._path.absolutify();
|
|
1613
|
+
}
|
|
1614
|
+
async function editBranchManage(projectName, ctx) {
|
|
1615
|
+
const { config, log } = ctx;
|
|
1616
|
+
const projects = config.getProjects();
|
|
1617
|
+
const branchPrefix = `${projectName}#`;
|
|
1618
|
+
const branches = Object.keys(projects).filter((name) => name.startsWith(branchPrefix));
|
|
1619
|
+
if (branches.length === 0) {
|
|
1620
|
+
log.info("No branch configurations to edit.");
|
|
1621
|
+
return;
|
|
501
1622
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
1623
|
+
const branchName = await select({
|
|
1624
|
+
message: "Select branch configuration to edit:",
|
|
1625
|
+
choices: branches.map((n) => ({
|
|
1626
|
+
name: n.substring(branchPrefix.length),
|
|
1627
|
+
value: n
|
|
1628
|
+
}))
|
|
1629
|
+
});
|
|
1630
|
+
const branch = projects[branchName];
|
|
1631
|
+
const keepEvents = await confirm3({
|
|
1632
|
+
message: "Keep existing event configuration?",
|
|
1633
|
+
default: true
|
|
1634
|
+
});
|
|
1635
|
+
let events = branch.events;
|
|
1636
|
+
if (!keepEvents) {
|
|
1637
|
+
const availableEvents = EventRegistry.getEventsForManageUI();
|
|
1638
|
+
const currentEvents = Object.keys(branch.events);
|
|
1639
|
+
const selectedEvents = await checkbox({
|
|
1640
|
+
message: "Select events to enable:",
|
|
1641
|
+
choices: availableEvents.map((e) => ({
|
|
1642
|
+
name: `${e.name} - ${e.description}`,
|
|
1643
|
+
value: e.value,
|
|
1644
|
+
checked: currentEvents.includes(e.value)
|
|
1645
|
+
}))
|
|
1646
|
+
});
|
|
1647
|
+
events = {};
|
|
1648
|
+
for (const eventName of selectedEvents) {
|
|
1649
|
+
if (branch.events[eventName]) {
|
|
1650
|
+
events[eventName] = branch.events[eventName];
|
|
1651
|
+
} else {
|
|
1652
|
+
const eventHandler = EventRegistry.getEventByName(eventName);
|
|
1653
|
+
if (eventHandler) {
|
|
1654
|
+
const eventConfig = await eventHandler.configuration.configureInteractive();
|
|
1655
|
+
events[eventName] = eventConfig;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
505
1658
|
}
|
|
506
|
-
return this._path;
|
|
507
|
-
}
|
|
508
|
-
set branch(branch) {
|
|
509
|
-
this._branch = branch;
|
|
510
|
-
}
|
|
511
|
-
get branch() {
|
|
512
|
-
return this._branch;
|
|
513
1659
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
var BaseEnvironment = class {
|
|
526
|
-
$isProjectEnvironment = false;
|
|
527
|
-
};
|
|
528
|
-
var ProjectEnvironment = class _ProjectEnvironment {
|
|
529
|
-
$isProjectEnvironment = true;
|
|
530
|
-
project;
|
|
531
|
-
constructor(projectCfg) {
|
|
532
|
-
this.project = new Project(projectCfg.name, projectCfg);
|
|
1660
|
+
const updatedConfig = {
|
|
1661
|
+
...branch,
|
|
1662
|
+
events
|
|
1663
|
+
};
|
|
1664
|
+
const confirmed = await confirm3({
|
|
1665
|
+
message: "Save changes?",
|
|
1666
|
+
default: true
|
|
1667
|
+
});
|
|
1668
|
+
if (confirmed) {
|
|
1669
|
+
config.setProject(branchName, updatedConfig);
|
|
1670
|
+
log.info(`Branch configuration '${branchName}' updated successfully!`);
|
|
533
1671
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
1672
|
+
}
|
|
1673
|
+
async function deleteBranchManage(projectName, ctx) {
|
|
1674
|
+
const { config, log } = ctx;
|
|
1675
|
+
const projects = config.getProjects();
|
|
1676
|
+
const branchPrefix = `${projectName}#`;
|
|
1677
|
+
const branches = Object.keys(projects).filter((name) => name.startsWith(branchPrefix));
|
|
1678
|
+
if (branches.length === 0) {
|
|
1679
|
+
log.info("No branch configurations to delete.");
|
|
1680
|
+
return;
|
|
537
1681
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
static async recognize(dir) {
|
|
553
|
-
this.ensureConfigured();
|
|
554
|
-
const theDir = File2.from(dir).canonicalize();
|
|
555
|
-
this.log.debug("Directory to recognize is: " + theDir.canonicalPath());
|
|
556
|
-
const allProjects = this.getAllProjects();
|
|
557
|
-
const matching = allProjects.filter((p) => p.path.canonicalPath() === theDir.path);
|
|
558
|
-
if (matching.length === 0) {
|
|
559
|
-
return new BaseEnvironment();
|
|
560
|
-
}
|
|
561
|
-
this.log.debug(`Found ${matching.length} matching projects`);
|
|
562
|
-
const base = matching.find((p) => !p.name.includes("#")) ?? matching[0];
|
|
563
|
-
this.log.debug("Base project is: " + base.name);
|
|
564
|
-
const gitDir = base.path.up(".git");
|
|
565
|
-
if (gitDir) {
|
|
566
|
-
try {
|
|
567
|
-
const git = simpleGit(gitDir.path);
|
|
568
|
-
const branchSummary = await git.branchLocal();
|
|
569
|
-
base.branch = branchSummary.current;
|
|
570
|
-
} catch {
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
return this.getProjectEnvironment(base, matching);
|
|
1682
|
+
const branchName = await select({
|
|
1683
|
+
message: "Select branch configuration to delete:",
|
|
1684
|
+
choices: branches.map((n) => ({
|
|
1685
|
+
name: n.substring(branchPrefix.length),
|
|
1686
|
+
value: n
|
|
1687
|
+
}))
|
|
1688
|
+
});
|
|
1689
|
+
const confirmed = await confirm3({
|
|
1690
|
+
message: `Are you sure you want to delete '${branchName}'?`,
|
|
1691
|
+
default: false
|
|
1692
|
+
});
|
|
1693
|
+
if (confirmed) {
|
|
1694
|
+
config.deleteProject(branchName);
|
|
1695
|
+
log.info(`Branch configuration '${branchName}' deleted.`);
|
|
574
1696
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
const
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}));
|
|
591
|
-
return this.projects;
|
|
592
|
-
}
|
|
593
|
-
static getProjectEnvironment(base, _matching) {
|
|
594
|
-
const exactName = `${base.name}#${base.branch}`;
|
|
595
|
-
const exactProj = this.projects.find((p) => p.name === exactName);
|
|
596
|
-
const toProjectConfig = (p) => ({
|
|
597
|
-
name: p.name,
|
|
598
|
-
path: p.path.path,
|
|
599
|
-
// Convert PhyloFile to string path
|
|
600
|
-
ide: p.ide,
|
|
601
|
-
homepage: p.homepage,
|
|
602
|
-
events: p.events,
|
|
603
|
-
branch: p.branch,
|
|
604
|
-
exactName
|
|
605
|
-
});
|
|
606
|
-
if (exactProj) {
|
|
607
|
-
return new ProjectEnvironment({ ...toProjectConfig(exactProj), branch: base.branch });
|
|
608
|
-
}
|
|
609
|
-
return new ProjectEnvironment(toProjectConfig(base));
|
|
610
|
-
}
|
|
611
|
-
static ensureConfigured() {
|
|
612
|
-
if (!this.configured) {
|
|
613
|
-
this.config = new Config();
|
|
614
|
-
this.log = {
|
|
615
|
-
debug: () => {
|
|
616
|
-
},
|
|
617
|
-
info: () => {
|
|
618
|
-
},
|
|
619
|
-
log: () => {
|
|
620
|
-
},
|
|
621
|
-
warn: () => {
|
|
622
|
-
},
|
|
623
|
-
error: () => {
|
|
624
|
-
},
|
|
625
|
-
setLogLevel: () => {
|
|
626
|
-
}
|
|
627
|
-
};
|
|
628
|
-
this.configured = true;
|
|
629
|
-
}
|
|
1697
|
+
}
|
|
1698
|
+
function listBranchesManage(projectName, ctx) {
|
|
1699
|
+
const { config } = ctx;
|
|
1700
|
+
const projects = config.getProjects();
|
|
1701
|
+
const branchPrefix = `${projectName}#`;
|
|
1702
|
+
const branches = Object.keys(projects).filter((name) => name.startsWith(branchPrefix));
|
|
1703
|
+
console.log(`
|
|
1704
|
+
Branch configurations for '${projectName}':
|
|
1705
|
+
`);
|
|
1706
|
+
for (const branchName of branches) {
|
|
1707
|
+
const branch = projects[branchName];
|
|
1708
|
+
const shortName = branchName.substring(branchPrefix.length);
|
|
1709
|
+
console.log(` ${shortName}`);
|
|
1710
|
+
console.log(` Events: ${Object.keys(branch.events).join(", ") || "none"}`);
|
|
1711
|
+
console.log();
|
|
630
1712
|
}
|
|
631
|
-
}
|
|
1713
|
+
}
|
|
1714
|
+
var IDE_CHOICES;
|
|
1715
|
+
var init_interactive = __esm({
|
|
1716
|
+
"src/commands/interactive.ts"() {
|
|
1717
|
+
"use strict";
|
|
1718
|
+
init_environment();
|
|
1719
|
+
init_registry();
|
|
1720
|
+
IDE_CHOICES = [
|
|
1721
|
+
{ name: "Visual Studio Code", value: "vscode" },
|
|
1722
|
+
{ name: "IntelliJ IDEA", value: "idea" },
|
|
1723
|
+
{ name: "Atom", value: "atom" }
|
|
1724
|
+
];
|
|
1725
|
+
}
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
// src/commands/index.ts
|
|
1729
|
+
init_config();
|
|
1730
|
+
init_environment();
|
|
1731
|
+
import { Command as Command8 } from "commander";
|
|
1732
|
+
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
1733
|
+
import { join, dirname } from "path";
|
|
1734
|
+
import { fileURLToPath } from "url";
|
|
1735
|
+
import loog from "loog";
|
|
1736
|
+
import omelette from "omelette";
|
|
1737
|
+
import File7 from "phylo";
|
|
632
1738
|
|
|
633
1739
|
// src/commands/open.ts
|
|
1740
|
+
init_environment();
|
|
634
1741
|
import { Command } from "commander";
|
|
635
1742
|
import File4 from "phylo";
|
|
636
1743
|
|
|
@@ -782,616 +1889,8 @@ var TmuxManager = class {
|
|
|
782
1889
|
}
|
|
783
1890
|
};
|
|
784
1891
|
|
|
785
|
-
// src/events/core/cwd.ts
|
|
786
|
-
import { spawn as spawn2 } from "child_process";
|
|
787
|
-
var CwdEvent = class {
|
|
788
|
-
static get metadata() {
|
|
789
|
-
return {
|
|
790
|
-
name: "cwd",
|
|
791
|
-
displayName: "Change directory (cwd)",
|
|
792
|
-
description: "Change current working directory to project path",
|
|
793
|
-
category: "core",
|
|
794
|
-
requiresTmux: false,
|
|
795
|
-
dependencies: []
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
static get validation() {
|
|
799
|
-
return {
|
|
800
|
-
validateConfig(config) {
|
|
801
|
-
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
802
|
-
return true;
|
|
803
|
-
}
|
|
804
|
-
return "cwd config must be a boolean (true/false)";
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
}
|
|
808
|
-
static get configuration() {
|
|
809
|
-
return {
|
|
810
|
-
async configureInteractive() {
|
|
811
|
-
return true;
|
|
812
|
-
},
|
|
813
|
-
getDefaultConfig() {
|
|
814
|
-
return true;
|
|
815
|
-
}
|
|
816
|
-
};
|
|
817
|
-
}
|
|
818
|
-
static get processing() {
|
|
819
|
-
return {
|
|
820
|
-
async processEvent(context) {
|
|
821
|
-
const { project, isShellMode, shellCommands } = context;
|
|
822
|
-
const projectPath = project.path.path;
|
|
823
|
-
if (isShellMode) {
|
|
824
|
-
shellCommands.push(`cd "${projectPath}"`);
|
|
825
|
-
} else {
|
|
826
|
-
const shell = process.env.SHELL || "/bin/bash";
|
|
827
|
-
spawn2(shell, [], {
|
|
828
|
-
cwd: projectPath,
|
|
829
|
-
stdio: "inherit"
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
},
|
|
833
|
-
generateShellCommand(context) {
|
|
834
|
-
const projectPath = context.project.path.path;
|
|
835
|
-
return [`cd "${projectPath}"`];
|
|
836
|
-
}
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
static get tmux() {
|
|
840
|
-
return null;
|
|
841
|
-
}
|
|
842
|
-
static get help() {
|
|
843
|
-
return {
|
|
844
|
-
usage: "cwd: true | false",
|
|
845
|
-
description: "Change the current working directory to the project path",
|
|
846
|
-
examples: [
|
|
847
|
-
{ config: true, description: "Enable directory change" },
|
|
848
|
-
{ config: false, description: "Disable directory change" }
|
|
849
|
-
]
|
|
850
|
-
};
|
|
851
|
-
}
|
|
852
|
-
};
|
|
853
|
-
|
|
854
|
-
// src/events/core/ide.ts
|
|
855
|
-
import { spawn as spawn3 } from "child_process";
|
|
856
|
-
var IdeEvent = class {
|
|
857
|
-
static get metadata() {
|
|
858
|
-
return {
|
|
859
|
-
name: "ide",
|
|
860
|
-
displayName: "Open in IDE",
|
|
861
|
-
description: "Open project in configured IDE/editor",
|
|
862
|
-
category: "core",
|
|
863
|
-
requiresTmux: false,
|
|
864
|
-
dependencies: []
|
|
865
|
-
};
|
|
866
|
-
}
|
|
867
|
-
static get validation() {
|
|
868
|
-
return {
|
|
869
|
-
validateConfig(config) {
|
|
870
|
-
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
871
|
-
return true;
|
|
872
|
-
}
|
|
873
|
-
return "ide config must be a boolean (true/false)";
|
|
874
|
-
}
|
|
875
|
-
};
|
|
876
|
-
}
|
|
877
|
-
static get configuration() {
|
|
878
|
-
return {
|
|
879
|
-
async configureInteractive() {
|
|
880
|
-
return true;
|
|
881
|
-
},
|
|
882
|
-
getDefaultConfig() {
|
|
883
|
-
return true;
|
|
884
|
-
}
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
static get processing() {
|
|
888
|
-
return {
|
|
889
|
-
async processEvent(context) {
|
|
890
|
-
const { project, isShellMode, shellCommands } = context;
|
|
891
|
-
const projectPath = project.path.path;
|
|
892
|
-
const ide = project.ide || "code";
|
|
893
|
-
if (isShellMode) {
|
|
894
|
-
shellCommands.push(`${ide} "${projectPath}" &`);
|
|
895
|
-
} else {
|
|
896
|
-
spawn3(ide, [projectPath], {
|
|
897
|
-
detached: true,
|
|
898
|
-
stdio: "ignore"
|
|
899
|
-
}).unref();
|
|
900
|
-
}
|
|
901
|
-
},
|
|
902
|
-
generateShellCommand(context) {
|
|
903
|
-
const projectPath = context.project.path.path;
|
|
904
|
-
const ide = context.project.ide || "code";
|
|
905
|
-
return [`${ide} "${projectPath}" &`];
|
|
906
|
-
}
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
|
-
static get tmux() {
|
|
910
|
-
return null;
|
|
911
|
-
}
|
|
912
|
-
static get help() {
|
|
913
|
-
return {
|
|
914
|
-
usage: "ide: true | false",
|
|
915
|
-
description: "Open the project in the configured IDE",
|
|
916
|
-
examples: [
|
|
917
|
-
{ config: true, description: "Enable IDE opening" },
|
|
918
|
-
{ config: false, description: "Disable IDE opening" }
|
|
919
|
-
]
|
|
920
|
-
};
|
|
921
|
-
}
|
|
922
|
-
};
|
|
923
|
-
|
|
924
|
-
// src/events/core/web.ts
|
|
925
|
-
import { spawn as spawn4 } from "child_process";
|
|
926
|
-
import { platform } from "os";
|
|
927
|
-
var WebEvent = class _WebEvent {
|
|
928
|
-
static get metadata() {
|
|
929
|
-
return {
|
|
930
|
-
name: "web",
|
|
931
|
-
displayName: "Open homepage in browser",
|
|
932
|
-
description: "Open project homepage in web browser",
|
|
933
|
-
category: "core",
|
|
934
|
-
requiresTmux: false,
|
|
935
|
-
dependencies: []
|
|
936
|
-
};
|
|
937
|
-
}
|
|
938
|
-
static get validation() {
|
|
939
|
-
return {
|
|
940
|
-
validateConfig(config) {
|
|
941
|
-
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
942
|
-
return true;
|
|
943
|
-
}
|
|
944
|
-
return "web config must be a boolean (true/false)";
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
static get configuration() {
|
|
949
|
-
return {
|
|
950
|
-
async configureInteractive() {
|
|
951
|
-
return true;
|
|
952
|
-
},
|
|
953
|
-
getDefaultConfig() {
|
|
954
|
-
return true;
|
|
955
|
-
}
|
|
956
|
-
};
|
|
957
|
-
}
|
|
958
|
-
static getOpenCommand() {
|
|
959
|
-
const os = platform();
|
|
960
|
-
switch (os) {
|
|
961
|
-
case "darwin":
|
|
962
|
-
return "open";
|
|
963
|
-
case "win32":
|
|
964
|
-
return "start";
|
|
965
|
-
default:
|
|
966
|
-
return "xdg-open";
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
static get processing() {
|
|
970
|
-
return {
|
|
971
|
-
async processEvent(context) {
|
|
972
|
-
const { project, isShellMode, shellCommands } = context;
|
|
973
|
-
const homepage = project.homepage;
|
|
974
|
-
if (!homepage) {
|
|
975
|
-
console.warn("No homepage configured for project");
|
|
976
|
-
return;
|
|
977
|
-
}
|
|
978
|
-
const openCmd = _WebEvent.getOpenCommand();
|
|
979
|
-
if (isShellMode) {
|
|
980
|
-
shellCommands.push(`${openCmd} "${homepage}" &`);
|
|
981
|
-
} else {
|
|
982
|
-
spawn4(openCmd, [homepage], {
|
|
983
|
-
detached: true,
|
|
984
|
-
stdio: "ignore"
|
|
985
|
-
}).unref();
|
|
986
|
-
}
|
|
987
|
-
},
|
|
988
|
-
generateShellCommand(context) {
|
|
989
|
-
const homepage = context.project.homepage;
|
|
990
|
-
if (!homepage) return [];
|
|
991
|
-
const openCmd = _WebEvent.getOpenCommand();
|
|
992
|
-
return [`${openCmd} "${homepage}" &`];
|
|
993
|
-
}
|
|
994
|
-
};
|
|
995
|
-
}
|
|
996
|
-
static get tmux() {
|
|
997
|
-
return null;
|
|
998
|
-
}
|
|
999
|
-
static get help() {
|
|
1000
|
-
return {
|
|
1001
|
-
usage: "web: true | false",
|
|
1002
|
-
description: "Open the project homepage in the default browser",
|
|
1003
|
-
examples: [
|
|
1004
|
-
{ config: true, description: "Enable browser opening" },
|
|
1005
|
-
{ config: false, description: "Disable browser opening" }
|
|
1006
|
-
]
|
|
1007
|
-
};
|
|
1008
|
-
}
|
|
1009
|
-
};
|
|
1010
|
-
|
|
1011
|
-
// src/events/extensions/claude.ts
|
|
1012
|
-
import { spawn as spawn5 } from "child_process";
|
|
1013
|
-
import { input, confirm } from "@inquirer/prompts";
|
|
1014
|
-
var ClaudeEvent = class _ClaudeEvent {
|
|
1015
|
-
static get metadata() {
|
|
1016
|
-
return {
|
|
1017
|
-
name: "claude",
|
|
1018
|
-
displayName: "Launch Claude Code",
|
|
1019
|
-
description: "Launch Claude Code with optional flags and configuration",
|
|
1020
|
-
category: "development",
|
|
1021
|
-
requiresTmux: true,
|
|
1022
|
-
dependencies: ["claude"]
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
static get validation() {
|
|
1026
|
-
return {
|
|
1027
|
-
validateConfig(config) {
|
|
1028
|
-
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
1029
|
-
return true;
|
|
1030
|
-
}
|
|
1031
|
-
if (typeof config === "object" && config !== null) {
|
|
1032
|
-
const cfg = config;
|
|
1033
|
-
if (cfg.flags !== void 0) {
|
|
1034
|
-
if (!Array.isArray(cfg.flags)) {
|
|
1035
|
-
return "claude.flags must be an array of strings";
|
|
1036
|
-
}
|
|
1037
|
-
for (const flag of cfg.flags) {
|
|
1038
|
-
if (typeof flag !== "string") {
|
|
1039
|
-
return "claude.flags must contain only strings";
|
|
1040
|
-
}
|
|
1041
|
-
if (!flag.startsWith("-")) {
|
|
1042
|
-
return `Invalid flag "${flag}": flags must start with - or --`;
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
if (cfg.split_terminal !== void 0 && typeof cfg.split_terminal !== "boolean") {
|
|
1047
|
-
return "claude.split_terminal must be a boolean";
|
|
1048
|
-
}
|
|
1049
|
-
return true;
|
|
1050
|
-
}
|
|
1051
|
-
return "claude config must be a boolean or object with flags/split_terminal";
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
static get configuration() {
|
|
1056
|
-
return {
|
|
1057
|
-
async configureInteractive() {
|
|
1058
|
-
const useAdvanced = await confirm({
|
|
1059
|
-
message: "Configure advanced Claude options?",
|
|
1060
|
-
default: false
|
|
1061
|
-
});
|
|
1062
|
-
if (!useAdvanced) {
|
|
1063
|
-
return true;
|
|
1064
|
-
}
|
|
1065
|
-
const flagsInput = await input({
|
|
1066
|
-
message: "Enter Claude flags (comma-separated, e.g., --resume, --debug):",
|
|
1067
|
-
default: ""
|
|
1068
|
-
});
|
|
1069
|
-
const flags = flagsInput.split(",").map((f) => f.trim()).filter((f) => f.length > 0 && f.startsWith("-"));
|
|
1070
|
-
const splitTerminal = await confirm({
|
|
1071
|
-
message: "Use split terminal layout (Claude + shell)?",
|
|
1072
|
-
default: true
|
|
1073
|
-
});
|
|
1074
|
-
if (flags.length === 0 && !splitTerminal) {
|
|
1075
|
-
return true;
|
|
1076
|
-
}
|
|
1077
|
-
const config = {};
|
|
1078
|
-
if (flags.length > 0) config.flags = flags;
|
|
1079
|
-
if (splitTerminal) config.split_terminal = splitTerminal;
|
|
1080
|
-
return config;
|
|
1081
|
-
},
|
|
1082
|
-
getDefaultConfig() {
|
|
1083
|
-
return true;
|
|
1084
|
-
}
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
static getClaudeCommand(config) {
|
|
1088
|
-
if (typeof config === "boolean" || config === void 0) {
|
|
1089
|
-
return "claude";
|
|
1090
|
-
}
|
|
1091
|
-
const flags = config.flags || [];
|
|
1092
|
-
return flags.length > 0 ? `claude ${flags.join(" ")}` : "claude";
|
|
1093
|
-
}
|
|
1094
|
-
static get processing() {
|
|
1095
|
-
return {
|
|
1096
|
-
async processEvent(context) {
|
|
1097
|
-
const { project, isShellMode, shellCommands } = context;
|
|
1098
|
-
const claudeConfig = project.events.claude;
|
|
1099
|
-
const claudeCommand = _ClaudeEvent.getClaudeCommand(claudeConfig);
|
|
1100
|
-
if (isShellMode) {
|
|
1101
|
-
shellCommands.push(claudeCommand);
|
|
1102
|
-
} else {
|
|
1103
|
-
const args = claudeCommand.split(" ").slice(1);
|
|
1104
|
-
spawn5("claude", args, {
|
|
1105
|
-
cwd: project.path.path,
|
|
1106
|
-
stdio: "inherit"
|
|
1107
|
-
});
|
|
1108
|
-
}
|
|
1109
|
-
},
|
|
1110
|
-
generateShellCommand(context) {
|
|
1111
|
-
const claudeConfig = context.project.events.claude;
|
|
1112
|
-
return [_ClaudeEvent.getClaudeCommand(claudeConfig)];
|
|
1113
|
-
}
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
static get tmux() {
|
|
1117
|
-
return {
|
|
1118
|
-
getLayoutPriority() {
|
|
1119
|
-
return 100;
|
|
1120
|
-
},
|
|
1121
|
-
contributeToLayout(enabledCommands) {
|
|
1122
|
-
if (enabledCommands.includes("npm")) {
|
|
1123
|
-
return "three-pane";
|
|
1124
|
-
}
|
|
1125
|
-
return "split";
|
|
1126
|
-
}
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
static get help() {
|
|
1130
|
-
return {
|
|
1131
|
-
usage: "claude: true | { flags: string[], split_terminal: boolean }",
|
|
1132
|
-
description: "Launch Claude Code in the project directory",
|
|
1133
|
-
examples: [
|
|
1134
|
-
{ config: true, description: "Launch Claude with defaults" },
|
|
1135
|
-
{ config: { flags: ["--resume"] }, description: "Resume previous session" },
|
|
1136
|
-
{
|
|
1137
|
-
config: { flags: ["--model", "opus"], split_terminal: true },
|
|
1138
|
-
description: "Use Opus model with split terminal"
|
|
1139
|
-
}
|
|
1140
|
-
]
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
|
-
};
|
|
1144
|
-
|
|
1145
|
-
// src/events/extensions/docker.ts
|
|
1146
|
-
import { spawn as spawn6 } from "child_process";
|
|
1147
|
-
import { input as input2 } from "@inquirer/prompts";
|
|
1148
|
-
var DockerEvent = class _DockerEvent {
|
|
1149
|
-
static get metadata() {
|
|
1150
|
-
return {
|
|
1151
|
-
name: "docker",
|
|
1152
|
-
displayName: "Docker container management",
|
|
1153
|
-
description: "Start/stop Docker containers for the project",
|
|
1154
|
-
category: "development",
|
|
1155
|
-
requiresTmux: false,
|
|
1156
|
-
dependencies: ["docker"]
|
|
1157
|
-
};
|
|
1158
|
-
}
|
|
1159
|
-
static get validation() {
|
|
1160
|
-
return {
|
|
1161
|
-
validateConfig(config) {
|
|
1162
|
-
if (typeof config === "boolean" || config === "true" || config === "false") {
|
|
1163
|
-
return true;
|
|
1164
|
-
}
|
|
1165
|
-
if (typeof config === "string") {
|
|
1166
|
-
return true;
|
|
1167
|
-
}
|
|
1168
|
-
if (typeof config === "object" && config !== null) {
|
|
1169
|
-
const cfg = config;
|
|
1170
|
-
if (cfg.compose_file !== void 0 && typeof cfg.compose_file !== "string") {
|
|
1171
|
-
return "docker.compose_file must be a string";
|
|
1172
|
-
}
|
|
1173
|
-
if (cfg.services !== void 0) {
|
|
1174
|
-
if (!Array.isArray(cfg.services)) {
|
|
1175
|
-
return "docker.services must be an array";
|
|
1176
|
-
}
|
|
1177
|
-
for (const service of cfg.services) {
|
|
1178
|
-
if (typeof service !== "string") {
|
|
1179
|
-
return "docker.services must contain only strings";
|
|
1180
|
-
}
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
return true;
|
|
1184
|
-
}
|
|
1185
|
-
return "docker config must be a boolean, string (compose file), or object";
|
|
1186
|
-
}
|
|
1187
|
-
};
|
|
1188
|
-
}
|
|
1189
|
-
static get configuration() {
|
|
1190
|
-
return {
|
|
1191
|
-
async configureInteractive() {
|
|
1192
|
-
const composeFile = await input2({
|
|
1193
|
-
message: "Enter docker-compose file path:",
|
|
1194
|
-
default: "docker-compose.yml"
|
|
1195
|
-
});
|
|
1196
|
-
const servicesInput = await input2({
|
|
1197
|
-
message: "Enter services to start (comma-separated, leave empty for all):",
|
|
1198
|
-
default: ""
|
|
1199
|
-
});
|
|
1200
|
-
const services = servicesInput.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
1201
|
-
if (composeFile === "docker-compose.yml" && services.length === 0) {
|
|
1202
|
-
return { compose_file: "docker-compose.yml" };
|
|
1203
|
-
}
|
|
1204
|
-
if (services.length === 0) {
|
|
1205
|
-
return composeFile;
|
|
1206
|
-
}
|
|
1207
|
-
return {
|
|
1208
|
-
compose_file: composeFile,
|
|
1209
|
-
services
|
|
1210
|
-
};
|
|
1211
|
-
},
|
|
1212
|
-
getDefaultConfig() {
|
|
1213
|
-
return { compose_file: "docker-compose.yml" };
|
|
1214
|
-
}
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
static getDockerCommand(config) {
|
|
1218
|
-
if (typeof config === "boolean" || config === void 0) {
|
|
1219
|
-
return "docker-compose up -d";
|
|
1220
|
-
}
|
|
1221
|
-
if (typeof config === "string") {
|
|
1222
|
-
return `docker-compose -f ${config} up -d`;
|
|
1223
|
-
}
|
|
1224
|
-
const composeFile = config.compose_file || "docker-compose.yml";
|
|
1225
|
-
const services = config.services?.join(" ") || "";
|
|
1226
|
-
return `docker-compose -f ${composeFile} up -d ${services}`.trim();
|
|
1227
|
-
}
|
|
1228
|
-
static get processing() {
|
|
1229
|
-
return {
|
|
1230
|
-
async processEvent(context) {
|
|
1231
|
-
const { project, isShellMode, shellCommands } = context;
|
|
1232
|
-
const dockerConfig = project.events.docker;
|
|
1233
|
-
const dockerCommand = _DockerEvent.getDockerCommand(
|
|
1234
|
-
dockerConfig
|
|
1235
|
-
);
|
|
1236
|
-
if (isShellMode) {
|
|
1237
|
-
shellCommands.push(dockerCommand);
|
|
1238
|
-
} else {
|
|
1239
|
-
const [cmd, ...args] = dockerCommand.split(" ");
|
|
1240
|
-
spawn6(cmd, args, {
|
|
1241
|
-
cwd: project.path.path,
|
|
1242
|
-
stdio: "inherit"
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
},
|
|
1246
|
-
generateShellCommand(context) {
|
|
1247
|
-
const dockerConfig = context.project.events.docker;
|
|
1248
|
-
return [_DockerEvent.getDockerCommand(dockerConfig)];
|
|
1249
|
-
}
|
|
1250
|
-
};
|
|
1251
|
-
}
|
|
1252
|
-
static get tmux() {
|
|
1253
|
-
return null;
|
|
1254
|
-
}
|
|
1255
|
-
static get help() {
|
|
1256
|
-
return {
|
|
1257
|
-
usage: 'docker: true | "compose-file.yml" | { compose_file: string, services?: string[] }',
|
|
1258
|
-
description: "Start Docker containers for the project",
|
|
1259
|
-
examples: [
|
|
1260
|
-
{ config: true, description: "Use default docker-compose.yml" },
|
|
1261
|
-
{ config: "docker-compose.dev.yml", description: "Use custom compose file" },
|
|
1262
|
-
{
|
|
1263
|
-
config: { compose_file: "docker-compose.yml", services: ["web", "db"] },
|
|
1264
|
-
description: "Start specific services"
|
|
1265
|
-
}
|
|
1266
|
-
]
|
|
1267
|
-
};
|
|
1268
|
-
}
|
|
1269
|
-
};
|
|
1270
|
-
|
|
1271
|
-
// src/events/registry.ts
|
|
1272
|
-
init_npm();
|
|
1273
|
-
var ALL_EVENTS = [CwdEvent, IdeEvent, WebEvent, ClaudeEvent, DockerEvent, NpmEvent];
|
|
1274
|
-
var EventRegistryClass = class {
|
|
1275
|
-
_events = /* @__PURE__ */ new Map();
|
|
1276
|
-
_initialized = false;
|
|
1277
|
-
/**
|
|
1278
|
-
* Initialize the registry by registering all events
|
|
1279
|
-
*/
|
|
1280
|
-
async initialize() {
|
|
1281
|
-
if (this._initialized) return;
|
|
1282
|
-
this.registerEvents();
|
|
1283
|
-
this._initialized = true;
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* Register all event classes
|
|
1287
|
-
*/
|
|
1288
|
-
registerEvents() {
|
|
1289
|
-
for (const EventClass of ALL_EVENTS) {
|
|
1290
|
-
if (this.isValidEvent(EventClass)) {
|
|
1291
|
-
const metadata = EventClass.metadata;
|
|
1292
|
-
this._events.set(metadata.name, EventClass);
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
/**
|
|
1297
|
-
* Validate if a class is a proper event
|
|
1298
|
-
*/
|
|
1299
|
-
isValidEvent(EventClass) {
|
|
1300
|
-
try {
|
|
1301
|
-
if (typeof EventClass !== "function") return false;
|
|
1302
|
-
const metadata = EventClass.metadata;
|
|
1303
|
-
return metadata !== void 0 && typeof metadata.name === "string" && typeof metadata.displayName === "string" && typeof EventClass.validation === "object" && typeof EventClass.configuration === "object" && typeof EventClass.processing === "object";
|
|
1304
|
-
} catch {
|
|
1305
|
-
return false;
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* Get all valid event names from registered events
|
|
1310
|
-
*/
|
|
1311
|
-
getValidEventNames() {
|
|
1312
|
-
this.ensureInitialized();
|
|
1313
|
-
return Array.from(this._events.keys());
|
|
1314
|
-
}
|
|
1315
|
-
/**
|
|
1316
|
-
* Get event by name
|
|
1317
|
-
*/
|
|
1318
|
-
getEventByName(name) {
|
|
1319
|
-
this.ensureInitialized();
|
|
1320
|
-
return this._events.get(name) ?? null;
|
|
1321
|
-
}
|
|
1322
|
-
/**
|
|
1323
|
-
* Get all events for management UI
|
|
1324
|
-
*/
|
|
1325
|
-
getEventsForManageUI() {
|
|
1326
|
-
this.ensureInitialized();
|
|
1327
|
-
const events = [];
|
|
1328
|
-
for (const [name, EventClass] of this._events) {
|
|
1329
|
-
const metadata = EventClass.metadata;
|
|
1330
|
-
events.push({
|
|
1331
|
-
name: metadata.displayName,
|
|
1332
|
-
value: name,
|
|
1333
|
-
description: metadata.description
|
|
1334
|
-
});
|
|
1335
|
-
}
|
|
1336
|
-
return events.sort((a, b) => a.name.localeCompare(b.name));
|
|
1337
|
-
}
|
|
1338
|
-
/**
|
|
1339
|
-
* Get events that support tmux integration
|
|
1340
|
-
*/
|
|
1341
|
-
getTmuxEnabledEvents() {
|
|
1342
|
-
this.ensureInitialized();
|
|
1343
|
-
const tmuxEvents = [];
|
|
1344
|
-
for (const [name, EventClass] of this._events) {
|
|
1345
|
-
const tmux = EventClass.tmux;
|
|
1346
|
-
if (tmux) {
|
|
1347
|
-
tmuxEvents.push({
|
|
1348
|
-
name,
|
|
1349
|
-
event: EventClass,
|
|
1350
|
-
priority: tmux.getLayoutPriority ? tmux.getLayoutPriority() : 0
|
|
1351
|
-
});
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
return tmuxEvents.sort((a, b) => b.priority - a.priority);
|
|
1355
|
-
}
|
|
1356
|
-
/**
|
|
1357
|
-
* Get all available events with their metadata
|
|
1358
|
-
*/
|
|
1359
|
-
getAllEvents() {
|
|
1360
|
-
this.ensureInitialized();
|
|
1361
|
-
const events = [];
|
|
1362
|
-
for (const [name, EventClass] of this._events) {
|
|
1363
|
-
const typedClass = EventClass;
|
|
1364
|
-
events.push({
|
|
1365
|
-
name,
|
|
1366
|
-
metadata: typedClass.metadata,
|
|
1367
|
-
hasValidation: !!typedClass.validation,
|
|
1368
|
-
hasConfiguration: !!typedClass.configuration,
|
|
1369
|
-
hasProcessing: !!typedClass.processing,
|
|
1370
|
-
hasTmux: !!typedClass.tmux,
|
|
1371
|
-
hasHelp: !!typedClass.help
|
|
1372
|
-
});
|
|
1373
|
-
}
|
|
1374
|
-
return events;
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* Ensure registry is initialized
|
|
1378
|
-
*/
|
|
1379
|
-
ensureInitialized() {
|
|
1380
|
-
if (!this._initialized) {
|
|
1381
|
-
throw new Error("EventRegistry must be initialized before use. Call initialize() first.");
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
/**
|
|
1385
|
-
* Clear the registry (useful for testing)
|
|
1386
|
-
*/
|
|
1387
|
-
clear() {
|
|
1388
|
-
this._events.clear();
|
|
1389
|
-
this._initialized = false;
|
|
1390
|
-
}
|
|
1391
|
-
};
|
|
1392
|
-
var EventRegistry = new EventRegistryClass();
|
|
1393
|
-
|
|
1394
1892
|
// src/commands/open.ts
|
|
1893
|
+
init_registry();
|
|
1395
1894
|
function createOpenCommand(ctx) {
|
|
1396
1895
|
const { config, log } = ctx;
|
|
1397
1896
|
const command = new Command("open").description("Open a project by passing its project id").argument("[project]", "The id of the project to open (supports project:command syntax)").option("-d, --debug", "Enable debug logging").option("-n, --dry-run", "Show what would happen without executing").option("--shell", "Output shell commands instead of spawning processes").action(async (projectArg, options) => {
|
|
@@ -1806,8 +2305,9 @@ function createConfigCommand(ctx) {
|
|
|
1806
2305
|
}
|
|
1807
2306
|
|
|
1808
2307
|
// src/commands/manage.ts
|
|
2308
|
+
init_registry();
|
|
1809
2309
|
import { Command as Command6 } from "commander";
|
|
1810
|
-
import { select as select2, input as input5, confirm as
|
|
2310
|
+
import { select as select2, input as input5, confirm as confirm4, checkbox as checkbox2 } from "@inquirer/prompts";
|
|
1811
2311
|
import File5 from "phylo";
|
|
1812
2312
|
var IDE_CHOICES2 = [
|
|
1813
2313
|
{ name: "Visual Studio Code", value: "vscode" },
|
|
@@ -1939,7 +2439,7 @@ async function createProject(ctx) {
|
|
|
1939
2439
|
}
|
|
1940
2440
|
console.log("\nProject configuration:");
|
|
1941
2441
|
console.log(JSON.stringify(projectConfig, null, 2));
|
|
1942
|
-
const confirmed = await
|
|
2442
|
+
const confirmed = await confirm4({
|
|
1943
2443
|
message: "Save this project?",
|
|
1944
2444
|
default: true
|
|
1945
2445
|
});
|
|
@@ -1990,7 +2490,7 @@ async function editProject(ctx) {
|
|
|
1990
2490
|
message: "Project homepage URL:",
|
|
1991
2491
|
default: project.homepage || ""
|
|
1992
2492
|
});
|
|
1993
|
-
const keepEvents = await
|
|
2493
|
+
const keepEvents = await confirm4({
|
|
1994
2494
|
message: "Keep existing event configuration?",
|
|
1995
2495
|
default: true
|
|
1996
2496
|
});
|
|
@@ -2029,7 +2529,7 @@ async function editProject(ctx) {
|
|
|
2029
2529
|
}
|
|
2030
2530
|
console.log("\nUpdated configuration:");
|
|
2031
2531
|
console.log(JSON.stringify(updatedConfig, null, 2));
|
|
2032
|
-
const confirmed = await
|
|
2532
|
+
const confirmed = await confirm4({
|
|
2033
2533
|
message: "Save changes?",
|
|
2034
2534
|
default: true
|
|
2035
2535
|
});
|
|
@@ -2052,7 +2552,7 @@ async function deleteProject(ctx) {
|
|
|
2052
2552
|
message: "Select project to delete:",
|
|
2053
2553
|
choices: projectNames.map((n) => ({ name: n, value: n }))
|
|
2054
2554
|
});
|
|
2055
|
-
const confirmed = await
|
|
2555
|
+
const confirmed = await confirm4({
|
|
2056
2556
|
message: `Are you sure you want to delete '${name}'?`,
|
|
2057
2557
|
default: false
|
|
2058
2558
|
});
|
|
@@ -2086,7 +2586,7 @@ import { Command as Command7 } from "commander";
|
|
|
2086
2586
|
import { existsSync, readFileSync } from "fs";
|
|
2087
2587
|
import { basename, resolve } from "path";
|
|
2088
2588
|
import File6 from "phylo";
|
|
2089
|
-
import { confirm as
|
|
2589
|
+
import { confirm as confirm5 } from "@inquirer/prompts";
|
|
2090
2590
|
function createAddCommand(ctx) {
|
|
2091
2591
|
const { log } = ctx;
|
|
2092
2592
|
return new Command7("add").description("Add a project from a directory path").argument("[path]", "Path to the project directory (defaults to current directory)", ".").option("-d, --debug", "Enable debug logging").option("-n, --name <name>", "Override the detected project name").option(
|
|
@@ -2130,7 +2630,7 @@ async function addProject(pathArg, options, ctx) {
|
|
|
2130
2630
|
process.exit(1);
|
|
2131
2631
|
}
|
|
2132
2632
|
if (projectName in projects && !options.force) {
|
|
2133
|
-
const overwrite = await
|
|
2633
|
+
const overwrite = await confirm5({
|
|
2134
2634
|
message: `Project '${projectName}' already exists. Overwrite?`,
|
|
2135
2635
|
default: false
|
|
2136
2636
|
});
|
|
@@ -2245,32 +2745,41 @@ function createCli() {
|
|
|
2245
2745
|
config.set("pkg", packageJson);
|
|
2246
2746
|
EnvironmentRecognizer.configure(config, log);
|
|
2247
2747
|
const completion = setupCompletion(config);
|
|
2248
|
-
program2.name("workon").description("Work on something great!").version(packageJson.version).option("-d, --debug", "Enable debug logging").option("--completion", "Setup shell tab completion").option("--shell", "Output shell commands for evaluation").option("--init", "Generate shell integration function").hook("preAction", (thisCommand) => {
|
|
2748
|
+
program2.name("workon").description("Work on something great!").version(packageJson.version).argument("[project]", "Project name to open (supports project:command syntax)").option("-d, --debug", "Enable debug logging").option("--completion", "Setup shell tab completion").option("--shell", "Output shell commands for evaluation").option("--init", "Generate shell integration function").hook("preAction", (thisCommand) => {
|
|
2249
2749
|
const opts = thisCommand.opts();
|
|
2250
2750
|
if (opts.debug) {
|
|
2251
2751
|
log.setLogLevel("debug");
|
|
2252
2752
|
}
|
|
2253
|
-
}).action(
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2753
|
+
}).action(
|
|
2754
|
+
async (project, options) => {
|
|
2755
|
+
if (options.debug) {
|
|
2756
|
+
log.setLogLevel("debug");
|
|
2757
|
+
}
|
|
2758
|
+
if (options.completion) {
|
|
2759
|
+
log.debug("Setting up command-line completion");
|
|
2760
|
+
completion.setupShellInitFile();
|
|
2761
|
+
return;
|
|
2762
|
+
}
|
|
2763
|
+
if (options.init) {
|
|
2764
|
+
log.debug("Generating shell integration function");
|
|
2765
|
+
outputShellInit(program2);
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
if (project) {
|
|
2769
|
+
const args = ["open", project];
|
|
2770
|
+
if (options.shell) args.push("--shell");
|
|
2771
|
+
if (options.debug) args.push("--debug");
|
|
2772
|
+
await program2.parseAsync(["node", "workon", ...args]);
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
const environment = await EnvironmentRecognizer.recognize(File7.cwd());
|
|
2776
|
+
program2.setOptionValue("_environment", environment);
|
|
2777
|
+
program2.setOptionValue("_config", config);
|
|
2778
|
+
program2.setOptionValue("_log", log);
|
|
2779
|
+
const { runInteractive: runInteractive2 } = await Promise.resolve().then(() => (init_interactive(), interactive_exports));
|
|
2780
|
+
await runInteractive2({ config, log, environment });
|
|
2266
2781
|
}
|
|
2267
|
-
|
|
2268
|
-
program2.setOptionValue("_environment", environment);
|
|
2269
|
-
program2.setOptionValue("_config", config);
|
|
2270
|
-
program2.setOptionValue("_log", log);
|
|
2271
|
-
const { runInteractive: runInteractive2 } = await Promise.resolve().then(() => (init_interactive(), interactive_exports));
|
|
2272
|
-
await runInteractive2({ config, log, environment });
|
|
2273
|
-
});
|
|
2782
|
+
);
|
|
2274
2783
|
program2.setOptionValue("_config", config);
|
|
2275
2784
|
program2.setOptionValue("_log", log);
|
|
2276
2785
|
program2.addCommand(createOpenCommand({ config, log }));
|