@sudocode-ai/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +330 -0
- package/dist/cli/feedback-commands.d.ts +53 -0
- package/dist/cli/init-commands.d.ts +22 -0
- package/dist/cli/issue-commands.d.ts +45 -0
- package/dist/cli/query-commands.d.ts +18 -0
- package/dist/cli/reference-commands.d.ts +22 -0
- package/dist/cli/relationship-commands.d.ts +14 -0
- package/dist/cli/server-commands.d.ts +17 -0
- package/dist/cli/spec-commands.d.ts +38 -0
- package/dist/cli/status-commands.d.ts +17 -0
- package/dist/cli/sync-commands.d.ts +24 -0
- package/dist/cli/update-commands.d.ts +12 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +196 -0
- package/dist/db.d.ts +21 -0
- package/dist/export.d.ts +79 -0
- package/dist/filename-generator.d.ts +30 -0
- package/dist/id-generator.d.ts +26 -0
- package/dist/import.d.ts +118 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +189 -0
- package/dist/jsonl.d.ts +69 -0
- package/dist/markdown.d.ts +146 -0
- package/dist/migrations.d.ts +23 -0
- package/dist/operations/events.d.ts +53 -0
- package/dist/operations/feedback-anchors.d.ts +92 -0
- package/dist/operations/feedback.d.ts +76 -0
- package/dist/operations/index.d.ts +10 -0
- package/dist/operations/issues.d.ts +82 -0
- package/dist/operations/references.d.ts +34 -0
- package/dist/operations/relationships.d.ts +57 -0
- package/dist/operations/specs.d.ts +64 -0
- package/dist/operations/tags.d.ts +42 -0
- package/dist/operations/transactions.d.ts +41 -0
- package/dist/sync.d.ts +47 -0
- package/dist/test-schema.d.ts +5 -0
- package/dist/types.d.ts +7 -0
- package/dist/update-checker.d.ts +24 -0
- package/dist/version.d.ts +12 -0
- package/dist/watcher.d.ts +63 -0
- package/package.json +68 -0
package/dist/import.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import JSONL data to SQLite with collision resolution
|
|
3
|
+
*/
|
|
4
|
+
import type Database from "better-sqlite3";
|
|
5
|
+
import type { SpecJSONL, IssueJSONL } from "@sudocode-ai/types";
|
|
6
|
+
export interface ImportOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Input directory for JSONL files
|
|
9
|
+
*/
|
|
10
|
+
inputDir?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Custom file names
|
|
13
|
+
*/
|
|
14
|
+
specsFile?: string;
|
|
15
|
+
issuesFile?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Dry run - detect changes but don't apply
|
|
18
|
+
*/
|
|
19
|
+
dryRun?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Automatically resolve collisions
|
|
22
|
+
*/
|
|
23
|
+
resolveCollisions?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Path to meta.json for collision logging
|
|
26
|
+
*/
|
|
27
|
+
metaPath?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ImportResult {
|
|
30
|
+
specs: {
|
|
31
|
+
added: number;
|
|
32
|
+
updated: number;
|
|
33
|
+
deleted: number;
|
|
34
|
+
};
|
|
35
|
+
issues: {
|
|
36
|
+
added: number;
|
|
37
|
+
updated: number;
|
|
38
|
+
deleted: number;
|
|
39
|
+
};
|
|
40
|
+
collisions: CollisionInfo[];
|
|
41
|
+
}
|
|
42
|
+
export interface CollisionInfo {
|
|
43
|
+
id: string;
|
|
44
|
+
uuid: string;
|
|
45
|
+
type: "spec" | "issue";
|
|
46
|
+
reason: string;
|
|
47
|
+
localContent: string;
|
|
48
|
+
incomingContent: string;
|
|
49
|
+
localCreatedAt?: string;
|
|
50
|
+
incomingCreatedAt: string;
|
|
51
|
+
resolution?: "keep-local" | "use-incoming" | "renumber";
|
|
52
|
+
newId?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface ChangeDetection {
|
|
55
|
+
added: string[];
|
|
56
|
+
updated: string[];
|
|
57
|
+
deleted: string[];
|
|
58
|
+
unchanged: string[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Detect changes between existing and incoming entities
|
|
62
|
+
* Uses UUID as the source of truth for entity identity
|
|
63
|
+
*/
|
|
64
|
+
export declare function detectChanges<T extends {
|
|
65
|
+
id: string;
|
|
66
|
+
uuid: string;
|
|
67
|
+
updated_at: string;
|
|
68
|
+
}>(existing: T[], incoming: T[]): ChangeDetection;
|
|
69
|
+
/**
|
|
70
|
+
* Detect ID collisions (same ID, different UUID)
|
|
71
|
+
* UUIDs are the source of truth for entity identity
|
|
72
|
+
*/
|
|
73
|
+
export declare function detectCollisions<T extends {
|
|
74
|
+
id: string;
|
|
75
|
+
uuid: string;
|
|
76
|
+
title: string;
|
|
77
|
+
created_at: string;
|
|
78
|
+
}>(existing: T[], incoming: T[]): CollisionInfo[];
|
|
79
|
+
/**
|
|
80
|
+
* Count references to an entity ID in content fields
|
|
81
|
+
*/
|
|
82
|
+
export declare function countReferences(db: Database.Database, entityId: string, entityType: "spec" | "issue"): number;
|
|
83
|
+
/**
|
|
84
|
+
* Resolve collisions using timestamp-based deterministic strategy
|
|
85
|
+
*
|
|
86
|
+
* The entity with the NEWER (more recent) created_at timestamp logically
|
|
87
|
+
* should be renumbered, while the OLDER entity keeps the original ID.
|
|
88
|
+
*
|
|
89
|
+
* For practical reasons (entities already in DB can't be easily renamed),
|
|
90
|
+
* incoming entities are always the ones that get new IDs. However, we
|
|
91
|
+
* deterministically decide which UUID gets which new ID based on timestamps.
|
|
92
|
+
*/
|
|
93
|
+
export declare function resolveCollisions(db: Database.Database, collisions: CollisionInfo[]): CollisionInfo[];
|
|
94
|
+
/**
|
|
95
|
+
* Update text references when an ID is renumbered
|
|
96
|
+
*/
|
|
97
|
+
export declare function updateTextReferences(db: Database.Database, oldId: string, newId: string): number;
|
|
98
|
+
/**
|
|
99
|
+
* Import specs from JSONL
|
|
100
|
+
*/
|
|
101
|
+
export declare function importSpecs(db: Database.Database, specs: SpecJSONL[], changes: ChangeDetection, dryRun?: boolean, skipRelationships?: boolean): {
|
|
102
|
+
added: number;
|
|
103
|
+
updated: number;
|
|
104
|
+
deleted: number;
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Import issues from JSONL
|
|
108
|
+
*/
|
|
109
|
+
export declare function importIssues(db: Database.Database, issues: IssueJSONL[], changes: ChangeDetection, dryRun?: boolean, skipRelationships?: boolean): {
|
|
110
|
+
added: number;
|
|
111
|
+
updated: number;
|
|
112
|
+
deleted: number;
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Import both specs and issues from JSONL files
|
|
116
|
+
*/
|
|
117
|
+
export declare function importFromJSONL(db: Database.Database, options?: ImportOptions): Promise<ImportResult>;
|
|
118
|
+
//# sourceMappingURL=import.d.ts.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for sudocode
|
|
3
|
+
*/
|
|
4
|
+
export * from "@sudocode-ai/types";
|
|
5
|
+
export * from "./db.js";
|
|
6
|
+
export * from "./operations/index.js";
|
|
7
|
+
export * from "./operations/transactions.js";
|
|
8
|
+
export * from "./jsonl.js";
|
|
9
|
+
export * from "./export.js";
|
|
10
|
+
export * from "./import.js";
|
|
11
|
+
export * from "./markdown.js";
|
|
12
|
+
export * from "./sync.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
var Ye=Object.defineProperty;var K=(n,e)=>()=>(n&&(e=n(n=0)),e);var ze=(n,e)=>{for(var t in e)Ye(n,t,{get:e[t],enumerable:!0})};import*as _e from"fs";import*as Z from"path";import{fileURLToPath as Xe}from"url";function nt(){let n=Z.join(tt,"..","package.json");return JSON.parse(_e.readFileSync(n,"utf8")).version}var et,tt,he,ye=K(()=>{"use strict";et=Xe(import.meta.url),tt=Z.dirname(et);he=nt()});import*as C from"fs";import*as se from"path";import*as Ee from"crypto";function be(n,e){let t=ie(e),s=st(n);return`${t.id_prefix.spec}-${String(s).padStart(3,"0")}`}function Se(n,e){let t=ie(e),s=it(n);return`${t.id_prefix.issue}-${String(s).padStart(3,"0")}`}function st(n){let t=n.prepare(`
|
|
2
|
+
SELECT id FROM specs
|
|
3
|
+
ORDER BY created_at DESC
|
|
4
|
+
LIMIT 1
|
|
5
|
+
`).get();if(t){let r=t.id.match(/(\d+)$/);if(r)return parseInt(r[1],10)+1}return n.prepare("SELECT COUNT(*) as count FROM specs").get().count+1}function it(n){let t=n.prepare(`
|
|
6
|
+
SELECT id FROM issues
|
|
7
|
+
ORDER BY created_at DESC
|
|
8
|
+
LIMIT 1
|
|
9
|
+
`).get();if(t){let r=t.id.match(/(\d+)$/);if(r)return parseInt(r[1],10)+1}return n.prepare("SELECT COUNT(*) as count FROM issues").get().count+1}function ie(n){let e=se.join(n,"config.json");if(!C.existsSync(e)){let s={version:he,id_prefix:{spec:"SPEC",issue:"ISSUE"}};return rt(n,s),s}let t=C.readFileSync(e,"utf8");return JSON.parse(t)}function rt(n,e){let t=se.join(n,"config.json");C.writeFileSync(t,JSON.stringify(e,null,2),"utf8")}function De(n){return ie(n)}function G(){return Ee.randomUUID()}var B=K(()=>{"use strict";ye()});var re={};ze(re,{addRelationship:()=>I,getAllRelationships:()=>lt,getDependencies:()=>ut,getDependents:()=>pt,getIncomingRelationships:()=>H,getOutgoingRelationships:()=>k,getRelationship:()=>ee,relationshipExists:()=>ft,removeAllRelationships:()=>A,removeRelationship:()=>ot});function I(n,e){let t=e.from_type==="spec"?"specs":"issues",s=n.prepare(`SELECT id, uuid FROM ${t} WHERE id = ?`).get(e.from_id);if(!s)throw new Error(`${e.from_type==="spec"?"Spec":"Issue"} not found: ${e.from_id}`);let i=e.to_type==="spec"?"specs":"issues",r=n.prepare(`SELECT id, uuid FROM ${i} WHERE id = ?`).get(e.to_id);if(!r)throw new Error(`${e.to_type==="spec"?"Spec":"Issue"} not found: ${e.to_id}`);let c=ee(n,e.from_id,e.from_type,e.to_id,e.to_type,e.relationship_type);if(c)return c;let o=n.prepare(`
|
|
10
|
+
INSERT INTO relationships (
|
|
11
|
+
from_id, from_uuid, from_type, to_id, to_uuid, to_type, relationship_type, metadata
|
|
12
|
+
) VALUES (
|
|
13
|
+
@from_id, @from_uuid, @from_type, @to_id, @to_uuid, @to_type, @relationship_type, @metadata
|
|
14
|
+
)
|
|
15
|
+
`);try{o.run({from_id:e.from_id,from_uuid:s.uuid,from_type:e.from_type,to_id:e.to_id,to_uuid:r.uuid,to_type:e.to_type,relationship_type:e.relationship_type,metadata:e.metadata??null});let d=ee(n,e.from_id,e.from_type,e.to_id,e.to_type,e.relationship_type);if(!d)throw new Error("Failed to create relationship");return e.relationship_type==="blocks"&&e.from_type==="issue"&&e.to_type==="issue"&&at(n,e.from_id,e.to_id),d}catch(d){throw d.code&&d.code.startsWith("SQLITE_CONSTRAINT")?new Error(`Relationship already exists: ${e.from_id} (${e.from_type}) --[${e.relationship_type}]--> ${e.to_id} (${e.to_type})`):d}}function at(n,e,t){let s=E(n,t);if(s&&s.status!=="closed"){let i=E(n,e);i&&(i.status==="open"||i.status==="in_progress")&&n.prepare(`
|
|
16
|
+
UPDATE issues
|
|
17
|
+
SET status = 'blocked', updated_at = CURRENT_TIMESTAMP
|
|
18
|
+
WHERE id = ?
|
|
19
|
+
`).run(e)}}function ee(n,e,t,s,i,r){return n.prepare(`
|
|
20
|
+
SELECT * FROM relationships
|
|
21
|
+
WHERE from_id = ? AND from_type = ?
|
|
22
|
+
AND to_id = ? AND to_type = ?
|
|
23
|
+
AND relationship_type = ?
|
|
24
|
+
`).get(e,t,s,i,r)??null}function ot(n,e,t,s,i,r){let d=n.prepare(`
|
|
25
|
+
DELETE FROM relationships
|
|
26
|
+
WHERE from_id = ? AND from_type = ?
|
|
27
|
+
AND to_id = ? AND to_type = ?
|
|
28
|
+
AND relationship_type = ?
|
|
29
|
+
`).run(e,t,s,i,r).changes>0;return d&&r==="blocks"&&t==="issue"&&i==="issue"&&ct(n,e),d}function ct(n,e){let t=E(n,e);t&&t.status==="blocked"&&(dt(n,e)||n.prepare(`
|
|
30
|
+
UPDATE issues
|
|
31
|
+
SET status = 'open', updated_at = CURRENT_TIMESTAMP
|
|
32
|
+
WHERE id = ?
|
|
33
|
+
`).run(e))}function dt(n,e){return n.prepare(`
|
|
34
|
+
SELECT COUNT(*) as count
|
|
35
|
+
FROM relationships r
|
|
36
|
+
JOIN issues blocker ON r.to_id = blocker.id AND r.to_type = 'issue'
|
|
37
|
+
WHERE r.from_id = ?
|
|
38
|
+
AND r.from_type = 'issue'
|
|
39
|
+
AND r.relationship_type = 'blocks'
|
|
40
|
+
AND blocker.status IN ('open', 'in_progress', 'blocked')
|
|
41
|
+
`).get(e).count>0}function k(n,e,t,s){let i=`
|
|
42
|
+
SELECT * FROM relationships
|
|
43
|
+
WHERE from_id = @entity_id AND from_type = @entity_type
|
|
44
|
+
`,r={entity_id:e,entity_type:t};return s!==void 0&&(i+=" AND relationship_type = @relationship_type",r.relationship_type=s),i+=" ORDER BY created_at DESC",n.prepare(i).all(r)}function H(n,e,t,s){let i=`
|
|
45
|
+
SELECT * FROM relationships
|
|
46
|
+
WHERE to_id = @entity_id AND to_type = @entity_type
|
|
47
|
+
`,r={entity_id:e,entity_type:t};return s!==void 0&&(i+=" AND relationship_type = @relationship_type",r.relationship_type=s),i+=" ORDER BY created_at DESC",n.prepare(i).all(r)}function ut(n,e,t){return k(n,e,t,"blocks")}function pt(n,e,t){return H(n,e,t,"blocks")}function lt(n,e,t){return{outgoing:k(n,e,t),incoming:H(n,e,t)}}function ft(n,e,t,s,i,r){return ee(n,e,t,s,i,r)!==null}function A(n,e,t){return n.prepare(`
|
|
48
|
+
DELETE FROM relationships
|
|
49
|
+
WHERE (from_id = ? AND from_type = ?)
|
|
50
|
+
OR (to_id = ? AND to_type = ?)
|
|
51
|
+
`).run(e,t,e,t).changes}var v=K(()=>{"use strict";w()});function te(n,e){let t=null;if(e.parent_id){let o=E(n,e.parent_id);if(!o)throw new Error(`Parent issue not found: ${e.parent_id}`);t=o.uuid}let s=e.uuid||G(),i=["id","uuid","title","content","status","priority","assignee","parent_id","parent_uuid","archived"],r=["@id","@uuid","@title","@content","@status","@priority","@assignee","@parent_id","@parent_uuid","@archived"];e.created_at&&(i.push("created_at"),r.push("@created_at")),e.updated_at&&(i.push("updated_at"),r.push("@updated_at")),e.closed_at!==void 0&&(i.push("closed_at"),r.push("@closed_at")),e.archived_at!==void 0&&(i.push("archived_at"),r.push("@archived_at"));let c=n.prepare(`
|
|
52
|
+
INSERT INTO issues (
|
|
53
|
+
${i.join(", ")}
|
|
54
|
+
) VALUES (
|
|
55
|
+
${r.join(", ")}
|
|
56
|
+
)
|
|
57
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
58
|
+
uuid = excluded.uuid,
|
|
59
|
+
title = excluded.title,
|
|
60
|
+
content = excluded.content,
|
|
61
|
+
status = excluded.status,
|
|
62
|
+
priority = excluded.priority,
|
|
63
|
+
assignee = excluded.assignee,
|
|
64
|
+
parent_id = excluded.parent_id,
|
|
65
|
+
parent_uuid = excluded.parent_uuid,
|
|
66
|
+
archived = excluded.archived,
|
|
67
|
+
archived_at = excluded.archived_at,
|
|
68
|
+
${e.created_at?"created_at = excluded.created_at,":""}
|
|
69
|
+
${e.updated_at?"updated_at = excluded.updated_at":"updated_at = CURRENT_TIMESTAMP"}
|
|
70
|
+
`);try{let o={id:e.id,uuid:s,title:e.title,content:e.content||"",status:e.status||"open",priority:e.priority??2,assignee:e.assignee??null,parent_id:e.parent_id??null,parent_uuid:t,archived:e.archived?1:0};e.created_at&&(o.created_at=e.created_at),e.updated_at&&(o.updated_at=e.updated_at),e.closed_at!==void 0&&(o.closed_at=e.closed_at),e.archived_at!==void 0&&(o.archived_at=e.archived_at),c.run(o);let d=E(n,e.id);if(!d)throw new Error(`Failed to create issue ${e.id}`);return d}catch(o){throw o.code&&o.code.startsWith("SQLITE_CONSTRAINT")?new Error(`Constraint violation: ${o.message}`):o}}function E(n,e){return n.prepare(`
|
|
71
|
+
SELECT * FROM issues WHERE id = ?
|
|
72
|
+
`).get(e)??null}function L(n,e,t){let s=E(n,e);if(!s)throw new Error(`Issue not found: ${e}`);if(t.parent_id&&!E(n,t.parent_id))throw new Error(`Parent issue not found: ${t.parent_id}`);let i=[],r={id:e};if(t.title!==void 0&&t.title!==s.title&&(i.push("title = @title"),r.title=t.title),t.content!==void 0&&t.content!==s.content&&(i.push("content = @content"),r.content=t.content),t.status!==void 0&&t.status!==s.status?(i.push("status = @status"),r.status=t.status,t.closed_at!==void 0?(i.push("closed_at = @closed_at"),r.closed_at=t.closed_at):t.status==="closed"&&s.status!=="closed"?i.push("closed_at = CURRENT_TIMESTAMP"):t.status!=="closed"&&s.status==="closed"&&i.push("closed_at = NULL")):t.closed_at!==void 0&&t.closed_at!==s.closed_at&&(i.push("closed_at = @closed_at"),r.closed_at=t.closed_at),t.priority!==void 0&&t.priority!==s.priority&&(i.push("priority = @priority"),r.priority=t.priority),t.assignee!==void 0&&t.assignee!==s.assignee&&(i.push("assignee = @assignee"),r.assignee=t.assignee),t.parent_id!==void 0&&t.parent_id!==s.parent_id&&(i.push("parent_id = @parent_id"),r.parent_id=t.parent_id),t.archived!==void 0&&(t.archived?1:0)!==s.archived?(i.push("archived = @archived"),r.archived=t.archived?1:0,t.archived_at!==void 0?(i.push("archived_at = @archived_at"),r.archived_at=t.archived_at):t.archived&&!s.archived?i.push("archived_at = CURRENT_TIMESTAMP"):!t.archived&&s.archived&&i.push("archived_at = NULL")):t.archived_at!==void 0&&t.archived_at!==s.archived_at&&(i.push("archived_at = @archived_at"),r.archived_at=t.archived_at),t.updated_at!==void 0?(i.push("updated_at = @updated_at"),r.updated_at=t.updated_at):i.length>0&&i.push("updated_at = CURRENT_TIMESTAMP"),i.length===0)return s;let c=n.prepare(`
|
|
73
|
+
UPDATE issues SET ${i.join(", ")} WHERE id = @id
|
|
74
|
+
`);try{c.run(r);let o=E(n,e);if(!o)throw new Error(`Failed to update issue ${e}`);return t.status==="closed"&&s.status!=="closed"&>(n,e),o}catch(o){throw o.code&&o.code.startsWith("SQLITE_CONSTRAINT")?new Error(`Constraint violation: ${o.message}`):o}}function gt(n,e){let t=H(n,e,"issue","blocks");for(let s of t){let i=s.from_id,r=E(n,i);if(!r||r.status!=="blocked")continue;mt(n,i,e)||n.prepare(`
|
|
75
|
+
UPDATE issues
|
|
76
|
+
SET status = 'open', updated_at = CURRENT_TIMESTAMP
|
|
77
|
+
WHERE id = ?
|
|
78
|
+
`).run(i)}}function mt(n,e,t){let s=n.prepare(`
|
|
79
|
+
SELECT COUNT(*) as count
|
|
80
|
+
FROM relationships r
|
|
81
|
+
JOIN issues blocker ON r.to_id = blocker.id AND r.to_type = 'issue'
|
|
82
|
+
WHERE r.from_id = ?
|
|
83
|
+
AND r.from_type = 'issue'
|
|
84
|
+
AND r.relationship_type = 'blocks'
|
|
85
|
+
AND blocker.status IN ('open', 'in_progress', 'blocked')
|
|
86
|
+
${t?"AND blocker.id != ?":""}
|
|
87
|
+
`),i=t?[e,t]:[e];return s.get(...i).count>0}function Ie(n,e){return n.prepare("DELETE FROM issues WHERE id = ?").run(e).changes>0}function gn(n,e){return L(n,e,{status:"closed"})}function mn(n,e){return L(n,e,{status:"open"})}function $(n,e={}){let t=[],s={};e.status!==void 0&&(t.push("status = @status"),s.status=e.status),e.priority!==void 0&&(t.push("priority = @priority"),s.priority=e.priority),e.assignee!==void 0&&(t.push("assignee = @assignee"),s.assignee=e.assignee),e.parent_id!==void 0&&(t.push("parent_id = @parent_id"),s.parent_id=e.parent_id),e.archived!==void 0&&(t.push("archived = @archived"),s.archived=e.archived?1:0);let i="SELECT * FROM issues";return t.length>0&&(i+=" WHERE "+t.join(" AND ")),i+=" ORDER BY priority DESC, created_at DESC",e.limit!==void 0&&(i+=" LIMIT @limit",s.limit=e.limit),e.offset!==void 0&&(i+=" OFFSET @offset",s.offset=e.offset),n.prepare(i).all(s)}function _n(n){return n.prepare("SELECT * FROM ready_issues ORDER BY priority DESC, created_at DESC").all()}function hn(n){return n.prepare("SELECT * FROM blocked_issues ORDER BY priority DESC, created_at DESC").all()}function yn(n,e,t={}){let s=["(title LIKE @query OR content LIKE @query)"],i={query:`%${e}%`};t.status!==void 0&&(s.push("status = @status"),i.status=t.status),t.priority!==void 0&&(s.push("priority = @priority"),i.priority=t.priority),t.assignee!==void 0&&(s.push("assignee = @assignee"),i.assignee=t.assignee),t.parent_id!==void 0&&(s.push("parent_id = @parent_id"),i.parent_id=t.parent_id),t.archived!==void 0&&(s.push("archived = @archived"),i.archived=t.archived?1:0);let r=`SELECT * FROM issues WHERE ${s.join(" AND ")} ORDER BY priority DESC, created_at DESC`;return t.limit!==void 0?(r+=" LIMIT @limit",i.limit=t.limit):r+=" LIMIT 50",n.prepare(r).all(i)}var w=K(()=>{"use strict";B();v()});export*from"@sudocode-ai/types";import Ze from"better-sqlite3";import*as R from"@sudocode-ai/types/schema";var Ve=[];function Qe(n){return n.exec(`
|
|
88
|
+
CREATE TABLE IF NOT EXISTS migrations (
|
|
89
|
+
version INTEGER PRIMARY KEY,
|
|
90
|
+
name TEXT NOT NULL,
|
|
91
|
+
applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
92
|
+
)
|
|
93
|
+
`),n.prepare("SELECT MAX(version) as version FROM migrations").get().version??0}function Ke(n,e){n.prepare(`
|
|
94
|
+
INSERT OR REPLACE INTO migrations (version, name)
|
|
95
|
+
VALUES (?, ?)
|
|
96
|
+
`).run(e.version,e.name)}function me(n){let e=Qe(n),t=Ve.filter(s=>s.version>e);if(t.length!==0){console.log(`Running ${t.length} pending migration(s)...`);for(let s of t){console.log(` Applying migration ${s.version}: ${s.name}`);try{s.up(n),Ke(n,s),console.log(` \u2713 Migration ${s.version} applied successfully`)}catch(i){throw console.error(` \u2717 Migration ${s.version} failed:`,i),i}}}}function Ge(n){let e=new Ze(n.path,{verbose:n.verbose?console.log:void 0});e.exec(R.DB_CONFIG);for(let t of R.ALL_TABLES)e.exec(t);for(let t of R.ALL_INDEXES)e.exec(t);for(let t of R.ALL_VIEWS)e.exec(t);return me(e),e}function en(n){return Ge({path:n})}function tn(n,e){let t=`sp_${Date.now()}`;try{n.exec(`SAVEPOINT ${t}`);let s=e(n);return n.exec(`RELEASE ${t}`),s}catch(s){throw n.exec(`ROLLBACK TO ${t}`),s}}B();function X(n,e){let t=null;if(e.parent_id){let o=T(n,e.parent_id);if(!o)throw new Error(`Parent spec not found: ${e.parent_id}`);t=o.uuid}let s=e.uuid||G(),i=["id","uuid","title","file_path","content","priority","parent_id","parent_uuid","archived"],r=["@id","@uuid","@title","@file_path","@content","@priority","@parent_id","@parent_uuid","@archived"];e.created_at&&(i.push("created_at"),r.push("@created_at")),e.updated_at&&(i.push("updated_at"),r.push("@updated_at")),e.archived_at!==void 0&&(i.push("archived_at"),r.push("@archived_at"));let c=n.prepare(`
|
|
97
|
+
INSERT INTO specs (
|
|
98
|
+
${i.join(", ")}
|
|
99
|
+
) VALUES (
|
|
100
|
+
${r.join(", ")}
|
|
101
|
+
)
|
|
102
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
103
|
+
uuid = excluded.uuid,
|
|
104
|
+
title = excluded.title,
|
|
105
|
+
file_path = excluded.file_path,
|
|
106
|
+
content = excluded.content,
|
|
107
|
+
priority = excluded.priority,
|
|
108
|
+
parent_id = excluded.parent_id,
|
|
109
|
+
parent_uuid = excluded.parent_uuid,
|
|
110
|
+
archived = excluded.archived,
|
|
111
|
+
archived_at = excluded.archived_at,
|
|
112
|
+
${e.created_at?"created_at = excluded.created_at,":""}
|
|
113
|
+
${e.updated_at?"updated_at = excluded.updated_at":"updated_at = CURRENT_TIMESTAMP"}
|
|
114
|
+
`);try{let o={id:e.id,uuid:s,title:e.title,file_path:e.file_path,content:e.content||"",priority:e.priority??2,parent_id:e.parent_id??null,parent_uuid:t,archived:e.archived?1:0};e.created_at&&(o.created_at=e.created_at),e.updated_at&&(o.updated_at=e.updated_at),e.archived_at!==void 0&&(o.archived_at=e.archived_at),c.run(o);let d=T(n,e.id);if(!d)throw new Error(`Failed to create spec ${e.id}`);return d}catch(o){throw o.code&&o.code.startsWith("SQLITE_CONSTRAINT")?new Error(`Constraint violation: ${o.message}`):o}}function T(n,e){return n.prepare(`
|
|
115
|
+
SELECT * FROM specs WHERE id = ?
|
|
116
|
+
`).get(e)??null}function xe(n,e){return n.prepare(`
|
|
117
|
+
SELECT * FROM specs WHERE file_path = ?
|
|
118
|
+
`).get(e)??null}function P(n,e,t){let s=T(n,e);if(!s)throw new Error(`Spec not found: ${e}`);if(t.parent_id&&!T(n,t.parent_id))throw new Error(`Parent spec not found: ${t.parent_id}`);let i=[],r={id:e};if(t.title!==void 0&&t.title!==s.title&&(i.push("title = @title"),r.title=t.title),t.file_path!==void 0&&t.file_path!==s.file_path&&(i.push("file_path = @file_path"),r.file_path=t.file_path),t.content!==void 0&&t.content!==s.content&&(i.push("content = @content"),r.content=t.content),t.priority!==void 0&&t.priority!==s.priority&&(i.push("priority = @priority"),r.priority=t.priority),t.parent_id!==void 0&&t.parent_id!==s.parent_id&&(i.push("parent_id = @parent_id"),r.parent_id=t.parent_id),t.archived!==void 0&&t.archived!==s.archived?(i.push("archived = @archived"),r.archived=t.archived?1:0,t.archived_at!==void 0?(i.push("archived_at = @archived_at"),r.archived_at=t.archived_at):t.archived&&!s.archived?i.push("archived_at = CURRENT_TIMESTAMP"):!t.archived&&s.archived&&i.push("archived_at = NULL")):t.archived_at!==void 0&&t.archived_at!==s.archived_at&&(i.push("archived_at = @archived_at"),r.archived_at=t.archived_at),t.updated_at!==void 0?(i.push("updated_at = @updated_at"),r.updated_at=t.updated_at):i.length>0&&i.push("updated_at = CURRENT_TIMESTAMP"),i.length===0)return s;let c=n.prepare(`
|
|
119
|
+
UPDATE specs SET ${i.join(", ")} WHERE id = @id
|
|
120
|
+
`);try{c.run(r);let o=T(n,e);if(!o)throw new Error(`Failed to update spec ${e}`);return o}catch(o){throw o.code&&o.code.startsWith("SQLITE_CONSTRAINT")?new Error(`Constraint violation: ${o.message}`):o}}function Te(n,e){return n.prepare("DELETE FROM specs WHERE id = ?").run(e).changes>0}function F(n,e={}){let t=[],s={};e.priority!==void 0&&(t.push("priority = @priority"),s.priority=e.priority),e.parent_id!==void 0&&(t.push("parent_id = @parent_id"),s.parent_id=e.parent_id),e.archived!==void 0&&(t.push("archived = @archived"),s.archived=e.archived?1:0);let i="SELECT * FROM specs";return t.length>0&&(i+=" WHERE "+t.join(" AND ")),i+=" ORDER BY priority DESC, created_at DESC",e.limit!==void 0&&(i+=" LIMIT @limit",s.limit=e.limit),e.offset!==void 0&&(i+=" OFFSET @offset",s.offset=e.offset),n.prepare(i).all(s)}function dn(n,e,t={}){let s=["(title LIKE @query OR content LIKE @query)"],i={query:`%${e}%`};t.priority!==void 0&&(s.push("priority = @priority"),i.priority=t.priority),t.parent_id!==void 0&&(s.push("parent_id = @parent_id"),i.parent_id=t.parent_id),t.archived!==void 0&&(s.push("archived = @archived"),i.archived=t.archived?1:0);let r=`SELECT * FROM specs WHERE ${s.join(" AND ")} ORDER BY priority DESC, created_at DESC`;return t.limit!==void 0?(r+=" LIMIT @limit",i.limit=t.limit):r+=" LIMIT 50",n.prepare(r).all(i)}w();v();function _t(n,e,t,s){let i=t==="spec"?"specs":"issues",r=n.prepare(`SELECT uuid FROM ${i} WHERE id = ?`).get(e);if(!r)throw new Error(`${t==="spec"?"Spec":"Issue"} not found: ${e}`);let c=n.prepare(`
|
|
121
|
+
INSERT INTO tags (entity_id, entity_uuid, entity_type, tag)
|
|
122
|
+
VALUES (@entity_id, @entity_uuid, @entity_type, @tag)
|
|
123
|
+
`);try{return c.run({entity_id:e,entity_uuid:r.uuid,entity_type:t,tag:s}),{entity_id:e,entity_uuid:r.uuid,entity_type:t,tag:s}}catch(o){if(o.code&&o.code.startsWith("SQLITE_CONSTRAINT"))return{entity_id:e,entity_uuid:r.uuid,entity_type:t,tag:s};throw o}}function ht(n,e,t,s){let i=[];for(let r of s)i.push(_t(n,e,t,r));return i}function bn(n,e,t,s){return n.prepare(`
|
|
124
|
+
DELETE FROM tags
|
|
125
|
+
WHERE entity_id = ? AND entity_type = ? AND tag = ?
|
|
126
|
+
`).run(e,t,s).changes>0}function q(n,e,t){return n.prepare(`
|
|
127
|
+
SELECT tag FROM tags
|
|
128
|
+
WHERE entity_id = ? AND entity_type = ?
|
|
129
|
+
ORDER BY tag
|
|
130
|
+
`).all(e,t).map(r=>r.tag)}function Sn(n,e,t){let s="SELECT * FROM tags WHERE tag = @tag",i={tag:e};return t!==void 0&&(s+=" AND entity_type = @entity_type",i.entity_type=t),s+=" ORDER BY entity_id",n.prepare(s).all(i)}function yt(n,e,t){return n.prepare(`
|
|
131
|
+
DELETE FROM tags
|
|
132
|
+
WHERE entity_id = ? AND entity_type = ?
|
|
133
|
+
`).run(e,t).changes}function Dn(n,e,t,s){return n.prepare(`
|
|
134
|
+
SELECT 1 FROM tags
|
|
135
|
+
WHERE entity_id = ? AND entity_type = ? AND tag = ?
|
|
136
|
+
`).get(e,t,s)!==void 0}function xn(n,e){let t="SELECT DISTINCT tag FROM tags",s={};return e!==void 0&&(t+=" WHERE entity_type = @entity_type",s.entity_type=e),t+=" ORDER BY tag",n.prepare(t).all(s).map(c=>c.tag)}function O(n,e,t,s){return yt(n,e,t),s.length>0&&ht(n,e,t,s),s}function In(n,e){let s=n.prepare(`
|
|
137
|
+
INSERT INTO events (
|
|
138
|
+
entity_id, entity_type, event_type, actor,
|
|
139
|
+
old_value, new_value, comment, git_commit_sha, source
|
|
140
|
+
) VALUES (
|
|
141
|
+
@entity_id, @entity_type, @event_type, @actor,
|
|
142
|
+
@old_value, @new_value, @comment, @git_commit_sha, @source
|
|
143
|
+
)
|
|
144
|
+
`).run({entity_id:e.entity_id,entity_type:e.entity_type,event_type:e.event_type,actor:e.actor,old_value:e.old_value||null,new_value:e.new_value||null,comment:e.comment||null,git_commit_sha:e.git_commit_sha||null,source:e.source||null}),i=Et(n,Number(s.lastInsertRowid));if(!i)throw new Error("Failed to create event");return i}function Et(n,e){return n.prepare("SELECT * FROM events WHERE id = ?").get(e)??null}function ae(n,e={}){let t=[],s={};e.entity_id!==void 0&&(t.push("entity_id = @entity_id"),s.entity_id=e.entity_id),e.entity_type!==void 0&&(t.push("entity_type = @entity_type"),s.entity_type=e.entity_type),e.event_type!==void 0&&(t.push("event_type = @event_type"),s.event_type=e.event_type),e.actor!==void 0&&(t.push("actor = @actor"),s.actor=e.actor);let i="SELECT * FROM events";return t.length>0&&(i+=" WHERE "+t.join(" AND ")),i+=" ORDER BY created_at DESC",e.limit!==void 0&&(i+=" LIMIT @limit",s.limit=e.limit),e.offset!==void 0&&(i+=" OFFSET @offset",s.offset=e.offset),n.prepare(i).all(s)}function Rn(n,e,t,s){return ae(n,{entity_id:e,entity_type:t,limit:s})}function vn(n,e=50){return ae(n,{limit:e})}function On(n,e,t){return ae(n,{actor:e,limit:t})}function wn(n,e,t){return n.prepare(`
|
|
145
|
+
DELETE FROM events
|
|
146
|
+
WHERE entity_id = ? AND entity_type = ?
|
|
147
|
+
`).run(e,t).changes}function bt(n,e,t){let s=`[[${n}`;return e&&(s+=`|${e}`),s+="]]",t&&(s+=`{ ${t} }`),s}function St(n,e){let t=n.split(`
|
|
148
|
+
`);if(e<1||e>t.length)throw new Error(`Line number ${e} is out of bounds (content has ${t.length} lines)`);let s=0;for(let i=0;i<e-1;i++)s+=t[i].length+1;return s}function Dt(n,e){let t=n.indexOf(e);if(t===-1)throw new Error(`Text not found: "${e}"`);return t}function Nn(n,e,t){let{referenceId:s,displayText:i,relationshipType:r,format:c="inline",position:o="after"}=t;if(!e.line&&!e.text)throw new Error("Either line or text must be specified");if(e.line&&e.text)throw new Error("Cannot specify both line and text");let d=bt(s,i,r),a;if(e.line){if(a=St(n,e.line),o==="after"){let f=n.indexOf(`
|
|
149
|
+
`,a);a=f===-1?n.length:f}}else if(e.text)a=Dt(n,e.text),o==="after"&&(a+=e.text.length);else throw new Error("Location must specify line or text");let u;return c==="newline"?o==="before"?u=`${d}
|
|
150
|
+
`:u=`
|
|
151
|
+
${d}`:o==="before"?u=`${d} `:u=` ${d}`,n.slice(0,a)+u+n.slice(a)}function oe(n,e){return n.inTransaction?e(n):n.transaction(e)(n)}function Wn(n,e){return oe(n,()=>{let t=[];for(let s of e)t.push(s());return t})}function jn(n,e,t=3,s=100){let i=null;for(let r=0;r<=t;r++)try{return e(n)}catch(c){if(i=c,(c.code==="SQLITE_BUSY"||c.code==="SQLITE_LOCKED")&&r<t){let o=s*Math.pow(2,r);new Promise(a=>setTimeout(a,o)).then(()=>{});continue}throw c}throw i||new Error("Transaction failed after retries")}var Re=class n{constructor(e,t){this.db=e;this.savepointId=t||`sp_${Date.now()}_${Math.random().toString().replace(".","")}`,this.db.prepare(`SAVEPOINT ${this.savepointId}`).run()}savepointId;released=!1;commit(){if(this.released)throw new Error("Savepoint already released");this.db.prepare(`RELEASE ${this.savepointId}`).run(),this.released=!0}rollback(){if(this.released)throw new Error("Savepoint already released");this.db.prepare(`ROLLBACK TO ${this.savepointId}`).run(),this.db.prepare(`RELEASE ${this.savepointId}`).run(),this.released=!0}static execute(e,t){let s=new n(e);try{let i=t(s);return s.released||s.commit(),i}catch(i){throw s.released||s.rollback(),i}}};import*as h from"fs";import*as ve from"readline";import*as ce from"path";async function M(n,e={}){let{skipErrors:t=!1,onError:s}=e;if(!h.existsSync(n))return[];let i=[],r=h.createReadStream(n,{encoding:"utf8"}),c=ve.createInterface({input:r,crlfDelay:1/0}),o=0;for await(let d of c)if(o++,d.trim()!=="")try{let a=JSON.parse(d);i.push(a)}catch(a){let u=a;if(s&&s(o,d,u),!t)throw new Error(`Failed to parse JSON at line ${o}: ${u.message}`)}return i}function de(n,e={}){let{skipErrors:t=!1,onError:s}=e;if(!h.existsSync(n))return[];let i=[],c=h.readFileSync(n,"utf8").split(`
|
|
152
|
+
`);for(let o=0;o<c.length;o++){let d=c[o].trim(),a=o+1;if(d!=="")try{let u=JSON.parse(d);i.push(u)}catch(u){let p=u;if(s&&s(a,d,p),!t)throw new Error(`Failed to parse JSON at line ${a}: ${p.message}`)}}return i}async function Y(n,e,t={}){let{atomic:s=!0}=t,i=ce.dirname(n);h.existsSync(i)||h.mkdirSync(i,{recursive:!0});let r=s?`${n}.tmp`:n,c=[...e].sort((a,u)=>{let p=a.created_at,f=u.created_at;if(!p&&!f){let b=a.id||"",y=u.id||"";return b<y?-1:b>y?1:0}if(!p)return 1;if(!f||p<f)return-1;if(p>f)return 1;let g=a.id||"",S=u.id||"";return g<S?-1:g>S?1:0}),d=c.map(a=>JSON.stringify(a)).join(`
|
|
153
|
+
`)+`
|
|
154
|
+
`;if(!(h.existsSync(n)&&h.readFileSync(n,"utf8")===d)&&(h.writeFileSync(r,d,"utf8"),s&&h.renameSync(r,n),c.length>0)){let a=c.map(u=>u.updated_at).filter(u=>u!=null).map(u=>{let p=String(u),g=p.endsWith("Z")||p.includes("+")||/[+-]\d{2}:\d{2}$/.test(p)?p:p.replace(" ","T")+"Z";return new Date(g).getTime()});if(a.length>0){let u=Math.max(...a),p=new Date(u);h.utimesSync(n,p,p)}}}function Oe(n,e,t={}){let{atomic:s=!0}=t,i=ce.dirname(n);h.existsSync(i)||h.mkdirSync(i,{recursive:!0});let r=s?`${n}.tmp`:n,c=[...e].sort((a,u)=>{let p=a.created_at,f=u.created_at;if(!p&&!f){let b=a.id||"",y=u.id||"";return b<y?-1:b>y?1:0}if(!p)return 1;if(!f||p<f)return-1;if(p>f)return 1;let g=a.id||"",S=u.id||"";return g<S?-1:g>S?1:0}),d=c.map(a=>JSON.stringify(a)).join(`
|
|
155
|
+
`)+`
|
|
156
|
+
`;if(!(h.existsSync(n)&&h.readFileSync(n,"utf8")===d)&&(h.writeFileSync(r,d,"utf8"),s&&h.renameSync(r,n),c.length>0)){let a=c.map(u=>u.updated_at).filter(u=>u!=null).map(u=>{let p=String(u),g=p.endsWith("Z")||p.includes("+")||/[+-]\d{2}:\d{2}$/.test(p)?p:p.replace(" ","T")+"Z";return new Date(g).getTime()});if(a.length>0){let u=Math.max(...a),p=new Date(u);h.utimesSync(n,p,p)}}}async function Pn(n,e,t="id"){let s=e[t];if(!s)throw new Error(`Entity missing ${t} field`);let i=await M(n,{skipErrors:!0}),r=i.findIndex(c=>c[t]===s);r>=0?i[r]=e:i.push(e),await Y(n,i)}function Hn(n,e,t="id"){let s=e[t];if(!s)throw new Error(`Entity missing ${t} field`);let i=de(n,{skipErrors:!0}),r=i.findIndex(c=>c[t]===s);r>=0?i[r]=e:i.push(e),Oe(n,i)}async function qn(n,e,t="id"){let s=await M(n,{skipErrors:!0}),i=s.length,r=s.filter(c=>c[t]!==e);return r.length===i?!1:(await Y(n,r),!0)}function Yn(n,e,t="id"){let s=de(n,{skipErrors:!0}),i=s.length,r=s.filter(c=>c[t]!==e);return r.length===i?!1:(Oe(n,r),!0)}async function zn(n,e,t="id"){return(await M(n,{skipErrors:!0})).find(i=>i[t]===e)??null}function Vn(n,e,t="id"){return de(n,{skipErrors:!0}).find(i=>i[t]===e)??null}w();v();function we(n){return{...n,dismissed:n.dismissed===1}}function xt(n){let t=n.prepare(`
|
|
157
|
+
SELECT id FROM issue_feedback ORDER BY id DESC LIMIT 1
|
|
158
|
+
`).get();if(!t)return"FB-001";let s=t.id.match(/^FB-(\d+)$/);if(!s)return"FB-001";let i=parseInt(s[1],10)+1;return`FB-${String(i).padStart(3,"0")}`}function Le(n,e){let t=e.id||xt(n),s=e.anchor?JSON.stringify(e.anchor):null,i=e.agent||"user",r=n.prepare("SELECT uuid FROM issues WHERE id = ?").get(e.issue_id);if(!r)throw new Error(`Issue not found: ${e.issue_id}`);let c=n.prepare("SELECT uuid FROM specs WHERE id = ?").get(e.spec_id);if(!c)throw new Error(`Spec not found: ${e.spec_id}`);let o=n.prepare(`
|
|
159
|
+
INSERT INTO issue_feedback (
|
|
160
|
+
id, issue_id, issue_uuid, spec_id, spec_uuid, feedback_type, content, agent, anchor, dismissed
|
|
161
|
+
) VALUES (
|
|
162
|
+
@id, @issue_id, @issue_uuid, @spec_id, @spec_uuid, @feedback_type, @content, @agent, @anchor, @dismissed
|
|
163
|
+
)
|
|
164
|
+
`);try{o.run({id:t,issue_id:e.issue_id,issue_uuid:r.uuid,spec_id:e.spec_id,spec_uuid:c.uuid,feedback_type:e.feedback_type,content:e.content,agent:i,anchor:s,dismissed:e.dismissed!==void 0&&e.dismissed?1:0});let d=ue(n,t);if(!d)throw new Error(`Failed to create feedback ${t}`);return d}catch(d){throw d.code&&d.code.startsWith("SQLITE_CONSTRAINT")?new Error(`Constraint violation: ${d.message}`):d}}function ue(n,e){let s=n.prepare(`
|
|
165
|
+
SELECT * FROM issue_feedback WHERE id = ?
|
|
166
|
+
`).get(e);return s?we(s):null}function Ne(n,e,t){let s=ue(n,e);if(!s)throw new Error(`Feedback not found: ${e}`);let i=[],r={id:e};if(t.content!==void 0&&(i.push("content = @content"),r.content=t.content),t.dismissed!==void 0&&(i.push("dismissed = @dismissed"),r.dismissed=t.dismissed?1:0),t.anchor!==void 0&&(i.push("anchor = @anchor"),r.anchor=JSON.stringify(t.anchor)),i.push("updated_at = CURRENT_TIMESTAMP"),i.length===1)return s;let c=n.prepare(`
|
|
167
|
+
UPDATE issue_feedback SET ${i.join(", ")} WHERE id = @id
|
|
168
|
+
`);try{c.run(r);let o=ue(n,e);if(!o)throw new Error(`Failed to update feedback ${e}`);return o}catch(o){throw o.code&&o.code.startsWith("SQLITE_CONSTRAINT")?new Error(`Constraint violation: ${o.message}`):o}}function Ce(n,e){return n.prepare("DELETE FROM issue_feedback WHERE id = ?").run(e).changes>0}function J(n,e={}){let t=[],s={};e.issue_id!==void 0&&(t.push("issue_id = @issue_id"),s.issue_id=e.issue_id),e.spec_id!==void 0&&(t.push("spec_id = @spec_id"),s.spec_id=e.spec_id),e.feedback_type!==void 0&&(t.push("feedback_type = @feedback_type"),s.feedback_type=e.feedback_type),e.dismissed!==void 0&&(t.push("dismissed = @dismissed"),s.dismissed=e.dismissed?1:0);let i="SELECT * FROM issue_feedback";return t.length>0&&(i+=" WHERE "+t.join(" AND ")),i+=" ORDER BY created_at DESC",e.limit!==void 0&&(i+=" LIMIT @limit",s.limit=e.limit),e.offset!==void 0&&(i+=" OFFSET @offset",s.offset=e.offset),n.prepare(i).all(s).map(we)}function Tt(n,e){let s=k(n,e.id,"spec").map(r=>({from:r.from_id,from_type:r.from_type,to:r.to_id,to_type:r.to_type,type:r.relationship_type})),i=q(n,e.id,"spec");return{...e,relationships:s,tags:i}}function It(n,e){let s=k(n,e.id,"issue").map(o=>({from:o.from_id,from_type:o.from_type,to:o.to_id,to_type:o.to_type,type:o.relationship_type})),i=q(n,e.id,"issue"),c=J(n,{issue_id:e.id}).map(o=>({id:o.id,issue_id:o.issue_id,spec_id:o.spec_id,feedback_type:o.feedback_type,content:o.content,agent:o.agent,anchor:o.anchor&&typeof o.anchor=="string"?JSON.parse(o.anchor):o.anchor,dismissed:o.dismissed,created_at:o.created_at,updated_at:o.updated_at}));return{...e,relationships:s,tags:i,feedback:c.length>0?c:void 0}}function Rt(n,e={}){let{since:t}=e,s=F(n);return(t?s.filter(r=>new Date(r.updated_at)>t):s).map(r=>Tt(n,r))}function vt(n,e={}){let{since:t}=e,s=$(n);return(t?s.filter(r=>new Date(r.updated_at)>t):s).map(r=>It(n,r))}async function le(n,e={}){let{outputDir:t=".sudocode",specsFile:s="specs.jsonl",issuesFile:i="issues.jsonl"}=e,r=`${t}/${s}`,c=`${t}/${i}`,o=Rt(n,e);await Y(r,o);let d=vt(n,e);return await Y(c,d),{specsCount:o.length,issuesCount:d.length}}var pe=class{constructor(e,t=5e3,s={}){this.db=e;this.delayMs=t;this.options=s}timeoutId=null;pending=!1;trigger(){this.pending=!0,this.timeoutId&&clearTimeout(this.timeoutId),this.timeoutId=setTimeout(()=>{this.execute()},this.delayMs)}async execute(){if(this.pending){this.pending=!1,this.timeoutId=null;try{await le(this.db,this.options)}catch(e){throw console.error("Export failed:",e),e}}}cancel(){this.timeoutId&&(clearTimeout(this.timeoutId),this.timeoutId=null),this.pending=!1}isPending(){return this.pending}async flush(){this.pending&&(this.timeoutId&&(clearTimeout(this.timeoutId),this.timeoutId=null),await this.execute())}};function ss(n,e=5e3,t={}){return new pe(n,e,t)}w();v();import*as fe from"path";function Fe(n,e){let t=new Map(n.map(d=>[d.uuid,d])),s=new Set(e.map(d=>d.uuid)),i=e.filter(d=>!t.has(d.uuid)).map(d=>d.id),r=n.filter(d=>!s.has(d.uuid)).map(d=>d.id),c=[],o=[];for(let d of e){let a=t.get(d.uuid);a&&(Ot(a,d)?c.push(d.id):o.push(d.id))}return{added:i,updated:c,deleted:r,unchanged:o}}function Ot(n,e){return n.updated_at!==e.updated_at}function ke(n,e){let t=[],s=new Map(n.map(r=>[r.id,r]));for(let r of e){let c=s.get(r.id);c&&c.uuid!==r.uuid&&t.push({id:r.id,uuid:r.uuid,type:"spec",reason:"Same ID but different UUID (different entities)",localContent:c.title,incomingContent:r.title,localCreatedAt:c.created_at,incomingCreatedAt:r.created_at})}let i=new Map;for(let r of e){let c=i.get(r.id)||[];c.push(r),i.set(r.id,c)}for(let[r,c]of i.entries())if(c.length>1&&new Set(c.map(d=>d.uuid)).size>1)for(let d=1;d<c.length;d++)t.push({id:c[d].id,uuid:c[d].uuid,type:"spec",reason:"Duplicate ID in incoming data with different UUID",localContent:c[0].title,incomingContent:c[d].title,localCreatedAt:c[0].created_at,incomingCreatedAt:c[d].created_at});return t}function ls(n,e,t){let s=0,i=F(n);for(let c of i){let o=new RegExp(`\\b${e}\\b`,"g"),d=c.content.match(o);d&&(s+=d.length)}let r=$(n);for(let c of r){let o=new RegExp(`\\b${e}\\b`,"g"),d=c.content.match(o);d&&(s+=d.length)}return s}function wt(n,e){let t=[],s=new Map;for(let i of e){let r=!0;if(i.localCreatedAt){let o=new Date(i.localCreatedAt).getTime(),d=new Date(i.incomingCreatedAt).getTime();d>o?r=!0:o>d?r=!1:r=i.uuid>i.localContent}let c=s.get(i.uuid);c||(c=Lt(n,i.id,i.type),s.set(i.uuid,c)),t.push({...i,resolution:"renumber",newId:c,...r&&{note:"incoming is newer - correctly renumbered"},...!r&&{note:"local is newer - incoming (older) being renumbered"}})}return t}function Lt(n,e,t){let s=e.match(/^([a-z]+-)?(\d+)$/i);if(!s)return`${t}-${Date.now()}`;let i=s[1]||`${t}-`,r=parseInt(s[2],10),c=`${i}${r+1e3}`,o=0;for(;o<1e3;){if(!(t==="spec"?T(n,c)!==null:E(n,c)!==null))return c;r++,c=`${i}${r+1e3}`,o++}return`${i}${Date.now()}`}function fs(n,e,t){let s=0,i=F(n);for(let c of i){let o=new RegExp(`\\b${e}\\b`,"g");if(o.test(c.content)){let d=c.content.replace(o,t);P(n,c.id,{content:d}),s++}}let r=$(n);for(let c of r){let o=new RegExp(`\\b${e}\\b`,"g"),d=!1,a=c.content;o.test(c.content)&&(a=c.content.replace(o,t),d=!0),d&&(L(n,c.id,{content:a}),s++)}return s}function Ae(n,e,t,s=!1,i=!1){if(s)return{added:t.added.length,updated:t.updated.length,deleted:t.deleted.length};let r=0,c=0,o=0;for(let d of t.added){let a=e.find(u=>u.id===d);a&&(X(n,{id:a.id,uuid:a.uuid,title:a.title,file_path:a.file_path,content:a.content,priority:a.priority,parent_id:a.parent_id,archived:a.archived,archived_at:a.archived_at,created_at:a.created_at,updated_at:a.updated_at}),O(n,a.id,"spec",a.tags||[]),r++)}if(!i)for(let d of t.added){let a=e.find(u=>u.id===d);if(a&&a.relationships&&a.relationships.length>0)for(let u of a.relationships)I(n,{from_id:u.from,from_type:u.from_type,to_id:u.to,to_type:u.to_type,relationship_type:u.type})}for(let d of t.updated){let a=e.find(u=>u.id===d);a&&(P(n,a.id,{title:a.title,file_path:a.file_path,content:a.content,priority:a.priority,parent_id:a.parent_id,archived:a.archived,archived_at:a.archived_at,updated_at:a.updated_at}),O(n,a.id,"spec",a.tags||[]),c++)}if(!i)for(let d of t.updated){let a=e.find(u=>u.id===d);if(a){A(n,a.id,"spec");for(let u of a.relationships||[])I(n,{from_id:u.from,from_type:u.from_type,to_id:u.to,to_type:u.to_type,relationship_type:u.type})}}for(let d of t.deleted)Te(n,d),o++;return{added:r,updated:c,deleted:o}}function $e(n,e,t){let s=J(n,{issue_id:e});for(let i of s)Ce(n,i.id);if(t&&t.length>0)for(let i of t)Le(n,{id:i.id,issue_id:i.issue_id,spec_id:i.spec_id,feedback_type:i.feedback_type,content:i.content,agent:i.agent,anchor:i.anchor,dismissed:i.dismissed})}function Me(n,e,t,s=!1,i=!1){if(s)return{added:t.added.length,updated:t.updated.length,deleted:t.deleted.length};let r=0,c=0,o=0;for(let d of t.added){let a=e.find(u=>u.id===d);a&&(te(n,{id:a.id,uuid:a.uuid,title:a.title,content:a.content,status:a.status,priority:a.priority,assignee:a.assignee,parent_id:a.parent_id,archived:a.archived,archived_at:a.archived_at,created_at:a.created_at,updated_at:a.updated_at,closed_at:a.closed_at}),O(n,a.id,"issue",a.tags||[]),$e(n,a.id,a.feedback),r++)}if(!i)for(let d of t.added){let a=e.find(u=>u.id===d);if(a)for(let u of a.relationships||[])I(n,{from_id:u.from,from_type:u.from_type,to_id:u.to,to_type:u.to_type,relationship_type:u.type})}for(let d of t.updated){let a=e.find(u=>u.id===d);a&&(L(n,a.id,{title:a.title,content:a.content,status:a.status,priority:a.priority,assignee:a.assignee,parent_id:a.parent_id,archived:a.archived,archived_at:a.archived_at,updated_at:a.updated_at,closed_at:a.closed_at}),O(n,a.id,"issue",a.tags||[]),$e(n,a.id,a.feedback),c++)}if(!i)for(let d of t.updated){let a=e.find(u=>u.id===d);if(a){A(n,a.id,"issue");for(let u of a.relationships||[])I(n,{from_id:u.from,from_type:u.from_type,to_id:u.to,to_type:u.to_type,relationship_type:u.type})}}for(let d of t.deleted)Ie(n,d),o++;return{added:r,updated:c,deleted:o}}async function gs(n,e={}){let{inputDir:t=".sudocode",specsFile:s="specs.jsonl",issuesFile:i="issues.jsonl",dryRun:r=!1,resolveCollisions:c=!0}=e,o=fe.join(t,s),d=fe.join(t,i),a=await M(o,{skipErrors:!0}),u=await M(d,{skipErrors:!0}),p=F(n),f=$(n),g=ke(p,a),S=ke(f,u),b=[...g.map(m=>({...m,type:"spec"})),...S.map(m=>({...m,type:"issue"}))],y=[];if(c&&b.length>0){y=wt(n,b);for(let m of y)if(m.resolution==="renumber"&&m.newId){if(m.type==="spec"){let _=a.find(l=>l.id===m.id&&l.uuid===m.uuid);_&&(_.id=m.newId)}else if(m.type==="issue"){let _=u.find(l=>l.id===m.id&&l.uuid===m.uuid);_&&(_.id=m.newId)}}}let D=Fe(p,a),x=Fe(f,u),N={specs:{added:0,updated:0,deleted:0},issues:{added:0,updated:0,deleted:0},collisions:y.length>0?y:b};return r?(N.specs=Ae(n,a,D,r),N.issues=Me(n,u,x,r)):oe(n,()=>{N.specs=Ae(n,a,D,r,!0),N.issues=Me(n,u,x,r,!0);for(let m of D.added){let _=a.find(l=>l.id===m);if(_&&_.relationships&&_.relationships.length>0)for(let l of _.relationships)I(n,{from_id:l.from,from_type:l.from_type,to_id:l.to,to_type:l.to_type,relationship_type:l.type})}for(let m of D.updated){let _=a.find(l=>l.id===m);if(_){A(n,_.id,"spec");for(let l of _.relationships||[])I(n,{from_id:l.from,from_type:l.from_type,to_id:l.to,to_type:l.to_type,relationship_type:l.type})}}for(let m of x.added){let _=u.find(l=>l.id===m);if(_&&_.relationships&&_.relationships.length>0)for(let l of _.relationships)I(n,{from_id:l.from,from_type:l.from_type,to_id:l.to,to_type:l.to_type,relationship_type:l.type})}for(let m of x.updated){let _=u.find(l=>l.id===m);if(_){A(n,_.id,"issue");for(let l of _.relationships||[])I(n,{from_id:l.from,from_type:l.from_type,to_id:l.to,to_type:l.to_type,relationship_type:l.type})}}}),N}import U from"gray-matter";import*as W from"fs";w();B();import*as Je from"crypto";function z(n,e,t){let s=n.split(`
|
|
169
|
+
`);if(e<1||e>s.length)throw new Error(`Line number ${e} is out of range (1-${s.length})`);let i=s[e-1],r=Nt(s,e),c=Ue(i,t,50),o=ne(s,e,-50),d=ne(s,e,50),a=r?e-r.startLine:void 0,u=We(c);return{section_heading:r?.heading,section_level:r?.level,line_number:e,line_offset:a,text_snippet:c,context_before:o,context_after:d,content_hash:u,anchor_status:"valid",last_verified_at:new Date().toISOString(),original_location:{line_number:e,section_heading:r?.heading}}}function Nt(n,e){for(let t=e-1;t>=0;t--){let i=n[t].match(/^(#{1,6})\s+(.+?)(\r)?$/);if(i){let r=i[1].length;return{heading:i[2].trim(),level:r,startLine:t+1}}}return null}function Ue(n,e,t=50){if(!n||n.trim()==="")return"";let s=n.trim(),i=0,r=s.length;if(e!==void 0){let o=n.length-n.trimStart().length,d=Math.max(0,e-o),a=Math.floor(t/2);i=Math.max(0,d-a),r=Math.min(s.length,d+a)}else r=Math.min(s.length,t);let c=s.substring(i,r);return i>0&&(c="..."+c),r<s.length&&(c=c+"..."),c}function ne(n,e,t){if(t===0)return"";let s=t>0?1:-1,i=Math.abs(t),r="",c=e-1;for(s>0?c+=1:c-=1;c>=0&&c<n.length&&r.length<i;){let o=n[c],d=i-r.length;if(s>0)r.length>0&&(r+=" "),r+=o.substring(0,d);else{let a=Math.max(0,o.length-d),u=o.substring(a);r.length>0?r=u+" "+r:r=u}c+=s}return r.trim()}function We(n){return Je.createHash("sha256").update(n).digest("hex").substring(0,16)}function Ct(n,e){if(!e.line_number)return!1;let t=n.split(`
|
|
170
|
+
`);if(e.line_number<1||e.line_number>t.length)return!1;let s=t[e.line_number-1];if(e.text_snippet&&e.text_snippet.trim()){let i=e.text_snippet.replace(/\.\.\./g,"").trim();if(i&&!s.includes(i))return!1}if(e.content_hash&&e.text_snippet&&e.text_snippet.trim()){let i=Ue(s,void 0,50);if(i&&We(i)!==e.content_hash)return!1}return!0}function je(n){let e=n.split(`
|
|
171
|
+
`),t=[];for(let s=0;s<e.length;s++){let r=e[s].match(/^(#{1,6})\s+(.+?)(\r)?$/);r&&t.push({heading:r[2].trim(),level:r[1].length,startLine:s+1})}return t}function Ft(n,e,t,s){if(!e)return[];let i=n.split(`
|
|
172
|
+
`),r=[],c=e.replace(/\.\.\./g,"").trim();for(let o=0;o<i.length;o++)if(i[o].includes(c)){let a=.5;t&&ne(i,o+1,-50).includes(t.substring(0,20))&&(a+=.25),s&&ne(i,o+1,50).includes(s.substring(0,20))&&(a+=.25),r.push({lineNumber:o+1,confidence:Math.min(a,1)})}return r.sort((o,d)=>d.confidence-o.confidence)}function kt(n,e){let t=n.length,s=e.length,i=Array(t+1).fill(null).map(()=>Array(s+1).fill(0));for(let r=0;r<=t;r++)i[r][0]=r;for(let r=0;r<=s;r++)i[0][r]=r;for(let r=1;r<=t;r++)for(let c=1;c<=s;c++){let o=n[r-1]===e[c-1]?0:1;i[r][c]=Math.min(i[r-1][c]+1,i[r][c-1]+1,i[r-1][c-1]+o)}return i[t][s]}function At(n,e,t=5){let s=je(n);if(s.length===0)return null;let i=e.toLowerCase().trim(),r=null,c=1/0;for(let o of s){let d=o.heading.toLowerCase().trim(),a=kt(i,d);a<c&&a<=t&&(c=a,r=o)}return r}function Be(n,e,t){if(Ct(e,t))return{...t,anchor_status:"valid",last_verified_at:new Date().toISOString()};if(t.section_heading&&t.line_offset!==void 0){let i=je(e).find(r=>r.heading===t.section_heading);if(i){let r=i.startLine+t.line_offset,c=e.split(`
|
|
173
|
+
`);if(r>0&&r<=c.length){let o=c[r-1];if(t.text_snippet){let d=t.text_snippet.replace(/\.\.\./g,"").trim();if(d&&o.includes(d))return{...z(e,r),anchor_status:"relocated",last_verified_at:new Date().toISOString(),original_location:t.original_location||{line_number:t.line_number||0,section_heading:t.section_heading}}}}}}if(t.text_snippet){let s=Ft(e,t.text_snippet,t.context_before,t.context_after);if(s.length>0&&s[0].confidence>=.7)return{...z(e,s[0].lineNumber),anchor_status:"relocated",last_verified_at:new Date().toISOString(),original_location:t.original_location||{line_number:t.line_number||0,section_heading:t.section_heading}}}if(t.section_heading&&t.line_offset!==void 0){let s=At(e,t.section_heading,5);if(s){let i=s.startLine+t.line_offset,r=e.split(`
|
|
174
|
+
`);if(i>0&&i<=r.length)return{...z(e,i),anchor_status:"relocated",last_verified_at:new Date().toISOString(),original_location:t.original_location||{line_number:t.line_number||0,section_heading:t.section_heading}}}}return{...t,anchor_status:"stale",last_verified_at:new Date().toISOString(),original_location:t.original_location||{line_number:t.line_number||0,section_heading:t.section_heading}}}function $t(n,e,t){let s=U(n),i;if(t&&e)try{i=De(t)}catch{}let r=Ut(s.content,e,i);return{data:s.data,content:s.content,raw:n,references:r}}function Pe(n,e,t){let s=W.readFileSync(n,"utf8");return $t(s,e,t)}function Mt(n,e){return n.substring(0,e).split(`
|
|
175
|
+
`).length}function Jt(n){return{section_heading:n.section_heading,section_level:n.section_level,line_number:n.line_number,line_offset:n.line_offset,text_snippet:n.text_snippet,context_before:n.context_before,context_after:n.context_after,content_hash:n.content_hash}}function Ut(n,e,t){let s=[],i=/\[\[(@)?([a-z]+-\d+)(?:\|([^\]]+))?\]\](?:\{\s*(?:type:\s*)?([a-z-]+)\s*\})?/gi,r;for(;(r=i.exec(n))!==null;){let c=r[1]==="@",o=r[2],d=r[3]?.trim(),a=r[4]?.trim(),u;try{let p=Mt(n,r.index),f=z(n,p,r.index);u=Jt(f)}catch{u=void 0}if(e){let p=null;try{T(e,o)&&(p="spec")}catch{}if(!p)try{E(e,o)&&(p="issue")}catch{}p&&s.push({match:r[0],id:o,type:p,index:r.index,displayText:d,relationshipType:a,anchor:u})}else{let p;if(t){let f=t.id_prefix.spec.toLowerCase(),g=t.id_prefix.issue.toLowerCase();c||o.toLowerCase().startsWith(g+"-")?p="issue":(o.toLowerCase().startsWith(f+"-"),p="spec")}else p=c||o.startsWith("issue-")?"issue":"spec";s.push({match:r[0],id:o,type:p,index:r.index,displayText:d,relationshipType:a,anchor:u})}}return s}function Wt(n,e){return U.stringify(e,n)}function jt(n,e){let t=U(n),s={...t.data,...e},i=Object.fromEntries(Object.entries(s).filter(([r,c])=>c!==void 0));return U.stringify(t.content,i)}function ge(n,e){let t=W.readFileSync(n,"utf8"),s=jt(t,e);W.writeFileSync(n,s,"utf8")}function Ds(n){return n.trimStart().startsWith("---")}function Bt(n,e){return Wt(n,e)}function He(n,e,t){let s=Bt(e,t);W.writeFileSync(n,s,"utf8")}function xs(n){return U(n).content}function Ts(n){return U(n).data}function Is(n){let e=[],t=n.match(/^## Spec Feedback Provided\s*$/m);if(!t)return e;let s=t.index+t[0].length,i=n.slice(s),r=i.match(/^## /m),c=r?i.slice(0,r.index):i,o=/^### (FB-\d+) → ([a-z]+-\d+)(?: \((.*?)\))?\s*\n\*\*Type:\*\* (.+?)\s*\n\*\*Location:\*\* (.*?)\s*\n\*\*Status:\*\* (.+?)\s*\n\n([\s\S]*?)(?=\n###|$)/gm,d;for(;(d=o.exec(c))!==null;){let[,a,u,p,f,g,S,b]=d,y=g.match(/(?:(.+?),\s+)?line (\d+)\s*([✓⚠✗])/),D={id:a,specId:u,specTitle:p||void 0,type:f.trim(),location:{section:y?.[1]?.trim(),line:y?.[2]?parseInt(y[2]):void 0,status:y?.[3]==="\u2713"?"valid":y?.[3]==="\u26A0"?"relocated":"stale"},status:S.trim(),content:b.trim(),createdAt:""},x=b.match(/\*\*Resolution:\*\* (.+)/);x&&(D.resolution=x[1].trim()),e.push(D)}return e}function Pt(n){if(n.length===0)return"";let e=`
|
|
176
|
+
## Spec Feedback Provided
|
|
177
|
+
|
|
178
|
+
`;for(let t of n){let s=t.location.status==="valid"?"\u2713":t.location.status==="relocated"?"\u26A0":"\u2717",i="";t.location.section&&t.location.line?i=`${t.location.section}, line ${t.location.line} ${s}`:t.location.line?i=`line ${t.location.line} ${s}`:i=`Unknown ${s}`;let r=t.specTitle?` (${t.specTitle})`:"";e+=`### ${t.id} \u2192 ${t.specId}${r}
|
|
179
|
+
`,e+=`**Type:** ${t.type}
|
|
180
|
+
`,e+=`**Location:** ${i}
|
|
181
|
+
`,e+=`**Status:** ${t.status}
|
|
182
|
+
|
|
183
|
+
`,e+=`${t.content}
|
|
184
|
+
`,t.resolution&&(e+=`
|
|
185
|
+
**Resolution:** ${t.resolution}
|
|
186
|
+
`),e+=`
|
|
187
|
+
`}return e}function Rs(n,e){let t=n.match(/^## Spec Feedback Provided\s*$/m);if(t){let i=t.index,c=n.slice(i).match(/^## /m);if(c&&c.index>0){let o=i+c.index;n=n.slice(0,i)+n.slice(o)}else n=n.slice(0,i)}let s=Pt(e);return s&&(n=n.trimEnd()+`
|
|
188
|
+
`+s),n}import*as j from"fs";import*as V from"path";w();v();B();function Ht(n){let{file_path:e,entity_type:t,...s}=n;return s}function qt(n,e,t,s,i,r){let c=new Date().toISOString(),o={...e};if(o.id||(o.id=t==="spec"?be(n,i):Se(n,i)),!o.title){let a=j.readFileSync(s,"utf8").match(/^#\s+(.+)$/m);o.title=a?a[1]:V.basename(s,".md")}return o.created_at||(o.created_at=c),o.updated_at||(o.updated_at=c),t==="spec"||o.status||(o.status="open"),!o.priority&&o.priority!==0&&(o.priority=2),o}async function Ms(n,e,t={}){let{outputDir:s=".sudocode",autoExport:i=!0,user:r="system",autoInitialize:c=!0,writeBackFrontmatter:o=!0}=t;try{let d=Pe(e,n,s),{data:a,content:u,references:p}=d,f=Yt(a,e),g=a.id,S=g,b=V.relative(s,e),y=b.startsWith("..")?V.relative(process.cwd(),e):b,D=null,x=null;if(f==="spec"){if(a.file_path=y,S&&(x=T(n,S)),D=xe(n,y),x)g=x.id,x.file_path!==y&&(console.log(`[sync] File renamed: ${x.file_path} \u2192 ${y}`),D&&D.id!==x.id&&console.warn(`[sync] Warning: File path conflict! Spec ${D.id} already exists at ${y}. Keeping ${x.id} and updating path.`));else if(D)g=D.id,a.id=g;else if(S&&D){let Q=D.id;console.warn(`[sync] Warning: ID in frontmatter (${S}) differs from existing spec ID (${Q}) for ${y}. Using existing ID.`),g=Q,a.id=g}}if(!g)if(c){if(a=qt(n,a,f,e,s,r),g=a.id,o){let Q=Ht(a);ge(e,Q)}}else return{success:!1,action:"no-change",entityId:"",entityType:f,error:"Missing id in frontmatter (auto-initialization disabled)"};let m=!(f==="spec"?T(n,g):E(n,g)),_=j.statSync(e),l=new Date(_.mtimeMs).toISOString();return f==="spec"?await zt(n,g,a,u,p,m,r,l):await Vt(n,g,a,u,p,m,r,l),i&&await le(n,{outputDir:s}),{success:!0,action:m?"created":"updated",entityId:g,entityType:f}}catch(d){return{success:!1,action:"no-change",entityId:"",entityType:"spec",error:d instanceof Error?d.message:String(d)}}}async function Js(n,e,t,s){try{let i=t==="spec"?T(n,e):E(n,e);if(!i)return{success:!1,action:"no-change",entityId:e,entityType:t,error:`${t} not found: ${e}`};let{getOutgoingRelationships:r}=await Promise.resolve().then(()=>(v(),re)),c=r(n,e,t),o=q(n,e,t),d=Qt(i,t,c,o),a=j.existsSync(s);if(a)ge(s,d);else{let u=i.content||"";He(s,d,u)}return{success:!0,action:a?"updated":"created",entityId:e,entityType:t}}catch(i){return{success:!1,action:"no-change",entityId:e,entityType:t,error:i instanceof Error?i.message:String(i)}}}function Yt(n,e){return n.entity_type==="issue"?"issue":n.entity_type==="spec"?"spec":e.includes("/issues/")||e.includes("/issue-")?"issue":(e.includes("/specs/")||e.includes("/spec-"),"spec")}async function zt(n,e,t,s,i,r,c,o){let d=r?null:T(n,e),a=d?.content||"",u={id:e,title:t.title||"Untitled",file_path:t.file_path||"",content:s,priority:t.priority??2,parent_id:t.parent_id||void 0};if(r)X(n,{...u,updated_at:o});else{let p=d?.title!==u.title||d?.content!==u.content||d?.priority!==u.priority||d?.parent_id!==u.parent_id||d?.file_path!==u.file_path;if(P(n,e,{...u,...p&&o?{updated_at:o}:{}}),a!==s){let f=J(n,{spec_id:e});if(f.length>0)for(let g of f){let S=typeof g.anchor=="string"?JSON.parse(g.anchor):g.anchor,b=Be(a,s,S);Ne(n,g.id,{anchor:b})}}}t.tags&&Array.isArray(t.tags)&&O(n,e,"spec",t.tags),await qe(n,e,"spec",i,t.relationships,c)}async function Vt(n,e,t,s,i,r,c,o){let d=r?null:E(n,e),a={id:e,title:t.title||"Untitled",content:s,status:t.status||"open",priority:t.priority??2,assignee:t.assignee||void 0,parent_id:t.parent_id||void 0};if(r)te(n,{...a,updated_at:o});else{let u=d?.title!==a.title||d?.content!==a.content||d?.status!==a.status||d?.priority!==a.priority||d?.assignee!==a.assignee||d?.parent_id!==a.parent_id;L(n,e,{...a,...u&&o?{updated_at:o}:{}})}t.tags&&Array.isArray(t.tags)&&O(n,e,"issue",t.tags),await qe(n,e,"issue",i,t.relationships,c)}async function qe(n,e,t,s,i,r="system"){let{getOutgoingRelationships:c}=await Promise.resolve().then(()=>(v(),re)),o=c(n,e,t),d=new Set(o.map(a=>`${a.relationship_type}:${a.to_type}:${a.to_id}`));for(let a of s){let u=a.relationshipType||"references",p=`${u}:${a.type}:${a.id}`;if(!d.has(p))try{I(n,{from_id:e,from_type:t,to_id:a.id,to_type:a.type,relationship_type:u,metadata:a.anchor?JSON.stringify({anchor:a.anchor}):void 0})}catch{}}if(i&&Array.isArray(i))for(let a of i){let u=`${a.relationship_type}:${a.target_type}:${a.target_id}`;if(!d.has(u))try{I(n,{from_id:e,from_type:t,to_id:a.target_id,to_type:a.target_type,relationship_type:a.relationship_type})}catch{}}}function Qt(n,e,t,s){let i={id:n.id,title:n.title,priority:n.priority,created_at:n.created_at};if(n.parent_id&&(i.parent_id=n.parent_id),s.length>0&&(i.tags=s),t.length>0&&(i.relationships=t),e==="spec")return i;{let r=n,c={...i,status:r.status};return r.assignee&&(c.assignee=r.assignee),r.closed_at&&(c.closed_at=r.closed_at),c}}export{pe as ExportDebouncer,Re as SavepointTransaction,Nn as addReferenceToContent,I as addRelationship,_t as addTag,ht as addTags,Wn as batchTransaction,gn as closeIssue,ls as countReferences,ss as createDebouncedExport,te as createIssue,Bt as createMarkdown,X as createSpec,wn as deleteEntityEvents,Ie as deleteIssue,qn as deleteJSONLLine,Yn as deleteJSONLLineSync,Te as deleteSpec,Fe as detectChanges,ke as detectCollisions,vt as exportIssuesToJSONL,Rt as exportSpecsToJSONL,le as exportToJSONL,Ut as extractCrossReferences,Pt as formatFeedbackForIssue,bt as formatReference,lt as getAllRelationships,xn as getAllTags,hn as getBlockedIssues,en as getDatabase,ut as getDependencies,pt as getDependents,Sn as getEntitiesByTag,Rn as getEntityEvents,Et as getEvent,On as getEventsByActor,Ts as getFrontmatter,H as getIncomingRelationships,E as getIssue,zn as getJSONLEntity,Vn as getJSONLEntitySync,k as getOutgoingRelationships,_n as getReadyIssues,vn as getRecentEvents,ee as getRelationship,T as getSpec,xe as getSpecByFilePath,q as getTags,Ds as hasFrontmatter,Dn as hasTag,gs as importFromJSONL,Me as importIssues,Ae as importSpecs,Ge as initDatabase,In as insertEvent,It as issueToJSONL,$ as listIssues,F as listSpecs,Is as parseFeedbackSection,$t as parseMarkdown,Pe as parseMarkdownFile,ae as queryEvents,M as readJSONL,de as readJSONLSync,ft as relationshipExists,A as removeAllRelationships,yt as removeAllTags,xs as removeFrontmatter,ot as removeRelationship,bn as removeTag,mn as reopenIssue,wt as resolveCollisions,yn as searchIssues,dn as searchSpecs,O as setTags,Tt as specToJSONL,Wt as stringifyMarkdown,Js as syncJSONLToMarkdown,Ms as syncMarkdownToJSONL,oe as transaction,Rs as updateFeedbackInIssue,jt as updateFrontmatter,ge as updateFrontmatterFile,L as updateIssue,Pn as updateJSONLLine,Hn as updateJSONLLineSync,P as updateSpec,fs as updateTextReferences,jn as withRetry,tn as withTransaction,Y as writeJSONL,Oe as writeJSONLSync,He as writeMarkdownFile};
|
|
189
|
+
//# sourceMappingURL=index.js.map
|
package/dist/jsonl.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSONL (JSON Lines) reader and writer
|
|
3
|
+
* Supports reading and writing .jsonl files for specs and issues
|
|
4
|
+
*/
|
|
5
|
+
import type { SpecJSONL, IssueJSONL } from "./types.js";
|
|
6
|
+
export type JSONLEntity = SpecJSONL | IssueJSONL | Record<string, any>;
|
|
7
|
+
export interface ReadJSONLOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Skip malformed lines instead of throwing
|
|
10
|
+
*/
|
|
11
|
+
skipErrors?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Custom error handler for malformed lines
|
|
14
|
+
*/
|
|
15
|
+
onError?: (lineNumber: number, line: string, error: Error) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface WriteJSONLOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Use atomic write (write to temp file, then rename)
|
|
20
|
+
*/
|
|
21
|
+
atomic?: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Read a JSONL file and parse all lines
|
|
25
|
+
* Uses streaming for memory efficiency with large files
|
|
26
|
+
*/
|
|
27
|
+
export declare function readJSONL<T extends JSONLEntity = JSONLEntity>(filePath: string, options?: ReadJSONLOptions): Promise<T[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Read a JSONL file synchronously (for smaller files)
|
|
30
|
+
*/
|
|
31
|
+
export declare function readJSONLSync<T extends JSONLEntity = JSONLEntity>(filePath: string, options?: ReadJSONLOptions): T[];
|
|
32
|
+
/**
|
|
33
|
+
* Write entities to a JSONL file
|
|
34
|
+
* Each entity is written as a single line of JSON
|
|
35
|
+
* Entities are sorted by created_at date to minimize merge conflicts
|
|
36
|
+
*/
|
|
37
|
+
export declare function writeJSONL<T extends JSONLEntity = JSONLEntity>(filePath: string, entities: T[], options?: WriteJSONLOptions): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Write entities to a JSONL file synchronously
|
|
40
|
+
* Entities are sorted by created_at date to minimize merge conflicts
|
|
41
|
+
*/
|
|
42
|
+
export declare function writeJSONLSync<T extends JSONLEntity = JSONLEntity>(filePath: string, entities: T[], options?: WriteJSONLOptions): void;
|
|
43
|
+
/**
|
|
44
|
+
* Update a single line in a JSONL file by entity ID
|
|
45
|
+
* If the entity doesn't exist, append it
|
|
46
|
+
* If it exists, replace the line
|
|
47
|
+
*/
|
|
48
|
+
export declare function updateJSONLLine<T extends JSONLEntity = JSONLEntity>(filePath: string, entity: T, idField?: string): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Update a single line in a JSONL file synchronously
|
|
51
|
+
*/
|
|
52
|
+
export declare function updateJSONLLineSync<T extends JSONLEntity = JSONLEntity>(filePath: string, entity: T, idField?: string): void;
|
|
53
|
+
/**
|
|
54
|
+
* Delete an entity from a JSONL file by ID
|
|
55
|
+
*/
|
|
56
|
+
export declare function deleteJSONLLine<T extends JSONLEntity = JSONLEntity>(filePath: string, entityId: string, idField?: string): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Delete an entity from a JSONL file synchronously
|
|
59
|
+
*/
|
|
60
|
+
export declare function deleteJSONLLineSync<T extends JSONLEntity = JSONLEntity>(filePath: string, entityId: string, idField?: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Get a single entity from a JSONL file by ID
|
|
63
|
+
*/
|
|
64
|
+
export declare function getJSONLEntity<T extends JSONLEntity = JSONLEntity>(filePath: string, entityId: string, idField?: string): Promise<T | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Get a single entity from a JSONL file synchronously
|
|
67
|
+
*/
|
|
68
|
+
export declare function getJSONLEntitySync<T extends JSONLEntity = JSONLEntity>(filePath: string, entityId: string, idField?: string): T | null;
|
|
69
|
+
//# sourceMappingURL=jsonl.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown parser with frontmatter support
|
|
3
|
+
*/
|
|
4
|
+
import type Database from "better-sqlite3";
|
|
5
|
+
import type { LocationAnchor, Config } from "@sudocode-ai/types";
|
|
6
|
+
export interface ParsedMarkdown<T extends object = Record<string, any>> {
|
|
7
|
+
/**
|
|
8
|
+
* Parsed frontmatter data
|
|
9
|
+
*/
|
|
10
|
+
data: T;
|
|
11
|
+
/**
|
|
12
|
+
* Markdown content (without frontmatter)
|
|
13
|
+
*/
|
|
14
|
+
content: string;
|
|
15
|
+
/**
|
|
16
|
+
* Original raw content
|
|
17
|
+
*/
|
|
18
|
+
raw: string;
|
|
19
|
+
/**
|
|
20
|
+
* Cross-references found in content
|
|
21
|
+
*/
|
|
22
|
+
references: CrossReference[];
|
|
23
|
+
}
|
|
24
|
+
export interface CrossReference {
|
|
25
|
+
/**
|
|
26
|
+
* The full matched text (e.g., "[[spec-001]]" or "[[@issue-042]]")
|
|
27
|
+
*/
|
|
28
|
+
match: string;
|
|
29
|
+
/**
|
|
30
|
+
* The entity ID (e.g., "spec-001" or "issue-042")
|
|
31
|
+
*/
|
|
32
|
+
id: string;
|
|
33
|
+
/**
|
|
34
|
+
* Entity type (spec or issue)
|
|
35
|
+
*/
|
|
36
|
+
type: "spec" | "issue";
|
|
37
|
+
/**
|
|
38
|
+
* Position in content
|
|
39
|
+
*/
|
|
40
|
+
index: number;
|
|
41
|
+
/**
|
|
42
|
+
* Optional display text (e.g., "Authentication" from "[[spec-001|Authentication]]")
|
|
43
|
+
*/
|
|
44
|
+
displayText?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional relationship type (e.g., "blocks" from "[[spec-001]]{ blocks }")
|
|
47
|
+
* Defaults to "references" if not specified
|
|
48
|
+
*/
|
|
49
|
+
relationshipType?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Optional location anchor for spatial context of the reference
|
|
52
|
+
*/
|
|
53
|
+
anchor?: LocationAnchor;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Parse markdown file with YAML frontmatter
|
|
57
|
+
* @param content - Markdown content to parse
|
|
58
|
+
* @param db - Optional database for validating cross-references
|
|
59
|
+
* @param outputDir - Optional output directory for loading config metadata
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseMarkdown<T extends object = Record<string, any>>(content: string, db?: Database.Database, outputDir?: string): ParsedMarkdown<T>;
|
|
62
|
+
/**
|
|
63
|
+
* Parse markdown file from disk
|
|
64
|
+
* @param filePath - Path to markdown file
|
|
65
|
+
* @param db - Optional database for validating cross-references
|
|
66
|
+
* @param outputDir - Optional output directory for loading config metadata
|
|
67
|
+
*/
|
|
68
|
+
export declare function parseMarkdownFile<T extends object = Record<string, any>>(filePath: string, db?: Database.Database, outputDir?: string): ParsedMarkdown<T>;
|
|
69
|
+
/**
|
|
70
|
+
* Extract cross-references from markdown content
|
|
71
|
+
* Supports formats:
|
|
72
|
+
* - [[entity-001]] - entity reference (type determined by database lookup)
|
|
73
|
+
* - [[@entity-042]] - entity reference with @ prefix (for clarity)
|
|
74
|
+
* - [[entity-001|Display Text]] - with custom display text
|
|
75
|
+
* - [[entity-001]]{ blocks } - with relationship type (shorthand)
|
|
76
|
+
* - [[entity-001]]{ type: blocks } - with relationship type (explicit)
|
|
77
|
+
* - [[entity-001|Display]]{ blocks } - combination of display text and type
|
|
78
|
+
*
|
|
79
|
+
* If db is provided, validates references against the database and determines entity type.
|
|
80
|
+
* Only returns references to entities that actually exist.
|
|
81
|
+
*
|
|
82
|
+
* If metadata is provided (but no db), uses configured ID prefixes for type detection.
|
|
83
|
+
*/
|
|
84
|
+
export declare function extractCrossReferences(content: string, db?: Database.Database, config?: Config): CrossReference[];
|
|
85
|
+
/**
|
|
86
|
+
* Stringify frontmatter and content back to markdown
|
|
87
|
+
*/
|
|
88
|
+
export declare function stringifyMarkdown<T extends object = Record<string, any>>(data: T, content: string): string;
|
|
89
|
+
/**
|
|
90
|
+
* Update frontmatter in an existing markdown file
|
|
91
|
+
* Preserves content unchanged
|
|
92
|
+
*/
|
|
93
|
+
export declare function updateFrontmatter<T extends object = Record<string, any>>(originalContent: string, updates: Partial<T>): string;
|
|
94
|
+
/**
|
|
95
|
+
* Update frontmatter in a file
|
|
96
|
+
*/
|
|
97
|
+
export declare function updateFrontmatterFile<T extends object = Record<string, any>>(filePath: string, updates: Partial<T>): void;
|
|
98
|
+
/**
|
|
99
|
+
* Check if a file has frontmatter
|
|
100
|
+
*/
|
|
101
|
+
export declare function hasFrontmatter(content: string): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Create markdown with frontmatter
|
|
104
|
+
*/
|
|
105
|
+
export declare function createMarkdown<T extends object = Record<string, any>>(data: T, content: string): string;
|
|
106
|
+
/**
|
|
107
|
+
* Write markdown file with frontmatter
|
|
108
|
+
*/
|
|
109
|
+
export declare function writeMarkdownFile<T extends object = Record<string, any>>(filePath: string, data: T, content: string): void;
|
|
110
|
+
/**
|
|
111
|
+
* Remove frontmatter from markdown content
|
|
112
|
+
*/
|
|
113
|
+
export declare function removeFrontmatter(content: string): string;
|
|
114
|
+
/**
|
|
115
|
+
* Get only frontmatter data from markdown
|
|
116
|
+
*/
|
|
117
|
+
export declare function getFrontmatter<T extends object = Record<string, any>>(content: string): T;
|
|
118
|
+
export interface FeedbackMarkdownData {
|
|
119
|
+
id: string;
|
|
120
|
+
specId: string;
|
|
121
|
+
specTitle?: string;
|
|
122
|
+
type: string;
|
|
123
|
+
location: {
|
|
124
|
+
section?: string;
|
|
125
|
+
line?: number;
|
|
126
|
+
status: "valid" | "relocated" | "stale";
|
|
127
|
+
};
|
|
128
|
+
status: string;
|
|
129
|
+
content: string;
|
|
130
|
+
createdAt: string;
|
|
131
|
+
resolution?: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Parse feedback section from issue markdown content
|
|
135
|
+
* Looks for "## Spec Feedback Provided" section
|
|
136
|
+
*/
|
|
137
|
+
export declare function parseFeedbackSection(content: string): FeedbackMarkdownData[];
|
|
138
|
+
/**
|
|
139
|
+
* Format feedback data for inclusion in issue markdown
|
|
140
|
+
*/
|
|
141
|
+
export declare function formatFeedbackForIssue(feedback: FeedbackMarkdownData[]): string;
|
|
142
|
+
/**
|
|
143
|
+
* Append or update feedback section in issue markdown
|
|
144
|
+
*/
|
|
145
|
+
export declare function updateFeedbackInIssue(issueContent: string, feedback: FeedbackMarkdownData[]): string;
|
|
146
|
+
//# sourceMappingURL=markdown.d.ts.map
|