stagent 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.
Files changed (333) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +399 -0
  3. package/components.json +21 -0
  4. package/dist/cli.js +171 -0
  5. package/drizzle.config.ts +12 -0
  6. package/next.config.mjs +15 -0
  7. package/package.json +114 -0
  8. package/postcss.config.mjs +8 -0
  9. package/public/icon-512.png +0 -0
  10. package/public/icon.svg +13 -0
  11. package/public/readme/home-workspace.png +0 -0
  12. package/public/readme/inbox-approvals.png +0 -0
  13. package/public/readme/workflow-blueprints.png +0 -0
  14. package/public/stagent-s-128.png +0 -0
  15. package/public/stagent-s-64.png +0 -0
  16. package/src/app/api/blueprints/[id]/instantiate/route.ts +27 -0
  17. package/src/app/api/blueprints/[id]/route.ts +39 -0
  18. package/src/app/api/blueprints/import/route.ts +68 -0
  19. package/src/app/api/blueprints/route.ts +29 -0
  20. package/src/app/api/command-palette/recent/route.ts +31 -0
  21. package/src/app/api/data/clear/route.ts +22 -0
  22. package/src/app/api/data/seed/route.ts +22 -0
  23. package/src/app/api/documents/[id]/file/route.ts +44 -0
  24. package/src/app/api/documents/[id]/route.ts +123 -0
  25. package/src/app/api/documents/route.ts +59 -0
  26. package/src/app/api/logs/stream/route.ts +101 -0
  27. package/src/app/api/notifications/[id]/route.ts +36 -0
  28. package/src/app/api/notifications/mark-all-read/route.ts +13 -0
  29. package/src/app/api/notifications/pending-approvals/route.ts +10 -0
  30. package/src/app/api/notifications/pending-approvals/stream/route.ts +101 -0
  31. package/src/app/api/notifications/route.ts +34 -0
  32. package/src/app/api/permissions/route.ts +46 -0
  33. package/src/app/api/profiles/[id]/route.ts +79 -0
  34. package/src/app/api/profiles/[id]/test/route.ts +42 -0
  35. package/src/app/api/profiles/import/route.ts +108 -0
  36. package/src/app/api/profiles/route.ts +50 -0
  37. package/src/app/api/projects/[id]/route.ts +72 -0
  38. package/src/app/api/projects/route.ts +53 -0
  39. package/src/app/api/schedules/[id]/route.ts +185 -0
  40. package/src/app/api/schedules/route.ts +117 -0
  41. package/src/app/api/settings/budgets/route.ts +24 -0
  42. package/src/app/api/settings/openai/route.ts +24 -0
  43. package/src/app/api/settings/route.ts +21 -0
  44. package/src/app/api/settings/test/route.ts +26 -0
  45. package/src/app/api/tasks/[id]/cancel/route.ts +21 -0
  46. package/src/app/api/tasks/[id]/execute/route.ts +90 -0
  47. package/src/app/api/tasks/[id]/logs/route.ts +95 -0
  48. package/src/app/api/tasks/[id]/output/route.ts +47 -0
  49. package/src/app/api/tasks/[id]/respond/route.ts +64 -0
  50. package/src/app/api/tasks/[id]/resume/route.ts +76 -0
  51. package/src/app/api/tasks/[id]/route.ts +77 -0
  52. package/src/app/api/tasks/assist/route.ts +35 -0
  53. package/src/app/api/tasks/route.ts +82 -0
  54. package/src/app/api/uploads/[id]/route.ts +81 -0
  55. package/src/app/api/uploads/cleanup/route.ts +7 -0
  56. package/src/app/api/uploads/route.ts +66 -0
  57. package/src/app/api/workflows/[id]/execute/route.ts +82 -0
  58. package/src/app/api/workflows/[id]/route.ts +133 -0
  59. package/src/app/api/workflows/[id]/status/route.ts +54 -0
  60. package/src/app/api/workflows/[id]/steps/[stepId]/retry/route.ts +22 -0
  61. package/src/app/api/workflows/route.ts +61 -0
  62. package/src/app/apple-icon.tsx +31 -0
  63. package/src/app/costs/page.tsx +256 -0
  64. package/src/app/dashboard/page.tsx +44 -0
  65. package/src/app/documents/[id]/page.tsx +46 -0
  66. package/src/app/documents/page.tsx +45 -0
  67. package/src/app/error.tsx +26 -0
  68. package/src/app/global-error.tsx +23 -0
  69. package/src/app/globals.css +733 -0
  70. package/src/app/icon.tsx +30 -0
  71. package/src/app/inbox/loading.tsx +15 -0
  72. package/src/app/inbox/page.tsx +35 -0
  73. package/src/app/layout.tsx +78 -0
  74. package/src/app/manifest.ts +32 -0
  75. package/src/app/monitor/page.tsx +37 -0
  76. package/src/app/page.tsx +162 -0
  77. package/src/app/profiles/[id]/edit/page.tsx +39 -0
  78. package/src/app/profiles/[id]/page.tsx +33 -0
  79. package/src/app/profiles/new/page.tsx +22 -0
  80. package/src/app/profiles/page.tsx +19 -0
  81. package/src/app/projects/[id]/page.tsx +134 -0
  82. package/src/app/projects/loading.tsx +17 -0
  83. package/src/app/projects/page.tsx +32 -0
  84. package/src/app/schedules/[id]/page.tsx +47 -0
  85. package/src/app/schedules/page.tsx +18 -0
  86. package/src/app/settings/loading.tsx +24 -0
  87. package/src/app/settings/page.tsx +27 -0
  88. package/src/app/tasks/[id]/page.tsx +45 -0
  89. package/src/app/tasks/new/page.tsx +27 -0
  90. package/src/app/workflows/[id]/edit/page.tsx +66 -0
  91. package/src/app/workflows/[id]/page.tsx +37 -0
  92. package/src/app/workflows/blueprints/[id]/page.tsx +40 -0
  93. package/src/app/workflows/blueprints/new/page.tsx +20 -0
  94. package/src/app/workflows/blueprints/page.tsx +11 -0
  95. package/src/app/workflows/new/page.tsx +36 -0
  96. package/src/app/workflows/page.tsx +18 -0
  97. package/src/components/charts/donut-ring.tsx +64 -0
  98. package/src/components/charts/mini-bar.tsx +75 -0
  99. package/src/components/charts/sparkline.tsx +107 -0
  100. package/src/components/costs/cost-dashboard.tsx +877 -0
  101. package/src/components/costs/cost-filters.tsx +179 -0
  102. package/src/components/dashboard/activity-feed.tsx +95 -0
  103. package/src/components/dashboard/greeting.tsx +30 -0
  104. package/src/components/dashboard/priority-queue.tsx +79 -0
  105. package/src/components/dashboard/quick-actions.tsx +62 -0
  106. package/src/components/dashboard/recent-projects.tsx +79 -0
  107. package/src/components/dashboard/stats-cards.tsx +114 -0
  108. package/src/components/documents/document-browser.tsx +235 -0
  109. package/src/components/documents/document-detail-view.tsx +367 -0
  110. package/src/components/documents/document-grid.tsx +78 -0
  111. package/src/components/documents/document-preview.tsx +68 -0
  112. package/src/components/documents/document-table.tsx +119 -0
  113. package/src/components/documents/document-upload-dialog.tsx +153 -0
  114. package/src/components/documents/types.ts +6 -0
  115. package/src/components/documents/utils.ts +57 -0
  116. package/src/components/monitoring/connection-indicator.tsx +14 -0
  117. package/src/components/monitoring/log-entry.tsx +79 -0
  118. package/src/components/monitoring/log-filters.tsx +57 -0
  119. package/src/components/monitoring/log-stream.tsx +144 -0
  120. package/src/components/monitoring/monitor-overview-wrapper.tsx +64 -0
  121. package/src/components/monitoring/monitor-overview.tsx +119 -0
  122. package/src/components/notifications/failure-action.tsx +38 -0
  123. package/src/components/notifications/inbox-list.tsx +165 -0
  124. package/src/components/notifications/message-response.tsx +196 -0
  125. package/src/components/notifications/notification-item.tsx +250 -0
  126. package/src/components/notifications/pending-approval-host.tsx +478 -0
  127. package/src/components/notifications/permission-action.tsx +37 -0
  128. package/src/components/notifications/permission-response-actions.tsx +126 -0
  129. package/src/components/notifications/unread-badge.tsx +35 -0
  130. package/src/components/profiles/profile-browser.tsx +117 -0
  131. package/src/components/profiles/profile-card.tsx +78 -0
  132. package/src/components/profiles/profile-detail-view.tsx +564 -0
  133. package/src/components/profiles/profile-form-view.tsx +480 -0
  134. package/src/components/profiles/profile-import-dialog.tsx +113 -0
  135. package/src/components/projects/project-card.tsx +58 -0
  136. package/src/components/projects/project-create-dialog.tsx +140 -0
  137. package/src/components/projects/project-detail.tsx +68 -0
  138. package/src/components/projects/project-edit-dialog.tsx +219 -0
  139. package/src/components/projects/project-list.tsx +108 -0
  140. package/src/components/schedules/schedule-create-dialog.tsx +403 -0
  141. package/src/components/schedules/schedule-detail-view.tsx +274 -0
  142. package/src/components/schedules/schedule-list.tsx +242 -0
  143. package/src/components/schedules/schedule-status-badge.tsx +16 -0
  144. package/src/components/settings/api-key-form.tsx +141 -0
  145. package/src/components/settings/auth-config-section.tsx +141 -0
  146. package/src/components/settings/auth-method-selector.tsx +67 -0
  147. package/src/components/settings/auth-status-badge.tsx +40 -0
  148. package/src/components/settings/auth-status-dot.tsx +59 -0
  149. package/src/components/settings/budget-guardrails-section.tsx +842 -0
  150. package/src/components/settings/data-management-section.tsx +141 -0
  151. package/src/components/settings/openai-runtime-section.tsx +104 -0
  152. package/src/components/settings/permissions-section.tsx +91 -0
  153. package/src/components/shared/app-sidebar.tsx +123 -0
  154. package/src/components/shared/card-skeleton.tsx +42 -0
  155. package/src/components/shared/command-palette.tsx +250 -0
  156. package/src/components/shared/confirm-dialog.tsx +52 -0
  157. package/src/components/shared/empty-state.tsx +24 -0
  158. package/src/components/shared/error-state.tsx +32 -0
  159. package/src/components/shared/form-section-card.tsx +33 -0
  160. package/src/components/shared/section-heading.tsx +14 -0
  161. package/src/components/shared/stagent-logo.tsx +21 -0
  162. package/src/components/shared/theme-toggle.tsx +46 -0
  163. package/src/components/tasks/ai-assist-panel.tsx +210 -0
  164. package/src/components/tasks/content-preview.tsx +89 -0
  165. package/src/components/tasks/empty-board.tsx +12 -0
  166. package/src/components/tasks/file-upload.tsx +120 -0
  167. package/src/components/tasks/kanban-board.tsx +275 -0
  168. package/src/components/tasks/kanban-column.tsx +75 -0
  169. package/src/components/tasks/skeleton-board.tsx +21 -0
  170. package/src/components/tasks/task-attachments.tsx +114 -0
  171. package/src/components/tasks/task-card.tsx +101 -0
  172. package/src/components/tasks/task-create-panel.tsx +360 -0
  173. package/src/components/tasks/task-detail-view.tsx +356 -0
  174. package/src/components/ui/alert-dialog.tsx +196 -0
  175. package/src/components/ui/badge.tsx +50 -0
  176. package/src/components/ui/button.tsx +71 -0
  177. package/src/components/ui/card.tsx +92 -0
  178. package/src/components/ui/checkbox.tsx +32 -0
  179. package/src/components/ui/command.tsx +184 -0
  180. package/src/components/ui/dialog.tsx +158 -0
  181. package/src/components/ui/dropdown-menu.tsx +257 -0
  182. package/src/components/ui/form.tsx +167 -0
  183. package/src/components/ui/input.tsx +21 -0
  184. package/src/components/ui/label.tsx +24 -0
  185. package/src/components/ui/popover.tsx +89 -0
  186. package/src/components/ui/progress.tsx +31 -0
  187. package/src/components/ui/radio-group.tsx +45 -0
  188. package/src/components/ui/scroll-area.tsx +58 -0
  189. package/src/components/ui/select.tsx +190 -0
  190. package/src/components/ui/separator.tsx +28 -0
  191. package/src/components/ui/sheet.tsx +143 -0
  192. package/src/components/ui/sidebar.tsx +726 -0
  193. package/src/components/ui/skeleton.tsx +13 -0
  194. package/src/components/ui/slider.tsx +63 -0
  195. package/src/components/ui/sonner.tsx +36 -0
  196. package/src/components/ui/switch.tsx +35 -0
  197. package/src/components/ui/table.tsx +116 -0
  198. package/src/components/ui/tabs.tsx +91 -0
  199. package/src/components/ui/textarea.tsx +18 -0
  200. package/src/components/ui/tooltip.tsx +57 -0
  201. package/src/components/workflows/blueprint-editor.tsx +109 -0
  202. package/src/components/workflows/blueprint-gallery.tsx +155 -0
  203. package/src/components/workflows/blueprint-preview.tsx +240 -0
  204. package/src/components/workflows/loop-status-view.tsx +272 -0
  205. package/src/components/workflows/swarm-dashboard.tsx +185 -0
  206. package/src/components/workflows/workflow-form-view.tsx +1376 -0
  207. package/src/components/workflows/workflow-list.tsx +230 -0
  208. package/src/components/workflows/workflow-status-view.tsx +477 -0
  209. package/src/hooks/use-mobile.ts +19 -0
  210. package/src/instrumentation.ts +7 -0
  211. package/src/lib/agents/claude-agent.ts +737 -0
  212. package/src/lib/agents/execution-manager.ts +27 -0
  213. package/src/lib/agents/profiles/assignment-validation.ts +75 -0
  214. package/src/lib/agents/profiles/builtins/code-reviewer/SKILL.md +21 -0
  215. package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +28 -0
  216. package/src/lib/agents/profiles/builtins/data-analyst/SKILL.md +25 -0
  217. package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +27 -0
  218. package/src/lib/agents/profiles/builtins/devops-engineer/SKILL.md +34 -0
  219. package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +27 -0
  220. package/src/lib/agents/profiles/builtins/document-writer/SKILL.md +16 -0
  221. package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +27 -0
  222. package/src/lib/agents/profiles/builtins/general/SKILL.md +13 -0
  223. package/src/lib/agents/profiles/builtins/general/profile.yaml +18 -0
  224. package/src/lib/agents/profiles/builtins/health-fitness-coach/SKILL.md +34 -0
  225. package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +26 -0
  226. package/src/lib/agents/profiles/builtins/learning-coach/SKILL.md +35 -0
  227. package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +26 -0
  228. package/src/lib/agents/profiles/builtins/project-manager/SKILL.md +26 -0
  229. package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +26 -0
  230. package/src/lib/agents/profiles/builtins/researcher/SKILL.md +15 -0
  231. package/src/lib/agents/profiles/builtins/researcher/profile.yaml +27 -0
  232. package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +34 -0
  233. package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +26 -0
  234. package/src/lib/agents/profiles/builtins/technical-writer/SKILL.md +31 -0
  235. package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +29 -0
  236. package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +23 -0
  237. package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +26 -0
  238. package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +24 -0
  239. package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +26 -0
  240. package/src/lib/agents/profiles/compatibility.ts +109 -0
  241. package/src/lib/agents/profiles/registry.ts +293 -0
  242. package/src/lib/agents/profiles/test-runner.ts +18 -0
  243. package/src/lib/agents/profiles/test-types.ts +20 -0
  244. package/src/lib/agents/profiles/types.ts +43 -0
  245. package/src/lib/agents/router.ts +56 -0
  246. package/src/lib/agents/runtime/catalog.ts +85 -0
  247. package/src/lib/agents/runtime/claude-sdk.ts +12 -0
  248. package/src/lib/agents/runtime/claude.ts +370 -0
  249. package/src/lib/agents/runtime/codex-app-server-client.ts +289 -0
  250. package/src/lib/agents/runtime/index.ts +167 -0
  251. package/src/lib/agents/runtime/openai-codex.ts +1089 -0
  252. package/src/lib/agents/runtime/task-assist-types.ts +8 -0
  253. package/src/lib/agents/runtime/types.ts +30 -0
  254. package/src/lib/constants/settings.ts +13 -0
  255. package/src/lib/constants/status-colors.ts +44 -0
  256. package/src/lib/constants/task-status.ts +49 -0
  257. package/src/lib/data/clear.ts +63 -0
  258. package/src/lib/data/seed-data/documents.ts +715 -0
  259. package/src/lib/data/seed-data/logs.ts +195 -0
  260. package/src/lib/data/seed-data/notifications.ts +141 -0
  261. package/src/lib/data/seed-data/profiles.ts +175 -0
  262. package/src/lib/data/seed-data/projects.ts +61 -0
  263. package/src/lib/data/seed-data/schedules.ts +108 -0
  264. package/src/lib/data/seed-data/tasks.ts +341 -0
  265. package/src/lib/data/seed-data/usage-ledger.ts +130 -0
  266. package/src/lib/data/seed-data/workflows.ts +213 -0
  267. package/src/lib/data/seed.ts +129 -0
  268. package/src/lib/db/index.ts +221 -0
  269. package/src/lib/db/migrations/0000_aromatic_gargoyle.sql +59 -0
  270. package/src/lib/db/migrations/0001_first_iron_patriot.sql +6 -0
  271. package/src/lib/db/migrations/0002_add_resume_count.sql +1 -0
  272. package/src/lib/db/migrations/0003_add_settings.sql +5 -0
  273. package/src/lib/db/migrations/0004_add_documents.sql +20 -0
  274. package/src/lib/db/migrations/0005_add_document_preprocessing.sql +4 -0
  275. package/src/lib/db/migrations/0006_add_agent_profile.sql +2 -0
  276. package/src/lib/db/migrations/0007_add_usage_metering_ledger.sql +30 -0
  277. package/src/lib/db/migrations/0008_add_document_version.sql +1 -0
  278. package/src/lib/db/migrations/meta/0000_snapshot.json +416 -0
  279. package/src/lib/db/migrations/meta/0001_snapshot.json +461 -0
  280. package/src/lib/db/migrations/meta/0002_snapshot.json +469 -0
  281. package/src/lib/db/migrations/meta/_journal.json +27 -0
  282. package/src/lib/db/schema.ts +227 -0
  283. package/src/lib/documents/cleanup.ts +50 -0
  284. package/src/lib/documents/context-builder.ts +75 -0
  285. package/src/lib/documents/output-scanner.ts +166 -0
  286. package/src/lib/documents/processor.ts +120 -0
  287. package/src/lib/documents/processors/image.ts +21 -0
  288. package/src/lib/documents/processors/office.ts +36 -0
  289. package/src/lib/documents/processors/pdf.ts +12 -0
  290. package/src/lib/documents/processors/spreadsheet.ts +18 -0
  291. package/src/lib/documents/processors/text.ts +8 -0
  292. package/src/lib/documents/registry.ts +25 -0
  293. package/src/lib/notifications/actionable.ts +108 -0
  294. package/src/lib/notifications/permissions.ts +169 -0
  295. package/src/lib/queries/chart-data.ts +184 -0
  296. package/src/lib/schedules/interval-parser.ts +110 -0
  297. package/src/lib/schedules/scheduler.ts +220 -0
  298. package/src/lib/settings/auth.ts +98 -0
  299. package/src/lib/settings/budget-guardrails.ts +590 -0
  300. package/src/lib/settings/helpers.ts +23 -0
  301. package/src/lib/settings/openai-auth.ts +80 -0
  302. package/src/lib/settings/permissions.ts +102 -0
  303. package/src/lib/usage/ledger.ts +489 -0
  304. package/src/lib/usage/pricing.ts +68 -0
  305. package/src/lib/utils/crypto.ts +90 -0
  306. package/src/lib/utils/format-timestamp.ts +46 -0
  307. package/src/lib/utils/session-cleanup.ts +26 -0
  308. package/src/lib/utils/stagent-paths.ts +18 -0
  309. package/src/lib/utils.ts +6 -0
  310. package/src/lib/validators/blueprint.ts +43 -0
  311. package/src/lib/validators/profile.ts +64 -0
  312. package/src/lib/validators/project.ts +17 -0
  313. package/src/lib/validators/settings.ts +57 -0
  314. package/src/lib/validators/task.ts +30 -0
  315. package/src/lib/workflows/blueprints/builtins/code-review-pipeline.yaml +72 -0
  316. package/src/lib/workflows/blueprints/builtins/documentation-generation.yaml +62 -0
  317. package/src/lib/workflows/blueprints/builtins/investment-research.yaml +81 -0
  318. package/src/lib/workflows/blueprints/builtins/meal-planning.yaml +73 -0
  319. package/src/lib/workflows/blueprints/builtins/product-research.yaml +72 -0
  320. package/src/lib/workflows/blueprints/builtins/research-report.yaml +77 -0
  321. package/src/lib/workflows/blueprints/builtins/sprint-planning.yaml +77 -0
  322. package/src/lib/workflows/blueprints/builtins/travel-planning.yaml +80 -0
  323. package/src/lib/workflows/blueprints/instantiator.ts +131 -0
  324. package/src/lib/workflows/blueprints/registry.ts +128 -0
  325. package/src/lib/workflows/blueprints/template.ts +58 -0
  326. package/src/lib/workflows/blueprints/types.ts +38 -0
  327. package/src/lib/workflows/definition-validation.ts +121 -0
  328. package/src/lib/workflows/engine.ts +1113 -0
  329. package/src/lib/workflows/loop-executor.ts +270 -0
  330. package/src/lib/workflows/parallel.ts +55 -0
  331. package/src/lib/workflows/swarm.ts +97 -0
  332. package/src/lib/workflows/types.ts +112 -0
  333. package/tsconfig.json +41 -0
package/dist/cli.js ADDED
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+
3
+ // bin/cli.ts
4
+ import { program } from "commander";
5
+ import { dirname, join as join2 } from "path";
6
+ import { fileURLToPath } from "url";
7
+ import {
8
+ mkdirSync,
9
+ existsSync,
10
+ readFileSync,
11
+ writeFileSync,
12
+ cpSync,
13
+ unlinkSync
14
+ } from "fs";
15
+ import { spawn } from "child_process";
16
+ import { createServer } from "net";
17
+ import Database from "better-sqlite3";
18
+ import { drizzle } from "drizzle-orm/better-sqlite3";
19
+ import { migrate } from "drizzle-orm/better-sqlite3/migrator";
20
+
21
+ // src/lib/utils/stagent-paths.ts
22
+ import { homedir } from "os";
23
+ import { join } from "path";
24
+ function getStagentDataDir() {
25
+ return process.env.STAGENT_DATA_DIR || join(homedir(), ".stagent");
26
+ }
27
+ function getStagentDbPath() {
28
+ return join(getStagentDataDir(), "stagent.db");
29
+ }
30
+
31
+ // bin/cli.ts
32
+ var __dirname = dirname(fileURLToPath(import.meta.url));
33
+ var appDir = join2(__dirname, "..");
34
+ var DATA_DIR = getStagentDataDir();
35
+ var dbPath = getStagentDbPath();
36
+ var pkg = JSON.parse(readFileSync(join2(appDir, "package.json"), "utf-8"));
37
+ var HELP_TEXT = `
38
+ Data:
39
+ Directory ${DATA_DIR}
40
+ Database ${dbPath}
41
+ Sessions ${join2(DATA_DIR, "sessions")}
42
+ Logs ${join2(DATA_DIR, "logs")}
43
+
44
+ Environment variables:
45
+ STAGENT_DATA_DIR Custom data directory for the CLI and web app
46
+ ANTHROPIC_API_KEY Claude runtime access
47
+ OPENAI_API_KEY OpenAI Codex runtime access
48
+
49
+ Examples:
50
+ npx stagent
51
+ npx stagent --port 3210 --no-open
52
+ STAGENT_DATA_DIR=/tmp/stagent-smoke npx stagent --reset
53
+ `;
54
+ program.name("stagent").description("Governed AI agent workspace \u2014 local-first").version(pkg.version).addHelpText("after", HELP_TEXT).option("-p, --port <number>", "port to start on", "3000").option("--reset", "delete the local database before starting").option("--no-open", "don't auto-open browser").parse();
55
+ var opts = program.opts();
56
+ var requestedPort = Number.parseInt(opts.port, 10);
57
+ if (Number.isNaN(requestedPort) || requestedPort <= 0) {
58
+ program.error(`Invalid port: ${opts.port}`);
59
+ }
60
+ for (const dir of [DATA_DIR, join2(DATA_DIR, "logs"), join2(DATA_DIR, "sessions")]) {
61
+ mkdirSync(dir, { recursive: true });
62
+ }
63
+ if (opts.reset) {
64
+ if (existsSync(dbPath)) {
65
+ unlinkSync(dbPath);
66
+ for (const suffix of ["-wal", "-shm"]) {
67
+ const filePath = dbPath + suffix;
68
+ if (existsSync(filePath)) unlinkSync(filePath);
69
+ }
70
+ console.log("Database reset.");
71
+ } else {
72
+ console.log("No database found to reset.");
73
+ }
74
+ }
75
+ var sqlite = new Database(dbPath);
76
+ sqlite.pragma("journal_mode = WAL");
77
+ sqlite.pragma("foreign_keys = ON");
78
+ var db = drizzle(sqlite);
79
+ var migrationsDir = join2(appDir, "src", "lib", "db", "migrations");
80
+ migrate(db, { migrationsFolder: migrationsDir });
81
+ sqlite.close();
82
+ console.log("Database ready.");
83
+ function findAvailablePort(preferred) {
84
+ return new Promise((resolve) => {
85
+ const server = createServer();
86
+ server.listen(preferred, () => {
87
+ server.close(() => resolve(preferred));
88
+ });
89
+ server.on("error", () => {
90
+ resolve(findAvailablePort(preferred + 1));
91
+ });
92
+ });
93
+ }
94
+ function findLocalBin(name, cwd) {
95
+ const local = join2(cwd, "node_modules", ".bin", name);
96
+ if (existsSync(local)) return local;
97
+ let dir = dirname(cwd);
98
+ while (dir !== dirname(dir)) {
99
+ const candidate = join2(dir, "node_modules", ".bin", name);
100
+ if (existsSync(candidate)) return candidate;
101
+ dir = dirname(dir);
102
+ }
103
+ return name;
104
+ }
105
+ async function main() {
106
+ const actualPort = await findAvailablePort(requestedPort);
107
+ let effectiveCwd = appDir;
108
+ const localNm = join2(appDir, "node_modules");
109
+ if (!existsSync(join2(localNm, "next", "package.json"))) {
110
+ let searchDir = dirname(appDir);
111
+ while (searchDir !== dirname(searchDir)) {
112
+ const candidate = join2(searchDir, "node_modules", "next", "package.json");
113
+ if (existsSync(candidate)) {
114
+ const hoistedRoot = searchDir;
115
+ for (const name of ["src", "public"]) {
116
+ const dest = join2(hoistedRoot, name);
117
+ const src = join2(appDir, name);
118
+ if (!existsSync(dest) && existsSync(src)) {
119
+ cpSync(src, dest, { recursive: true });
120
+ }
121
+ }
122
+ for (const name of [
123
+ "next.config.mjs",
124
+ "tsconfig.json",
125
+ "postcss.config.mjs",
126
+ "package.json",
127
+ "components.json",
128
+ "drizzle.config.ts"
129
+ ]) {
130
+ const dest = join2(hoistedRoot, name);
131
+ const src = join2(appDir, name);
132
+ if (!existsSync(dest) && existsSync(src)) {
133
+ writeFileSync(dest, readFileSync(src));
134
+ }
135
+ }
136
+ effectiveCwd = hoistedRoot;
137
+ break;
138
+ }
139
+ searchDir = dirname(searchDir);
140
+ }
141
+ }
142
+ const nextBin = findLocalBin("next", effectiveCwd);
143
+ console.log(`Stagent ${pkg.version}`);
144
+ console.log(`Data dir: ${DATA_DIR}`);
145
+ console.log(`Starting Stagent on http://localhost:${actualPort}`);
146
+ const child = spawn(nextBin, ["dev", "--turbopack", "--port", String(actualPort)], {
147
+ cwd: effectiveCwd,
148
+ stdio: "inherit",
149
+ env: {
150
+ ...process.env,
151
+ STAGENT_DATA_DIR: DATA_DIR,
152
+ PORT: String(actualPort)
153
+ }
154
+ });
155
+ if (opts.open !== false) {
156
+ setTimeout(async () => {
157
+ try {
158
+ const open = (await import("open")).default;
159
+ await open(`http://localhost:${actualPort}`);
160
+ } catch {
161
+ }
162
+ }, 3e3);
163
+ }
164
+ process.on("SIGINT", () => child.kill("SIGINT"));
165
+ process.on("SIGTERM", () => child.kill("SIGTERM"));
166
+ child.on("exit", (code) => process.exit(code ?? 0));
167
+ }
168
+ main().catch((err) => {
169
+ console.error("Failed to start Stagent:", err);
170
+ process.exit(1);
171
+ });
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "drizzle-kit";
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+
5
+ export default defineConfig({
6
+ schema: "./src/lib/db/schema.ts",
7
+ out: "./src/lib/db/migrations",
8
+ dialect: "sqlite",
9
+ dbCredentials: {
10
+ url: join(homedir(), ".stagent", "stagent.db"),
11
+ },
12
+ });
@@ -0,0 +1,15 @@
1
+ import { dirname } from "path";
2
+ import { fileURLToPath } from "url";
3
+
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+
6
+ /** @type {import('next').NextConfig} */
7
+ const nextConfig = {
8
+ serverExternalPackages: ["better-sqlite3"],
9
+ devIndicators: false,
10
+ turbopack: {
11
+ root: __dirname,
12
+ },
13
+ };
14
+
15
+ export default nextConfig;
package/package.json ADDED
@@ -0,0 +1,114 @@
1
+ {
2
+ "name": "stagent",
3
+ "version": "0.1.0",
4
+ "description": "Governed AI agent workspace for supervised local execution, workflows, documents, and provider runtimes.",
5
+ "keywords": [
6
+ "ai",
7
+ "agents",
8
+ "automation",
9
+ "workflow",
10
+ "claude",
11
+ "openai",
12
+ "codex",
13
+ "nextjs",
14
+ "local-first"
15
+ ],
16
+ "license": "Apache-2.0",
17
+ "type": "module",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/navam-io/stagent.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/navam-io/stagent/issues"
24
+ },
25
+ "homepage": "https://github.com/navam-io/stagent#readme",
26
+ "bin": {
27
+ "stagent": "./dist/cli.js"
28
+ },
29
+ "files": [
30
+ "dist/",
31
+ "src/",
32
+ "!src/**/__tests__/",
33
+ "!src/**/*.test.ts",
34
+ "!src/**/*.test.tsx",
35
+ "!src/**/*.spec.ts",
36
+ "!src/**/*.spec.tsx",
37
+ "!src/test/",
38
+ "public/",
39
+ "next.config.mjs",
40
+ "tsconfig.json",
41
+ "drizzle.config.ts",
42
+ "postcss.config.mjs",
43
+ "components.json",
44
+ "package.json"
45
+ ],
46
+ "scripts": {
47
+ "dev": "next dev --turbopack",
48
+ "build": "next build",
49
+ "build:cli": "tsup",
50
+ "prepublishOnly": "npm run build:cli",
51
+ "smoke:npm": "node scripts/smoke-npm-package.mjs",
52
+ "test": "vitest run",
53
+ "test:watch": "vitest",
54
+ "test:coverage": "vitest run --coverage",
55
+ "test:ui": "vitest --ui"
56
+ },
57
+ "engines": {
58
+ "node": ">=20.0.0"
59
+ },
60
+ "dependencies": {
61
+ "@anthropic-ai/claude-agent-sdk": "^0.2.71",
62
+ "@dnd-kit/core": "^6.3.1",
63
+ "@dnd-kit/sortable": "^10.0.0",
64
+ "@dnd-kit/utilities": "^3.2.2",
65
+ "@hookform/resolvers": "^5.2.2",
66
+ "@tailwindcss/typography": "^0.5",
67
+ "better-sqlite3": "^12",
68
+ "class-variance-authority": "^0.7.1",
69
+ "clsx": "^2",
70
+ "cmdk": "^1.1.1",
71
+ "commander": "^14",
72
+ "cron-parser": "^5.5.0",
73
+ "drizzle-orm": "^0.45",
74
+ "image-size": "^2.0.2",
75
+ "js-yaml": "^4.1.1",
76
+ "jszip": "^3.10.1",
77
+ "lucide-react": "^0.474.0",
78
+ "mammoth": "^1.11.0",
79
+ "next": "^16",
80
+ "next-themes": "^0.4.6",
81
+ "open": "^10",
82
+ "pdf-parse": "^2.4.5",
83
+ "radix-ui": "^1.4.3",
84
+ "react": "^19",
85
+ "react-dom": "^19",
86
+ "react-hook-form": "^7.71.2",
87
+ "react-markdown": "^10.1.0",
88
+ "remark-gfm": "^4.0.1",
89
+ "sonner": "^2.0.7",
90
+ "tailwind-merge": "^3",
91
+ "tw-animate-css": "^1",
92
+ "xlsx": "^0.18.5",
93
+ "zod": "^4.3.6"
94
+ },
95
+ "devDependencies": {
96
+ "@tailwindcss/postcss": "^4",
97
+ "@testing-library/dom": "^10.4.1",
98
+ "@testing-library/jest-dom": "^6.9.1",
99
+ "@testing-library/react": "^16.3.2",
100
+ "@types/better-sqlite3": "^7",
101
+ "@types/js-yaml": "^4.0.9",
102
+ "@types/node": "^22",
103
+ "@types/react": "^19",
104
+ "@types/react-dom": "^19",
105
+ "@vitejs/plugin-react": "^5.1.4",
106
+ "@vitest/coverage-v8": "^4.0.18",
107
+ "drizzle-kit": "^0.30",
108
+ "jsdom": "^28.1.0",
109
+ "tailwindcss": "^4",
110
+ "tsup": "^8.5",
111
+ "typescript": "^5",
112
+ "vitest": "^4.0.18"
113
+ }
114
+ }
@@ -0,0 +1,8 @@
1
+ /** @type {import('postcss-load-config').Config} */
2
+ const config = {
3
+ plugins: {
4
+ "@tailwindcss/postcss": {},
5
+ },
6
+ };
7
+
8
+ export default config;
Binary file
@@ -0,0 +1,13 @@
1
+ <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="512" height="512" rx="112" fill="#0f172a"/>
3
+ <defs>
4
+ <linearGradient id="s-grad" x1="150" y1="80" x2="362" y2="432" gradientUnits="userSpaceOnUse">
5
+ <stop offset="0%" stop-color="#22d3ee"/>
6
+ <stop offset="50%" stop-color="#3b82f6"/>
7
+ <stop offset="100%" stop-color="#2563eb"/>
8
+ </linearGradient>
9
+ </defs>
10
+ <path d="M340 130c0 0-60-10-110 30s-50 90-10 115c40 25 70 30 70 30s50 15 30 65c-20 50-80 40-80 40" stroke="url(#s-grad)" stroke-width="56" stroke-linecap="round" fill="none"/>
11
+ <circle cx="340" cy="130" r="36" fill="#22d3ee"/>
12
+ <circle cx="240" cy="410" r="36" fill="#2563eb"/>
13
+ </svg>
Binary file
Binary file
Binary file
@@ -0,0 +1,27 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { instantiateBlueprint } from "@/lib/workflows/blueprints/instantiator";
3
+
4
+ export async function POST(
5
+ req: NextRequest,
6
+ { params }: { params: Promise<{ id: string }> }
7
+ ) {
8
+ const { id } = await params;
9
+
10
+ try {
11
+ const { variables, projectId } = await req.json();
12
+
13
+ if (!variables || typeof variables !== "object") {
14
+ return NextResponse.json(
15
+ { error: "variables object is required" },
16
+ { status: 400 }
17
+ );
18
+ }
19
+
20
+ const result = await instantiateBlueprint(id, variables, projectId);
21
+ return NextResponse.json(result, { status: 201 });
22
+ } catch (err: unknown) {
23
+ const message =
24
+ err instanceof Error ? err.message : "Failed to instantiate blueprint";
25
+ return NextResponse.json({ error: message }, { status: 400 });
26
+ }
27
+ }
@@ -0,0 +1,39 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import {
3
+ getBlueprint,
4
+ deleteBlueprint,
5
+ } from "@/lib/workflows/blueprints/registry";
6
+
7
+ export async function GET(
8
+ _req: NextRequest,
9
+ { params }: { params: Promise<{ id: string }> }
10
+ ) {
11
+ const { id } = await params;
12
+ const blueprint = getBlueprint(id);
13
+
14
+ if (!blueprint) {
15
+ return NextResponse.json(
16
+ { error: "Blueprint not found" },
17
+ { status: 404 }
18
+ );
19
+ }
20
+
21
+ return NextResponse.json(blueprint);
22
+ }
23
+
24
+ export async function DELETE(
25
+ _req: NextRequest,
26
+ { params }: { params: Promise<{ id: string }> }
27
+ ) {
28
+ const { id } = await params;
29
+
30
+ try {
31
+ deleteBlueprint(id);
32
+ return NextResponse.json({ ok: true });
33
+ } catch (err: unknown) {
34
+ const message =
35
+ err instanceof Error ? err.message : "Failed to delete blueprint";
36
+ const status = message.includes("built-in") ? 403 : 400;
37
+ return NextResponse.json({ error: message }, { status });
38
+ }
39
+ }
@@ -0,0 +1,68 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import { createBlueprint } from "@/lib/workflows/blueprints/registry";
3
+
4
+ /**
5
+ * POST /api/blueprints/import
6
+ *
7
+ * Import a blueprint from a GitHub URL pointing to a YAML file.
8
+ */
9
+ export async function POST(req: NextRequest) {
10
+ try {
11
+ const { url } = await req.json();
12
+
13
+ if (!url || typeof url !== "string") {
14
+ return NextResponse.json({ error: "url is required" }, { status: 400 });
15
+ }
16
+
17
+ const rawUrl = toRawGitHubUrl(url);
18
+ if (!rawUrl) {
19
+ return NextResponse.json(
20
+ { error: "Only GitHub URLs are supported" },
21
+ { status: 400 }
22
+ );
23
+ }
24
+
25
+ const res = await fetch(rawUrl);
26
+ if (!res.ok) {
27
+ return NextResponse.json(
28
+ { error: `Failed to fetch blueprint: ${res.status}` },
29
+ { status: 400 }
30
+ );
31
+ }
32
+
33
+ const yamlContent = await res.text();
34
+ const blueprint = createBlueprint(yamlContent);
35
+
36
+ return NextResponse.json(
37
+ { ok: true, id: blueprint.id, name: blueprint.name },
38
+ { status: 201 }
39
+ );
40
+ } catch (err: unknown) {
41
+ const message = err instanceof Error ? err.message : "Import failed";
42
+ return NextResponse.json({ error: message }, { status: 400 });
43
+ }
44
+ }
45
+
46
+ function toRawGitHubUrl(url: string): string | null {
47
+ try {
48
+ const u = new URL(url);
49
+
50
+ if (u.hostname === "raw.githubusercontent.com") {
51
+ return url;
52
+ }
53
+
54
+ if (u.hostname !== "github.com") return null;
55
+
56
+ const match = u.pathname.match(
57
+ /^\/([^/]+)\/([^/]+)\/(tree|blob)\/([^/]+)\/(.+)/
58
+ );
59
+ if (match) {
60
+ const [, owner, repo, , branch, filePath] = match;
61
+ return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`;
62
+ }
63
+
64
+ return null;
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
@@ -0,0 +1,29 @@
1
+ import { NextRequest, NextResponse } from "next/server";
2
+ import {
3
+ listBlueprints,
4
+ createBlueprint,
5
+ } from "@/lib/workflows/blueprints/registry";
6
+
7
+ export async function GET() {
8
+ const blueprints = listBlueprints();
9
+ return NextResponse.json(blueprints);
10
+ }
11
+
12
+ export async function POST(req: NextRequest) {
13
+ try {
14
+ const { yaml: yamlContent } = await req.json();
15
+ if (!yamlContent || typeof yamlContent !== "string") {
16
+ return NextResponse.json(
17
+ { error: "yaml field is required" },
18
+ { status: 400 }
19
+ );
20
+ }
21
+
22
+ const blueprint = createBlueprint(yamlContent);
23
+ return NextResponse.json(blueprint, { status: 201 });
24
+ } catch (err: unknown) {
25
+ const message =
26
+ err instanceof Error ? err.message : "Failed to create blueprint";
27
+ return NextResponse.json({ error: message }, { status: 400 });
28
+ }
29
+ }
@@ -0,0 +1,31 @@
1
+ import { NextResponse } from "next/server";
2
+ import { db } from "@/lib/db";
3
+ import { projects, tasks } from "@/lib/db/schema";
4
+ import { desc } from "drizzle-orm";
5
+
6
+ export async function GET() {
7
+ const recentProjects = await db
8
+ .select({
9
+ id: projects.id,
10
+ name: projects.name,
11
+ status: projects.status,
12
+ })
13
+ .from(projects)
14
+ .orderBy(desc(projects.updatedAt))
15
+ .limit(5);
16
+
17
+ const recentTasks = await db
18
+ .select({
19
+ id: tasks.id,
20
+ title: tasks.title,
21
+ status: tasks.status,
22
+ })
23
+ .from(tasks)
24
+ .orderBy(desc(tasks.updatedAt))
25
+ .limit(5);
26
+
27
+ return NextResponse.json({
28
+ projects: recentProjects,
29
+ tasks: recentTasks,
30
+ });
31
+ }
@@ -0,0 +1,22 @@
1
+ import { NextResponse } from "next/server";
2
+ import { clearAllData } from "@/lib/data/clear";
3
+
4
+ export async function POST() {
5
+ if (process.env.NODE_ENV === "production") {
6
+ return NextResponse.json(
7
+ { error: "This endpoint is disabled in production" },
8
+ { status: 403 }
9
+ );
10
+ }
11
+
12
+ try {
13
+ const deleted = clearAllData();
14
+ return NextResponse.json({ success: true, deleted });
15
+ } catch (error: unknown) {
16
+ const message = error instanceof Error ? error.message : String(error);
17
+ return NextResponse.json(
18
+ { success: false, error: message },
19
+ { status: 500 }
20
+ );
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ import { NextResponse } from "next/server";
2
+ import { seedSampleData } from "@/lib/data/seed";
3
+
4
+ export async function POST() {
5
+ if (process.env.NODE_ENV === "production") {
6
+ return NextResponse.json(
7
+ { error: "This endpoint is disabled in production" },
8
+ { status: 403 }
9
+ );
10
+ }
11
+
12
+ try {
13
+ const seeded = await seedSampleData();
14
+ return NextResponse.json({ success: true, seeded });
15
+ } catch (error: unknown) {
16
+ const message = error instanceof Error ? error.message : String(error);
17
+ return NextResponse.json(
18
+ { success: false, error: message },
19
+ { status: 500 }
20
+ );
21
+ }
22
+ }
@@ -0,0 +1,44 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { basename } from "node:path";
3
+ import { NextRequest, NextResponse } from "next/server";
4
+ import { db } from "@/lib/db";
5
+ import { documents } from "@/lib/db/schema";
6
+ import { eq } from "drizzle-orm";
7
+
8
+ export async function GET(
9
+ req: NextRequest,
10
+ { params }: { params: Promise<{ id: string }> }
11
+ ) {
12
+ const { id } = await params;
13
+ const inline = req.nextUrl.searchParams.get("inline") === "1";
14
+
15
+ const [doc] = await db
16
+ .select({
17
+ originalName: documents.originalName,
18
+ mimeType: documents.mimeType,
19
+ storagePath: documents.storagePath,
20
+ })
21
+ .from(documents)
22
+ .where(eq(documents.id, id));
23
+
24
+ if (!doc) {
25
+ return NextResponse.json({ error: "Document not found" }, { status: 404 });
26
+ }
27
+
28
+ try {
29
+ const data = await readFile(doc.storagePath);
30
+ const filename = basename(doc.originalName);
31
+ const canInline =
32
+ inline && (doc.mimeType.startsWith("image/") || doc.mimeType === "application/pdf");
33
+
34
+ return new NextResponse(data, {
35
+ headers: {
36
+ "Content-Type": doc.mimeType,
37
+ "Content-Disposition": `${canInline ? "inline" : "attachment"}; filename="${filename}"`,
38
+ "X-Content-Type-Options": "nosniff",
39
+ },
40
+ });
41
+ } catch {
42
+ return NextResponse.json({ error: "File not found" }, { status: 404 });
43
+ }
44
+ }