jfl 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -4
- package/dist/commands/init.d.ts +13 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +214 -127
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +17 -1
- package/dist/commands/setup.js.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-session.d.ts.map +1 -1
- package/dist/lib/agent-session.js +66 -33
- package/dist/lib/agent-session.js.map +1 -1
- package/dist/lib/setup/agent-generator.d.ts +5 -0
- package/dist/lib/setup/agent-generator.d.ts.map +1 -1
- package/dist/lib/setup/agent-generator.js +209 -3
- package/dist/lib/setup/agent-generator.js.map +1 -1
- package/package.json +1 -1
- package/scripts/train/v2/domain.json +62 -14
package/README.md
CHANGED
|
@@ -4,10 +4,6 @@
|
|
|
4
4
|
<strong>Multiplayer AI. Persistent context. Self-driving agents.</strong>
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
<p align="center">
|
|
8
|
-
<img src="https://raw.githubusercontent.com/402goose/jfl-cli/main/.github/assets/jfl-kanban-demo.gif" width="700" alt="JFL Dashboard — Kanban, Eval, Agents" />
|
|
9
|
-
</p>
|
|
10
|
-
|
|
11
7
|
<p align="center">
|
|
12
8
|
<a href="https://www.npmjs.com/package/jfl"><img src="https://img.shields.io/npm/v/jfl.svg" alt="npm version" /></a>
|
|
13
9
|
<a href="https://github.com/402goose/jfl-cli/actions"><img src="https://img.shields.io/github/actions/workflow/status/402goose/jfl-cli/ci.yml?branch=main" alt="CI" /></a>
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
interface InitOptions {
|
|
2
2
|
name?: string;
|
|
3
|
-
|
|
3
|
+
type?: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
productRepo?: string;
|
|
6
|
+
service?: string[];
|
|
7
|
+
ci?: boolean;
|
|
8
|
+
interactive?: boolean;
|
|
9
|
+
search?: boolean;
|
|
10
|
+
services?: boolean;
|
|
11
|
+
launch?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function initCommand(options?: InitOptions): Promise<void>;
|
|
14
|
+
export {};
|
|
4
15
|
//# sourceMappingURL=init.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAoBA,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAoBA,UAAU,WAAW;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,EAAE,CAAC,EAAE,OAAO,CAAA;IACZ,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE,WAAW,iBA6lCtD"}
|
package/dist/commands/init.js
CHANGED
|
@@ -16,6 +16,7 @@ import { persistProjectPort } from "../utils/context-hub-port.js";
|
|
|
16
16
|
import { ensureDaemonInstalled } from "./context-hub.js";
|
|
17
17
|
const TEMPLATE_REPO = "https://github.com/402goose/jfl-template.git";
|
|
18
18
|
export async function initCommand(options) {
|
|
19
|
+
const interactive = options?.interactive !== false && process.stdout.isTTY !== false;
|
|
19
20
|
// Start Clawdbot-style flow
|
|
20
21
|
p.intro(chalk.hex("#FFD700")("┌ JFL - Initialize Workspace"));
|
|
21
22
|
// Check authentication - owner needs to be verified
|
|
@@ -49,7 +50,7 @@ export async function initCommand(options) {
|
|
|
49
50
|
}
|
|
50
51
|
// Get project name
|
|
51
52
|
let projectName = options?.name;
|
|
52
|
-
if (!projectName) {
|
|
53
|
+
if (!projectName && interactive) {
|
|
53
54
|
const name = await p.text({
|
|
54
55
|
message: "Project name:",
|
|
55
56
|
placeholder: "my-project-gtm",
|
|
@@ -67,17 +68,27 @@ export async function initCommand(options) {
|
|
|
67
68
|
}
|
|
68
69
|
projectName = name;
|
|
69
70
|
}
|
|
71
|
+
if (!projectName) {
|
|
72
|
+
// Derive from current directory name
|
|
73
|
+
projectName = basename(process.cwd()).replace(/[^a-z0-9-]/g, "-").toLowerCase() || "my-project";
|
|
74
|
+
if (!interactive) {
|
|
75
|
+
p.log.info(`Using project name: ${projectName} (pass -n to override)`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
70
78
|
// Ask workspace type
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
let workspaceType = options?.type ?? "gtm";
|
|
80
|
+
if (interactive && !options?.type) {
|
|
81
|
+
workspaceType = await p.select({
|
|
82
|
+
message: "Workspace type:",
|
|
83
|
+
options: [
|
|
84
|
+
{ label: "GTM", value: "gtm", hint: "Single product workspace" },
|
|
85
|
+
{ label: "Portfolio", value: "portfolio", hint: "Manages multiple products" },
|
|
86
|
+
],
|
|
87
|
+
});
|
|
88
|
+
if (p.isCancel(workspaceType)) {
|
|
89
|
+
p.cancel("Setup cancelled.");
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
81
92
|
}
|
|
82
93
|
const isPortfolio = workspaceType === "portfolio";
|
|
83
94
|
// If cwd already matches the project name, initialize in place
|
|
@@ -185,13 +196,16 @@ export async function initCommand(options) {
|
|
|
185
196
|
validationSpinner.warn("Could not validate settings.json");
|
|
186
197
|
}
|
|
187
198
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
199
|
+
let description = options?.description || projectName;
|
|
200
|
+
if (interactive) {
|
|
201
|
+
description = await p.text({
|
|
202
|
+
message: "One-line description:",
|
|
203
|
+
placeholder: "My project",
|
|
204
|
+
});
|
|
205
|
+
if (p.isCancel(description)) {
|
|
206
|
+
p.cancel("Setup cancelled.");
|
|
207
|
+
process.exit(0);
|
|
208
|
+
}
|
|
195
209
|
}
|
|
196
210
|
let productRepo = null;
|
|
197
211
|
const portfolioChildGtms = [];
|
|
@@ -200,10 +214,13 @@ export async function initCommand(options) {
|
|
|
200
214
|
p.note("Register existing GTM workspaces under this portfolio.\n" +
|
|
201
215
|
"Each GTM gets its own eval pipeline, journals, and event bus.\n" +
|
|
202
216
|
"The portfolio aggregates across all of them.", chalk.hex("#FFA500")("Portfolio Setup"));
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
217
|
+
let registerGtms = false;
|
|
218
|
+
if (interactive) {
|
|
219
|
+
registerGtms = await p.confirm({
|
|
220
|
+
message: "Register GTM workspaces now?",
|
|
221
|
+
initialValue: true,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
207
224
|
if (!p.isCancel(registerGtms) && registerGtms) {
|
|
208
225
|
let adding = true;
|
|
209
226
|
let gtmCount = 0;
|
|
@@ -262,44 +279,53 @@ export async function initCommand(options) {
|
|
|
262
279
|
}
|
|
263
280
|
if (!isPortfolio) {
|
|
264
281
|
// Ask about product repo (registered as service, NOT a submodule)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
282
|
+
let productChoice = "later";
|
|
283
|
+
if (interactive) {
|
|
284
|
+
productChoice = await p.select({
|
|
285
|
+
message: "Product repo:",
|
|
286
|
+
options: [
|
|
287
|
+
{
|
|
288
|
+
label: "I have an existing repo",
|
|
289
|
+
value: "existing",
|
|
290
|
+
hint: "Register as a service"
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
label: "Create a new repo for me",
|
|
294
|
+
value: "create",
|
|
295
|
+
hint: "Requires: gh CLI"
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
label: "I'll add it later",
|
|
299
|
+
value: "later",
|
|
300
|
+
hint: "Recommended if unsure"
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
});
|
|
304
|
+
if (p.isCancel(productChoice)) {
|
|
305
|
+
p.cancel("Setup cancelled.");
|
|
306
|
+
process.exit(0);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else if (options?.productRepo) {
|
|
310
|
+
productChoice = "existing";
|
|
288
311
|
}
|
|
289
312
|
if (productChoice !== "later") {
|
|
290
313
|
if (productChoice === "existing") {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
314
|
+
let repoUrl = options?.productRepo || "";
|
|
315
|
+
if (interactive) {
|
|
316
|
+
repoUrl = await p.text({
|
|
317
|
+
message: "Product repo URL:",
|
|
318
|
+
placeholder: "https://github.com/user/repo.git",
|
|
319
|
+
validate: (input) => {
|
|
320
|
+
if (!input.trim()) {
|
|
321
|
+
return "Please enter a repo URL";
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
});
|
|
325
|
+
if (p.isCancel(repoUrl)) {
|
|
326
|
+
p.cancel("Setup cancelled.");
|
|
327
|
+
process.exit(0);
|
|
328
|
+
}
|
|
303
329
|
}
|
|
304
330
|
productRepo = repoUrl;
|
|
305
331
|
p.log.success(`Product repo registered: ${productRepo}`);
|
|
@@ -314,37 +340,43 @@ export async function initCommand(options) {
|
|
|
314
340
|
const maxAttempts = 3;
|
|
315
341
|
while (!repoCreated && attemptCount < maxAttempts) {
|
|
316
342
|
attemptCount++;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
?
|
|
321
|
-
:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
343
|
+
let repoName = projectName.replace(/-gtm$/, "");
|
|
344
|
+
if (interactive) {
|
|
345
|
+
repoName = await p.text({
|
|
346
|
+
message: attemptCount > 1 ? "Try a different name:" : "New repo name:",
|
|
347
|
+
placeholder: attemptCount > 1
|
|
348
|
+
? `${projectName.replace(/-gtm$/, "")}-${attemptCount}`
|
|
349
|
+
: projectName.replace(/-gtm$/, ""),
|
|
350
|
+
validate: (input) => {
|
|
351
|
+
if (!input.trim()) {
|
|
352
|
+
return "Please enter a repo name";
|
|
353
|
+
}
|
|
354
|
+
if (/\s/.test(input)) {
|
|
355
|
+
return "Repo names cannot contain spaces. Use hyphens instead (e.g., 'my-project')";
|
|
356
|
+
}
|
|
357
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(input)) {
|
|
358
|
+
return "Repo names can only contain letters, numbers, hyphens, underscores, and dots";
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
if (p.isCancel(repoName)) {
|
|
363
|
+
p.cancel("Setup cancelled.");
|
|
364
|
+
process.exit(0);
|
|
365
|
+
}
|
|
337
366
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
367
|
+
let visibility = "private";
|
|
368
|
+
if (interactive) {
|
|
369
|
+
visibility = await p.select({
|
|
370
|
+
message: "Visibility:",
|
|
371
|
+
options: [
|
|
372
|
+
{ label: "Private", value: "private" },
|
|
373
|
+
{ label: "Public", value: "public" },
|
|
374
|
+
],
|
|
375
|
+
});
|
|
376
|
+
if (p.isCancel(visibility)) {
|
|
377
|
+
p.cancel("Setup cancelled.");
|
|
378
|
+
process.exit(0);
|
|
379
|
+
}
|
|
348
380
|
}
|
|
349
381
|
const createSpinner = ora("Creating product repo...").start();
|
|
350
382
|
try {
|
|
@@ -373,10 +405,13 @@ export async function initCommand(options) {
|
|
|
373
405
|
console.log(chalk.yellow("That name is already taken on your GitHub"));
|
|
374
406
|
console.log("");
|
|
375
407
|
if (attemptCount < maxAttempts) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
408
|
+
let retry = false;
|
|
409
|
+
if (interactive) {
|
|
410
|
+
retry = await p.confirm({
|
|
411
|
+
message: "Try a different name?",
|
|
412
|
+
initialValue: true,
|
|
413
|
+
});
|
|
414
|
+
}
|
|
380
415
|
if (p.isCancel(retry) || !retry) {
|
|
381
416
|
p.log.info("Skipping repo creation. Register it later with:");
|
|
382
417
|
p.log.info(" jfl services add <repo-url>");
|
|
@@ -507,13 +542,16 @@ export async function initCommand(options) {
|
|
|
507
542
|
// Offer semantic search setup (before service onboarding for smoother flow)
|
|
508
543
|
p.note("Search your workspace by meaning, not just keywords.\n" +
|
|
509
544
|
"Requires qmd (local search engine) + ~1.5GB for models.", chalk.hex("#FFA500")("📚 Semantic Search"));
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
545
|
+
let enableSearch = options?.search !== false;
|
|
546
|
+
if (interactive) {
|
|
547
|
+
enableSearch = await p.confirm({
|
|
548
|
+
message: "Enable semantic search?",
|
|
549
|
+
initialValue: true,
|
|
550
|
+
});
|
|
551
|
+
if (p.isCancel(enableSearch)) {
|
|
552
|
+
p.cancel("Setup cancelled.");
|
|
553
|
+
process.exit(0);
|
|
554
|
+
}
|
|
517
555
|
}
|
|
518
556
|
if (enableSearch) {
|
|
519
557
|
// Check if qmd is installed
|
|
@@ -536,13 +574,16 @@ export async function initCommand(options) {
|
|
|
536
574
|
// bun not installed
|
|
537
575
|
}
|
|
538
576
|
if (bunInstalled) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
577
|
+
let installQmd = false;
|
|
578
|
+
if (interactive) {
|
|
579
|
+
installQmd = await p.confirm({
|
|
580
|
+
message: "qmd not found. Install it now?",
|
|
581
|
+
initialValue: true,
|
|
582
|
+
});
|
|
583
|
+
if (p.isCancel(installQmd)) {
|
|
584
|
+
p.cancel("Setup cancelled.");
|
|
585
|
+
process.exit(0);
|
|
586
|
+
}
|
|
546
587
|
}
|
|
547
588
|
if (installQmd) {
|
|
548
589
|
const qmdSpinner = ora("Installing qmd...").start();
|
|
@@ -606,19 +647,44 @@ export async function initCommand(options) {
|
|
|
606
647
|
// Offer service onboarding
|
|
607
648
|
p.note("Set up service agents for your repos (API, web app, etc.).\n" +
|
|
608
649
|
"Each service gets its own agent that you can invoke via @-mentions.", chalk.hex("#FFA500")("Service Agents"));
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
650
|
+
let onboardServices = !!(options?.services === true || (options?.service && options.service.length > 0));
|
|
651
|
+
if (interactive && !options?.service?.length) {
|
|
652
|
+
onboardServices = await p.confirm({
|
|
653
|
+
message: "Onboard services now?",
|
|
654
|
+
initialValue: true,
|
|
655
|
+
});
|
|
656
|
+
if (p.isCancel(onboardServices)) {
|
|
657
|
+
p.cancel("Setup cancelled.");
|
|
658
|
+
process.exit(0);
|
|
659
|
+
}
|
|
616
660
|
}
|
|
617
661
|
const onboardedServices = [];
|
|
618
662
|
let serviceCount = 0;
|
|
619
|
-
|
|
663
|
+
// Auto-onboard services from --service flags (non-interactive)
|
|
664
|
+
if (options?.service && options.service.length > 0) {
|
|
665
|
+
for (const svcPath of options.service) {
|
|
666
|
+
const serviceSpinner = ora(`Onboarding service: ${svcPath}`).start();
|
|
667
|
+
try {
|
|
668
|
+
const metadata = extractServiceMetadata(svcPath);
|
|
669
|
+
const agentDef = generateAgentDefinition(metadata, svcPath, projectPath);
|
|
670
|
+
writeAgentDefinition(agentDef, projectPath);
|
|
671
|
+
writeSkillFiles(metadata, svcPath, projectPath);
|
|
672
|
+
onboardedServices.push(metadata.name);
|
|
673
|
+
serviceCount++;
|
|
674
|
+
serviceSpinner.succeed(`Service onboarded: ${metadata.name}`);
|
|
675
|
+
}
|
|
676
|
+
catch (err) {
|
|
677
|
+
serviceSpinner.fail(`Failed to onboard ${svcPath}: ${err.message}`);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (onboardServices && !options?.service?.length) {
|
|
620
682
|
let addingServices = true;
|
|
621
683
|
while (addingServices) {
|
|
684
|
+
if (!interactive) {
|
|
685
|
+
addingServices = false;
|
|
686
|
+
break;
|
|
687
|
+
}
|
|
622
688
|
const servicePath = await p.text({
|
|
623
689
|
message: onboardedServices.length === 0
|
|
624
690
|
? "Service path or git URL:"
|
|
@@ -859,26 +925,47 @@ export async function initCommand(options) {
|
|
|
859
925
|
});
|
|
860
926
|
}
|
|
861
927
|
p.note(successMessage, chalk.hex("#00FF88")("✅ GTM workspace initialized!"));
|
|
862
|
-
//
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
928
|
+
// Deploy CI workflows if --ci flag
|
|
929
|
+
if (options?.ci) {
|
|
930
|
+
const ciSpinner = ora("Deploying eval + review CI workflows...").start();
|
|
931
|
+
try {
|
|
932
|
+
const { ciSetupCommand } = await import("./ci-setup.js");
|
|
933
|
+
const origCwd = process.cwd();
|
|
934
|
+
process.chdir(projectPath);
|
|
935
|
+
await ciSetupCommand();
|
|
936
|
+
process.chdir(origCwd);
|
|
937
|
+
ciSpinner.succeed("CI workflows deployed (jfl-eval.yml + jfl-review.yml)");
|
|
938
|
+
}
|
|
939
|
+
catch (err) {
|
|
940
|
+
ciSpinner.fail(`CI setup failed: ${err.message}`);
|
|
941
|
+
p.log.info("Run manually later: jfl ci setup");
|
|
942
|
+
}
|
|
870
943
|
}
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
944
|
+
// Ask about launching Claude Code
|
|
945
|
+
let launchClaude = options?.launch === true;
|
|
946
|
+
if (interactive) {
|
|
947
|
+
launchClaude = await p.confirm({
|
|
948
|
+
message: "Start Claude Code now?",
|
|
875
949
|
initialValue: true,
|
|
876
950
|
});
|
|
877
|
-
if (p.isCancel(
|
|
951
|
+
if (p.isCancel(launchClaude)) {
|
|
878
952
|
p.cancel("Setup cancelled.");
|
|
879
953
|
process.exit(0);
|
|
880
954
|
}
|
|
881
|
-
|
|
955
|
+
}
|
|
956
|
+
let dangerMode = false;
|
|
957
|
+
if (launchClaude) {
|
|
958
|
+
if (interactive) {
|
|
959
|
+
const dangerouslySkip = await p.confirm({
|
|
960
|
+
message: "Skip permission prompts? (dangerously-skip-permissions)",
|
|
961
|
+
initialValue: true,
|
|
962
|
+
});
|
|
963
|
+
if (p.isCancel(dangerouslySkip)) {
|
|
964
|
+
p.cancel("Setup cancelled.");
|
|
965
|
+
process.exit(0);
|
|
966
|
+
}
|
|
967
|
+
dangerMode = dangerouslySkip;
|
|
968
|
+
}
|
|
882
969
|
}
|
|
883
970
|
if (launchClaude) {
|
|
884
971
|
p.log.info(chalk.hex("#FFA500")("🚀 Launching Claude Code..."));
|