feinai 0.5.7 → 0.5.8
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/package.json +1 -1
- package/src/cli.ts +45 -39
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
type OutputFormat,
|
|
49
49
|
} from "./format";
|
|
50
50
|
|
|
51
|
-
const VERSION = "0.5.
|
|
51
|
+
const VERSION = "0.5.8";
|
|
52
52
|
|
|
53
53
|
interface ParsedArgs {
|
|
54
54
|
positional: string[];
|
|
@@ -123,7 +123,7 @@ function detectFormat(args: ParsedArgs): OutputFormat {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
* Build a stable identifier for whoever is invoking
|
|
126
|
+
* Build a stable identifier for whoever is invoking feinai.
|
|
127
127
|
*
|
|
128
128
|
* Priority:
|
|
129
129
|
* 1. $FEINAI_USER env var (explicit override — used by agents to identify themselves)
|
|
@@ -163,7 +163,7 @@ function getCurrentUser(): string {
|
|
|
163
163
|
function ensureDb(): ReturnType<typeof openDb> {
|
|
164
164
|
if (!findDbPath()) {
|
|
165
165
|
console.error(
|
|
166
|
-
"Error: no .tasca/tasca.db found. Run '
|
|
166
|
+
"Error: no .tasca/tasca.db found. Run 'feinai init' to create one.",
|
|
167
167
|
);
|
|
168
168
|
process.exit(2);
|
|
169
169
|
}
|
|
@@ -182,10 +182,10 @@ function readContentFromArgs(args: ParsedArgs): string | undefined {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
function showHelp(): void {
|
|
185
|
-
console.log(`
|
|
185
|
+
console.log(`feinai v\${VERSION} — task & spec manager for AI agents and humans
|
|
186
186
|
|
|
187
187
|
USAGE:
|
|
188
|
-
|
|
188
|
+
feinai <command> [options]
|
|
189
189
|
|
|
190
190
|
DB MANAGEMENT:
|
|
191
191
|
init [--force] Create .tasca/tasca.db in cwd
|
|
@@ -250,7 +250,7 @@ SERVER:
|
|
|
250
250
|
--port <N> Port (default: 8272 — TASC on phone keypad)
|
|
251
251
|
--host <addr> Bind host (default: 127.0.0.1)
|
|
252
252
|
server --daemon / -d Start detached (no job control noise)
|
|
253
|
-
server --down Stop the running
|
|
253
|
+
server --down Stop the running feinai server (by port)
|
|
254
254
|
|
|
255
255
|
GLOBAL FLAGS:
|
|
256
256
|
--json Output as JSON
|
|
@@ -261,11 +261,17 @@ GLOBAL FLAGS:
|
|
|
261
261
|
ENV:
|
|
262
262
|
FEINAI_USER Override owner/actor identity used in audit log
|
|
263
263
|
|
|
264
|
-
\x1b[34m\x1b[1m──
|
|
265
|
-
\x1b[
|
|
266
|
-
|
|
267
|
-
\x1b[
|
|
268
|
-
|
|
264
|
+
\x1b[34m\x1b[1m── Claude Code Skills ─────────────────────────────────────────────\x1b[0m
|
|
265
|
+
\x1b[36mActivate feinai skills for Claude Code (run once after install):\x1b[0m
|
|
266
|
+
|
|
267
|
+
\x1b[33mmkdir -p ~/.claude/skills\x1b[0m
|
|
268
|
+
\x1b[33mSKILLS=~/.bun/install/global/node_modules/feinai/skills\x1b[0m
|
|
269
|
+
\x1b[33mfor skill in feinai-sdd feinai-write-spec feinai-write-tasks feinai-dispatch feinai-implement; do\x1b[0m
|
|
270
|
+
\x1b[33m ln -sf "$SKILLS/$skill" ~/.claude/skills/$skill\x1b[0m
|
|
271
|
+
\x1b[33mdone\x1b[0m
|
|
272
|
+
|
|
273
|
+
\x1b[36mSkills included:\x1b[0m
|
|
274
|
+
\x1b[34m \x1b[1;94mfeinai-sdd\x1b[0m \x1b[1;94mfeinai-write-spec\x1b[0m \x1b[1;94mfeinai-write-tasks\x1b[0m \x1b[1;94mfeinai-dispatch\x1b[0m \x1b[1;94mfeinai-implement\x1b[0m
|
|
269
275
|
\x1b[34m\x1b[1m────────────────────────────────────────────────────────────────────\x1b[0m
|
|
270
276
|
`);
|
|
271
277
|
}
|
|
@@ -328,7 +334,7 @@ async function main(): Promise<void> {
|
|
|
328
334
|
return;
|
|
329
335
|
default:
|
|
330
336
|
console.error(`Unknown command: ${command}`);
|
|
331
|
-
console.error("Run '
|
|
337
|
+
console.error("Run 'feinai --help' for usage.");
|
|
332
338
|
process.exit(1);
|
|
333
339
|
}
|
|
334
340
|
} catch (err) {
|
|
@@ -347,7 +353,7 @@ function cmdGit(rest: string[]): void {
|
|
|
347
353
|
const opengitPath = resolve(__dirname, "opengit.sh");
|
|
348
354
|
|
|
349
355
|
if (rest.length === 0) {
|
|
350
|
-
console.error("Usage:
|
|
356
|
+
console.error("Usage: feinai git <subcommand> [args...]");
|
|
351
357
|
console.error("Runs opengit — safe git wrapper for parallel worktree workflows.");
|
|
352
358
|
process.exit(1);
|
|
353
359
|
}
|
|
@@ -383,12 +389,12 @@ async function cmdDestroy(args: ParsedArgs): Promise<void> {
|
|
|
383
389
|
function cmdInit(args: ParsedArgs): void {
|
|
384
390
|
const existing = findDbPath();
|
|
385
391
|
if (existing && !args.flags.force) {
|
|
386
|
-
console.error(`
|
|
392
|
+
console.error(`feinai DB already exists at ${existing}`);
|
|
387
393
|
console.error(`Use --force to create a nested DB anyway.`);
|
|
388
394
|
process.exit(1);
|
|
389
395
|
}
|
|
390
396
|
const path = initDb();
|
|
391
|
-
console.log(`Initialized
|
|
397
|
+
console.log(`Initialized feinai DB at ${path}`);
|
|
392
398
|
|
|
393
399
|
// Auto-add .tasca/ to .gitignore if inside a git repo
|
|
394
400
|
if (existsSync(".git")) {
|
|
@@ -459,7 +465,7 @@ function cmdAdd(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
459
465
|
const [id, ...subjectParts] = rest;
|
|
460
466
|
if (!id || subjectParts.length === 0) {
|
|
461
467
|
console.error(
|
|
462
|
-
"Usage:
|
|
468
|
+
"Usage: feinai add <TASK-ID> <subject> [--spec ID] [--desc text] [--package X] [--gate X]",
|
|
463
469
|
);
|
|
464
470
|
process.exit(1);
|
|
465
471
|
}
|
|
@@ -479,7 +485,7 @@ function cmdAdd(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
479
485
|
function cmdShow(rest: string[], format: OutputFormat): void {
|
|
480
486
|
const [id] = rest;
|
|
481
487
|
if (!id) {
|
|
482
|
-
console.error("Usage:
|
|
488
|
+
console.error("Usage: feinai show <TASK-ID>");
|
|
483
489
|
process.exit(1);
|
|
484
490
|
}
|
|
485
491
|
const db = ensureDb();
|
|
@@ -510,7 +516,7 @@ function cmdShow(rest: string[], format: OutputFormat): void {
|
|
|
510
516
|
function cmdTake(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
511
517
|
const [id] = rest;
|
|
512
518
|
if (!id) {
|
|
513
|
-
console.error("Usage:
|
|
519
|
+
console.error("Usage: feinai take <TASK-ID> [--owner name]");
|
|
514
520
|
process.exit(1);
|
|
515
521
|
}
|
|
516
522
|
const db = ensureDb();
|
|
@@ -539,7 +545,7 @@ function cmdDone(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
539
545
|
const [id] = rest;
|
|
540
546
|
const result = args.options.result;
|
|
541
547
|
if (!id || !result) {
|
|
542
|
-
console.error("Usage:
|
|
548
|
+
console.error("Usage: feinai done <TASK-ID> --result <text>");
|
|
543
549
|
process.exit(1);
|
|
544
550
|
}
|
|
545
551
|
const db = ensureDb();
|
|
@@ -551,7 +557,7 @@ function cmdFail(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
551
557
|
const [id] = rest;
|
|
552
558
|
const error = args.options.error;
|
|
553
559
|
if (!id || !error) {
|
|
554
|
-
console.error("Usage:
|
|
560
|
+
console.error("Usage: feinai fail <TASK-ID> --error <text>");
|
|
555
561
|
process.exit(1);
|
|
556
562
|
}
|
|
557
563
|
const db = ensureDb();
|
|
@@ -561,7 +567,7 @@ function cmdFail(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
561
567
|
|
|
562
568
|
function cmdRelease(rest: string[], format: OutputFormat): void {
|
|
563
569
|
const [id] = rest;
|
|
564
|
-
if (!id) { console.error("Usage:
|
|
570
|
+
if (!id) { console.error("Usage: feinai release <TASK-ID>"); process.exit(1); }
|
|
565
571
|
const db = ensureDb();
|
|
566
572
|
const task = releaseTask(db, id, getCurrentUser());
|
|
567
573
|
console.log(formatTask(task, format));
|
|
@@ -569,7 +575,7 @@ function cmdRelease(rest: string[], format: OutputFormat): void {
|
|
|
569
575
|
|
|
570
576
|
function cmdReopen(rest: string[], format: OutputFormat): void {
|
|
571
577
|
const [id] = rest;
|
|
572
|
-
if (!id) { console.error("Usage:
|
|
578
|
+
if (!id) { console.error("Usage: feinai reopen <TASK-ID>"); process.exit(1); }
|
|
573
579
|
const db = ensureDb();
|
|
574
580
|
const task = reopenTask(db, id, getCurrentUser());
|
|
575
581
|
console.log(formatTask(task, format));
|
|
@@ -579,7 +585,7 @@ function cmdBlock(rest: string[], args: ParsedArgs, format: OutputFormat): void
|
|
|
579
585
|
const [id] = rest;
|
|
580
586
|
const by = args.options.by;
|
|
581
587
|
if (!id || !by) {
|
|
582
|
-
console.error("Usage:
|
|
588
|
+
console.error("Usage: feinai block <TASK-ID> --by <BLOCKER-ID>");
|
|
583
589
|
process.exit(1);
|
|
584
590
|
}
|
|
585
591
|
const db = ensureDb();
|
|
@@ -591,7 +597,7 @@ function cmdUnblock(rest: string[], args: ParsedArgs, format: OutputFormat): voi
|
|
|
591
597
|
const [id] = rest;
|
|
592
598
|
const dep = args.options.dep;
|
|
593
599
|
if (!id || !dep) {
|
|
594
|
-
console.error("Usage:
|
|
600
|
+
console.error("Usage: feinai unblock <TASK-ID> --dep <BLOCKER-ID>");
|
|
595
601
|
process.exit(1);
|
|
596
602
|
}
|
|
597
603
|
const db = ensureDb();
|
|
@@ -602,7 +608,7 @@ function cmdUnblock(rest: string[], args: ParsedArgs, format: OutputFormat): voi
|
|
|
602
608
|
function cmdTaskEdit(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
603
609
|
const [id] = rest;
|
|
604
610
|
if (!id) {
|
|
605
|
-
console.error('Usage:
|
|
611
|
+
console.error('Usage: feinai task edit <TASK-ID> [--subject text] [--desc text] [--package name] [--gate cmd]');
|
|
606
612
|
console.error(' Supports --stdin and --file for --desc');
|
|
607
613
|
process.exit(1);
|
|
608
614
|
}
|
|
@@ -639,7 +645,7 @@ function cmdSpec(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
639
645
|
case "show": {
|
|
640
646
|
const [id] = subRest;
|
|
641
647
|
if (!id) {
|
|
642
|
-
console.error("Usage:
|
|
648
|
+
console.error("Usage: feinai spec show <SPEC-ID> [--full]");
|
|
643
649
|
process.exit(1);
|
|
644
650
|
}
|
|
645
651
|
const spec = getSpec(db, id);
|
|
@@ -654,7 +660,7 @@ function cmdSpec(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
654
660
|
case "content": {
|
|
655
661
|
const [id] = subRest;
|
|
656
662
|
if (!id) {
|
|
657
|
-
console.error("Usage:
|
|
663
|
+
console.error("Usage: feinai spec content <SPEC-ID>");
|
|
658
664
|
process.exit(1);
|
|
659
665
|
}
|
|
660
666
|
const spec = getSpec(db, id);
|
|
@@ -673,7 +679,7 @@ function cmdSpec(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
673
679
|
const [id, ...titleParts] = subRest;
|
|
674
680
|
if (!id || titleParts.length === 0) {
|
|
675
681
|
console.error(
|
|
676
|
-
"Usage:
|
|
682
|
+
"Usage: feinai spec add <SPEC-ID> <title> [--content text | --file path | --stdin]",
|
|
677
683
|
);
|
|
678
684
|
process.exit(1);
|
|
679
685
|
}
|
|
@@ -689,7 +695,7 @@ function cmdSpec(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
689
695
|
const [id] = subRest;
|
|
690
696
|
if (!id) {
|
|
691
697
|
console.error(
|
|
692
|
-
"Usage:
|
|
698
|
+
"Usage: feinai spec set-content <SPEC-ID> --content text | --file path | --stdin",
|
|
693
699
|
);
|
|
694
700
|
process.exit(1);
|
|
695
701
|
}
|
|
@@ -705,7 +711,7 @@ function cmdSpec(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
705
711
|
case "start": {
|
|
706
712
|
const [id] = subRest;
|
|
707
713
|
if (!id) {
|
|
708
|
-
console.error("Usage:
|
|
714
|
+
console.error("Usage: feinai spec start <SPEC-ID>");
|
|
709
715
|
process.exit(1);
|
|
710
716
|
}
|
|
711
717
|
console.log(formatSpec(startSpec(db, id, actor), format));
|
|
@@ -714,7 +720,7 @@ function cmdSpec(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
714
720
|
case "done": {
|
|
715
721
|
const [id] = subRest;
|
|
716
722
|
if (!id) {
|
|
717
|
-
console.error("Usage:
|
|
723
|
+
console.error("Usage: feinai spec done <SPEC-ID> [--pr N] [--merged YYYY-MM-DD]");
|
|
718
724
|
process.exit(1);
|
|
719
725
|
}
|
|
720
726
|
const spec = doneSpec(
|
|
@@ -729,7 +735,7 @@ function cmdSpec(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
729
735
|
case "edit": {
|
|
730
736
|
const [id] = subRest;
|
|
731
737
|
if (!id) {
|
|
732
|
-
console.error("Usage:
|
|
738
|
+
console.error("Usage: feinai spec edit <SPEC-ID> [--title text]");
|
|
733
739
|
process.exit(1);
|
|
734
740
|
}
|
|
735
741
|
const spec = editSpec(db, id, { title: args.options.title }, actor);
|
|
@@ -738,19 +744,19 @@ function cmdSpec(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
738
744
|
}
|
|
739
745
|
case "archive": {
|
|
740
746
|
const [id] = subRest;
|
|
741
|
-
if (!id) { console.error("Usage:
|
|
747
|
+
if (!id) { console.error("Usage: feinai spec archive <SPEC-ID>"); process.exit(1); }
|
|
742
748
|
console.log(formatSpec(archiveSpec(db, id, actor), format));
|
|
743
749
|
return;
|
|
744
750
|
}
|
|
745
751
|
case "unarchive": {
|
|
746
752
|
const [id] = subRest;
|
|
747
|
-
if (!id) { console.error("Usage:
|
|
753
|
+
if (!id) { console.error("Usage: feinai spec unarchive <SPEC-ID>"); process.exit(1); }
|
|
748
754
|
console.log(formatSpec(unarchiveSpec(db, id, actor), format));
|
|
749
755
|
return;
|
|
750
756
|
}
|
|
751
757
|
case "delete": {
|
|
752
758
|
const [id] = subRest;
|
|
753
|
-
if (!id) { console.error("Usage:
|
|
759
|
+
if (!id) { console.error("Usage: feinai spec delete <SPEC-ID>"); process.exit(1); }
|
|
754
760
|
console.log(JSON.stringify(deleteSpec(db, id, actor)));
|
|
755
761
|
return;
|
|
756
762
|
}
|
|
@@ -789,7 +795,7 @@ async function cmdServer(args: ParsedArgs): Promise<void> {
|
|
|
789
795
|
|
|
790
796
|
// Verify DB exists before starting server (fast fail)
|
|
791
797
|
if (!findDbPath()) {
|
|
792
|
-
console.error("Error: no .tasca/tasca.db found. Run '
|
|
798
|
+
console.error("Error: no .tasca/tasca.db found. Run 'feinai init' first.");
|
|
793
799
|
process.exit(2);
|
|
794
800
|
}
|
|
795
801
|
|
|
@@ -836,7 +842,7 @@ function cmdPlan(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
836
842
|
const [specId] = subRest;
|
|
837
843
|
if (!specId) {
|
|
838
844
|
console.error(
|
|
839
|
-
"Usage:
|
|
845
|
+
"Usage: feinai plan add <SPEC-ID> --content text | --file path | --stdin",
|
|
840
846
|
);
|
|
841
847
|
process.exit(1);
|
|
842
848
|
}
|
|
@@ -852,7 +858,7 @@ function cmdPlan(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
852
858
|
case "show": {
|
|
853
859
|
const [specId] = subRest;
|
|
854
860
|
if (!specId) {
|
|
855
|
-
console.error("Usage:
|
|
861
|
+
console.error("Usage: feinai plan show <SPEC-ID>");
|
|
856
862
|
process.exit(1);
|
|
857
863
|
}
|
|
858
864
|
const plan = getLatestPlan(db, specId);
|
|
@@ -866,7 +872,7 @@ function cmdPlan(rest: string[], args: ParsedArgs, format: OutputFormat): void {
|
|
|
866
872
|
case "list": {
|
|
867
873
|
const [specId] = subRest;
|
|
868
874
|
if (!specId) {
|
|
869
|
-
console.error("Usage:
|
|
875
|
+
console.error("Usage: feinai plan list <SPEC-ID>");
|
|
870
876
|
process.exit(1);
|
|
871
877
|
}
|
|
872
878
|
console.log(formatPlanList(listPlans(db, specId), format));
|