feinai 0.5.6 → 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/README.md CHANGED
@@ -165,7 +165,7 @@ tasks — atomic work units with blocked_by, quality_gates, worktree
165
165
  events — append-only audit log: {parent_process}:{pid}:{user}
166
166
  ```
167
167
 
168
- Every mutation is logged. Every agent is identified. Override identity with `$FEINA_USER`.
168
+ Every mutation is logged. Every agent is identified. Override identity with `$FEINAI_USER`.
169
169
 
170
170
  ---
171
171
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feinai",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Task & spec manager for AI agents — parallel worktrees, live dashboard, SDD skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,7 +3,7 @@ name: feinai-sdd
3
3
  description: Use when working in spec-driven development (SDD) workflows that involve brainstorming, writing-plans, or subagent-driven-development AND the project has a `.tasca/tasca.db` file. Replaces creating markdown files in `docs/superpowers/specs/` and `docs/superpowers/plans/` with atomic `feinai` CLI calls, keeping state queryable, race-free, and audit-logged. Also use when the user asks to create a spec, add a task, claim work, or mark progress, in a project that uses tasca.
4
4
  ---
5
5
 
6
- # Tasca SDD integration
6
+ # feinai SDD integration
7
7
 
8
8
  `feinai` is a CLI + local SQLite database for managing specs, plans, and tasks in
9
9
  SDD workflows. When a project has `.tasca/tasca.db`, you should write to that
@@ -20,7 +20,7 @@ just change *where the artifacts get stored*.
20
20
  Invoke when **all** of the following are true:
21
21
 
22
22
  1. The project has a `.tasca/tasca.db` file (walk up the directory tree from
23
- the current working directory; tasca uses the same discovery pattern as git).
23
+ the current working directory; feinai uses the same discovery pattern as git).
24
24
  Check with: `feinai status` (exit code 0 = tasca is set up).
25
25
  2. You are about to:
26
26
  - Write a spec via the `brainstorming` skill, OR
@@ -41,7 +41,7 @@ Before any spec / plan / task write, run:
41
41
  feinai status 2>/dev/null
42
42
  ```
43
43
 
44
- - Exit code 0: tasca is active in this project → use this skill.
44
+ - Exit code 0: feinai is active in this project → use this skill.
45
45
  - Exit code 2: `No .tasca/tasca.db found` → fall back to vanilla superpowers
46
46
  (or ask the user `feinai init` if it seems intended).
47
47
  - Command not found: feinai is not installed or not on PATH. Tell the user:
@@ -96,7 +96,7 @@ FEINAI_EOF
96
96
 
97
97
  ### What replaces what
98
98
 
99
- | Superpowers step | tasca equivalent |
99
+ | Superpowers step | feinai equivalent |
100
100
  |---|---|
101
101
  | `Write(docs/superpowers/specs/YYYY-MM-DD-X-design.md, ...)` | `feinai spec add SPEC-NNN "title" --stdin <<<` content |
102
102
  | Spec self-review (re-read the markdown) | `feinai spec content SPEC-NNN` (returns the markdown) |
@@ -169,7 +169,7 @@ single response, the full payload: `description` (workplan), `quality_gates`,
169
169
  `packages`, `blocked_by`. **The subagent never has to read the plan markdown.**
170
170
 
171
171
  This is the single biggest token saving and the strongest correctness
172
- guarantee from using tasca: each subagent's context contains exactly the task
172
+ guarantee from using feinai: each subagent's context contains exactly the task
173
173
  it's executing, nothing else.
174
174
 
175
175
  ---
@@ -248,7 +248,7 @@ human-readable rationale; the tasks are the executable state.
248
248
 
249
249
  ### ❌ Don't create `docs/superpowers/specs/*.md` files
250
250
 
251
- If tasca is active, those files become a source of drift. The spec lives in
251
+ If feinai is active, those files become a source of drift. The spec lives in
252
252
  the database. If you need to publish the spec elsewhere (PR description, wiki,
253
253
  etc.), export it on demand: `feinai spec content SPEC-X > /tmp/spec.md`.
254
254
 
@@ -262,9 +262,9 @@ subagents that both `show` then act will collide. Always `feinai take`.
262
262
  The CLI handles concurrency, audit logging, and validation. Direct SQL
263
263
  mutations bypass the events table and leave the audit log incomplete.
264
264
 
265
- ### ✅ Do set `FEINA_USER` for explicit subagent identity
265
+ ### ✅ Do set `FEINAI_USER` for explicit subagent identity
266
266
 
267
- When dispatching a subagent, pass `FEINA_USER=subagent-N` in its environment
267
+ When dispatching a subagent, pass `FEINAI_USER=subagent-N` in its environment
268
268
  so the audit log distinguishes which subagent did what. Otherwise the actor
269
269
  field will read `bun:<pid>:<user>` for everyone.
270
270
 
@@ -294,8 +294,8 @@ field will read `bun:<pid>:<user>` for everyone.
294
294
 
295
295
  This skill is **additive**. The superpowers skills (`brainstorming`,
296
296
  `writing-plans`, `subagent-driven-development`, etc.) still run; they still
297
- own the process. tasca only changes the storage of the artifacts they produce.
297
+ own the process. feinai only changes the storage of the artifacts they produce.
298
298
 
299
- If you start brainstorming and discover the project has no tasca DB, fall back
299
+ If you start brainstorming and discover the project has no feinai DB, fall back
300
300
  to the normal superpowers behavior (markdown files). Do not silently switch
301
301
  storage strategy mid-project.
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.6";
51
+ const VERSION = "0.5.8";
52
52
 
53
53
  interface ParsedArgs {
54
54
  positional: string[];
@@ -123,10 +123,10 @@ function detectFormat(args: ParsedArgs): OutputFormat {
123
123
  }
124
124
 
125
125
  /**
126
- * Build a stable identifier for whoever is invoking feina.
126
+ * Build a stable identifier for whoever is invoking feinai.
127
127
  *
128
128
  * Priority:
129
- * 1. $FEINA_USER env var (explicit override — used by agents to identify themselves)
129
+ * 1. $FEINAI_USER env var (explicit override — used by agents to identify themselves)
130
130
  * 2. parent process name (on Linux via /proc/$PPID/comm) → "{parent_name}:{ppid}:{user}"
131
131
  * 3. fallback: "{user}@{hostname}"
132
132
  *
@@ -134,7 +134,7 @@ function detectFormat(args: ParsedArgs): OutputFormat {
134
134
  * from "bash:9999:m" in the events audit log without manual configuration.
135
135
  */
136
136
  function getCurrentUser(): string {
137
- const override = process.env.FEINA_USER;
137
+ const override = process.env.FEINAI_USER;
138
138
  if (override) return override;
139
139
 
140
140
  const username = process.env.USER ?? userInfo().username ?? "unknown";
@@ -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 'feina init' to create one.",
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(`feina v\${VERSION} — task & spec manager for AI agents and humans
185
+ console.log(`feinai v\${VERSION} — task & spec manager for AI agents and humans
186
186
 
187
187
  USAGE:
188
- feina <command> [options]
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 feina server (by port)
253
+ server --down Stop the running feinai server (by port)
254
254
 
255
255
  GLOBAL FLAGS:
256
256
  --json Output as JSON
@@ -259,13 +259,19 @@ GLOBAL FLAGS:
259
259
  --version Show version
260
260
 
261
261
  ENV:
262
- FEINA_USER Override owner/actor identity used in audit log
262
+ FEINAI_USER Override owner/actor identity used in audit log
263
263
 
264
- \x1b[34m\x1b[1m── Agent Integration ──────────────────────────────────────────────\x1b[0m
265
- \x1b[36mTeach your AI agents to use \x1b[1mfeina\x1b[0m\x1b[36m as their single source of truth for\x1b[0m
266
- \x1b[36mspecs, tasks, and plans.\x1b[0m
267
- \x1b[36mOr load \x1b[1mfeina skills\x1b[0m\x1b[36m from the Claude Code skills marketplace:\x1b[0m
268
- \x1b[34m \x1b[1;94mtasca-sdd\x1b[0m \x1b[1;94mtasca-write-spec\x1b[0m \x1b[1;94mtasca-write-tasks\x1b[0m \x1b[1;94mtasca-dispatch\x1b[0m
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 'feina --help' for usage.");
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: feina git <subcommand> [args...]");
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(`feina DB already exists at ${existing}`);
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 feina DB at ${path}`);
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: feina add <TASK-ID> <subject> [--spec ID] [--desc text] [--package X] [--gate X]",
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: feina show <TASK-ID>");
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: feina take <TASK-ID> [--owner name]");
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: feina done <TASK-ID> --result <text>");
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: feina fail <TASK-ID> --error <text>");
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: feina release <TASK-ID>"); process.exit(1); }
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: feina reopen <TASK-ID>"); process.exit(1); }
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: feina block <TASK-ID> --by <BLOCKER-ID>");
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: feina unblock <TASK-ID> --dep <BLOCKER-ID>");
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: feina task edit <TASK-ID> [--subject text] [--desc text] [--package name] [--gate cmd]');
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: feina spec show <SPEC-ID> [--full]");
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: feina spec content <SPEC-ID>");
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: feina spec add <SPEC-ID> <title> [--content text | --file path | --stdin]",
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: feina spec set-content <SPEC-ID> --content text | --file path | --stdin",
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: feina spec start <SPEC-ID>");
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: feina spec done <SPEC-ID> [--pr N] [--merged YYYY-MM-DD]");
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: feina spec edit <SPEC-ID> [--title text]");
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: feina spec archive <SPEC-ID>"); process.exit(1); }
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: feina spec unarchive <SPEC-ID>"); process.exit(1); }
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: feina spec delete <SPEC-ID>"); process.exit(1); }
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 'feina init' first.");
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: feina plan add <SPEC-ID> --content text | --file path | --stdin",
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: feina plan show <SPEC-ID>");
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: feina plan list <SPEC-ID>");
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));