@proletariat/cli 0.3.21 → 0.3.23
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/LICENSE +190 -21
- package/README.md +7 -7
- package/dist/commands/action/create.d.ts +0 -1
- package/dist/commands/action/delete.d.ts +0 -1
- package/dist/commands/action/index.d.ts +0 -1
- package/dist/commands/action/list.d.ts +0 -1
- package/dist/commands/action/list.js +2 -0
- package/dist/commands/action/run.d.ts +0 -1
- package/dist/commands/action/show.d.ts +0 -1
- package/dist/commands/action/update.d.ts +0 -1
- package/dist/commands/agent/auth.d.ts +0 -1
- package/dist/commands/agent/auth.js +3 -7
- package/dist/commands/agent/discover.d.ts +0 -1
- package/dist/commands/agent/discover.js +3 -7
- package/dist/commands/agent/index.d.ts +0 -1
- package/dist/commands/agent/index.js +2 -0
- package/dist/commands/agent/list.d.ts +0 -1
- package/dist/commands/agent/list.js +30 -1
- package/dist/commands/agent/login.d.ts +0 -1
- package/dist/commands/agent/login.js +2 -0
- package/dist/commands/agent/rebuild.d.ts +0 -1
- package/dist/commands/agent/rebuild.js +2 -0
- package/dist/commands/agent/remove.d.ts +0 -1
- package/dist/commands/agent/remove.js +2 -0
- package/dist/commands/agent/restart.d.ts +0 -1
- package/dist/commands/agent/restart.js +2 -0
- package/dist/commands/agent/shell.d.ts +0 -1
- package/dist/commands/agent/shell.js +2 -0
- package/dist/commands/agent/staff/add.d.ts +0 -1
- package/dist/commands/agent/staff/add.js +3 -7
- package/dist/commands/agent/staff/index.d.ts +0 -1
- package/dist/commands/agent/staff/index.js +2 -0
- package/dist/commands/agent/staff/remove.d.ts +0 -1
- package/dist/commands/agent/staff/remove.js +2 -0
- package/dist/commands/agent/status.d.ts +0 -1
- package/dist/commands/agent/status.js +2 -0
- package/dist/commands/agent/temp/cleanup.d.ts +0 -1
- package/dist/commands/agent/temp/cleanup.js +2 -0
- package/dist/commands/agent/temp/index.d.ts +0 -1
- package/dist/commands/agent/temp/index.js +2 -0
- package/dist/commands/agent/themes/index.d.ts +0 -1
- package/dist/commands/agent/themes/index.js +3 -7
- package/dist/commands/agent/themes/set.d.ts +0 -1
- package/dist/commands/agent/themes/set.js +3 -7
- package/dist/commands/agent/visit.d.ts +0 -1
- package/dist/commands/agent/visit.js +2 -0
- package/dist/commands/autocomplete/setup.d.ts +0 -1
- package/dist/commands/board/index.d.ts +0 -1
- package/dist/commands/board/view.d.ts +0 -1
- package/dist/commands/board/watch.d.ts +0 -1
- package/dist/commands/branch/create.d.ts +0 -1
- package/dist/commands/branch/create.js +2 -0
- package/dist/commands/branch/index.d.ts +0 -1
- package/dist/commands/branch/index.js +2 -0
- package/dist/commands/branch/list.d.ts +0 -1
- package/dist/commands/branch/validate.d.ts +0 -1
- package/dist/commands/branch/where.d.ts +0 -1
- package/dist/commands/branch/where.js +2 -0
- package/dist/commands/category/create.d.ts +18 -0
- package/dist/commands/category/create.js +108 -0
- package/dist/commands/category/delete.d.ts +17 -0
- package/dist/commands/category/delete.js +103 -0
- package/dist/commands/category/index.d.ts +15 -0
- package/dist/commands/category/index.js +87 -0
- package/dist/commands/category/list.d.ts +17 -0
- package/dist/commands/category/list.js +85 -0
- package/dist/commands/category/rename.d.ts +18 -0
- package/dist/commands/category/rename.js +127 -0
- package/dist/commands/claude.js +2 -0
- package/dist/commands/commit.js +2 -0
- package/dist/commands/config/index.js +2 -0
- package/dist/commands/docker/clean.d.ts +0 -1
- package/dist/commands/docker/index.d.ts +0 -1
- package/dist/commands/docker/prune.d.ts +0 -1
- package/dist/commands/docker/restart.d.ts +0 -1
- package/dist/commands/docker/stop.d.ts +0 -1
- package/dist/commands/epic/activate.d.ts +0 -1
- package/dist/commands/epic/activate.js +2 -0
- package/dist/commands/epic/archive.d.ts +0 -1
- package/dist/commands/epic/archive.js +2 -0
- package/dist/commands/epic/create.d.ts +0 -1
- package/dist/commands/epic/create.js +3 -1
- package/dist/commands/epic/delete.d.ts +14 -0
- package/dist/commands/epic/delete.js +129 -0
- package/dist/commands/epic/index.d.ts +0 -1
- package/dist/commands/epic/index.js +6 -0
- package/dist/commands/epic/link/block.d.ts +0 -1
- package/dist/commands/epic/link/block.js +2 -0
- package/dist/commands/epic/link/duplicates.d.ts +0 -1
- package/dist/commands/epic/link/duplicates.js +2 -0
- package/dist/commands/epic/link/index.d.ts +0 -1
- package/dist/commands/epic/link/index.js +2 -0
- package/dist/commands/epic/link/relates.d.ts +0 -1
- package/dist/commands/epic/link/relates.js +2 -0
- package/dist/commands/epic/link/remove.d.ts +0 -1
- package/dist/commands/epic/link/remove.js +2 -0
- package/dist/commands/epic/list.d.ts +0 -1
- package/dist/commands/epic/move.d.ts +0 -1
- package/dist/commands/epic/move.js +2 -0
- package/dist/commands/epic/progress.d.ts +0 -1
- package/dist/commands/epic/progress.js +2 -0
- package/dist/commands/epic/project.d.ts +0 -1
- package/dist/commands/epic/project.js +2 -0
- package/dist/commands/epic/reorder.d.ts +0 -1
- package/dist/commands/epic/reorder.js +2 -0
- package/dist/commands/epic/spec.d.ts +0 -1
- package/dist/commands/epic/spec.js +2 -0
- package/dist/commands/epic/ticket.d.ts +0 -1
- package/dist/commands/epic/ticket.js +2 -0
- package/dist/commands/epic/view.d.ts +0 -1
- package/dist/commands/epic/view.js +2 -0
- package/dist/commands/execution/config.d.ts +0 -1
- package/dist/commands/execution/config.js +2 -0
- package/dist/commands/execution/index.d.ts +0 -1
- package/dist/commands/execution/index.js +3 -1
- package/dist/commands/execution/list.d.ts +0 -1
- package/dist/commands/execution/logs.d.ts +0 -1
- package/dist/commands/execution/logs.js +3 -1
- package/dist/commands/execution/stop.d.ts +0 -1
- package/dist/commands/execution/stop.js +3 -1
- package/dist/commands/execution/view.d.ts +0 -1
- package/dist/commands/execution/view.js +3 -1
- package/dist/commands/gh/index.d.ts +0 -1
- package/dist/commands/gh/login.d.ts +0 -1
- package/dist/commands/gh/status.d.ts +0 -1
- package/dist/commands/gh/token.d.ts +0 -1
- package/dist/commands/init.js +2 -0
- package/dist/commands/phase/create.d.ts +0 -1
- package/dist/commands/phase/create.js +1 -2
- package/dist/commands/phase/delete.d.ts +0 -1
- package/dist/commands/phase/delete.js +1 -1
- package/dist/commands/phase/list.d.ts +0 -1
- package/dist/commands/phase/move.d.ts +0 -1
- package/dist/commands/phase/move.js +2 -2
- package/dist/commands/phase/template/apply.d.ts +0 -1
- package/dist/commands/phase/template/apply.js +2 -0
- package/dist/commands/phase/template/create.d.ts +0 -1
- package/dist/commands/phase/template/create.js +0 -1
- package/dist/commands/phase/template/delete.d.ts +0 -1
- package/dist/commands/phase/template/delete.js +2 -0
- package/dist/commands/phase/template/index.d.ts +0 -1
- package/dist/commands/phase/template/index.js +2 -0
- package/dist/commands/phase/template/list.d.ts +0 -1
- package/dist/commands/phase/template/list.js +2 -0
- package/dist/commands/phase/template/update.d.ts +0 -1
- package/dist/commands/phase/update.d.ts +0 -1
- package/dist/commands/phase/update.js +2 -2
- package/dist/commands/pmo/init.js +2 -0
- package/dist/commands/pr/create.d.ts +0 -1
- package/dist/commands/pr/index.d.ts +0 -1
- package/dist/commands/pr/link.d.ts +0 -1
- package/dist/commands/pr/list.d.ts +0 -1
- package/dist/commands/pr/status.d.ts +0 -1
- package/dist/commands/project/archive.d.ts +0 -1
- package/dist/commands/project/create.d.ts +0 -1
- package/dist/commands/project/delete.d.ts +0 -1
- package/dist/commands/project/index.d.ts +0 -1
- package/dist/commands/project/list.d.ts +0 -1
- package/dist/commands/project/spec.d.ts +0 -1
- package/dist/commands/project/unarchive.d.ts +0 -1
- package/dist/commands/project/update.d.ts +0 -1
- package/dist/commands/project/view.d.ts +0 -1
- package/dist/commands/repo/add.d.ts +0 -1
- package/dist/commands/repo/add.js +2 -0
- package/dist/commands/repo/index.d.ts +0 -1
- package/dist/commands/repo/list.d.ts +0 -1
- package/dist/commands/repo/remove.d.ts +0 -1
- package/dist/commands/repo/view.d.ts +0 -1
- package/dist/commands/roadmap/add-project.d.ts +0 -1
- package/dist/commands/roadmap/add-project.js +2 -0
- package/dist/commands/roadmap/create.d.ts +0 -1
- package/dist/commands/roadmap/create.js +2 -0
- package/dist/commands/roadmap/delete.d.ts +0 -1
- package/dist/commands/roadmap/delete.js +12 -1
- package/dist/commands/roadmap/generate.d.ts +0 -1
- package/dist/commands/roadmap/generate.js +2 -0
- package/dist/commands/roadmap/index.d.ts +0 -1
- package/dist/commands/roadmap/index.js +2 -0
- package/dist/commands/roadmap/list.d.ts +0 -1
- package/dist/commands/roadmap/remove-project.d.ts +0 -1
- package/dist/commands/roadmap/remove-project.js +2 -0
- package/dist/commands/roadmap/reorder.d.ts +0 -1
- package/dist/commands/roadmap/reorder.js +2 -0
- package/dist/commands/roadmap/update.d.ts +0 -1
- package/dist/commands/roadmap/update.js +2 -0
- package/dist/commands/roadmap/view.d.ts +0 -1
- package/dist/commands/roadmap/view.js +2 -0
- package/dist/commands/session/attach.d.ts +0 -1
- package/dist/commands/session/attach.js +9 -0
- package/dist/commands/session/index.d.ts +0 -1
- package/dist/commands/session/index.js +2 -0
- package/dist/commands/session/list.d.ts +0 -1
- package/dist/commands/spec/create.d.ts +0 -1
- package/dist/commands/spec/create.js +1 -1
- package/dist/commands/spec/delete.d.ts +0 -1
- package/dist/commands/spec/edit.d.ts +0 -1
- package/dist/commands/spec/index.d.ts +0 -1
- package/dist/commands/spec/link/depends.d.ts +0 -1
- package/dist/commands/spec/link/duplicates.d.ts +0 -1
- package/dist/commands/spec/link/index.d.ts +0 -1
- package/dist/commands/spec/link/relates.d.ts +0 -1
- package/dist/commands/spec/link/remove.d.ts +0 -1
- package/dist/commands/spec/list.d.ts +0 -1
- package/dist/commands/spec/plan.d.ts +0 -1
- package/dist/commands/spec/ticket.d.ts +0 -3
- package/dist/commands/spec/ticket.js +7 -38
- package/dist/commands/spec/view.d.ts +0 -1
- package/dist/commands/status/category.d.ts +14 -0
- package/dist/commands/status/category.js +63 -0
- package/dist/commands/status/create.d.ts +0 -1
- package/dist/commands/status/create.js +1 -1
- package/dist/commands/status/delete.d.ts +0 -1
- package/dist/commands/status/index.d.ts +0 -1
- package/dist/commands/status/list.d.ts +0 -1
- package/dist/commands/status/move.d.ts +0 -1
- package/dist/commands/status/update.d.ts +0 -1
- package/dist/commands/template/delete.d.ts +0 -1
- package/dist/commands/template/delete.js +2 -0
- package/dist/commands/template/index.d.ts +0 -1
- package/dist/commands/template/list.d.ts +0 -1
- package/dist/commands/template/list.js +2 -0
- package/dist/commands/template/phase/apply.js +2 -0
- package/dist/commands/template/phase/create.d.ts +0 -1
- package/dist/commands/template/phase/create.js +3 -9
- package/dist/commands/template/phase/delete.js +2 -0
- package/dist/commands/template/phase/index.d.ts +0 -1
- package/dist/commands/template/phase/list.js +2 -0
- package/dist/commands/template/phase/update.js +2 -0
- package/dist/commands/template/ticket/apply.js +2 -0
- package/dist/commands/template/ticket/create.js +2 -0
- package/dist/commands/template/ticket/delete.js +2 -0
- package/dist/commands/template/ticket/index.d.ts +0 -1
- package/dist/commands/template/ticket/list.js +2 -0
- package/dist/commands/template/ticket/save.d.ts +0 -1
- package/dist/commands/template/ticket/save.js +0 -6
- package/dist/commands/terminal/title.d.ts +0 -1
- package/dist/commands/ticket/bulk.d.ts +0 -1
- package/dist/commands/ticket/bulk.js +2 -0
- package/dist/commands/ticket/category.d.ts +14 -0
- package/dist/commands/ticket/category.js +63 -0
- package/dist/commands/ticket/complete.d.ts +0 -1
- package/dist/commands/ticket/complete.js +2 -0
- package/dist/commands/ticket/create.d.ts +0 -1
- package/dist/commands/ticket/create.js +6 -4
- package/dist/commands/ticket/delete.d.ts +0 -1
- package/dist/commands/ticket/delete.js +2 -0
- package/dist/commands/ticket/edit.d.ts +0 -1
- package/dist/commands/ticket/edit.js +4 -2
- package/dist/commands/ticket/epic.d.ts +0 -1
- package/dist/commands/ticket/epic.js +2 -0
- package/dist/commands/ticket/index.d.ts +0 -1
- package/dist/commands/ticket/index.js +2 -0
- package/dist/commands/ticket/link/block.d.ts +0 -1
- package/dist/commands/ticket/link/block.js +2 -0
- package/dist/commands/ticket/link/duplicates.d.ts +0 -1
- package/dist/commands/ticket/link/duplicates.js +2 -0
- package/dist/commands/ticket/link/index.d.ts +0 -1
- package/dist/commands/ticket/link/index.js +2 -0
- package/dist/commands/ticket/link/relates.d.ts +0 -1
- package/dist/commands/ticket/link/relates.js +2 -0
- package/dist/commands/ticket/link/remove.d.ts +0 -1
- package/dist/commands/ticket/link/remove.js +2 -0
- package/dist/commands/ticket/list.d.ts +0 -1
- package/dist/commands/ticket/move.d.ts +0 -1
- package/dist/commands/ticket/move.js +2 -0
- package/dist/commands/ticket/project.d.ts +0 -1
- package/dist/commands/ticket/project.js +2 -0
- package/dist/commands/ticket/reassign.d.ts +0 -1
- package/dist/commands/ticket/reassign.js +29 -0
- package/dist/commands/ticket/spec.d.ts +0 -1
- package/dist/commands/ticket/spec.js +2 -0
- package/dist/commands/ticket/status.d.ts +0 -1
- package/dist/commands/ticket/status.js +2 -0
- package/dist/commands/ticket/template/apply.d.ts +0 -1
- package/dist/commands/ticket/template/apply.js +2 -0
- package/dist/commands/ticket/template/create.d.ts +0 -1
- package/dist/commands/ticket/template/create.js +4 -2
- package/dist/commands/ticket/template/delete.d.ts +0 -1
- package/dist/commands/ticket/template/delete.js +2 -0
- package/dist/commands/ticket/template/index.d.ts +0 -1
- package/dist/commands/ticket/template/index.js +2 -0
- package/dist/commands/ticket/template/list.d.ts +0 -1
- package/dist/commands/ticket/template/list.js +2 -0
- package/dist/commands/ticket/template/save.d.ts +0 -1
- package/dist/commands/ticket/template/save.js +2 -0
- package/dist/commands/ticket/update.d.ts +0 -1
- package/dist/commands/ticket/update.js +2 -0
- package/dist/commands/ticket/view.d.ts +0 -1
- package/dist/commands/ticket/view.js +2 -0
- package/dist/commands/work/complete.d.ts +0 -1
- package/dist/commands/work/complete.js +2 -0
- package/dist/commands/work/index.d.ts +0 -1
- package/dist/commands/work/index.js +2 -0
- package/dist/commands/work/ready.d.ts +1 -2
- package/dist/commands/work/ready.js +11 -5
- package/dist/commands/work/revise.d.ts +0 -1
- package/dist/commands/work/revise.js +3 -1
- package/dist/commands/work/spawn-all.d.ts +0 -1
- package/dist/commands/work/spawn-all.js +2 -0
- package/dist/commands/work/spawn.d.ts +0 -1
- package/dist/commands/work/spawn.js +2 -0
- package/dist/commands/work/start.d.ts +0 -1
- package/dist/commands/work/start.js +6 -0
- package/dist/commands/work/watch.d.ts +0 -1
- package/dist/commands/work/watch.js +3 -1
- package/dist/commands/workflow/create.d.ts +0 -1
- package/dist/commands/workflow/delete.d.ts +0 -1
- package/dist/commands/workflow/index.d.ts +0 -1
- package/dist/commands/workflow/index.js +2 -0
- package/dist/commands/workflow/list.d.ts +0 -1
- package/dist/commands/workflow/switch.d.ts +0 -1
- package/dist/commands/workflow/view.d.ts +0 -1
- package/dist/commands/workspace/list.js +2 -0
- package/dist/commands/workspace/prune.d.ts +13 -0
- package/dist/commands/workspace/prune.js +186 -0
- package/dist/commands/workspace/remove.js +2 -0
- package/dist/commands/workspace/use.js +2 -0
- package/dist/lib/pmo/base-command.d.ts +2 -4
- package/dist/lib/pmo/base-command.js +8 -10
- package/dist/lib/pmo/schema.d.ts +2 -0
- package/dist/lib/pmo/schema.js +17 -0
- package/dist/lib/pmo/storage/base.d.ts +4 -0
- package/dist/lib/pmo/storage/base.js +31 -0
- package/dist/lib/pmo/storage/categories.d.ts +50 -0
- package/dist/lib/pmo/storage/categories.js +205 -0
- package/dist/lib/pmo/storage/index.d.ts +14 -1
- package/dist/lib/pmo/storage/index.js +35 -1
- package/dist/lib/pmo/storage/tickets.d.ts +5 -0
- package/dist/lib/pmo/storage/tickets.js +31 -3
- package/dist/lib/pmo/storage/types.d.ts +10 -0
- package/dist/lib/pmo/types.d.ts +25 -0
- package/dist/lib/prompt-json.d.ts +10 -16
- package/dist/lib/prompt-json.js +8 -16
- package/oclif.manifest.json +4037 -4099
- package/package.json +5 -4
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* This module handles database setup and provides shared utilities.
|
|
4
4
|
*/
|
|
5
5
|
import { PMO_TABLES, PMO_SCHEMA_SQL, validateTicketSchema } from '../schema.js';
|
|
6
|
+
import { TICKET_CATEGORIES, STATE_CATEGORY_ORDER } from '../types.js';
|
|
6
7
|
import { BUILTIN_TEMPLATES } from '../templates-builtin.js';
|
|
7
8
|
const T = PMO_TABLES;
|
|
8
9
|
/**
|
|
@@ -802,6 +803,36 @@ Why is this refactor needed?
|
|
|
802
803
|
insertTemplate.run(template.id, template.name, template.description || null, template.titlePattern || null, template.descriptionTemplate || null, template.defaultPriority || null, template.defaultCategory || null, null, null, null, '[]', JSON.stringify(template.suggestedSubtasks || []), now);
|
|
803
804
|
}
|
|
804
805
|
}
|
|
806
|
+
/**
|
|
807
|
+
* Seed built-in categories from TICKET_CATEGORIES and STATE_CATEGORY_ORDER.
|
|
808
|
+
*/
|
|
809
|
+
export function seedBuiltinCategories(db) {
|
|
810
|
+
const insertCategory = db.prepare(`
|
|
811
|
+
INSERT OR IGNORE INTO ${T.categories} (id, name, type, description, position, is_builtin, created_at)
|
|
812
|
+
VALUES (?, ?, ?, ?, ?, 1, ?)
|
|
813
|
+
`);
|
|
814
|
+
const now = new Date().toISOString();
|
|
815
|
+
// Seed ticket categories from TICKET_CATEGORIES
|
|
816
|
+
for (let i = 0; i < TICKET_CATEGORIES.length; i++) {
|
|
817
|
+
const category = TICKET_CATEGORIES[i];
|
|
818
|
+
const id = `ticket-${category}`;
|
|
819
|
+
insertCategory.run(id, category, 'ticket', null, i, now);
|
|
820
|
+
}
|
|
821
|
+
// Seed status categories from STATE_CATEGORY_ORDER
|
|
822
|
+
const statusCategoryDescriptions = {
|
|
823
|
+
triage: 'Inbox - needs review before entering workflow',
|
|
824
|
+
backlog: 'Not yet scheduled for work',
|
|
825
|
+
unstarted: 'Scheduled but work has not begun',
|
|
826
|
+
started: 'Work is actively in progress',
|
|
827
|
+
completed: 'Work finished successfully',
|
|
828
|
+
canceled: 'Work will not be done',
|
|
829
|
+
};
|
|
830
|
+
for (let i = 0; i < STATE_CATEGORY_ORDER.length; i++) {
|
|
831
|
+
const category = STATE_CATEGORY_ORDER[i];
|
|
832
|
+
const id = `status-${category}`;
|
|
833
|
+
insertCategory.run(id, category, 'status', statusCategoryDescriptions[category] || null, i, now);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
805
836
|
/**
|
|
806
837
|
* Update board timestamp for a project.
|
|
807
838
|
*/
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Category operations.
|
|
3
|
+
* Manages ticket and status categories.
|
|
4
|
+
*/
|
|
5
|
+
import { Category, CategoryFilter, CategoryType } from '../types.js';
|
|
6
|
+
import { StorageContext } from './types.js';
|
|
7
|
+
export declare class CategoryStorage {
|
|
8
|
+
private ctx;
|
|
9
|
+
constructor(ctx: StorageContext);
|
|
10
|
+
/**
|
|
11
|
+
* List categories.
|
|
12
|
+
*/
|
|
13
|
+
listCategories(filter?: CategoryFilter): Promise<Category[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Get a category by ID.
|
|
16
|
+
*/
|
|
17
|
+
getCategory(id: string): Promise<Category | null>;
|
|
18
|
+
/**
|
|
19
|
+
* Get a category by name and type.
|
|
20
|
+
*/
|
|
21
|
+
getCategoryByName(name: string, type: CategoryType): Promise<Category | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Create a new category.
|
|
24
|
+
*/
|
|
25
|
+
createCategory(category: Partial<Category> & {
|
|
26
|
+
name: string;
|
|
27
|
+
type: CategoryType;
|
|
28
|
+
}): Promise<Category>;
|
|
29
|
+
/**
|
|
30
|
+
* Update a category.
|
|
31
|
+
*/
|
|
32
|
+
updateCategory(id: string, changes: Partial<Category>): Promise<Category>;
|
|
33
|
+
/**
|
|
34
|
+
* Rename a category.
|
|
35
|
+
*/
|
|
36
|
+
renameCategory(id: string, newName: string): Promise<Category>;
|
|
37
|
+
/**
|
|
38
|
+
* Delete a category.
|
|
39
|
+
*/
|
|
40
|
+
deleteCategory(id: string): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Get category names for a type (for validation and autocomplete).
|
|
43
|
+
*/
|
|
44
|
+
getCategoryNames(type: CategoryType): Promise<string[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Check if a category name is valid for a type.
|
|
47
|
+
*/
|
|
48
|
+
isValidCategory(name: string, type: CategoryType): Promise<boolean>;
|
|
49
|
+
private rowToCategory;
|
|
50
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Category operations.
|
|
3
|
+
* Manages ticket and status categories.
|
|
4
|
+
*/
|
|
5
|
+
import { PMO_TABLES } from '../schema.js';
|
|
6
|
+
import { PMOError } from '../types.js';
|
|
7
|
+
import { slugify } from '../utils.js';
|
|
8
|
+
const T = PMO_TABLES;
|
|
9
|
+
export class CategoryStorage {
|
|
10
|
+
ctx;
|
|
11
|
+
constructor(ctx) {
|
|
12
|
+
this.ctx = ctx;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* List categories.
|
|
16
|
+
*/
|
|
17
|
+
async listCategories(filter) {
|
|
18
|
+
let sql = `SELECT * FROM ${T.categories}`;
|
|
19
|
+
const conditions = [];
|
|
20
|
+
const params = [];
|
|
21
|
+
if (filter?.type) {
|
|
22
|
+
conditions.push('type = ?');
|
|
23
|
+
params.push(filter.type);
|
|
24
|
+
}
|
|
25
|
+
if (filter?.isBuiltin !== undefined) {
|
|
26
|
+
conditions.push('is_builtin = ?');
|
|
27
|
+
params.push(filter.isBuiltin ? 1 : 0);
|
|
28
|
+
}
|
|
29
|
+
if (filter?.search) {
|
|
30
|
+
conditions.push('(name LIKE ? OR description LIKE ?)');
|
|
31
|
+
params.push(`%${filter.search}%`, `%${filter.search}%`);
|
|
32
|
+
}
|
|
33
|
+
if (conditions.length > 0) {
|
|
34
|
+
sql += ` WHERE ${conditions.join(' AND ')}`;
|
|
35
|
+
}
|
|
36
|
+
sql += ' ORDER BY type, position ASC, name ASC';
|
|
37
|
+
const rows = this.ctx.db.prepare(sql).all(...params);
|
|
38
|
+
return rows.map((row) => this.rowToCategory(row));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get a category by ID.
|
|
42
|
+
*/
|
|
43
|
+
async getCategory(id) {
|
|
44
|
+
const row = this.ctx.db.prepare(`SELECT * FROM ${T.categories} WHERE id = ?`).get(id);
|
|
45
|
+
if (!row)
|
|
46
|
+
return null;
|
|
47
|
+
return this.rowToCategory(row);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get a category by name and type.
|
|
51
|
+
*/
|
|
52
|
+
async getCategoryByName(name, type) {
|
|
53
|
+
const row = this.ctx.db.prepare(`
|
|
54
|
+
SELECT * FROM ${T.categories} WHERE LOWER(name) = LOWER(?) AND type = ?
|
|
55
|
+
`).get(name, type);
|
|
56
|
+
if (!row)
|
|
57
|
+
return null;
|
|
58
|
+
return this.rowToCategory(row);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a new category.
|
|
62
|
+
*/
|
|
63
|
+
async createCategory(category) {
|
|
64
|
+
if (!category.name) {
|
|
65
|
+
throw new PMOError('INVALID', 'Category name is required');
|
|
66
|
+
}
|
|
67
|
+
if (!category.type) {
|
|
68
|
+
throw new PMOError('INVALID', 'Category type is required');
|
|
69
|
+
}
|
|
70
|
+
const id = category.id || slugify(category.name);
|
|
71
|
+
// Check for duplicate name within the same type
|
|
72
|
+
const existing = this.ctx.db.prepare(`
|
|
73
|
+
SELECT id FROM ${T.categories} WHERE LOWER(name) = LOWER(?) AND type = ?
|
|
74
|
+
`).get(category.name, category.type);
|
|
75
|
+
if (existing) {
|
|
76
|
+
throw new PMOError('CONFLICT', `Category "${category.name}" already exists for type "${category.type}"`);
|
|
77
|
+
}
|
|
78
|
+
// Get the next position
|
|
79
|
+
const maxPos = this.ctx.db.prepare(`
|
|
80
|
+
SELECT MAX(position) as max FROM ${T.categories} WHERE type = ?
|
|
81
|
+
`).get(category.type);
|
|
82
|
+
const position = category.position ?? (maxPos.max ?? -1) + 1;
|
|
83
|
+
const now = new Date().toISOString();
|
|
84
|
+
this.ctx.db.prepare(`
|
|
85
|
+
INSERT INTO ${T.categories} (id, name, type, description, color, position, is_builtin, created_at)
|
|
86
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
87
|
+
`).run(id, category.name, category.type, category.description || null, category.color || null, position, category.isBuiltin ? 1 : 0, now);
|
|
88
|
+
return {
|
|
89
|
+
id,
|
|
90
|
+
name: category.name,
|
|
91
|
+
type: category.type,
|
|
92
|
+
description: category.description,
|
|
93
|
+
color: category.color,
|
|
94
|
+
position,
|
|
95
|
+
isBuiltin: category.isBuiltin || false,
|
|
96
|
+
createdAt: new Date(now),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Update a category.
|
|
101
|
+
*/
|
|
102
|
+
async updateCategory(id, changes) {
|
|
103
|
+
const existing = await this.getCategory(id);
|
|
104
|
+
if (!existing) {
|
|
105
|
+
throw new PMOError('NOT_FOUND', `Category not found: ${id}`);
|
|
106
|
+
}
|
|
107
|
+
if (existing.isBuiltin) {
|
|
108
|
+
throw new PMOError('INVALID', 'Cannot modify built-in categories');
|
|
109
|
+
}
|
|
110
|
+
// Check for duplicate name if name is changing
|
|
111
|
+
if (changes.name && changes.name.toLowerCase() !== existing.name.toLowerCase()) {
|
|
112
|
+
const dup = this.ctx.db.prepare(`
|
|
113
|
+
SELECT id FROM ${T.categories} WHERE LOWER(name) = LOWER(?) AND type = ? AND id != ?
|
|
114
|
+
`).get(changes.name, existing.type, id);
|
|
115
|
+
if (dup) {
|
|
116
|
+
throw new PMOError('CONFLICT', `Category "${changes.name}" already exists for type "${existing.type}"`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const updates = [];
|
|
120
|
+
const params = [];
|
|
121
|
+
if (changes.name !== undefined) {
|
|
122
|
+
updates.push('name = ?');
|
|
123
|
+
params.push(changes.name);
|
|
124
|
+
}
|
|
125
|
+
if (changes.description !== undefined) {
|
|
126
|
+
updates.push('description = ?');
|
|
127
|
+
params.push(changes.description || null);
|
|
128
|
+
}
|
|
129
|
+
if (changes.color !== undefined) {
|
|
130
|
+
updates.push('color = ?');
|
|
131
|
+
params.push(changes.color || null);
|
|
132
|
+
}
|
|
133
|
+
if (changes.position !== undefined) {
|
|
134
|
+
updates.push('position = ?');
|
|
135
|
+
params.push(changes.position);
|
|
136
|
+
}
|
|
137
|
+
if (updates.length > 0) {
|
|
138
|
+
params.push(id);
|
|
139
|
+
this.ctx.db.prepare(`UPDATE ${T.categories} SET ${updates.join(', ')} WHERE id = ?`).run(...params);
|
|
140
|
+
}
|
|
141
|
+
return (await this.getCategory(id));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Rename a category.
|
|
145
|
+
*/
|
|
146
|
+
async renameCategory(id, newName) {
|
|
147
|
+
return this.updateCategory(id, { name: newName });
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Delete a category.
|
|
151
|
+
*/
|
|
152
|
+
async deleteCategory(id) {
|
|
153
|
+
const existing = await this.getCategory(id);
|
|
154
|
+
if (!existing) {
|
|
155
|
+
throw new PMOError('NOT_FOUND', `Category not found: ${id}`);
|
|
156
|
+
}
|
|
157
|
+
if (existing.isBuiltin) {
|
|
158
|
+
throw new PMOError('INVALID', 'Cannot delete built-in categories');
|
|
159
|
+
}
|
|
160
|
+
// Check if the category is in use
|
|
161
|
+
if (existing.type === 'ticket') {
|
|
162
|
+
const ticketsUsing = this.ctx.db.prepare(`
|
|
163
|
+
SELECT COUNT(*) as count FROM ${T.tickets} WHERE category = ?
|
|
164
|
+
`).get(existing.name);
|
|
165
|
+
if (ticketsUsing.count > 0) {
|
|
166
|
+
throw new PMOError('INVALID', `Cannot delete category "${existing.name}": ${ticketsUsing.count} ticket(s) are using it. Reassign tickets first.`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (existing.type === 'status') {
|
|
170
|
+
const statusesUsing = this.ctx.db.prepare(`
|
|
171
|
+
SELECT COUNT(*) as count FROM ${T.workflow_statuses} WHERE category = ?
|
|
172
|
+
`).get(existing.name);
|
|
173
|
+
if (statusesUsing.count > 0) {
|
|
174
|
+
throw new PMOError('INVALID', `Cannot delete category "${existing.name}": ${statusesUsing.count} status(es) are using it. Reassign statuses first.`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
this.ctx.db.prepare(`DELETE FROM ${T.categories} WHERE id = ?`).run(id);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get category names for a type (for validation and autocomplete).
|
|
181
|
+
*/
|
|
182
|
+
async getCategoryNames(type) {
|
|
183
|
+
const categories = await this.listCategories({ type });
|
|
184
|
+
return categories.map(c => c.name);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if a category name is valid for a type.
|
|
188
|
+
*/
|
|
189
|
+
async isValidCategory(name, type) {
|
|
190
|
+
const category = await this.getCategoryByName(name, type);
|
|
191
|
+
return category !== null;
|
|
192
|
+
}
|
|
193
|
+
rowToCategory(row) {
|
|
194
|
+
return {
|
|
195
|
+
id: row.id,
|
|
196
|
+
name: row.name,
|
|
197
|
+
type: row.type,
|
|
198
|
+
description: row.description || undefined,
|
|
199
|
+
color: row.color || undefined,
|
|
200
|
+
position: row.position,
|
|
201
|
+
isBuiltin: row.is_builtin === 1,
|
|
202
|
+
createdAt: new Date(row.created_at),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import Database from 'better-sqlite3';
|
|
11
11
|
import { DrizzleDB } from '../../database/drizzle.js';
|
|
12
|
-
import { AcceptanceCriterion, Board, BoardConfig, BoardView, BoardViewFilter, BoardViewFilters, Column, CreateTicketInput, Epic, EpicDependency, EpicDependencyType, EpicFilter, PhaseFilter, PhaseTemplate, PhaseTemplateFilter, PMOStorage, Project, ProjectFilter, ProjectPhase, Roadmap, RoadmapFilter, RoadmapProject, Spec, SpecDependency, SpecDependencyType, SpecFilter, StateCategory, Subtask, SyncResult, SyncStatus, Ticket, TicketDependency, TicketDependencyType, TicketFilter, TicketTemplate, TicketTemplateFilter, WorkAction, WorkActionFilter, Workflow, WorkflowFilter, WorkflowStatus } from '../types.js';
|
|
12
|
+
import { AcceptanceCriterion, Board, BoardConfig, BoardView, BoardViewFilter, BoardViewFilters, Category, CategoryFilter, CategoryType, Column, CreateTicketInput, Epic, EpicDependency, EpicDependencyType, EpicFilter, PhaseFilter, PhaseTemplate, PhaseTemplateFilter, PMOStorage, Project, ProjectFilter, ProjectPhase, Roadmap, RoadmapFilter, RoadmapProject, Spec, SpecDependency, SpecDependencyType, SpecFilter, StateCategory, Subtask, SyncResult, SyncStatus, Ticket, TicketDependency, TicketDependencyType, TicketFilter, TicketTemplate, TicketTemplateFilter, WorkAction, WorkActionFilter, Workflow, WorkflowFilter, WorkflowStatus } from '../types.js';
|
|
13
13
|
export declare class SQLiteStorage implements PMOStorage {
|
|
14
14
|
readonly type: "sqlite";
|
|
15
15
|
private db;
|
|
@@ -28,6 +28,7 @@ export declare class SQLiteStorage implements PMOStorage {
|
|
|
28
28
|
private actionStorage;
|
|
29
29
|
private viewStorage;
|
|
30
30
|
private roadmapStorage;
|
|
31
|
+
private categoryStorage;
|
|
31
32
|
constructor(dbPath: string);
|
|
32
33
|
/**
|
|
33
34
|
* Get the underlying database connection.
|
|
@@ -186,6 +187,18 @@ export declare class SQLiteStorage implements PMOStorage {
|
|
|
186
187
|
removeProjectFromRoadmap(roadmapId: string, projectId: string): Promise<void>;
|
|
187
188
|
reorderRoadmapProject(roadmapId: string, projectId: string, newPosition: number): Promise<RoadmapProject>;
|
|
188
189
|
getRoadmapsForProject(projectId: string): Promise<Roadmap[]>;
|
|
190
|
+
listCategories(filter?: CategoryFilter): Promise<Category[]>;
|
|
191
|
+
getCategory(id: string): Promise<Category | null>;
|
|
192
|
+
getCategoryByName(name: string, type: CategoryType): Promise<Category | null>;
|
|
193
|
+
createCategory(category: Partial<Category> & {
|
|
194
|
+
name: string;
|
|
195
|
+
type: CategoryType;
|
|
196
|
+
}): Promise<Category>;
|
|
197
|
+
updateCategory(id: string, changes: Partial<Category>): Promise<Category>;
|
|
198
|
+
renameCategory(id: string, newName: string): Promise<Category>;
|
|
199
|
+
deleteCategory(id: string): Promise<void>;
|
|
200
|
+
getCategoryNames(type: CategoryType): Promise<string[]>;
|
|
201
|
+
isValidCategory(name: string, type: CategoryType): Promise<boolean>;
|
|
189
202
|
pull(): Promise<SyncResult>;
|
|
190
203
|
push(): Promise<SyncResult>;
|
|
191
204
|
status(): Promise<SyncStatus>;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import Database from 'better-sqlite3';
|
|
11
11
|
import { createDrizzleConnection } from '../../database/drizzle.js';
|
|
12
12
|
import { PMO_TABLES, PMO_SCHEMA_SQL, validateTicketSchema } from '../schema.js';
|
|
13
|
-
import { runMigrations, seedBuiltinWorkflows, seedBuiltinPhases, seedBuiltinPhaseTemplates, seedBuiltinActions, seedBuiltinTicketTemplates, updateBoardTimestamp, } from './base.js';
|
|
13
|
+
import { runMigrations, seedBuiltinWorkflows, seedBuiltinPhases, seedBuiltinPhaseTemplates, seedBuiltinActions, seedBuiltinTicketTemplates, seedBuiltinCategories, updateBoardTimestamp, } from './base.js';
|
|
14
14
|
import { ProjectStorage } from './projects.js';
|
|
15
15
|
import { TicketStorage } from './tickets.js';
|
|
16
16
|
import { SubtaskStorage, AcceptanceCriteriaStorage } from './subtasks.js';
|
|
@@ -23,6 +23,7 @@ import { PhaseStorage } from './phases.js';
|
|
|
23
23
|
import { ActionStorage } from './actions.js';
|
|
24
24
|
import { ViewStorage } from './views.js';
|
|
25
25
|
import { RoadmapStorage } from './roadmaps.js';
|
|
26
|
+
import { CategoryStorage } from './categories.js';
|
|
26
27
|
const T = PMO_TABLES;
|
|
27
28
|
export class SQLiteStorage {
|
|
28
29
|
type = 'sqlite';
|
|
@@ -43,6 +44,7 @@ export class SQLiteStorage {
|
|
|
43
44
|
actionStorage;
|
|
44
45
|
viewStorage;
|
|
45
46
|
roadmapStorage;
|
|
47
|
+
categoryStorage;
|
|
46
48
|
constructor(dbPath) {
|
|
47
49
|
this.dbPath = dbPath;
|
|
48
50
|
// Open database (creates if doesn't exist)
|
|
@@ -71,6 +73,7 @@ export class SQLiteStorage {
|
|
|
71
73
|
this.actionStorage = new ActionStorage(ctx);
|
|
72
74
|
this.viewStorage = new ViewStorage(ctx);
|
|
73
75
|
this.roadmapStorage = new RoadmapStorage(ctx);
|
|
76
|
+
this.categoryStorage = new CategoryStorage(ctx);
|
|
74
77
|
// Ensure PMO tables exist
|
|
75
78
|
this.ensurePMOTables();
|
|
76
79
|
}
|
|
@@ -100,6 +103,7 @@ export class SQLiteStorage {
|
|
|
100
103
|
seedBuiltinPhaseTemplates(this.db);
|
|
101
104
|
seedBuiltinActions(this.db);
|
|
102
105
|
seedBuiltinTicketTemplates(this.db);
|
|
106
|
+
seedBuiltinCategories(this.db);
|
|
103
107
|
// Validate schema
|
|
104
108
|
validateTicketSchema(this.db);
|
|
105
109
|
}
|
|
@@ -579,6 +583,36 @@ export class SQLiteStorage {
|
|
|
579
583
|
return this.roadmapStorage.getRoadmapsForProject(projectId);
|
|
580
584
|
}
|
|
581
585
|
// ===========================================================================
|
|
586
|
+
// Category Operations
|
|
587
|
+
// ===========================================================================
|
|
588
|
+
async listCategories(filter) {
|
|
589
|
+
return this.categoryStorage.listCategories(filter);
|
|
590
|
+
}
|
|
591
|
+
async getCategory(id) {
|
|
592
|
+
return this.categoryStorage.getCategory(id);
|
|
593
|
+
}
|
|
594
|
+
async getCategoryByName(name, type) {
|
|
595
|
+
return this.categoryStorage.getCategoryByName(name, type);
|
|
596
|
+
}
|
|
597
|
+
async createCategory(category) {
|
|
598
|
+
return this.categoryStorage.createCategory(category);
|
|
599
|
+
}
|
|
600
|
+
async updateCategory(id, changes) {
|
|
601
|
+
return this.categoryStorage.updateCategory(id, changes);
|
|
602
|
+
}
|
|
603
|
+
async renameCategory(id, newName) {
|
|
604
|
+
return this.categoryStorage.renameCategory(id, newName);
|
|
605
|
+
}
|
|
606
|
+
async deleteCategory(id) {
|
|
607
|
+
return this.categoryStorage.deleteCategory(id);
|
|
608
|
+
}
|
|
609
|
+
async getCategoryNames(type) {
|
|
610
|
+
return this.categoryStorage.getCategoryNames(type);
|
|
611
|
+
}
|
|
612
|
+
async isValidCategory(name, type) {
|
|
613
|
+
return this.categoryStorage.isValidCategory(name, type);
|
|
614
|
+
}
|
|
615
|
+
// ===========================================================================
|
|
582
616
|
// Sync Operations (no-op for pure SQLite)
|
|
583
617
|
// ===========================================================================
|
|
584
618
|
async pull() {
|
|
@@ -8,6 +8,11 @@ import { StorageContext } from './types.js';
|
|
|
8
8
|
export declare class TicketStorage {
|
|
9
9
|
private ctx;
|
|
10
10
|
constructor(ctx: StorageContext);
|
|
11
|
+
/**
|
|
12
|
+
* Validate a category against the DB.
|
|
13
|
+
* Returns the valid category name if found, throws error if invalid.
|
|
14
|
+
*/
|
|
15
|
+
private validateCategory;
|
|
11
16
|
/**
|
|
12
17
|
* Resolve a project identifier to its actual ID.
|
|
13
18
|
* Tries multiple strategies:
|
|
@@ -13,6 +13,27 @@ export class TicketStorage {
|
|
|
13
13
|
constructor(ctx) {
|
|
14
14
|
this.ctx = ctx;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Validate a category against the DB.
|
|
18
|
+
* Returns the valid category name if found, throws error if invalid.
|
|
19
|
+
*/
|
|
20
|
+
async validateCategory(category) {
|
|
21
|
+
if (!category)
|
|
22
|
+
return null;
|
|
23
|
+
// Check if category exists in DB for ticket type
|
|
24
|
+
const row = this.ctx.db.prepare(`
|
|
25
|
+
SELECT name FROM ${T.categories} WHERE LOWER(name) = LOWER(?) AND type = 'ticket'
|
|
26
|
+
`).get(category);
|
|
27
|
+
if (!row) {
|
|
28
|
+
// Get valid categories for error message
|
|
29
|
+
const validCategories = this.ctx.db.prepare(`
|
|
30
|
+
SELECT name FROM ${T.categories} WHERE type = 'ticket' ORDER BY position
|
|
31
|
+
`).all();
|
|
32
|
+
const validNames = validCategories.map(c => c.name).join(', ');
|
|
33
|
+
throw new PMOError('INVALID', `Invalid category "${category}". Valid categories: ${validNames}`);
|
|
34
|
+
}
|
|
35
|
+
return row.name;
|
|
36
|
+
}
|
|
16
37
|
/**
|
|
17
38
|
* Resolve a project identifier to its actual ID.
|
|
18
39
|
* Tries multiple strategies:
|
|
@@ -71,6 +92,8 @@ export class TicketStorage {
|
|
|
71
92
|
const title = ticket.title || 'Untitled';
|
|
72
93
|
const now = Date.now();
|
|
73
94
|
const specId = ticket.specId || null;
|
|
95
|
+
// Validate category against DB
|
|
96
|
+
const validatedCategory = await this.validateCategory(ticket.category);
|
|
74
97
|
// Get status_id from project's workflow
|
|
75
98
|
let statusId = ticket.statusId;
|
|
76
99
|
// Get the project's workflow
|
|
@@ -134,7 +157,7 @@ export class TicketStorage {
|
|
|
134
157
|
created_at, updated_at, last_synced_from_spec, last_synced_from_board
|
|
135
158
|
)
|
|
136
159
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
137
|
-
`).run(id, projectId, title, ticket.description || null, ticket.priority || null,
|
|
160
|
+
`).run(id, projectId, title, ticket.description || null, ticket.priority || null, validatedCategory, statusId, ticket.owner || null, ticket.assignee || null, specId, ticket.epicId || null, JSON.stringify(labels), now, now, ticket.lastSyncedFromSpec || null, ticket.lastSyncedFromBoard || null);
|
|
138
161
|
}
|
|
139
162
|
catch (err) {
|
|
140
163
|
wrapSqliteError('Ticket', 'create', err);
|
|
@@ -196,6 +219,11 @@ export class TicketStorage {
|
|
|
196
219
|
if (!existing) {
|
|
197
220
|
throw new PMOError('NOT_FOUND', `Ticket not found: ${id}`, id);
|
|
198
221
|
}
|
|
222
|
+
// Validate category if being updated
|
|
223
|
+
let validatedCategory;
|
|
224
|
+
if (changes.category !== undefined) {
|
|
225
|
+
validatedCategory = await this.validateCategory(changes.category);
|
|
226
|
+
}
|
|
199
227
|
const updates = [];
|
|
200
228
|
const params = [];
|
|
201
229
|
if (changes.title !== undefined) {
|
|
@@ -210,9 +238,9 @@ export class TicketStorage {
|
|
|
210
238
|
updates.push('priority = ?');
|
|
211
239
|
params.push(changes.priority);
|
|
212
240
|
}
|
|
213
|
-
if (
|
|
241
|
+
if (validatedCategory !== undefined) {
|
|
214
242
|
updates.push('category = ?');
|
|
215
|
-
params.push(
|
|
243
|
+
params.push(validatedCategory);
|
|
216
244
|
}
|
|
217
245
|
if (changes.statusId !== undefined) {
|
|
218
246
|
updates.push('status_id = ?');
|
|
@@ -216,3 +216,13 @@ export interface RoadmapProjectRow {
|
|
|
216
216
|
position: number;
|
|
217
217
|
created_at: string;
|
|
218
218
|
}
|
|
219
|
+
export interface CategoryRow {
|
|
220
|
+
id: string;
|
|
221
|
+
name: string;
|
|
222
|
+
type: string;
|
|
223
|
+
description: string | null;
|
|
224
|
+
color: string | null;
|
|
225
|
+
position: number;
|
|
226
|
+
is_builtin: number;
|
|
227
|
+
created_at: string;
|
|
228
|
+
}
|
package/dist/lib/pmo/types.d.ts
CHANGED
|
@@ -83,6 +83,31 @@ export declare const LEGACY_PRIORITY_MAP: Record<LegacyPriority, Priority>;
|
|
|
83
83
|
*/
|
|
84
84
|
export declare const TICKET_CATEGORIES: readonly ["feature", "bug", "refactor", "docs", "test", "chore", "performance", "ci", "build", "security", "database", "release"];
|
|
85
85
|
export type TicketCategory = typeof TICKET_CATEGORIES[number];
|
|
86
|
+
/**
|
|
87
|
+
* Category types (ticket vs status).
|
|
88
|
+
*/
|
|
89
|
+
export type CategoryType = 'ticket' | 'status';
|
|
90
|
+
/**
|
|
91
|
+
* Category record for ticket or status classification.
|
|
92
|
+
*/
|
|
93
|
+
export interface Category {
|
|
94
|
+
id: string;
|
|
95
|
+
name: string;
|
|
96
|
+
type: CategoryType;
|
|
97
|
+
description?: string;
|
|
98
|
+
color?: string;
|
|
99
|
+
position: number;
|
|
100
|
+
isBuiltin: boolean;
|
|
101
|
+
createdAt: Date;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Filter options for listing categories.
|
|
105
|
+
*/
|
|
106
|
+
export interface CategoryFilter {
|
|
107
|
+
type?: CategoryType;
|
|
108
|
+
isBuiltin?: boolean;
|
|
109
|
+
search?: string;
|
|
110
|
+
}
|
|
86
111
|
/**
|
|
87
112
|
* Check if a string is a valid Priority value.
|
|
88
113
|
*/
|
|
@@ -128,20 +128,17 @@ export interface ErrorJsonOutput {
|
|
|
128
128
|
*/
|
|
129
129
|
export type JsonOutput = PromptJsonOutput | SuccessJsonOutput | ErrorJsonOutput;
|
|
130
130
|
/**
|
|
131
|
-
* Flags interface for JSON mode detection
|
|
132
|
-
* @deprecated Use MachineOutputFlags instead
|
|
131
|
+
* Flags interface for JSON mode detection
|
|
133
132
|
*/
|
|
134
133
|
export interface JsonFlags {
|
|
135
134
|
json?: boolean;
|
|
136
135
|
}
|
|
137
136
|
/**
|
|
138
137
|
* Flags interface for machine-readable output mode detection
|
|
139
|
-
*
|
|
138
|
+
* --json is the primary flag, -m/--machine are aliases that set json=true
|
|
140
139
|
*/
|
|
141
140
|
export interface MachineOutputFlags {
|
|
142
|
-
/**
|
|
143
|
-
machine?: boolean;
|
|
144
|
-
/** Legacy format: --json (deprecated, use --machine instead) */
|
|
141
|
+
/** Primary JSON output flag. -m and --machine are aliases to this flag */
|
|
145
142
|
json?: boolean;
|
|
146
143
|
}
|
|
147
144
|
/**
|
|
@@ -154,16 +151,13 @@ export declare function isNonTTY(): boolean;
|
|
|
154
151
|
* Determine if JSON output mode is active (for AI agents)
|
|
155
152
|
*
|
|
156
153
|
* Returns true if:
|
|
157
|
-
* - The --
|
|
158
|
-
* - The --json flag is explicitly set
|
|
154
|
+
* - The --json flag is set (or -m/--machine aliases)
|
|
159
155
|
* - The environment is non-TTY (piped output)
|
|
160
156
|
*
|
|
161
157
|
* @param flags - Command flags object
|
|
162
158
|
* @returns true if JSON mode should be used
|
|
163
159
|
*/
|
|
164
|
-
export declare function shouldOutputJson(flags: JsonFlags
|
|
165
|
-
machine?: boolean;
|
|
166
|
-
}): boolean;
|
|
160
|
+
export declare function shouldOutputJson(flags: JsonFlags): boolean;
|
|
167
161
|
/**
|
|
168
162
|
* Alias for shouldOutputJson - clearer name for agent-focused code
|
|
169
163
|
*/
|
|
@@ -172,8 +166,7 @@ export declare const isAgentMode: typeof shouldOutputJson;
|
|
|
172
166
|
* Determine if machine-readable output mode is active (for AI agents/scripts)
|
|
173
167
|
*
|
|
174
168
|
* Returns true if:
|
|
175
|
-
* - The --
|
|
176
|
-
* - The --json flag is explicitly set (legacy support)
|
|
169
|
+
* - The --json flag is set (or -m/--machine aliases)
|
|
177
170
|
* - The environment is non-TTY (piped output)
|
|
178
171
|
*
|
|
179
172
|
* @param flags - Command flags object
|
|
@@ -200,15 +193,16 @@ export declare function createMetadata(command: string, flags: Record<string, un
|
|
|
200
193
|
*/
|
|
201
194
|
export declare function outputPromptAsJson(config: PromptConfig, metadata: OutputMetadata): never;
|
|
202
195
|
/**
|
|
203
|
-
* Output success result as JSON
|
|
196
|
+
* Output success result as JSON and exit
|
|
204
197
|
*
|
|
205
198
|
* Use this when all required data was provided via flags
|
|
206
|
-
* and no prompt is needed.
|
|
199
|
+
* and no prompt is needed. Exits with EXIT_SUCCESS (0) to signal
|
|
200
|
+
* successful command completion.
|
|
207
201
|
*
|
|
208
202
|
* @param result - Command-specific result data
|
|
209
203
|
* @param metadata - Command metadata
|
|
210
204
|
*/
|
|
211
|
-
export declare function outputSuccessAsJson(result: Record<string, unknown>, metadata: OutputMetadata):
|
|
205
|
+
export declare function outputSuccessAsJson(result: Record<string, unknown>, metadata: OutputMetadata): never;
|
|
212
206
|
/**
|
|
213
207
|
* Output error as JSON and exit
|
|
214
208
|
*
|
package/dist/lib/prompt-json.js
CHANGED
|
@@ -41,19 +41,14 @@ export function isNonTTY() {
|
|
|
41
41
|
* Determine if JSON output mode is active (for AI agents)
|
|
42
42
|
*
|
|
43
43
|
* Returns true if:
|
|
44
|
-
* - The --
|
|
45
|
-
* - The --json flag is explicitly set
|
|
44
|
+
* - The --json flag is set (or -m/--machine aliases)
|
|
46
45
|
* - The environment is non-TTY (piped output)
|
|
47
46
|
*
|
|
48
47
|
* @param flags - Command flags object
|
|
49
48
|
* @returns true if JSON mode should be used
|
|
50
49
|
*/
|
|
51
50
|
export function shouldOutputJson(flags) {
|
|
52
|
-
//
|
|
53
|
-
if (flags.machine === true) {
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
// Legacy --json flag
|
|
51
|
+
// --json flag (includes -m and --machine aliases)
|
|
57
52
|
if (flags.json === true) {
|
|
58
53
|
return true;
|
|
59
54
|
}
|
|
@@ -68,19 +63,14 @@ export const isAgentMode = shouldOutputJson;
|
|
|
68
63
|
* Determine if machine-readable output mode is active (for AI agents/scripts)
|
|
69
64
|
*
|
|
70
65
|
* Returns true if:
|
|
71
|
-
* - The --
|
|
72
|
-
* - The --json flag is explicitly set (legacy support)
|
|
66
|
+
* - The --json flag is set (or -m/--machine aliases)
|
|
73
67
|
* - The environment is non-TTY (piped output)
|
|
74
68
|
*
|
|
75
69
|
* @param flags - Command flags object
|
|
76
70
|
* @returns true if machine-readable output mode should be used
|
|
77
71
|
*/
|
|
78
72
|
export function isMachineOutput(flags) {
|
|
79
|
-
//
|
|
80
|
-
if (flags.machine === true) {
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
// Legacy --json flag support
|
|
73
|
+
// --json flag (includes -m and --machine aliases)
|
|
84
74
|
if (flags.json === true) {
|
|
85
75
|
return true;
|
|
86
76
|
}
|
|
@@ -128,10 +118,11 @@ export function outputPromptAsJson(config, metadata) {
|
|
|
128
118
|
process.exit(EXIT_NEEDS_INPUT);
|
|
129
119
|
}
|
|
130
120
|
/**
|
|
131
|
-
* Output success result as JSON
|
|
121
|
+
* Output success result as JSON and exit
|
|
132
122
|
*
|
|
133
123
|
* Use this when all required data was provided via flags
|
|
134
|
-
* and no prompt is needed.
|
|
124
|
+
* and no prompt is needed. Exits with EXIT_SUCCESS (0) to signal
|
|
125
|
+
* successful command completion.
|
|
135
126
|
*
|
|
136
127
|
* @param result - Command-specific result data
|
|
137
128
|
* @param metadata - Command metadata
|
|
@@ -144,6 +135,7 @@ export function outputSuccessAsJson(result, metadata) {
|
|
|
144
135
|
metadata,
|
|
145
136
|
};
|
|
146
137
|
console.log(JSON.stringify(output, null, 2));
|
|
138
|
+
process.exit(EXIT_SUCCESS);
|
|
147
139
|
}
|
|
148
140
|
/**
|
|
149
141
|
* Output error as JSON and exit
|