feinai 0.5.8 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/feinai-sdd/SKILL.md +5 -5
- package/src/cli.ts +6 -6
- package/src/db.ts +43 -4
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: feinai-sdd
|
|
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
|
|
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 `\.feinai/feinai.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
6
|
# feinai SDD integration
|
|
7
7
|
|
|
8
8
|
`feinai` is a CLI + local SQLite database for managing specs, plans, and tasks in
|
|
9
|
-
SDD workflows. When a project has
|
|
9
|
+
SDD workflows. When a project has `\.feinai/feinai.db`, you should write to that
|
|
10
10
|
database instead of producing markdown files under `docs/superpowers/`.
|
|
11
11
|
|
|
12
12
|
This skill extends superpowers — it does not replace it. You still follow the
|
|
@@ -19,7 +19,7 @@ just change *where the artifacts get stored*.
|
|
|
19
19
|
|
|
20
20
|
Invoke when **all** of the following are true:
|
|
21
21
|
|
|
22
|
-
1. The project has a
|
|
22
|
+
1. The project has a `\.feinai/feinai.db` file (walk up the directory tree from
|
|
23
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:
|
|
@@ -28,7 +28,7 @@ Invoke when **all** of the following are true:
|
|
|
28
28
|
- Execute tasks via `subagent-driven-development` or `executing-plans`, OR
|
|
29
29
|
- The user explicitly mentions creating/claiming/marking tasks or specs.
|
|
30
30
|
|
|
31
|
-
If
|
|
31
|
+
If `\.feinai/feinai.db` does not exist, skip this skill — let superpowers do its
|
|
32
32
|
normal markdown-based flow.
|
|
33
33
|
|
|
34
34
|
---
|
|
@@ -42,7 +42,7 @@ feinai status 2>/dev/null
|
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
- Exit code 0: feinai is active in this project → use this skill.
|
|
45
|
-
- Exit code 2: `No
|
|
45
|
+
- Exit code 2: `No \.feinai/feinai.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:
|
|
48
48
|
|
package/src/cli.ts
CHANGED
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
type OutputFormat,
|
|
49
49
|
} from "./format";
|
|
50
50
|
|
|
51
|
-
const VERSION = "0.
|
|
51
|
+
const VERSION = "0.6.0";
|
|
52
52
|
|
|
53
53
|
interface ParsedArgs {
|
|
54
54
|
positional: string[];
|
|
@@ -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 .
|
|
166
|
+
"Error: no .feinai/feinai.db found. Run 'feinai init' to create one.",
|
|
167
167
|
);
|
|
168
168
|
process.exit(2);
|
|
169
169
|
}
|
|
@@ -188,8 +188,8 @@ USAGE:
|
|
|
188
188
|
feinai <command> [options]
|
|
189
189
|
|
|
190
190
|
DB MANAGEMENT:
|
|
191
|
-
init [--force] Create .
|
|
192
|
-
destroy [--yes] Delete .
|
|
191
|
+
init [--force] Create .feinai/feinai.db in cwd
|
|
192
|
+
destroy [--yes] Delete .feinai/ entirely (prompts unless --yes)
|
|
193
193
|
(--force allows nesting under an existing DB)
|
|
194
194
|
status Summary of pending/in_progress/completed counts
|
|
195
195
|
|
|
@@ -365,7 +365,7 @@ function cmdGit(rest: string[]): void {
|
|
|
365
365
|
async function cmdDestroy(args: ParsedArgs): Promise<void> {
|
|
366
366
|
const dbPath = findDbPath();
|
|
367
367
|
if (!dbPath) {
|
|
368
|
-
console.error("Error: no .
|
|
368
|
+
console.error("Error: no .feinai/feinai.db found.");
|
|
369
369
|
process.exit(2);
|
|
370
370
|
}
|
|
371
371
|
|
|
@@ -795,7 +795,7 @@ async function cmdServer(args: ParsedArgs): Promise<void> {
|
|
|
795
795
|
|
|
796
796
|
// Verify DB exists before starting server (fast fail)
|
|
797
797
|
if (!findDbPath()) {
|
|
798
|
-
console.error("Error: no .
|
|
798
|
+
console.error("Error: no .feinai/feinai.db found. Run 'feinai init' first.");
|
|
799
799
|
process.exit(2);
|
|
800
800
|
}
|
|
801
801
|
|
package/src/db.ts
CHANGED
|
@@ -1,9 +1,46 @@
|
|
|
1
|
-
import { existsSync, mkdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, renameSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { openSqlite, type DbAdapter } from "./sqlite-adapter";
|
|
4
4
|
|
|
5
|
-
const DB_DIR = ".
|
|
6
|
-
const DB_FILE = "
|
|
5
|
+
const DB_DIR = ".feinai";
|
|
6
|
+
const DB_FILE = "feinai.db";
|
|
7
|
+
|
|
8
|
+
// v0.5.x migration: rename .tasca/tasca.db → .feinai/feinai.db
|
|
9
|
+
// TODO: remove in v0.7
|
|
10
|
+
function migrateLegacyDir(dir: string): void {
|
|
11
|
+
const legacyDir = join(dir, ".tasca");
|
|
12
|
+
const legacyDb = join(legacyDir, "tasca.db");
|
|
13
|
+
const newDir = join(dir, ".feinai");
|
|
14
|
+
const newDb = join(newDir, "feinai.db");
|
|
15
|
+
if (!existsSync(legacyDb) || existsSync(newDb)) return;
|
|
16
|
+
|
|
17
|
+
// Prompt user — only works in TTY contexts
|
|
18
|
+
if (process.stdout.isTTY) {
|
|
19
|
+
process.stdout.write(
|
|
20
|
+
`\n⚠ Found legacy .tasca/tasca.db at ${legacyDir}\n` +
|
|
21
|
+
` Rename to .feinai/feinai.db? [Y/n] `
|
|
22
|
+
);
|
|
23
|
+
const buf = Buffer.alloc(4);
|
|
24
|
+
let answer = "y";
|
|
25
|
+
try {
|
|
26
|
+
const n = require("node:fs").readSync(0, buf, 0, 4, null);
|
|
27
|
+
answer = buf.slice(0, n).toString().trim().toLowerCase() || "y";
|
|
28
|
+
} catch {}
|
|
29
|
+
if (answer !== "y" && answer !== "") {
|
|
30
|
+
process.stdout.write("Skipped. Run 'feinai init' to create a new DB.\n\n");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
// Non-interactive (agent/CI): migrate silently
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!existsSync(newDir)) mkdirSync(newDir, { recursive: true });
|
|
38
|
+
renameSync(legacyDb, newDb);
|
|
39
|
+
try { require("node:fs").rmdirSync(legacyDir); } catch {}
|
|
40
|
+
if (process.stdout.isTTY) {
|
|
41
|
+
process.stdout.write(`✓ Migrated to .feinai/feinai.db\n\n`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
7
44
|
|
|
8
45
|
export type DbInstance = DbAdapter;
|
|
9
46
|
|
|
@@ -152,6 +189,8 @@ function applySchema(db: DbInstance): void {
|
|
|
152
189
|
export function findDbPath(startDir: string = process.cwd()): string | null {
|
|
153
190
|
let current = resolve(startDir);
|
|
154
191
|
while (true) {
|
|
192
|
+
// migrate legacy .tasca/tasca.db → .feinai/feinai.db if found
|
|
193
|
+
migrateLegacyDir(current);
|
|
155
194
|
const candidate = join(current, DB_DIR, DB_FILE);
|
|
156
195
|
if (existsSync(candidate)) return candidate;
|
|
157
196
|
const parent = dirname(current);
|
|
@@ -187,7 +226,7 @@ export function openDb(): DbInstance {
|
|
|
187
226
|
const path = findDbPath();
|
|
188
227
|
if (!path) {
|
|
189
228
|
throw new Error(
|
|
190
|
-
`No
|
|
229
|
+
`No feinai DB found. Run 'feinai init' to create one in the current directory.`,
|
|
191
230
|
);
|
|
192
231
|
}
|
|
193
232
|
const db = openSqlite(path);
|