@straiffi/archon 1.0.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 (124) hide show
  1. package/README.md +224 -0
  2. package/dist/cli.js +216 -0
  3. package/dist/client/assets/index-8_-boBBA.css +2 -0
  4. package/dist/client/assets/index-s_jjeqha.js +176 -0
  5. package/dist/client/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  6. package/dist/client/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  7. package/dist/client/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  8. package/dist/client/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  9. package/dist/client/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  10. package/dist/client/favicon.svg +62 -0
  11. package/dist/client/icons.svg +24 -0
  12. package/dist/client/index.html +14 -0
  13. package/dist/server/db.js +764 -0
  14. package/dist/server/db.js.map +1 -0
  15. package/dist/server/index.js +5134 -0
  16. package/dist/server/index.js.map +1 -0
  17. package/dist/server/lib/agent.js +1302 -0
  18. package/dist/server/lib/agent.js.map +1 -0
  19. package/dist/server/lib/buildChains.js +2 -0
  20. package/dist/server/lib/buildChains.js.map +1 -0
  21. package/dist/server/lib/buildFlow.js +59 -0
  22. package/dist/server/lib/buildFlow.js.map +1 -0
  23. package/dist/server/lib/buildSequences.js +599 -0
  24. package/dist/server/lib/buildSequences.js.map +1 -0
  25. package/dist/server/lib/bundleActivity.js +95 -0
  26. package/dist/server/lib/bundleActivity.js.map +1 -0
  27. package/dist/server/lib/bundlePullRequests.js +126 -0
  28. package/dist/server/lib/bundlePullRequests.js.map +1 -0
  29. package/dist/server/lib/chatMessages.js +60 -0
  30. package/dist/server/lib/chatMessages.js.map +1 -0
  31. package/dist/server/lib/chatTargets.js +123 -0
  32. package/dist/server/lib/chatTargets.js.map +1 -0
  33. package/dist/server/lib/chatTicketProposals.js +180 -0
  34. package/dist/server/lib/chatTicketProposals.js.map +1 -0
  35. package/dist/server/lib/chats.js +279 -0
  36. package/dist/server/lib/chats.js.map +1 -0
  37. package/dist/server/lib/config.js +3 -0
  38. package/dist/server/lib/config.js.map +1 -0
  39. package/dist/server/lib/cors.js +30 -0
  40. package/dist/server/lib/cors.js.map +1 -0
  41. package/dist/server/lib/directoryPicker.js +174 -0
  42. package/dist/server/lib/directoryPicker.js.map +1 -0
  43. package/dist/server/lib/git.js +1284 -0
  44. package/dist/server/lib/git.js.map +1 -0
  45. package/dist/server/lib/integrations/github.js +511 -0
  46. package/dist/server/lib/integrations/github.js.map +1 -0
  47. package/dist/server/lib/integrations/index.js +162 -0
  48. package/dist/server/lib/integrations/index.js.map +1 -0
  49. package/dist/server/lib/integrations/jira.js +283 -0
  50. package/dist/server/lib/integrations/jira.js.map +1 -0
  51. package/dist/server/lib/integrations/planning.js +27 -0
  52. package/dist/server/lib/integrations/planning.js.map +1 -0
  53. package/dist/server/lib/integrations/types.js +2 -0
  54. package/dist/server/lib/integrations/types.js.map +1 -0
  55. package/dist/server/lib/lightweightPrompt.js +88 -0
  56. package/dist/server/lib/lightweightPrompt.js.map +1 -0
  57. package/dist/server/lib/models.js +219 -0
  58. package/dist/server/lib/models.js.map +1 -0
  59. package/dist/server/lib/preview.js +377 -0
  60. package/dist/server/lib/preview.js.map +1 -0
  61. package/dist/server/lib/previewProxy.js +659 -0
  62. package/dist/server/lib/previewProxy.js.map +1 -0
  63. package/dist/server/lib/projectAutoConfig.js +682 -0
  64. package/dist/server/lib/projectAutoConfig.js.map +1 -0
  65. package/dist/server/lib/projectFileSuggestions.js +133 -0
  66. package/dist/server/lib/projectFileSuggestions.js.map +1 -0
  67. package/dist/server/lib/projectMemory.js +1519 -0
  68. package/dist/server/lib/projectMemory.js.map +1 -0
  69. package/dist/server/lib/projectMemoryPrompt.js +390 -0
  70. package/dist/server/lib/projectMemoryPrompt.js.map +1 -0
  71. package/dist/server/lib/projectMemoryScan.js +681 -0
  72. package/dist/server/lib/projectMemoryScan.js.map +1 -0
  73. package/dist/server/lib/projectMemorySuggestions.js +166 -0
  74. package/dist/server/lib/projectMemorySuggestions.js.map +1 -0
  75. package/dist/server/lib/projectMemoryTransfer.js +958 -0
  76. package/dist/server/lib/projectMemoryTransfer.js.map +1 -0
  77. package/dist/server/lib/projects.js +569 -0
  78. package/dist/server/lib/projects.js.map +1 -0
  79. package/dist/server/lib/promptSkills.js +28 -0
  80. package/dist/server/lib/promptSkills.js.map +1 -0
  81. package/dist/server/lib/queue.js +15 -0
  82. package/dist/server/lib/queue.js.map +1 -0
  83. package/dist/server/lib/reviewFindings.js +390 -0
  84. package/dist/server/lib/reviewFindings.js.map +1 -0
  85. package/dist/server/lib/run.js +416 -0
  86. package/dist/server/lib/run.js.map +1 -0
  87. package/dist/server/lib/runtimePaths.js +93 -0
  88. package/dist/server/lib/runtimePaths.js.map +1 -0
  89. package/dist/server/lib/shell.js +27 -0
  90. package/dist/server/lib/shell.js.map +1 -0
  91. package/dist/server/lib/skills.js +124 -0
  92. package/dist/server/lib/skills.js.map +1 -0
  93. package/dist/server/lib/startDev.js +18 -0
  94. package/dist/server/lib/startDev.js.map +1 -0
  95. package/dist/server/lib/staticClient.js +80 -0
  96. package/dist/server/lib/staticClient.js.map +1 -0
  97. package/dist/server/lib/terminal.js +366 -0
  98. package/dist/server/lib/terminal.js.map +1 -0
  99. package/dist/server/lib/ticketDependencies.js +174 -0
  100. package/dist/server/lib/ticketDependencies.js.map +1 -0
  101. package/dist/server/lib/ticketMessages.js +65 -0
  102. package/dist/server/lib/ticketMessages.js.map +1 -0
  103. package/dist/server/lib/ticketOpenQuestions.js +128 -0
  104. package/dist/server/lib/ticketOpenQuestions.js.map +1 -0
  105. package/dist/server/lib/ticketUndo.js +549 -0
  106. package/dist/server/lib/ticketUndo.js.map +1 -0
  107. package/dist/server/lib/tickets.js +981 -0
  108. package/dist/server/lib/tickets.js.map +1 -0
  109. package/dist/server/lib/types.js +2 -0
  110. package/dist/server/lib/types.js.map +1 -0
  111. package/dist/server/package.json +3 -0
  112. package/dist/server/workers/build.js +229 -0
  113. package/dist/server/workers/build.js.map +1 -0
  114. package/dist/server/workers/chat.js +190 -0
  115. package/dist/server/workers/chat.js.map +1 -0
  116. package/dist/server/workers/followUp.js +204 -0
  117. package/dist/server/workers/followUp.js.map +1 -0
  118. package/dist/server/workers/plan.js +1130 -0
  119. package/dist/server/workers/plan.js.map +1 -0
  120. package/dist/server/workers/planFollowUp.js +360 -0
  121. package/dist/server/workers/planFollowUp.js.map +1 -0
  122. package/dist/server/workers/review.js +167 -0
  123. package/dist/server/workers/review.js.map +1 -0
  124. package/package.json +40 -0
package/README.md ADDED
@@ -0,0 +1,224 @@
1
+ # Archon
2
+
3
+ A ticket-driven interface for AI-assisted code automation. Create tickets, plan features, then let an AI agent (Claude or OpenCode) implement them in isolated git worktrees.
4
+
5
+ ## Install
6
+
7
+ Archon ships as a global npm package:
8
+
9
+ ```bash
10
+ npm install -g @straiffi/archon
11
+ ```
12
+
13
+ Then start it with:
14
+
15
+ ```bash
16
+ archon
17
+ ```
18
+
19
+ Archon will:
20
+
21
+ 1. start the backend and frontend as one local process
22
+ 2. serve the UI and API from the same origin
23
+ 3. open your default browser automatically
24
+ 4. stay attached until you press `Ctrl+C`
25
+
26
+ Typical startup output:
27
+
28
+ ```text
29
+ Archon running at http://localhost:43110
30
+ Press Ctrl+C to stop.
31
+ ```
32
+
33
+ Archon starts on port `43110` by default and scans upward if that port is already in use.
34
+
35
+ ### Update
36
+
37
+ ```bash
38
+ archon update
39
+ ```
40
+
41
+ ### Supported platforms
42
+
43
+ V1 targets macOS and Linux first.
44
+
45
+ ## External tool prerequisites
46
+
47
+ Archon does not bundle external developer tools.
48
+
49
+ Required or commonly expected tools:
50
+
51
+ | Tool | Why it matters |
52
+ | --- | --- |
53
+ | `git` | Required for project repositories, worktrees, diffing, and commit operations |
54
+ | `opencode` | Required for OpenCode plan/build/chat/review workflows |
55
+ | `claude` | Required for Claude-backed workflows |
56
+ | `gh` | Required for GitHub PR and review integration |
57
+
58
+ If one of the optional AI tools is missing, Archon should still start, but features that depend on that binary will return actionable errors.
59
+
60
+ ## How it works
61
+
62
+ Tickets move through three states:
63
+
64
+ 1. **Plan** — write a title and description for the feature or task
65
+ 2. **Build** — the server spawns your configured AI tool in a new git worktree on a dedicated branch; real-time logs stream back to the UI
66
+ 3. **Review** — an agent conducts a code review and fixes any issues found
67
+
68
+ The frontend is a React kanban board. The backend is an Express server with a SQLite database and Socket.io for live status updates.
69
+
70
+ ## Development
71
+
72
+ For normal source development, use the repo checkout workflow instead of the packaged CLI.
73
+
74
+ Install dependencies from the repository root:
75
+
76
+ ```bash
77
+ npm install
78
+ ```
79
+
80
+ Start dev mode:
81
+
82
+ ```bash
83
+ npm run start:dev
84
+ ```
85
+
86
+ `npm run start:dev` is the primary local development command. It keeps the Express server and Vite frontend as separate processes and configures the dev database plus API base URL overrides for you.
87
+
88
+ Other useful commands:
89
+
90
+ ```bash
91
+ npm run build
92
+ npm test
93
+ npm run smoke:package
94
+ ```
95
+
96
+ `npm run smoke:package` builds the package, installs the packed tarball into a temporary npm prefix, runs `archon --version`, starts `archon --no-open`, fetches the app shell and an API route, then shuts it down.
97
+
98
+ ## Project Setup
99
+
100
+ After Archon starts, create a project from the UI:
101
+
102
+ 1. Click `Create project`
103
+ 2. Enter a project name and repository path
104
+ 3. Optionally click `Auto-config` to inspect top-level repo files and fill any empty setup, service, or worktree sync settings
105
+ 4. Optionally open `Advanced settings` to review or edit setup, service, worktree sync, and IDE settings
106
+ 5. Save the project, select it in the project switcher, and create tickets inside that project
107
+
108
+ ## Project configuration
109
+
110
+ Archon stores repository and run settings per project in its database. Use the project form in the UI to configure each project.
111
+
112
+ ### Required fields
113
+
114
+ | Field | What to enter |
115
+ |-------|---------------|
116
+ | `Project name` | The label shown in the project switcher, such as `Platform API` or `Marketing Site` |
117
+ | `Repository path` | Any path inside the target git repository; Archon resolves it to the repo root before saving |
118
+
119
+ `Auto-config` is available in both the create and edit project modals. It uses the currently selected global tool, reads only top-level README/config files, and fills only empty `Setup commands`, `Services`, and `Worktree sync` sections. It never changes `IDE command`.
120
+
121
+ ### Advanced fields
122
+
123
+ | Field | Guidance |
124
+ |-------|----------|
125
+ | `Setup commands` | Optional newline-separated commands that run inside the new worktree before any services start. Use this for dependency install, code generation, or other one-time setup such as `pnpm install` or `pnpm db:generate`. |
126
+ | `Worktree sync` | Optional file sync rules that run only when Archon creates a worktree for the first time. Each entry can either copy a file or directory into the new worktree, or symlink back to the project repo. |
127
+ | `Services` | Optional background processes to launch after setup completes. Each service needs a command, and can also include a `cwd`. |
128
+ | `cwd` | Optional working directory for a service, relative to the worktree root. For example, use `client` for a frontend dev server or `server` for an API process. Leave it blank to run from the worktree root. |
129
+ | `IDE command` | Optional command to open the finished worktree in your editor after services start, such as `code --reuse-window` or `idea`. |
130
+
131
+ Worktree sync entries are stored as `{ source, target?, mode }` objects. `source` is required, `target` is optional and defaults to the same path, and `mode` is either `copy` or `symlink`.
132
+
133
+ Service entries are stored as `{ cmd, cwd }` objects. `cmd` is required, while `cwd` is optional.
134
+
135
+ ### Example project setup
136
+
137
+ ```text
138
+ Project name: My App
139
+ Repository path: /Users/you/projects/my-app
140
+ Setup commands:
141
+ pnpm install
142
+ pnpm db:generate
143
+
144
+ Worktree sync:
145
+ - source: node_modules
146
+ mode: symlink
147
+ - source: .env
148
+ mode: copy
149
+
150
+ Services:
151
+ - cmd: pnpm dev
152
+ cwd: client
153
+ - cmd: pnpm dev
154
+ cwd: server
155
+
156
+ IDE command: code --reuse-window
157
+ ```
158
+
159
+ Each ticket gets its own worktree and branch derived from the ticket title, using the active project's repository settings. Archon reuses an existing local branch when one already exists; otherwise it creates a new branch for the worktree.
160
+
161
+ ## Data And Migration
162
+
163
+ Archon keeps runtime state out of the installed package tree.
164
+
165
+ Default database locations:
166
+
167
+ | Platform | Default path |
168
+ | --- | --- |
169
+ | macOS | `~/Library/Application Support/Archon/archon.db` |
170
+ | Linux | `~/.local/share/archon/archon.db` |
171
+
172
+ You can override the database location with `DB_PATH`.
173
+
174
+ ### Migrating from an existing dev database
175
+
176
+ Older local development runs may already have data in one of these files:
177
+
178
+ - `server/archon-dev.db`
179
+ - `server/archon.db`
180
+
181
+ Recommended manual migration flow:
182
+
183
+ 1. Stop all Archon dev and production processes.
184
+ 2. Back up the source database and any current production database.
185
+ 3. Create the production app-data directory if it does not exist.
186
+ 4. Copy the chosen development database to the production database path.
187
+ 5. If SQLite sidecar files such as `-wal` or `-shm` exist, only copy them while Archon is fully stopped.
188
+ 6. Start Archon normally and let its startup schema checks apply any missing migrations.
189
+
190
+ If you want to run production Archon against an existing database temporarily, launch it with:
191
+
192
+ ```bash
193
+ DB_PATH=/path/to/archon-dev.db archon
194
+ ```
195
+
196
+ ## Release Process
197
+
198
+ Archon uses a manual GitHub Actions release workflow.
199
+
200
+ ### GitHub setup
201
+
202
+ 1. Publish the npm package scope or package name from the npm account or organization that owns `@straiffi/archon`.
203
+ 2. Add an `NPM_TOKEN` repository secret in GitHub with publish access to that package.
204
+ 3. Run the `Release` workflow from the `main` branch.
205
+ 4. Choose one of `patch`, `minor`, or `major` for `release_type`.
206
+
207
+ The workflow will:
208
+
209
+ 1. install dependencies
210
+ 2. run type checks, lint, tests, build, tarball validation, and packaged-install smoke checks
211
+ 3. run `npm version <release_type>`
212
+ 4. publish with `npm publish --access public`
213
+ 5. push the version commit and git tag back to GitHub
214
+
215
+ ### Local release dry run
216
+
217
+ Before running the workflow, you can validate the release path locally with:
218
+
219
+ ```bash
220
+ npm install
221
+ npm run build
222
+ npm run smoke:package
223
+ npm pack --dry-run
224
+ ```
package/dist/cli.js ADDED
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createServer } from 'node:net'
4
+ import { spawn } from 'node:child_process'
5
+ import { existsSync, readFileSync } from 'node:fs'
6
+ import { dirname, resolve } from 'node:path'
7
+ import { fileURLToPath, pathToFileURL } from 'node:url'
8
+
9
+ const CURRENT_FILE_PATH = fileURLToPath(import.meta.url)
10
+ const CURRENT_DIRECTORY = dirname(CURRENT_FILE_PATH)
11
+ const PACKAGE_JSON_PATH = resolve(CURRENT_DIRECTORY, '../package.json')
12
+ const DIST_SERVER_ENTRY_PATH = resolve(CURRENT_DIRECTORY, './server/index.js')
13
+ const DEFAULT_PORT = 43110
14
+ const PACKAGE_NAME = '@straiffi/archon'
15
+
16
+ const readPackageVersion = () => {
17
+ try {
18
+ const packageJson = JSON.parse(readFileSync(PACKAGE_JSON_PATH, 'utf8'))
19
+ return typeof packageJson?.version === 'string' ? packageJson.version : '0.0.0'
20
+ } catch {
21
+ return '0.0.0'
22
+ }
23
+ }
24
+
25
+ const printUsage = () => {
26
+ console.log(`Usage: archon [--port <port>] [--no-open]\n archon update\n archon --version`)
27
+ }
28
+
29
+ const parsePort = (value) => {
30
+ const parsed = Number(value)
31
+ if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
32
+ throw new Error(`Invalid port: ${value}`)
33
+ }
34
+
35
+ return parsed
36
+ }
37
+
38
+ const parseArgs = (argv) => {
39
+ let command = 'start'
40
+ let port = DEFAULT_PORT
41
+ let shouldOpenBrowser = true
42
+
43
+ for (let index = 0; index < argv.length; index += 1) {
44
+ const argument = argv[index]
45
+
46
+ if (argument === 'update') {
47
+ command = 'update'
48
+ continue
49
+ }
50
+
51
+ if (argument === '--version' || argument === '-v') {
52
+ command = 'version'
53
+ continue
54
+ }
55
+
56
+ if (argument === '--help' || argument === '-h') {
57
+ command = 'help'
58
+ continue
59
+ }
60
+
61
+ if (argument === '--no-open') {
62
+ shouldOpenBrowser = false
63
+ continue
64
+ }
65
+
66
+ if (argument === '--port') {
67
+ const nextValue = argv[index + 1]
68
+ if (!nextValue) {
69
+ throw new Error('--port requires a value')
70
+ }
71
+
72
+ port = parsePort(nextValue)
73
+ index += 1
74
+ continue
75
+ }
76
+
77
+ throw new Error(`Unknown argument: ${argument}`)
78
+ }
79
+
80
+ return {
81
+ command,
82
+ port,
83
+ shouldOpenBrowser,
84
+ }
85
+ }
86
+
87
+ const canUsePort = (port) => {
88
+ return new Promise(resolvePromise => {
89
+ const probeServer = createServer()
90
+
91
+ probeServer.once('error', () => {
92
+ resolvePromise(false)
93
+ })
94
+
95
+ probeServer.once('listening', () => {
96
+ probeServer.close(() => {
97
+ resolvePromise(true)
98
+ })
99
+ })
100
+
101
+ probeServer.listen(port, '127.0.0.1')
102
+ })
103
+ }
104
+
105
+ const findAvailablePort = async (startingPort) => {
106
+ let port = startingPort
107
+
108
+ while (!(await canUsePort(port))) {
109
+ port += 1
110
+ }
111
+
112
+ return port
113
+ }
114
+
115
+ const openBrowser = (url) => {
116
+ const openCommand = process.platform === 'darwin'
117
+ ? ['open', [url]]
118
+ : process.platform === 'win32'
119
+ ? ['cmd', ['/c', 'start', '', url]]
120
+ : ['xdg-open', [url]]
121
+
122
+ const [command, args] = openCommand
123
+
124
+ return new Promise(resolvePromise => {
125
+ const child = spawn(command, args, {
126
+ stdio: 'ignore',
127
+ detached: true,
128
+ })
129
+
130
+ child.on('error', () => resolvePromise(false))
131
+ child.unref()
132
+ resolvePromise(true)
133
+ })
134
+ }
135
+
136
+ const runUpdate = () => {
137
+ return new Promise(resolvePromise => {
138
+ const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm'
139
+ const child = spawn(npmCommand, ['install', '-g', `${PACKAGE_NAME}@latest`], {
140
+ stdio: 'inherit',
141
+ env: process.env,
142
+ })
143
+
144
+ child.on('error', () => {
145
+ console.error(`Update failed. Run: npm install -g ${PACKAGE_NAME}@latest`)
146
+ resolvePromise(1)
147
+ })
148
+
149
+ child.on('exit', code => {
150
+ if (code && code !== 0) {
151
+ console.error(`Update failed. Run: npm install -g ${PACKAGE_NAME}@latest`)
152
+ }
153
+
154
+ resolvePromise(code ?? 0)
155
+ })
156
+ })
157
+ }
158
+
159
+ const startArchon = async ({ port, shouldOpenBrowser }) => {
160
+ if (!existsSync(DIST_SERVER_ENTRY_PATH)) {
161
+ throw new Error('Archon is not built yet. Run `npm run build` before starting the packaged runtime from a source checkout.')
162
+ }
163
+
164
+ process.env.PORT = String(await findAvailablePort(port))
165
+
166
+ const serverModule = await import(pathToFileURL(DIST_SERVER_ENTRY_PATH).href)
167
+ if (typeof serverModule.registerShutdownHandlers === 'function') {
168
+ serverModule.registerShutdownHandlers()
169
+ }
170
+
171
+ const result = await serverModule.startServer({ port: Number(process.env.PORT) })
172
+
173
+ console.log(`Archon running at ${result.url}`)
174
+ console.log('Press Ctrl+C to stop.')
175
+
176
+ if (shouldOpenBrowser) {
177
+ const opened = await openBrowser(result.url)
178
+ if (!opened) {
179
+ console.warn(`Unable to open a browser automatically. Open ${result.url} manually.`)
180
+ }
181
+ }
182
+ }
183
+
184
+ const main = async () => {
185
+ let parsedArgs
186
+
187
+ try {
188
+ parsedArgs = parseArgs(process.argv.slice(2))
189
+ } catch (error) {
190
+ console.error(error instanceof Error ? error.message : String(error))
191
+ printUsage()
192
+ process.exit(1)
193
+ }
194
+
195
+ if (parsedArgs.command === 'help') {
196
+ printUsage()
197
+ return
198
+ }
199
+
200
+ if (parsedArgs.command === 'version') {
201
+ console.log(readPackageVersion())
202
+ return
203
+ }
204
+
205
+ if (parsedArgs.command === 'update') {
206
+ process.exitCode = await runUpdate()
207
+ return
208
+ }
209
+
210
+ await startArchon(parsedArgs)
211
+ }
212
+
213
+ void main().catch(error => {
214
+ console.error(error instanceof Error ? error.message : String(error))
215
+ process.exit(1)
216
+ })