schub 0.1.25 → 0.1.26

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.
@@ -1200,9 +1200,6 @@ __export(exports_schema, {
1200
1200
  templates: () => templates,
1201
1201
  sessions: () => sessions,
1202
1202
  repos: () => repos,
1203
- proposals: () => proposals,
1204
- proposal_statuses: () => proposal_statuses,
1205
- proposal_files: () => proposal_files,
1206
1203
  projects: () => projects,
1207
1204
  project_repos: () => project_repos,
1208
1205
  project_docs: () => project_docs,
@@ -2576,34 +2573,10 @@ var ticket_statuses = sqliteTable("ticket_statuses", {
2576
2573
  created_at: text("created_at").notNull(),
2577
2574
  updated_at: text("updated_at").notNull()
2578
2575
  });
2579
- var proposal_statuses = sqliteTable("proposal_statuses", {
2580
- id: text("id").primaryKey(),
2581
- project_id: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
2582
- name: text("name").notNull(),
2583
- sort_order: integer("sort_order").notNull(),
2584
- is_default: integer("is_default").notNull(),
2585
- created_at: text("created_at").notNull(),
2586
- updated_at: text("updated_at").notNull()
2587
- });
2588
- var proposals = sqliteTable("proposals", {
2589
- id: text("id").primaryKey(),
2590
- shorthand: text("shorthand").notNull(),
2591
- project_id: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
2592
- repo_id: text("repo_id").references(() => repos.id, { onDelete: "set null" }),
2593
- input: text("input"),
2594
- status: text("status").notNull().default("draft"),
2595
- archived: integer("archived").notNull().default(0),
2596
- staged: integer("staged").notNull().default(0),
2597
- created_at: text("created_at").notNull(),
2598
- updated_at: text("updated_at").notNull()
2599
- }, (table) => [uniqueIndex("proposals_project_shorthand_idx").on(table.project_id, table.shorthand)]);
2600
2576
  var tickets = sqliteTable("tickets", {
2601
2577
  id: text("id").primaryKey(),
2602
2578
  shorthand: text("shorthand").notNull(),
2603
2579
  project_id: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
2604
- proposal_id: text("proposal_id").references(() => proposals.id, {
2605
- onDelete: "set null"
2606
- }),
2607
2580
  status_id: text("status_id").references(() => ticket_statuses.id, {
2608
2581
  onDelete: "set null"
2609
2582
  }),
@@ -2697,12 +2670,6 @@ var project_doc_files = sqliteTable("project_doc_files", {
2697
2670
  file_id: text("file_id").notNull().references(() => files.id, { onDelete: "cascade" }),
2698
2671
  created_at: text("created_at").notNull()
2699
2672
  }, (table) => [uniqueIndex("project_doc_files_project_doc_file_idx").on(table.project_doc_id, table.file_id)]);
2700
- var proposal_files = sqliteTable("proposal_files", {
2701
- id: text("id").primaryKey(),
2702
- proposal_id: text("proposal_id").notNull().references(() => proposals.id, { onDelete: "cascade" }),
2703
- file_id: text("file_id").notNull().references(() => files.id, { onDelete: "cascade" }),
2704
- created_at: text("created_at").notNull()
2705
- });
2706
2673
  var ticket_files = sqliteTable("ticket_files", {
2707
2674
  id: text("id").primaryKey(),
2708
2675
  ticket_id: text("ticket_id").notNull().references(() => tickets.id, { onDelete: "cascade" }),
@@ -2723,7 +2690,8 @@ var templates = sqliteTable("templates", {
2723
2690
  }),
2724
2691
  name: text("name").notNull(),
2725
2692
  template_type: text("template_type").notNull(),
2726
- content: text("content").notNull(),
2693
+ file_id: text("file_id").notNull().references(() => files.id),
2694
+ is_default: integer("is_default").notNull().default(0),
2727
2695
  created_at: text("created_at").notNull(),
2728
2696
  updated_at: text("updated_at").notNull()
2729
2697
  });
@@ -16557,7 +16525,6 @@ var sessionSelectSchema = createSelectSchema(sessions, {
16557
16525
  });
16558
16526
  var ticketTagSelectSchema = createSelectSchema(ticket_tags);
16559
16527
  var workspaceSelectSchema = createSelectSchema(workspaces);
16560
- var proposalSelectSchema = createSelectSchema(proposals);
16561
16528
  var ticketAttemptSchema = exports_external.object({
16562
16529
  id: exports_external.string(),
16563
16530
  label: exports_external.string(),
@@ -16747,7 +16714,6 @@ var createFilesService = (db, storageRoot) => {
16747
16714
  }).where(eq(files.id, fileId)).run();
16748
16715
  return updated;
16749
16716
  };
16750
- const listForProposal = (proposalId) => db.select({ file: files }).from(proposal_files).innerJoin(files, eq(proposal_files.file_id, files.id)).where(eq(proposal_files.proposal_id, proposalId)).orderBy(proposal_files.created_at).all().map((row) => row.file);
16751
16717
  const listForProjectDocs = (projectDocId) => db.select({ file: files }).from(project_doc_files).innerJoin(files, eq(project_doc_files.file_id, files.id)).where(eq(project_doc_files.project_doc_id, projectDocId)).orderBy(project_doc_files.created_at).all().map((row) => row.file);
16752
16718
  const attachToProjectDocs = (projectDocId, fileId) => {
16753
16719
  const existing = get(fileId);
@@ -16772,26 +16738,6 @@ var createFilesService = (db, storageRoot) => {
16772
16738
  db.delete(project_doc_files).where(and(eq(project_doc_files.project_doc_id, projectDocId), eq(project_doc_files.file_id, fileId))).run();
16773
16739
  return true;
16774
16740
  };
16775
- const attachToProposal = (proposalId, fileId) => {
16776
- const existing = get(fileId);
16777
- if (!existing)
16778
- return null;
16779
- const link = {
16780
- id: crypto.randomUUID(),
16781
- proposal_id: proposalId,
16782
- file_id: fileId,
16783
- created_at: nowTimestamp()
16784
- };
16785
- db.insert(proposal_files).values(link).run();
16786
- return existing;
16787
- };
16788
- const detachFromProposal = (proposalId, fileId) => {
16789
- const existing = db.select().from(proposal_files).where(and(eq(proposal_files.proposal_id, proposalId), eq(proposal_files.file_id, fileId))).get();
16790
- if (!existing)
16791
- return false;
16792
- db.delete(proposal_files).where(and(eq(proposal_files.proposal_id, proposalId), eq(proposal_files.file_id, fileId))).run();
16793
- return true;
16794
- };
16795
16741
  const listForTicket = (ticketId) => db.select({ file: files }).from(ticket_files).innerJoin(files, eq(ticket_files.file_id, files.id)).where(eq(ticket_files.ticket_id, ticketId)).orderBy(ticket_files.created_at).all().map((row) => row.file);
16796
16742
  const attachToTicket = (ticketId, fileId) => {
16797
16743
  const existing = get(fileId);
@@ -16822,9 +16768,6 @@ var createFilesService = (db, storageRoot) => {
16822
16768
  listForProjectDocs,
16823
16769
  attachToProjectDocs,
16824
16770
  detachFromProjectDocs,
16825
- listForProposal,
16826
- attachToProposal,
16827
- detachFromProposal,
16828
16771
  listForTicket,
16829
16772
  attachToTicket,
16830
16773
  detachFromTicket
@@ -17531,15 +17474,6 @@ var DEFAULT_TICKET_STATUSES = [
17531
17474
  column_actions: []
17532
17475
  }
17533
17476
  ];
17534
- var DEFAULT_PROPOSAL_STATUSES = [
17535
- { name: "draft", is_default: 1 },
17536
- { name: "pending review", is_default: 0 },
17537
- { name: "wip", is_default: 0 },
17538
- { name: "in-review", is_default: 0 },
17539
- { name: "accepted", is_default: 0 },
17540
- { name: "rejected", is_default: 0 },
17541
- { name: "done", is_default: 0 }
17542
- ];
17543
17477
  var DEFAULT_TICKET_TAGS = [
17544
17478
  { name: "bug", color: "red" },
17545
17479
  { name: "feature", color: "blue" },
@@ -17602,15 +17536,6 @@ var createProjectsService = (db) => {
17602
17536
  created_at: timestamp,
17603
17537
  updated_at: timestamp
17604
17538
  }))).run();
17605
- db.insert(proposal_statuses).values(DEFAULT_PROPOSAL_STATUSES.map((status, index) => ({
17606
- id: crypto.randomUUID(),
17607
- project_id: project.id,
17608
- name: status.name,
17609
- sort_order: index + 1,
17610
- is_default: status.is_default,
17611
- created_at: timestamp,
17612
- updated_at: timestamp
17613
- }))).run();
17614
17539
  db.insert(ticket_tags).values(DEFAULT_TICKET_TAGS.map((tag) => ({
17615
17540
  id: crypto.randomUUID(),
17616
17541
  project_id: project.id,
@@ -17663,165 +17588,9 @@ var createProjectsService = (db) => {
17663
17588
  return { list, get, getSnapshot, getSnapshotPatch, subscribe, create, update, remove };
17664
17589
  };
17665
17590
 
17666
- // ../schub-db/src/services/proposal-statuses.ts
17667
- var createProposalStatusesService = (db) => {
17668
- const list = (projectId) => db.select().from(proposal_statuses).where(eq(proposal_statuses.project_id, projectId)).orderBy(proposal_statuses.sort_order).all();
17669
- const get = (projectId, statusId) => {
17670
- const status = db.select().from(proposal_statuses).where(and(eq(proposal_statuses.project_id, projectId), eq(proposal_statuses.id, statusId))).get();
17671
- return status ?? null;
17672
- };
17673
- const getByName = (projectId, statusName) => {
17674
- const status = db.select().from(proposal_statuses).where(and(eq(proposal_statuses.project_id, projectId), eq(proposal_statuses.name, statusName))).get();
17675
- return status ?? null;
17676
- };
17677
- const getDefault = (projectId) => {
17678
- const explicitDefault = db.select().from(proposal_statuses).where(and(eq(proposal_statuses.project_id, projectId), eq(proposal_statuses.is_default, 1))).orderBy(proposal_statuses.sort_order).get();
17679
- if (explicitDefault) {
17680
- return explicitDefault;
17681
- }
17682
- return list(projectId)[0] ?? null;
17683
- };
17684
- return { list, get, getByName, getDefault };
17685
- };
17686
-
17687
- // ../schub-db/src/services/proposals.ts
17688
- var nowTimestamp4 = () => new Date().toISOString();
17689
- var parseShorthandSequence = (shorthand) => {
17690
- const match = shorthand.match(/^[CP](\d+)/i);
17691
- if (!match) {
17692
- return null;
17693
- }
17694
- return Number.parseInt(match[1], 10);
17695
- };
17696
- var formatShorthand = (sequence) => `P${sequence.toString().padStart(4, "0")}`;
17697
- var normalizeProposalShorthand = (shorthand) => {
17698
- const sequence = parseShorthandSequence(shorthand);
17699
- if (sequence === null) {
17700
- throw new Error(`Invalid proposal shorthand '${shorthand}'.`);
17701
- }
17702
- return formatShorthand(sequence);
17703
- };
17704
- var createProposalsService = (db) => {
17705
- const getDefaultStatusName = (projectId) => {
17706
- const explicitDefault = db.select({ name: proposal_statuses.name }).from(proposal_statuses).where(and(eq(proposal_statuses.project_id, projectId), eq(proposal_statuses.is_default, 1))).orderBy(proposal_statuses.sort_order).get();
17707
- if (explicitDefault) {
17708
- return explicitDefault.name;
17709
- }
17710
- const firstStatus = db.select({ name: proposal_statuses.name }).from(proposal_statuses).where(eq(proposal_statuses.project_id, projectId)).orderBy(proposal_statuses.sort_order).get();
17711
- return firstStatus?.name ?? "draft";
17712
- };
17713
- const assertKnownStatus = (projectId, status) => {
17714
- const normalizedStatus = status.trim().toLowerCase();
17715
- const exists = db.select({ name: proposal_statuses.name }).from(proposal_statuses).where(eq(proposal_statuses.project_id, projectId)).all().some((candidate) => candidate.name.trim().toLowerCase() === normalizedStatus);
17716
- if (!exists) {
17717
- throw new Error(`Proposal status '${status}' does not exist for project '${projectId}'.`);
17718
- }
17719
- };
17720
- const allocateProposalShorthand = () => {
17721
- return db.transaction((tx) => {
17722
- const priorShorthands = tx.select({ shorthand: proposals.shorthand }).from(proposals).all();
17723
- const maxSequence = priorShorthands.reduce((max, proposal) => {
17724
- if (!proposal.shorthand) {
17725
- return max;
17726
- }
17727
- const parsed = parseShorthandSequence(proposal.shorthand);
17728
- if (parsed === null) {
17729
- return max;
17730
- }
17731
- return Math.max(max, parsed);
17732
- }, 0);
17733
- return formatShorthand(maxSequence + 1);
17734
- });
17735
- };
17736
- const list = (projectId) => db.select().from(proposals).where(eq(proposals.project_id, projectId)).orderBy(proposals.created_at).all();
17737
- const get = (projectId, proposalId) => {
17738
- const proposal = db.select().from(proposals).where(and(eq(proposals.project_id, projectId), eq(proposals.id, proposalId))).get();
17739
- return proposal ?? null;
17740
- };
17741
- const create = (projectId, input) => {
17742
- const timestamp = nowTimestamp4();
17743
- const shorthand = allocateProposalShorthand();
17744
- const defaultStatus = getDefaultStatusName(projectId);
17745
- const proposal = {
17746
- id: crypto.randomUUID(),
17747
- shorthand,
17748
- project_id: projectId,
17749
- repo_id: input.repo_id ?? null,
17750
- input: input.input ?? null,
17751
- status: defaultStatus,
17752
- archived: 0,
17753
- staged: input.staged ?? 0,
17754
- created_at: timestamp,
17755
- updated_at: timestamp
17756
- };
17757
- db.insert(proposals).values(proposal).run();
17758
- return proposal;
17759
- };
17760
- const createWithShorthand = (projectId, shorthand, input) => {
17761
- const timestamp = nowTimestamp4();
17762
- const normalizedShorthand = normalizeProposalShorthand(shorthand);
17763
- const defaultStatus = getDefaultStatusName(projectId);
17764
- const proposal = {
17765
- id: crypto.randomUUID(),
17766
- shorthand: normalizedShorthand,
17767
- project_id: projectId,
17768
- repo_id: null,
17769
- input: input.input ?? null,
17770
- status: defaultStatus,
17771
- archived: 0,
17772
- staged: input.staged ?? 0,
17773
- created_at: timestamp,
17774
- updated_at: timestamp
17775
- };
17776
- db.insert(proposals).values(proposal).run();
17777
- return proposal;
17778
- };
17779
- const update = (projectId, proposalId, input) => {
17780
- const existing = get(projectId, proposalId);
17781
- if (!existing)
17782
- return null;
17783
- const proposalInput = input.input === undefined ? existing.input : input.input;
17784
- const repo_id = input.repo_id === undefined ? existing.repo_id : input.repo_id;
17785
- const status = input.status ?? existing.status;
17786
- const archived = input.archived === undefined ? existing.archived : input.archived;
17787
- const staged = input.staged === undefined ? existing.staged : input.staged;
17788
- if (input.status !== undefined) {
17789
- assertKnownStatus(projectId, status);
17790
- }
17791
- const updated = {
17792
- ...existing,
17793
- repo_id,
17794
- input: proposalInput,
17795
- status,
17796
- archived,
17797
- staged,
17798
- updated_at: nowTimestamp4()
17799
- };
17800
- db.update(proposals).set({
17801
- repo_id: updated.repo_id,
17802
- input: updated.input,
17803
- status: updated.status,
17804
- archived: updated.archived,
17805
- staged: updated.staged,
17806
- updated_at: updated.updated_at
17807
- }).where(and(eq(proposals.project_id, projectId), eq(proposals.id, proposalId))).run();
17808
- return updated;
17809
- };
17810
- const remove = (projectId, proposalId) => {
17811
- const existing = get(projectId, proposalId);
17812
- if (!existing)
17813
- return false;
17814
- const timestamp = nowTimestamp4();
17815
- db.update(tickets).set({ proposal_id: null, updated_at: timestamp }).where(and(eq(tickets.project_id, projectId), eq(tickets.proposal_id, proposalId))).run();
17816
- db.delete(proposals).where(and(eq(proposals.project_id, projectId), eq(proposals.id, proposalId))).run();
17817
- return true;
17818
- };
17819
- return { list, get, create, createWithShorthand, update, remove };
17820
- };
17821
-
17822
17591
  // ../schub-db/src/services/repos.ts
17823
17592
  import { basename } from "node:path";
17824
- var nowTimestamp5 = () => new Date().toISOString();
17593
+ var nowTimestamp4 = () => new Date().toISOString();
17825
17594
  var normalizeRepoPath = (value) => value.replace(/[\\/]+$/, "");
17826
17595
  var deriveRepoName = (repoPath) => {
17827
17596
  const normalized = normalizeRepoPath(repoPath);
@@ -17844,7 +17613,7 @@ var createReposService = (db) => {
17844
17613
  return row?.repo ?? null;
17845
17614
  };
17846
17615
  const upsertRepo = (input) => {
17847
- const timestamp = nowTimestamp5();
17616
+ const timestamp = nowTimestamp4();
17848
17617
  const displayName = resolveDisplayName(input.display_name);
17849
17618
  const pathValue = normalizeRepoPath(input.path);
17850
17619
  const name = deriveRepoName(pathValue);
@@ -17883,13 +17652,13 @@ var createReposService = (db) => {
17883
17652
  const updated = {
17884
17653
  ...existing,
17885
17654
  display_name: displayName,
17886
- updated_at: nowTimestamp5()
17655
+ updated_at: nowTimestamp4()
17887
17656
  };
17888
17657
  db.update(repos).set({ display_name: updated.display_name, updated_at: updated.updated_at }).where(eq(repos.id, repoId)).run();
17889
17658
  return updated;
17890
17659
  };
17891
17660
  const addToProject = (projectId, input) => {
17892
- const timestamp = nowTimestamp5();
17661
+ const timestamp = nowTimestamp4();
17893
17662
  const repo = upsertRepo({ path: input.git_repo_path, display_name: input.display_name });
17894
17663
  const existingLink = db.select().from(project_repos).where(and(eq(project_repos.project_id, projectId), eq(project_repos.repo_id, repo.id))).get();
17895
17664
  if (!existingLink) {
@@ -17917,7 +17686,7 @@ var createReposService = (db) => {
17917
17686
  };
17918
17687
 
17919
17688
  // ../schub-db/src/services/sessions.ts
17920
- var nowTimestamp6 = () => new Date().toISOString();
17689
+ var nowTimestamp5 = () => new Date().toISOString();
17921
17690
  var SESSION_STATUSES = ["in_progress", "completed", "failed"];
17922
17691
  var normalizeSessionStatus = (status) => {
17923
17692
  if (!status)
@@ -17966,7 +17735,7 @@ var createSessionsService = (db) => {
17966
17735
  return session ? formatSession(session) : null;
17967
17736
  };
17968
17737
  const create = (input) => {
17969
- const timestamp = nowTimestamp6();
17738
+ const timestamp = nowTimestamp5();
17970
17739
  const session = {
17971
17740
  id: input.id?.trim() || crypto.randomUUID(),
17972
17741
  title: input.title,
@@ -18008,7 +17777,7 @@ var createSessionsService = (db) => {
18008
17777
  agent,
18009
17778
  agent_session_id: agentSessionId,
18010
17779
  content,
18011
- updated_at: nowTimestamp6()
17780
+ updated_at: nowTimestamp5()
18012
17781
  };
18013
17782
  db.update(sessions).set({
18014
17783
  title: updated.title,
@@ -18035,21 +17804,106 @@ var createSessionsService = (db) => {
18035
17804
  };
18036
17805
 
18037
17806
  // ../schub-db/src/services/templates.ts
18038
- var nowTimestamp7 = () => new Date().toISOString();
17807
+ var nowTimestamp6 = () => new Date().toISOString();
18039
17808
  var createTemplatesService = (db) => {
18040
- const list = () => db.select().from(templates).orderBy(templates.created_at).all();
17809
+ const list = () => db.select({
17810
+ id: templates.id,
17811
+ project_id: templates.project_id,
17812
+ name: templates.name,
17813
+ template_type: templates.template_type,
17814
+ file_id: templates.file_id,
17815
+ is_default: templates.is_default,
17816
+ created_at: templates.created_at,
17817
+ updated_at: templates.updated_at,
17818
+ file_name: files.file_name,
17819
+ storage_path: files.storage_path,
17820
+ mime_type: files.mime_type,
17821
+ size_bytes: files.size_bytes
17822
+ }).from(templates).innerJoin(files, eq(templates.file_id, files.id)).orderBy(templates.created_at).all();
17823
+ const listByProject = (projectId) => db.select({
17824
+ id: templates.id,
17825
+ project_id: templates.project_id,
17826
+ name: templates.name,
17827
+ template_type: templates.template_type,
17828
+ file_id: templates.file_id,
17829
+ is_default: templates.is_default,
17830
+ created_at: templates.created_at,
17831
+ updated_at: templates.updated_at,
17832
+ file_name: files.file_name,
17833
+ storage_path: files.storage_path,
17834
+ mime_type: files.mime_type,
17835
+ size_bytes: files.size_bytes
17836
+ }).from(templates).innerJoin(files, eq(templates.file_id, files.id)).where(eq(templates.project_id, projectId)).orderBy(templates.created_at).all();
18041
17837
  const get = (templateId) => {
18042
- const template = db.select().from(templates).where(eq(templates.id, templateId)).get();
18043
- return template ?? null;
17838
+ const row = db.select({
17839
+ id: templates.id,
17840
+ project_id: templates.project_id,
17841
+ name: templates.name,
17842
+ template_type: templates.template_type,
17843
+ file_id: templates.file_id,
17844
+ is_default: templates.is_default,
17845
+ created_at: templates.created_at,
17846
+ updated_at: templates.updated_at,
17847
+ file_name: files.file_name,
17848
+ storage_path: files.storage_path,
17849
+ mime_type: files.mime_type,
17850
+ size_bytes: files.size_bytes
17851
+ }).from(templates).innerJoin(files, eq(templates.file_id, files.id)).where(eq(templates.id, templateId)).get();
17852
+ return row ?? null;
17853
+ };
17854
+ const getDefault = (projectId, templateType) => {
17855
+ const row = db.select({
17856
+ id: templates.id,
17857
+ project_id: templates.project_id,
17858
+ name: templates.name,
17859
+ template_type: templates.template_type,
17860
+ file_id: templates.file_id,
17861
+ is_default: templates.is_default,
17862
+ created_at: templates.created_at,
17863
+ updated_at: templates.updated_at,
17864
+ file_name: files.file_name,
17865
+ storage_path: files.storage_path,
17866
+ mime_type: files.mime_type,
17867
+ size_bytes: files.size_bytes
17868
+ }).from(templates).innerJoin(files, eq(templates.file_id, files.id)).where(and(eq(templates.project_id, projectId), eq(templates.template_type, templateType), eq(templates.is_default, 1))).get();
17869
+ return row ?? null;
17870
+ };
17871
+ const getByName = (name, projectId) => {
17872
+ const row = db.select({
17873
+ id: templates.id,
17874
+ project_id: templates.project_id,
17875
+ name: templates.name,
17876
+ template_type: templates.template_type,
17877
+ file_id: templates.file_id,
17878
+ is_default: templates.is_default,
17879
+ created_at: templates.created_at,
17880
+ updated_at: templates.updated_at,
17881
+ file_name: files.file_name,
17882
+ storage_path: files.storage_path,
17883
+ mime_type: files.mime_type,
17884
+ size_bytes: files.size_bytes
17885
+ }).from(templates).innerJoin(files, eq(templates.file_id, files.id)).where(and(eq(templates.name, name), eq(templates.project_id, projectId))).get();
17886
+ return row ?? null;
17887
+ };
17888
+ const checkNameUniqueness = (name, projectId, excludeId) => {
17889
+ if (!projectId)
17890
+ return;
17891
+ const existing = db.select({ id: templates.id }).from(templates).where(and(eq(templates.name, name), eq(templates.project_id, projectId))).get();
17892
+ if (existing && existing.id !== excludeId) {
17893
+ throw new Error(`Duplicate template name '${name}' in project '${projectId}'.`);
17894
+ }
18044
17895
  };
18045
17896
  const create = (input) => {
18046
- const timestamp = nowTimestamp7();
17897
+ const projectId = input.project_id ?? null;
17898
+ checkNameUniqueness(input.name, projectId);
17899
+ const timestamp = nowTimestamp6();
18047
17900
  const template = {
18048
17901
  id: crypto.randomUUID(),
18049
- project_id: input.project_id ?? null,
17902
+ project_id: projectId,
18050
17903
  name: input.name,
18051
17904
  template_type: input.template_type,
18052
- content: input.content,
17905
+ file_id: input.file_id,
17906
+ is_default: input.is_default ?? 0,
18053
17907
  created_at: timestamp,
18054
17908
  updated_at: timestamp
18055
17909
  };
@@ -18057,39 +17911,42 @@ var createTemplatesService = (db) => {
18057
17911
  return template;
18058
17912
  };
18059
17913
  const update = (templateId, input) => {
18060
- const existing = get(templateId);
17914
+ const existing = db.select().from(templates).where(eq(templates.id, templateId)).get();
18061
17915
  if (!existing)
18062
17916
  return null;
18063
- const project_id = input.project_id === undefined ? existing.project_id : input.project_id ?? null;
17917
+ const projectId = input.project_id === undefined ? existing.project_id : input.project_id ?? null;
17918
+ checkNameUniqueness(input.name, projectId, templateId);
18064
17919
  const updated = {
18065
17920
  ...existing,
18066
- project_id,
17921
+ project_id: projectId,
18067
17922
  name: input.name,
18068
17923
  template_type: input.template_type,
18069
- content: input.content,
18070
- updated_at: nowTimestamp7()
17924
+ file_id: input.file_id,
17925
+ is_default: input.is_default ?? existing.is_default,
17926
+ updated_at: nowTimestamp6()
18071
17927
  };
18072
17928
  db.update(templates).set({
18073
17929
  project_id: updated.project_id,
18074
17930
  name: updated.name,
18075
17931
  template_type: updated.template_type,
18076
- content: updated.content,
17932
+ file_id: updated.file_id,
17933
+ is_default: updated.is_default,
18077
17934
  updated_at: updated.updated_at
18078
17935
  }).where(eq(templates.id, templateId)).run();
18079
17936
  return updated;
18080
17937
  };
18081
17938
  const remove = (templateId) => {
18082
- const existing = get(templateId);
17939
+ const existing = db.select().from(templates).where(eq(templates.id, templateId)).get();
18083
17940
  if (!existing)
18084
17941
  return false;
18085
17942
  db.delete(templates).where(eq(templates.id, templateId)).run();
18086
17943
  return true;
18087
17944
  };
18088
- return { list, get, create, update, remove };
17945
+ return { list, listByProject, get, getDefault, getByName, create, update, remove };
18089
17946
  };
18090
17947
 
18091
17948
  // ../schub-db/src/services/ticket-statuses.ts
18092
- var nowTimestamp8 = () => new Date().toISOString();
17949
+ var nowTimestamp7 = () => new Date().toISOString();
18093
17950
  var TICKET_STATUS_COLORS = ["gray", "blue", "cyan", "green", "yellow", "orange", "red", "purple", "pink"];
18094
17951
  var TICKET_COLUMN_ACTIONS = ["archive_all"];
18095
17952
  var isAllowedTicketStatusColor = (color) => TICKET_STATUS_COLORS.includes(color);
@@ -18143,7 +18000,7 @@ var createTicketStatusesService = (db) => {
18143
18000
  assertValidTicketColumnActions(input.column_actions ?? []);
18144
18001
  const normalizedName = normalizeTicketStatusName(input.name);
18145
18002
  assertValidTicketStatusName(normalizedName);
18146
- const timestamp = nowTimestamp8();
18003
+ const timestamp = nowTimestamp7();
18147
18004
  const columnActions = normalizeTicketColumnActions(input.column_actions ?? []);
18148
18005
  const status = {
18149
18006
  id: crypto.randomUUID(),
@@ -18185,7 +18042,7 @@ var createTicketStatusesService = (db) => {
18185
18042
  can_create: input.can_create,
18186
18043
  can_attempt_on_drop: input.can_attempt_on_drop,
18187
18044
  column_actions: JSON.stringify(columnActions),
18188
- updated_at: nowTimestamp8()
18045
+ updated_at: nowTimestamp7()
18189
18046
  };
18190
18047
  db.update(ticket_statuses).set({
18191
18048
  name: updated.name,
@@ -18216,9 +18073,9 @@ var createTicketStatusesService = (db) => {
18216
18073
  }
18217
18074
  if (!remaining.some((status) => status.is_default === 1)) {
18218
18075
  db.update(ticket_statuses).set({ is_default: 0 }).where(eq(ticket_statuses.project_id, projectId)).run();
18219
- db.update(ticket_statuses).set({ is_default: 1, updated_at: nowTimestamp8() }).where(eq(ticket_statuses.id, defaultStatus.id)).run();
18076
+ db.update(ticket_statuses).set({ is_default: 1, updated_at: nowTimestamp7() }).where(eq(ticket_statuses.id, defaultStatus.id)).run();
18220
18077
  }
18221
- db.update(tickets).set({ status_id: defaultStatus.id, updated_at: nowTimestamp8() }).where(eq(tickets.status_id, statusId)).run();
18078
+ db.update(tickets).set({ status_id: defaultStatus.id, updated_at: nowTimestamp7() }).where(eq(tickets.status_id, statusId)).run();
18222
18079
  db.delete(ticket_statuses).where(and(eq(ticket_statuses.project_id, projectId), eq(ticket_statuses.id, statusId))).run();
18223
18080
  return { ok: true, reassignedTo: defaultStatus.id };
18224
18081
  };
@@ -18226,7 +18083,7 @@ var createTicketStatusesService = (db) => {
18226
18083
  };
18227
18084
 
18228
18085
  // ../schub-db/src/services/ticket-tags.ts
18229
- var nowTimestamp9 = () => new Date().toISOString();
18086
+ var nowTimestamp8 = () => new Date().toISOString();
18230
18087
  var normalizeTagName = (name) => name.trim().toLowerCase().replace(/[\s-]+/g, "_");
18231
18088
  var createTicketTagsService = (db) => {
18232
18089
  const list = (projectId) => db.select().from(ticket_tags).where(eq(ticket_tags.project_id, projectId)).orderBy(ticket_tags.name).all();
@@ -18235,7 +18092,7 @@ var createTicketTagsService = (db) => {
18235
18092
  return tag ?? null;
18236
18093
  };
18237
18094
  const create = (projectId, input) => {
18238
- const timestamp = nowTimestamp9();
18095
+ const timestamp = nowTimestamp8();
18239
18096
  const tag = {
18240
18097
  id: crypto.randomUUID(),
18241
18098
  project_id: projectId,
@@ -18255,7 +18112,7 @@ var createTicketTagsService = (db) => {
18255
18112
  ...existing,
18256
18113
  name: normalizeTagName(input.name),
18257
18114
  color: input.color,
18258
- updated_at: nowTimestamp9()
18115
+ updated_at: nowTimestamp8()
18259
18116
  };
18260
18117
  db.update(ticket_tags).set({
18261
18118
  name: updated.name,
@@ -18301,7 +18158,7 @@ var createTicketTagsService = (db) => {
18301
18158
  id: crypto.randomUUID(),
18302
18159
  ticket_id: ticketId,
18303
18160
  ticket_tag_id: tagId,
18304
- created_at: nowTimestamp9()
18161
+ created_at: nowTimestamp8()
18305
18162
  }))).run();
18306
18163
  });
18307
18164
  return listIdsForTicket(ticketId);
@@ -18310,7 +18167,7 @@ var createTicketTagsService = (db) => {
18310
18167
  };
18311
18168
 
18312
18169
  // ../schub-db/src/services/tickets.ts
18313
- var nowTimestamp10 = () => new Date().toISOString();
18170
+ var nowTimestamp9 = () => new Date().toISOString();
18314
18171
  var formatTicketId = (sequence) => `TK${sequence.toString().padStart(4, "0")}`;
18315
18172
  var parseTicketSequence = (value) => {
18316
18173
  const match = value.match(/^TK(\d+)$/i);
@@ -18377,13 +18234,12 @@ var createTicketsService = (db) => {
18377
18234
  throw new Error("Parent ticket not found");
18378
18235
  }
18379
18236
  }
18380
- const timestamp = nowTimestamp10();
18237
+ const timestamp = nowTimestamp9();
18381
18238
  const shorthand = allocateTicketShorthand();
18382
18239
  const ticket = {
18383
18240
  id: crypto.randomUUID(),
18384
18241
  shorthand,
18385
18242
  project_id: input.project_id,
18386
- proposal_id: input.proposal_id ?? null,
18387
18243
  status_id: input.status_id ?? null,
18388
18244
  parent_id: input.parent_id ?? null,
18389
18245
  title: input.title ?? null,
@@ -18406,7 +18262,7 @@ var createTicketsService = (db) => {
18406
18262
  const existing = get(ticketId);
18407
18263
  if (!existing)
18408
18264
  return null;
18409
- if (input.parent_id !== undefined) {
18265
+ if (input.parent_id !== undefined && input.parent_id !== existing.parent_id) {
18410
18266
  validateParentId(existing.id, input.parent_id, get);
18411
18267
  }
18412
18268
  const title = input.title === undefined ? existing.title : input.title;
@@ -18415,7 +18271,6 @@ var createTicketsService = (db) => {
18415
18271
  const parallelizable = input.parallelizable === undefined ? existing.parallelizable : input.parallelizable;
18416
18272
  const complexity = input.complexity === undefined ? existing.complexity : input.complexity;
18417
18273
  const status_id = input.status_id === undefined ? existing.status_id : input.status_id;
18418
- const proposal_id = input.proposal_id === undefined ? existing.proposal_id : input.proposal_id;
18419
18274
  const parent_id = input.parent_id === undefined ? existing.parent_id : input.parent_id;
18420
18275
  const blocked_reason = input.blocked_reason === undefined ? existing.blocked_reason : input.blocked_reason;
18421
18276
  const depends_on = input.depends_on === undefined ? existing.depends_on : input.depends_on;
@@ -18429,13 +18284,12 @@ var createTicketsService = (db) => {
18429
18284
  parallelizable,
18430
18285
  complexity,
18431
18286
  status_id,
18432
- proposal_id,
18433
18287
  parent_id,
18434
18288
  blocked_reason,
18435
18289
  depends_on,
18436
18290
  archived,
18437
18291
  staged,
18438
- updated_at: nowTimestamp10()
18292
+ updated_at: nowTimestamp9()
18439
18293
  };
18440
18294
  db.update(tickets).set({
18441
18295
  title: updated.title,
@@ -18444,7 +18298,6 @@ var createTicketsService = (db) => {
18444
18298
  parallelizable: updated.parallelizable,
18445
18299
  complexity: updated.complexity,
18446
18300
  status_id: updated.status_id,
18447
- proposal_id: updated.proposal_id,
18448
18301
  parent_id: updated.parent_id,
18449
18302
  blocked_reason: updated.blocked_reason,
18450
18303
  depends_on: updated.depends_on,
@@ -18459,105 +18312,12 @@ var createTicketsService = (db) => {
18459
18312
  if (!existing)
18460
18313
  return false;
18461
18314
  db.update(tickets).set({ parent_id: null }).where(eq(tickets.parent_id, existing.id)).run();
18462
- db.update(tickets).set({ deleted_at: nowTimestamp10(), updated_at: nowTimestamp10() }).where(eq(tickets.id, existing.id)).run();
18463
- return true;
18464
- };
18465
- const getForProposal = (projectId, proposalId, ticketId) => {
18466
- const ticket = db.select().from(tickets).where(and(eq(tickets.project_id, projectId), eq(tickets.proposal_id, proposalId), or(eq(tickets.id, ticketId), eq(tickets.shorthand, ticketId)), isNull(tickets.deleted_at))).get();
18467
- return ticket ?? null;
18468
- };
18469
- const listByProposal = (projectId, proposalId) => db.select().from(tickets).where(and(eq(tickets.project_id, projectId), eq(tickets.proposal_id, proposalId), isNull(tickets.deleted_at))).orderBy(tickets.created_at).all();
18470
- const createForProposal = (projectId, proposalId, input) => {
18471
- if (input.parent_id) {
18472
- const parent = get(input.parent_id);
18473
- if (!parent) {
18474
- throw new Error("Parent ticket not found");
18475
- }
18476
- }
18477
- const timestamp = nowTimestamp10();
18478
- const shorthand = allocateTicketShorthand();
18479
- const ticket = {
18480
- id: crypto.randomUUID(),
18481
- shorthand,
18482
- project_id: projectId,
18483
- proposal_id: proposalId,
18484
- status_id: input.status_id ?? null,
18485
- parent_id: input.parent_id ?? null,
18486
- title: input.title ?? null,
18487
- input: input.input ?? null,
18488
- priority: input.priority ?? null,
18489
- parallelizable: input.parallelizable ?? null,
18490
- complexity: input.complexity ?? null,
18491
- blocked_reason: input.blocked_reason ?? null,
18492
- depends_on: input.depends_on ?? null,
18493
- archived: input.archived ?? 0,
18494
- staged: input.staged ?? 0,
18495
- deleted_at: null,
18496
- created_at: timestamp,
18497
- updated_at: timestamp
18498
- };
18499
- db.insert(tickets).values(ticket).run();
18500
- return ticket;
18501
- };
18502
- const updateForProposal = (projectId, proposalId, ticketId, input) => {
18503
- const existing = getForProposal(projectId, proposalId, ticketId);
18504
- if (!existing)
18505
- return null;
18506
- if (input.parent_id !== undefined) {
18507
- validateParentId(existing.id, input.parent_id, get);
18508
- }
18509
- const title = input.title === undefined ? existing.title : input.title;
18510
- const ticketInput = input.input === undefined ? existing.input : input.input;
18511
- const priority = input.priority === undefined ? existing.priority : input.priority;
18512
- const parallelizable = input.parallelizable === undefined ? existing.parallelizable : input.parallelizable;
18513
- const complexity = input.complexity === undefined ? existing.complexity : input.complexity;
18514
- const status_id = input.status_id === undefined ? existing.status_id : input.status_id;
18515
- const parent_id = input.parent_id === undefined ? existing.parent_id : input.parent_id;
18516
- const blocked_reason = input.blocked_reason === undefined ? existing.blocked_reason : input.blocked_reason;
18517
- const depends_on = input.depends_on === undefined ? existing.depends_on : input.depends_on;
18518
- const archived = input.archived === undefined ? existing.archived : input.archived;
18519
- const staged = input.staged === undefined ? existing.staged : input.staged;
18520
- const updated = {
18521
- ...existing,
18522
- title,
18523
- input: ticketInput,
18524
- priority,
18525
- parallelizable,
18526
- complexity,
18527
- status_id,
18528
- parent_id,
18529
- blocked_reason,
18530
- depends_on,
18531
- archived,
18532
- staged,
18533
- updated_at: nowTimestamp10()
18534
- };
18535
- db.update(tickets).set({
18536
- title: updated.title,
18537
- input: updated.input,
18538
- priority: updated.priority,
18539
- parallelizable: updated.parallelizable,
18540
- complexity: updated.complexity,
18541
- status_id: updated.status_id,
18542
- parent_id: updated.parent_id,
18543
- blocked_reason: updated.blocked_reason,
18544
- depends_on: updated.depends_on,
18545
- archived: updated.archived,
18546
- staged: updated.staged,
18547
- updated_at: updated.updated_at
18548
- }).where(and(eq(tickets.project_id, projectId), eq(tickets.proposal_id, proposalId), eq(tickets.id, existing.id))).run();
18549
- return updated;
18550
- };
18551
- const removeForProposal = (projectId, proposalId, ticketId) => {
18552
- const existing = getForProposal(projectId, proposalId, ticketId);
18553
- if (!existing)
18554
- return false;
18555
- db.update(tickets).set({ deleted_at: nowTimestamp10(), updated_at: nowTimestamp10() }).where(and(eq(tickets.project_id, projectId), eq(tickets.proposal_id, proposalId), eq(tickets.id, existing.id))).run();
18315
+ db.update(tickets).set({ deleted_at: nowTimestamp9(), updated_at: nowTimestamp9() }).where(eq(tickets.id, existing.id)).run();
18556
18316
  return true;
18557
18317
  };
18558
18318
  const save = (input) => {
18559
18319
  const existing = get(input.id);
18560
- const timestamp = nowTimestamp10();
18320
+ const timestamp = nowTimestamp9();
18561
18321
  const normalizedShorthand = input.shorthand ? normalizeTicketShorthand(input.shorthand) : normalizeTicketShorthand(input.id);
18562
18322
  if (existing) {
18563
18323
  db.update(tickets).set({
@@ -18568,7 +18328,6 @@ var createTicketsService = (db) => {
18568
18328
  parallelizable: input.parallelizable ?? existing.parallelizable,
18569
18329
  complexity: input.complexity ?? existing.complexity,
18570
18330
  status_id: input.status_id ?? existing.status_id,
18571
- proposal_id: input.proposal_id ?? existing.proposal_id,
18572
18331
  parent_id: input.parent_id ?? existing.parent_id,
18573
18332
  blocked_reason: input.blocked_reason ?? existing.blocked_reason,
18574
18333
  depends_on: input.depends_on ?? existing.depends_on,
@@ -18585,7 +18344,6 @@ var createTicketsService = (db) => {
18585
18344
  parallelizable: input.parallelizable ?? existing.parallelizable,
18586
18345
  complexity: input.complexity ?? existing.complexity,
18587
18346
  status_id: input.status_id ?? existing.status_id,
18588
- proposal_id: input.proposal_id ?? existing.proposal_id,
18589
18347
  parent_id: input.parent_id ?? existing.parent_id,
18590
18348
  blocked_reason: input.blocked_reason ?? existing.blocked_reason,
18591
18349
  depends_on: input.depends_on ?? existing.depends_on,
@@ -18598,7 +18356,6 @@ var createTicketsService = (db) => {
18598
18356
  id: UUID_PATTERN.test(input.id) ? input.id : crypto.randomUUID(),
18599
18357
  shorthand: normalizedShorthand ?? allocateTicketShorthand(),
18600
18358
  project_id: input.project_id,
18601
- proposal_id: input.proposal_id ?? null,
18602
18359
  status_id: input.status_id ?? null,
18603
18360
  parent_id: input.parent_id ?? null,
18604
18361
  title: input.title ?? null,
@@ -18624,16 +18381,12 @@ var createTicketsService = (db) => {
18624
18381
  create,
18625
18382
  update,
18626
18383
  remove,
18627
- save,
18628
- listByProposal,
18629
- createForProposal,
18630
- updateForProposal,
18631
- removeForProposal
18384
+ save
18632
18385
  };
18633
18386
  };
18634
18387
 
18635
18388
  // ../schub-db/src/services/workspace-artifacts.ts
18636
- var nowTimestamp11 = () => new Date().toISOString();
18389
+ var nowTimestamp10 = () => new Date().toISOString();
18637
18390
  var createWorkspaceArtifactsService = (db) => {
18638
18391
  const list = (ticketId) => db.select({
18639
18392
  id: workspace_artifacts.id,
@@ -18651,7 +18404,7 @@ var createWorkspaceArtifactsService = (db) => {
18651
18404
  return artifact ?? null;
18652
18405
  };
18653
18406
  const upsertByPath = (ticketId, input) => {
18654
- const timestamp = nowTimestamp11();
18407
+ const timestamp = nowTimestamp10();
18655
18408
  const existing = db.select().from(workspace_artifacts).where(and(eq(workspace_artifacts.ticket_id, ticketId), eq(workspace_artifacts.relative_path, input.relative_path))).get();
18656
18409
  if (existing) {
18657
18410
  db.update(workspace_artifacts).set({ file_id: input.file_id }).where(eq(workspace_artifacts.id, existing.id)).run();
@@ -18696,7 +18449,7 @@ var createWorkspaceArtifactsService = (db) => {
18696
18449
  };
18697
18450
 
18698
18451
  // ../schub-db/src/services/workspaces.ts
18699
- var nowTimestamp12 = () => new Date().toISOString();
18452
+ var nowTimestamp11 = () => new Date().toISOString();
18700
18453
  var formatWorkspaceShorthand = (sequence) => `A${sequence.toString().padStart(4, "0")}`;
18701
18454
  var parseWorkspaceShorthand = (value) => {
18702
18455
  if (!value) {
@@ -18767,7 +18520,7 @@ var createWorkspacesService = (db) => {
18767
18520
  }).run();
18768
18521
  };
18769
18522
  const create = (input) => {
18770
- const timestamp = nowTimestamp12();
18523
+ const timestamp = nowTimestamp11();
18771
18524
  const workspaceShorthand = input.workspace_shorthand === undefined || input.workspace_shorthand === null ? null : normalizeWorkspaceShorthand(input.workspace_shorthand);
18772
18525
  const workspace = {
18773
18526
  id: input.id?.trim() || crypto.randomUUID(),
@@ -18798,7 +18551,7 @@ var createWorkspacesService = (db) => {
18798
18551
  worktree_path: input.worktree_path === undefined ? existing.worktree_path ?? null : input.worktree_path,
18799
18552
  workspace_shorthand: resolveWorkspaceShorthandForUpdate(input.workspace_shorthand, existing.workspace_shorthand),
18800
18553
  ticket_id: input.ticket_id === undefined ? existing.ticket_id : input.ticket_id,
18801
- updated_at: nowTimestamp12()
18554
+ updated_at: nowTimestamp11()
18802
18555
  };
18803
18556
  db.update(workspaces).set({
18804
18557
  name: updated.name,
@@ -18841,10 +18594,8 @@ var createDbServices = (config2 = {}) => {
18841
18594
  const docs = createDocsService();
18842
18595
  const files2 = createFilesService(db, storageRoot);
18843
18596
  const gitDiff = createGitDiffService();
18844
- const proposalStatuses = createProposalStatusesService(db);
18845
18597
  const projects2 = createProjectsService(db);
18846
18598
  const projectDocs = createProjectDocsService(db, files2);
18847
- const proposals2 = createProposalsService(db);
18848
18599
  const repos2 = createReposService(db);
18849
18600
  const sessions2 = createSessionsService(db);
18850
18601
  const settings = createSettingsService(config2.settingsPath);
@@ -18859,10 +18610,8 @@ var createDbServices = (config2 = {}) => {
18859
18610
  docs,
18860
18611
  files: files2,
18861
18612
  gitDiff,
18862
- proposalStatuses,
18863
18613
  projects: projects2,
18864
18614
  projectDocs,
18865
- proposals: proposals2,
18866
18615
  repos: repos2,
18867
18616
  sessions: sessions2,
18868
18617
  settings,