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.
- package/README.md +3 -2
- package/dist/api/server.js +521 -1221
- package/dist/dashboard/assets/{EquationComponent-CmMddHEw.js → EquationComponent-DYTYbmpl.js} +1 -1
- package/dist/dashboard/assets/index-raRkw0ib.js +354 -0
- package/dist/dashboard/index.html +1 -1
- package/dist/index.js +7458 -10106
- package/dist/services/runtime.js +133 -384
- package/drizzle/0010_restructure_templates.sql +11 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/skills/create-proposal/SKILL.md +17 -20
- package/skills/create-sub-tickets/SKILL.md +2 -14
- package/skills/create-ticket/SKILL.md +0 -1
- package/skills/refine-ticket/SKILL.md +3 -3
- package/skills/review-ticket/SKILL.md +40 -0
- package/skills/update-documentation/SKILL.md +30 -0
- package/templates/{create-proposal/adr-template.md → adr-template.md} +3 -3
- package/templates/{create-proposal/cookbook-template.md → cookbook-template.md} +1 -1
- package/templates/{create-proposal/proposal-template.md → proposal-template.md} +10 -2
- package/templates/{review-proposal/q&a-template.md → q&a-template.md} +2 -2
- package/templates/{review-proposal/review-me-template.md → review-me-template.md} +2 -2
- package/templates/{create-ticket/ticket-template.md → ticket-template.md} +1 -2
- package/dist/dashboard/assets/index-UwpVAg9D.js +0 -354
- package/skills/review-proposal/SKILL.md +0 -40
- package/templates/create-tasks/task-template.md +0 -69
package/dist/api/server.js
CHANGED
|
@@ -31796,9 +31796,6 @@ __export(exports_schema, {
|
|
|
31796
31796
|
templates: () => templates,
|
|
31797
31797
|
sessions: () => sessions,
|
|
31798
31798
|
repos: () => repos,
|
|
31799
|
-
proposals: () => proposals,
|
|
31800
|
-
proposal_statuses: () => proposal_statuses,
|
|
31801
|
-
proposal_files: () => proposal_files,
|
|
31802
31799
|
projects: () => projects,
|
|
31803
31800
|
project_repos: () => project_repos,
|
|
31804
31801
|
project_docs: () => project_docs,
|
|
@@ -33172,34 +33169,10 @@ var ticket_statuses = sqliteTable("ticket_statuses", {
|
|
|
33172
33169
|
created_at: text("created_at").notNull(),
|
|
33173
33170
|
updated_at: text("updated_at").notNull()
|
|
33174
33171
|
});
|
|
33175
|
-
var proposal_statuses = sqliteTable("proposal_statuses", {
|
|
33176
|
-
id: text("id").primaryKey(),
|
|
33177
|
-
project_id: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
33178
|
-
name: text("name").notNull(),
|
|
33179
|
-
sort_order: integer2("sort_order").notNull(),
|
|
33180
|
-
is_default: integer2("is_default").notNull(),
|
|
33181
|
-
created_at: text("created_at").notNull(),
|
|
33182
|
-
updated_at: text("updated_at").notNull()
|
|
33183
|
-
});
|
|
33184
|
-
var proposals = sqliteTable("proposals", {
|
|
33185
|
-
id: text("id").primaryKey(),
|
|
33186
|
-
shorthand: text("shorthand").notNull(),
|
|
33187
|
-
project_id: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
33188
|
-
repo_id: text("repo_id").references(() => repos.id, { onDelete: "set null" }),
|
|
33189
|
-
input: text("input"),
|
|
33190
|
-
status: text("status").notNull().default("draft"),
|
|
33191
|
-
archived: integer2("archived").notNull().default(0),
|
|
33192
|
-
staged: integer2("staged").notNull().default(0),
|
|
33193
|
-
created_at: text("created_at").notNull(),
|
|
33194
|
-
updated_at: text("updated_at").notNull()
|
|
33195
|
-
}, (table) => [uniqueIndex("proposals_project_shorthand_idx").on(table.project_id, table.shorthand)]);
|
|
33196
33172
|
var tickets = sqliteTable("tickets", {
|
|
33197
33173
|
id: text("id").primaryKey(),
|
|
33198
33174
|
shorthand: text("shorthand").notNull(),
|
|
33199
33175
|
project_id: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
|
|
33200
|
-
proposal_id: text("proposal_id").references(() => proposals.id, {
|
|
33201
|
-
onDelete: "set null"
|
|
33202
|
-
}),
|
|
33203
33176
|
status_id: text("status_id").references(() => ticket_statuses.id, {
|
|
33204
33177
|
onDelete: "set null"
|
|
33205
33178
|
}),
|
|
@@ -33293,12 +33266,6 @@ var project_doc_files = sqliteTable("project_doc_files", {
|
|
|
33293
33266
|
file_id: text("file_id").notNull().references(() => files.id, { onDelete: "cascade" }),
|
|
33294
33267
|
created_at: text("created_at").notNull()
|
|
33295
33268
|
}, (table) => [uniqueIndex("project_doc_files_project_doc_file_idx").on(table.project_doc_id, table.file_id)]);
|
|
33296
|
-
var proposal_files = sqliteTable("proposal_files", {
|
|
33297
|
-
id: text("id").primaryKey(),
|
|
33298
|
-
proposal_id: text("proposal_id").notNull().references(() => proposals.id, { onDelete: "cascade" }),
|
|
33299
|
-
file_id: text("file_id").notNull().references(() => files.id, { onDelete: "cascade" }),
|
|
33300
|
-
created_at: text("created_at").notNull()
|
|
33301
|
-
});
|
|
33302
33269
|
var ticket_files = sqliteTable("ticket_files", {
|
|
33303
33270
|
id: text("id").primaryKey(),
|
|
33304
33271
|
ticket_id: text("ticket_id").notNull().references(() => tickets.id, { onDelete: "cascade" }),
|
|
@@ -33319,7 +33286,8 @@ var templates = sqliteTable("templates", {
|
|
|
33319
33286
|
}),
|
|
33320
33287
|
name: text("name").notNull(),
|
|
33321
33288
|
template_type: text("template_type").notNull(),
|
|
33322
|
-
|
|
33289
|
+
file_id: text("file_id").notNull().references(() => files.id),
|
|
33290
|
+
is_default: integer2("is_default").notNull().default(0),
|
|
33323
33291
|
created_at: text("created_at").notNull(),
|
|
33324
33292
|
updated_at: text("updated_at").notNull()
|
|
33325
33293
|
});
|
|
@@ -33622,7 +33590,6 @@ var sessionSelectSchema = createSelectSchema(sessions, {
|
|
|
33622
33590
|
});
|
|
33623
33591
|
var ticketTagSelectSchema = createSelectSchema(ticket_tags);
|
|
33624
33592
|
var workspaceSelectSchema = createSelectSchema(workspaces);
|
|
33625
|
-
var proposalSelectSchema = createSelectSchema(proposals);
|
|
33626
33593
|
var ticketAttemptSchema = exports_external.object({
|
|
33627
33594
|
id: exports_external.string(),
|
|
33628
33595
|
label: exports_external.string(),
|
|
@@ -33813,7 +33780,6 @@ var createFilesService = (db, storageRoot) => {
|
|
|
33813
33780
|
}).where(eq(files.id, fileId)).run();
|
|
33814
33781
|
return updated;
|
|
33815
33782
|
};
|
|
33816
|
-
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);
|
|
33817
33783
|
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);
|
|
33818
33784
|
const attachToProjectDocs = (projectDocId, fileId) => {
|
|
33819
33785
|
const existing = get(fileId);
|
|
@@ -33838,26 +33804,6 @@ var createFilesService = (db, storageRoot) => {
|
|
|
33838
33804
|
db.delete(project_doc_files).where(and(eq(project_doc_files.project_doc_id, projectDocId), eq(project_doc_files.file_id, fileId))).run();
|
|
33839
33805
|
return true;
|
|
33840
33806
|
};
|
|
33841
|
-
const attachToProposal = (proposalId, fileId) => {
|
|
33842
|
-
const existing = get(fileId);
|
|
33843
|
-
if (!existing)
|
|
33844
|
-
return null;
|
|
33845
|
-
const link = {
|
|
33846
|
-
id: crypto.randomUUID(),
|
|
33847
|
-
proposal_id: proposalId,
|
|
33848
|
-
file_id: fileId,
|
|
33849
|
-
created_at: nowTimestamp()
|
|
33850
|
-
};
|
|
33851
|
-
db.insert(proposal_files).values(link).run();
|
|
33852
|
-
return existing;
|
|
33853
|
-
};
|
|
33854
|
-
const detachFromProposal = (proposalId, fileId) => {
|
|
33855
|
-
const existing = db.select().from(proposal_files).where(and(eq(proposal_files.proposal_id, proposalId), eq(proposal_files.file_id, fileId))).get();
|
|
33856
|
-
if (!existing)
|
|
33857
|
-
return false;
|
|
33858
|
-
db.delete(proposal_files).where(and(eq(proposal_files.proposal_id, proposalId), eq(proposal_files.file_id, fileId))).run();
|
|
33859
|
-
return true;
|
|
33860
|
-
};
|
|
33861
33807
|
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);
|
|
33862
33808
|
const attachToTicket = (ticketId, fileId) => {
|
|
33863
33809
|
const existing = get(fileId);
|
|
@@ -33888,9 +33834,6 @@ var createFilesService = (db, storageRoot) => {
|
|
|
33888
33834
|
listForProjectDocs,
|
|
33889
33835
|
attachToProjectDocs,
|
|
33890
33836
|
detachFromProjectDocs,
|
|
33891
|
-
listForProposal,
|
|
33892
|
-
attachToProposal,
|
|
33893
|
-
detachFromProposal,
|
|
33894
33837
|
listForTicket,
|
|
33895
33838
|
attachToTicket,
|
|
33896
33839
|
detachFromTicket
|
|
@@ -34597,15 +34540,6 @@ var DEFAULT_TICKET_STATUSES = [
|
|
|
34597
34540
|
column_actions: []
|
|
34598
34541
|
}
|
|
34599
34542
|
];
|
|
34600
|
-
var DEFAULT_PROPOSAL_STATUSES = [
|
|
34601
|
-
{ name: "draft", is_default: 1 },
|
|
34602
|
-
{ name: "pending review", is_default: 0 },
|
|
34603
|
-
{ name: "wip", is_default: 0 },
|
|
34604
|
-
{ name: "in-review", is_default: 0 },
|
|
34605
|
-
{ name: "accepted", is_default: 0 },
|
|
34606
|
-
{ name: "rejected", is_default: 0 },
|
|
34607
|
-
{ name: "done", is_default: 0 }
|
|
34608
|
-
];
|
|
34609
34543
|
var DEFAULT_TICKET_TAGS = [
|
|
34610
34544
|
{ name: "bug", color: "red" },
|
|
34611
34545
|
{ name: "feature", color: "blue" },
|
|
@@ -34668,15 +34602,6 @@ var createProjectsService = (db) => {
|
|
|
34668
34602
|
created_at: timestamp,
|
|
34669
34603
|
updated_at: timestamp
|
|
34670
34604
|
}))).run();
|
|
34671
|
-
db.insert(proposal_statuses).values(DEFAULT_PROPOSAL_STATUSES.map((status, index) => ({
|
|
34672
|
-
id: crypto.randomUUID(),
|
|
34673
|
-
project_id: project.id,
|
|
34674
|
-
name: status.name,
|
|
34675
|
-
sort_order: index + 1,
|
|
34676
|
-
is_default: status.is_default,
|
|
34677
|
-
created_at: timestamp,
|
|
34678
|
-
updated_at: timestamp
|
|
34679
|
-
}))).run();
|
|
34680
34605
|
db.insert(ticket_tags).values(DEFAULT_TICKET_TAGS.map((tag) => ({
|
|
34681
34606
|
id: crypto.randomUUID(),
|
|
34682
34607
|
project_id: project.id,
|
|
@@ -34729,165 +34654,9 @@ var createProjectsService = (db) => {
|
|
|
34729
34654
|
return { list, get, getSnapshot, getSnapshotPatch, subscribe, create, update, remove };
|
|
34730
34655
|
};
|
|
34731
34656
|
|
|
34732
|
-
// ../schub-db/src/services/proposal-statuses.ts
|
|
34733
|
-
var createProposalStatusesService = (db) => {
|
|
34734
|
-
const list = (projectId) => db.select().from(proposal_statuses).where(eq(proposal_statuses.project_id, projectId)).orderBy(proposal_statuses.sort_order).all();
|
|
34735
|
-
const get = (projectId, statusId) => {
|
|
34736
|
-
const status = db.select().from(proposal_statuses).where(and(eq(proposal_statuses.project_id, projectId), eq(proposal_statuses.id, statusId))).get();
|
|
34737
|
-
return status ?? null;
|
|
34738
|
-
};
|
|
34739
|
-
const getByName = (projectId, statusName) => {
|
|
34740
|
-
const status = db.select().from(proposal_statuses).where(and(eq(proposal_statuses.project_id, projectId), eq(proposal_statuses.name, statusName))).get();
|
|
34741
|
-
return status ?? null;
|
|
34742
|
-
};
|
|
34743
|
-
const getDefault = (projectId) => {
|
|
34744
|
-
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();
|
|
34745
|
-
if (explicitDefault) {
|
|
34746
|
-
return explicitDefault;
|
|
34747
|
-
}
|
|
34748
|
-
return list(projectId)[0] ?? null;
|
|
34749
|
-
};
|
|
34750
|
-
return { list, get, getByName, getDefault };
|
|
34751
|
-
};
|
|
34752
|
-
|
|
34753
|
-
// ../schub-db/src/services/proposals.ts
|
|
34754
|
-
var nowTimestamp4 = () => new Date().toISOString();
|
|
34755
|
-
var parseShorthandSequence = (shorthand) => {
|
|
34756
|
-
const match2 = shorthand.match(/^[CP](\d+)/i);
|
|
34757
|
-
if (!match2) {
|
|
34758
|
-
return null;
|
|
34759
|
-
}
|
|
34760
|
-
return Number.parseInt(match2[1], 10);
|
|
34761
|
-
};
|
|
34762
|
-
var formatShorthand = (sequence) => `P${sequence.toString().padStart(4, "0")}`;
|
|
34763
|
-
var normalizeProposalShorthand = (shorthand) => {
|
|
34764
|
-
const sequence = parseShorthandSequence(shorthand);
|
|
34765
|
-
if (sequence === null) {
|
|
34766
|
-
throw new Error(`Invalid proposal shorthand '${shorthand}'.`);
|
|
34767
|
-
}
|
|
34768
|
-
return formatShorthand(sequence);
|
|
34769
|
-
};
|
|
34770
|
-
var createProposalsService = (db) => {
|
|
34771
|
-
const getDefaultStatusName = (projectId) => {
|
|
34772
|
-
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();
|
|
34773
|
-
if (explicitDefault) {
|
|
34774
|
-
return explicitDefault.name;
|
|
34775
|
-
}
|
|
34776
|
-
const firstStatus = db.select({ name: proposal_statuses.name }).from(proposal_statuses).where(eq(proposal_statuses.project_id, projectId)).orderBy(proposal_statuses.sort_order).get();
|
|
34777
|
-
return firstStatus?.name ?? "draft";
|
|
34778
|
-
};
|
|
34779
|
-
const assertKnownStatus = (projectId, status) => {
|
|
34780
|
-
const normalizedStatus = status.trim().toLowerCase();
|
|
34781
|
-
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);
|
|
34782
|
-
if (!exists) {
|
|
34783
|
-
throw new Error(`Proposal status '${status}' does not exist for project '${projectId}'.`);
|
|
34784
|
-
}
|
|
34785
|
-
};
|
|
34786
|
-
const allocateProposalShorthand = () => {
|
|
34787
|
-
return db.transaction((tx) => {
|
|
34788
|
-
const priorShorthands = tx.select({ shorthand: proposals.shorthand }).from(proposals).all();
|
|
34789
|
-
const maxSequence = priorShorthands.reduce((max, proposal) => {
|
|
34790
|
-
if (!proposal.shorthand) {
|
|
34791
|
-
return max;
|
|
34792
|
-
}
|
|
34793
|
-
const parsed = parseShorthandSequence(proposal.shorthand);
|
|
34794
|
-
if (parsed === null) {
|
|
34795
|
-
return max;
|
|
34796
|
-
}
|
|
34797
|
-
return Math.max(max, parsed);
|
|
34798
|
-
}, 0);
|
|
34799
|
-
return formatShorthand(maxSequence + 1);
|
|
34800
|
-
});
|
|
34801
|
-
};
|
|
34802
|
-
const list = (projectId) => db.select().from(proposals).where(eq(proposals.project_id, projectId)).orderBy(proposals.created_at).all();
|
|
34803
|
-
const get = (projectId, proposalId) => {
|
|
34804
|
-
const proposal = db.select().from(proposals).where(and(eq(proposals.project_id, projectId), eq(proposals.id, proposalId))).get();
|
|
34805
|
-
return proposal ?? null;
|
|
34806
|
-
};
|
|
34807
|
-
const create = (projectId, input) => {
|
|
34808
|
-
const timestamp = nowTimestamp4();
|
|
34809
|
-
const shorthand = allocateProposalShorthand();
|
|
34810
|
-
const defaultStatus = getDefaultStatusName(projectId);
|
|
34811
|
-
const proposal = {
|
|
34812
|
-
id: crypto.randomUUID(),
|
|
34813
|
-
shorthand,
|
|
34814
|
-
project_id: projectId,
|
|
34815
|
-
repo_id: input.repo_id ?? null,
|
|
34816
|
-
input: input.input ?? null,
|
|
34817
|
-
status: defaultStatus,
|
|
34818
|
-
archived: 0,
|
|
34819
|
-
staged: input.staged ?? 0,
|
|
34820
|
-
created_at: timestamp,
|
|
34821
|
-
updated_at: timestamp
|
|
34822
|
-
};
|
|
34823
|
-
db.insert(proposals).values(proposal).run();
|
|
34824
|
-
return proposal;
|
|
34825
|
-
};
|
|
34826
|
-
const createWithShorthand = (projectId, shorthand, input) => {
|
|
34827
|
-
const timestamp = nowTimestamp4();
|
|
34828
|
-
const normalizedShorthand = normalizeProposalShorthand(shorthand);
|
|
34829
|
-
const defaultStatus = getDefaultStatusName(projectId);
|
|
34830
|
-
const proposal = {
|
|
34831
|
-
id: crypto.randomUUID(),
|
|
34832
|
-
shorthand: normalizedShorthand,
|
|
34833
|
-
project_id: projectId,
|
|
34834
|
-
repo_id: null,
|
|
34835
|
-
input: input.input ?? null,
|
|
34836
|
-
status: defaultStatus,
|
|
34837
|
-
archived: 0,
|
|
34838
|
-
staged: input.staged ?? 0,
|
|
34839
|
-
created_at: timestamp,
|
|
34840
|
-
updated_at: timestamp
|
|
34841
|
-
};
|
|
34842
|
-
db.insert(proposals).values(proposal).run();
|
|
34843
|
-
return proposal;
|
|
34844
|
-
};
|
|
34845
|
-
const update = (projectId, proposalId, input) => {
|
|
34846
|
-
const existing = get(projectId, proposalId);
|
|
34847
|
-
if (!existing)
|
|
34848
|
-
return null;
|
|
34849
|
-
const proposalInput = input.input === undefined ? existing.input : input.input;
|
|
34850
|
-
const repo_id = input.repo_id === undefined ? existing.repo_id : input.repo_id;
|
|
34851
|
-
const status = input.status ?? existing.status;
|
|
34852
|
-
const archived = input.archived === undefined ? existing.archived : input.archived;
|
|
34853
|
-
const staged = input.staged === undefined ? existing.staged : input.staged;
|
|
34854
|
-
if (input.status !== undefined) {
|
|
34855
|
-
assertKnownStatus(projectId, status);
|
|
34856
|
-
}
|
|
34857
|
-
const updated = {
|
|
34858
|
-
...existing,
|
|
34859
|
-
repo_id,
|
|
34860
|
-
input: proposalInput,
|
|
34861
|
-
status,
|
|
34862
|
-
archived,
|
|
34863
|
-
staged,
|
|
34864
|
-
updated_at: nowTimestamp4()
|
|
34865
|
-
};
|
|
34866
|
-
db.update(proposals).set({
|
|
34867
|
-
repo_id: updated.repo_id,
|
|
34868
|
-
input: updated.input,
|
|
34869
|
-
status: updated.status,
|
|
34870
|
-
archived: updated.archived,
|
|
34871
|
-
staged: updated.staged,
|
|
34872
|
-
updated_at: updated.updated_at
|
|
34873
|
-
}).where(and(eq(proposals.project_id, projectId), eq(proposals.id, proposalId))).run();
|
|
34874
|
-
return updated;
|
|
34875
|
-
};
|
|
34876
|
-
const remove = (projectId, proposalId) => {
|
|
34877
|
-
const existing = get(projectId, proposalId);
|
|
34878
|
-
if (!existing)
|
|
34879
|
-
return false;
|
|
34880
|
-
const timestamp = nowTimestamp4();
|
|
34881
|
-
db.update(tickets).set({ proposal_id: null, updated_at: timestamp }).where(and(eq(tickets.project_id, projectId), eq(tickets.proposal_id, proposalId))).run();
|
|
34882
|
-
db.delete(proposals).where(and(eq(proposals.project_id, projectId), eq(proposals.id, proposalId))).run();
|
|
34883
|
-
return true;
|
|
34884
|
-
};
|
|
34885
|
-
return { list, get, create, createWithShorthand, update, remove };
|
|
34886
|
-
};
|
|
34887
|
-
|
|
34888
34657
|
// ../schub-db/src/services/repos.ts
|
|
34889
34658
|
import { basename } from "node:path";
|
|
34890
|
-
var
|
|
34659
|
+
var nowTimestamp4 = () => new Date().toISOString();
|
|
34891
34660
|
var normalizeRepoPath = (value) => value.replace(/[\\/]+$/, "");
|
|
34892
34661
|
var deriveRepoName = (repoPath) => {
|
|
34893
34662
|
const normalized = normalizeRepoPath(repoPath);
|
|
@@ -34910,7 +34679,7 @@ var createReposService = (db) => {
|
|
|
34910
34679
|
return row?.repo ?? null;
|
|
34911
34680
|
};
|
|
34912
34681
|
const upsertRepo = (input) => {
|
|
34913
|
-
const timestamp =
|
|
34682
|
+
const timestamp = nowTimestamp4();
|
|
34914
34683
|
const displayName = resolveDisplayName(input.display_name);
|
|
34915
34684
|
const pathValue = normalizeRepoPath(input.path);
|
|
34916
34685
|
const name = deriveRepoName(pathValue);
|
|
@@ -34949,13 +34718,13 @@ var createReposService = (db) => {
|
|
|
34949
34718
|
const updated = {
|
|
34950
34719
|
...existing,
|
|
34951
34720
|
display_name: displayName,
|
|
34952
|
-
updated_at:
|
|
34721
|
+
updated_at: nowTimestamp4()
|
|
34953
34722
|
};
|
|
34954
34723
|
db.update(repos).set({ display_name: updated.display_name, updated_at: updated.updated_at }).where(eq(repos.id, repoId)).run();
|
|
34955
34724
|
return updated;
|
|
34956
34725
|
};
|
|
34957
34726
|
const addToProject = (projectId, input) => {
|
|
34958
|
-
const timestamp =
|
|
34727
|
+
const timestamp = nowTimestamp4();
|
|
34959
34728
|
const repo = upsertRepo({ path: input.git_repo_path, display_name: input.display_name });
|
|
34960
34729
|
const existingLink = db.select().from(project_repos).where(and(eq(project_repos.project_id, projectId), eq(project_repos.repo_id, repo.id))).get();
|
|
34961
34730
|
if (!existingLink) {
|
|
@@ -34983,7 +34752,7 @@ var createReposService = (db) => {
|
|
|
34983
34752
|
};
|
|
34984
34753
|
|
|
34985
34754
|
// ../schub-db/src/services/sessions.ts
|
|
34986
|
-
var
|
|
34755
|
+
var nowTimestamp5 = () => new Date().toISOString();
|
|
34987
34756
|
var SESSION_STATUSES = ["in_progress", "completed", "failed"];
|
|
34988
34757
|
var normalizeSessionStatus = (status) => {
|
|
34989
34758
|
if (!status)
|
|
@@ -35032,7 +34801,7 @@ var createSessionsService = (db) => {
|
|
|
35032
34801
|
return session ? formatSession(session) : null;
|
|
35033
34802
|
};
|
|
35034
34803
|
const create = (input) => {
|
|
35035
|
-
const timestamp =
|
|
34804
|
+
const timestamp = nowTimestamp5();
|
|
35036
34805
|
const session = {
|
|
35037
34806
|
id: input.id?.trim() || crypto.randomUUID(),
|
|
35038
34807
|
title: input.title,
|
|
@@ -35074,7 +34843,7 @@ var createSessionsService = (db) => {
|
|
|
35074
34843
|
agent,
|
|
35075
34844
|
agent_session_id: agentSessionId,
|
|
35076
34845
|
content,
|
|
35077
|
-
updated_at:
|
|
34846
|
+
updated_at: nowTimestamp5()
|
|
35078
34847
|
};
|
|
35079
34848
|
db.update(sessions).set({
|
|
35080
34849
|
title: updated.title,
|
|
@@ -35101,21 +34870,106 @@ var createSessionsService = (db) => {
|
|
|
35101
34870
|
};
|
|
35102
34871
|
|
|
35103
34872
|
// ../schub-db/src/services/templates.ts
|
|
35104
|
-
var
|
|
34873
|
+
var nowTimestamp6 = () => new Date().toISOString();
|
|
35105
34874
|
var createTemplatesService = (db) => {
|
|
35106
|
-
const list = () => db.select(
|
|
34875
|
+
const list = () => db.select({
|
|
34876
|
+
id: templates.id,
|
|
34877
|
+
project_id: templates.project_id,
|
|
34878
|
+
name: templates.name,
|
|
34879
|
+
template_type: templates.template_type,
|
|
34880
|
+
file_id: templates.file_id,
|
|
34881
|
+
is_default: templates.is_default,
|
|
34882
|
+
created_at: templates.created_at,
|
|
34883
|
+
updated_at: templates.updated_at,
|
|
34884
|
+
file_name: files.file_name,
|
|
34885
|
+
storage_path: files.storage_path,
|
|
34886
|
+
mime_type: files.mime_type,
|
|
34887
|
+
size_bytes: files.size_bytes
|
|
34888
|
+
}).from(templates).innerJoin(files, eq(templates.file_id, files.id)).orderBy(templates.created_at).all();
|
|
34889
|
+
const listByProject = (projectId) => db.select({
|
|
34890
|
+
id: templates.id,
|
|
34891
|
+
project_id: templates.project_id,
|
|
34892
|
+
name: templates.name,
|
|
34893
|
+
template_type: templates.template_type,
|
|
34894
|
+
file_id: templates.file_id,
|
|
34895
|
+
is_default: templates.is_default,
|
|
34896
|
+
created_at: templates.created_at,
|
|
34897
|
+
updated_at: templates.updated_at,
|
|
34898
|
+
file_name: files.file_name,
|
|
34899
|
+
storage_path: files.storage_path,
|
|
34900
|
+
mime_type: files.mime_type,
|
|
34901
|
+
size_bytes: files.size_bytes
|
|
34902
|
+
}).from(templates).innerJoin(files, eq(templates.file_id, files.id)).where(eq(templates.project_id, projectId)).orderBy(templates.created_at).all();
|
|
35107
34903
|
const get = (templateId) => {
|
|
35108
|
-
const
|
|
35109
|
-
|
|
34904
|
+
const row = db.select({
|
|
34905
|
+
id: templates.id,
|
|
34906
|
+
project_id: templates.project_id,
|
|
34907
|
+
name: templates.name,
|
|
34908
|
+
template_type: templates.template_type,
|
|
34909
|
+
file_id: templates.file_id,
|
|
34910
|
+
is_default: templates.is_default,
|
|
34911
|
+
created_at: templates.created_at,
|
|
34912
|
+
updated_at: templates.updated_at,
|
|
34913
|
+
file_name: files.file_name,
|
|
34914
|
+
storage_path: files.storage_path,
|
|
34915
|
+
mime_type: files.mime_type,
|
|
34916
|
+
size_bytes: files.size_bytes
|
|
34917
|
+
}).from(templates).innerJoin(files, eq(templates.file_id, files.id)).where(eq(templates.id, templateId)).get();
|
|
34918
|
+
return row ?? null;
|
|
34919
|
+
};
|
|
34920
|
+
const getDefault = (projectId, templateType) => {
|
|
34921
|
+
const row = db.select({
|
|
34922
|
+
id: templates.id,
|
|
34923
|
+
project_id: templates.project_id,
|
|
34924
|
+
name: templates.name,
|
|
34925
|
+
template_type: templates.template_type,
|
|
34926
|
+
file_id: templates.file_id,
|
|
34927
|
+
is_default: templates.is_default,
|
|
34928
|
+
created_at: templates.created_at,
|
|
34929
|
+
updated_at: templates.updated_at,
|
|
34930
|
+
file_name: files.file_name,
|
|
34931
|
+
storage_path: files.storage_path,
|
|
34932
|
+
mime_type: files.mime_type,
|
|
34933
|
+
size_bytes: files.size_bytes
|
|
34934
|
+
}).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();
|
|
34935
|
+
return row ?? null;
|
|
34936
|
+
};
|
|
34937
|
+
const getByName = (name, projectId) => {
|
|
34938
|
+
const row = db.select({
|
|
34939
|
+
id: templates.id,
|
|
34940
|
+
project_id: templates.project_id,
|
|
34941
|
+
name: templates.name,
|
|
34942
|
+
template_type: templates.template_type,
|
|
34943
|
+
file_id: templates.file_id,
|
|
34944
|
+
is_default: templates.is_default,
|
|
34945
|
+
created_at: templates.created_at,
|
|
34946
|
+
updated_at: templates.updated_at,
|
|
34947
|
+
file_name: files.file_name,
|
|
34948
|
+
storage_path: files.storage_path,
|
|
34949
|
+
mime_type: files.mime_type,
|
|
34950
|
+
size_bytes: files.size_bytes
|
|
34951
|
+
}).from(templates).innerJoin(files, eq(templates.file_id, files.id)).where(and(eq(templates.name, name), eq(templates.project_id, projectId))).get();
|
|
34952
|
+
return row ?? null;
|
|
34953
|
+
};
|
|
34954
|
+
const checkNameUniqueness = (name, projectId, excludeId) => {
|
|
34955
|
+
if (!projectId)
|
|
34956
|
+
return;
|
|
34957
|
+
const existing = db.select({ id: templates.id }).from(templates).where(and(eq(templates.name, name), eq(templates.project_id, projectId))).get();
|
|
34958
|
+
if (existing && existing.id !== excludeId) {
|
|
34959
|
+
throw new Error(`Duplicate template name '${name}' in project '${projectId}'.`);
|
|
34960
|
+
}
|
|
35110
34961
|
};
|
|
35111
34962
|
const create = (input) => {
|
|
35112
|
-
const
|
|
34963
|
+
const projectId = input.project_id ?? null;
|
|
34964
|
+
checkNameUniqueness(input.name, projectId);
|
|
34965
|
+
const timestamp = nowTimestamp6();
|
|
35113
34966
|
const template = {
|
|
35114
34967
|
id: crypto.randomUUID(),
|
|
35115
|
-
project_id:
|
|
34968
|
+
project_id: projectId,
|
|
35116
34969
|
name: input.name,
|
|
35117
34970
|
template_type: input.template_type,
|
|
35118
|
-
|
|
34971
|
+
file_id: input.file_id,
|
|
34972
|
+
is_default: input.is_default ?? 0,
|
|
35119
34973
|
created_at: timestamp,
|
|
35120
34974
|
updated_at: timestamp
|
|
35121
34975
|
};
|
|
@@ -35123,39 +34977,42 @@ var createTemplatesService = (db) => {
|
|
|
35123
34977
|
return template;
|
|
35124
34978
|
};
|
|
35125
34979
|
const update = (templateId, input) => {
|
|
35126
|
-
const existing =
|
|
34980
|
+
const existing = db.select().from(templates).where(eq(templates.id, templateId)).get();
|
|
35127
34981
|
if (!existing)
|
|
35128
34982
|
return null;
|
|
35129
|
-
const
|
|
34983
|
+
const projectId = input.project_id === undefined ? existing.project_id : input.project_id ?? null;
|
|
34984
|
+
checkNameUniqueness(input.name, projectId, templateId);
|
|
35130
34985
|
const updated = {
|
|
35131
34986
|
...existing,
|
|
35132
|
-
project_id,
|
|
34987
|
+
project_id: projectId,
|
|
35133
34988
|
name: input.name,
|
|
35134
34989
|
template_type: input.template_type,
|
|
35135
|
-
|
|
35136
|
-
|
|
34990
|
+
file_id: input.file_id,
|
|
34991
|
+
is_default: input.is_default ?? existing.is_default,
|
|
34992
|
+
updated_at: nowTimestamp6()
|
|
35137
34993
|
};
|
|
35138
34994
|
db.update(templates).set({
|
|
35139
34995
|
project_id: updated.project_id,
|
|
35140
34996
|
name: updated.name,
|
|
35141
34997
|
template_type: updated.template_type,
|
|
35142
|
-
|
|
34998
|
+
file_id: updated.file_id,
|
|
34999
|
+
is_default: updated.is_default,
|
|
35143
35000
|
updated_at: updated.updated_at
|
|
35144
35001
|
}).where(eq(templates.id, templateId)).run();
|
|
35145
35002
|
return updated;
|
|
35146
35003
|
};
|
|
35147
35004
|
const remove = (templateId) => {
|
|
35148
|
-
const existing =
|
|
35005
|
+
const existing = db.select().from(templates).where(eq(templates.id, templateId)).get();
|
|
35149
35006
|
if (!existing)
|
|
35150
35007
|
return false;
|
|
35151
35008
|
db.delete(templates).where(eq(templates.id, templateId)).run();
|
|
35152
35009
|
return true;
|
|
35153
35010
|
};
|
|
35154
|
-
return { list, get, create, update, remove };
|
|
35011
|
+
return { list, listByProject, get, getDefault, getByName, create, update, remove };
|
|
35155
35012
|
};
|
|
35156
35013
|
|
|
35157
35014
|
// ../schub-db/src/services/ticket-statuses.ts
|
|
35158
|
-
var
|
|
35015
|
+
var nowTimestamp7 = () => new Date().toISOString();
|
|
35159
35016
|
var TICKET_STATUS_COLORS = ["gray", "blue", "cyan", "green", "yellow", "orange", "red", "purple", "pink"];
|
|
35160
35017
|
var TICKET_COLUMN_ACTIONS = ["archive_all"];
|
|
35161
35018
|
var isAllowedTicketStatusColor = (color) => TICKET_STATUS_COLORS.includes(color);
|
|
@@ -35209,7 +35066,7 @@ var createTicketStatusesService = (db) => {
|
|
|
35209
35066
|
assertValidTicketColumnActions(input.column_actions ?? []);
|
|
35210
35067
|
const normalizedName = normalizeTicketStatusName(input.name);
|
|
35211
35068
|
assertValidTicketStatusName(normalizedName);
|
|
35212
|
-
const timestamp =
|
|
35069
|
+
const timestamp = nowTimestamp7();
|
|
35213
35070
|
const columnActions = normalizeTicketColumnActions(input.column_actions ?? []);
|
|
35214
35071
|
const status = {
|
|
35215
35072
|
id: crypto.randomUUID(),
|
|
@@ -35251,7 +35108,7 @@ var createTicketStatusesService = (db) => {
|
|
|
35251
35108
|
can_create: input.can_create,
|
|
35252
35109
|
can_attempt_on_drop: input.can_attempt_on_drop,
|
|
35253
35110
|
column_actions: JSON.stringify(columnActions),
|
|
35254
|
-
updated_at:
|
|
35111
|
+
updated_at: nowTimestamp7()
|
|
35255
35112
|
};
|
|
35256
35113
|
db.update(ticket_statuses).set({
|
|
35257
35114
|
name: updated.name,
|
|
@@ -35282,9 +35139,9 @@ var createTicketStatusesService = (db) => {
|
|
|
35282
35139
|
}
|
|
35283
35140
|
if (!remaining.some((status) => status.is_default === 1)) {
|
|
35284
35141
|
db.update(ticket_statuses).set({ is_default: 0 }).where(eq(ticket_statuses.project_id, projectId)).run();
|
|
35285
|
-
db.update(ticket_statuses).set({ is_default: 1, updated_at:
|
|
35142
|
+
db.update(ticket_statuses).set({ is_default: 1, updated_at: nowTimestamp7() }).where(eq(ticket_statuses.id, defaultStatus.id)).run();
|
|
35286
35143
|
}
|
|
35287
|
-
db.update(tickets).set({ status_id: defaultStatus.id, updated_at:
|
|
35144
|
+
db.update(tickets).set({ status_id: defaultStatus.id, updated_at: nowTimestamp7() }).where(eq(tickets.status_id, statusId)).run();
|
|
35288
35145
|
db.delete(ticket_statuses).where(and(eq(ticket_statuses.project_id, projectId), eq(ticket_statuses.id, statusId))).run();
|
|
35289
35146
|
return { ok: true, reassignedTo: defaultStatus.id };
|
|
35290
35147
|
};
|
|
@@ -35292,7 +35149,7 @@ var createTicketStatusesService = (db) => {
|
|
|
35292
35149
|
};
|
|
35293
35150
|
|
|
35294
35151
|
// ../schub-db/src/services/ticket-tags.ts
|
|
35295
|
-
var
|
|
35152
|
+
var nowTimestamp8 = () => new Date().toISOString();
|
|
35296
35153
|
var normalizeTagName = (name) => name.trim().toLowerCase().replace(/[\s-]+/g, "_");
|
|
35297
35154
|
var createTicketTagsService = (db) => {
|
|
35298
35155
|
const list = (projectId) => db.select().from(ticket_tags).where(eq(ticket_tags.project_id, projectId)).orderBy(ticket_tags.name).all();
|
|
@@ -35301,7 +35158,7 @@ var createTicketTagsService = (db) => {
|
|
|
35301
35158
|
return tag ?? null;
|
|
35302
35159
|
};
|
|
35303
35160
|
const create = (projectId, input) => {
|
|
35304
|
-
const timestamp =
|
|
35161
|
+
const timestamp = nowTimestamp8();
|
|
35305
35162
|
const tag = {
|
|
35306
35163
|
id: crypto.randomUUID(),
|
|
35307
35164
|
project_id: projectId,
|
|
@@ -35321,7 +35178,7 @@ var createTicketTagsService = (db) => {
|
|
|
35321
35178
|
...existing,
|
|
35322
35179
|
name: normalizeTagName(input.name),
|
|
35323
35180
|
color: input.color,
|
|
35324
|
-
updated_at:
|
|
35181
|
+
updated_at: nowTimestamp8()
|
|
35325
35182
|
};
|
|
35326
35183
|
db.update(ticket_tags).set({
|
|
35327
35184
|
name: updated.name,
|
|
@@ -35367,7 +35224,7 @@ var createTicketTagsService = (db) => {
|
|
|
35367
35224
|
id: crypto.randomUUID(),
|
|
35368
35225
|
ticket_id: ticketId,
|
|
35369
35226
|
ticket_tag_id: tagId,
|
|
35370
|
-
created_at:
|
|
35227
|
+
created_at: nowTimestamp8()
|
|
35371
35228
|
}))).run();
|
|
35372
35229
|
});
|
|
35373
35230
|
return listIdsForTicket(ticketId);
|
|
@@ -35376,7 +35233,7 @@ var createTicketTagsService = (db) => {
|
|
|
35376
35233
|
};
|
|
35377
35234
|
|
|
35378
35235
|
// ../schub-db/src/services/tickets.ts
|
|
35379
|
-
var
|
|
35236
|
+
var nowTimestamp9 = () => new Date().toISOString();
|
|
35380
35237
|
var formatTicketId = (sequence) => `TK${sequence.toString().padStart(4, "0")}`;
|
|
35381
35238
|
var parseTicketSequence = (value) => {
|
|
35382
35239
|
const match2 = value.match(/^TK(\d+)$/i);
|
|
@@ -35443,13 +35300,12 @@ var createTicketsService = (db) => {
|
|
|
35443
35300
|
throw new Error("Parent ticket not found");
|
|
35444
35301
|
}
|
|
35445
35302
|
}
|
|
35446
|
-
const timestamp =
|
|
35303
|
+
const timestamp = nowTimestamp9();
|
|
35447
35304
|
const shorthand = allocateTicketShorthand();
|
|
35448
35305
|
const ticket = {
|
|
35449
35306
|
id: crypto.randomUUID(),
|
|
35450
35307
|
shorthand,
|
|
35451
35308
|
project_id: input.project_id,
|
|
35452
|
-
proposal_id: input.proposal_id ?? null,
|
|
35453
35309
|
status_id: input.status_id ?? null,
|
|
35454
35310
|
parent_id: input.parent_id ?? null,
|
|
35455
35311
|
title: input.title ?? null,
|
|
@@ -35472,7 +35328,7 @@ var createTicketsService = (db) => {
|
|
|
35472
35328
|
const existing = get(ticketId);
|
|
35473
35329
|
if (!existing)
|
|
35474
35330
|
return null;
|
|
35475
|
-
if (input.parent_id !== undefined) {
|
|
35331
|
+
if (input.parent_id !== undefined && input.parent_id !== existing.parent_id) {
|
|
35476
35332
|
validateParentId(existing.id, input.parent_id, get);
|
|
35477
35333
|
}
|
|
35478
35334
|
const title = input.title === undefined ? existing.title : input.title;
|
|
@@ -35481,7 +35337,6 @@ var createTicketsService = (db) => {
|
|
|
35481
35337
|
const parallelizable = input.parallelizable === undefined ? existing.parallelizable : input.parallelizable;
|
|
35482
35338
|
const complexity = input.complexity === undefined ? existing.complexity : input.complexity;
|
|
35483
35339
|
const status_id = input.status_id === undefined ? existing.status_id : input.status_id;
|
|
35484
|
-
const proposal_id = input.proposal_id === undefined ? existing.proposal_id : input.proposal_id;
|
|
35485
35340
|
const parent_id = input.parent_id === undefined ? existing.parent_id : input.parent_id;
|
|
35486
35341
|
const blocked_reason = input.blocked_reason === undefined ? existing.blocked_reason : input.blocked_reason;
|
|
35487
35342
|
const depends_on = input.depends_on === undefined ? existing.depends_on : input.depends_on;
|
|
@@ -35495,13 +35350,12 @@ var createTicketsService = (db) => {
|
|
|
35495
35350
|
parallelizable,
|
|
35496
35351
|
complexity,
|
|
35497
35352
|
status_id,
|
|
35498
|
-
proposal_id,
|
|
35499
35353
|
parent_id,
|
|
35500
35354
|
blocked_reason,
|
|
35501
35355
|
depends_on,
|
|
35502
35356
|
archived,
|
|
35503
35357
|
staged,
|
|
35504
|
-
updated_at:
|
|
35358
|
+
updated_at: nowTimestamp9()
|
|
35505
35359
|
};
|
|
35506
35360
|
db.update(tickets).set({
|
|
35507
35361
|
title: updated.title,
|
|
@@ -35510,7 +35364,6 @@ var createTicketsService = (db) => {
|
|
|
35510
35364
|
parallelizable: updated.parallelizable,
|
|
35511
35365
|
complexity: updated.complexity,
|
|
35512
35366
|
status_id: updated.status_id,
|
|
35513
|
-
proposal_id: updated.proposal_id,
|
|
35514
35367
|
parent_id: updated.parent_id,
|
|
35515
35368
|
blocked_reason: updated.blocked_reason,
|
|
35516
35369
|
depends_on: updated.depends_on,
|
|
@@ -35525,105 +35378,12 @@ var createTicketsService = (db) => {
|
|
|
35525
35378
|
if (!existing)
|
|
35526
35379
|
return false;
|
|
35527
35380
|
db.update(tickets).set({ parent_id: null }).where(eq(tickets.parent_id, existing.id)).run();
|
|
35528
|
-
db.update(tickets).set({ deleted_at:
|
|
35529
|
-
return true;
|
|
35530
|
-
};
|
|
35531
|
-
const getForProposal = (projectId, proposalId, ticketId) => {
|
|
35532
|
-
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();
|
|
35533
|
-
return ticket ?? null;
|
|
35534
|
-
};
|
|
35535
|
-
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();
|
|
35536
|
-
const createForProposal = (projectId, proposalId, input) => {
|
|
35537
|
-
if (input.parent_id) {
|
|
35538
|
-
const parent = get(input.parent_id);
|
|
35539
|
-
if (!parent) {
|
|
35540
|
-
throw new Error("Parent ticket not found");
|
|
35541
|
-
}
|
|
35542
|
-
}
|
|
35543
|
-
const timestamp = nowTimestamp10();
|
|
35544
|
-
const shorthand = allocateTicketShorthand();
|
|
35545
|
-
const ticket = {
|
|
35546
|
-
id: crypto.randomUUID(),
|
|
35547
|
-
shorthand,
|
|
35548
|
-
project_id: projectId,
|
|
35549
|
-
proposal_id: proposalId,
|
|
35550
|
-
status_id: input.status_id ?? null,
|
|
35551
|
-
parent_id: input.parent_id ?? null,
|
|
35552
|
-
title: input.title ?? null,
|
|
35553
|
-
input: input.input ?? null,
|
|
35554
|
-
priority: input.priority ?? null,
|
|
35555
|
-
parallelizable: input.parallelizable ?? null,
|
|
35556
|
-
complexity: input.complexity ?? null,
|
|
35557
|
-
blocked_reason: input.blocked_reason ?? null,
|
|
35558
|
-
depends_on: input.depends_on ?? null,
|
|
35559
|
-
archived: input.archived ?? 0,
|
|
35560
|
-
staged: input.staged ?? 0,
|
|
35561
|
-
deleted_at: null,
|
|
35562
|
-
created_at: timestamp,
|
|
35563
|
-
updated_at: timestamp
|
|
35564
|
-
};
|
|
35565
|
-
db.insert(tickets).values(ticket).run();
|
|
35566
|
-
return ticket;
|
|
35567
|
-
};
|
|
35568
|
-
const updateForProposal = (projectId, proposalId, ticketId, input) => {
|
|
35569
|
-
const existing = getForProposal(projectId, proposalId, ticketId);
|
|
35570
|
-
if (!existing)
|
|
35571
|
-
return null;
|
|
35572
|
-
if (input.parent_id !== undefined) {
|
|
35573
|
-
validateParentId(existing.id, input.parent_id, get);
|
|
35574
|
-
}
|
|
35575
|
-
const title = input.title === undefined ? existing.title : input.title;
|
|
35576
|
-
const ticketInput = input.input === undefined ? existing.input : input.input;
|
|
35577
|
-
const priority = input.priority === undefined ? existing.priority : input.priority;
|
|
35578
|
-
const parallelizable = input.parallelizable === undefined ? existing.parallelizable : input.parallelizable;
|
|
35579
|
-
const complexity = input.complexity === undefined ? existing.complexity : input.complexity;
|
|
35580
|
-
const status_id = input.status_id === undefined ? existing.status_id : input.status_id;
|
|
35581
|
-
const parent_id = input.parent_id === undefined ? existing.parent_id : input.parent_id;
|
|
35582
|
-
const blocked_reason = input.blocked_reason === undefined ? existing.blocked_reason : input.blocked_reason;
|
|
35583
|
-
const depends_on = input.depends_on === undefined ? existing.depends_on : input.depends_on;
|
|
35584
|
-
const archived = input.archived === undefined ? existing.archived : input.archived;
|
|
35585
|
-
const staged = input.staged === undefined ? existing.staged : input.staged;
|
|
35586
|
-
const updated = {
|
|
35587
|
-
...existing,
|
|
35588
|
-
title,
|
|
35589
|
-
input: ticketInput,
|
|
35590
|
-
priority,
|
|
35591
|
-
parallelizable,
|
|
35592
|
-
complexity,
|
|
35593
|
-
status_id,
|
|
35594
|
-
parent_id,
|
|
35595
|
-
blocked_reason,
|
|
35596
|
-
depends_on,
|
|
35597
|
-
archived,
|
|
35598
|
-
staged,
|
|
35599
|
-
updated_at: nowTimestamp10()
|
|
35600
|
-
};
|
|
35601
|
-
db.update(tickets).set({
|
|
35602
|
-
title: updated.title,
|
|
35603
|
-
input: updated.input,
|
|
35604
|
-
priority: updated.priority,
|
|
35605
|
-
parallelizable: updated.parallelizable,
|
|
35606
|
-
complexity: updated.complexity,
|
|
35607
|
-
status_id: updated.status_id,
|
|
35608
|
-
parent_id: updated.parent_id,
|
|
35609
|
-
blocked_reason: updated.blocked_reason,
|
|
35610
|
-
depends_on: updated.depends_on,
|
|
35611
|
-
archived: updated.archived,
|
|
35612
|
-
staged: updated.staged,
|
|
35613
|
-
updated_at: updated.updated_at
|
|
35614
|
-
}).where(and(eq(tickets.project_id, projectId), eq(tickets.proposal_id, proposalId), eq(tickets.id, existing.id))).run();
|
|
35615
|
-
return updated;
|
|
35616
|
-
};
|
|
35617
|
-
const removeForProposal = (projectId, proposalId, ticketId) => {
|
|
35618
|
-
const existing = getForProposal(projectId, proposalId, ticketId);
|
|
35619
|
-
if (!existing)
|
|
35620
|
-
return false;
|
|
35621
|
-
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();
|
|
35381
|
+
db.update(tickets).set({ deleted_at: nowTimestamp9(), updated_at: nowTimestamp9() }).where(eq(tickets.id, existing.id)).run();
|
|
35622
35382
|
return true;
|
|
35623
35383
|
};
|
|
35624
35384
|
const save = (input) => {
|
|
35625
35385
|
const existing = get(input.id);
|
|
35626
|
-
const timestamp =
|
|
35386
|
+
const timestamp = nowTimestamp9();
|
|
35627
35387
|
const normalizedShorthand = input.shorthand ? normalizeTicketShorthand(input.shorthand) : normalizeTicketShorthand(input.id);
|
|
35628
35388
|
if (existing) {
|
|
35629
35389
|
db.update(tickets).set({
|
|
@@ -35634,7 +35394,6 @@ var createTicketsService = (db) => {
|
|
|
35634
35394
|
parallelizable: input.parallelizable ?? existing.parallelizable,
|
|
35635
35395
|
complexity: input.complexity ?? existing.complexity,
|
|
35636
35396
|
status_id: input.status_id ?? existing.status_id,
|
|
35637
|
-
proposal_id: input.proposal_id ?? existing.proposal_id,
|
|
35638
35397
|
parent_id: input.parent_id ?? existing.parent_id,
|
|
35639
35398
|
blocked_reason: input.blocked_reason ?? existing.blocked_reason,
|
|
35640
35399
|
depends_on: input.depends_on ?? existing.depends_on,
|
|
@@ -35651,7 +35410,6 @@ var createTicketsService = (db) => {
|
|
|
35651
35410
|
parallelizable: input.parallelizable ?? existing.parallelizable,
|
|
35652
35411
|
complexity: input.complexity ?? existing.complexity,
|
|
35653
35412
|
status_id: input.status_id ?? existing.status_id,
|
|
35654
|
-
proposal_id: input.proposal_id ?? existing.proposal_id,
|
|
35655
35413
|
parent_id: input.parent_id ?? existing.parent_id,
|
|
35656
35414
|
blocked_reason: input.blocked_reason ?? existing.blocked_reason,
|
|
35657
35415
|
depends_on: input.depends_on ?? existing.depends_on,
|
|
@@ -35664,7 +35422,6 @@ var createTicketsService = (db) => {
|
|
|
35664
35422
|
id: UUID_PATTERN.test(input.id) ? input.id : crypto.randomUUID(),
|
|
35665
35423
|
shorthand: normalizedShorthand ?? allocateTicketShorthand(),
|
|
35666
35424
|
project_id: input.project_id,
|
|
35667
|
-
proposal_id: input.proposal_id ?? null,
|
|
35668
35425
|
status_id: input.status_id ?? null,
|
|
35669
35426
|
parent_id: input.parent_id ?? null,
|
|
35670
35427
|
title: input.title ?? null,
|
|
@@ -35690,16 +35447,12 @@ var createTicketsService = (db) => {
|
|
|
35690
35447
|
create,
|
|
35691
35448
|
update,
|
|
35692
35449
|
remove,
|
|
35693
|
-
save
|
|
35694
|
-
listByProposal,
|
|
35695
|
-
createForProposal,
|
|
35696
|
-
updateForProposal,
|
|
35697
|
-
removeForProposal
|
|
35450
|
+
save
|
|
35698
35451
|
};
|
|
35699
35452
|
};
|
|
35700
35453
|
|
|
35701
35454
|
// ../schub-db/src/services/workspace-artifacts.ts
|
|
35702
|
-
var
|
|
35455
|
+
var nowTimestamp10 = () => new Date().toISOString();
|
|
35703
35456
|
var createWorkspaceArtifactsService = (db) => {
|
|
35704
35457
|
const list = (ticketId) => db.select({
|
|
35705
35458
|
id: workspace_artifacts.id,
|
|
@@ -35717,7 +35470,7 @@ var createWorkspaceArtifactsService = (db) => {
|
|
|
35717
35470
|
return artifact ?? null;
|
|
35718
35471
|
};
|
|
35719
35472
|
const upsertByPath = (ticketId, input) => {
|
|
35720
|
-
const timestamp =
|
|
35473
|
+
const timestamp = nowTimestamp10();
|
|
35721
35474
|
const existing = db.select().from(workspace_artifacts).where(and(eq(workspace_artifacts.ticket_id, ticketId), eq(workspace_artifacts.relative_path, input.relative_path))).get();
|
|
35722
35475
|
if (existing) {
|
|
35723
35476
|
db.update(workspace_artifacts).set({ file_id: input.file_id }).where(eq(workspace_artifacts.id, existing.id)).run();
|
|
@@ -35762,7 +35515,7 @@ var createWorkspaceArtifactsService = (db) => {
|
|
|
35762
35515
|
};
|
|
35763
35516
|
|
|
35764
35517
|
// ../schub-db/src/services/workspaces.ts
|
|
35765
|
-
var
|
|
35518
|
+
var nowTimestamp11 = () => new Date().toISOString();
|
|
35766
35519
|
var formatWorkspaceShorthand = (sequence) => `A${sequence.toString().padStart(4, "0")}`;
|
|
35767
35520
|
var parseWorkspaceShorthand = (value) => {
|
|
35768
35521
|
if (!value) {
|
|
@@ -35833,7 +35586,7 @@ var createWorkspacesService = (db) => {
|
|
|
35833
35586
|
}).run();
|
|
35834
35587
|
};
|
|
35835
35588
|
const create = (input) => {
|
|
35836
|
-
const timestamp =
|
|
35589
|
+
const timestamp = nowTimestamp11();
|
|
35837
35590
|
const workspaceShorthand = input.workspace_shorthand === undefined || input.workspace_shorthand === null ? null : normalizeWorkspaceShorthand(input.workspace_shorthand);
|
|
35838
35591
|
const workspace = {
|
|
35839
35592
|
id: input.id?.trim() || crypto.randomUUID(),
|
|
@@ -35864,7 +35617,7 @@ var createWorkspacesService = (db) => {
|
|
|
35864
35617
|
worktree_path: input.worktree_path === undefined ? existing.worktree_path ?? null : input.worktree_path,
|
|
35865
35618
|
workspace_shorthand: resolveWorkspaceShorthandForUpdate(input.workspace_shorthand, existing.workspace_shorthand),
|
|
35866
35619
|
ticket_id: input.ticket_id === undefined ? existing.ticket_id : input.ticket_id,
|
|
35867
|
-
updated_at:
|
|
35620
|
+
updated_at: nowTimestamp11()
|
|
35868
35621
|
};
|
|
35869
35622
|
db.update(workspaces).set({
|
|
35870
35623
|
name: updated.name,
|
|
@@ -35907,10 +35660,8 @@ var createDbServices = (config2 = {}) => {
|
|
|
35907
35660
|
const docs = createDocsService();
|
|
35908
35661
|
const files2 = createFilesService(db, storageRoot);
|
|
35909
35662
|
const gitDiff = createGitDiffService();
|
|
35910
|
-
const proposalStatuses = createProposalStatusesService(db);
|
|
35911
35663
|
const projects2 = createProjectsService(db);
|
|
35912
35664
|
const projectDocs = createProjectDocsService(db, files2);
|
|
35913
|
-
const proposals2 = createProposalsService(db);
|
|
35914
35665
|
const repos2 = createReposService(db);
|
|
35915
35666
|
const sessions2 = createSessionsService(db);
|
|
35916
35667
|
const settings = createSettingsService(config2.settingsPath);
|
|
@@ -35925,10 +35676,8 @@ var createDbServices = (config2 = {}) => {
|
|
|
35925
35676
|
docs,
|
|
35926
35677
|
files: files2,
|
|
35927
35678
|
gitDiff,
|
|
35928
|
-
proposalStatuses,
|
|
35929
35679
|
projects: projects2,
|
|
35930
35680
|
projectDocs,
|
|
35931
|
-
proposals: proposals2,
|
|
35932
35681
|
repos: repos2,
|
|
35933
35682
|
sessions: sessions2,
|
|
35934
35683
|
settings,
|
|
@@ -36413,13 +36162,6 @@ var fileParamsSchema = exports_external.object({
|
|
|
36413
36162
|
var fileQuerySchema = exports_external.object({
|
|
36414
36163
|
project_id: exports_external.string()
|
|
36415
36164
|
});
|
|
36416
|
-
var proposalParamsSchema = exports_external.object({
|
|
36417
|
-
proposal_id: exports_external.string()
|
|
36418
|
-
});
|
|
36419
|
-
var proposalFileParamsSchema = exports_external.object({
|
|
36420
|
-
proposal_id: exports_external.string(),
|
|
36421
|
-
file_id: exports_external.string()
|
|
36422
|
-
});
|
|
36423
36165
|
var attachFileSchema = exports_external.object({
|
|
36424
36166
|
file_id: exports_external.string()
|
|
36425
36167
|
});
|
|
@@ -36427,51 +36169,6 @@ var updateFileSchema = exports_external.object({
|
|
|
36427
36169
|
content: exports_external.string()
|
|
36428
36170
|
});
|
|
36429
36171
|
|
|
36430
|
-
// ../schub-api/src/features/files/endpoints/attach-proposal-file.ts
|
|
36431
|
-
var attachProposalFileRoute = (path5) => createRoute({
|
|
36432
|
-
method: "post",
|
|
36433
|
-
path: path5,
|
|
36434
|
-
tags: ["Files"],
|
|
36435
|
-
summary: "Attach file to proposal",
|
|
36436
|
-
request: {
|
|
36437
|
-
params: proposalParamsSchema,
|
|
36438
|
-
body: {
|
|
36439
|
-
content: {
|
|
36440
|
-
"application/json": {
|
|
36441
|
-
schema: attachFileSchema
|
|
36442
|
-
}
|
|
36443
|
-
}
|
|
36444
|
-
}
|
|
36445
|
-
},
|
|
36446
|
-
responses: {
|
|
36447
|
-
201: {
|
|
36448
|
-
description: "File attached",
|
|
36449
|
-
content: {
|
|
36450
|
-
"application/json": {
|
|
36451
|
-
schema: apiSuccessSchema(fileSchema)
|
|
36452
|
-
}
|
|
36453
|
-
}
|
|
36454
|
-
},
|
|
36455
|
-
404: {
|
|
36456
|
-
description: "Not found",
|
|
36457
|
-
content: {
|
|
36458
|
-
"application/json": {
|
|
36459
|
-
schema: apiErrorSchema
|
|
36460
|
-
}
|
|
36461
|
-
}
|
|
36462
|
-
}
|
|
36463
|
-
}
|
|
36464
|
-
});
|
|
36465
|
-
var attachProposalFile = (c) => {
|
|
36466
|
-
const { proposal_id } = c.req.valid("param");
|
|
36467
|
-
const payload = c.req.valid("json");
|
|
36468
|
-
const attached = c.get("services").files.attachToProposal(proposal_id, payload.file_id);
|
|
36469
|
-
if (!attached) {
|
|
36470
|
-
return c.json(errorResponse("File not found"), 404);
|
|
36471
|
-
}
|
|
36472
|
-
return c.json(okResponse(attached), 201);
|
|
36473
|
-
};
|
|
36474
|
-
|
|
36475
36172
|
// ../schub-api/src/features/files/endpoints/delete-file.ts
|
|
36476
36173
|
var deleteFileRoute = (path5) => createRoute({
|
|
36477
36174
|
method: "delete",
|
|
@@ -36504,38 +36201,6 @@ var deleteFile = (c) => {
|
|
|
36504
36201
|
return c.body(null, 204);
|
|
36505
36202
|
};
|
|
36506
36203
|
|
|
36507
|
-
// ../schub-api/src/features/files/endpoints/detach-proposal-file.ts
|
|
36508
|
-
var detachProposalFileRoute = (path5) => createRoute({
|
|
36509
|
-
method: "delete",
|
|
36510
|
-
path: path5,
|
|
36511
|
-
tags: ["Files"],
|
|
36512
|
-
summary: "Detach file from proposal",
|
|
36513
|
-
request: {
|
|
36514
|
-
params: proposalFileParamsSchema
|
|
36515
|
-
},
|
|
36516
|
-
responses: {
|
|
36517
|
-
204: {
|
|
36518
|
-
description: "File detached"
|
|
36519
|
-
},
|
|
36520
|
-
404: {
|
|
36521
|
-
description: "Not found",
|
|
36522
|
-
content: {
|
|
36523
|
-
"application/json": {
|
|
36524
|
-
schema: apiErrorSchema
|
|
36525
|
-
}
|
|
36526
|
-
}
|
|
36527
|
-
}
|
|
36528
|
-
}
|
|
36529
|
-
});
|
|
36530
|
-
var detachProposalFile = (c) => {
|
|
36531
|
-
const { proposal_id, file_id } = c.req.valid("param");
|
|
36532
|
-
const removed = c.get("services").files.detachFromProposal(proposal_id, file_id);
|
|
36533
|
-
if (!removed) {
|
|
36534
|
-
return c.json(errorResponse("File attachment not found"), 404);
|
|
36535
|
-
}
|
|
36536
|
-
return c.body(null, 204);
|
|
36537
|
-
};
|
|
36538
|
-
|
|
36539
36204
|
// ../schub-api/src/features/files/endpoints/get-file.ts
|
|
36540
36205
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
36541
36206
|
var getFileRoute = (path5) => createRoute({
|
|
@@ -36602,32 +36267,6 @@ var listFiles = (c) => {
|
|
|
36602
36267
|
return c.json(okResponse(files2), 200);
|
|
36603
36268
|
};
|
|
36604
36269
|
|
|
36605
|
-
// ../schub-api/src/features/files/endpoints/list-proposal-files.ts
|
|
36606
|
-
var listProposalFilesRoute = (path5) => createRoute({
|
|
36607
|
-
method: "get",
|
|
36608
|
-
path: path5,
|
|
36609
|
-
tags: ["Files"],
|
|
36610
|
-
summary: "List proposal files",
|
|
36611
|
-
request: {
|
|
36612
|
-
params: proposalParamsSchema
|
|
36613
|
-
},
|
|
36614
|
-
responses: {
|
|
36615
|
-
200: {
|
|
36616
|
-
description: "Proposal files list",
|
|
36617
|
-
content: {
|
|
36618
|
-
"application/json": {
|
|
36619
|
-
schema: apiSuccessSchema(exports_external.array(fileSchema))
|
|
36620
|
-
}
|
|
36621
|
-
}
|
|
36622
|
-
}
|
|
36623
|
-
}
|
|
36624
|
-
});
|
|
36625
|
-
var listProposalFiles = (c) => {
|
|
36626
|
-
const { proposal_id } = c.req.valid("param");
|
|
36627
|
-
const files2 = c.get("services").files.listForProposal(proposal_id);
|
|
36628
|
-
return c.json(okResponse(files2), 200);
|
|
36629
|
-
};
|
|
36630
|
-
|
|
36631
36270
|
// ../schub-api/src/features/files/endpoints/update-file.ts
|
|
36632
36271
|
var updateFileRoute = (path5) => createRoute({
|
|
36633
36272
|
method: "put",
|
|
@@ -36747,16 +36386,6 @@ var createFileRoutes = () => {
|
|
|
36747
36386
|
routes.openapi(deleteRoute, deleteFile);
|
|
36748
36387
|
return routes;
|
|
36749
36388
|
};
|
|
36750
|
-
var createProposalFileRoutes = () => {
|
|
36751
|
-
const routes = new OpenAPIHono;
|
|
36752
|
-
const listForProposalRoute = listProposalFilesRoute("/");
|
|
36753
|
-
routes.openapi(listForProposalRoute, listProposalFiles);
|
|
36754
|
-
const attachRoute = attachProposalFileRoute("/");
|
|
36755
|
-
routes.openapi(attachRoute, attachProposalFile);
|
|
36756
|
-
const detachRoute = detachProposalFileRoute("/:file_id");
|
|
36757
|
-
routes.openapi(detachRoute, detachProposalFile);
|
|
36758
|
-
return routes;
|
|
36759
|
-
};
|
|
36760
36389
|
|
|
36761
36390
|
// ../schub-api/src/features/filesystem/helpers/filesystem.ts
|
|
36762
36391
|
import { readdir, stat } from "node:fs/promises";
|
|
@@ -36989,18 +36618,23 @@ var createImageRoutes = () => {
|
|
|
36989
36618
|
return routes;
|
|
36990
36619
|
};
|
|
36991
36620
|
|
|
36621
|
+
// ../schub-api/src/features/project-template-assets/endpoints/list-project-template-assets.ts
|
|
36622
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
36623
|
+
|
|
36992
36624
|
// ../schub-api/src/features/project-template-assets/helpers/template-assets.ts
|
|
36993
36625
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
36994
36626
|
import { dirname as dirname4, join as join6, resolve as resolve3 } from "node:path";
|
|
36995
36627
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
36996
36628
|
|
|
36997
36629
|
// ../schub-api/src/features/project-template-assets/schemas.ts
|
|
36998
|
-
var PROJECT_TEMPLATE_TYPES = ["template", "skill"];
|
|
36630
|
+
var PROJECT_TEMPLATE_TYPES = ["artifact", "ticket-template", "skill"];
|
|
36999
36631
|
var projectTemplateAssetSchema = exports_external.object({
|
|
37000
36632
|
id: exports_external.string(),
|
|
37001
36633
|
project_id: exports_external.string().nullable(),
|
|
37002
36634
|
name: exports_external.string(),
|
|
37003
36635
|
template_type: exports_external.enum(PROJECT_TEMPLATE_TYPES),
|
|
36636
|
+
file_id: exports_external.string(),
|
|
36637
|
+
is_default: exports_external.number(),
|
|
37004
36638
|
content: exports_external.string(),
|
|
37005
36639
|
created_at: exports_external.string(),
|
|
37006
36640
|
updated_at: exports_external.string()
|
|
@@ -37018,50 +36652,48 @@ var updateProjectTemplateAssetSchema = exports_external.object({
|
|
|
37018
36652
|
|
|
37019
36653
|
// ../schub-api/src/features/project-template-assets/helpers/template-assets.ts
|
|
37020
36654
|
var DEFAULT_PROJECT_TEMPLATE_ASSETS = [
|
|
36655
|
+
{ name: "Ticket", template_type: "ticket-template", source_path: "templates/ticket-template.md", is_default: 1 },
|
|
36656
|
+
{ name: "Proposal", template_type: "ticket-template", source_path: "templates/proposal-template.md", is_default: 0 },
|
|
36657
|
+
{ name: "ADR", template_type: "artifact", source_path: "templates/adr-template.md", is_default: 0 },
|
|
36658
|
+
{ name: "Cookbook", template_type: "artifact", source_path: "templates/cookbook-template.md", is_default: 0 },
|
|
36659
|
+
{ name: "Review", template_type: "artifact", source_path: "templates/review-me-template.md", is_default: 0 },
|
|
36660
|
+
{ name: "Q&A", template_type: "artifact", source_path: "templates/q&a-template.md", is_default: 0 },
|
|
37021
36661
|
{
|
|
37022
|
-
name: "create-proposal/
|
|
37023
|
-
template_type: "
|
|
37024
|
-
source_path: "
|
|
37025
|
-
|
|
37026
|
-
{
|
|
37027
|
-
name: "create-proposal/cookbook-template.md",
|
|
37028
|
-
template_type: "template",
|
|
37029
|
-
source_path: "templates/create-proposal/cookbook-template.md"
|
|
37030
|
-
},
|
|
37031
|
-
{
|
|
37032
|
-
name: "create-proposal/adr-template.md",
|
|
37033
|
-
template_type: "template",
|
|
37034
|
-
source_path: "templates/create-proposal/adr-template.md"
|
|
36662
|
+
name: "create-proposal/SKILL.md",
|
|
36663
|
+
template_type: "skill",
|
|
36664
|
+
source_path: "skills/create-proposal/SKILL.md",
|
|
36665
|
+
is_default: 0
|
|
37035
36666
|
},
|
|
37036
36667
|
{
|
|
37037
|
-
name: "create-ticket/
|
|
37038
|
-
template_type: "
|
|
37039
|
-
source_path: "
|
|
36668
|
+
name: "create-ticket/SKILL.md",
|
|
36669
|
+
template_type: "skill",
|
|
36670
|
+
source_path: "skills/create-ticket/SKILL.md",
|
|
36671
|
+
is_default: 0
|
|
37040
36672
|
},
|
|
37041
36673
|
{
|
|
37042
|
-
name: "create-
|
|
37043
|
-
template_type: "
|
|
37044
|
-
source_path: "
|
|
36674
|
+
name: "create-sub-tickets/SKILL.md",
|
|
36675
|
+
template_type: "skill",
|
|
36676
|
+
source_path: "skills/create-sub-tickets/SKILL.md",
|
|
36677
|
+
is_default: 0
|
|
37045
36678
|
},
|
|
37046
36679
|
{
|
|
37047
|
-
name: "
|
|
37048
|
-
template_type: "
|
|
37049
|
-
source_path: "
|
|
36680
|
+
name: "implement-ticket/SKILL.md",
|
|
36681
|
+
template_type: "skill",
|
|
36682
|
+
source_path: "skills/implement-ticket/SKILL.md",
|
|
36683
|
+
is_default: 0
|
|
37050
36684
|
},
|
|
37051
36685
|
{
|
|
37052
|
-
name: "
|
|
37053
|
-
template_type: "
|
|
37054
|
-
source_path: "
|
|
36686
|
+
name: "refine-ticket/SKILL.md",
|
|
36687
|
+
template_type: "skill",
|
|
36688
|
+
source_path: "skills/refine-ticket/SKILL.md",
|
|
36689
|
+
is_default: 0
|
|
37055
36690
|
},
|
|
37056
|
-
{ name: "create-proposal/SKILL.md", template_type: "skill", source_path: "skills/create-proposal/SKILL.md" },
|
|
37057
|
-
{ name: "create-ticket/SKILL.md", template_type: "skill", source_path: "skills/create-ticket/SKILL.md" },
|
|
37058
36691
|
{
|
|
37059
|
-
name: "
|
|
36692
|
+
name: "update-documentation/SKILL.md",
|
|
37060
36693
|
template_type: "skill",
|
|
37061
|
-
source_path: "skills/
|
|
37062
|
-
|
|
37063
|
-
|
|
37064
|
-
{ name: "review-proposal/SKILL.md", template_type: "skill", source_path: "skills/review-proposal/SKILL.md" }
|
|
36694
|
+
source_path: "skills/update-documentation/SKILL.md",
|
|
36695
|
+
is_default: 0
|
|
36696
|
+
}
|
|
37065
36697
|
];
|
|
37066
36698
|
var templateTypeOrder = new Map(PROJECT_TEMPLATE_TYPES.map((type, index) => [type, index]));
|
|
37067
36699
|
var isProjectTemplateType = (value) => PROJECT_TEMPLATE_TYPES.includes(value);
|
|
@@ -37079,15 +36711,16 @@ var loadBundledTemplateAssets = () => {
|
|
|
37079
36711
|
if (!schubRoot)
|
|
37080
36712
|
return [];
|
|
37081
36713
|
return DEFAULT_PROJECT_TEMPLATE_ASSETS.flatMap((asset) => {
|
|
37082
|
-
const
|
|
37083
|
-
if (!existsSync3(
|
|
36714
|
+
const sourcePath = join6(schubRoot, asset.source_path);
|
|
36715
|
+
if (!existsSync3(sourcePath)) {
|
|
37084
36716
|
return [];
|
|
37085
36717
|
}
|
|
37086
36718
|
return [
|
|
37087
36719
|
{
|
|
37088
36720
|
name: asset.name,
|
|
37089
36721
|
template_type: asset.template_type,
|
|
37090
|
-
|
|
36722
|
+
is_default: asset.is_default,
|
|
36723
|
+
data: Buffer.from(readFileSync4(sourcePath, "utf8"))
|
|
37091
36724
|
}
|
|
37092
36725
|
];
|
|
37093
36726
|
});
|
|
@@ -37109,11 +36742,18 @@ var seedMissingAssets = (context, projectId) => {
|
|
|
37109
36742
|
const key = `${asset.template_type}:${asset.name}`;
|
|
37110
36743
|
if (existingKeys.has(key))
|
|
37111
36744
|
return;
|
|
36745
|
+
const file2 = services.files.upload({
|
|
36746
|
+
project_id: projectId,
|
|
36747
|
+
file_name: asset.name,
|
|
36748
|
+
file_kind: "template",
|
|
36749
|
+
data: asset.data
|
|
36750
|
+
});
|
|
37112
36751
|
services.templates.create({
|
|
37113
36752
|
project_id: projectId,
|
|
37114
36753
|
name: asset.name,
|
|
37115
36754
|
template_type: asset.template_type,
|
|
37116
|
-
|
|
36755
|
+
file_id: file2.id,
|
|
36756
|
+
is_default: asset.is_default
|
|
37117
36757
|
});
|
|
37118
36758
|
});
|
|
37119
36759
|
};
|
|
@@ -37154,11 +36794,16 @@ var listProjectTemplateAssets = (c) => {
|
|
|
37154
36794
|
return c.json(errorResponse("Project not found"), 404);
|
|
37155
36795
|
}
|
|
37156
36796
|
seedMissingAssets(c, project_id);
|
|
37157
|
-
const
|
|
36797
|
+
const templates2 = sortAssets(services.templates.list().filter((asset) => asset.project_id === project_id && isProjectTemplateType(asset.template_type)));
|
|
36798
|
+
const assets = templates2.map((asset) => ({
|
|
36799
|
+
...asset,
|
|
36800
|
+
content: readFileSync5(asset.storage_path, "utf8")
|
|
36801
|
+
}));
|
|
37158
36802
|
return c.json(okResponse(assets), 200);
|
|
37159
36803
|
};
|
|
37160
36804
|
|
|
37161
36805
|
// ../schub-api/src/features/project-template-assets/endpoints/update-project-template-asset.ts
|
|
36806
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
37162
36807
|
var updateProjectTemplateAssetRoute = (path5) => createRoute({
|
|
37163
36808
|
method: "put",
|
|
37164
36809
|
path: path5,
|
|
@@ -37205,16 +36850,12 @@ var updateProjectTemplateAsset = (c) => {
|
|
|
37205
36850
|
if (!existing || existing.project_id !== project_id || !isProjectTemplateType(existing.template_type)) {
|
|
37206
36851
|
return c.json(errorResponse("Template asset not found"), 404);
|
|
37207
36852
|
}
|
|
37208
|
-
|
|
37209
|
-
|
|
37210
|
-
name: existing.name,
|
|
37211
|
-
template_type: existing.template_type,
|
|
37212
|
-
content
|
|
37213
|
-
});
|
|
36853
|
+
services.files.update(existing.file_id, { data: Buffer.from(content) });
|
|
36854
|
+
const updated = services.templates.get(asset_id);
|
|
37214
36855
|
if (!updated) {
|
|
37215
36856
|
return c.json(errorResponse("Template asset not found"), 404);
|
|
37216
36857
|
}
|
|
37217
|
-
return c.json(okResponse(updated), 200);
|
|
36858
|
+
return c.json(okResponse({ ...updated, content: readFileSync6(updated.storage_path, "utf8") }), 200);
|
|
37218
36859
|
};
|
|
37219
36860
|
|
|
37220
36861
|
// ../schub-api/src/features/project-template-assets/routes.ts
|
|
@@ -37572,642 +37213,43 @@ var streamProjects = (upgradeWebSocket) => upgradeWebSocket((c) => {
|
|
|
37572
37213
|
onOpen: (_event, ws) => {
|
|
37573
37214
|
const snapshot = services.projects.getSnapshotPatch();
|
|
37574
37215
|
ws.send(JSON.stringify({ JsonPatch: snapshot }));
|
|
37575
|
-
ws.send(JSON.stringify({ Ready: true }));
|
|
37576
|
-
unsubscribe = services.projects.subscribe((patches) => {
|
|
37577
|
-
ws.send(JSON.stringify({ JsonPatch: patches }));
|
|
37578
|
-
});
|
|
37579
|
-
},
|
|
37580
|
-
onMessage: () => {},
|
|
37581
|
-
onClose: () => {
|
|
37582
|
-
if (unsubscribe) {
|
|
37583
|
-
unsubscribe();
|
|
37584
|
-
unsubscribe = null;
|
|
37585
|
-
}
|
|
37586
|
-
}
|
|
37587
|
-
};
|
|
37588
|
-
});
|
|
37589
|
-
|
|
37590
|
-
// ../schub-api/src/features/projects/endpoints/update-project.ts
|
|
37591
|
-
var updateProjectRoute = (path5) => createRoute({
|
|
37592
|
-
method: "put",
|
|
37593
|
-
path: path5,
|
|
37594
|
-
tags: ["Projects"],
|
|
37595
|
-
summary: "Update project",
|
|
37596
|
-
request: {
|
|
37597
|
-
params: projectIdParamsSchema2,
|
|
37598
|
-
body: {
|
|
37599
|
-
content: {
|
|
37600
|
-
"application/json": {
|
|
37601
|
-
schema: updateProjectSchema
|
|
37602
|
-
}
|
|
37603
|
-
}
|
|
37604
|
-
}
|
|
37605
|
-
},
|
|
37606
|
-
responses: {
|
|
37607
|
-
200: {
|
|
37608
|
-
description: "Project updated",
|
|
37609
|
-
content: {
|
|
37610
|
-
"application/json": {
|
|
37611
|
-
schema: apiSuccessSchema(projectSchema)
|
|
37612
|
-
}
|
|
37613
|
-
}
|
|
37614
|
-
},
|
|
37615
|
-
404: {
|
|
37616
|
-
description: "Not found",
|
|
37617
|
-
content: {
|
|
37618
|
-
"application/json": {
|
|
37619
|
-
schema: apiErrorSchema
|
|
37620
|
-
}
|
|
37621
|
-
}
|
|
37622
|
-
}
|
|
37623
|
-
}
|
|
37624
|
-
});
|
|
37625
|
-
var updateProject = (c) => {
|
|
37626
|
-
const { id } = c.req.valid("param");
|
|
37627
|
-
const payload = c.req.valid("json");
|
|
37628
|
-
const project = c.get("services").projects.update(id, payload);
|
|
37629
|
-
if (!project) {
|
|
37630
|
-
return c.json(errorResponse("Project not found"), 404);
|
|
37631
|
-
}
|
|
37632
|
-
return c.json(okResponse(project), 200);
|
|
37633
|
-
};
|
|
37634
|
-
|
|
37635
|
-
// ../schub-api/src/features/projects/routes.ts
|
|
37636
|
-
var createProjectRoutes = (upgradeWebSocket) => {
|
|
37637
|
-
const routes = new OpenAPIHono;
|
|
37638
|
-
const listRoute = listProjectsRoute("/");
|
|
37639
|
-
routes.openapi(listRoute, listProjects);
|
|
37640
|
-
const createRoute2 = createProjectRoute("/");
|
|
37641
|
-
routes.openapi(createRoute2, createProject);
|
|
37642
|
-
const getRoute = getProjectRoute("/:id");
|
|
37643
|
-
routes.openapi(getRoute, getProject);
|
|
37644
|
-
const updateRoute = updateProjectRoute("/:id");
|
|
37645
|
-
routes.openapi(updateRoute, updateProject);
|
|
37646
|
-
const deleteRoute = deleteProjectRoute("/:id");
|
|
37647
|
-
routes.openapi(deleteRoute, deleteProject);
|
|
37648
|
-
const listReposRoute = listProjectRepositoriesRoute("/:id/repositories");
|
|
37649
|
-
routes.openapi(listReposRoute, listProjectRepositories);
|
|
37650
|
-
const addRepoRoute = addProjectRepositoryRoute("/:id/repositories");
|
|
37651
|
-
routes.openapi(addRepoRoute, addProjectRepository);
|
|
37652
|
-
const getRepoRoute = getProjectRepositoryRoute("/:project_id/repositories/:repo_id");
|
|
37653
|
-
routes.openapi(getRepoRoute, getProjectRepository);
|
|
37654
|
-
const deleteRepoRoute = deleteProjectRepositoryRoute("/:project_id/repositories/:repo_id");
|
|
37655
|
-
routes.openapi(deleteRepoRoute, deleteProjectRepository);
|
|
37656
|
-
const streamRoute = streamProjectsRoute("/stream/ws");
|
|
37657
|
-
routes.openapi(streamRoute, streamProjects(upgradeWebSocket));
|
|
37658
|
-
const searchRoute = searchProjectRoute("/:id/search");
|
|
37659
|
-
routes.openapi(searchRoute, searchProject);
|
|
37660
|
-
const openEditorRoute = openProjectEditorRoute("/:id/open-editor");
|
|
37661
|
-
routes.openapi(openEditorRoute, openProjectEditor);
|
|
37662
|
-
return routes;
|
|
37663
|
-
};
|
|
37664
|
-
|
|
37665
|
-
// ../schub-api/src/features/tickets/helpers/ticket-responses.ts
|
|
37666
|
-
import { readFileSync as readFileSync5 } from "node:fs";
|
|
37667
|
-
var readTicketContent = (services, ticketId) => {
|
|
37668
|
-
const files2 = services.files.listForTicket(ticketId);
|
|
37669
|
-
const contentFile = files2.find((f) => f.file_kind === "ticket");
|
|
37670
|
-
if (!contentFile)
|
|
37671
|
-
return null;
|
|
37672
|
-
try {
|
|
37673
|
-
return readFileSync5(contentFile.storage_path, "utf8");
|
|
37674
|
-
} catch {
|
|
37675
|
-
return null;
|
|
37676
|
-
}
|
|
37677
|
-
};
|
|
37678
|
-
var uploadTicketContent = (services, projectId, ticketId, content) => {
|
|
37679
|
-
const existingFiles = services.files.listForTicket(ticketId);
|
|
37680
|
-
const contentFile = existingFiles.find((f) => f.file_kind === "ticket");
|
|
37681
|
-
const contentBuffer = Buffer.from(content, "utf8");
|
|
37682
|
-
if (contentFile) {
|
|
37683
|
-
services.files.update(contentFile.id, { data: contentBuffer });
|
|
37684
|
-
} else {
|
|
37685
|
-
const file2 = services.files.upload({
|
|
37686
|
-
project_id: projectId,
|
|
37687
|
-
file_name: "ticket.md",
|
|
37688
|
-
file_kind: "ticket",
|
|
37689
|
-
data: contentBuffer
|
|
37690
|
-
});
|
|
37691
|
-
services.files.attachToTicket(ticketId, file2.id);
|
|
37692
|
-
}
|
|
37693
|
-
};
|
|
37694
|
-
var buildAttemptPrompt = (ticketShorthand, ticketContent, fallback) => {
|
|
37695
|
-
const content = ticketContent?.trim();
|
|
37696
|
-
const body = content && content.length > 0 ? content : fallback;
|
|
37697
|
-
return `Implement ticket ${ticketShorthand}:
|
|
37698
|
-
${body}`;
|
|
37699
|
-
};
|
|
37700
|
-
var buildTicketAttempts = (services, ticketId) => {
|
|
37701
|
-
const workspaces2 = services.workspaces.list(ticketId);
|
|
37702
|
-
return workspaces2.flatMap((workspace) => {
|
|
37703
|
-
const sessions2 = services.sessions.list(workspace.id);
|
|
37704
|
-
const session = sessions2[sessions2.length - 1];
|
|
37705
|
-
if (!session) {
|
|
37706
|
-
return [];
|
|
37707
|
-
}
|
|
37708
|
-
return [
|
|
37709
|
-
{
|
|
37710
|
-
id: workspace.id,
|
|
37711
|
-
label: workspace.name,
|
|
37712
|
-
shorthand: workspace.workspace_shorthand,
|
|
37713
|
-
session_id: session.id,
|
|
37714
|
-
updated_at: session.updated_at
|
|
37715
|
-
}
|
|
37716
|
-
];
|
|
37717
|
-
});
|
|
37718
|
-
};
|
|
37719
|
-
var buildTicketResponse = (services, ticket) => {
|
|
37720
|
-
if (!ticket) {
|
|
37721
|
-
return null;
|
|
37722
|
-
}
|
|
37723
|
-
const attempts = buildTicketAttempts(services, ticket.id);
|
|
37724
|
-
const title = ticket.title || extractTitle(null, ticket.id);
|
|
37725
|
-
const content = readTicketContent(services, ticket.id);
|
|
37726
|
-
const tag_ids = services.ticketTags.listIdsForTicket(ticket.id);
|
|
37727
|
-
const children = services.tickets.listChildren(ticket.id);
|
|
37728
|
-
const sub_tickets = children.map((child) => ({
|
|
37729
|
-
id: child.id,
|
|
37730
|
-
shorthand: child.shorthand,
|
|
37731
|
-
title: child.title || child.shorthand,
|
|
37732
|
-
status_id: child.status_id
|
|
37733
|
-
}));
|
|
37734
|
-
const artifact_count = services.workspaceArtifacts.list(ticket.id).length;
|
|
37735
|
-
return { ...ticket, title, content, attempts, tag_ids, sub_tickets, artifact_count };
|
|
37736
|
-
};
|
|
37737
|
-
var buildTicketResponseOrFallback = (services, ticket) => {
|
|
37738
|
-
return buildTicketResponse(services, ticket) ?? {
|
|
37739
|
-
...ticket,
|
|
37740
|
-
title: ticket.title || ticket.id,
|
|
37741
|
-
content: null,
|
|
37742
|
-
attempts: [],
|
|
37743
|
-
tag_ids: []
|
|
37744
|
-
};
|
|
37745
|
-
};
|
|
37746
|
-
var buildTicketListResponse = (services, tickets2) => {
|
|
37747
|
-
const tagIdsByTicket = services.ticketTags.listIdsForTickets(tickets2.map((ticket) => ticket.id));
|
|
37748
|
-
return tickets2.map((ticket) => {
|
|
37749
|
-
const attempts = buildTicketAttempts(services, ticket.id);
|
|
37750
|
-
const title = ticket.title || ticket.id;
|
|
37751
|
-
const content = readTicketContent(services, ticket.id);
|
|
37752
|
-
const tag_ids = tagIdsByTicket.get(ticket.id) ?? [];
|
|
37753
|
-
return { ...ticket, title, content, attempts, tag_ids };
|
|
37754
|
-
});
|
|
37755
|
-
};
|
|
37756
|
-
|
|
37757
|
-
// ../schub-api/src/features/proposals/schemas.ts
|
|
37758
|
-
var proposalSchema = exports_external.object({
|
|
37759
|
-
id: exports_external.string(),
|
|
37760
|
-
shorthand: exports_external.string(),
|
|
37761
|
-
display_title: exports_external.string().optional(),
|
|
37762
|
-
project_id: exports_external.string(),
|
|
37763
|
-
repo_id: exports_external.string().nullable(),
|
|
37764
|
-
input: exports_external.string().nullable(),
|
|
37765
|
-
status: exports_external.string(),
|
|
37766
|
-
archived: exports_external.number().int(),
|
|
37767
|
-
created_at: exports_external.string(),
|
|
37768
|
-
updated_at: exports_external.string()
|
|
37769
|
-
});
|
|
37770
|
-
var ticketSchema = exports_external.object({
|
|
37771
|
-
id: exports_external.string(),
|
|
37772
|
-
project_id: exports_external.string(),
|
|
37773
|
-
proposal_id: exports_external.string().nullable(),
|
|
37774
|
-
status_id: exports_external.string().nullable(),
|
|
37775
|
-
depends_on: exports_external.string().nullable(),
|
|
37776
|
-
input: exports_external.string().nullable(),
|
|
37777
|
-
priority: exports_external.string().nullable(),
|
|
37778
|
-
parallelizable: exports_external.string().nullable(),
|
|
37779
|
-
title: exports_external.string(),
|
|
37780
|
-
content: exports_external.string().nullable(),
|
|
37781
|
-
complexity: exports_external.enum(["low", "medium", "high"]).nullable().optional(),
|
|
37782
|
-
archived: exports_external.number().int(),
|
|
37783
|
-
created_at: exports_external.string(),
|
|
37784
|
-
updated_at: exports_external.string()
|
|
37785
|
-
});
|
|
37786
|
-
var proposalProjectParamsSchema = exports_external.object({
|
|
37787
|
-
project_id: exports_external.string()
|
|
37788
|
-
});
|
|
37789
|
-
var proposalParamsSchema2 = exports_external.object({
|
|
37790
|
-
project_id: exports_external.string(),
|
|
37791
|
-
proposal_id: exports_external.string()
|
|
37792
|
-
});
|
|
37793
|
-
var proposalTicketParamsSchema = exports_external.object({
|
|
37794
|
-
project_id: exports_external.string(),
|
|
37795
|
-
proposal_id: exports_external.string(),
|
|
37796
|
-
ticket_id: exports_external.string()
|
|
37797
|
-
});
|
|
37798
|
-
var createProposalSchema = exports_external.object({
|
|
37799
|
-
repo_id: exports_external.string().nullable().optional(),
|
|
37800
|
-
input: exports_external.string().nullable().optional()
|
|
37801
|
-
});
|
|
37802
|
-
var updateProposalSchema = exports_external.object({
|
|
37803
|
-
repo_id: exports_external.string().nullable().optional(),
|
|
37804
|
-
input: exports_external.string().nullable().optional(),
|
|
37805
|
-
status: exports_external.string().optional(),
|
|
37806
|
-
archived: exports_external.number().int().min(0).max(1).optional()
|
|
37807
|
-
});
|
|
37808
|
-
var createProposalTicketSchema = exports_external.object({
|
|
37809
|
-
content: exports_external.string().nullable().optional(),
|
|
37810
|
-
input: exports_external.string().nullable().optional(),
|
|
37811
|
-
priority: exports_external.string().nullable().optional(),
|
|
37812
|
-
parallelizable: exports_external.string().nullable().optional(),
|
|
37813
|
-
complexity: exports_external.enum(["low", "medium", "high"]).nullable().optional(),
|
|
37814
|
-
status_id: exports_external.string().nullable().optional(),
|
|
37815
|
-
depends_on: exports_external.string().nullable().optional()
|
|
37816
|
-
});
|
|
37817
|
-
var updateProposalTicketSchema = createProposalTicketSchema;
|
|
37818
|
-
|
|
37819
|
-
// ../schub-api/src/features/proposals/endpoints/convert-proposal-to-ticket.ts
|
|
37820
|
-
var convertProposalToTicketRoute = (path5) => createRoute({
|
|
37821
|
-
method: "post",
|
|
37822
|
-
path: path5,
|
|
37823
|
-
tags: ["Proposals"],
|
|
37824
|
-
summary: "Convert proposal to ticket",
|
|
37825
|
-
request: {
|
|
37826
|
-
params: proposalParamsSchema2
|
|
37827
|
-
},
|
|
37828
|
-
responses: {
|
|
37829
|
-
201: {
|
|
37830
|
-
description: "Proposal converted to ticket",
|
|
37831
|
-
content: {
|
|
37832
|
-
"application/json": {
|
|
37833
|
-
schema: apiSuccessSchema(ticketSchema)
|
|
37834
|
-
}
|
|
37835
|
-
}
|
|
37836
|
-
},
|
|
37837
|
-
404: {
|
|
37838
|
-
description: "Not found",
|
|
37839
|
-
content: {
|
|
37840
|
-
"application/json": {
|
|
37841
|
-
schema: apiErrorSchema
|
|
37842
|
-
}
|
|
37843
|
-
}
|
|
37844
|
-
},
|
|
37845
|
-
409: {
|
|
37846
|
-
description: "Proposal already has tickets",
|
|
37847
|
-
content: {
|
|
37848
|
-
"application/json": {
|
|
37849
|
-
schema: apiErrorSchema
|
|
37850
|
-
}
|
|
37851
|
-
}
|
|
37852
|
-
}
|
|
37853
|
-
}
|
|
37854
|
-
});
|
|
37855
|
-
var convertProposalToTicket = (c) => {
|
|
37856
|
-
const { project_id, proposal_id } = c.req.valid("param");
|
|
37857
|
-
const services = c.get("services");
|
|
37858
|
-
const proposal = services.proposals.get(project_id, proposal_id);
|
|
37859
|
-
if (!proposal) {
|
|
37860
|
-
return c.json(errorResponse("Proposal not found"), 404);
|
|
37861
|
-
}
|
|
37862
|
-
const existingTickets = services.tickets.listByProposal(project_id, proposal_id);
|
|
37863
|
-
if (existingTickets.length > 0) {
|
|
37864
|
-
return c.json(errorResponse("Proposal already has tickets"), 409);
|
|
37865
|
-
}
|
|
37866
|
-
const statuses = services.ticketStatuses.list(project_id);
|
|
37867
|
-
const defaultStatus2 = statuses.find((status) => status.is_default === 1) ?? statuses[0] ?? null;
|
|
37868
|
-
const rawContent = `# ${proposal.shorthand}`;
|
|
37869
|
-
const title = extractTitle(rawContent, proposal.shorthand);
|
|
37870
|
-
const ticket = services.tickets.create({
|
|
37871
|
-
project_id,
|
|
37872
|
-
title,
|
|
37873
|
-
status_id: defaultStatus2?.id ?? null,
|
|
37874
|
-
proposal_id: null
|
|
37875
|
-
});
|
|
37876
|
-
uploadTicketContent(services, project_id, ticket.id, rawContent);
|
|
37877
|
-
services.proposals.remove(project_id, proposal_id);
|
|
37878
|
-
return c.json(okResponse({ ...ticket, title, content: rawContent }), 201);
|
|
37879
|
-
};
|
|
37880
|
-
|
|
37881
|
-
// ../schub-api/src/features/proposals/endpoints/create-proposal.ts
|
|
37882
|
-
var createProposalRoute = (path5) => createRoute({
|
|
37883
|
-
method: "post",
|
|
37884
|
-
path: path5,
|
|
37885
|
-
tags: ["Proposals"],
|
|
37886
|
-
summary: "Create proposal",
|
|
37887
|
-
request: {
|
|
37888
|
-
params: proposalProjectParamsSchema,
|
|
37889
|
-
body: {
|
|
37890
|
-
content: {
|
|
37891
|
-
"application/json": {
|
|
37892
|
-
schema: createProposalSchema
|
|
37893
|
-
}
|
|
37894
|
-
}
|
|
37895
|
-
}
|
|
37896
|
-
},
|
|
37897
|
-
responses: {
|
|
37898
|
-
201: {
|
|
37899
|
-
description: "Proposal created",
|
|
37900
|
-
content: {
|
|
37901
|
-
"application/json": {
|
|
37902
|
-
schema: apiSuccessSchema(proposalSchema)
|
|
37903
|
-
}
|
|
37904
|
-
}
|
|
37905
|
-
}
|
|
37906
|
-
}
|
|
37907
|
-
});
|
|
37908
|
-
var createProposal = (c) => {
|
|
37909
|
-
const { project_id } = c.req.valid("param");
|
|
37910
|
-
const payload = c.req.valid("json");
|
|
37911
|
-
const proposal = c.get("services").proposals.create(project_id, payload);
|
|
37912
|
-
return c.json(okResponse(proposal), 201);
|
|
37913
|
-
};
|
|
37914
|
-
|
|
37915
|
-
// ../schub-api/src/features/proposals/endpoints/create-proposal-ticket.ts
|
|
37916
|
-
var createProposalTicketRoute = (path5) => createRoute({
|
|
37917
|
-
method: "post",
|
|
37918
|
-
path: path5,
|
|
37919
|
-
tags: ["Proposals"],
|
|
37920
|
-
summary: "Create proposal ticket",
|
|
37921
|
-
request: {
|
|
37922
|
-
params: proposalParamsSchema2,
|
|
37923
|
-
body: {
|
|
37924
|
-
content: {
|
|
37925
|
-
"application/json": {
|
|
37926
|
-
schema: createProposalTicketSchema
|
|
37927
|
-
}
|
|
37928
|
-
}
|
|
37929
|
-
}
|
|
37930
|
-
},
|
|
37931
|
-
responses: {
|
|
37932
|
-
201: {
|
|
37933
|
-
description: "Proposal ticket created",
|
|
37934
|
-
content: {
|
|
37935
|
-
"application/json": {
|
|
37936
|
-
schema: apiSuccessSchema(ticketSchema)
|
|
37937
|
-
}
|
|
37938
|
-
}
|
|
37939
|
-
}
|
|
37940
|
-
}
|
|
37941
|
-
});
|
|
37942
|
-
var createProposalTicket = (c) => {
|
|
37943
|
-
const { project_id, proposal_id } = c.req.valid("param");
|
|
37944
|
-
const payload = c.req.valid("json");
|
|
37945
|
-
const services = c.get("services");
|
|
37946
|
-
const title = extractTitle(payload.content, "untitled");
|
|
37947
|
-
const { content: rawContent, ...ticketPayload } = payload;
|
|
37948
|
-
const ticket = services.tickets.createForProposal(project_id, proposal_id, { ...ticketPayload, title });
|
|
37949
|
-
if (rawContent) {
|
|
37950
|
-
uploadTicketContent(services, project_id, ticket.id, rawContent);
|
|
37951
|
-
}
|
|
37952
|
-
return c.json(okResponse({ ...ticket, title, content: rawContent ?? null }), 201);
|
|
37953
|
-
};
|
|
37954
|
-
|
|
37955
|
-
// ../schub-api/src/features/proposals/endpoints/delete-proposal.ts
|
|
37956
|
-
var deleteProposalRoute = (path5) => createRoute({
|
|
37957
|
-
method: "delete",
|
|
37958
|
-
path: path5,
|
|
37959
|
-
tags: ["Proposals"],
|
|
37960
|
-
summary: "Delete proposal",
|
|
37961
|
-
request: {
|
|
37962
|
-
params: proposalParamsSchema2
|
|
37963
|
-
},
|
|
37964
|
-
responses: {
|
|
37965
|
-
204: {
|
|
37966
|
-
description: "Proposal removed"
|
|
37967
|
-
},
|
|
37968
|
-
404: {
|
|
37969
|
-
description: "Not found",
|
|
37970
|
-
content: {
|
|
37971
|
-
"application/json": {
|
|
37972
|
-
schema: apiErrorSchema
|
|
37973
|
-
}
|
|
37974
|
-
}
|
|
37975
|
-
}
|
|
37976
|
-
}
|
|
37977
|
-
});
|
|
37978
|
-
var deleteProposal = (c) => {
|
|
37979
|
-
const { project_id, proposal_id } = c.req.valid("param");
|
|
37980
|
-
const removed = c.get("services").proposals.remove(project_id, proposal_id);
|
|
37981
|
-
if (!removed) {
|
|
37982
|
-
return c.json(errorResponse("Proposal not found"), 404);
|
|
37983
|
-
}
|
|
37984
|
-
return c.body(null, 204);
|
|
37985
|
-
};
|
|
37986
|
-
|
|
37987
|
-
// ../schub-api/src/features/proposals/endpoints/delete-proposal-ticket.ts
|
|
37988
|
-
var deleteProposalTicketRoute = (path5) => createRoute({
|
|
37989
|
-
method: "delete",
|
|
37990
|
-
path: path5,
|
|
37991
|
-
tags: ["Proposals"],
|
|
37992
|
-
summary: "Delete proposal ticket",
|
|
37993
|
-
request: {
|
|
37994
|
-
params: proposalTicketParamsSchema
|
|
37995
|
-
},
|
|
37996
|
-
responses: {
|
|
37997
|
-
204: {
|
|
37998
|
-
description: "Proposal ticket removed"
|
|
37999
|
-
},
|
|
38000
|
-
404: {
|
|
38001
|
-
description: "Not found",
|
|
38002
|
-
content: {
|
|
38003
|
-
"application/json": {
|
|
38004
|
-
schema: apiErrorSchema
|
|
38005
|
-
}
|
|
38006
|
-
}
|
|
38007
|
-
}
|
|
38008
|
-
}
|
|
38009
|
-
});
|
|
38010
|
-
var deleteProposalTicket = (c) => {
|
|
38011
|
-
const { project_id, proposal_id, ticket_id } = c.req.valid("param");
|
|
38012
|
-
const removed = c.get("services").tickets.removeForProposal(project_id, proposal_id, ticket_id);
|
|
38013
|
-
if (!removed) {
|
|
38014
|
-
return c.json(errorResponse("Ticket not found"), 404);
|
|
38015
|
-
}
|
|
38016
|
-
return c.body(null, 204);
|
|
38017
|
-
};
|
|
38018
|
-
|
|
38019
|
-
// ../schub-api/src/features/proposals/endpoints/get-proposal.ts
|
|
38020
|
-
var getProposalRoute = (path5) => createRoute({
|
|
38021
|
-
method: "get",
|
|
38022
|
-
path: path5,
|
|
38023
|
-
tags: ["Proposals"],
|
|
38024
|
-
summary: "Get proposal",
|
|
38025
|
-
request: {
|
|
38026
|
-
params: proposalParamsSchema2
|
|
38027
|
-
},
|
|
38028
|
-
responses: {
|
|
38029
|
-
200: {
|
|
38030
|
-
description: "Proposal",
|
|
38031
|
-
content: {
|
|
38032
|
-
"application/json": {
|
|
38033
|
-
schema: apiSuccessSchema(proposalSchema)
|
|
38034
|
-
}
|
|
38035
|
-
}
|
|
38036
|
-
},
|
|
38037
|
-
404: {
|
|
38038
|
-
description: "Not found",
|
|
38039
|
-
content: {
|
|
38040
|
-
"application/json": {
|
|
38041
|
-
schema: apiErrorSchema
|
|
38042
|
-
}
|
|
38043
|
-
}
|
|
38044
|
-
}
|
|
38045
|
-
}
|
|
38046
|
-
});
|
|
38047
|
-
var getProposal = (c) => {
|
|
38048
|
-
const { project_id, proposal_id } = c.req.valid("param");
|
|
38049
|
-
const proposal = c.get("services").proposals.get(project_id, proposal_id);
|
|
38050
|
-
if (!proposal) {
|
|
38051
|
-
return c.json(errorResponse("Proposal not found"), 404);
|
|
38052
|
-
}
|
|
38053
|
-
return c.json(okResponse(proposal), 200);
|
|
38054
|
-
};
|
|
38055
|
-
|
|
38056
|
-
// ../schub-api/src/features/proposals/endpoints/list-proposal-tickets.ts
|
|
38057
|
-
var listProposalTicketsRoute = (path5) => createRoute({
|
|
38058
|
-
method: "get",
|
|
38059
|
-
path: path5,
|
|
38060
|
-
tags: ["Proposals"],
|
|
38061
|
-
summary: "List proposal tickets",
|
|
38062
|
-
request: {
|
|
38063
|
-
params: proposalParamsSchema2
|
|
38064
|
-
},
|
|
38065
|
-
responses: {
|
|
38066
|
-
200: {
|
|
38067
|
-
description: "Proposal tickets list",
|
|
38068
|
-
content: {
|
|
38069
|
-
"application/json": {
|
|
38070
|
-
schema: apiSuccessSchema(ticketSchema.array())
|
|
38071
|
-
}
|
|
38072
|
-
}
|
|
38073
|
-
}
|
|
38074
|
-
}
|
|
38075
|
-
});
|
|
38076
|
-
var listProposalTickets = (c) => {
|
|
38077
|
-
const { project_id, proposal_id } = c.req.valid("param");
|
|
38078
|
-
const tickets2 = c.get("services").tickets.listByProposal(project_id, proposal_id).filter((t) => t.staged !== 1);
|
|
38079
|
-
const services = c.get("services");
|
|
38080
|
-
const ticketsWithTitle = tickets2.map((ticket) => ({
|
|
38081
|
-
...ticket,
|
|
38082
|
-
title: ticket.title || ticket.id,
|
|
38083
|
-
content: readTicketContent(services, ticket.id)
|
|
38084
|
-
}));
|
|
38085
|
-
return c.json(okResponse(ticketsWithTitle), 200);
|
|
38086
|
-
};
|
|
38087
|
-
|
|
38088
|
-
// ../schub-api/src/features/proposals/endpoints/list-proposals.ts
|
|
38089
|
-
import { readFileSync as readFileSync6 } from "node:fs";
|
|
38090
|
-
var extractTitleFromMarkdown = (content) => {
|
|
38091
|
-
const headerMatch = content.match(/^#+\s+(.*)$/m);
|
|
38092
|
-
if (headerMatch?.[1]) {
|
|
38093
|
-
return headerMatch[1].trim();
|
|
38094
|
-
}
|
|
38095
|
-
const body = content.replace(/^---[\s\S]*?---/, "").trim();
|
|
38096
|
-
const firstLine = body.split(`
|
|
38097
|
-
`)[0]?.trim() ?? "";
|
|
38098
|
-
if (firstLine.length > 0) {
|
|
38099
|
-
return firstLine.length > 60 ? `${firstLine.slice(0, 57)}...` : firstLine;
|
|
38100
|
-
}
|
|
38101
|
-
return "";
|
|
38102
|
-
};
|
|
38103
|
-
var resolveProposalDisplayTitle = (c, proposalId, shorthand) => {
|
|
38104
|
-
const proposalFile = c.get("services").files.listForProposal(proposalId).find((file2) => file2.file_name.trim().toLowerCase() === "proposal.md");
|
|
38105
|
-
if (!proposalFile) {
|
|
38106
|
-
return shorthand;
|
|
38107
|
-
}
|
|
38108
|
-
try {
|
|
38109
|
-
const content = readFileSync6(proposalFile.storage_path, "utf8");
|
|
38110
|
-
const title = extractTitleFromMarkdown(content);
|
|
38111
|
-
return title || shorthand;
|
|
38112
|
-
} catch {
|
|
38113
|
-
return shorthand;
|
|
38114
|
-
}
|
|
38115
|
-
};
|
|
38116
|
-
var listProposalsRoute = (path5) => createRoute({
|
|
38117
|
-
method: "get",
|
|
38118
|
-
path: path5,
|
|
38119
|
-
tags: ["Proposals"],
|
|
38120
|
-
summary: "List proposals",
|
|
38121
|
-
request: {
|
|
38122
|
-
params: proposalProjectParamsSchema
|
|
38123
|
-
},
|
|
38124
|
-
responses: {
|
|
38125
|
-
200: {
|
|
38126
|
-
description: "Proposals list",
|
|
38127
|
-
content: {
|
|
38128
|
-
"application/json": {
|
|
38129
|
-
schema: apiSuccessSchema(proposalSchema.array())
|
|
38130
|
-
}
|
|
38131
|
-
}
|
|
38132
|
-
}
|
|
38133
|
-
}
|
|
38134
|
-
});
|
|
38135
|
-
var listProposals = (c) => {
|
|
38136
|
-
const { project_id } = c.req.valid("param");
|
|
38137
|
-
const proposals2 = c.get("services").proposals.list(project_id).filter((p) => p.staged !== 1 && p.archived !== 1);
|
|
38138
|
-
return c.json(okResponse(proposals2.map((proposal) => ({
|
|
38139
|
-
...proposal,
|
|
38140
|
-
display_title: resolveProposalDisplayTitle(c, proposal.id, proposal.shorthand)
|
|
38141
|
-
}))), 200);
|
|
38142
|
-
};
|
|
38143
|
-
|
|
38144
|
-
// ../schub-api/src/features/proposals/endpoints/update-proposal.ts
|
|
38145
|
-
var updateProposalRoute = (path5) => createRoute({
|
|
38146
|
-
method: "put",
|
|
38147
|
-
path: path5,
|
|
38148
|
-
tags: ["Proposals"],
|
|
38149
|
-
summary: "Update proposal",
|
|
38150
|
-
request: {
|
|
38151
|
-
params: proposalParamsSchema2,
|
|
38152
|
-
body: {
|
|
38153
|
-
content: {
|
|
38154
|
-
"application/json": {
|
|
38155
|
-
schema: updateProposalSchema
|
|
38156
|
-
}
|
|
38157
|
-
}
|
|
38158
|
-
}
|
|
38159
|
-
},
|
|
38160
|
-
responses: {
|
|
38161
|
-
200: {
|
|
38162
|
-
description: "Proposal updated",
|
|
38163
|
-
content: {
|
|
38164
|
-
"application/json": {
|
|
38165
|
-
schema: apiSuccessSchema(proposalSchema)
|
|
38166
|
-
}
|
|
38167
|
-
}
|
|
37216
|
+
ws.send(JSON.stringify({ Ready: true }));
|
|
37217
|
+
unsubscribe = services.projects.subscribe((patches) => {
|
|
37218
|
+
ws.send(JSON.stringify({ JsonPatch: patches }));
|
|
37219
|
+
});
|
|
38168
37220
|
},
|
|
38169
|
-
|
|
38170
|
-
|
|
38171
|
-
|
|
38172
|
-
|
|
38173
|
-
|
|
38174
|
-
}
|
|
37221
|
+
onMessage: () => {},
|
|
37222
|
+
onClose: () => {
|
|
37223
|
+
if (unsubscribe) {
|
|
37224
|
+
unsubscribe();
|
|
37225
|
+
unsubscribe = null;
|
|
38175
37226
|
}
|
|
38176
37227
|
}
|
|
38177
|
-
}
|
|
37228
|
+
};
|
|
38178
37229
|
});
|
|
38179
|
-
var updateProposal = (c) => {
|
|
38180
|
-
const { project_id, proposal_id } = c.req.valid("param");
|
|
38181
|
-
const payload = c.req.valid("json");
|
|
38182
|
-
const proposal = c.get("services").proposals.update(project_id, proposal_id, payload);
|
|
38183
|
-
if (!proposal) {
|
|
38184
|
-
return c.json(errorResponse("Proposal not found"), 404);
|
|
38185
|
-
}
|
|
38186
|
-
return c.json(okResponse(proposal), 200);
|
|
38187
|
-
};
|
|
38188
37230
|
|
|
38189
|
-
// ../schub-api/src/features/
|
|
38190
|
-
var
|
|
37231
|
+
// ../schub-api/src/features/projects/endpoints/update-project.ts
|
|
37232
|
+
var updateProjectRoute = (path5) => createRoute({
|
|
38191
37233
|
method: "put",
|
|
38192
37234
|
path: path5,
|
|
38193
|
-
tags: ["
|
|
38194
|
-
summary: "Update
|
|
37235
|
+
tags: ["Projects"],
|
|
37236
|
+
summary: "Update project",
|
|
38195
37237
|
request: {
|
|
38196
|
-
params:
|
|
37238
|
+
params: projectIdParamsSchema2,
|
|
38197
37239
|
body: {
|
|
38198
37240
|
content: {
|
|
38199
37241
|
"application/json": {
|
|
38200
|
-
schema:
|
|
37242
|
+
schema: updateProjectSchema
|
|
38201
37243
|
}
|
|
38202
37244
|
}
|
|
38203
37245
|
}
|
|
38204
37246
|
},
|
|
38205
37247
|
responses: {
|
|
38206
37248
|
200: {
|
|
38207
|
-
description: "
|
|
37249
|
+
description: "Project updated",
|
|
38208
37250
|
content: {
|
|
38209
37251
|
"application/json": {
|
|
38210
|
-
schema: apiSuccessSchema(
|
|
37252
|
+
schema: apiSuccessSchema(projectSchema)
|
|
38211
37253
|
}
|
|
38212
37254
|
}
|
|
38213
37255
|
},
|
|
@@ -38221,50 +37263,43 @@ var updateProposalTicketRoute = (path5) => createRoute({
|
|
|
38221
37263
|
}
|
|
38222
37264
|
}
|
|
38223
37265
|
});
|
|
38224
|
-
var
|
|
38225
|
-
const {
|
|
37266
|
+
var updateProject = (c) => {
|
|
37267
|
+
const { id } = c.req.valid("param");
|
|
38226
37268
|
const payload = c.req.valid("json");
|
|
38227
|
-
const
|
|
38228
|
-
|
|
38229
|
-
|
|
38230
|
-
const ticket = services.tickets.updateForProposal(project_id, proposal_id, ticket_id, {
|
|
38231
|
-
...ticketPayload,
|
|
38232
|
-
...titleUpdate
|
|
38233
|
-
});
|
|
38234
|
-
if (!ticket) {
|
|
38235
|
-
return c.json(errorResponse("Ticket not found"), 404);
|
|
38236
|
-
}
|
|
38237
|
-
if (rawContent) {
|
|
38238
|
-
uploadTicketContent(services, project_id, ticket.id, rawContent);
|
|
37269
|
+
const project = c.get("services").projects.update(id, payload);
|
|
37270
|
+
if (!project) {
|
|
37271
|
+
return c.json(errorResponse("Project not found"), 404);
|
|
38239
37272
|
}
|
|
38240
|
-
|
|
38241
|
-
const content = rawContent ?? readTicketContent(services, ticket.id);
|
|
38242
|
-
return c.json(okResponse({ ...ticket, title, content }), 200);
|
|
37273
|
+
return c.json(okResponse(project), 200);
|
|
38243
37274
|
};
|
|
38244
37275
|
|
|
38245
|
-
// ../schub-api/src/features/
|
|
38246
|
-
var
|
|
37276
|
+
// ../schub-api/src/features/projects/routes.ts
|
|
37277
|
+
var createProjectRoutes = (upgradeWebSocket) => {
|
|
38247
37278
|
const routes = new OpenAPIHono;
|
|
38248
|
-
const listRoute =
|
|
38249
|
-
routes.openapi(listRoute,
|
|
38250
|
-
const createRoute2 =
|
|
38251
|
-
routes.openapi(createRoute2,
|
|
38252
|
-
const getRoute =
|
|
38253
|
-
routes.openapi(getRoute,
|
|
38254
|
-
const updateRoute =
|
|
38255
|
-
routes.openapi(updateRoute,
|
|
38256
|
-
const deleteRoute =
|
|
38257
|
-
routes.openapi(deleteRoute,
|
|
38258
|
-
const
|
|
38259
|
-
routes.openapi(
|
|
38260
|
-
const
|
|
38261
|
-
routes.openapi(
|
|
38262
|
-
const
|
|
38263
|
-
routes.openapi(
|
|
38264
|
-
const
|
|
38265
|
-
routes.openapi(
|
|
38266
|
-
const
|
|
38267
|
-
routes.openapi(
|
|
37279
|
+
const listRoute = listProjectsRoute("/");
|
|
37280
|
+
routes.openapi(listRoute, listProjects);
|
|
37281
|
+
const createRoute2 = createProjectRoute("/");
|
|
37282
|
+
routes.openapi(createRoute2, createProject);
|
|
37283
|
+
const getRoute = getProjectRoute("/:id");
|
|
37284
|
+
routes.openapi(getRoute, getProject);
|
|
37285
|
+
const updateRoute = updateProjectRoute("/:id");
|
|
37286
|
+
routes.openapi(updateRoute, updateProject);
|
|
37287
|
+
const deleteRoute = deleteProjectRoute("/:id");
|
|
37288
|
+
routes.openapi(deleteRoute, deleteProject);
|
|
37289
|
+
const listReposRoute = listProjectRepositoriesRoute("/:id/repositories");
|
|
37290
|
+
routes.openapi(listReposRoute, listProjectRepositories);
|
|
37291
|
+
const addRepoRoute = addProjectRepositoryRoute("/:id/repositories");
|
|
37292
|
+
routes.openapi(addRepoRoute, addProjectRepository);
|
|
37293
|
+
const getRepoRoute = getProjectRepositoryRoute("/:project_id/repositories/:repo_id");
|
|
37294
|
+
routes.openapi(getRepoRoute, getProjectRepository);
|
|
37295
|
+
const deleteRepoRoute = deleteProjectRepositoryRoute("/:project_id/repositories/:repo_id");
|
|
37296
|
+
routes.openapi(deleteRepoRoute, deleteProjectRepository);
|
|
37297
|
+
const streamRoute = streamProjectsRoute("/stream/ws");
|
|
37298
|
+
routes.openapi(streamRoute, streamProjects(upgradeWebSocket));
|
|
37299
|
+
const searchRoute = searchProjectRoute("/:id/search");
|
|
37300
|
+
routes.openapi(searchRoute, searchProject);
|
|
37301
|
+
const openEditorRoute = openProjectEditorRoute("/:id/open-editor");
|
|
37302
|
+
routes.openapi(openEditorRoute, openProjectEditor);
|
|
38268
37303
|
return routes;
|
|
38269
37304
|
};
|
|
38270
37305
|
|
|
@@ -39699,7 +38734,7 @@ var listProfilesRoute = (path5) => createPlaceholderRoute("System", { method: "g
|
|
|
39699
38734
|
var listProfiles = createPlaceholderHandler();
|
|
39700
38735
|
|
|
39701
38736
|
// ../schub-api/src/features/system/helpers/opencode-skills.ts
|
|
39702
|
-
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
38737
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "node:fs";
|
|
39703
38738
|
import { homedir as homedir5 } from "node:os";
|
|
39704
38739
|
import { join as join9 } from "node:path";
|
|
39705
38740
|
var skillPathSuffix = "/SKILL.md";
|
|
@@ -39733,10 +38768,11 @@ var installOpencodeSkill = (homeDirectory, skill) => {
|
|
|
39733
38768
|
if (!skillName) {
|
|
39734
38769
|
return null;
|
|
39735
38770
|
}
|
|
38771
|
+
const content = readFileSync7(skill.storage_path, "utf8");
|
|
39736
38772
|
const skillDirectory = join9(homeDirectory, ".opencode", "skills", skillName);
|
|
39737
38773
|
const skillPath = join9(skillDirectory, "SKILL.md");
|
|
39738
38774
|
mkdirSync3(skillDirectory, { recursive: true });
|
|
39739
|
-
writeFileSync3(skillPath,
|
|
38775
|
+
writeFileSync3(skillPath, content, "utf8");
|
|
39740
38776
|
return {
|
|
39741
38777
|
name: skillName,
|
|
39742
38778
|
path: skillPath
|
|
@@ -39874,15 +38910,24 @@ var createEditorRoutes = () => {
|
|
|
39874
38910
|
return routes;
|
|
39875
38911
|
};
|
|
39876
38912
|
|
|
38913
|
+
// ../schub-api/src/features/templates/endpoints/create-template.ts
|
|
38914
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
38915
|
+
|
|
39877
38916
|
// ../schub-api/src/features/templates/schemas.ts
|
|
39878
38917
|
var templateSchema = exports_external.object({
|
|
39879
38918
|
id: exports_external.string(),
|
|
39880
38919
|
project_id: exports_external.string().nullable(),
|
|
39881
38920
|
name: exports_external.string(),
|
|
39882
38921
|
template_type: exports_external.string(),
|
|
38922
|
+
file_id: exports_external.string(),
|
|
38923
|
+
is_default: exports_external.number(),
|
|
39883
38924
|
content: exports_external.string(),
|
|
39884
38925
|
created_at: exports_external.string(),
|
|
39885
|
-
updated_at: exports_external.string()
|
|
38926
|
+
updated_at: exports_external.string(),
|
|
38927
|
+
file_name: exports_external.string(),
|
|
38928
|
+
storage_path: exports_external.string(),
|
|
38929
|
+
mime_type: exports_external.string().nullable(),
|
|
38930
|
+
size_bytes: exports_external.number()
|
|
39886
38931
|
});
|
|
39887
38932
|
var templateParamsSchema = exports_external.object({
|
|
39888
38933
|
template_id: exports_external.string()
|
|
@@ -39891,7 +38936,8 @@ var createTemplateSchema = exports_external.object({
|
|
|
39891
38936
|
project_id: exports_external.string().nullable().optional(),
|
|
39892
38937
|
name: exports_external.string().min(1),
|
|
39893
38938
|
template_type: exports_external.string().min(1),
|
|
39894
|
-
|
|
38939
|
+
file_id: exports_external.string().min(1),
|
|
38940
|
+
is_default: exports_external.number().optional()
|
|
39895
38941
|
});
|
|
39896
38942
|
var updateTemplateSchema = createTemplateSchema;
|
|
39897
38943
|
|
|
@@ -39918,13 +38964,30 @@ var createTemplateRoute = (path5) => createRoute({
|
|
|
39918
38964
|
schema: apiSuccessSchema(templateSchema)
|
|
39919
38965
|
}
|
|
39920
38966
|
}
|
|
38967
|
+
},
|
|
38968
|
+
409: {
|
|
38969
|
+
description: "Duplicate name",
|
|
38970
|
+
content: {
|
|
38971
|
+
"application/json": {
|
|
38972
|
+
schema: apiErrorSchema
|
|
38973
|
+
}
|
|
38974
|
+
}
|
|
39921
38975
|
}
|
|
39922
38976
|
}
|
|
39923
38977
|
});
|
|
39924
38978
|
var createTemplate = (c) => {
|
|
39925
38979
|
const payload = c.req.valid("json");
|
|
39926
|
-
|
|
39927
|
-
|
|
38980
|
+
try {
|
|
38981
|
+
const services = c.get("services");
|
|
38982
|
+
const created = services.templates.create(payload);
|
|
38983
|
+
const template = services.templates.get(created.id);
|
|
38984
|
+
return c.json(okResponse({ ...template, content: readFileSync8(template.storage_path, "utf8") }), 201);
|
|
38985
|
+
} catch (error48) {
|
|
38986
|
+
if (error48 instanceof Error && error48.message.includes("Duplicate template name")) {
|
|
38987
|
+
return c.json(errorResponse(error48.message), 409);
|
|
38988
|
+
}
|
|
38989
|
+
throw error48;
|
|
38990
|
+
}
|
|
39928
38991
|
};
|
|
39929
38992
|
|
|
39930
38993
|
// ../schub-api/src/features/templates/endpoints/delete-template.ts
|
|
@@ -39960,6 +39023,7 @@ var deleteTemplate = (c) => {
|
|
|
39960
39023
|
};
|
|
39961
39024
|
|
|
39962
39025
|
// ../schub-api/src/features/templates/endpoints/get-template.ts
|
|
39026
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
39963
39027
|
var getTemplateRoute = (path5) => createRoute({
|
|
39964
39028
|
method: "get",
|
|
39965
39029
|
path: path5,
|
|
@@ -39993,10 +39057,11 @@ var getTemplate = (c) => {
|
|
|
39993
39057
|
if (!template) {
|
|
39994
39058
|
return c.json(errorResponse("Template not found"), 404);
|
|
39995
39059
|
}
|
|
39996
|
-
return c.json(okResponse(template), 200);
|
|
39060
|
+
return c.json(okResponse({ ...template, content: readFileSync9(template.storage_path, "utf8") }), 200);
|
|
39997
39061
|
};
|
|
39998
39062
|
|
|
39999
39063
|
// ../schub-api/src/features/templates/endpoints/list-templates.ts
|
|
39064
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
40000
39065
|
var listTemplatesRoute = (path5) => createRoute({
|
|
40001
39066
|
method: "get",
|
|
40002
39067
|
path: path5,
|
|
@@ -40015,10 +39080,15 @@ var listTemplatesRoute = (path5) => createRoute({
|
|
|
40015
39080
|
});
|
|
40016
39081
|
var listTemplates = (c) => {
|
|
40017
39082
|
const templates2 = c.get("services").templates.list();
|
|
40018
|
-
|
|
39083
|
+
const withContent = templates2.map((t) => ({
|
|
39084
|
+
...t,
|
|
39085
|
+
content: readFileSync10(t.storage_path, "utf8")
|
|
39086
|
+
}));
|
|
39087
|
+
return c.json(okResponse(withContent), 200);
|
|
40019
39088
|
};
|
|
40020
39089
|
|
|
40021
39090
|
// ../schub-api/src/features/templates/endpoints/update-template.ts
|
|
39091
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
40022
39092
|
var updateTemplateRoute = (path5) => createRoute({
|
|
40023
39093
|
method: "put",
|
|
40024
39094
|
path: path5,
|
|
@@ -40050,17 +39120,34 @@ var updateTemplateRoute = (path5) => createRoute({
|
|
|
40050
39120
|
schema: apiErrorSchema
|
|
40051
39121
|
}
|
|
40052
39122
|
}
|
|
39123
|
+
},
|
|
39124
|
+
409: {
|
|
39125
|
+
description: "Duplicate name",
|
|
39126
|
+
content: {
|
|
39127
|
+
"application/json": {
|
|
39128
|
+
schema: apiErrorSchema
|
|
39129
|
+
}
|
|
39130
|
+
}
|
|
40053
39131
|
}
|
|
40054
39132
|
}
|
|
40055
39133
|
});
|
|
40056
39134
|
var updateTemplate = (c) => {
|
|
40057
39135
|
const { template_id } = c.req.valid("param");
|
|
40058
39136
|
const payload = c.req.valid("json");
|
|
40059
|
-
|
|
40060
|
-
|
|
40061
|
-
|
|
39137
|
+
try {
|
|
39138
|
+
const services = c.get("services");
|
|
39139
|
+
const updated = services.templates.update(template_id, payload);
|
|
39140
|
+
if (!updated) {
|
|
39141
|
+
return c.json(errorResponse("Template not found"), 404);
|
|
39142
|
+
}
|
|
39143
|
+
const template = services.templates.get(template_id);
|
|
39144
|
+
return c.json(okResponse({ ...template, content: readFileSync11(template.storage_path, "utf8") }), 200);
|
|
39145
|
+
} catch (error48) {
|
|
39146
|
+
if (error48 instanceof Error && error48.message.includes("Duplicate template name")) {
|
|
39147
|
+
return c.json(errorResponse(error48.message), 409);
|
|
39148
|
+
}
|
|
39149
|
+
throw error48;
|
|
40062
39150
|
}
|
|
40063
|
-
return c.json(okResponse(template), 200);
|
|
40064
39151
|
};
|
|
40065
39152
|
|
|
40066
39153
|
// ../schub-api/src/features/templates/routes.ts
|
|
@@ -40484,6 +39571,99 @@ var createTicketTagRoutes = () => {
|
|
|
40484
39571
|
return routes;
|
|
40485
39572
|
};
|
|
40486
39573
|
|
|
39574
|
+
// ../schub-api/src/features/tickets/helpers/ticket-responses.ts
|
|
39575
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
39576
|
+
var readTicketContent = (services, ticketId) => {
|
|
39577
|
+
const files2 = services.files.listForTicket(ticketId);
|
|
39578
|
+
const contentFile = files2.find((f) => f.file_kind === "ticket");
|
|
39579
|
+
if (!contentFile)
|
|
39580
|
+
return null;
|
|
39581
|
+
try {
|
|
39582
|
+
return readFileSync12(contentFile.storage_path, "utf8");
|
|
39583
|
+
} catch {
|
|
39584
|
+
return null;
|
|
39585
|
+
}
|
|
39586
|
+
};
|
|
39587
|
+
var uploadTicketContent = (services, projectId, ticketId, content) => {
|
|
39588
|
+
const existingFiles = services.files.listForTicket(ticketId);
|
|
39589
|
+
const contentFile = existingFiles.find((f) => f.file_kind === "ticket");
|
|
39590
|
+
const contentBuffer = Buffer.from(content, "utf8");
|
|
39591
|
+
if (contentFile) {
|
|
39592
|
+
services.files.update(contentFile.id, { data: contentBuffer });
|
|
39593
|
+
} else {
|
|
39594
|
+
const file2 = services.files.upload({
|
|
39595
|
+
project_id: projectId,
|
|
39596
|
+
file_name: "ticket.md",
|
|
39597
|
+
file_kind: "ticket",
|
|
39598
|
+
data: contentBuffer
|
|
39599
|
+
});
|
|
39600
|
+
services.files.attachToTicket(ticketId, file2.id);
|
|
39601
|
+
}
|
|
39602
|
+
};
|
|
39603
|
+
var buildAttemptPrompt = (ticketShorthand, ticketContent, fallback) => {
|
|
39604
|
+
const content = ticketContent?.trim();
|
|
39605
|
+
const body = content && content.length > 0 ? content : fallback;
|
|
39606
|
+
return `Implement ticket ${ticketShorthand}:
|
|
39607
|
+
${body}`;
|
|
39608
|
+
};
|
|
39609
|
+
var buildTicketAttempts = (services, ticketId) => {
|
|
39610
|
+
const workspaces2 = services.workspaces.list(ticketId);
|
|
39611
|
+
return workspaces2.flatMap((workspace) => {
|
|
39612
|
+
const sessions2 = services.sessions.list(workspace.id);
|
|
39613
|
+
const session = sessions2[sessions2.length - 1];
|
|
39614
|
+
if (!session) {
|
|
39615
|
+
return [];
|
|
39616
|
+
}
|
|
39617
|
+
return [
|
|
39618
|
+
{
|
|
39619
|
+
id: workspace.id,
|
|
39620
|
+
label: workspace.name,
|
|
39621
|
+
shorthand: workspace.workspace_shorthand,
|
|
39622
|
+
session_id: session.id,
|
|
39623
|
+
updated_at: session.updated_at,
|
|
39624
|
+
worktree_path: workspace.worktree_path ?? null
|
|
39625
|
+
}
|
|
39626
|
+
];
|
|
39627
|
+
});
|
|
39628
|
+
};
|
|
39629
|
+
var buildTicketResponse = (services, ticket) => {
|
|
39630
|
+
if (!ticket) {
|
|
39631
|
+
return null;
|
|
39632
|
+
}
|
|
39633
|
+
const attempts = buildTicketAttempts(services, ticket.id);
|
|
39634
|
+
const title = ticket.title || extractTitle(null, ticket.id);
|
|
39635
|
+
const content = readTicketContent(services, ticket.id);
|
|
39636
|
+
const tag_ids = services.ticketTags.listIdsForTicket(ticket.id);
|
|
39637
|
+
const children = services.tickets.listChildren(ticket.id);
|
|
39638
|
+
const sub_tickets = children.map((child) => ({
|
|
39639
|
+
id: child.id,
|
|
39640
|
+
shorthand: child.shorthand,
|
|
39641
|
+
title: child.title || child.shorthand,
|
|
39642
|
+
status_id: child.status_id
|
|
39643
|
+
}));
|
|
39644
|
+
const artifact_count = services.workspaceArtifacts.list(ticket.id).length;
|
|
39645
|
+
return { ...ticket, title, content, attempts, tag_ids, sub_tickets, artifact_count };
|
|
39646
|
+
};
|
|
39647
|
+
var buildTicketResponseOrFallback = (services, ticket) => {
|
|
39648
|
+
return buildTicketResponse(services, ticket) ?? {
|
|
39649
|
+
...ticket,
|
|
39650
|
+
title: ticket.title || ticket.id,
|
|
39651
|
+
content: null,
|
|
39652
|
+
attempts: [],
|
|
39653
|
+
tag_ids: []
|
|
39654
|
+
};
|
|
39655
|
+
};
|
|
39656
|
+
var buildTicketListResponse = (services, tickets2) => {
|
|
39657
|
+
const tagIdsByTicket = services.ticketTags.listIdsForTickets(tickets2.map((ticket) => ticket.id));
|
|
39658
|
+
return tickets2.map((ticket) => {
|
|
39659
|
+
const attempts = buildTicketAttempts(services, ticket.id);
|
|
39660
|
+
const title = ticket.title || ticket.id;
|
|
39661
|
+
const content = readTicketContent(services, ticket.id);
|
|
39662
|
+
const tag_ids = tagIdsByTicket.get(ticket.id) ?? [];
|
|
39663
|
+
return { ...ticket, title, content, attempts, tag_ids };
|
|
39664
|
+
});
|
|
39665
|
+
};
|
|
39666
|
+
|
|
40487
39667
|
// ../schub-api/src/features/tickets/schemas.ts
|
|
40488
39668
|
var ticketQuerySchema = exports_external.object({
|
|
40489
39669
|
project_id: exports_external.string(),
|
|
@@ -41686,8 +40866,130 @@ var markTicketAttemptSeenRoute = (path5) => createPlaceholderRoute("Workspaces",
|
|
|
41686
40866
|
var markTicketAttemptSeen = createPlaceholderHandler();
|
|
41687
40867
|
|
|
41688
40868
|
// ../schub-api/src/features/workspaces/endpoints/merge-ticket-attempt.ts
|
|
41689
|
-
|
|
41690
|
-
var
|
|
40869
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
40870
|
+
var mergeResultSchema = exports_external.discriminatedUnion("status", [
|
|
40871
|
+
exports_external.object({
|
|
40872
|
+
status: exports_external.literal("merged"),
|
|
40873
|
+
filesChanged: exports_external.number(),
|
|
40874
|
+
commitMessage: exports_external.string()
|
|
40875
|
+
}),
|
|
40876
|
+
exports_external.object({
|
|
40877
|
+
status: exports_external.literal("conflict"),
|
|
40878
|
+
conflictFiles: exports_external.array(exports_external.string())
|
|
40879
|
+
})
|
|
40880
|
+
]);
|
|
40881
|
+
var mergeTicketAttemptRoute = (path5) => createRoute({
|
|
40882
|
+
method: "post",
|
|
40883
|
+
path: path5,
|
|
40884
|
+
tags: ["Workspaces"],
|
|
40885
|
+
summary: "Merge ticket attempt into the repo's current branch",
|
|
40886
|
+
request: {
|
|
40887
|
+
params: ticketAttemptIdParamsSchema
|
|
40888
|
+
},
|
|
40889
|
+
responses: {
|
|
40890
|
+
200: {
|
|
40891
|
+
description: "Merge completed (merged or conflict)",
|
|
40892
|
+
content: {
|
|
40893
|
+
"application/json": {
|
|
40894
|
+
schema: apiSuccessSchema(mergeResultSchema)
|
|
40895
|
+
}
|
|
40896
|
+
}
|
|
40897
|
+
},
|
|
40898
|
+
400: {
|
|
40899
|
+
description: "Workspace has no worktree or branch",
|
|
40900
|
+
content: { "application/json": { schema: apiErrorSchema } }
|
|
40901
|
+
},
|
|
40902
|
+
404: {
|
|
40903
|
+
description: "Workspace or repo not found",
|
|
40904
|
+
content: { "application/json": { schema: apiErrorSchema } }
|
|
40905
|
+
},
|
|
40906
|
+
500: {
|
|
40907
|
+
description: "Merge failed",
|
|
40908
|
+
content: { "application/json": { schema: apiErrorSchema } }
|
|
40909
|
+
}
|
|
40910
|
+
}
|
|
40911
|
+
});
|
|
40912
|
+
var git = (cwd, args) => spawnSync6("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
40913
|
+
var gitError = (result) => result.stderr?.trim() || result.stdout?.trim() || "unknown error";
|
|
40914
|
+
var autoCommitWorktree2 = (worktreePath, label) => {
|
|
40915
|
+
const status = git(worktreePath, ["status", "--porcelain"]);
|
|
40916
|
+
if (status.status !== 0)
|
|
40917
|
+
return;
|
|
40918
|
+
const changedFiles = status.stdout.split(`
|
|
40919
|
+
`).filter((line) => line.trim().length > 0);
|
|
40920
|
+
if (changedFiles.length === 0)
|
|
40921
|
+
return;
|
|
40922
|
+
const addResult = git(worktreePath, ["add", "-A"]);
|
|
40923
|
+
if (addResult.status !== 0)
|
|
40924
|
+
return;
|
|
40925
|
+
git(worktreePath, ["commit", "-m", `agent: uncommitted work for ${label}`]);
|
|
40926
|
+
};
|
|
40927
|
+
var mergeTicketAttempt = async (c) => {
|
|
40928
|
+
const { id } = c.req.valid("param");
|
|
40929
|
+
const services = c.get("services");
|
|
40930
|
+
const workspace = services.workspaces.get(id);
|
|
40931
|
+
if (!workspace) {
|
|
40932
|
+
return c.json(errorResponse("Workspace not found"), 404);
|
|
40933
|
+
}
|
|
40934
|
+
if (!workspace.worktree_path) {
|
|
40935
|
+
return c.json(errorResponse("Workspace has no worktree"), 400);
|
|
40936
|
+
}
|
|
40937
|
+
if (!workspace.branch) {
|
|
40938
|
+
return c.json(errorResponse("Workspace has no branch"), 400);
|
|
40939
|
+
}
|
|
40940
|
+
if (!workspace.repo_id) {
|
|
40941
|
+
return c.json(errorResponse("Workspace has no associated repository"), 400);
|
|
40942
|
+
}
|
|
40943
|
+
const repo = services.repos.get(workspace.repo_id);
|
|
40944
|
+
if (!repo) {
|
|
40945
|
+
return c.json(errorResponse("Repository not found"), 404);
|
|
40946
|
+
}
|
|
40947
|
+
const gitRootResult = git(repo.path, ["rev-parse", "--show-toplevel"]);
|
|
40948
|
+
if (gitRootResult.status !== 0) {
|
|
40949
|
+
return c.json(errorResponse("Repository is not a git repository"), 400);
|
|
40950
|
+
}
|
|
40951
|
+
const gitRoot = gitRootResult.stdout.trim();
|
|
40952
|
+
const ticket = workspace.ticket_id ? services.tickets.get(workspace.ticket_id) : null;
|
|
40953
|
+
const ticketLabel = ticket?.shorthand ?? "unknown";
|
|
40954
|
+
const attemptLabel = workspace.workspace_shorthand ?? "unknown";
|
|
40955
|
+
try {
|
|
40956
|
+
autoCommitWorktree2(workspace.worktree_path, ticketLabel);
|
|
40957
|
+
const mergeResult = git(gitRoot, ["merge", "--squash", workspace.branch]);
|
|
40958
|
+
if (mergeResult.status !== 0) {
|
|
40959
|
+
const statusResult = git(gitRoot, ["diff", "--name-only", "--diff-filter=U"]);
|
|
40960
|
+
const conflictFiles = statusResult.stdout.split(`
|
|
40961
|
+
`).filter(Boolean);
|
|
40962
|
+
git(gitRoot, ["reset", "--merge"]);
|
|
40963
|
+
if (conflictFiles.length > 0) {
|
|
40964
|
+
return c.json(okResponse({ status: "conflict", conflictFiles }), 200);
|
|
40965
|
+
}
|
|
40966
|
+
throw new Error(`Merge failed: ${gitError(mergeResult)}`);
|
|
40967
|
+
}
|
|
40968
|
+
const diffResult = git(gitRoot, ["diff", "--cached", "--name-only"]);
|
|
40969
|
+
const filesChanged = diffResult.stdout.split(`
|
|
40970
|
+
`).filter(Boolean).length;
|
|
40971
|
+
const commitMessage = `merge: ${ticketLabel}/${attemptLabel}`;
|
|
40972
|
+
const commitResult = git(gitRoot, ["commit", "-m", commitMessage]);
|
|
40973
|
+
if (commitResult.status !== 0) {
|
|
40974
|
+
throw new Error(`Commit failed: ${gitError(commitResult)}`);
|
|
40975
|
+
}
|
|
40976
|
+
if (ticket) {
|
|
40977
|
+
const doneStatus = services.ticketStatuses.list(ticket.project_id).find((s) => s.name.toLowerCase() === "done");
|
|
40978
|
+
if (doneStatus) {
|
|
40979
|
+
services.tickets.save({
|
|
40980
|
+
id: ticket.id,
|
|
40981
|
+
project_id: ticket.project_id,
|
|
40982
|
+
status_id: doneStatus.id,
|
|
40983
|
+
staged: 0
|
|
40984
|
+
});
|
|
40985
|
+
}
|
|
40986
|
+
}
|
|
40987
|
+
return c.json(okResponse({ status: "merged", filesChanged, commitMessage }), 200);
|
|
40988
|
+
} catch (error48) {
|
|
40989
|
+
const message = error48 instanceof Error ? error48.message : "Merge failed";
|
|
40990
|
+
return c.json(errorResponse(message), 500);
|
|
40991
|
+
}
|
|
40992
|
+
};
|
|
41691
40993
|
|
|
41692
40994
|
// ../schub-api/src/features/workspaces/endpoints/open-ticket-attempt-editor.ts
|
|
41693
40995
|
var openTicketAttemptEditorRoute = (path5) => createPlaceholderRoute("Workspaces", { method: "post", path: path5 });
|
|
@@ -41836,12 +41138,10 @@ var createRoutes = (options) => {
|
|
|
41836
41138
|
routes.route("/mcp-config", createMcpConfigRoutes());
|
|
41837
41139
|
routes.route("/profiles", createProfileRoutes());
|
|
41838
41140
|
routes.route("/projects", createProjectRoutes(options.upgradeWebSocket));
|
|
41839
|
-
routes.route("/projects/:project_id/proposals", createProposalRoutes());
|
|
41840
41141
|
routes.route("/projects/:project_id/template-assets", createProjectTemplateAssetRoutes());
|
|
41841
41142
|
routes.route("/projects/:project_id/ticket-statuses", createTicketStatusRoutes());
|
|
41842
41143
|
routes.route("/projects/:project_id/ticket-tags", createTicketTagRoutes());
|
|
41843
41144
|
routes.route("/projects/:projectId/docs", createProjectDocsRoutes());
|
|
41844
|
-
routes.route("/proposals/:proposal_id/files", createProposalFileRoutes());
|
|
41845
41145
|
routes.route("/repos", createRepoRoutes());
|
|
41846
41146
|
routes.route("/sessions", createSessionRoutes(options.upgradeWebSocket));
|
|
41847
41147
|
routes.route("/sessions/:session_id/queue", createSessionQueueRoutes());
|