floq 0.0.1 → 0.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/README.ja.md +157 -29
- package/README.md +157 -29
- package/dist/changelog.d.ts +13 -0
- package/dist/changelog.js +95 -0
- package/dist/cli.js +70 -1
- package/dist/commands/add.js +5 -6
- package/dist/commands/comment.d.ts +2 -0
- package/dist/commands/comment.js +67 -0
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.js +163 -14
- package/dist/commands/done.js +4 -6
- package/dist/commands/list.js +9 -13
- package/dist/commands/move.js +4 -6
- package/dist/commands/project.js +18 -26
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +13 -0
- package/dist/config.d.ts +15 -1
- package/dist/config.js +53 -2
- package/dist/db/index.d.ts +5 -4
- package/dist/db/index.js +127 -32
- package/dist/db/schema.d.ts +83 -0
- package/dist/db/schema.js +6 -0
- package/dist/i18n/en.d.ts +258 -0
- package/dist/i18n/en.js +138 -3
- package/dist/i18n/ja.js +138 -3
- package/dist/index.js +33 -1
- package/dist/paths.d.ts +4 -0
- package/dist/paths.js +63 -5
- package/dist/ui/App.js +384 -136
- package/dist/ui/ModeSelector.d.ts +7 -0
- package/dist/ui/ModeSelector.js +37 -0
- package/dist/ui/SetupWizard.d.ts +6 -0
- package/dist/ui/SetupWizard.js +321 -0
- package/dist/ui/ThemeSelector.d.ts +1 -1
- package/dist/ui/ThemeSelector.js +23 -10
- package/dist/ui/components/FunctionKeyBar.d.ts +5 -4
- package/dist/ui/components/FunctionKeyBar.js +19 -15
- package/dist/ui/components/HelpModal.d.ts +2 -1
- package/dist/ui/components/HelpModal.js +118 -4
- package/dist/ui/components/KanbanBoard.d.ts +6 -0
- package/dist/ui/components/KanbanBoard.js +508 -0
- package/dist/ui/components/KanbanColumn.d.ts +12 -0
- package/dist/ui/components/KanbanColumn.js +11 -0
- package/dist/ui/components/ProgressBar.d.ts +7 -0
- package/dist/ui/components/ProgressBar.js +13 -0
- package/dist/ui/components/SearchBar.d.ts +8 -0
- package/dist/ui/components/SearchBar.js +11 -0
- package/dist/ui/components/SearchResults.d.ts +9 -0
- package/dist/ui/components/SearchResults.js +18 -0
- package/dist/ui/components/TaskItem.d.ts +6 -1
- package/dist/ui/components/TaskItem.js +3 -2
- package/dist/ui/theme/themes.d.ts +12 -0
- package/dist/ui/theme/themes.js +495 -3
- package/dist/ui/theme/types.d.ts +1 -1
- package/package.json +2 -1
package/dist/commands/add.js
CHANGED
|
@@ -8,16 +8,15 @@ export async function addTask(title, options) {
|
|
|
8
8
|
const i18n = t();
|
|
9
9
|
let parentId;
|
|
10
10
|
if (options.project) {
|
|
11
|
-
const
|
|
11
|
+
const projects = await db
|
|
12
12
|
.select()
|
|
13
13
|
.from(schema.tasks)
|
|
14
|
-
.where(and(eq(schema.tasks.isProject, true), eq(schema.tasks.title, options.project)))
|
|
15
|
-
|
|
16
|
-
if (!project) {
|
|
14
|
+
.where(and(eq(schema.tasks.isProject, true), eq(schema.tasks.title, options.project)));
|
|
15
|
+
if (projects.length === 0) {
|
|
17
16
|
console.error(fmt(i18n.commands.add.projectNotFound, { project: options.project }));
|
|
18
17
|
process.exit(1);
|
|
19
18
|
}
|
|
20
|
-
parentId =
|
|
19
|
+
parentId = projects[0].id;
|
|
21
20
|
}
|
|
22
21
|
const task = {
|
|
23
22
|
id: uuidv4(),
|
|
@@ -28,7 +27,7 @@ export async function addTask(title, options) {
|
|
|
28
27
|
createdAt: now,
|
|
29
28
|
updatedAt: now,
|
|
30
29
|
};
|
|
31
|
-
db.insert(schema.tasks).values(task)
|
|
30
|
+
await db.insert(schema.tasks).values(task);
|
|
32
31
|
console.log(fmt(i18n.commands.add.success, { title }));
|
|
33
32
|
if (parentId) {
|
|
34
33
|
console.log(fmt(i18n.commands.add.withProject, { project: options.project }));
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { eq, like } from 'drizzle-orm';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { getDb, schema } from '../db/index.js';
|
|
4
|
+
import { t, fmt } from '../i18n/index.js';
|
|
5
|
+
export async function addComment(taskId, content) {
|
|
6
|
+
const db = getDb();
|
|
7
|
+
const i18n = t();
|
|
8
|
+
// Find task by ID prefix
|
|
9
|
+
const tasks = await db
|
|
10
|
+
.select()
|
|
11
|
+
.from(schema.tasks)
|
|
12
|
+
.where(like(schema.tasks.id, `${taskId}%`));
|
|
13
|
+
if (tasks.length === 0) {
|
|
14
|
+
console.error(fmt(i18n.commands.comment.notFound, { id: taskId }));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
if (tasks.length > 1) {
|
|
18
|
+
console.error(fmt(i18n.commands.comment.multipleMatch, { id: taskId }));
|
|
19
|
+
for (const task of tasks) {
|
|
20
|
+
console.error(` [${task.id.slice(0, 8)}] ${task.title}`);
|
|
21
|
+
}
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const task = tasks[0];
|
|
25
|
+
await db.insert(schema.comments).values({
|
|
26
|
+
id: uuidv4(),
|
|
27
|
+
taskId: task.id,
|
|
28
|
+
content: content.trim(),
|
|
29
|
+
createdAt: new Date(),
|
|
30
|
+
});
|
|
31
|
+
console.log(fmt(i18n.commands.comment.added, { title: task.title }));
|
|
32
|
+
}
|
|
33
|
+
export async function listComments(taskId) {
|
|
34
|
+
const db = getDb();
|
|
35
|
+
const i18n = t();
|
|
36
|
+
// Find task by ID prefix
|
|
37
|
+
const tasks = await db
|
|
38
|
+
.select()
|
|
39
|
+
.from(schema.tasks)
|
|
40
|
+
.where(like(schema.tasks.id, `${taskId}%`));
|
|
41
|
+
if (tasks.length === 0) {
|
|
42
|
+
console.error(fmt(i18n.commands.comment.notFound, { id: taskId }));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
if (tasks.length > 1) {
|
|
46
|
+
console.error(fmt(i18n.commands.comment.multipleMatch, { id: taskId }));
|
|
47
|
+
for (const task of tasks) {
|
|
48
|
+
console.error(` [${task.id.slice(0, 8)}] ${task.title}`);
|
|
49
|
+
}
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const task = tasks[0];
|
|
53
|
+
const comments = await db
|
|
54
|
+
.select()
|
|
55
|
+
.from(schema.comments)
|
|
56
|
+
.where(eq(schema.comments.taskId, task.id));
|
|
57
|
+
if (comments.length === 0) {
|
|
58
|
+
console.log(fmt(i18n.commands.comment.listHeader, { title: task.title }));
|
|
59
|
+
console.log(` ${i18n.commands.comment.noComments}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console.log(fmt(i18n.commands.comment.listHeader, { title: task.title }));
|
|
63
|
+
for (const comment of comments) {
|
|
64
|
+
const date = comment.createdAt.toLocaleString();
|
|
65
|
+
console.log(` [${date}] ${comment.content}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -4,3 +4,10 @@ export declare function setDbPath(dbPath: string): Promise<void>;
|
|
|
4
4
|
export declare function resetDbPath(): Promise<void>;
|
|
5
5
|
export declare function setTheme(theme: string): Promise<void>;
|
|
6
6
|
export declare function selectTheme(): Promise<void>;
|
|
7
|
+
export declare function showViewMode(): Promise<void>;
|
|
8
|
+
export declare function setViewModeCommand(mode: string): Promise<void>;
|
|
9
|
+
export declare function selectMode(): Promise<void>;
|
|
10
|
+
export declare function setTurso(url: string, token: string): Promise<void>;
|
|
11
|
+
export declare function disableTurso(): Promise<void>;
|
|
12
|
+
export declare function syncCommand(): Promise<void>;
|
|
13
|
+
export declare function resetDatabase(force: boolean): Promise<void>;
|
package/dist/commands/config.js
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { render } from 'ink';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { createInterface } from 'readline';
|
|
4
|
+
import { unlinkSync, existsSync, readdirSync } from 'fs';
|
|
5
|
+
import { dirname, basename, join } from 'path';
|
|
6
|
+
import { loadConfig, saveConfig, getDbPath, getViewMode, setViewMode, isTursoEnabled, getTursoConfig, setTursoConfig } from '../config.js';
|
|
4
7
|
import { CONFIG_FILE } from '../paths.js';
|
|
5
8
|
import { ThemeSelector } from '../ui/ThemeSelector.js';
|
|
9
|
+
import { ModeSelector } from '../ui/ModeSelector.js';
|
|
10
|
+
import { syncDb } from '../db/index.js';
|
|
11
|
+
import { VALID_THEMES, themes } from '../ui/theme/themes.js';
|
|
6
12
|
const VALID_LOCALES = ['en', 'ja'];
|
|
7
|
-
const
|
|
13
|
+
const VALID_VIEW_MODES = ['gtd', 'kanban'];
|
|
8
14
|
export async function showConfig() {
|
|
9
15
|
const config = loadConfig();
|
|
10
16
|
console.log('GTD CLI Configuration');
|
|
@@ -13,9 +19,14 @@ export async function showConfig() {
|
|
|
13
19
|
console.log(`Language: ${config.locale}`);
|
|
14
20
|
console.log(`Database: ${getDbPath()}`);
|
|
15
21
|
console.log(`Theme: ${config.theme || 'modern'}`);
|
|
22
|
+
console.log(`View Mode: ${config.viewMode || 'gtd'}`);
|
|
23
|
+
console.log(`Turso: ${isTursoEnabled() ? 'enabled' : 'disabled'}`);
|
|
16
24
|
if (config.db_path) {
|
|
17
25
|
console.log(` (custom: ${config.db_path})`);
|
|
18
26
|
}
|
|
27
|
+
if (config.turso) {
|
|
28
|
+
console.log(` URL: ${config.turso.url}`);
|
|
29
|
+
}
|
|
19
30
|
}
|
|
20
31
|
export async function setLanguage(locale) {
|
|
21
32
|
if (!VALID_LOCALES.includes(locale)) {
|
|
@@ -45,13 +56,8 @@ export async function setTheme(theme) {
|
|
|
45
56
|
process.exit(1);
|
|
46
57
|
}
|
|
47
58
|
saveConfig({ theme: theme });
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
'norton-commander': 'Theme set to Norton Commander (MS-DOS style)',
|
|
51
|
-
'dos-prompt': 'Theme set to DOS Prompt (green on black)',
|
|
52
|
-
'turbo-pascal': 'Theme set to Turbo Pascal IDE',
|
|
53
|
-
};
|
|
54
|
-
console.log(messages[theme]);
|
|
59
|
+
const themeData = themes[theme];
|
|
60
|
+
console.log(`Theme set to ${themeData.displayName}`);
|
|
55
61
|
}
|
|
56
62
|
export async function selectTheme() {
|
|
57
63
|
return new Promise((resolve) => {
|
|
@@ -59,15 +65,158 @@ export async function selectTheme() {
|
|
|
59
65
|
onSelect: (theme) => {
|
|
60
66
|
unmount();
|
|
61
67
|
saveConfig({ theme });
|
|
68
|
+
const themeData = themes[theme];
|
|
69
|
+
console.log(`Theme set to ${themeData.displayName}`);
|
|
70
|
+
resolve();
|
|
71
|
+
},
|
|
72
|
+
}));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export async function showViewMode() {
|
|
76
|
+
const mode = getViewMode();
|
|
77
|
+
console.log(`Current view mode: ${mode}`);
|
|
78
|
+
}
|
|
79
|
+
export async function setViewModeCommand(mode) {
|
|
80
|
+
if (!VALID_VIEW_MODES.includes(mode)) {
|
|
81
|
+
console.error(`Invalid view mode: ${mode}`);
|
|
82
|
+
console.error(`Valid modes: ${VALID_VIEW_MODES.join(', ')}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
setViewMode(mode);
|
|
86
|
+
const messages = {
|
|
87
|
+
gtd: 'View mode set to GTD',
|
|
88
|
+
kanban: 'View mode set to Kanban',
|
|
89
|
+
};
|
|
90
|
+
console.log(messages[mode]);
|
|
91
|
+
}
|
|
92
|
+
export async function selectMode() {
|
|
93
|
+
return new Promise((resolve) => {
|
|
94
|
+
const { unmount } = render(React.createElement(ModeSelector, {
|
|
95
|
+
onSelect: (mode) => {
|
|
96
|
+
unmount();
|
|
97
|
+
setViewMode(mode);
|
|
62
98
|
const messages = {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
'dos-prompt': 'Theme set to DOS Prompt (green on black)',
|
|
66
|
-
'turbo-pascal': 'Theme set to Turbo Pascal IDE',
|
|
99
|
+
gtd: 'View mode set to GTD',
|
|
100
|
+
kanban: 'View mode set to Kanban',
|
|
67
101
|
};
|
|
68
|
-
console.log(messages[
|
|
102
|
+
console.log(messages[mode]);
|
|
69
103
|
resolve();
|
|
70
104
|
},
|
|
71
105
|
}));
|
|
72
106
|
});
|
|
73
107
|
}
|
|
108
|
+
export async function setTurso(url, token) {
|
|
109
|
+
setTursoConfig({ url, authToken: token });
|
|
110
|
+
console.log('Turso sync enabled');
|
|
111
|
+
console.log(` URL: ${url}`);
|
|
112
|
+
console.log('Run "floq sync" to sync with Turso cloud');
|
|
113
|
+
}
|
|
114
|
+
export async function disableTurso() {
|
|
115
|
+
setTursoConfig(undefined);
|
|
116
|
+
console.log('Turso sync disabled');
|
|
117
|
+
}
|
|
118
|
+
export async function syncCommand() {
|
|
119
|
+
if (!isTursoEnabled()) {
|
|
120
|
+
console.error('Turso sync is not enabled.');
|
|
121
|
+
console.error('Use "floq config turso --url <url> --token <token>" to enable.');
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
console.log('Syncing with Turso cloud...');
|
|
125
|
+
try {
|
|
126
|
+
await syncDb();
|
|
127
|
+
console.log('Sync complete');
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
131
|
+
console.error(`Sync failed: ${message}`);
|
|
132
|
+
console.error('');
|
|
133
|
+
console.error('Possible solutions:');
|
|
134
|
+
console.error(' 1. Check your Turso URL and auth token');
|
|
135
|
+
console.error(' 2. Verify network connectivity');
|
|
136
|
+
console.error(' 3. Try again later');
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function confirm(message) {
|
|
141
|
+
const rl = createInterface({
|
|
142
|
+
input: process.stdin,
|
|
143
|
+
output: process.stdout,
|
|
144
|
+
});
|
|
145
|
+
return new Promise((resolve) => {
|
|
146
|
+
rl.question(`${message} (y/N): `, (answer) => {
|
|
147
|
+
rl.close();
|
|
148
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async function clearTursoData() {
|
|
153
|
+
const turso = getTursoConfig();
|
|
154
|
+
if (!turso)
|
|
155
|
+
return;
|
|
156
|
+
const { createClient } = await import('@libsql/client');
|
|
157
|
+
const client = createClient({
|
|
158
|
+
url: turso.url,
|
|
159
|
+
authToken: turso.authToken,
|
|
160
|
+
});
|
|
161
|
+
try {
|
|
162
|
+
await client.execute('DELETE FROM comments');
|
|
163
|
+
await client.execute('DELETE FROM tasks');
|
|
164
|
+
console.log('Turso cloud data cleared.');
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
client.close();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
export async function resetDatabase(force) {
|
|
171
|
+
const dbPath = getDbPath();
|
|
172
|
+
const dbDir = dirname(dbPath);
|
|
173
|
+
const dbName = basename(dbPath, '.db');
|
|
174
|
+
const tursoEnabled = isTursoEnabled();
|
|
175
|
+
// Find all related database files
|
|
176
|
+
const relatedFiles = [];
|
|
177
|
+
if (existsSync(dbDir)) {
|
|
178
|
+
const files = readdirSync(dbDir);
|
|
179
|
+
for (const file of files) {
|
|
180
|
+
// Match: floq.db, floq.db-wal, floq.db-shm, floq-turso.db-info, etc.
|
|
181
|
+
if (file.startsWith(dbName)) {
|
|
182
|
+
relatedFiles.push(join(dbDir, file));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (relatedFiles.length === 0 && !tursoEnabled) {
|
|
187
|
+
console.log('Database files do not exist.');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (!force) {
|
|
191
|
+
if (relatedFiles.length > 0) {
|
|
192
|
+
console.log('This will delete the following local files:');
|
|
193
|
+
for (const file of relatedFiles) {
|
|
194
|
+
console.log(` ${file}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (tursoEnabled) {
|
|
198
|
+
console.log('This will also delete all data in Turso cloud.');
|
|
199
|
+
}
|
|
200
|
+
const confirmed = await confirm('Are you sure?');
|
|
201
|
+
if (!confirmed) {
|
|
202
|
+
console.log('Cancelled.');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
// Clear Turso cloud data first
|
|
208
|
+
if (tursoEnabled) {
|
|
209
|
+
await clearTursoData();
|
|
210
|
+
}
|
|
211
|
+
// Delete local files
|
|
212
|
+
for (const file of relatedFiles) {
|
|
213
|
+
unlinkSync(file);
|
|
214
|
+
}
|
|
215
|
+
console.log('Database reset complete.');
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
219
|
+
console.error(`Failed to reset database: ${message}`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
}
|
package/dist/commands/done.js
CHANGED
|
@@ -5,11 +5,10 @@ export async function markDone(taskId) {
|
|
|
5
5
|
const db = getDb();
|
|
6
6
|
const i18n = t();
|
|
7
7
|
// Find task by ID prefix
|
|
8
|
-
const tasks = db
|
|
8
|
+
const tasks = await db
|
|
9
9
|
.select()
|
|
10
10
|
.from(schema.tasks)
|
|
11
|
-
.where(like(schema.tasks.id, `${taskId}%`))
|
|
12
|
-
.all();
|
|
11
|
+
.where(like(schema.tasks.id, `${taskId}%`));
|
|
13
12
|
if (tasks.length === 0) {
|
|
14
13
|
console.error(fmt(i18n.commands.done.notFound, { id: taskId }));
|
|
15
14
|
process.exit(1);
|
|
@@ -26,12 +25,11 @@ export async function markDone(taskId) {
|
|
|
26
25
|
console.log(fmt(i18n.commands.done.alreadyDone, { title: task.title }));
|
|
27
26
|
return;
|
|
28
27
|
}
|
|
29
|
-
db.update(schema.tasks)
|
|
28
|
+
await db.update(schema.tasks)
|
|
30
29
|
.set({
|
|
31
30
|
status: 'done',
|
|
32
31
|
updatedAt: new Date(),
|
|
33
32
|
})
|
|
34
|
-
.where(eq(schema.tasks.id, task.id))
|
|
35
|
-
.run();
|
|
33
|
+
.where(eq(schema.tasks.id, task.id));
|
|
36
34
|
console.log(fmt(i18n.commands.done.success, { title: task.title }));
|
|
37
35
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -11,11 +11,10 @@ export async function listTasks(status) {
|
|
|
11
11
|
console.error(fmt(i18n.commands.list.validStatuses, { statuses: validStatuses.join(', ') }));
|
|
12
12
|
process.exit(1);
|
|
13
13
|
}
|
|
14
|
-
const tasks = db
|
|
14
|
+
const tasks = await db
|
|
15
15
|
.select()
|
|
16
16
|
.from(schema.tasks)
|
|
17
|
-
.where(eq(schema.tasks.status, status))
|
|
18
|
-
.all();
|
|
17
|
+
.where(eq(schema.tasks.status, status));
|
|
19
18
|
console.log(`\n${i18n.status[status]} (${tasks.length})`);
|
|
20
19
|
console.log('─'.repeat(40));
|
|
21
20
|
if (tasks.length === 0) {
|
|
@@ -40,11 +39,10 @@ export async function listTasks(status) {
|
|
|
40
39
|
// Show all lists
|
|
41
40
|
const statuses = ['inbox', 'next', 'waiting', 'someday'];
|
|
42
41
|
for (const s of statuses) {
|
|
43
|
-
const tasks = db
|
|
42
|
+
const tasks = await db
|
|
44
43
|
.select()
|
|
45
44
|
.from(schema.tasks)
|
|
46
|
-
.where(eq(schema.tasks.status, s))
|
|
47
|
-
.all();
|
|
45
|
+
.where(eq(schema.tasks.status, s));
|
|
48
46
|
console.log(`\n${i18n.status[s]} (${tasks.length})`);
|
|
49
47
|
console.log('─'.repeat(40));
|
|
50
48
|
if (tasks.length === 0) {
|
|
@@ -66,12 +64,11 @@ export async function listTasks(status) {
|
|
|
66
64
|
export async function listProjects() {
|
|
67
65
|
const db = getDb();
|
|
68
66
|
const i18n = t();
|
|
69
|
-
const
|
|
67
|
+
const allProjects = await db
|
|
70
68
|
.select()
|
|
71
69
|
.from(schema.tasks)
|
|
72
|
-
.where(eq(schema.tasks.isProject, true))
|
|
73
|
-
|
|
74
|
-
.filter(p => p.status !== 'done');
|
|
70
|
+
.where(eq(schema.tasks.isProject, true));
|
|
71
|
+
const projects = allProjects.filter(p => p.status !== 'done');
|
|
75
72
|
console.log(`\n${i18n.projectStatus.active} (${projects.length})`);
|
|
76
73
|
console.log('─'.repeat(40));
|
|
77
74
|
if (projects.length === 0) {
|
|
@@ -79,11 +76,10 @@ export async function listProjects() {
|
|
|
79
76
|
}
|
|
80
77
|
else {
|
|
81
78
|
for (const project of projects) {
|
|
82
|
-
const childTasks = db
|
|
79
|
+
const childTasks = await db
|
|
83
80
|
.select()
|
|
84
81
|
.from(schema.tasks)
|
|
85
|
-
.where(eq(schema.tasks.parentId, project.id))
|
|
86
|
-
.all();
|
|
82
|
+
.where(eq(schema.tasks.parentId, project.id));
|
|
87
83
|
const activeTasks = childTasks.filter(t => t.status !== 'done').length;
|
|
88
84
|
const shortId = project.id.slice(0, 8);
|
|
89
85
|
console.log(` [${shortId}] ${project.title} (${fmt(i18n.commands.list.tasks, { count: activeTasks })})`);
|
package/dist/commands/move.js
CHANGED
|
@@ -15,11 +15,10 @@ export async function moveTask(taskId, targetStatus, waitingFor) {
|
|
|
15
15
|
process.exit(1);
|
|
16
16
|
}
|
|
17
17
|
// Find task by ID prefix
|
|
18
|
-
const tasks = db
|
|
18
|
+
const tasks = await db
|
|
19
19
|
.select()
|
|
20
20
|
.from(schema.tasks)
|
|
21
|
-
.where(like(schema.tasks.id, `${taskId}%`))
|
|
22
|
-
.all();
|
|
21
|
+
.where(like(schema.tasks.id, `${taskId}%`));
|
|
23
22
|
if (tasks.length === 0) {
|
|
24
23
|
console.error(fmt(i18n.commands.move.notFound, { id: taskId }));
|
|
25
24
|
process.exit(1);
|
|
@@ -32,14 +31,13 @@ export async function moveTask(taskId, targetStatus, waitingFor) {
|
|
|
32
31
|
process.exit(1);
|
|
33
32
|
}
|
|
34
33
|
const task = tasks[0];
|
|
35
|
-
db.update(schema.tasks)
|
|
34
|
+
await db.update(schema.tasks)
|
|
36
35
|
.set({
|
|
37
36
|
status: targetStatus,
|
|
38
37
|
waitingFor: targetStatus === 'waiting' ? waitingFor : null,
|
|
39
38
|
updatedAt: new Date(),
|
|
40
39
|
})
|
|
41
|
-
.where(eq(schema.tasks.id, task.id))
|
|
42
|
-
.run();
|
|
40
|
+
.where(eq(schema.tasks.id, task.id));
|
|
43
41
|
console.log(fmt(i18n.commands.move.success, {
|
|
44
42
|
title: task.title,
|
|
45
43
|
status: i18n.status[targetStatus]
|
package/dist/commands/project.js
CHANGED
|
@@ -7,12 +7,11 @@ export async function addProject(name, options) {
|
|
|
7
7
|
const now = new Date();
|
|
8
8
|
const i18n = t();
|
|
9
9
|
// Check if project already exists
|
|
10
|
-
const
|
|
10
|
+
const existingProjects = await db
|
|
11
11
|
.select()
|
|
12
12
|
.from(schema.tasks)
|
|
13
|
-
.where(and(eq(schema.tasks.title, name), eq(schema.tasks.isProject, true)))
|
|
14
|
-
|
|
15
|
-
if (existing) {
|
|
13
|
+
.where(and(eq(schema.tasks.title, name), eq(schema.tasks.isProject, true)));
|
|
14
|
+
if (existingProjects.length > 0) {
|
|
16
15
|
console.error(fmt(i18n.commands.project.alreadyExists, { name }));
|
|
17
16
|
process.exit(1);
|
|
18
17
|
}
|
|
@@ -25,7 +24,7 @@ export async function addProject(name, options) {
|
|
|
25
24
|
createdAt: now,
|
|
26
25
|
updatedAt: now,
|
|
27
26
|
};
|
|
28
|
-
db.insert(schema.tasks).values(project)
|
|
27
|
+
await db.insert(schema.tasks).values(project);
|
|
29
28
|
console.log(fmt(i18n.commands.project.created, { name }));
|
|
30
29
|
}
|
|
31
30
|
export async function listProjectsCommand() {
|
|
@@ -38,11 +37,10 @@ export async function listProjectsCommand() {
|
|
|
38
37
|
done: i18n.projectStatus.completed,
|
|
39
38
|
};
|
|
40
39
|
for (const status of statuses) {
|
|
41
|
-
const projects = db
|
|
40
|
+
const projects = await db
|
|
42
41
|
.select()
|
|
43
42
|
.from(schema.tasks)
|
|
44
|
-
.where(and(eq(schema.tasks.isProject, true), eq(schema.tasks.status, status)))
|
|
45
|
-
.all();
|
|
43
|
+
.where(and(eq(schema.tasks.isProject, true), eq(schema.tasks.status, status)));
|
|
46
44
|
if (projects.length === 0 && status !== 'next')
|
|
47
45
|
continue;
|
|
48
46
|
console.log(`\n${statusLabels[status]} (${projects.length})`);
|
|
@@ -52,11 +50,10 @@ export async function listProjectsCommand() {
|
|
|
52
50
|
}
|
|
53
51
|
else {
|
|
54
52
|
for (const project of projects) {
|
|
55
|
-
const childTasks = db
|
|
53
|
+
const childTasks = await db
|
|
56
54
|
.select()
|
|
57
55
|
.from(schema.tasks)
|
|
58
|
-
.where(eq(schema.tasks.parentId, project.id))
|
|
59
|
-
.all();
|
|
56
|
+
.where(eq(schema.tasks.parentId, project.id));
|
|
60
57
|
const activeTasks = childTasks.filter(t => t.status !== 'done').length;
|
|
61
58
|
const doneTasks = childTasks.filter(t => t.status === 'done').length;
|
|
62
59
|
const shortId = project.id.slice(0, 8);
|
|
@@ -73,17 +70,15 @@ export async function showProject(projectId) {
|
|
|
73
70
|
const db = getDb();
|
|
74
71
|
const i18n = t();
|
|
75
72
|
// Find project by ID prefix or name
|
|
76
|
-
let projects = db
|
|
73
|
+
let projects = await db
|
|
77
74
|
.select()
|
|
78
75
|
.from(schema.tasks)
|
|
79
|
-
.where(and(eq(schema.tasks.isProject, true), like(schema.tasks.id, `${projectId}%`)))
|
|
80
|
-
.all();
|
|
76
|
+
.where(and(eq(schema.tasks.isProject, true), like(schema.tasks.id, `${projectId}%`)));
|
|
81
77
|
if (projects.length === 0) {
|
|
82
|
-
projects = db
|
|
78
|
+
projects = await db
|
|
83
79
|
.select()
|
|
84
80
|
.from(schema.tasks)
|
|
85
|
-
.where(and(eq(schema.tasks.isProject, true), eq(schema.tasks.title, projectId)))
|
|
86
|
-
.all();
|
|
81
|
+
.where(and(eq(schema.tasks.isProject, true), eq(schema.tasks.title, projectId)));
|
|
87
82
|
}
|
|
88
83
|
if (projects.length === 0) {
|
|
89
84
|
console.error(fmt(i18n.commands.project.notFound, { id: projectId }));
|
|
@@ -97,11 +92,10 @@ export async function showProject(projectId) {
|
|
|
97
92
|
process.exit(1);
|
|
98
93
|
}
|
|
99
94
|
const project = projects[0];
|
|
100
|
-
const childTasks = db
|
|
95
|
+
const childTasks = await db
|
|
101
96
|
.select()
|
|
102
97
|
.from(schema.tasks)
|
|
103
|
-
.where(eq(schema.tasks.parentId, project.id))
|
|
104
|
-
.all();
|
|
98
|
+
.where(eq(schema.tasks.parentId, project.id));
|
|
105
99
|
console.log(`\nProject: ${project.title}`);
|
|
106
100
|
console.log('─'.repeat(40));
|
|
107
101
|
if (project.description) {
|
|
@@ -135,11 +129,10 @@ export async function showProject(projectId) {
|
|
|
135
129
|
export async function completeProject(projectId) {
|
|
136
130
|
const db = getDb();
|
|
137
131
|
const i18n = t();
|
|
138
|
-
const projects = db
|
|
132
|
+
const projects = await db
|
|
139
133
|
.select()
|
|
140
134
|
.from(schema.tasks)
|
|
141
|
-
.where(and(eq(schema.tasks.isProject, true), like(schema.tasks.id, `${projectId}%`)))
|
|
142
|
-
.all();
|
|
135
|
+
.where(and(eq(schema.tasks.isProject, true), like(schema.tasks.id, `${projectId}%`)));
|
|
143
136
|
if (projects.length === 0) {
|
|
144
137
|
console.error(fmt(i18n.commands.project.notFound, { id: projectId }));
|
|
145
138
|
process.exit(1);
|
|
@@ -149,12 +142,11 @@ export async function completeProject(projectId) {
|
|
|
149
142
|
process.exit(1);
|
|
150
143
|
}
|
|
151
144
|
const project = projects[0];
|
|
152
|
-
db.update(schema.tasks)
|
|
145
|
+
await db.update(schema.tasks)
|
|
153
146
|
.set({
|
|
154
147
|
status: 'done',
|
|
155
148
|
updatedAt: new Date(),
|
|
156
149
|
})
|
|
157
|
-
.where(eq(schema.tasks.id, project.id))
|
|
158
|
-
.run();
|
|
150
|
+
.where(eq(schema.tasks.id, project.id));
|
|
159
151
|
console.log(fmt(i18n.commands.project.completed, { name: project.title }));
|
|
160
152
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runSetupWizard(): Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { render } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { SetupWizard } from '../ui/SetupWizard.js';
|
|
4
|
+
export async function runSetupWizard() {
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
const { unmount } = render(React.createElement(SetupWizard, {
|
|
7
|
+
onComplete: () => {
|
|
8
|
+
unmount();
|
|
9
|
+
resolve();
|
|
10
|
+
},
|
|
11
|
+
}));
|
|
12
|
+
});
|
|
13
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
|
+
import type { ThemeName } from './ui/theme/types.js';
|
|
1
2
|
export type Locale = 'en' | 'ja';
|
|
2
|
-
export type
|
|
3
|
+
export type ViewMode = 'gtd' | 'kanban';
|
|
4
|
+
export type { ThemeName };
|
|
5
|
+
export interface TursoConfig {
|
|
6
|
+
url: string;
|
|
7
|
+
authToken: string;
|
|
8
|
+
}
|
|
3
9
|
export interface Config {
|
|
4
10
|
locale: Locale;
|
|
5
11
|
db_path?: string;
|
|
6
12
|
theme: ThemeName;
|
|
13
|
+
viewMode: ViewMode;
|
|
14
|
+
turso?: TursoConfig;
|
|
7
15
|
}
|
|
8
16
|
export declare function loadConfig(): Config;
|
|
9
17
|
export declare function saveConfig(updates: Partial<Config>): void;
|
|
18
|
+
export declare function getTursoConfig(): TursoConfig | undefined;
|
|
19
|
+
export declare function setTursoConfig(config: TursoConfig | undefined): void;
|
|
20
|
+
export declare function isTursoEnabled(): boolean;
|
|
10
21
|
export declare function getDbPath(): string;
|
|
11
22
|
export declare function getLocale(): Locale;
|
|
12
23
|
export declare function setLocale(locale: Locale): void;
|
|
13
24
|
export declare function getThemeName(): ThemeName;
|
|
14
25
|
export declare function setThemeName(theme: ThemeName): void;
|
|
26
|
+
export declare function getViewMode(): ViewMode;
|
|
27
|
+
export declare function setViewMode(viewMode: ViewMode): void;
|
|
28
|
+
export declare function isFirstRun(): boolean;
|