@obsfx/trekker 1.6.0 → 1.7.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/dist/index.js +166 -235
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -952,6 +952,7 @@ function migrateHistoryTable(sqlite) {
|
|
|
952
952
|
}
|
|
953
953
|
}
|
|
954
954
|
function rebuildSearchIndex() {
|
|
955
|
+
getDb();
|
|
955
956
|
const sqlite = getSqliteInstance();
|
|
956
957
|
if (!sqlite) {
|
|
957
958
|
throw new Error("Database not initialized");
|
|
@@ -962,6 +963,13 @@ function rebuildSearchIndex() {
|
|
|
962
963
|
function getSqliteInstance() {
|
|
963
964
|
return sqliteInstance;
|
|
964
965
|
}
|
|
966
|
+
function requireSqliteInstance() {
|
|
967
|
+
getDb();
|
|
968
|
+
if (!sqliteInstance) {
|
|
969
|
+
throw new Error("Database not initialized");
|
|
970
|
+
}
|
|
971
|
+
return sqliteInstance;
|
|
972
|
+
}
|
|
965
973
|
function closeDb() {
|
|
966
974
|
if (sqliteInstance) {
|
|
967
975
|
sqliteInstance.close();
|
|
@@ -994,6 +1002,24 @@ var EPIC_STATUSES = [
|
|
|
994
1002
|
"completed",
|
|
995
1003
|
"archived"
|
|
996
1004
|
];
|
|
1005
|
+
var DEFAULT_PRIORITY = 2;
|
|
1006
|
+
var DEFAULT_TASK_STATUS = "todo";
|
|
1007
|
+
var DEFAULT_EPIC_STATUS = "todo";
|
|
1008
|
+
var PAGINATION_DEFAULTS = {
|
|
1009
|
+
LIST_PAGE_SIZE: 50,
|
|
1010
|
+
SEARCH_PAGE_SIZE: 20,
|
|
1011
|
+
HISTORY_PAGE_SIZE: 50,
|
|
1012
|
+
DEFAULT_PAGE: 1
|
|
1013
|
+
};
|
|
1014
|
+
var VALID_SORT_FIELDS = [
|
|
1015
|
+
"created",
|
|
1016
|
+
"updated",
|
|
1017
|
+
"title",
|
|
1018
|
+
"priority",
|
|
1019
|
+
"status"
|
|
1020
|
+
];
|
|
1021
|
+
var LIST_ENTITY_TYPES = ["epic", "task", "subtask"];
|
|
1022
|
+
var SEARCH_ENTITY_TYPES = ["epic", "task", "subtask", "comment"];
|
|
997
1023
|
var PREFIX_MAP = {
|
|
998
1024
|
task: "TREK",
|
|
999
1025
|
epic: "EPIC",
|
|
@@ -1556,6 +1582,24 @@ function error(message, details) {
|
|
|
1556
1582
|
}
|
|
1557
1583
|
}
|
|
1558
1584
|
}
|
|
1585
|
+
function handleCommandError(err) {
|
|
1586
|
+
error(err instanceof Error ? err.message : String(err));
|
|
1587
|
+
process.exit(1);
|
|
1588
|
+
}
|
|
1589
|
+
function handleNotFound(entityType, id) {
|
|
1590
|
+
error(`${entityType} not found: ${id}`);
|
|
1591
|
+
process.exit(1);
|
|
1592
|
+
}
|
|
1593
|
+
function outputResult(data, formatter, successMessage) {
|
|
1594
|
+
if (isToonMode()) {
|
|
1595
|
+
output(data);
|
|
1596
|
+
} else {
|
|
1597
|
+
if (successMessage) {
|
|
1598
|
+
success(successMessage);
|
|
1599
|
+
}
|
|
1600
|
+
console.log(formatter(data));
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1559
1603
|
function info(message) {
|
|
1560
1604
|
if (!toonMode) {
|
|
1561
1605
|
console.log(message);
|
|
@@ -1721,8 +1765,8 @@ function createEpic(input) {
|
|
|
1721
1765
|
projectId: project.id,
|
|
1722
1766
|
title: input.title,
|
|
1723
1767
|
description: input.description ?? null,
|
|
1724
|
-
status: input.status ??
|
|
1725
|
-
priority: input.priority ??
|
|
1768
|
+
status: input.status ?? DEFAULT_EPIC_STATUS,
|
|
1769
|
+
priority: input.priority ?? DEFAULT_PRIORITY,
|
|
1726
1770
|
createdAt: now,
|
|
1727
1771
|
updatedAt: now
|
|
1728
1772
|
};
|
|
@@ -1842,6 +1886,35 @@ function validateRequired(value, fieldName) {
|
|
|
1842
1886
|
throw new Error(`${fieldName} is required`);
|
|
1843
1887
|
}
|
|
1844
1888
|
}
|
|
1889
|
+
function validatePagination(limit, page) {
|
|
1890
|
+
if (isNaN(limit) || limit < 1) {
|
|
1891
|
+
throw new Error("Invalid limit value");
|
|
1892
|
+
}
|
|
1893
|
+
if (isNaN(page) || page < 1) {
|
|
1894
|
+
throw new Error("Invalid page value");
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
function validateListEntityTypes(types) {
|
|
1898
|
+
for (const t of types) {
|
|
1899
|
+
if (!LIST_ENTITY_TYPES.includes(t)) {
|
|
1900
|
+
throw new Error(`Invalid type: ${t}. Valid types: ${LIST_ENTITY_TYPES.join(", ")}`);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
function validateSearchEntityTypes(types) {
|
|
1905
|
+
for (const t of types) {
|
|
1906
|
+
if (!SEARCH_ENTITY_TYPES.includes(t)) {
|
|
1907
|
+
throw new Error(`Invalid type: ${t}. Valid types: ${SEARCH_ENTITY_TYPES.join(", ")}`);
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
function validatePriorities(priorities) {
|
|
1912
|
+
for (const p of priorities) {
|
|
1913
|
+
if (isNaN(p) || p < 0 || p > 5) {
|
|
1914
|
+
throw new Error(`Invalid priority: ${p}. Valid priorities: 0-5`);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1845
1918
|
|
|
1846
1919
|
// src/commands/epic.ts
|
|
1847
1920
|
var epicCommand = new Command3("epic").description("Manage epics");
|
|
@@ -1854,46 +1927,28 @@ epicCommand.command("create").description("Create a new epic").requiredOption("-
|
|
|
1854
1927
|
priority: parsePriority(options.priority),
|
|
1855
1928
|
status: parseStatus(options.status, "epic")
|
|
1856
1929
|
});
|
|
1857
|
-
|
|
1858
|
-
output(epic);
|
|
1859
|
-
} else {
|
|
1860
|
-
success(`Epic created: ${epic.id}`);
|
|
1861
|
-
console.log(formatEpic(epic));
|
|
1862
|
-
}
|
|
1930
|
+
outputResult(epic, formatEpic, `Epic created: ${epic.id}`);
|
|
1863
1931
|
} catch (err) {
|
|
1864
|
-
|
|
1865
|
-
process.exit(1);
|
|
1932
|
+
handleCommandError(err);
|
|
1866
1933
|
}
|
|
1867
1934
|
});
|
|
1868
1935
|
epicCommand.command("list").description("List all epics").option("-s, --status <status>", "Filter by status").action((options) => {
|
|
1869
1936
|
try {
|
|
1870
1937
|
const status = parseStatus(options.status, "epic");
|
|
1871
1938
|
const epics2 = listEpics(status);
|
|
1872
|
-
|
|
1873
|
-
output(epics2);
|
|
1874
|
-
} else {
|
|
1875
|
-
console.log(formatEpicList(epics2));
|
|
1876
|
-
}
|
|
1939
|
+
outputResult(epics2, formatEpicList);
|
|
1877
1940
|
} catch (err) {
|
|
1878
|
-
|
|
1879
|
-
process.exit(1);
|
|
1941
|
+
handleCommandError(err);
|
|
1880
1942
|
}
|
|
1881
1943
|
});
|
|
1882
1944
|
epicCommand.command("show <epic-id>").description("Show epic details").action((epicId) => {
|
|
1883
1945
|
try {
|
|
1884
1946
|
const epic = getEpic(epicId);
|
|
1885
|
-
if (!epic)
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
}
|
|
1889
|
-
if (isToonMode()) {
|
|
1890
|
-
output(epic);
|
|
1891
|
-
} else {
|
|
1892
|
-
console.log(formatEpic(epic));
|
|
1893
|
-
}
|
|
1947
|
+
if (!epic)
|
|
1948
|
+
return handleNotFound("Epic", epicId);
|
|
1949
|
+
outputResult(epic, formatEpic);
|
|
1894
1950
|
} catch (err) {
|
|
1895
|
-
|
|
1896
|
-
process.exit(1);
|
|
1951
|
+
handleCommandError(err);
|
|
1897
1952
|
}
|
|
1898
1953
|
});
|
|
1899
1954
|
epicCommand.command("update <epic-id>").description("Update an epic").option("-t, --title <title>", "New title").option("-d, --description <description>", "New description").option("-p, --priority <priority>", "New priority (0-5)").option("-s, --status <status>", "New status").action((epicId, options) => {
|
|
@@ -1904,15 +1959,9 @@ epicCommand.command("update <epic-id>").description("Update an epic").option("-t
|
|
|
1904
1959
|
priority: parsePriority(options.priority),
|
|
1905
1960
|
status: parseStatus(options.status, "epic")
|
|
1906
1961
|
});
|
|
1907
|
-
|
|
1908
|
-
output(epic);
|
|
1909
|
-
} else {
|
|
1910
|
-
success(`Epic updated: ${epic.id}`);
|
|
1911
|
-
console.log(formatEpic(epic));
|
|
1912
|
-
}
|
|
1962
|
+
outputResult(epic, formatEpic, `Epic updated: ${epic.id}`);
|
|
1913
1963
|
} catch (err) {
|
|
1914
|
-
|
|
1915
|
-
process.exit(1);
|
|
1964
|
+
handleCommandError(err);
|
|
1916
1965
|
}
|
|
1917
1966
|
});
|
|
1918
1967
|
epicCommand.command("delete <epic-id>").description("Delete an epic").action((epicId) => {
|
|
@@ -1920,8 +1969,7 @@ epicCommand.command("delete <epic-id>").description("Delete an epic").action((ep
|
|
|
1920
1969
|
deleteEpic(epicId);
|
|
1921
1970
|
success(`Epic deleted: ${epicId}`);
|
|
1922
1971
|
} catch (err) {
|
|
1923
|
-
|
|
1924
|
-
process.exit(1);
|
|
1972
|
+
handleCommandError(err);
|
|
1925
1973
|
}
|
|
1926
1974
|
});
|
|
1927
1975
|
epicCommand.command("complete <epic-id>").description("Complete an epic and archive all its tasks and subtasks").action((epicId) => {
|
|
@@ -1934,8 +1982,7 @@ epicCommand.command("complete <epic-id>").description("Complete an epic and arch
|
|
|
1934
1982
|
console.log(`Archived ${result.archived.tasks} task(s) and ${result.archived.subtasks} subtask(s)`);
|
|
1935
1983
|
}
|
|
1936
1984
|
} catch (err) {
|
|
1937
|
-
|
|
1938
|
-
process.exit(1);
|
|
1985
|
+
handleCommandError(err);
|
|
1939
1986
|
}
|
|
1940
1987
|
});
|
|
1941
1988
|
|
|
@@ -1971,8 +2018,8 @@ function createTask(input) {
|
|
|
1971
2018
|
parentTaskId: input.parentTaskId ?? null,
|
|
1972
2019
|
title: input.title,
|
|
1973
2020
|
description: input.description ?? null,
|
|
1974
|
-
priority: input.priority ??
|
|
1975
|
-
status: input.status ??
|
|
2021
|
+
priority: input.priority ?? DEFAULT_PRIORITY,
|
|
2022
|
+
status: input.status ?? DEFAULT_TASK_STATUS,
|
|
1976
2023
|
tags: input.tags ?? null,
|
|
1977
2024
|
createdAt: now,
|
|
1978
2025
|
updatedAt: now
|
|
@@ -2060,15 +2107,9 @@ taskCommand.command("create").description("Create a new task").requiredOption("-
|
|
|
2060
2107
|
tags: options.tags,
|
|
2061
2108
|
epicId: options.epic
|
|
2062
2109
|
});
|
|
2063
|
-
|
|
2064
|
-
output(task);
|
|
2065
|
-
} else {
|
|
2066
|
-
success(`Task created: ${task.id}`);
|
|
2067
|
-
console.log(formatTask(task));
|
|
2068
|
-
}
|
|
2110
|
+
outputResult(task, formatTask, `Task created: ${task.id}`);
|
|
2069
2111
|
} catch (err) {
|
|
2070
|
-
|
|
2071
|
-
process.exit(1);
|
|
2112
|
+
handleCommandError(err);
|
|
2072
2113
|
}
|
|
2073
2114
|
});
|
|
2074
2115
|
taskCommand.command("list").description("List all tasks").option("-s, --status <status>", "Filter by status").option("-e, --epic <epic-id>", "Filter by epic").action((options) => {
|
|
@@ -2079,31 +2120,19 @@ taskCommand.command("list").description("List all tasks").option("-s, --status <
|
|
|
2079
2120
|
epicId: options.epic,
|
|
2080
2121
|
parentTaskId: null
|
|
2081
2122
|
});
|
|
2082
|
-
|
|
2083
|
-
output(tasks2);
|
|
2084
|
-
} else {
|
|
2085
|
-
console.log(formatTaskList(tasks2));
|
|
2086
|
-
}
|
|
2123
|
+
outputResult(tasks2, formatTaskList);
|
|
2087
2124
|
} catch (err) {
|
|
2088
|
-
|
|
2089
|
-
process.exit(1);
|
|
2125
|
+
handleCommandError(err);
|
|
2090
2126
|
}
|
|
2091
2127
|
});
|
|
2092
2128
|
taskCommand.command("show <task-id>").description("Show task details").action((taskId) => {
|
|
2093
2129
|
try {
|
|
2094
2130
|
const task = getTask(taskId);
|
|
2095
|
-
if (!task)
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
}
|
|
2099
|
-
if (isToonMode()) {
|
|
2100
|
-
output(task);
|
|
2101
|
-
} else {
|
|
2102
|
-
console.log(formatTask(task));
|
|
2103
|
-
}
|
|
2131
|
+
if (!task)
|
|
2132
|
+
return handleNotFound("Task", taskId);
|
|
2133
|
+
outputResult(task, formatTask);
|
|
2104
2134
|
} catch (err) {
|
|
2105
|
-
|
|
2106
|
-
process.exit(1);
|
|
2135
|
+
handleCommandError(err);
|
|
2107
2136
|
}
|
|
2108
2137
|
});
|
|
2109
2138
|
taskCommand.command("update <task-id>").description("Update a task").option("-t, --title <title>", "New title").option("-d, --description <description>", "New description").option("-p, --priority <priority>", "New priority (0-5)").option("-s, --status <status>", "New status").option("--tags <tags>", "New tags (comma-separated)").option("-e, --epic <epic-id>", "New epic ID").option("--no-epic", "Remove from epic").action((taskId, options) => {
|
|
@@ -2115,9 +2144,8 @@ taskCommand.command("update <task-id>").description("Update a task").option("-t,
|
|
|
2115
2144
|
updateInput.description = options.description;
|
|
2116
2145
|
if (options.priority !== undefined)
|
|
2117
2146
|
updateInput.priority = parsePriority(options.priority);
|
|
2118
|
-
if (options.status !== undefined)
|
|
2147
|
+
if (options.status !== undefined)
|
|
2119
2148
|
updateInput.status = parseStatus(options.status, "task");
|
|
2120
|
-
}
|
|
2121
2149
|
if (options.tags !== undefined)
|
|
2122
2150
|
updateInput.tags = options.tags;
|
|
2123
2151
|
if (options.epic === false) {
|
|
@@ -2126,15 +2154,9 @@ taskCommand.command("update <task-id>").description("Update a task").option("-t,
|
|
|
2126
2154
|
updateInput.epicId = options.epic;
|
|
2127
2155
|
}
|
|
2128
2156
|
const task = updateTask(taskId, updateInput);
|
|
2129
|
-
|
|
2130
|
-
output(task);
|
|
2131
|
-
} else {
|
|
2132
|
-
success(`Task updated: ${task.id}`);
|
|
2133
|
-
console.log(formatTask(task));
|
|
2134
|
-
}
|
|
2157
|
+
outputResult(task, formatTask, `Task updated: ${task.id}`);
|
|
2135
2158
|
} catch (err) {
|
|
2136
|
-
|
|
2137
|
-
process.exit(1);
|
|
2159
|
+
handleCommandError(err);
|
|
2138
2160
|
}
|
|
2139
2161
|
});
|
|
2140
2162
|
taskCommand.command("delete <task-id>").description("Delete a task").action((taskId) => {
|
|
@@ -2142,8 +2164,7 @@ taskCommand.command("delete <task-id>").description("Delete a task").action((tas
|
|
|
2142
2164
|
deleteTask(taskId);
|
|
2143
2165
|
success(`Task deleted: ${taskId}`);
|
|
2144
2166
|
} catch (err) {
|
|
2145
|
-
|
|
2146
|
-
process.exit(1);
|
|
2167
|
+
handleCommandError(err);
|
|
2147
2168
|
}
|
|
2148
2169
|
});
|
|
2149
2170
|
|
|
@@ -2154,10 +2175,8 @@ subtaskCommand.command("create <parent-task-id>").description("Create a new subt
|
|
|
2154
2175
|
try {
|
|
2155
2176
|
validateRequired(options.title, "Title");
|
|
2156
2177
|
const parent = getTask(parentTaskId);
|
|
2157
|
-
if (!parent)
|
|
2158
|
-
|
|
2159
|
-
process.exit(1);
|
|
2160
|
-
}
|
|
2178
|
+
if (!parent)
|
|
2179
|
+
return handleNotFound("Parent task", parentTaskId);
|
|
2161
2180
|
const subtask = createTask({
|
|
2162
2181
|
title: options.title,
|
|
2163
2182
|
description: options.description,
|
|
@@ -2166,24 +2185,16 @@ subtaskCommand.command("create <parent-task-id>").description("Create a new subt
|
|
|
2166
2185
|
parentTaskId,
|
|
2167
2186
|
epicId: parent.epicId ?? undefined
|
|
2168
2187
|
});
|
|
2169
|
-
|
|
2170
|
-
output(subtask);
|
|
2171
|
-
} else {
|
|
2172
|
-
success(`Subtask created: ${subtask.id}`);
|
|
2173
|
-
console.log(formatTask(subtask));
|
|
2174
|
-
}
|
|
2188
|
+
outputResult(subtask, formatTask, `Subtask created: ${subtask.id}`);
|
|
2175
2189
|
} catch (err) {
|
|
2176
|
-
|
|
2177
|
-
process.exit(1);
|
|
2190
|
+
handleCommandError(err);
|
|
2178
2191
|
}
|
|
2179
2192
|
});
|
|
2180
2193
|
subtaskCommand.command("list <parent-task-id>").description("List all subtasks of a task").action((parentTaskId) => {
|
|
2181
2194
|
try {
|
|
2182
2195
|
const parent = getTask(parentTaskId);
|
|
2183
|
-
if (!parent)
|
|
2184
|
-
|
|
2185
|
-
process.exit(1);
|
|
2186
|
-
}
|
|
2196
|
+
if (!parent)
|
|
2197
|
+
return handleNotFound("Parent task", parentTaskId);
|
|
2187
2198
|
const subtasks = listSubtasks(parentTaskId);
|
|
2188
2199
|
if (isToonMode()) {
|
|
2189
2200
|
output(subtasks);
|
|
@@ -2196,17 +2207,14 @@ subtaskCommand.command("list <parent-task-id>").description("List all subtasks o
|
|
|
2196
2207
|
}
|
|
2197
2208
|
}
|
|
2198
2209
|
} catch (err) {
|
|
2199
|
-
|
|
2200
|
-
process.exit(1);
|
|
2210
|
+
handleCommandError(err);
|
|
2201
2211
|
}
|
|
2202
2212
|
});
|
|
2203
2213
|
subtaskCommand.command("update <subtask-id>").description("Update a subtask").option("-t, --title <title>", "New title").option("-d, --description <description>", "New description").option("-p, --priority <priority>", "New priority (0-5)").option("-s, --status <status>", "New status").action((subtaskId, options) => {
|
|
2204
2214
|
try {
|
|
2205
2215
|
const subtask = getTask(subtaskId);
|
|
2206
|
-
if (!subtask)
|
|
2207
|
-
|
|
2208
|
-
process.exit(1);
|
|
2209
|
-
}
|
|
2216
|
+
if (!subtask)
|
|
2217
|
+
return handleNotFound("Subtask", subtaskId);
|
|
2210
2218
|
if (!subtask.parentTaskId) {
|
|
2211
2219
|
error(`${subtaskId} is not a subtask. Use 'trekker task update' instead.`);
|
|
2212
2220
|
process.exit(1);
|
|
@@ -2218,28 +2226,19 @@ subtaskCommand.command("update <subtask-id>").description("Update a subtask").op
|
|
|
2218
2226
|
updateInput.description = options.description;
|
|
2219
2227
|
if (options.priority !== undefined)
|
|
2220
2228
|
updateInput.priority = parsePriority(options.priority);
|
|
2221
|
-
if (options.status !== undefined)
|
|
2229
|
+
if (options.status !== undefined)
|
|
2222
2230
|
updateInput.status = parseStatus(options.status, "task");
|
|
2223
|
-
}
|
|
2224
2231
|
const updated = updateTask(subtaskId, updateInput);
|
|
2225
|
-
|
|
2226
|
-
output(updated);
|
|
2227
|
-
} else {
|
|
2228
|
-
success(`Subtask updated: ${updated.id}`);
|
|
2229
|
-
console.log(formatTask(updated));
|
|
2230
|
-
}
|
|
2232
|
+
outputResult(updated, formatTask, `Subtask updated: ${updated.id}`);
|
|
2231
2233
|
} catch (err) {
|
|
2232
|
-
|
|
2233
|
-
process.exit(1);
|
|
2234
|
+
handleCommandError(err);
|
|
2234
2235
|
}
|
|
2235
2236
|
});
|
|
2236
2237
|
subtaskCommand.command("delete <subtask-id>").description("Delete a subtask").action((subtaskId) => {
|
|
2237
2238
|
try {
|
|
2238
2239
|
const subtask = getTask(subtaskId);
|
|
2239
|
-
if (!subtask)
|
|
2240
|
-
|
|
2241
|
-
process.exit(1);
|
|
2242
|
-
}
|
|
2240
|
+
if (!subtask)
|
|
2241
|
+
return handleNotFound("Subtask", subtaskId);
|
|
2243
2242
|
if (!subtask.parentTaskId) {
|
|
2244
2243
|
error(`${subtaskId} is not a subtask. Use 'trekker task delete' instead.`);
|
|
2245
2244
|
process.exit(1);
|
|
@@ -2247,8 +2246,7 @@ subtaskCommand.command("delete <subtask-id>").description("Delete a subtask").ac
|
|
|
2247
2246
|
deleteTask(subtaskId);
|
|
2248
2247
|
success(`Subtask deleted: ${subtaskId}`);
|
|
2249
2248
|
} catch (err) {
|
|
2250
|
-
|
|
2251
|
-
process.exit(1);
|
|
2249
|
+
handleCommandError(err);
|
|
2252
2250
|
}
|
|
2253
2251
|
});
|
|
2254
2252
|
|
|
@@ -2321,15 +2319,9 @@ commentCommand.command("add <task-id>").description("Add a comment to a task").r
|
|
|
2321
2319
|
author: options.author,
|
|
2322
2320
|
content: options.content
|
|
2323
2321
|
});
|
|
2324
|
-
|
|
2325
|
-
output(comment);
|
|
2326
|
-
} else {
|
|
2327
|
-
success(`Comment added: ${comment.id}`);
|
|
2328
|
-
console.log(formatComment(comment));
|
|
2329
|
-
}
|
|
2322
|
+
outputResult(comment, formatComment, `Comment added: ${comment.id}`);
|
|
2330
2323
|
} catch (err) {
|
|
2331
|
-
|
|
2332
|
-
process.exit(1);
|
|
2324
|
+
handleCommandError(err);
|
|
2333
2325
|
}
|
|
2334
2326
|
});
|
|
2335
2327
|
commentCommand.command("list <task-id>").description("List all comments on a task").action((taskId) => {
|
|
@@ -2346,8 +2338,7 @@ commentCommand.command("list <task-id>").description("List all comments on a tas
|
|
|
2346
2338
|
}
|
|
2347
2339
|
}
|
|
2348
2340
|
} catch (err) {
|
|
2349
|
-
|
|
2350
|
-
process.exit(1);
|
|
2341
|
+
handleCommandError(err);
|
|
2351
2342
|
}
|
|
2352
2343
|
});
|
|
2353
2344
|
commentCommand.command("update <comment-id>").description("Update a comment").requiredOption("-c, --content <content>", "New comment content").action((commentId, options) => {
|
|
@@ -2356,15 +2347,9 @@ commentCommand.command("update <comment-id>").description("Update a comment").re
|
|
|
2356
2347
|
const comment = updateComment(commentId, {
|
|
2357
2348
|
content: options.content
|
|
2358
2349
|
});
|
|
2359
|
-
|
|
2360
|
-
output(comment);
|
|
2361
|
-
} else {
|
|
2362
|
-
success(`Comment updated: ${comment.id}`);
|
|
2363
|
-
console.log(formatComment(comment));
|
|
2364
|
-
}
|
|
2350
|
+
outputResult(comment, formatComment, `Comment updated: ${comment.id}`);
|
|
2365
2351
|
} catch (err) {
|
|
2366
|
-
|
|
2367
|
-
process.exit(1);
|
|
2352
|
+
handleCommandError(err);
|
|
2368
2353
|
}
|
|
2369
2354
|
});
|
|
2370
2355
|
commentCommand.command("delete <comment-id>").description("Delete a comment").action((commentId) => {
|
|
@@ -2372,8 +2357,7 @@ commentCommand.command("delete <comment-id>").description("Delete a comment").ac
|
|
|
2372
2357
|
deleteComment(commentId);
|
|
2373
2358
|
success(`Comment deleted: ${commentId}`);
|
|
2374
2359
|
} catch (err) {
|
|
2375
|
-
|
|
2376
|
-
process.exit(1);
|
|
2360
|
+
handleCommandError(err);
|
|
2377
2361
|
}
|
|
2378
2362
|
});
|
|
2379
2363
|
|
|
@@ -2467,8 +2451,7 @@ depCommand.command("add <task-id> <depends-on-id>").description("Add a dependenc
|
|
|
2467
2451
|
success(`Dependency added: ${taskId} \u2192 depends on ${dependsOnId}`);
|
|
2468
2452
|
}
|
|
2469
2453
|
} catch (err) {
|
|
2470
|
-
|
|
2471
|
-
process.exit(1);
|
|
2454
|
+
handleCommandError(err);
|
|
2472
2455
|
}
|
|
2473
2456
|
});
|
|
2474
2457
|
depCommand.command("remove <task-id> <depends-on-id>").description("Remove a dependency").action((taskId, dependsOnId) => {
|
|
@@ -2476,8 +2459,7 @@ depCommand.command("remove <task-id> <depends-on-id>").description("Remove a dep
|
|
|
2476
2459
|
removeDependency(taskId, dependsOnId);
|
|
2477
2460
|
success(`Dependency removed: ${taskId} \u2192 ${dependsOnId}`);
|
|
2478
2461
|
} catch (err) {
|
|
2479
|
-
|
|
2480
|
-
process.exit(1);
|
|
2462
|
+
handleCommandError(err);
|
|
2481
2463
|
}
|
|
2482
2464
|
});
|
|
2483
2465
|
depCommand.command("list <task-id>").description("List dependencies for a task").action((taskId) => {
|
|
@@ -2495,8 +2477,7 @@ Blocks:`);
|
|
|
2495
2477
|
console.log(formatDependencyList(blocks, "blocks"));
|
|
2496
2478
|
}
|
|
2497
2479
|
} catch (err) {
|
|
2498
|
-
|
|
2499
|
-
process.exit(1);
|
|
2480
|
+
handleCommandError(err);
|
|
2500
2481
|
}
|
|
2501
2482
|
});
|
|
2502
2483
|
|
|
@@ -2910,13 +2891,9 @@ import { Command as Command10 } from "commander";
|
|
|
2910
2891
|
|
|
2911
2892
|
// src/services/search.ts
|
|
2912
2893
|
function search(query, options) {
|
|
2913
|
-
|
|
2914
|
-
const
|
|
2915
|
-
|
|
2916
|
-
throw new Error("Database not initialized");
|
|
2917
|
-
}
|
|
2918
|
-
const limit = options?.limit ?? 20;
|
|
2919
|
-
const page = options?.page ?? 1;
|
|
2894
|
+
const sqlite = requireSqliteInstance();
|
|
2895
|
+
const limit = options?.limit ?? PAGINATION_DEFAULTS.SEARCH_PAGE_SIZE;
|
|
2896
|
+
const page = options?.page ?? PAGINATION_DEFAULTS.DEFAULT_PAGE;
|
|
2920
2897
|
const offset = (page - 1) * limit;
|
|
2921
2898
|
const conditions = ["search_index MATCH ?"];
|
|
2922
2899
|
const params = [query];
|
|
@@ -2975,37 +2952,21 @@ var searchCommand = new Command10("search").description("Search across epics, ta
|
|
|
2975
2952
|
if (options.rebuildIndex) {
|
|
2976
2953
|
rebuildSearchIndex();
|
|
2977
2954
|
}
|
|
2978
|
-
const types = options.type ? options.type.split(",").map((t) => t.trim()) : undefined;
|
|
2979
2955
|
const limit = parseInt(options.limit, 10);
|
|
2980
2956
|
const page = parseInt(options.page, 10);
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
throw new Error("Invalid page value");
|
|
2986
|
-
}
|
|
2987
|
-
const validTypes = ["epic", "task", "subtask", "comment"];
|
|
2988
|
-
if (types) {
|
|
2989
|
-
for (const t of types) {
|
|
2990
|
-
if (!validTypes.includes(t)) {
|
|
2991
|
-
throw new Error(`Invalid type: ${t}. Valid types: ${validTypes.join(", ")}`);
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
}
|
|
2957
|
+
validatePagination(limit, page);
|
|
2958
|
+
const types = options.type ? options.type.split(",").map((t) => t.trim()) : undefined;
|
|
2959
|
+
if (types)
|
|
2960
|
+
validateSearchEntityTypes(types);
|
|
2995
2961
|
const result = search(query, {
|
|
2996
2962
|
types,
|
|
2997
2963
|
status: options.status,
|
|
2998
2964
|
limit,
|
|
2999
2965
|
page
|
|
3000
2966
|
});
|
|
3001
|
-
|
|
3002
|
-
output(result);
|
|
3003
|
-
} else {
|
|
3004
|
-
console.log(formatSearchResults(result));
|
|
3005
|
-
}
|
|
2967
|
+
outputResult(result, formatSearchResults);
|
|
3006
2968
|
} catch (err) {
|
|
3007
|
-
|
|
3008
|
-
process.exit(1);
|
|
2969
|
+
handleCommandError(err);
|
|
3009
2970
|
}
|
|
3010
2971
|
});
|
|
3011
2972
|
function formatSearchResults(result) {
|
|
@@ -3044,13 +3005,9 @@ import { Command as Command11 } from "commander";
|
|
|
3044
3005
|
|
|
3045
3006
|
// src/services/history.ts
|
|
3046
3007
|
function getHistory(options) {
|
|
3047
|
-
|
|
3048
|
-
const
|
|
3049
|
-
|
|
3050
|
-
throw new Error("Database not initialized");
|
|
3051
|
-
}
|
|
3052
|
-
const limit = options?.limit ?? 50;
|
|
3053
|
-
const page = options?.page ?? 1;
|
|
3008
|
+
const sqlite = requireSqliteInstance();
|
|
3009
|
+
const limit = options?.limit ?? PAGINATION_DEFAULTS.HISTORY_PAGE_SIZE;
|
|
3010
|
+
const page = options?.page ?? PAGINATION_DEFAULTS.DEFAULT_PAGE;
|
|
3054
3011
|
const offset = (page - 1) * limit;
|
|
3055
3012
|
const conditions = [];
|
|
3056
3013
|
const params = [];
|
|
@@ -3244,15 +3201,10 @@ var import_customParseFormat2 = __toESM(require_customParseFormat(), 1);
|
|
|
3244
3201
|
import { Command as Command12 } from "commander";
|
|
3245
3202
|
|
|
3246
3203
|
// src/services/list.ts
|
|
3247
|
-
var VALID_SORT_FIELDS = ["created", "updated", "title", "priority", "status"];
|
|
3248
3204
|
function listAll(options) {
|
|
3249
|
-
|
|
3250
|
-
const
|
|
3251
|
-
|
|
3252
|
-
throw new Error("Database not initialized");
|
|
3253
|
-
}
|
|
3254
|
-
const limit = options?.limit ?? 50;
|
|
3255
|
-
const page = options?.page ?? 1;
|
|
3205
|
+
const sqlite = requireSqliteInstance();
|
|
3206
|
+
const limit = options?.limit ?? PAGINATION_DEFAULTS.LIST_PAGE_SIZE;
|
|
3207
|
+
const page = options?.page ?? PAGINATION_DEFAULTS.DEFAULT_PAGE;
|
|
3256
3208
|
const offset = (page - 1) * limit;
|
|
3257
3209
|
const conditions = [];
|
|
3258
3210
|
const params = [];
|
|
@@ -3339,52 +3291,29 @@ function parseSort(sortStr) {
|
|
|
3339
3291
|
import_dayjs2.default.extend(import_customParseFormat2.default);
|
|
3340
3292
|
var listCommand = new Command12("list").description("List all epics, tasks, and subtasks").option("--type <types>", "Filter by type: epic,task,subtask (comma-separated)").option("--status <statuses>", "Filter by status (comma-separated)").option("--priority <levels>", "Filter by priority: 0-5 (comma-separated)").option("--since <date>", "Created after date (YYYY-MM-DD)").option("--until <date>", "Created before date (YYYY-MM-DD)").option("--sort <fields>", "Sort by fields (field:direction, comma-separated)", "created:desc").option("--limit <n>", "Results per page (default: 50)", "50").option("--page <n>", "Page number (default: 1)", "1").action((options) => {
|
|
3341
3293
|
try {
|
|
3294
|
+
const limit = parseInt(options.limit, 10);
|
|
3295
|
+
const page = parseInt(options.page, 10);
|
|
3296
|
+
validatePagination(limit, page);
|
|
3342
3297
|
const types = options.type ? options.type.split(",").map((t) => t.trim()) : undefined;
|
|
3298
|
+
if (types)
|
|
3299
|
+
validateListEntityTypes(types);
|
|
3343
3300
|
const statuses = options.status ? options.status.split(",").map((s) => s.trim()) : undefined;
|
|
3344
3301
|
const priorities = options.priority ? options.priority.split(",").map((p) => parseInt(p.trim(), 10)) : undefined;
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
if (isNaN(limit) || limit < 1) {
|
|
3348
|
-
throw new Error("Invalid limit value");
|
|
3349
|
-
}
|
|
3350
|
-
if (isNaN(page) || page < 1) {
|
|
3351
|
-
throw new Error("Invalid page value");
|
|
3352
|
-
}
|
|
3353
|
-
const validTypes = ["epic", "task", "subtask"];
|
|
3354
|
-
if (types) {
|
|
3355
|
-
for (const t of types) {
|
|
3356
|
-
if (!validTypes.includes(t)) {
|
|
3357
|
-
throw new Error(`Invalid type: ${t}. Valid types: ${validTypes.join(", ")}`);
|
|
3358
|
-
}
|
|
3359
|
-
}
|
|
3360
|
-
}
|
|
3361
|
-
if (priorities) {
|
|
3362
|
-
for (const p of priorities) {
|
|
3363
|
-
if (isNaN(p) || p < 0 || p > 5) {
|
|
3364
|
-
throw new Error(`Invalid priority: ${p}. Valid priorities: 0-5`);
|
|
3365
|
-
}
|
|
3366
|
-
}
|
|
3367
|
-
}
|
|
3302
|
+
if (priorities)
|
|
3303
|
+
validatePriorities(priorities);
|
|
3368
3304
|
let sort;
|
|
3369
3305
|
try {
|
|
3370
3306
|
sort = parseSort(options.sort);
|
|
3371
3307
|
} catch (err) {
|
|
3372
3308
|
throw new Error(`Invalid sort: ${err instanceof Error ? err.message : String(err)}`);
|
|
3373
3309
|
}
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
since = parseDate2(options.since);
|
|
3378
|
-
if (!since) {
|
|
3379
|
-
throw new Error("Invalid since date. Use YYYY-MM-DD format.");
|
|
3380
|
-
}
|
|
3310
|
+
const since = parseDate2(options.since);
|
|
3311
|
+
if (options.since && !since) {
|
|
3312
|
+
throw new Error("Invalid since date. Use YYYY-MM-DD format.");
|
|
3381
3313
|
}
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
throw new Error("Invalid until date. Use YYYY-MM-DD format.");
|
|
3386
|
-
}
|
|
3387
|
-
until = parsedUntil.endOf("day").toDate();
|
|
3314
|
+
const until = parseUntilDate(options.until);
|
|
3315
|
+
if (options.until && !until) {
|
|
3316
|
+
throw new Error("Invalid until date. Use YYYY-MM-DD format.");
|
|
3388
3317
|
}
|
|
3389
3318
|
const result = listAll({
|
|
3390
3319
|
types,
|
|
@@ -3396,22 +3325,22 @@ var listCommand = new Command12("list").description("List all epics, tasks, and
|
|
|
3396
3325
|
limit,
|
|
3397
3326
|
page
|
|
3398
3327
|
});
|
|
3399
|
-
|
|
3400
|
-
output(result);
|
|
3401
|
-
} else {
|
|
3402
|
-
console.log(formatListResults(result));
|
|
3403
|
-
}
|
|
3328
|
+
outputResult(result, formatListResults);
|
|
3404
3329
|
} catch (err) {
|
|
3405
|
-
|
|
3406
|
-
process.exit(1);
|
|
3330
|
+
handleCommandError(err);
|
|
3407
3331
|
}
|
|
3408
3332
|
});
|
|
3409
3333
|
function parseDate2(dateStr) {
|
|
3334
|
+
if (!dateStr)
|
|
3335
|
+
return;
|
|
3410
3336
|
const parsed = import_dayjs2.default(dateStr, "YYYY-MM-DD", true);
|
|
3411
|
-
|
|
3337
|
+
return parsed.isValid() ? parsed.toDate() : undefined;
|
|
3338
|
+
}
|
|
3339
|
+
function parseUntilDate(dateStr) {
|
|
3340
|
+
if (!dateStr)
|
|
3412
3341
|
return;
|
|
3413
|
-
|
|
3414
|
-
return parsed.toDate();
|
|
3342
|
+
const parsed = import_dayjs2.default(dateStr, "YYYY-MM-DD", true);
|
|
3343
|
+
return parsed.isValid() ? parsed.endOf("day").toDate() : undefined;
|
|
3415
3344
|
}
|
|
3416
3345
|
function formatListResults(result) {
|
|
3417
3346
|
const lines = [];
|
|
@@ -3443,7 +3372,7 @@ function formatItem(item) {
|
|
|
3443
3372
|
// package.json
|
|
3444
3373
|
var package_default = {
|
|
3445
3374
|
name: "@obsfx/trekker",
|
|
3446
|
-
version: "1.
|
|
3375
|
+
version: "1.7.0",
|
|
3447
3376
|
description: "A CLI-based issue tracker built for AI coding agents. Stores tasks, epics, and dependencies in a local SQLite database.",
|
|
3448
3377
|
type: "module",
|
|
3449
3378
|
main: "dist/index.js",
|
|
@@ -3457,6 +3386,8 @@ var package_default = {
|
|
|
3457
3386
|
scripts: {
|
|
3458
3387
|
build: "bun build src/index.ts --outdir dist --target bun --external commander --external drizzle-orm",
|
|
3459
3388
|
dev: "bun run src/index.ts",
|
|
3389
|
+
test: "bun test",
|
|
3390
|
+
"test:watch": "bun test --watch",
|
|
3460
3391
|
"db:generate": "drizzle-kit generate",
|
|
3461
3392
|
"db:migrate": "drizzle-kit migrate",
|
|
3462
3393
|
"release:patch": "npm version patch && npm run build && npm publish --access public",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obsfx/trekker",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "A CLI-based issue tracker built for AI coding agents. Stores tasks, epics, and dependencies in a local SQLite database.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "bun build src/index.ts --outdir dist --target bun --external commander --external drizzle-orm",
|
|
16
16
|
"dev": "bun run src/index.ts",
|
|
17
|
+
"test": "bun test",
|
|
18
|
+
"test:watch": "bun test --watch",
|
|
17
19
|
"db:generate": "drizzle-kit generate",
|
|
18
20
|
"db:migrate": "drizzle-kit migrate",
|
|
19
21
|
"release:patch": "npm version patch && npm run build && npm publish --access public",
|