spiracha 1.0.0 → 1.1.1

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.
Files changed (92) hide show
  1. package/AGENTS.md +31 -1
  2. package/README.md +61 -7
  3. package/apps/ui/AGENTS.md +70 -0
  4. package/apps/ui/README.md +72 -0
  5. package/apps/ui/dist/client/assets/_threadId-CAIeH5mq.js +1 -0
  6. package/apps/ui/dist/client/assets/analytics-CqWZmyV6.js +1 -0
  7. package/apps/ui/dist/client/assets/checkbox-DXM4lkJq.js +1 -0
  8. package/apps/ui/dist/client/assets/data-table-DnPYMPCD.js +4 -0
  9. package/apps/ui/dist/client/assets/delete-confirm-dialog-CcZaRX33.js +11 -0
  10. package/apps/ui/dist/client/assets/download-DOwxk-cG.js +1 -0
  11. package/apps/ui/dist/client/assets/es2015-Bm0kEzx2.js +41 -0
  12. package/apps/ui/dist/client/assets/formatters-C12LmYaa.js +1 -0
  13. package/apps/ui/dist/client/assets/index-DdJ7ahIt.js +22 -0
  14. package/apps/ui/dist/client/assets/input-CEsI7EpI.js +1 -0
  15. package/apps/ui/dist/client/assets/metric-card-9jwBF7rG.js +1 -0
  16. package/apps/ui/dist/client/assets/page-header-Dr_h1CVv.js +1 -0
  17. package/apps/ui/dist/client/assets/projects._project-uyNGnpjH.js +1 -0
  18. package/apps/ui/dist/client/assets/projects._project-zoM8d2nH.js +1 -0
  19. package/apps/ui/dist/client/assets/projects.index-D1CWVN-O.js +1 -0
  20. package/apps/ui/dist/client/assets/projects.index-DukMuny6.js +1 -0
  21. package/apps/ui/dist/client/assets/routes-Gr2Wwh83.js +1 -0
  22. package/apps/ui/dist/client/assets/select-CFim44gT.js +1 -0
  23. package/apps/ui/dist/client/assets/settings-DqhyDxo2.js +1 -0
  24. package/apps/ui/dist/client/assets/styles-CMrP9Jb4.css +1 -0
  25. package/apps/ui/dist/client/assets/threads._threadId-DT75NiBa.js +1 -0
  26. package/apps/ui/dist/client/assets/threads._threadId-Df5VXIuZ.js +7 -0
  27. package/apps/ui/dist/client/favicon.ico +0 -0
  28. package/apps/ui/dist/client/logo192.png +0 -0
  29. package/apps/ui/dist/client/logo512.png +0 -0
  30. package/apps/ui/dist/client/manifest.json +25 -0
  31. package/apps/ui/dist/client/robots.txt +3 -0
  32. package/apps/ui/dist/server/assets/__23tanstack-start-plugin-adapters-BzCA6dXo.js +5 -0
  33. package/apps/ui/dist/server/assets/_tanstack-start-manifest_v-C0V305Nt.js +99 -0
  34. package/apps/ui/dist/server/assets/_threadId-B6SrBR9E.js +6 -0
  35. package/apps/ui/dist/server/assets/analytics-BMxW_bZL.js +139 -0
  36. package/apps/ui/dist/server/assets/button-CmTDnzOn.js +46 -0
  37. package/apps/ui/dist/server/assets/checkbox-C0hovF41.js +19 -0
  38. package/apps/ui/dist/server/assets/codex-queries-CAF6HYiG.js +109 -0
  39. package/apps/ui/dist/server/assets/codex-server-BFZq2Y2O.js +2062 -0
  40. package/apps/ui/dist/server/assets/data-table-Cdct823O.js +189 -0
  41. package/apps/ui/dist/server/assets/delete-confirm-dialog-CWqcTXTF.js +139 -0
  42. package/apps/ui/dist/server/assets/download-C5rkk_Bo.js +289 -0
  43. package/apps/ui/dist/server/assets/formatters-FJaGZgJk.js +91 -0
  44. package/apps/ui/dist/server/assets/input-B4tEzctc.js +46 -0
  45. package/apps/ui/dist/server/assets/loading-panel-DbLdvjtR.js +27 -0
  46. package/apps/ui/dist/server/assets/metric-card-ByEeLu0r.js +23 -0
  47. package/apps/ui/dist/server/assets/model-label-B1NWGc65.js +13 -0
  48. package/apps/ui/dist/server/assets/page-header-CxdZM86z.js +25 -0
  49. package/apps/ui/dist/server/assets/path-transforms-DL2IwtYd.js +31 -0
  50. package/apps/ui/dist/server/assets/projects._project-CJ7l0ynC.js +18 -0
  51. package/apps/ui/dist/server/assets/projects._project-CLSohrBp.js +26 -0
  52. package/apps/ui/dist/server/assets/projects._project-CcJLp_A8.js +337 -0
  53. package/apps/ui/dist/server/assets/projects.index-CaplpeMy.js +26 -0
  54. package/apps/ui/dist/server/assets/projects.index-srtogpuF.js +172 -0
  55. package/apps/ui/dist/server/assets/router-C_w-haH6.js +307 -0
  56. package/apps/ui/dist/server/assets/routes-BhbxvJE7.js +34 -0
  57. package/apps/ui/dist/server/assets/routes-CPe-ppmC.js +169 -0
  58. package/apps/ui/dist/server/assets/select-GW76p-ld.js +76 -0
  59. package/apps/ui/dist/server/assets/settings-MvWDgc1u.js +100 -0
  60. package/apps/ui/dist/server/assets/settings-store-DpEJEQ7M.js +52 -0
  61. package/apps/ui/dist/server/assets/sqlite-error-LZDrnxdd.js +13 -0
  62. package/apps/ui/dist/server/assets/start-HeKLHD9b.js +4 -0
  63. package/apps/ui/dist/server/assets/threads._threadId-BSSK4nkI.js +26 -0
  64. package/apps/ui/dist/server/assets/threads._threadId-Ba7vv6-K.js +18 -0
  65. package/apps/ui/dist/server/assets/threads._threadId-euyNckhj.js +1059 -0
  66. package/apps/ui/dist/server/assets/utils-C_uf36nf.js +8 -0
  67. package/apps/ui/dist/server/server.js +5678 -0
  68. package/package.json +53 -7
  69. package/src/export-chats.ts +4 -18
  70. package/src/lib/claude-exporter.ts +1 -1
  71. package/src/lib/codex-analytics.ts +100 -0
  72. package/src/lib/codex-browser-db.ts +605 -0
  73. package/src/lib/codex-browser-export.ts +429 -0
  74. package/src/lib/codex-browser-types.ts +224 -0
  75. package/src/lib/codex-exporter-cli.ts +6 -1
  76. package/src/lib/codex-exporter-db.ts +19 -20
  77. package/src/lib/codex-exporter-transcript.ts +158 -34
  78. package/src/lib/codex-exporter-types.ts +8 -0
  79. package/src/lib/codex-thread-cache.ts +58 -0
  80. package/src/lib/codex-thread-parser.ts +604 -0
  81. package/src/lib/interactive-cli.ts +10 -25
  82. package/src/lib/model-label.ts +24 -0
  83. package/src/lib/native-open.ts +54 -0
  84. package/src/lib/path-transforms.ts +46 -0
  85. package/src/lib/shared.ts +15 -1
  86. package/src/lib/sqlite-error.ts +14 -0
  87. package/src/lib/sqlite-retry.ts +53 -0
  88. package/src/lib/ui-cache.ts +96 -0
  89. package/src/lib/ui-export-files.ts +77 -0
  90. package/src/mcp-server.ts +1 -0
  91. package/src/spiracha.ts +16 -4
  92. package/src/ui-cli.ts +310 -0
@@ -0,0 +1,76 @@
1
+ import { t as cn } from "./utils-C_uf36nf.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react";
4
+ import { Select } from "radix-ui";
5
+ //#region src/components/ui/select.tsx
6
+ function Select$1({ ...props }) {
7
+ return /* @__PURE__ */ jsx(Select.Root, {
8
+ "data-slot": "select",
9
+ ...props
10
+ });
11
+ }
12
+ function SelectValue({ ...props }) {
13
+ return /* @__PURE__ */ jsx(Select.Value, {
14
+ "data-slot": "select-value",
15
+ ...props
16
+ });
17
+ }
18
+ function SelectTrigger({ className, size = "default", children, ...props }) {
19
+ return /* @__PURE__ */ jsxs(Select.Trigger, {
20
+ "data-slot": "select-trigger",
21
+ "data-size": size,
22
+ className: cn("flex w-fit items-center justify-between gap-2 whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-xs outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[size=default]:h-9 data-[size=sm]:h-8 data-[placeholder]:text-muted-foreground *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:hover:bg-input/50 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0", className),
23
+ ...props,
24
+ children: [children, /* @__PURE__ */ jsx(Select.Icon, {
25
+ asChild: true,
26
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4 opacity-50" })
27
+ })]
28
+ });
29
+ }
30
+ function SelectContent({ className, children, position = "item-aligned", align = "center", ...props }) {
31
+ return /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsxs(Select.Content, {
32
+ "data-slot": "select-content",
33
+ className: cn("data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=closed]:animate-out data-[state=open]:animate-in", position === "popper" && "data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1", className),
34
+ position,
35
+ align,
36
+ ...props,
37
+ children: [
38
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
39
+ /* @__PURE__ */ jsx(Select.Viewport, {
40
+ className: cn("p-1", position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"),
41
+ children
42
+ }),
43
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
44
+ ]
45
+ }) });
46
+ }
47
+ function SelectItem({ className, children, ...props }) {
48
+ return /* @__PURE__ */ jsxs(Select.Item, {
49
+ "data-slot": "select-item",
50
+ className: cn("relative flex w-full cursor-default select-none items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className),
51
+ ...props,
52
+ children: [/* @__PURE__ */ jsx("span", {
53
+ "data-slot": "select-item-indicator",
54
+ className: "absolute right-2 flex size-3.5 items-center justify-center",
55
+ children: /* @__PURE__ */ jsx(Select.ItemIndicator, { children: /* @__PURE__ */ jsx(CheckIcon, { className: "size-4" }) })
56
+ }), /* @__PURE__ */ jsx(Select.ItemText, { children })]
57
+ });
58
+ }
59
+ function SelectScrollUpButton({ className, ...props }) {
60
+ return /* @__PURE__ */ jsx(Select.ScrollUpButton, {
61
+ "data-slot": "select-scroll-up-button",
62
+ className: cn("flex cursor-default items-center justify-center py-1", className),
63
+ ...props,
64
+ children: /* @__PURE__ */ jsx(ChevronUpIcon, { className: "size-4" })
65
+ });
66
+ }
67
+ function SelectScrollDownButton({ className, ...props }) {
68
+ return /* @__PURE__ */ jsx(Select.ScrollDownButton, {
69
+ "data-slot": "select-scroll-down-button",
70
+ className: cn("flex cursor-default items-center justify-center py-1", className),
71
+ ...props,
72
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-4" })
73
+ });
74
+ }
75
+ //#endregion
76
+ export { SelectValue as a, SelectTrigger as i, SelectContent as n, SelectItem as r, Select$1 as t };
@@ -0,0 +1,100 @@
1
+ import { n as useSettings } from "./settings-store-DpEJEQ7M.js";
2
+ import { t as Checkbox } from "./checkbox-C0hovF41.js";
3
+ import { t as PageHeader } from "./page-header-CxdZM86z.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { Settings2 } from "lucide-react";
6
+ //#region src/routes/settings.tsx?tsr-split=component
7
+ function SettingsPage() {
8
+ const { settings, updateSetting } = useSettings();
9
+ return /* @__PURE__ */ jsxs("div", {
10
+ className: "space-y-6",
11
+ children: [/* @__PURE__ */ jsx(PageHeader, {
12
+ eyebrow: "Configuration",
13
+ subtitle: "Control how paths and usernames appear in transcript messages.",
14
+ title: "Settings"
15
+ }), /* @__PURE__ */ jsxs("section", {
16
+ className: "rounded-[1.6rem] border border-[var(--border)] bg-[var(--panel)] p-5 shadow-[var(--panel-shadow)]",
17
+ children: [/* @__PURE__ */ jsxs("h3", {
18
+ className: "flex items-center gap-2 font-semibold text-[var(--muted-foreground)] text-xs uppercase tracking-[0.18em]",
19
+ children: [/* @__PURE__ */ jsx(Settings2, { className: "size-3.5" }), "Privacy"]
20
+ }), /* @__PURE__ */ jsxs("div", {
21
+ className: "mt-5 space-y-5",
22
+ children: [/* @__PURE__ */ jsxs("div", {
23
+ className: "flex items-center gap-3",
24
+ children: [/* @__PURE__ */ jsx(Checkbox, {
25
+ checked: settings.redactUsername,
26
+ id: "redact-username",
27
+ onCheckedChange: (checked) => updateSetting("redactUsername", checked === true)
28
+ }), /* @__PURE__ */ jsxs("div", {
29
+ className: "min-w-0",
30
+ children: [/* @__PURE__ */ jsx("label", {
31
+ className: "cursor-pointer font-medium text-sm leading-none",
32
+ htmlFor: "redact-username",
33
+ children: "Redact Username"
34
+ }), /* @__PURE__ */ jsxs("p", {
35
+ className: "mt-1.5 text-[var(--muted-foreground)] text-xs leading-5",
36
+ children: [
37
+ "Replaces",
38
+ " ",
39
+ /* @__PURE__ */ jsx("code", {
40
+ className: "rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]",
41
+ children: "/Users/[username]/"
42
+ }),
43
+ " ",
44
+ "with",
45
+ " ",
46
+ /* @__PURE__ */ jsx("code", {
47
+ className: "rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]",
48
+ children: "~/"
49
+ }),
50
+ " ",
51
+ "in all transcript messages."
52
+ ]
53
+ })]
54
+ })]
55
+ }), /* @__PURE__ */ jsxs("div", {
56
+ className: "flex items-center gap-3",
57
+ children: [/* @__PURE__ */ jsx(Checkbox, {
58
+ checked: settings.convertToProjectRoot,
59
+ id: "convert-project-root",
60
+ onCheckedChange: (checked) => updateSetting("convertToProjectRoot", checked === true)
61
+ }), /* @__PURE__ */ jsxs("div", {
62
+ className: "min-w-0",
63
+ children: [/* @__PURE__ */ jsx("label", {
64
+ className: "cursor-pointer font-medium text-sm leading-none",
65
+ htmlFor: "convert-project-root",
66
+ children: "Convert Absolute Paths to Project Root"
67
+ }), /* @__PURE__ */ jsxs("p", {
68
+ className: "mt-1.5 text-[var(--muted-foreground)] text-xs leading-5",
69
+ children: [
70
+ "Converts the current thread or project cwd from",
71
+ " ",
72
+ /* @__PURE__ */ jsx("code", {
73
+ className: "rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]",
74
+ children: "/Users/[username]/workspace/[projectname]/"
75
+ }),
76
+ " ",
77
+ "to project-root-relative paths, so",
78
+ " ",
79
+ /* @__PURE__ */ jsx("code", {
80
+ className: "rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]",
81
+ children: "/Users/jane/workspace/myapp/src/index.ts"
82
+ }),
83
+ " ",
84
+ "appear as",
85
+ " ",
86
+ /* @__PURE__ */ jsx("code", {
87
+ className: "rounded bg-[var(--code-background)] px-1 py-0.5 font-mono text-[var(--code-foreground)]",
88
+ children: "src/index.ts"
89
+ }),
90
+ ". If Redact Username is also enabled, remaining absolute paths outside the project root are redacted afterward."
91
+ ]
92
+ })]
93
+ })]
94
+ })]
95
+ })]
96
+ })]
97
+ });
98
+ }
99
+ //#endregion
100
+ export { SettingsPage as component };
@@ -0,0 +1,52 @@
1
+ import { createContext, useContext, useEffect, useState } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region src/lib/settings-store.tsx
4
+ var STORAGE_KEY = "spiracha-settings";
5
+ var defaultSettings = {
6
+ convertToProjectRoot: false,
7
+ redactUsername: false
8
+ };
9
+ var SettingsContext = createContext({
10
+ settings: defaultSettings,
11
+ updateSetting: () => {}
12
+ });
13
+ var loadSettings = () => {
14
+ if (typeof window === "undefined") return defaultSettings;
15
+ try {
16
+ const stored = window.localStorage.getItem(STORAGE_KEY);
17
+ return stored ? {
18
+ ...defaultSettings,
19
+ ...JSON.parse(stored)
20
+ } : defaultSettings;
21
+ } catch {
22
+ return defaultSettings;
23
+ }
24
+ };
25
+ function SettingsProvider({ children }) {
26
+ const [settings, setSettings] = useState(defaultSettings);
27
+ useEffect(() => {
28
+ setSettings(loadSettings());
29
+ }, []);
30
+ const updateSetting = (key, value) => {
31
+ setSettings((prev) => {
32
+ const next = {
33
+ ...prev,
34
+ [key]: value
35
+ };
36
+ try {
37
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
38
+ } catch {}
39
+ return next;
40
+ });
41
+ };
42
+ return /* @__PURE__ */ jsx(SettingsContext.Provider, {
43
+ value: {
44
+ settings,
45
+ updateSetting
46
+ },
47
+ children
48
+ });
49
+ }
50
+ var useSettings = () => useContext(SettingsContext);
51
+ //#endregion
52
+ export { useSettings as n, SettingsProvider as t };
@@ -0,0 +1,13 @@
1
+ //#region ../../src/lib/sqlite-error.ts
2
+ var SQLITE_RETRYABLE_PATTERNS = [
3
+ /unable to open database file/iu,
4
+ /database is locked/iu,
5
+ /SQLITE_BUSY/iu,
6
+ /SQLITE_CANTOPEN/iu
7
+ ];
8
+ var isRetryableSqliteError = (error) => {
9
+ if (!(error instanceof Error)) return false;
10
+ return SQLITE_RETRYABLE_PATTERNS.some((pattern) => pattern.test(error.message));
11
+ };
12
+ //#endregion
13
+ export { isRetryableSqliteError as t };
@@ -0,0 +1,4 @@
1
+ //#region ../../node_modules/.bun/@tanstack+react-start@1.168.11+5cefe02f681c7619/node_modules/@tanstack/react-start/dist/plugin/default-entry/start.ts
2
+ var startInstance = void 0;
3
+ //#endregion
4
+ export { startInstance };
@@ -0,0 +1,26 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ //#region src/routes/threads.$threadId.tsx?tsr-split=errorComponent
3
+ function ThreadErrorComponent({ error }) {
4
+ const isSqlite = error.message.includes("unable to open database") || error.message.includes("database is locked");
5
+ return /* @__PURE__ */ jsxs("div", {
6
+ className: "rounded-xl border border-[var(--border)] bg-[var(--panel)] px-6 py-10 text-center",
7
+ children: [
8
+ /* @__PURE__ */ jsx("p", {
9
+ className: "font-medium text-[var(--destructive)] text-sm",
10
+ children: isSqlite ? "Database unavailable" : "Failed to load thread"
11
+ }),
12
+ /* @__PURE__ */ jsx("p", {
13
+ className: "mt-2 text-[var(--muted-foreground)] text-sm",
14
+ children: isSqlite ? "Codex may have an exclusive lock on the database. Reload to retry." : error.message
15
+ }),
16
+ /* @__PURE__ */ jsx("button", {
17
+ className: "mt-4 text-[var(--accent)] text-sm underline-offset-2 hover:underline",
18
+ type: "button",
19
+ onClick: () => window.location.reload(),
20
+ children: "Reload"
21
+ })
22
+ ]
23
+ });
24
+ }
25
+ //#endregion
26
+ export { ThreadErrorComponent as errorComponent };
@@ -0,0 +1,18 @@
1
+ import { a as threadSnapshotQueryOptions } from "./codex-queries-CAF6HYiG.js";
2
+ import { t as LoadingPanel } from "./loading-panel-DbLdvjtR.js";
3
+ import { createFileRoute, lazyRouteComponent } from "@tanstack/react-router";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/routes/threads.$threadId.tsx
6
+ var $$splitErrorComponentImporter = () => import("./threads._threadId-BSSK4nkI.js");
7
+ var $$splitComponentImporter = () => import("./threads._threadId-euyNckhj.js");
8
+ var Route = createFileRoute("/threads/$threadId")({
9
+ component: lazyRouteComponent($$splitComponentImporter, "component"),
10
+ errorComponent: lazyRouteComponent($$splitErrorComponentImporter, "errorComponent"),
11
+ loader: ({ context, params }) => context.queryClient.ensureQueryData(threadSnapshotQueryOptions(params.threadId)),
12
+ pendingComponent: () => /* @__PURE__ */ jsx(LoadingPanel, {
13
+ description: "Loading the transcript, metadata, and parsed event stream for this thread.",
14
+ title: "Loading thread"
15
+ })
16
+ });
17
+ //#endregion
18
+ export { Route as t };