bonecode 1.0.0 → 1.2.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/LICENSE +21 -0
- package/README.md +64 -50
- package/bin/bonecode +47 -42
- package/bone/output/agent/.dockerignore +7 -7
- package/bone/output/agent/.env.example +36 -36
- package/bone/output/agent/.github/workflows/ci.yaml +58 -58
- package/bone/output/agent/AgentDomain.bone.map +349 -349
- package/bone/output/agent/AgentDomain.postman_collection.json +957 -957
- package/bone/output/agent/Dockerfile +22 -22
- package/bone/output/agent/README.md +47 -47
- package/bone/output/agent/admin/index.html +739 -739
- package/bone/output/agent/docker-compose.yaml +22 -22
- package/bone/output/agent/k8s/deployment.yaml +75 -75
- package/bone/output/agent/migrations/agent.sql +36 -36
- package/bone/output/agent/migrations/agent_instance.sql +36 -36
- package/bone/output/agent/migrations/audit_log.sql +18 -18
- package/bone/output/agent/migrations/build_step.sql +34 -34
- package/bone/output/agent/migrations/event_outbox.sql +31 -31
- package/bone/output/agent/migrations/plan.sql +30 -30
- package/bone/output/agent/migrations/task.sql +30 -30
- package/bone/output/agent/migrations/tool_call.sql +33 -33
- package/bone/output/agent/openapi.yaml +1116 -1116
- package/bone/output/agent/package.json +35 -35
- package/bone/output/agent/schema.graphql +233 -233
- package/bone/output/agent/sdk/client.ts +231 -231
- package/bone/output/agent/src/algorithms.ts +2 -2
- package/bone/output/agent/src/audit.ts +44 -44
- package/bone/output/agent/src/auth.ts +57 -57
- package/bone/output/agent/src/cron.ts +12 -12
- package/bone/output/agent/src/db.ts +31 -31
- package/bone/output/agent/src/debug.ts +66 -66
- package/bone/output/agent/src/events.ts +243 -243
- package/bone/output/agent/src/extensions.ts +54 -54
- package/bone/output/agent/src/failure_rules.ts +322 -322
- package/bone/output/agent/src/flows.ts +168 -168
- package/bone/output/agent/src/health.ts +43 -43
- package/bone/output/agent/src/index.ts +99 -99
- package/bone/output/agent/src/logger.ts +69 -66
- package/bone/output/agent/src/metrics.ts +75 -75
- package/bone/output/agent/src/migrate.ts +351 -351
- package/bone/output/agent/src/migration_diff.ts +108 -108
- package/bone/output/agent/src/notify.ts +125 -125
- package/bone/output/agent/src/routes/plan.ts +91 -91
- package/bone/output/agent/src/routes/task.ts +105 -105
- package/bone/output/agent/src/routes/tool_call.ts +166 -166
- package/bone/output/agent/src/schemas.ts +384 -384
- package/bone/output/agent/src/state_machines/agent_instance.ts +24 -24
- package/bone/output/agent/src/state_machines/build_step.ts +22 -22
- package/bone/output/agent/src/state_machines/plan.ts +22 -22
- package/bone/output/agent/src/state_machines/task.ts +22 -22
- package/bone/output/agent/src/state_machines/tool_call.ts +22 -22
- package/bone/output/agent/src/tests.ts +361 -361
- package/bone/output/agent/src/websocket.ts +200 -200
- package/bone/output/agent/tsconfig.json +24 -24
- package/bone/output/rag/.dockerignore +7 -7
- package/bone/output/rag/.env.example +36 -36
- package/bone/output/rag/.github/workflows/ci.yaml +58 -58
- package/bone/output/rag/Dockerfile +22 -22
- package/bone/output/rag/RAGDomain.bone.map +286 -286
- package/bone/output/rag/RAGDomain.postman_collection.json +922 -922
- package/bone/output/rag/README.md +47 -47
- package/bone/output/rag/admin/index.html +817 -817
- package/bone/output/rag/docker-compose.yaml +22 -22
- package/bone/output/rag/k8s/deployment.yaml +75 -75
- package/bone/output/rag/migrations/audit_log.sql +18 -18
- package/bone/output/rag/migrations/code_chunk.sql +34 -34
- package/bone/output/rag/migrations/code_file.sql +33 -33
- package/bone/output/rag/migrations/event_outbox.sql +31 -31
- package/bone/output/rag/migrations/indexing_job.sql +33 -33
- package/bone/output/rag/migrations/knowledge_base.sql +35 -35
- package/bone/output/rag/migrations/memory_entry.sql +34 -34
- package/bone/output/rag/openapi.yaml +1097 -1097
- package/bone/output/rag/package.json +35 -35
- package/bone/output/rag/schema.graphql +245 -245
- package/bone/output/rag/sdk/client.ts +234 -234
- package/bone/output/rag/src/algorithms.ts +2 -2
- package/bone/output/rag/src/audit.ts +37 -37
- package/bone/output/rag/src/auth.ts +57 -57
- package/bone/output/rag/src/cron.ts +12 -12
- package/bone/output/rag/src/db.ts +31 -31
- package/bone/output/rag/src/debug.ts +66 -66
- package/bone/output/rag/src/events.ts +243 -243
- package/bone/output/rag/src/extensions.ts +350 -350
- package/bone/output/rag/src/failure_rules.ts +314 -314
- package/bone/output/rag/src/flows.ts +239 -239
- package/bone/output/rag/src/health.ts +43 -43
- package/bone/output/rag/src/index.ts +94 -94
- package/bone/output/rag/src/logger.ts +69 -66
- package/bone/output/rag/src/metrics.ts +75 -75
- package/bone/output/rag/src/migrate.ts +363 -363
- package/bone/output/rag/src/migration_diff.ts +108 -108
- package/bone/output/rag/src/notify.ts +99 -99
- package/bone/output/rag/src/routes/code_chunk.ts +75 -75
- package/bone/output/rag/src/routes/code_file.ts +101 -101
- package/bone/output/rag/src/routes/indexing_job.ts +87 -87
- package/bone/output/rag/src/routes/knowledge_base.ts +230 -230
- package/bone/output/rag/src/routes/memory_entry.ts +87 -87
- package/bone/output/rag/src/schemas.ts +394 -394
- package/bone/output/rag/src/state_machines/code_file.ts +23 -23
- package/bone/output/rag/src/state_machines/indexing_job.ts +22 -22
- package/bone/output/rag/src/state_machines/knowledge_base.ts +23 -23
- package/bone/output/rag/src/state_machines/memory_entry.ts +20 -20
- package/bone/output/rag/src/tests.ts +339 -339
- package/bone/output/rag/tsconfig.json +24 -24
- package/bone/output/session/.dockerignore +7 -7
- package/bone/output/session/.env.example +36 -36
- package/bone/output/session/.github/workflows/ci.yaml +58 -58
- package/bone/output/session/Dockerfile +22 -22
- package/bone/output/session/README.md +47 -47
- package/bone/output/session/SessionDomain.bone.map +349 -349
- package/bone/output/session/SessionDomain.postman_collection.json +957 -957
- package/bone/output/session/admin/index.html +666 -666
- package/bone/output/session/docker-compose.yaml +22 -22
- package/bone/output/session/k8s/deployment.yaml +75 -75
- package/bone/output/session/migrations/audit_log.sql +18 -18
- package/bone/output/session/migrations/event_outbox.sql +31 -31
- package/bone/output/session/migrations/message.sql +31 -31
- package/bone/output/session/migrations/part.sql +28 -28
- package/bone/output/session/migrations/permission.sql +28 -28
- package/bone/output/session/migrations/project.sql +28 -28
- package/bone/output/session/migrations/session.sql +38 -38
- package/bone/output/session/openapi.yaml +1101 -1101
- package/bone/output/session/package.json +35 -35
- package/bone/output/session/schema.graphql +222 -222
- package/bone/output/session/sdk/client.ts +225 -225
- package/bone/output/session/src/algorithms.ts +2 -2
- package/bone/output/session/src/audit.ts +44 -44
- package/bone/output/session/src/auth.ts +57 -57
- package/bone/output/session/src/cron.ts +12 -12
- package/bone/output/session/src/db.ts +31 -31
- package/bone/output/session/src/debug.ts +66 -66
- package/bone/output/session/src/events.ts +270 -270
- package/bone/output/session/src/extensions.ts +215 -215
- package/bone/output/session/src/failure_rules.ts +283 -283
- package/bone/output/session/src/flows.ts +168 -168
- package/bone/output/session/src/health.ts +43 -43
- package/bone/output/session/src/index.ts +99 -99
- package/bone/output/session/src/logger.ts +67 -66
- package/bone/output/session/src/metrics.ts +75 -75
- package/bone/output/session/src/migrate.ts +331 -331
- package/bone/output/session/src/migration_diff.ts +108 -108
- package/bone/output/session/src/notify.ts +112 -112
- package/bone/output/session/src/routes/message.ts +93 -93
- package/bone/output/session/src/routes/part.ts +79 -79
- package/bone/output/session/src/routes/permission.ts +79 -79
- package/bone/output/session/src/routes/project.ts +79 -79
- package/bone/output/session/src/routes/session.ts +294 -294
- package/bone/output/session/src/schemas.ts +357 -357
- package/bone/output/session/src/state_machines/session.ts +23 -23
- package/bone/output/session/src/tests.ts +325 -325
- package/bone/output/session/src/websocket.ts +223 -200
- package/bone/output/session/tsconfig.json +24 -24
- package/bone/output/workspace/.dockerignore +7 -7
- package/bone/output/workspace/.env.example +36 -36
- package/bone/output/workspace/.github/workflows/ci.yaml +58 -58
- package/bone/output/workspace/Dockerfile +22 -22
- package/bone/output/workspace/README.md +45 -45
- package/bone/output/workspace/WorkspaceDomain.bone.map +188 -188
- package/bone/output/workspace/WorkspaceDomain.postman_collection.json +620 -620
- package/bone/output/workspace/admin/index.html +484 -484
- package/bone/output/workspace/docker-compose.yaml +22 -22
- package/bone/output/workspace/k8s/deployment.yaml +75 -75
- package/bone/output/workspace/migrations/audit_log.sql +18 -18
- package/bone/output/workspace/migrations/codebase.sql +34 -34
- package/bone/output/workspace/migrations/event_outbox.sql +31 -31
- package/bone/output/workspace/migrations/snapshot.sql +32 -32
- package/bone/output/workspace/migrations/workspace.sql +33 -33
- package/bone/output/workspace/openapi.yaml +721 -721
- package/bone/output/workspace/package.json +35 -35
- package/bone/output/workspace/schema.graphql +153 -153
- package/bone/output/workspace/sdk/client.ts +155 -155
- package/bone/output/workspace/src/algorithms.ts +2 -2
- package/bone/output/workspace/src/audit.ts +37 -37
- package/bone/output/workspace/src/auth.ts +57 -57
- package/bone/output/workspace/src/cron.ts +12 -12
- package/bone/output/workspace/src/db.ts +31 -31
- package/bone/output/workspace/src/debug.ts +66 -66
- package/bone/output/workspace/src/events.ts +243 -243
- package/bone/output/workspace/src/extensions.ts +44 -44
- package/bone/output/workspace/src/failure_rules.ts +152 -152
- package/bone/output/workspace/src/health.ts +43 -43
- package/bone/output/workspace/src/index.ts +88 -88
- package/bone/output/workspace/src/logger.ts +69 -66
- package/bone/output/workspace/src/metrics.ts +75 -75
- package/bone/output/workspace/src/migrate.ts +219 -219
- package/bone/output/workspace/src/migration_diff.ts +108 -108
- package/bone/output/workspace/src/notify.ts +73 -73
- package/bone/output/workspace/src/routes/codebase.ts +87 -87
- package/bone/output/workspace/src/routes/snapshot.ts +127 -127
- package/bone/output/workspace/src/routes/workspace.ts +190 -190
- package/bone/output/workspace/src/schemas.ts +231 -231
- package/bone/output/workspace/src/state_machines/codebase.ts +21 -21
- package/bone/output/workspace/src/state_machines/snapshot.ts +20 -20
- package/bone/output/workspace/src/state_machines/workspace.ts +21 -21
- package/bone/output/workspace/src/tests.ts +248 -248
- package/bone/output/workspace/tsconfig.json +24 -24
- package/compat/opencode_adapter.ts +282 -34
- package/dist/bone/output/agent/src/algorithms.d.ts +1 -0
- package/dist/bone/output/agent/src/algorithms.js +3 -0
- package/dist/bone/output/agent/src/algorithms.js.map +1 -0
- package/dist/bone/output/agent/src/audit.d.ts +3 -0
- package/dist/bone/output/agent/src/audit.js +40 -0
- package/dist/bone/output/agent/src/audit.js.map +1 -0
- package/dist/bone/output/agent/src/auth.d.ts +8 -0
- package/dist/bone/output/agent/src/auth.js +56 -0
- package/dist/bone/output/agent/src/auth.js.map +1 -0
- package/dist/bone/output/agent/src/db.d.ts +6 -0
- package/dist/bone/output/agent/src/db.js +63 -0
- package/dist/bone/output/agent/src/db.js.map +1 -0
- package/dist/bone/output/agent/src/events.d.ts +25 -0
- package/dist/bone/output/agent/src/events.js +184 -0
- package/dist/bone/output/agent/src/events.js.map +1 -0
- package/dist/bone/output/agent/src/logger.d.ts +28 -0
- package/dist/bone/output/agent/src/logger.js +45 -0
- package/dist/bone/output/agent/src/logger.js.map +1 -0
- package/dist/bone/output/agent/src/metrics.d.ts +5 -0
- package/dist/bone/output/agent/src/metrics.js +60 -0
- package/dist/bone/output/agent/src/metrics.js.map +1 -0
- package/dist/bone/output/agent/src/routes/agent_instance.d.ts +1 -0
- package/dist/bone/output/agent/src/routes/agent_instance.js +253 -0
- package/dist/bone/output/agent/src/routes/agent_instance.js.map +1 -0
- package/dist/bone/output/agent/src/routes/build_step.d.ts +1 -0
- package/dist/bone/output/agent/src/routes/build_step.js +133 -0
- package/dist/bone/output/agent/src/routes/build_step.js.map +1 -0
- package/dist/bone/output/agent/src/routes/plan.d.ts +1 -0
- package/dist/bone/output/agent/src/routes/plan.js +119 -0
- package/dist/bone/output/agent/src/routes/plan.js.map +1 -0
- package/dist/bone/output/agent/src/routes/task.d.ts +1 -0
- package/dist/bone/output/agent/src/routes/task.js +133 -0
- package/dist/bone/output/agent/src/routes/task.js.map +1 -0
- package/dist/bone/output/agent/src/routes/tool_call.d.ts +1 -0
- package/dist/bone/output/agent/src/routes/tool_call.js +190 -0
- package/dist/bone/output/agent/src/routes/tool_call.js.map +1 -0
- package/dist/bone/output/agent/src/state_machines/agent_instance.d.ts +9 -0
- package/dist/bone/output/agent/src/state_machines/agent_instance.js +22 -0
- package/dist/bone/output/agent/src/state_machines/agent_instance.js.map +1 -0
- package/dist/bone/output/agent/src/state_machines/build_step.d.ts +9 -0
- package/dist/bone/output/agent/src/state_machines/build_step.js +20 -0
- package/dist/bone/output/agent/src/state_machines/build_step.js.map +1 -0
- package/dist/bone/output/agent/src/state_machines/plan.d.ts +9 -0
- package/dist/bone/output/agent/src/state_machines/plan.js +20 -0
- package/dist/bone/output/agent/src/state_machines/plan.js.map +1 -0
- package/dist/bone/output/agent/src/state_machines/task.d.ts +9 -0
- package/dist/bone/output/agent/src/state_machines/task.js +20 -0
- package/dist/bone/output/agent/src/state_machines/task.js.map +1 -0
- package/dist/bone/output/agent/src/state_machines/tool_call.d.ts +9 -0
- package/dist/bone/output/agent/src/state_machines/tool_call.js +20 -0
- package/dist/bone/output/agent/src/state_machines/tool_call.js.map +1 -0
- package/dist/bone/output/rag/src/algorithms.d.ts +1 -0
- package/dist/bone/output/rag/src/algorithms.js +3 -0
- package/dist/bone/output/rag/src/algorithms.js.map +1 -0
- package/dist/bone/output/rag/src/auth.d.ts +8 -0
- package/dist/bone/output/rag/src/auth.js +56 -0
- package/dist/bone/output/rag/src/auth.js.map +1 -0
- package/dist/bone/output/rag/src/db.d.ts +6 -0
- package/dist/bone/output/rag/src/db.js +63 -0
- package/dist/bone/output/rag/src/db.js.map +1 -0
- package/dist/bone/output/rag/src/events.d.ts +25 -0
- package/dist/bone/output/rag/src/events.js +184 -0
- package/dist/bone/output/rag/src/events.js.map +1 -0
- package/dist/bone/output/rag/src/extensions.d.ts +83 -0
- package/dist/bone/output/rag/src/extensions.js +329 -0
- package/dist/bone/output/rag/src/extensions.js.map +1 -0
- package/dist/bone/output/rag/src/flows.d.ts +24 -0
- package/dist/bone/output/rag/src/flows.js +236 -0
- package/dist/bone/output/rag/src/flows.js.map +1 -0
- package/dist/bone/output/rag/src/logger.d.ts +28 -0
- package/dist/bone/output/rag/src/logger.js +45 -0
- package/dist/bone/output/rag/src/logger.js.map +1 -0
- package/dist/bone/output/rag/src/metrics.d.ts +5 -0
- package/dist/bone/output/rag/src/metrics.js +60 -0
- package/dist/bone/output/rag/src/metrics.js.map +1 -0
- package/dist/bone/output/rag/src/routes/code_chunk.d.ts +1 -0
- package/dist/bone/output/rag/src/routes/code_chunk.js +100 -0
- package/dist/bone/output/rag/src/routes/code_chunk.js.map +1 -0
- package/dist/bone/output/rag/src/routes/code_file.d.ts +1 -0
- package/dist/bone/output/rag/src/routes/code_file.js +127 -0
- package/dist/bone/output/rag/src/routes/code_file.js.map +1 -0
- package/dist/bone/output/rag/src/routes/indexing_job.d.ts +1 -0
- package/dist/bone/output/rag/src/routes/indexing_job.js +113 -0
- package/dist/bone/output/rag/src/routes/indexing_job.js.map +1 -0
- package/dist/bone/output/rag/src/routes/knowledge_base.d.ts +1 -0
- package/dist/bone/output/rag/src/routes/knowledge_base.js +242 -0
- package/dist/bone/output/rag/src/routes/knowledge_base.js.map +1 -0
- package/dist/bone/output/rag/src/routes/memory_entry.d.ts +1 -0
- package/dist/bone/output/rag/src/routes/memory_entry.js +113 -0
- package/dist/bone/output/rag/src/routes/memory_entry.js.map +1 -0
- package/dist/bone/output/rag/src/state_machines/code_file.d.ts +9 -0
- package/dist/bone/output/rag/src/state_machines/code_file.js +21 -0
- package/dist/bone/output/rag/src/state_machines/code_file.js.map +1 -0
- package/dist/bone/output/rag/src/state_machines/indexing_job.d.ts +9 -0
- package/dist/bone/output/rag/src/state_machines/indexing_job.js +20 -0
- package/dist/bone/output/rag/src/state_machines/indexing_job.js.map +1 -0
- package/dist/bone/output/rag/src/state_machines/knowledge_base.d.ts +9 -0
- package/dist/bone/output/rag/src/state_machines/knowledge_base.js +21 -0
- package/dist/bone/output/rag/src/state_machines/knowledge_base.js.map +1 -0
- package/dist/bone/output/rag/src/state_machines/memory_entry.d.ts +9 -0
- package/dist/bone/output/rag/src/state_machines/memory_entry.js +18 -0
- package/dist/bone/output/rag/src/state_machines/memory_entry.js.map +1 -0
- package/dist/bone/output/session/src/algorithms.d.ts +1 -0
- package/dist/bone/output/session/src/algorithms.js +3 -0
- package/dist/bone/output/session/src/algorithms.js.map +1 -0
- package/dist/bone/output/session/src/audit.d.ts +3 -0
- package/dist/bone/output/session/src/audit.js +40 -0
- package/dist/bone/output/session/src/audit.js.map +1 -0
- package/dist/bone/output/session/src/auth.d.ts +8 -0
- package/dist/bone/output/session/src/auth.js +56 -0
- package/dist/bone/output/session/src/auth.js.map +1 -0
- package/dist/bone/output/session/src/db.d.ts +6 -0
- package/dist/bone/output/session/src/db.js +63 -0
- package/dist/bone/output/session/src/db.js.map +1 -0
- package/dist/bone/output/session/src/events.d.ts +26 -0
- package/dist/bone/output/session/src/events.js +212 -0
- package/dist/bone/output/session/src/events.js.map +1 -0
- package/dist/bone/output/session/src/extensions.d.ts +41 -0
- package/dist/bone/output/session/src/extensions.js +217 -0
- package/dist/bone/output/session/src/extensions.js.map +1 -0
- package/dist/bone/output/session/src/logger.d.ts +28 -0
- package/dist/bone/output/session/src/logger.js +44 -0
- package/dist/bone/output/session/src/logger.js.map +1 -0
- package/dist/bone/output/session/src/metrics.d.ts +5 -0
- package/dist/bone/output/session/src/metrics.js +60 -0
- package/dist/bone/output/session/src/metrics.js.map +1 -0
- package/dist/bone/output/session/src/routes/message.d.ts +1 -0
- package/dist/bone/output/session/src/routes/message.js +120 -0
- package/dist/bone/output/session/src/routes/message.js.map +1 -0
- package/dist/bone/output/session/src/routes/part.d.ts +1 -0
- package/dist/bone/output/session/src/routes/part.js +106 -0
- package/dist/bone/output/session/src/routes/part.js.map +1 -0
- package/dist/bone/output/session/src/routes/permission.d.ts +1 -0
- package/dist/bone/output/session/src/routes/permission.js +106 -0
- package/dist/bone/output/session/src/routes/permission.js.map +1 -0
- package/dist/bone/output/session/src/routes/project.d.ts +1 -0
- package/dist/bone/output/session/src/routes/project.js +106 -0
- package/dist/bone/output/session/src/routes/project.js.map +1 -0
- package/dist/bone/output/session/src/routes/session.d.ts +1 -0
- package/dist/bone/output/session/src/routes/session.js +308 -0
- package/dist/bone/output/session/src/routes/session.js.map +1 -0
- package/dist/bone/output/session/src/state_machines/session.d.ts +9 -0
- package/dist/bone/output/session/src/state_machines/session.js +21 -0
- package/dist/bone/output/session/src/state_machines/session.js.map +1 -0
- package/dist/bone/output/session/src/websocket.d.ts +15 -0
- package/dist/bone/output/session/src/websocket.js +215 -0
- package/dist/bone/output/session/src/websocket.js.map +1 -0
- package/dist/bone/output/workspace/src/algorithms.d.ts +1 -0
- package/dist/bone/output/workspace/src/algorithms.js +3 -0
- package/dist/bone/output/workspace/src/algorithms.js.map +1 -0
- package/dist/bone/output/workspace/src/auth.d.ts +8 -0
- package/dist/bone/output/workspace/src/auth.js +56 -0
- package/dist/bone/output/workspace/src/auth.js.map +1 -0
- package/dist/bone/output/workspace/src/db.d.ts +6 -0
- package/dist/bone/output/workspace/src/db.js +63 -0
- package/dist/bone/output/workspace/src/db.js.map +1 -0
- package/dist/bone/output/workspace/src/events.d.ts +25 -0
- package/dist/bone/output/workspace/src/events.js +184 -0
- package/dist/bone/output/workspace/src/events.js.map +1 -0
- package/dist/bone/output/workspace/src/logger.d.ts +28 -0
- package/dist/bone/output/workspace/src/logger.js +45 -0
- package/dist/bone/output/workspace/src/logger.js.map +1 -0
- package/dist/bone/output/workspace/src/metrics.d.ts +5 -0
- package/dist/bone/output/workspace/src/metrics.js +60 -0
- package/dist/bone/output/workspace/src/metrics.js.map +1 -0
- package/dist/bone/output/workspace/src/routes/codebase.d.ts +1 -0
- package/dist/bone/output/workspace/src/routes/codebase.js +113 -0
- package/dist/bone/output/workspace/src/routes/codebase.js.map +1 -0
- package/dist/bone/output/workspace/src/routes/snapshot.d.ts +1 -0
- package/dist/bone/output/workspace/src/routes/snapshot.js +151 -0
- package/dist/bone/output/workspace/src/routes/snapshot.js.map +1 -0
- package/dist/bone/output/workspace/src/routes/workspace.d.ts +1 -0
- package/dist/bone/output/workspace/src/routes/workspace.js +209 -0
- package/dist/bone/output/workspace/src/routes/workspace.js.map +1 -0
- package/dist/bone/output/workspace/src/state_machines/codebase.d.ts +9 -0
- package/dist/bone/output/workspace/src/state_machines/codebase.js +19 -0
- package/dist/bone/output/workspace/src/state_machines/codebase.js.map +1 -0
- package/dist/bone/output/workspace/src/state_machines/snapshot.d.ts +9 -0
- package/dist/bone/output/workspace/src/state_machines/snapshot.js +18 -0
- package/dist/bone/output/workspace/src/state_machines/snapshot.js.map +1 -0
- package/dist/bone/output/workspace/src/state_machines/workspace.d.ts +9 -0
- package/dist/bone/output/workspace/src/state_machines/workspace.js +19 -0
- package/dist/bone/output/workspace/src/state_machines/workspace.js.map +1 -0
- package/dist/compat/opencode_adapter.d.ts +25 -0
- package/dist/compat/opencode_adapter.js +599 -0
- package/dist/compat/opencode_adapter.js.map +1 -0
- package/dist/extensions/chunker.d.ts +24 -0
- package/dist/extensions/chunker.js +360 -0
- package/dist/extensions/chunker.js.map +1 -0
- package/dist/extensions/embedding_provider.d.ts +18 -0
- package/dist/extensions/embedding_provider.js +150 -0
- package/dist/extensions/embedding_provider.js.map +1 -0
- package/dist/extensions/llm_provider.d.ts +33 -0
- package/dist/extensions/llm_provider.js +338 -0
- package/dist/extensions/llm_provider.js.map +1 -0
- package/dist/extensions/mcp_bridge.d.ts +44 -0
- package/dist/extensions/mcp_bridge.js +151 -0
- package/dist/extensions/mcp_bridge.js.map +1 -0
- package/dist/extensions/rag_search.d.ts +38 -0
- package/dist/extensions/rag_search.js +242 -0
- package/dist/extensions/rag_search.js.map +1 -0
- package/dist/extensions/snapshot.d.ts +14 -0
- package/dist/extensions/snapshot.js +158 -0
- package/dist/extensions/snapshot.js.map +1 -0
- package/dist/extensions/tool_executor.d.ts +28 -0
- package/dist/extensions/tool_executor.js +268 -0
- package/dist/extensions/tool_executor.js.map +1 -0
- package/dist/src/cli.d.ts +15 -0
- package/dist/src/cli.js +687 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +44 -0
- package/dist/src/config.js +165 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/context_builder.d.ts +51 -0
- package/dist/src/context_builder.js +558 -0
- package/dist/src/context_builder.js.map +1 -0
- package/dist/src/db_adapter.d.ts +24 -0
- package/dist/src/db_adapter.js +341 -0
- package/dist/src/db_adapter.js.map +1 -0
- package/dist/src/engine/session/compaction_logic.d.ts +11 -0
- package/dist/src/engine/session/compaction_logic.js +113 -0
- package/dist/src/engine/session/compaction_logic.js.map +1 -0
- package/dist/src/engine/session/instruction_loader.d.ts +5 -0
- package/dist/src/engine/session/instruction_loader.js +78 -0
- package/dist/src/engine/session/instruction_loader.js.map +1 -0
- package/dist/src/engine/session/overflow_check.d.ts +14 -0
- package/dist/src/engine/session/overflow_check.js +45 -0
- package/dist/src/engine/session/overflow_check.js.map +1 -0
- package/dist/src/engine/session/prompt.d.ts +45 -0
- package/dist/src/engine/session/prompt.js +584 -0
- package/dist/src/engine/session/prompt.js.map +1 -0
- package/dist/src/engine/session/provider_transform.d.ts +59 -0
- package/dist/src/engine/session/provider_transform.js +193 -0
- package/dist/src/engine/session/provider_transform.js.map +1 -0
- package/dist/src/engine/session/retry_logic.d.ts +12 -0
- package/dist/src/engine/session/retry_logic.js +72 -0
- package/dist/src/engine/session/retry_logic.js.map +1 -0
- package/dist/src/engine/session/system_prompt.d.ts +9 -0
- package/dist/src/engine/session/system_prompt.js +96 -0
- package/dist/src/engine/session/system_prompt.js.map +1 -0
- package/dist/src/engine/session/tool_registry.d.ts +5 -0
- package/dist/src/engine/session/tool_registry.js +117 -0
- package/dist/src/engine/session/tool_registry.js.map +1 -0
- package/dist/src/export.d.ts +13 -0
- package/dist/src/export.js +103 -0
- package/dist/src/export.js.map +1 -0
- package/dist/src/mdns.d.ts +7 -0
- package/dist/src/mdns.js +60 -0
- package/dist/src/mdns.js.map +1 -0
- package/dist/src/rag_worker.d.ts +38 -0
- package/dist/src/rag_worker.js +435 -0
- package/dist/src/rag_worker.js.map +1 -0
- package/dist/src/server.d.ts +11 -0
- package/dist/src/server.js +214 -0
- package/dist/src/server.js.map +1 -0
- package/dist/src/stats.d.ts +45 -0
- package/dist/src/stats.js +233 -0
- package/dist/src/stats.js.map +1 -0
- package/dist/src/tui.d.ts +29 -0
- package/dist/src/tui.js +1053 -0
- package/dist/src/tui.js.map +1 -0
- package/package.json +21 -5
- package/src/cli.ts +314 -113
- package/src/db_adapter.ts +354 -0
- package/src/engine/account/account.sql.ts +39 -39
- package/src/engine/account/account.ts +456 -456
- package/src/engine/account/repo.ts +166 -166
- package/src/engine/account/schema.ts +99 -99
- package/src/engine/account/url.ts +8 -8
- package/src/engine/acp/README.md +174 -174
- package/src/engine/acp/agent.ts +1968 -1968
- package/src/engine/acp/runtime.ts +22 -22
- package/src/engine/acp/session.ts +122 -122
- package/src/engine/acp/types.ts +24 -24
- package/src/engine/agent/agent.ts +463 -463
- package/src/engine/agent/generate.txt +75 -75
- package/src/engine/agent/prompt/compaction.txt +9 -9
- package/src/engine/agent/prompt/explore.txt +18 -18
- package/src/engine/agent/prompt/scout.txt +36 -36
- package/src/engine/agent/prompt/summary.txt +11 -11
- package/src/engine/agent/prompt/title.txt +44 -44
- package/src/engine/agent/subagent-permissions.ts +34 -34
- package/src/engine/auth/index.ts +96 -96
- package/src/engine/background/background/job.ts +200 -200
- package/src/engine/background/job.ts +200 -200
- package/src/engine/bus/bus-event.ts +45 -45
- package/src/engine/bus/global.ts +22 -22
- package/src/engine/bus/index.ts +203 -203
- package/src/engine/command/command/index.ts +181 -181
- package/src/engine/command/command/template/initialize.txt +66 -66
- package/src/engine/command/command/template/review.txt +101 -101
- package/src/engine/command/index.ts +181 -181
- package/src/engine/command/template/initialize.txt +66 -66
- package/src/engine/command/template/review.txt +101 -101
- package/src/engine/config/agent.ts +172 -172
- package/src/engine/config/attachment.ts +25 -25
- package/src/engine/config/command.ts +62 -62
- package/src/engine/config/config.ts +833 -833
- package/src/engine/config/console-state.ts +14 -14
- package/src/engine/config/entry-name.ts +16 -16
- package/src/engine/config/error.ts +23 -23
- package/src/engine/config/formatter.ts +13 -13
- package/src/engine/config/layout.ts +6 -6
- package/src/engine/config/lsp.ts +43 -43
- package/src/engine/config/managed.ts +71 -71
- package/src/engine/config/markdown.ts +96 -96
- package/src/engine/config/mcp.ts +56 -56
- package/src/engine/config/model-id.ts +5 -5
- package/src/engine/config/parse.ts +79 -79
- package/src/engine/config/paths.ts +45 -45
- package/src/engine/config/permission.ts +58 -58
- package/src/engine/config/plugin.ts +84 -84
- package/src/engine/config/provider.ts +111 -111
- package/src/engine/config/reference.ts +23 -23
- package/src/engine/config/server.ts +19 -19
- package/src/engine/config/skills.ts +14 -14
- package/src/engine/config/variable.ts +90 -90
- package/src/engine/control-plane/adapters/index.ts +41 -41
- package/src/engine/control-plane/adapters/worktree.ts +96 -96
- package/src/engine/control-plane/dev/README.md +19 -19
- package/src/engine/control-plane/dev/debug-workspace-plugin.ts +73 -73
- package/src/engine/control-plane/schema.ts +14 -14
- package/src/engine/control-plane/types.ts +59 -59
- package/src/engine/control-plane/util.ts +39 -39
- package/src/engine/control-plane/workspace-adapter-runtime.ts +51 -51
- package/src/engine/control-plane/workspace-context.ts +26 -26
- package/src/engine/control-plane/workspace.sql.ts +20 -20
- package/src/engine/control-plane/workspace.ts +1072 -1072
- package/src/engine/data-migration.ts +161 -161
- package/src/engine/effect/app-runtime.ts +143 -143
- package/src/engine/effect/bootstrap-runtime.ts +29 -29
- package/src/engine/effect/bridge.ts +84 -84
- package/src/engine/effect/config-service.ts +67 -67
- package/src/engine/effect/instance-ref.ts +11 -11
- package/src/engine/effect/instance-registry.ts +12 -12
- package/src/engine/effect/instance-state.ts +72 -72
- package/src/engine/effect/promise.ts +17 -17
- package/src/engine/effect/run-service.ts +47 -47
- package/src/engine/effect/runner.ts +217 -217
- package/src/engine/effect/runtime-flags.ts +74 -74
- package/src/engine/effect/service-use.ts +38 -38
- package/src/engine/env/index.ts +37 -37
- package/src/engine/event-v2-bridge.ts +89 -89
- package/src/engine/file/file/ignore.ts +81 -81
- package/src/engine/file/file/index.ts +651 -651
- package/src/engine/file/file/protected.ts +59 -59
- package/src/engine/file/file/ripgrep.ts +481 -481
- package/src/engine/file/file/watcher.ts +167 -167
- package/src/engine/file/ignore.ts +81 -81
- package/src/engine/file/index.ts +651 -651
- package/src/engine/file/protected.ts +59 -59
- package/src/engine/file/ripgrep.ts +481 -481
- package/src/engine/file/watcher.ts +167 -167
- package/src/engine/format/format/formatter.ts +404 -404
- package/src/engine/format/format/index.ts +209 -209
- package/src/engine/format/formatter.ts +404 -404
- package/src/engine/format/index.ts +209 -209
- package/src/engine/git/git/index.ts +347 -347
- package/src/engine/git/index.ts +347 -347
- package/src/engine/id/id.ts +80 -80
- package/src/engine/ide/index.ts +70 -70
- package/src/engine/image/image/image.ts +176 -176
- package/src/engine/image/image.ts +176 -176
- package/src/engine/index.ts +251 -251
- package/src/engine/installation/index.ts +327 -327
- package/src/engine/lsp/client.ts +707 -707
- package/src/engine/lsp/diagnostic.ts +29 -29
- package/src/engine/lsp/language.ts +121 -121
- package/src/engine/lsp/launch.ts +21 -21
- package/src/engine/lsp/lsp/client.ts +707 -707
- package/src/engine/lsp/lsp/diagnostic.ts +29 -29
- package/src/engine/lsp/lsp/language.ts +121 -121
- package/src/engine/lsp/lsp/launch.ts +21 -21
- package/src/engine/lsp/lsp/lsp.ts +507 -507
- package/src/engine/lsp/lsp/server.ts +2064 -2064
- package/src/engine/lsp/lsp.ts +507 -507
- package/src/engine/lsp/server.ts +2064 -2064
- package/src/engine/mcp/auth.ts +146 -146
- package/src/engine/mcp/index.ts +958 -958
- package/src/engine/mcp/mcp/auth.ts +146 -146
- package/src/engine/mcp/mcp/index.ts +958 -958
- package/src/engine/mcp/mcp/oauth-callback.ts +232 -232
- package/src/engine/mcp/mcp/oauth-provider.ts +214 -214
- package/src/engine/mcp/oauth-callback.ts +232 -232
- package/src/engine/mcp/oauth-provider.ts +214 -214
- package/src/engine/node.ts +6 -6
- package/src/engine/patch/index.ts +689 -689
- package/src/engine/patch/patch/index.ts +689 -689
- package/src/engine/permission/arity.ts +163 -163
- package/src/engine/permission/evaluate.ts +15 -15
- package/src/engine/permission/index.ts +306 -306
- package/src/engine/permission/permission/arity.ts +163 -163
- package/src/engine/permission/permission/evaluate.ts +15 -15
- package/src/engine/permission/permission/index.ts +306 -306
- package/src/engine/permission/permission/schema.ts +13 -13
- package/src/engine/permission/schema.ts +13 -13
- package/src/engine/plugin/azure.ts +26 -26
- package/src/engine/plugin/cloudflare.ts +76 -76
- package/src/engine/plugin/codex.ts +622 -622
- package/src/engine/plugin/digitalocean.ts +411 -411
- package/src/engine/plugin/github-copilot/copilot.ts +394 -394
- package/src/engine/plugin/github-copilot/models.ts +196 -196
- package/src/engine/plugin/index.ts +295 -295
- package/src/engine/plugin/install.ts +439 -439
- package/src/engine/plugin/loader.ts +216 -216
- package/src/engine/plugin/meta.ts +188 -188
- package/src/engine/plugin/shared.ts +323 -323
- package/src/engine/project/bootstrap-service.ts +9 -9
- package/src/engine/project/bootstrap.ts +75 -75
- package/src/engine/project/instance-context.ts +24 -24
- package/src/engine/project/instance-layer.ts +11 -11
- package/src/engine/project/instance-runtime.ts +16 -16
- package/src/engine/project/instance-store.ts +193 -193
- package/src/engine/project/project.sql.ts +17 -17
- package/src/engine/project/project.ts +537 -537
- package/src/engine/project/schema.ts +13 -13
- package/src/engine/project/vcs.ts +405 -405
- package/src/engine/provider/auth.ts +225 -225
- package/src/engine/provider/error.ts +204 -204
- package/src/engine/provider/model-status.ts +8 -8
- package/src/engine/provider/provider.ts +1843 -1843
- package/src/engine/provider/schema.ts +30 -30
- package/src/engine/provider/transform.ts +1376 -1376
- package/src/engine/pty/index.ts +365 -365
- package/src/engine/pty/input.ts +24 -24
- package/src/engine/pty/pty/index.ts +365 -365
- package/src/engine/pty/pty/input.ts +24 -24
- package/src/engine/pty/pty/pty.bun.ts +26 -26
- package/src/engine/pty/pty/pty.node.ts +27 -27
- package/src/engine/pty/pty/pty.ts +25 -25
- package/src/engine/pty/pty/schema.ts +14 -14
- package/src/engine/pty/pty/ticket.ts +68 -68
- package/src/engine/pty/pty.bun.ts +26 -26
- package/src/engine/pty/pty.node.ts +27 -27
- package/src/engine/pty/pty.ts +25 -25
- package/src/engine/pty/schema.ts +14 -14
- package/src/engine/pty/ticket.ts +68 -68
- package/src/engine/question/index.ts +213 -213
- package/src/engine/question/question/index.ts +213 -213
- package/src/engine/question/question/schema.ts +10 -10
- package/src/engine/question/schema.ts +10 -10
- package/src/engine/reference/reference/reference.ts +241 -241
- package/src/engine/reference/reference/repository-cache.ts +147 -147
- package/src/engine/reference/reference.ts +241 -241
- package/src/engine/reference/repository-cache.ts +147 -147
- package/src/engine/session/compaction.ts +651 -651
- package/src/engine/session/instruction.ts +238 -238
- package/src/engine/session/llm.ts +459 -459
- package/src/engine/session/message-error.ts +14 -14
- package/src/engine/session/message-v2.ts +1202 -1202
- package/src/engine/session/message.ts +146 -146
- package/src/engine/session/overflow.ts +32 -32
- package/src/engine/session/processor.ts +823 -823
- package/src/engine/session/prompt/anthropic.txt +105 -105
- package/src/engine/session/prompt/beast.txt +147 -147
- package/src/engine/session/prompt/build-switch.txt +5 -5
- package/src/engine/session/prompt/codex.txt +79 -79
- package/src/engine/session/prompt/copilot-gpt-5.txt +143 -143
- package/src/engine/session/prompt/default.txt +105 -105
- package/src/engine/session/prompt/gemini.txt +155 -155
- package/src/engine/session/prompt/gpt.txt +107 -107
- package/src/engine/session/prompt/kimi.txt +95 -95
- package/src/engine/session/prompt/max-steps.txt +15 -15
- package/src/engine/session/prompt/plan-reminder-anthropic.txt +67 -67
- package/src/engine/session/prompt/plan.txt +26 -26
- package/src/engine/session/prompt/trinity.txt +97 -97
- package/src/engine/session/prompt.ts +66 -9
- package/src/engine/session/retry.ts +200 -200
- package/src/engine/session/revert.ts +162 -162
- package/src/engine/session/run-state.ts +153 -153
- package/src/engine/session/schema.ts +26 -26
- package/src/engine/session/session.sql.ts +137 -137
- package/src/engine/session/session.ts +1011 -1011
- package/src/engine/session/status.ts +94 -94
- package/src/engine/session/summary.ts +164 -164
- package/src/engine/session/system.ts +84 -84
- package/src/engine/session/todo.ts +81 -81
- package/src/engine/share/session.ts +61 -61
- package/src/engine/share/share-next.ts +376 -376
- package/src/engine/share/share.sql.ts +13 -13
- package/src/engine/shell/shell/shell.ts +215 -215
- package/src/engine/shell/shell.ts +215 -215
- package/src/engine/skill/discovery.ts +116 -116
- package/src/engine/skill/index.ts +336 -336
- package/src/engine/skill/prompt/customize-opencode.md +377 -377
- package/src/engine/skill/skill/discovery.ts +116 -116
- package/src/engine/skill/skill/index.ts +336 -336
- package/src/engine/skill/skill/prompt/customize-opencode.md +377 -377
- package/src/engine/snapshot/index.ts +762 -762
- package/src/engine/snapshot/snapshot/index.ts +762 -762
- package/src/engine/sync/README.md +179 -179
- package/src/engine/sync/event.sql.ts +17 -17
- package/src/engine/sync/index.ts +410 -410
- package/src/engine/sync/schema.ts +11 -11
- package/src/engine/temporary.ts +33 -33
- package/src/engine/tool/apply_patch.ts +313 -313
- package/src/engine/tool/apply_patch.txt +33 -33
- package/src/engine/tool/edit.ts +711 -711
- package/src/engine/tool/edit.txt +10 -10
- package/src/engine/tool/external-directory.ts +49 -49
- package/src/engine/tool/glob.ts +103 -103
- package/src/engine/tool/glob.txt +6 -6
- package/src/engine/tool/grep.ts +156 -156
- package/src/engine/tool/grep.txt +8 -8
- package/src/engine/tool/invalid.ts +21 -21
- package/src/engine/tool/json-schema.ts +164 -164
- package/src/engine/tool/lsp.ts +113 -113
- package/src/engine/tool/lsp.txt +24 -24
- package/src/engine/tool/mcp-websearch.ts +96 -96
- package/src/engine/tool/plan-enter.txt +14 -14
- package/src/engine/tool/plan-exit.txt +13 -13
- package/src/engine/tool/plan.ts +78 -78
- package/src/engine/tool/question.ts +44 -44
- package/src/engine/tool/question.txt +10 -10
- package/src/engine/tool/read.ts +337 -337
- package/src/engine/tool/read.txt +14 -14
- package/src/engine/tool/registry.ts +472 -472
- package/src/engine/tool/repo_clone.ts +80 -80
- package/src/engine/tool/repo_clone.txt +5 -5
- package/src/engine/tool/repo_overview.ts +279 -279
- package/src/engine/tool/repo_overview.txt +4 -4
- package/src/engine/tool/schema.ts +14 -14
- package/src/engine/tool/shell/id.ts +19 -19
- package/src/engine/tool/shell/prompt.ts +295 -295
- package/src/engine/tool/shell/shell.txt +77 -77
- package/src/engine/tool/shell.ts +647 -647
- package/src/engine/tool/skill.ts +75 -75
- package/src/engine/tool/skill.txt +5 -5
- package/src/engine/tool/task.ts +337 -337
- package/src/engine/tool/task.txt +58 -58
- package/src/engine/tool/task_status.ts +179 -179
- package/src/engine/tool/task_status.txt +13 -13
- package/src/engine/tool/todo.ts +57 -57
- package/src/engine/tool/todowrite.txt +167 -167
- package/src/engine/tool/tool/apply_patch.ts +313 -313
- package/src/engine/tool/tool/apply_patch.txt +33 -33
- package/src/engine/tool/tool/edit.ts +711 -711
- package/src/engine/tool/tool/edit.txt +10 -10
- package/src/engine/tool/tool/external-directory.ts +49 -49
- package/src/engine/tool/tool/glob.ts +103 -103
- package/src/engine/tool/tool/glob.txt +6 -6
- package/src/engine/tool/tool/grep.ts +156 -156
- package/src/engine/tool/tool/grep.txt +8 -8
- package/src/engine/tool/tool/invalid.ts +21 -21
- package/src/engine/tool/tool/json-schema.ts +164 -164
- package/src/engine/tool/tool/lsp.ts +113 -113
- package/src/engine/tool/tool/lsp.txt +24 -24
- package/src/engine/tool/tool/mcp-websearch.ts +96 -96
- package/src/engine/tool/tool/plan-enter.txt +14 -14
- package/src/engine/tool/tool/plan-exit.txt +13 -13
- package/src/engine/tool/tool/plan.ts +78 -78
- package/src/engine/tool/tool/question.ts +44 -44
- package/src/engine/tool/tool/question.txt +10 -10
- package/src/engine/tool/tool/read.ts +337 -337
- package/src/engine/tool/tool/read.txt +14 -14
- package/src/engine/tool/tool/registry.ts +472 -472
- package/src/engine/tool/tool/repo_clone.ts +80 -80
- package/src/engine/tool/tool/repo_clone.txt +5 -5
- package/src/engine/tool/tool/repo_overview.ts +279 -279
- package/src/engine/tool/tool/repo_overview.txt +4 -4
- package/src/engine/tool/tool/schema.ts +14 -14
- package/src/engine/tool/tool/shell/id.ts +19 -19
- package/src/engine/tool/tool/shell/prompt.ts +295 -295
- package/src/engine/tool/tool/shell/shell.txt +77 -77
- package/src/engine/tool/tool/shell.ts +647 -647
- package/src/engine/tool/tool/skill.ts +75 -75
- package/src/engine/tool/tool/skill.txt +5 -5
- package/src/engine/tool/tool/task.ts +337 -337
- package/src/engine/tool/tool/task.txt +58 -58
- package/src/engine/tool/tool/task_status.ts +179 -179
- package/src/engine/tool/tool/task_status.txt +13 -13
- package/src/engine/tool/tool/todo.ts +57 -57
- package/src/engine/tool/tool/todowrite.txt +167 -167
- package/src/engine/tool/tool/tool.ts +164 -164
- package/src/engine/tool/tool/truncate.ts +160 -160
- package/src/engine/tool/tool/truncation-dir.ts +4 -4
- package/src/engine/tool/tool/webfetch.ts +192 -192
- package/src/engine/tool/tool/webfetch.txt +13 -13
- package/src/engine/tool/tool/websearch.ts +143 -143
- package/src/engine/tool/tool/websearch.txt +14 -14
- package/src/engine/tool/tool/write.ts +104 -104
- package/src/engine/tool/tool/write.txt +8 -8
- package/src/engine/tool/tool.ts +164 -164
- package/src/engine/tool/truncate.ts +160 -160
- package/src/engine/tool/truncation-dir.ts +4 -4
- package/src/engine/tool/webfetch.ts +192 -192
- package/src/engine/tool/webfetch.txt +13 -13
- package/src/engine/tool/websearch.ts +143 -143
- package/src/engine/tool/websearch.txt +14 -14
- package/src/engine/tool/write.ts +104 -104
- package/src/engine/tool/write.txt +8 -8
- package/src/engine/util/archive.ts +17 -17
- package/src/engine/util/bom.ts +31 -31
- package/src/engine/util/data-url.ts +9 -9
- package/src/engine/util/defer.ts +10 -10
- package/src/engine/util/effect-http-client.ts +11 -11
- package/src/engine/util/error.ts +88 -88
- package/src/engine/util/filesystem.ts +252 -252
- package/src/engine/util/format.ts +20 -20
- package/src/engine/util/iife.ts +3 -3
- package/src/engine/util/lazy.ts +20 -20
- package/src/engine/util/local-context.ts +25 -25
- package/src/engine/util/locale.ts +86 -86
- package/src/engine/util/media.ts +26 -26
- package/src/engine/util/process.ts +176 -176
- package/src/engine/util/queue.ts +32 -32
- package/src/engine/util/record.ts +3 -3
- package/src/engine/util/repository.ts +158 -158
- package/src/engine/util/rpc.ts +66 -66
- package/src/engine/util/signal.ts +12 -12
- package/src/engine/util/timeout.ts +13 -13
- package/src/engine/util/token.ts +7 -7
- package/src/engine/util/util/archive.ts +17 -17
- package/src/engine/util/util/bom.ts +31 -31
- package/src/engine/util/util/data-url.ts +9 -9
- package/src/engine/util/util/defer.ts +10 -10
- package/src/engine/util/util/effect-http-client.ts +11 -11
- package/src/engine/util/util/error.ts +88 -88
- package/src/engine/util/util/filesystem.ts +252 -252
- package/src/engine/util/util/format.ts +20 -20
- package/src/engine/util/util/iife.ts +3 -3
- package/src/engine/util/util/lazy.ts +20 -20
- package/src/engine/util/util/local-context.ts +25 -25
- package/src/engine/util/util/locale.ts +86 -86
- package/src/engine/util/util/media.ts +26 -26
- package/src/engine/util/util/process.ts +176 -176
- package/src/engine/util/util/queue.ts +32 -32
- package/src/engine/util/util/record.ts +3 -3
- package/src/engine/util/util/repository.ts +158 -158
- package/src/engine/util/util/rpc.ts +66 -66
- package/src/engine/util/util/signal.ts +12 -12
- package/src/engine/util/util/timeout.ts +13 -13
- package/src/engine/util/util/token.ts +7 -7
- package/src/engine/util/util/which.ts +14 -14
- package/src/engine/util/util/wildcard.ts +59 -59
- package/src/engine/util/which.ts +14 -14
- package/src/engine/util/wildcard.ts +59 -59
- package/src/engine/worktree/index.ts +621 -621
- package/src/export.ts +122 -0
- package/src/mdns.ts +53 -0
- package/src/server.ts +151 -156
- package/src/stats.ts +290 -0
- package/src/tui.ts +964 -480
package/src/tui.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BoneCode
|
|
1
|
+
/**
|
|
2
|
+
* BoneCode TUI — terminal interface modeled after OpenCode
|
|
3
|
+
*
|
|
4
|
+
* Three core behaviours:
|
|
5
|
+
* 1. Tool activity is displayed as concise status lines
|
|
6
|
+
* (← Edit src/foo.ts, → Read package.json, $ npm test) — never raw code dumps.
|
|
7
|
+
* Assistant text is shown inline; tool calls and tool outputs are summarized.
|
|
3
8
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - A persistent prompt sits at the bottom
|
|
7
|
-
* - Streaming LLM output appears above the prompt in real time
|
|
8
|
-
* - Ctrl+C interrupts the current request (does not exit)
|
|
9
|
-
* - /new starts a fresh session, /exit or Ctrl+D quits
|
|
10
|
-
* - Up/Down arrows navigate prompt history (readline built-in)
|
|
11
|
-
* - @<path> autocomplete for files in the current worktree
|
|
9
|
+
* 2. Typing "/" shows an inline command menu above the prompt with
|
|
10
|
+
* arrow-key selection and tab/enter to insert.
|
|
12
11
|
*
|
|
13
|
-
*
|
|
12
|
+
* 3. Ctrl+C aborts the in-flight LLM stream by aborting the fetch
|
|
13
|
+
* AND notifying the server to cancel the agent loop.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import * as path from "path";
|
|
@@ -18,67 +18,144 @@ import * as fs from "fs";
|
|
|
18
18
|
import * as readline from "readline";
|
|
19
19
|
import * as http from "http";
|
|
20
20
|
|
|
21
|
-
// ─── ANSI
|
|
21
|
+
// ─── ANSI ─────────────────────────────────────────────────────────────────────
|
|
22
22
|
|
|
23
23
|
const ESC = "\x1b";
|
|
24
|
-
const
|
|
25
|
-
const BOLD
|
|
26
|
-
const DIM
|
|
27
|
-
const CYAN
|
|
28
|
-
const GREEN = `${ESC}[
|
|
29
|
-
const YELLOW
|
|
30
|
-
const RED
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
const R = `${ESC}[0m`;
|
|
25
|
+
const BOLD = `${ESC}[1m`;
|
|
26
|
+
const DIM = `${ESC}[2m`;
|
|
27
|
+
const CYAN = `${ESC}[96m`;
|
|
28
|
+
const GREEN = `${ESC}[92m`;
|
|
29
|
+
const YELLOW= `${ESC}[93m`;
|
|
30
|
+
const RED = `${ESC}[91m`;
|
|
31
|
+
const GRAY = `${ESC}[90m`;
|
|
32
|
+
const WHITE = `${ESC}[97m`;
|
|
33
|
+
const BLUE = `${ESC}[94m`;
|
|
34
|
+
|
|
35
|
+
// Box drawing
|
|
36
|
+
const VERT = "┃";
|
|
37
|
+
const CORN = "╹";
|
|
38
|
+
const SHADE = "▀";
|
|
39
|
+
const BLOCK = "▣";
|
|
40
|
+
const ARROW_R = "→";
|
|
41
|
+
const ARROW_L = "←";
|
|
42
|
+
const DOLLAR = "$";
|
|
43
|
+
const STAR = "✱";
|
|
44
|
+
const PCT = "%";
|
|
45
|
+
const GEAR = "⚙";
|
|
46
|
+
const HASH = "#";
|
|
47
|
+
|
|
48
|
+
function cols(): number { return process.stdout.columns || 80; }
|
|
49
|
+
function out(s: string) { process.stdout.write(s); }
|
|
50
|
+
function nl(s = "") { process.stdout.write(s + "\n"); }
|
|
51
|
+
function clearLine() { out(`\r${ESC}[2K`); }
|
|
52
|
+
|
|
53
|
+
// ─── Logo ─────────────────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
function printLogo() {
|
|
56
|
+
nl();
|
|
57
|
+
nl(`${GRAY}${BOLD}█▀▀▄ █▀▀█ █▀▀█ █▀▀▀ █▀▀▀ █▀▀█ █▀▀▄ █▀▀▀${R}`);
|
|
58
|
+
nl(`${GRAY}${BOLD}█▀▀▄ █ █ █ █ █▀▀ █ █ █ █ █ █▀▀ ${R}`);
|
|
59
|
+
nl(`${GRAY}${BOLD}▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀ ▀▀▀▀${R}`);
|
|
60
|
+
nl();
|
|
36
61
|
}
|
|
37
62
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
63
|
+
// ─── Package version ──────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
const PKG_ROOT = (() => {
|
|
66
|
+
const fromSrc = path.resolve(__dirname, "..");
|
|
67
|
+
const fromDist = path.resolve(__dirname, "..", "..");
|
|
68
|
+
if (fs.existsSync(path.join(fromDist, "package.json"))) return fromDist;
|
|
69
|
+
return fromSrc;
|
|
70
|
+
})();
|
|
71
|
+
|
|
72
|
+
function getVersion(): string {
|
|
73
|
+
try { return JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf-8")).version; }
|
|
74
|
+
catch { return "0.0.0"; }
|
|
42
75
|
}
|
|
43
76
|
|
|
44
|
-
// ───
|
|
77
|
+
// ─── Slash command catalog ────────────────────────────────────────────────────
|
|
45
78
|
|
|
46
|
-
|
|
79
|
+
interface Command {
|
|
80
|
+
name: string;
|
|
81
|
+
description: string;
|
|
82
|
+
args?: string;
|
|
83
|
+
}
|
|
47
84
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
85
|
+
const COMMANDS: Command[] = [
|
|
86
|
+
{ name: "/new", description: "Start a new session" },
|
|
87
|
+
{ name: "/session", description: "Show current session ID" },
|
|
88
|
+
{ name: "/sessions", description: "List recent sessions" },
|
|
89
|
+
{ name: "/model", description: "Switch model", args: "<provider/model>" },
|
|
90
|
+
{ name: "/provider", description: "Switch provider", args: "<id>" },
|
|
91
|
+
{ name: "/providers", description: "List all providers" },
|
|
92
|
+
{ name: "/clear", description: "Clear screen" },
|
|
93
|
+
{ name: "/history", description: "Show last 10 prompts" },
|
|
94
|
+
{ name: "/help", description: "Show this help" },
|
|
95
|
+
{ name: "/exit", description: "Exit BoneCode" },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
// ─── @file autocomplete helpers ───────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
const IGNORED_DIRS = new Set([
|
|
101
|
+
"node_modules", ".git", "dist", "build", ".next", "__pycache__",
|
|
102
|
+
".venv", "venv", "target", "vendor", ".cache", "coverage",
|
|
103
|
+
]);
|
|
104
|
+
const CODE_EXTS = new Set([
|
|
105
|
+
".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", ".go", ".rs",
|
|
106
|
+
".java", ".kt", ".cs", ".cpp", ".c", ".h", ".rb", ".php", ".swift",
|
|
107
|
+
".md", ".mdx", ".json", ".yaml", ".yml", ".toml", ".env", ".sql", ".sh", ".graphql",
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
function listFiles(worktree: string, prefix: string): string[] {
|
|
111
|
+
const results: string[] = [];
|
|
112
|
+
function walk(dir: string, depth: number) {
|
|
113
|
+
if (results.length >= 50 || depth > 4) return;
|
|
114
|
+
let entries: fs.Dirent[];
|
|
115
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
116
|
+
for (const e of entries) {
|
|
117
|
+
if (results.length >= 50) break;
|
|
118
|
+
if (IGNORED_DIRS.has(e.name) || e.name.startsWith(".")) continue;
|
|
119
|
+
const rel = path.relative(worktree, path.join(dir, e.name));
|
|
120
|
+
if (e.isDirectory()) {
|
|
121
|
+
if (!prefix || rel.startsWith(prefix) || prefix.startsWith(rel)) {
|
|
122
|
+
results.push(rel + "/");
|
|
123
|
+
walk(path.join(dir, e.name), depth + 1);
|
|
124
|
+
}
|
|
125
|
+
} else if (CODE_EXTS.has(path.extname(e.name).toLowerCase())) {
|
|
126
|
+
if (!prefix || rel.startsWith(prefix)) results.push(rel);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
53
129
|
}
|
|
130
|
+
walk(worktree, 0);
|
|
131
|
+
return results.sort();
|
|
54
132
|
}
|
|
55
133
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
134
|
+
function buildCompleter(worktree: string) {
|
|
135
|
+
return (line: string): [string[], string] => {
|
|
136
|
+
if (line.startsWith("/")) {
|
|
137
|
+
// Tab-complete commands too (in addition to the inline menu)
|
|
138
|
+
const matches = COMMANDS.map(c => c.name).filter(c => c.startsWith(line));
|
|
139
|
+
return [matches, line];
|
|
140
|
+
}
|
|
141
|
+
const atIdx = line.lastIndexOf("@");
|
|
142
|
+
if (atIdx !== -1) {
|
|
143
|
+
const prefix = line.slice(atIdx + 1);
|
|
144
|
+
const completions = listFiles(worktree, prefix).map(f => line.slice(0, atIdx + 1) + f);
|
|
145
|
+
return [completions, line];
|
|
146
|
+
}
|
|
147
|
+
return [[], line];
|
|
148
|
+
};
|
|
70
149
|
}
|
|
71
150
|
|
|
72
|
-
// ─── Server health
|
|
151
|
+
// ─── Server health ────────────────────────────────────────────────────────────
|
|
73
152
|
|
|
74
|
-
async function waitForServer(port: number,
|
|
153
|
+
async function waitForServer(port: number, maxMs = 30_000): Promise<boolean> {
|
|
75
154
|
const start = Date.now();
|
|
76
|
-
while (Date.now() - start <
|
|
155
|
+
while (Date.now() - start < maxMs) {
|
|
77
156
|
try {
|
|
78
157
|
const ok = await new Promise<boolean>((resolve) => {
|
|
79
|
-
const req = http.get(`http://localhost:${port}/health`, (res) =>
|
|
80
|
-
resolve(res.statusCode === 200);
|
|
81
|
-
});
|
|
158
|
+
const req = http.get(`http://localhost:${port}/health`, (res) => resolve(res.statusCode === 200));
|
|
82
159
|
req.on("error", () => resolve(false));
|
|
83
160
|
req.setTimeout(1000, () => { req.destroy(); resolve(false); });
|
|
84
161
|
});
|
|
@@ -89,7 +166,13 @@ async function waitForServer(port: number, maxWaitMs = 30_000): Promise<boolean>
|
|
|
89
166
|
return false;
|
|
90
167
|
}
|
|
91
168
|
|
|
92
|
-
// ─── API
|
|
169
|
+
// ─── API ──────────────────────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
async function apiGet<T>(url: string, token: string): Promise<T> {
|
|
172
|
+
const r = await fetch(url, { headers: { "Authorization": `Bearer ${token}` } });
|
|
173
|
+
if (!r.ok) throw new Error(`API ${r.status}`);
|
|
174
|
+
return r.json() as Promise<T>;
|
|
175
|
+
}
|
|
93
176
|
|
|
94
177
|
async function apiPost(url: string, body: object, token: string): Promise<Response> {
|
|
95
178
|
return fetch(url, {
|
|
@@ -99,538 +182,939 @@ async function apiPost(url: string, body: object, token: string): Promise<Respon
|
|
|
99
182
|
});
|
|
100
183
|
}
|
|
101
184
|
|
|
102
|
-
async function
|
|
103
|
-
|
|
104
|
-
if (!r.ok) throw new Error(`API error ${r.status}: ${await r.text()}`);
|
|
105
|
-
return r.json() as Promise<T>;
|
|
185
|
+
async function apiDelete(url: string, token: string): Promise<void> {
|
|
186
|
+
await fetch(url, { method: "DELETE", headers: { "Authorization": `Bearer ${token}` } });
|
|
106
187
|
}
|
|
107
188
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
async function createSession(state: TUIState, title: string): Promise<string> {
|
|
111
|
-
const r = await apiPost(
|
|
112
|
-
`http://localhost:${state.port}/v2/session`,
|
|
113
|
-
{ title, directory: state.worktree },
|
|
114
|
-
state.token
|
|
115
|
-
);
|
|
189
|
+
async function createSession(port: number, token: string, worktree: string, title: string): Promise<string> {
|
|
190
|
+
const r = await apiPost(`http://localhost:${port}/v2/session`, { title, directory: worktree }, token);
|
|
116
191
|
if (!r.ok) throw new Error(`Failed to create session: ${await r.text()}`);
|
|
117
192
|
const sess = await r.json() as { id?: string };
|
|
118
|
-
if (!sess.id) throw new Error("
|
|
193
|
+
if (!sess.id) throw new Error("No session ID returned");
|
|
119
194
|
return sess.id;
|
|
120
195
|
}
|
|
121
196
|
|
|
122
|
-
// ───
|
|
197
|
+
// ─── Tool display rules — modeled after opencode's tool.ts ────────────────────
|
|
123
198
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
199
|
+
interface ToolDisplay {
|
|
200
|
+
icon: string;
|
|
201
|
+
color: string;
|
|
202
|
+
title: string;
|
|
203
|
+
detail?: string;
|
|
204
|
+
}
|
|
128
205
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
206
|
+
function relPath(p: string | undefined, worktree: string): string {
|
|
207
|
+
if (!p) return "";
|
|
208
|
+
try {
|
|
209
|
+
if (path.isAbsolute(p)) {
|
|
210
|
+
const rel = path.relative(worktree, p);
|
|
211
|
+
if (!rel.startsWith("..")) return rel.replace(/\\/g, "/");
|
|
212
|
+
}
|
|
213
|
+
return p.replace(/\\/g, "/");
|
|
214
|
+
} catch { return p; }
|
|
215
|
+
}
|
|
136
216
|
|
|
137
|
-
function
|
|
138
|
-
const
|
|
139
|
-
const
|
|
217
|
+
function describeTool(toolName: string, input: any, worktree: string): ToolDisplay {
|
|
218
|
+
const n = (toolName || "").toLowerCase();
|
|
219
|
+
const inp = input || {};
|
|
220
|
+
|
|
221
|
+
// Map BoneCode's tool names AND opencode's names
|
|
222
|
+
const isWrite = n === "write" || n === "write_file";
|
|
223
|
+
const isEdit = n === "edit" || n === "edit_file";
|
|
224
|
+
const isRead = n === "read" || n === "read_file";
|
|
225
|
+
const isShell = n === "bash" || n === "shell" || n === "run_command";
|
|
226
|
+
const isGlob = n === "glob";
|
|
227
|
+
const isGrep = n === "grep" || n === "search_files";
|
|
228
|
+
const isList = n === "list_directory" || n === "list" || n === "ls";
|
|
229
|
+
const isWebfetch = n === "webfetch";
|
|
230
|
+
const isWebsearch = n === "websearch";
|
|
231
|
+
const isPatch = n === "apply_patch" || n === "patch";
|
|
232
|
+
const isTodo = n === "todo_write" || n === "todowrite" || n === "todo";
|
|
233
|
+
const isTask = n === "task" || n === "subagent";
|
|
234
|
+
|
|
235
|
+
if (isWrite) {
|
|
236
|
+
return { icon: ARROW_L, color: CYAN, title: "Write", detail: relPath(inp.path || inp.filePath, worktree) };
|
|
237
|
+
}
|
|
238
|
+
if (isEdit) {
|
|
239
|
+
return { icon: ARROW_L, color: CYAN, title: "Edit", detail: relPath(inp.path || inp.filePath, worktree) };
|
|
240
|
+
}
|
|
241
|
+
if (isPatch) {
|
|
242
|
+
return { icon: PCT, color: CYAN, title: "Patch", detail: inp.patch ? `${String(inp.patch).split("\n").length} lines` : "" };
|
|
243
|
+
}
|
|
244
|
+
if (isRead) {
|
|
245
|
+
let detail = relPath(inp.path || inp.filePath, worktree);
|
|
246
|
+
if (inp.start_line || inp.end_line) {
|
|
247
|
+
detail += ` ${GRAY}[${inp.start_line || 1}-${inp.end_line || ""}]${R}`;
|
|
248
|
+
}
|
|
249
|
+
return { icon: ARROW_R, color: GRAY, title: "Read", detail };
|
|
250
|
+
}
|
|
251
|
+
if (isShell) {
|
|
252
|
+
const cmd = String(inp.command || "").trim();
|
|
253
|
+
const short = cmd.length > 60 ? cmd.slice(0, 57) + "..." : cmd;
|
|
254
|
+
return { icon: DOLLAR, color: YELLOW, title: short || "Shell" };
|
|
255
|
+
}
|
|
256
|
+
if (isGlob) {
|
|
257
|
+
return { icon: STAR, color: GRAY, title: "Glob", detail: inp.pattern || "" };
|
|
258
|
+
}
|
|
259
|
+
if (isGrep) {
|
|
260
|
+
const pattern = inp.pattern || "";
|
|
261
|
+
const glob = inp.glob ? ` in ${inp.glob}` : "";
|
|
262
|
+
return { icon: STAR, color: GRAY, title: "Grep", detail: `"${pattern}"${glob}` };
|
|
263
|
+
}
|
|
264
|
+
if (isList) {
|
|
265
|
+
return { icon: ARROW_R, color: GRAY, title: "List", detail: relPath(inp.path, worktree) };
|
|
266
|
+
}
|
|
267
|
+
if (isWebfetch) {
|
|
268
|
+
return { icon: PCT, color: BLUE, title: "WebFetch", detail: inp.url || "" };
|
|
269
|
+
}
|
|
270
|
+
if (isWebsearch) {
|
|
271
|
+
return { icon: PCT, color: BLUE, title: "WebSearch", detail: inp.query ? `"${inp.query}"` : "" };
|
|
272
|
+
}
|
|
273
|
+
if (isTodo) {
|
|
274
|
+
const todos = Array.isArray(inp.todos) ? inp.todos : [];
|
|
275
|
+
const done = todos.filter((t: any) => t.status === "completed").length;
|
|
276
|
+
return { icon: HASH, color: GRAY, title: "Todos", detail: `${done}/${todos.length} done` };
|
|
277
|
+
}
|
|
278
|
+
if (isTask) {
|
|
279
|
+
return { icon: HASH, color: CYAN, title: "Task", detail: inp.description || "" };
|
|
280
|
+
}
|
|
140
281
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
|
|
145
|
-
catch { return; }
|
|
282
|
+
// Fallback
|
|
283
|
+
return { icon: GEAR, color: GRAY, title: toolName };
|
|
284
|
+
}
|
|
146
285
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
286
|
+
function renderToolStart(d: ToolDisplay) {
|
|
287
|
+
const detail = d.detail ? ` ${GRAY}${d.detail}${R}` : "";
|
|
288
|
+
nl(` ${d.color}${d.icon}${R} ${WHITE}${d.title}${R}${detail}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function renderToolDone(d: ToolDisplay, ms?: number) {
|
|
292
|
+
const detail = d.detail ? ` ${GRAY}${d.detail}${R}` : "";
|
|
293
|
+
const time = ms !== undefined ? ` ${GRAY}${(ms / 1000).toFixed(1)}s${R}` : "";
|
|
294
|
+
// Replace the "..." with a checkmark (or just write a new line if not interactive)
|
|
295
|
+
nl(` ${GREEN}✓${R} ${GRAY}${d.title}${R}${detail}${time}`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function renderToolError(d: ToolDisplay, err: string) {
|
|
299
|
+
const detail = d.detail ? ` ${GRAY}${d.detail}${R}` : "";
|
|
300
|
+
nl(` ${RED}✗${R} ${GRAY}${d.title}${R}${detail} ${RED}${err}${R}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// ─── User message rendering ───────────────────────────────────────────────────
|
|
304
|
+
|
|
305
|
+
function renderUserMessage(text: string) {
|
|
306
|
+
nl();
|
|
307
|
+
for (const line of text.split("\n")) {
|
|
308
|
+
nl(`${GRAY}${VERT}${R} ${WHITE}${line}${R}`);
|
|
309
|
+
}
|
|
310
|
+
nl();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function renderTurnEnd(model: string, elapsedMs: number, interrupted: boolean) {
|
|
314
|
+
const elapsed = (elapsedMs / 1000).toFixed(1);
|
|
315
|
+
const dur = interrupted ? `${YELLOW}interrupted${R}` : `${GRAY}${elapsed}s${R}`;
|
|
316
|
+
nl();
|
|
317
|
+
nl(` ${CYAN}${BLOCK}${R} ${WHITE}Build${R}${GRAY} · ${model} · ${R}${dur}`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// ─── Help / providers / commands menu ─────────────────────────────────────────
|
|
321
|
+
|
|
322
|
+
function printHelp() {
|
|
323
|
+
nl();
|
|
324
|
+
nl(`${CYAN}${BOLD}BoneCode${R} ${GRAY}commands${R}`);
|
|
325
|
+
nl();
|
|
326
|
+
for (const c of COMMANDS) {
|
|
327
|
+
const args = c.args ? ` ${GRAY}${c.args}${R}` : "";
|
|
328
|
+
nl(` ${CYAN}${c.name.padEnd(12)}${R}${args.padEnd(20)} ${GRAY}${c.description}${R}`);
|
|
162
329
|
}
|
|
330
|
+
nl();
|
|
331
|
+
nl(` ${CYAN}Ctrl+C${R} ${GRAY}Interrupt current request${R}`);
|
|
332
|
+
nl(` ${CYAN}Ctrl+D${R} ${GRAY}Exit BoneCode${R}`);
|
|
333
|
+
nl(` ${CYAN}↑ / ↓${R} ${GRAY}Prompt history${R}`);
|
|
334
|
+
nl(` ${CYAN}@<path>${R} ${GRAY}Attach a file (Tab completes)${R}`);
|
|
335
|
+
nl();
|
|
336
|
+
}
|
|
163
337
|
|
|
164
|
-
|
|
165
|
-
return
|
|
338
|
+
async function fetchProviders(port: number, token: string): Promise<any[]> {
|
|
339
|
+
try { return await apiGet<any[]>(`http://localhost:${port}/v2/provider`, token); }
|
|
340
|
+
catch { return []; }
|
|
166
341
|
}
|
|
167
342
|
|
|
168
|
-
function
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
343
|
+
function printProviders(providers: any[], currentProvider: string) {
|
|
344
|
+
const width = cols();
|
|
345
|
+
nl();
|
|
346
|
+
nl(`${CYAN}${BOLD}Providers${R}`);
|
|
347
|
+
nl(`${GRAY}${"─".repeat(Math.min(width, 56))}${R}`);
|
|
348
|
+
for (const p of providers) {
|
|
349
|
+
const active = p.id === currentProvider;
|
|
350
|
+
const dot = active ? `${GREEN}●${R}` : `${GRAY}○${R}`;
|
|
351
|
+
const free = p.free ? ` ${GREEN}free${R}` : "";
|
|
352
|
+
const local = p.id === "local" ? ` ${CYAN}local config${R}` : "";
|
|
353
|
+
nl(` ${dot} ${active ? CYAN + BOLD : WHITE}${p.id}${R}${free}${local} ${GRAY}${p.name}${R}`);
|
|
354
|
+
if (p.freeNote) nl(` ${GRAY}${p.freeNote}${R}`);
|
|
355
|
+
if (p.models?.length) nl(` ${GRAY}${p.models.slice(0, 3).join(" ")}${R}`);
|
|
356
|
+
if (p.keyEnv) nl(` ${GRAY}${p.keyEnv}${R}`);
|
|
357
|
+
}
|
|
358
|
+
nl(`${GRAY}${"─".repeat(Math.min(width, 56))}${R}`);
|
|
359
|
+
nl(` ${GRAY}/provider <id> /model <id>${R}`);
|
|
360
|
+
nl();
|
|
361
|
+
}
|
|
173
362
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
363
|
+
// ─── Streaming ────────────────────────────────────────────────────────────────
|
|
364
|
+
|
|
365
|
+
interface StreamResult {
|
|
366
|
+
text: string;
|
|
367
|
+
tokens: number;
|
|
368
|
+
elapsedMs: number;
|
|
369
|
+
error?: string;
|
|
370
|
+
interrupted: boolean;
|
|
179
371
|
}
|
|
180
372
|
|
|
181
|
-
|
|
373
|
+
interface ToolTrack {
|
|
374
|
+
callId: string;
|
|
375
|
+
display: ToolDisplay;
|
|
376
|
+
startedAt: number;
|
|
377
|
+
}
|
|
182
378
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
)
|
|
189
|
-
|
|
379
|
+
/**
|
|
380
|
+
* Stateful code-fence collapser.
|
|
381
|
+
*
|
|
382
|
+
* The LLM streams text token-by-token, including ```python\n...\n``` blocks.
|
|
383
|
+
* We don't want to dump that raw to the terminal because:
|
|
384
|
+
* (a) The code is going to be saved via a tool call anyway, so showing it
|
|
385
|
+
* twice (raw stream + saved file path) is redundant.
|
|
386
|
+
* (b) It can be 100s of lines long and overwhelms the TUI.
|
|
387
|
+
*
|
|
388
|
+
* Strategy: buffer everything between ``` and ```. When the closing fence
|
|
389
|
+
* arrives, emit a one-line marker like [code: python, 42 lines]
|
|
390
|
+
* instead of the buffered content.
|
|
391
|
+
*
|
|
392
|
+
* State machine:
|
|
393
|
+
* "text" — pass deltas through verbatim (with partial-fence detection)
|
|
394
|
+
* "fence" — buffer everything; emit marker on close fence
|
|
395
|
+
*/
|
|
396
|
+
function makeCodeFenceCollapser() {
|
|
397
|
+
let mode: "text" | "fence" = "text";
|
|
398
|
+
let lang = "";
|
|
399
|
+
let buffered = ""; // bytes accumulated inside a fence
|
|
400
|
+
let pending = ""; // partial-fence buffer (when we see backticks but don't know yet)
|
|
401
|
+
|
|
402
|
+
const lines = (s: string) => s.split("\n").length;
|
|
403
|
+
|
|
404
|
+
function flushPending(): string {
|
|
405
|
+
const out = pending;
|
|
406
|
+
pending = "";
|
|
407
|
+
return out;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
feed(chunk: string): string {
|
|
412
|
+
let result = "";
|
|
413
|
+
let i = 0;
|
|
414
|
+
const buf = pending + chunk;
|
|
415
|
+
pending = "";
|
|
416
|
+
|
|
417
|
+
while (i < buf.length) {
|
|
418
|
+
if (mode === "text") {
|
|
419
|
+
// Look for ``` to enter fence mode
|
|
420
|
+
const fenceIdx = buf.indexOf("```", i);
|
|
421
|
+
if (fenceIdx === -1) {
|
|
422
|
+
// No fence in remaining text — emit it all, but hold the last 2 chars
|
|
423
|
+
// in case they're part of an upcoming fence.
|
|
424
|
+
const safeEnd = Math.max(i, buf.length - 2);
|
|
425
|
+
result += buf.slice(i, safeEnd);
|
|
426
|
+
pending = buf.slice(safeEnd);
|
|
427
|
+
i = buf.length;
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
430
|
+
// Emit text up to the fence
|
|
431
|
+
result += buf.slice(i, fenceIdx);
|
|
432
|
+
// Read the language (everything until newline)
|
|
433
|
+
const nlIdx = buf.indexOf("\n", fenceIdx + 3);
|
|
434
|
+
if (nlIdx === -1) {
|
|
435
|
+
// Don't have the full opening line yet — buffer
|
|
436
|
+
pending = buf.slice(fenceIdx);
|
|
437
|
+
i = buf.length;
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
lang = buf.slice(fenceIdx + 3, nlIdx).trim() || "code";
|
|
441
|
+
i = nlIdx + 1;
|
|
442
|
+
mode = "fence";
|
|
443
|
+
buffered = "";
|
|
444
|
+
// Print a leading marker (will be amended when we close)
|
|
445
|
+
result += `${GRAY}┃ code: ${lang}…${R}`;
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// mode === "fence" — look for closing ```
|
|
450
|
+
const closeIdx = buf.indexOf("```", i);
|
|
451
|
+
if (closeIdx === -1) {
|
|
452
|
+
buffered += buf.slice(i);
|
|
453
|
+
// Hold last 2 chars in case they're part of an upcoming close fence
|
|
454
|
+
const safeEnd = Math.max(i, buf.length - 2);
|
|
455
|
+
buffered = buffered.slice(0, buffered.length - (buf.length - safeEnd));
|
|
456
|
+
pending = buf.slice(safeEnd);
|
|
457
|
+
i = buf.length;
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
// Closing fence found
|
|
461
|
+
buffered += buf.slice(i, closeIdx);
|
|
462
|
+
const lineCount = lines(buffered.replace(/\n+$/, ""));
|
|
463
|
+
// Replace the placeholder we already wrote with the final marker.
|
|
464
|
+
// Carriage return + clear line + reprint marker.
|
|
465
|
+
result += `\r${ESC}[2K ${GRAY}┃ code: ${lang}, ${lineCount} line${lineCount === 1 ? "" : "s"}${R}\n`;
|
|
466
|
+
i = closeIdx + 3;
|
|
467
|
+
// Skip optional newline after closing fence
|
|
468
|
+
if (buf[i] === "\n") i++;
|
|
469
|
+
mode = "text";
|
|
470
|
+
lang = "";
|
|
471
|
+
buffered = "";
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return result;
|
|
475
|
+
},
|
|
476
|
+
flush(): string {
|
|
477
|
+
const tail = flushPending();
|
|
478
|
+
if (mode === "fence") {
|
|
479
|
+
// Stream ended mid-fence — close it with an approximate count
|
|
480
|
+
const lineCount = lines(buffered.replace(/\n+$/, ""));
|
|
481
|
+
mode = "text";
|
|
482
|
+
return tail + `\r${ESC}[2K ${GRAY}┃ code: ${lang}, ${lineCount}+ lines${R}\n`;
|
|
483
|
+
}
|
|
484
|
+
return tail;
|
|
485
|
+
},
|
|
486
|
+
};
|
|
487
|
+
}
|
|
190
488
|
|
|
191
|
-
|
|
192
|
-
|
|
489
|
+
async function streamPrompt(opts: {
|
|
490
|
+
port: number; token: string; sessionId: string;
|
|
491
|
+
model: string; provider: string; message: string;
|
|
492
|
+
worktree: string;
|
|
493
|
+
abortSignal: AbortSignal;
|
|
494
|
+
}): Promise<StreamResult> {
|
|
495
|
+
const { port, token, sessionId, model, provider, message, worktree, abortSignal } = opts;
|
|
496
|
+
const t0 = Date.now();
|
|
497
|
+
let fullText = "";
|
|
498
|
+
let tokens = 0;
|
|
499
|
+
let inAssistantText = false;
|
|
500
|
+
let tools = new Map<string, ToolTrack>();
|
|
501
|
+
|
|
502
|
+
// Code-fence collapser — replaces ```...``` blocks with [code: lang, N lines]
|
|
503
|
+
// markers across delta boundaries.
|
|
504
|
+
const fence = makeCodeFenceCollapser();
|
|
505
|
+
const collapseCodeFences = (chunk: string): string => fence.feed(chunk);
|
|
506
|
+
|
|
507
|
+
// Track which message the deltas belong to so we can detect tool boundaries
|
|
508
|
+
// The compat adapter emits these event types:
|
|
509
|
+
// part.delta — text chunk
|
|
510
|
+
// tool.requested — tool call started
|
|
511
|
+
// message.updated — message saved
|
|
512
|
+
// session.updated — session state changed
|
|
513
|
+
// error — error event
|
|
514
|
+
|
|
515
|
+
const flushTextLine = () => {
|
|
516
|
+
if (inAssistantText) {
|
|
517
|
+
// Make sure assistant text ends with a newline before the next thing
|
|
518
|
+
if (fullText && !fullText.endsWith("\n")) out("\n");
|
|
519
|
+
inAssistantText = false;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
193
522
|
|
|
194
523
|
try {
|
|
195
|
-
const r = await fetch(`http://localhost:${
|
|
524
|
+
const r = await fetch(`http://localhost:${port}/v2/session/${sessionId}/prompt`, {
|
|
196
525
|
method: "POST",
|
|
197
|
-
headers: {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
},
|
|
201
|
-
body: JSON.stringify({ content: message, modelID: state.model, providerID: state.provider }),
|
|
202
|
-
signal: ctrl.signal,
|
|
526
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` },
|
|
527
|
+
body: JSON.stringify({ content: message, modelID: model, providerID: provider }),
|
|
528
|
+
signal: abortSignal,
|
|
203
529
|
});
|
|
204
|
-
|
|
205
530
|
if (!r.ok) {
|
|
206
|
-
|
|
207
|
-
return;
|
|
531
|
+
return { text: "", tokens: 0, elapsedMs: Date.now() - t0, error: `HTTP ${r.status}: ${await r.text()}`, interrupted: false };
|
|
208
532
|
}
|
|
209
533
|
|
|
210
534
|
const reader = r.body!.getReader();
|
|
211
|
-
const
|
|
212
|
-
let
|
|
535
|
+
const dec = new TextDecoder();
|
|
536
|
+
let buf = "";
|
|
213
537
|
|
|
214
538
|
while (true) {
|
|
215
539
|
const { value, done } = await reader.read();
|
|
216
540
|
if (done) break;
|
|
217
|
-
|
|
541
|
+
buf += dec.decode(value, { stream: true });
|
|
542
|
+
const lines = buf.split("\n");
|
|
543
|
+
buf = lines.pop() || "";
|
|
218
544
|
|
|
219
|
-
const
|
|
220
|
-
|
|
545
|
+
for (const raw of lines) {
|
|
546
|
+
if (!raw.startsWith("data: ")) continue;
|
|
547
|
+
const json = raw.slice(6).trim();
|
|
548
|
+
if (!json || json === "[DONE]") continue;
|
|
221
549
|
|
|
222
|
-
for (const line of lines) {
|
|
223
|
-
if (!line.startsWith("data: ")) continue;
|
|
224
550
|
try {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
551
|
+
const ev = JSON.parse(json);
|
|
552
|
+
|
|
553
|
+
// Text delta — assistant is generating prose
|
|
554
|
+
if (ev.type === "part.delta" && ev.delta?.type === "text") {
|
|
555
|
+
const text = ev.delta.text || "";
|
|
556
|
+
if (!text) continue;
|
|
557
|
+
if (!inAssistantText) {
|
|
558
|
+
// Start of assistant text — print the indent prefix once
|
|
559
|
+
out(` `);
|
|
560
|
+
inAssistantText = true;
|
|
561
|
+
}
|
|
562
|
+
// Process the delta through the code-fence collapser so streaming
|
|
563
|
+
// code blocks appear as "[code: lang]" placeholders instead of
|
|
564
|
+
// dumping raw source.
|
|
565
|
+
const piece = collapseCodeFences(text);
|
|
566
|
+
// Print with leading-newline indenting (so each new line gets the 3-space prefix)
|
|
567
|
+
const indented = piece.replace(/\n/g, `\n `);
|
|
568
|
+
out(indented);
|
|
569
|
+
fullText += text;
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Tool requested — show concise activity line
|
|
574
|
+
if (ev.type === "tool.requested") {
|
|
575
|
+
flushTextLine();
|
|
576
|
+
const callId = ev.tool_call_id || ev.id || `${ev.tool_name}-${Date.now()}`;
|
|
577
|
+
const display = describeTool(ev.tool_name || "tool", ev.tool_input || {}, worktree);
|
|
578
|
+
tools.set(callId, { callId, display, startedAt: Date.now() });
|
|
579
|
+
renderToolStart(display);
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Tool completed
|
|
584
|
+
if (ev.type === "tool.completed" || ev.type === "tool.success") {
|
|
585
|
+
flushTextLine();
|
|
586
|
+
const callId = ev.tool_call_id || ev.id || "";
|
|
587
|
+
const tracked = tools.get(callId);
|
|
588
|
+
if (tracked) {
|
|
589
|
+
const ms = Date.now() - tracked.startedAt;
|
|
590
|
+
renderToolDone(tracked.display, ms);
|
|
591
|
+
tools.delete(callId);
|
|
592
|
+
}
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Tool failed
|
|
597
|
+
if (ev.type === "tool.failed" || ev.type === "tool.error") {
|
|
598
|
+
flushTextLine();
|
|
599
|
+
const callId = ev.tool_call_id || ev.id || "";
|
|
600
|
+
const tracked = tools.get(callId);
|
|
601
|
+
const err = ev.error || ev.message || "failed";
|
|
602
|
+
if (tracked) {
|
|
603
|
+
renderToolError(tracked.display, err);
|
|
604
|
+
tools.delete(callId);
|
|
605
|
+
} else {
|
|
606
|
+
nl(` ${RED}✗ ${err}${R}`);
|
|
607
|
+
}
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// Retry notification
|
|
612
|
+
if (ev.type === "session.retry") {
|
|
613
|
+
flushTextLine();
|
|
614
|
+
nl(` ${YELLOW}⟳ Retry ${ev.attempt || ""}: ${ev.message || ""}${R}`);
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// Server-side error
|
|
619
|
+
if (ev.type === "error") {
|
|
620
|
+
flushTextLine();
|
|
621
|
+
return {
|
|
622
|
+
text: fullText,
|
|
623
|
+
tokens,
|
|
624
|
+
elapsedMs: Date.now() - t0,
|
|
625
|
+
error: ev.properties?.message || "error",
|
|
626
|
+
interrupted: false,
|
|
627
|
+
};
|
|
228
628
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
629
|
+
|
|
630
|
+
// Compaction
|
|
631
|
+
if (ev.type === "session.compacted") {
|
|
632
|
+
flushTextLine();
|
|
633
|
+
nl(` ${BLUE}⊕ Context compacted${R}`);
|
|
634
|
+
continue;
|
|
232
635
|
}
|
|
233
|
-
} catch {
|
|
636
|
+
} catch {
|
|
637
|
+
// Ignore malformed events
|
|
638
|
+
}
|
|
234
639
|
}
|
|
235
640
|
}
|
|
236
641
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
);
|
|
244
|
-
const lastAssistant = msgs.filter(m => m.role === "assistant").slice(-1)[0];
|
|
245
|
-
const tokens = (lastAssistant?.tokens?.input || 0) + (lastAssistant?.tokens?.output || 0);
|
|
246
|
-
onDone({ tokens });
|
|
247
|
-
} catch {
|
|
248
|
-
onDone({});
|
|
642
|
+
flushTextLine();
|
|
643
|
+
|
|
644
|
+
// Drain any tools that didn't get a completion event
|
|
645
|
+
for (const tracked of tools.values()) {
|
|
646
|
+
const ms = Date.now() - tracked.startedAt;
|
|
647
|
+
renderToolDone(tracked.display, ms);
|
|
249
648
|
}
|
|
649
|
+
tools.clear();
|
|
650
|
+
|
|
651
|
+
// Flush any unclosed code fence
|
|
652
|
+
const tail = fence.flush();
|
|
653
|
+
if (tail) out(tail);
|
|
654
|
+
|
|
655
|
+
// Fetch the final message to get token totals
|
|
656
|
+
try {
|
|
657
|
+
const msgs = await apiGet<any[]>(`http://localhost:${port}/v2/session/${sessionId}/message`, token);
|
|
658
|
+
const last = msgs.filter((m: any) => m.role === "assistant").slice(-1)[0];
|
|
659
|
+
tokens = (last?.tokens?.input || 0) + (last?.tokens?.output || 0);
|
|
660
|
+
// If we didn't get any text deltas but the stored message has text, render it now
|
|
661
|
+
if (!fullText && last?.parts) {
|
|
662
|
+
for (const p of last.parts) {
|
|
663
|
+
if (p.type === "text" && p.text) {
|
|
664
|
+
out(` ${p.text.replace(/\n/g, `\n `)}\n`);
|
|
665
|
+
fullText = p.text;
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
} catch {}
|
|
671
|
+
|
|
672
|
+
return { text: fullText, tokens, elapsedMs: Date.now() - t0, interrupted: false };
|
|
250
673
|
} catch (e: any) {
|
|
674
|
+
flushTextLine();
|
|
251
675
|
if (e.name === "AbortError") {
|
|
252
|
-
|
|
253
|
-
} else {
|
|
254
|
-
onDone({ error: e.message });
|
|
676
|
+
return { text: fullText, tokens, elapsedMs: Date.now() - t0, interrupted: true };
|
|
255
677
|
}
|
|
256
|
-
|
|
257
|
-
state.abortController = null;
|
|
678
|
+
return { text: fullText, tokens, elapsedMs: Date.now() - t0, error: e.message, interrupted: false };
|
|
258
679
|
}
|
|
259
680
|
}
|
|
260
681
|
|
|
261
|
-
// ───
|
|
682
|
+
// ─── Slash command menu — inline, above the prompt ────────────────────────────
|
|
262
683
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
684
|
+
interface MenuState {
|
|
685
|
+
visible: boolean;
|
|
686
|
+
options: Command[];
|
|
687
|
+
selected: number;
|
|
688
|
+
rowsRendered: number;
|
|
689
|
+
}
|
|
268
690
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
691
|
+
function filterCommands(query: string): Command[] {
|
|
692
|
+
if (!query || query === "/") return COMMANDS;
|
|
693
|
+
const q = query.toLowerCase();
|
|
694
|
+
return COMMANDS.filter(c => c.name.toLowerCase().startsWith(q));
|
|
695
|
+
}
|
|
275
696
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
697
|
+
function renderCommandMenu(state: MenuState): number {
|
|
698
|
+
if (!state.visible || state.options.length === 0) return 0;
|
|
699
|
+
const max = Math.min(state.options.length, 8);
|
|
700
|
+
for (let i = 0; i < max; i++) {
|
|
701
|
+
const c = state.options[i]!;
|
|
702
|
+
const selected = i === state.selected;
|
|
703
|
+
const prefix = selected ? `${CYAN}▌${R} ` : ` `;
|
|
704
|
+
const name = selected ? `${CYAN}${BOLD}${c.name}${R}` : `${WHITE}${c.name}${R}`;
|
|
705
|
+
const args = c.args ? ` ${GRAY}${c.args}${R}` : "";
|
|
706
|
+
const desc = `${GRAY}${c.description}${R}`;
|
|
707
|
+
nl(`${prefix}${name}${args} ${desc}`);
|
|
280
708
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
process.stdout.write(text);
|
|
285
|
-
this.streamBuffer += text;
|
|
286
|
-
this.redrawPromptArea();
|
|
709
|
+
if (state.options.length > max) {
|
|
710
|
+
nl(` ${GRAY}... ${state.options.length - max} more (keep typing)${R}`);
|
|
711
|
+
return max + 1;
|
|
287
712
|
}
|
|
713
|
+
return max;
|
|
714
|
+
}
|
|
288
715
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
this.redrawPromptArea();
|
|
716
|
+
function clearMenu(rows: number) {
|
|
717
|
+
if (rows <= 0) return;
|
|
718
|
+
// Move up `rows` lines and clear each one
|
|
719
|
+
for (let i = 0; i < rows; i++) {
|
|
720
|
+
out(`${ESC}[1A${ESC}[2K`);
|
|
295
721
|
}
|
|
722
|
+
}
|
|
296
723
|
|
|
297
|
-
|
|
298
|
-
this.statusLine = text;
|
|
299
|
-
this.redrawPromptArea();
|
|
300
|
-
}
|
|
724
|
+
// ─── Main TUI loop ────────────────────────────────────────────────────────────
|
|
301
725
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
726
|
+
export async function runTUI(opts: {
|
|
727
|
+
port: number; token: string; model: string;
|
|
728
|
+
provider: string; worktree: string; sessionId?: string;
|
|
729
|
+
}): Promise<void> {
|
|
730
|
+
let { model, provider } = opts;
|
|
731
|
+
const { port, token, worktree } = opts;
|
|
732
|
+
let sessionId = opts.sessionId || null;
|
|
733
|
+
const history: string[] = [];
|
|
734
|
+
let abort: AbortController | null = null;
|
|
735
|
+
let streaming = false;
|
|
736
|
+
|
|
737
|
+
// Initial session
|
|
738
|
+
if (!sessionId) {
|
|
739
|
+
try { sessionId = await createSession(port, token, worktree, "BoneCode Session"); }
|
|
740
|
+
catch (e: any) {
|
|
741
|
+
process.stderr.write(`${RED}Failed to create session: ${e.message}${R}\n`);
|
|
742
|
+
process.exit(1);
|
|
743
|
+
}
|
|
305
744
|
}
|
|
306
745
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
746
|
+
// Header
|
|
747
|
+
printLogo();
|
|
748
|
+
nl(` ${GRAY}v${getVersion()} · ${model} · ${path.basename(worktree)}${R}`);
|
|
749
|
+
nl(` ${GRAY}session ${sessionId.slice(0, 8)}${R}`);
|
|
750
|
+
nl();
|
|
751
|
+
nl(` ${GRAY}Type ${R}${CYAN}/${R}${GRAY} to see commands · Ctrl+C interrupt · Ctrl+D exit${R}`);
|
|
752
|
+
nl();
|
|
753
|
+
|
|
754
|
+
// ─── Slash command menu state ──────────────────────────────────────────────
|
|
755
|
+
const menu: MenuState = {
|
|
756
|
+
visible: false,
|
|
757
|
+
options: [],
|
|
758
|
+
selected: 0,
|
|
759
|
+
rowsRendered: 0,
|
|
760
|
+
};
|
|
311
761
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
process.stdout
|
|
316
|
-
|
|
317
|
-
|
|
762
|
+
// Set up readline AFTER setting up SIGINT so we control it
|
|
763
|
+
const rl = readline.createInterface({
|
|
764
|
+
input: process.stdin,
|
|
765
|
+
output: process.stdout,
|
|
766
|
+
terminal: true,
|
|
767
|
+
historySize: 200,
|
|
768
|
+
completer: buildCompleter(worktree),
|
|
769
|
+
});
|
|
318
770
|
|
|
319
|
-
|
|
320
|
-
process.stdout.write("\n\n");
|
|
321
|
-
this.clearPromptArea();
|
|
322
|
-
this.redrawPromptArea();
|
|
323
|
-
}
|
|
771
|
+
const promptStr = () => `${CYAN}${BOLD}>${R} `;
|
|
324
772
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
773
|
+
// ─── Ctrl+C handling ──────────────────────────────────────────────────────
|
|
774
|
+
// When streaming: abort the request AND notify server
|
|
775
|
+
// When idle: clear menu/input or hint to use /exit
|
|
776
|
+
const onSigint = async () => {
|
|
777
|
+
if (streaming && abort) {
|
|
778
|
+
abort.abort();
|
|
779
|
+
// Also tell the server to cancel the agent loop
|
|
780
|
+
try {
|
|
781
|
+
await fetch(`http://localhost:${port}/v2/session/${sessionId}/cancel`, {
|
|
782
|
+
method: "POST",
|
|
783
|
+
headers: { "Authorization": `Bearer ${token}` },
|
|
784
|
+
});
|
|
785
|
+
} catch { /* server may not have the endpoint, abort is enough */ }
|
|
786
|
+
// Don't reprompt here — the streamPrompt finally block will handle UI
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
328
789
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
790
|
+
// Idle: clear menu if visible, else hint
|
|
791
|
+
if (menu.visible) {
|
|
792
|
+
clearMenu(menu.rowsRendered);
|
|
793
|
+
menu.visible = false;
|
|
794
|
+
menu.rowsRendered = 0;
|
|
795
|
+
menu.selected = 0;
|
|
796
|
+
}
|
|
797
|
+
out(`\n${GRAY}(Ctrl+D or /exit to quit)${R}\n`);
|
|
798
|
+
rl.setPrompt(promptStr());
|
|
799
|
+
rl.prompt();
|
|
800
|
+
};
|
|
339
801
|
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
private onSubmit: (text: string) => Promise<void>
|
|
350
|
-
) {
|
|
351
|
-
this.rl = readline.createInterface({
|
|
352
|
-
input: process.stdin,
|
|
353
|
-
output: process.stdout,
|
|
354
|
-
terminal: true,
|
|
355
|
-
historySize: 100,
|
|
356
|
-
completer: buildCompleter(state.worktree),
|
|
357
|
-
});
|
|
802
|
+
// Detach readline's default SIGINT (which closes the line buffer)
|
|
803
|
+
// and route it to our handler.
|
|
804
|
+
rl.on("SIGINT", onSigint);
|
|
805
|
+
|
|
806
|
+
rl.on("close", () => {
|
|
807
|
+
if (streaming && abort) abort.abort();
|
|
808
|
+
nl(`\n${GRAY}Goodbye.${R}`);
|
|
809
|
+
process.exit(0);
|
|
810
|
+
});
|
|
358
811
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
812
|
+
// ─── Live menu update on every keystroke ──────────────────────────────────
|
|
813
|
+
// We hook into the keypress events of stdin to redraw the menu.
|
|
814
|
+
// The menu sits ABOVE the prompt line.
|
|
815
|
+
const stdin: any = (rl as any).input;
|
|
816
|
+
|
|
817
|
+
const updateMenu = () => {
|
|
818
|
+
if (streaming) return;
|
|
819
|
+
|
|
820
|
+
const line: string = (rl as any).line || "";
|
|
821
|
+
const startsWithSlash = line.startsWith("/");
|
|
822
|
+
const shouldShow = startsWithSlash && !line.includes(" ");
|
|
823
|
+
|
|
824
|
+
if (!shouldShow) {
|
|
825
|
+
if (menu.visible) {
|
|
826
|
+
// Need to clear the menu — but we need to preserve the current input line.
|
|
827
|
+
// Strategy: save current line content & cursor, clear menu lines above,
|
|
828
|
+
// then restore the prompt and content.
|
|
829
|
+
const cursor = (rl as any).cursor || 0;
|
|
830
|
+
out(`\r${ESC}[2K`); // clear current prompt line
|
|
831
|
+
clearMenu(menu.rowsRendered); // clear menu lines above
|
|
832
|
+
menu.visible = false;
|
|
833
|
+
menu.rowsRendered = 0;
|
|
834
|
+
menu.selected = 0;
|
|
835
|
+
// Redraw prompt + line
|
|
836
|
+
out(promptStr() + line);
|
|
837
|
+
// Restore cursor position
|
|
838
|
+
const drawn = line.length;
|
|
839
|
+
if (cursor < drawn) {
|
|
840
|
+
out(`${ESC}[${drawn - cursor}D`);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return;
|
|
362
844
|
}
|
|
363
845
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
846
|
+
// Filter and reset selection if list changed
|
|
847
|
+
const newOptions = filterCommands(line);
|
|
848
|
+
if (newOptions.length === 0) {
|
|
849
|
+
// Clear menu if visible
|
|
850
|
+
if (menu.visible) {
|
|
851
|
+
const cursor = (rl as any).cursor || 0;
|
|
852
|
+
out(`\r${ESC}[2K`);
|
|
853
|
+
clearMenu(menu.rowsRendered);
|
|
854
|
+
menu.visible = false;
|
|
855
|
+
menu.rowsRendered = 0;
|
|
856
|
+
out(promptStr() + line);
|
|
857
|
+
if (cursor < line.length) out(`${ESC}[${line.length - cursor}D`);
|
|
369
858
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
});
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
373
861
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
});
|
|
862
|
+
// Save current input line
|
|
863
|
+
const cursor = (rl as any).cursor || 0;
|
|
864
|
+
out(`\r${ESC}[2K`); // clear prompt line
|
|
865
|
+
clearMenu(menu.rowsRendered); // clear old menu
|
|
379
866
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
renderer.print(`\n${YELLOW}⟳ Interrupted${RESET}`);
|
|
385
|
-
this.prompt();
|
|
386
|
-
} else {
|
|
387
|
-
renderer.print(`\n${DIM}(Use Ctrl+D or /exit to quit)${RESET}`);
|
|
388
|
-
this.prompt();
|
|
389
|
-
}
|
|
390
|
-
};
|
|
391
|
-
process.on("SIGINT", this.sigintHandler);
|
|
392
|
-
}
|
|
867
|
+
menu.options = newOptions;
|
|
868
|
+
menu.selected = Math.min(menu.selected, newOptions.length - 1);
|
|
869
|
+
if (menu.selected < 0) menu.selected = 0;
|
|
870
|
+
menu.visible = true;
|
|
393
871
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
? `${DIM}[${this.state.sessionId.slice(0, 8)}]${RESET} `
|
|
397
|
-
: "";
|
|
398
|
-
const promptStr = `${CYAN}${BOLD}>${RESET} `;
|
|
399
|
-
const full = `${sessionLabel}${promptStr}`;
|
|
400
|
-
this.renderer.setPrompt(full);
|
|
401
|
-
this.rl.setPrompt(full);
|
|
402
|
-
this.rl.prompt(true);
|
|
403
|
-
}
|
|
872
|
+
// Render the menu (each item ends in newline)
|
|
873
|
+
menu.rowsRendered = renderCommandMenu(menu);
|
|
404
874
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
875
|
+
// Redraw prompt + line
|
|
876
|
+
out(promptStr() + line);
|
|
877
|
+
if (cursor < line.length) {
|
|
878
|
+
out(`${ESC}[${line.length - cursor}D`);
|
|
409
879
|
}
|
|
410
|
-
}
|
|
880
|
+
};
|
|
411
881
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}
|
|
882
|
+
// Capture special keys for menu navigation
|
|
883
|
+
const onKeypress = (_chunk: any, key: any) => {
|
|
884
|
+
if (!key) return;
|
|
885
|
+
if (streaming) return;
|
|
417
886
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
switch (name) {
|
|
426
|
-
case "help":
|
|
427
|
-
case "h":
|
|
428
|
-
renderer.print(`
|
|
429
|
-
${CYAN}${BOLD}BoneCode Commands${RESET}
|
|
430
|
-
${CYAN}/new${RESET} Start a new session
|
|
431
|
-
${CYAN}/session${RESET} Show current session ID
|
|
432
|
-
${CYAN}/model <id>${RESET} Switch model (e.g. /model gpt-4o or /model anthropic/claude-sonnet-4-5)
|
|
433
|
-
${CYAN}/clear${RESET} Clear the screen
|
|
434
|
-
${CYAN}/history${RESET} Show last 10 prompts
|
|
435
|
-
${CYAN}/exit${RESET}, ${CYAN}/quit${RESET} Exit BoneCode
|
|
436
|
-
${CYAN}Ctrl+C${RESET} Interrupt current request (stays in session)
|
|
437
|
-
${CYAN}Ctrl+D${RESET} Exit BoneCode
|
|
438
|
-
${CYAN}↑ / ↓${RESET} Navigate prompt history
|
|
439
|
-
${CYAN}@<path>${RESET} Attach a file — press Tab to autocomplete`);
|
|
440
|
-
break;
|
|
441
|
-
|
|
442
|
-
case "new":
|
|
443
|
-
renderer.print(`${DIM}Starting new session...${RESET}`);
|
|
444
|
-
try {
|
|
445
|
-
state.sessionId = await createSession(state, "New Session");
|
|
446
|
-
state.turnCount = 0;
|
|
447
|
-
renderer.print(`${GREEN}✓ New session: ${state.sessionId.slice(0, 8)}...${RESET}`);
|
|
448
|
-
renderer.separator();
|
|
449
|
-
} catch (e: any) {
|
|
450
|
-
renderer.print(`${RED}✗ Failed to create session: ${e.message}${RESET}`);
|
|
887
|
+
if (menu.visible && menu.options.length > 0) {
|
|
888
|
+
// Arrow up/down navigate the menu
|
|
889
|
+
if (key.name === "up") {
|
|
890
|
+
menu.selected = (menu.selected - 1 + menu.options.length) % menu.options.length;
|
|
891
|
+
updateMenu();
|
|
892
|
+
return;
|
|
451
893
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
renderer.print(`${DIM}Session: ${RESET}${state.sessionId}`);
|
|
457
|
-
} else {
|
|
458
|
-
renderer.print(`${YELLOW}No active session${RESET}`);
|
|
894
|
+
if (key.name === "down") {
|
|
895
|
+
menu.selected = (menu.selected + 1) % menu.options.length;
|
|
896
|
+
updateMenu();
|
|
897
|
+
return;
|
|
459
898
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
899
|
+
// Tab: replace input with the selected command
|
|
900
|
+
if (key.name === "tab") {
|
|
901
|
+
const sel = menu.options[menu.selected];
|
|
902
|
+
if (sel) {
|
|
903
|
+
// Replace the line buffer with the command + space
|
|
904
|
+
(rl as any).line = sel.name + " ";
|
|
905
|
+
(rl as any).cursor = sel.name.length + 1;
|
|
906
|
+
updateMenu();
|
|
907
|
+
out(`\r${ESC}[2K`);
|
|
908
|
+
out(promptStr() + (rl as any).line);
|
|
470
909
|
}
|
|
471
|
-
|
|
472
|
-
renderer.setStatus(`${DIM}${state.provider}/${state.model}${RESET}`);
|
|
473
|
-
} else {
|
|
474
|
-
renderer.print(`${DIM}Current model: ${RESET}${state.provider}/${state.model}`);
|
|
475
|
-
}
|
|
476
|
-
break;
|
|
477
|
-
|
|
478
|
-
case "clear":
|
|
479
|
-
process.stdout.write(`${ESC}[2J${ESC}[H`);
|
|
480
|
-
renderer.banner(`${state.provider}/${state.model}`, state.worktree);
|
|
481
|
-
renderer.initPromptArea();
|
|
482
|
-
break;
|
|
483
|
-
|
|
484
|
-
case "history":
|
|
485
|
-
if (state.promptHistory.length === 0) {
|
|
486
|
-
renderer.print(`${DIM}No history yet${RESET}`);
|
|
487
|
-
} else {
|
|
488
|
-
renderer.print(`${DIM}Recent prompts:${RESET}`);
|
|
489
|
-
state.promptHistory.slice(-10).forEach((h, i) => {
|
|
490
|
-
renderer.print(` ${DIM}${i + 1}.${RESET} ${truncate(h, 70)}`);
|
|
491
|
-
});
|
|
910
|
+
return;
|
|
492
911
|
}
|
|
493
|
-
|
|
912
|
+
}
|
|
494
913
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
renderer.print(`\n${DIM}Goodbye.${RESET}`);
|
|
499
|
-
process.exit(0);
|
|
500
|
-
break;
|
|
914
|
+
// Default: trigger menu update on the next tick (after readline updates the buffer)
|
|
915
|
+
setImmediate(updateMenu);
|
|
916
|
+
};
|
|
501
917
|
|
|
502
|
-
|
|
503
|
-
renderer.print(`${YELLOW}Unknown command: /${name}${RESET} — type ${CYAN}/help${RESET} for commands`);
|
|
504
|
-
}
|
|
505
|
-
}
|
|
918
|
+
stdin.on("keypress", onKeypress);
|
|
506
919
|
|
|
507
|
-
|
|
920
|
+
rl.setPrompt(promptStr());
|
|
921
|
+
rl.prompt();
|
|
508
922
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
model: string;
|
|
513
|
-
provider: string;
|
|
514
|
-
worktree: string;
|
|
515
|
-
sessionId?: string;
|
|
516
|
-
}): Promise<void> {
|
|
517
|
-
const state: TUIState = {
|
|
518
|
-
sessionId: opts.sessionId || null,
|
|
519
|
-
model: opts.model,
|
|
520
|
-
provider: opts.provider,
|
|
521
|
-
port: opts.port,
|
|
522
|
-
token: opts.token,
|
|
523
|
-
worktree: opts.worktree,
|
|
524
|
-
promptHistory: [],
|
|
525
|
-
streaming: false,
|
|
526
|
-
abortController: null,
|
|
527
|
-
turnCount: 0,
|
|
528
|
-
totalTokens: 0,
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
const renderer = new TUIRenderer();
|
|
923
|
+
// ─── Main input loop ──────────────────────────────────────────────────────
|
|
924
|
+
for await (const rawLine of rl) {
|
|
925
|
+
const text = rawLine.trim();
|
|
532
926
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
process.stderr.write(`${RED}Failed to create session: ${e.message}${RESET}\n`);
|
|
539
|
-
process.exit(1);
|
|
927
|
+
// Clear any visible menu before processing
|
|
928
|
+
if (menu.visible) {
|
|
929
|
+
menu.visible = false;
|
|
930
|
+
menu.rowsRendered = 0;
|
|
931
|
+
menu.selected = 0;
|
|
540
932
|
}
|
|
541
|
-
}
|
|
542
933
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
934
|
+
if (!text) {
|
|
935
|
+
rl.setPrompt(promptStr());
|
|
936
|
+
rl.prompt();
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
546
939
|
|
|
547
|
-
|
|
548
|
-
if (
|
|
940
|
+
history.push(text);
|
|
941
|
+
if (history.length > 200) history.shift();
|
|
549
942
|
|
|
943
|
+
// ── Slash commands ──────────────────────────────────────────────────────
|
|
550
944
|
if (text.startsWith("/")) {
|
|
551
|
-
|
|
552
|
-
|
|
945
|
+
const parts = text.slice(1).trim().split(/\s+/);
|
|
946
|
+
const cmd = parts[0]?.toLowerCase() || "";
|
|
947
|
+
const args = parts.slice(1);
|
|
948
|
+
|
|
949
|
+
switch (cmd) {
|
|
950
|
+
case "help": case "h":
|
|
951
|
+
printHelp();
|
|
952
|
+
break;
|
|
953
|
+
|
|
954
|
+
case "new":
|
|
955
|
+
try {
|
|
956
|
+
sessionId = await createSession(port, token, worktree, "New Session");
|
|
957
|
+
nl(`${GREEN}✓${R} ${GRAY}session ${sessionId.slice(0, 8)}${R}`);
|
|
958
|
+
} catch (e: any) { nl(`${RED}✗ ${e.message}${R}`); }
|
|
959
|
+
break;
|
|
960
|
+
|
|
961
|
+
case "session":
|
|
962
|
+
nl(`${GRAY}${sessionId}${R}`);
|
|
963
|
+
break;
|
|
964
|
+
|
|
965
|
+
case "sessions": {
|
|
966
|
+
try {
|
|
967
|
+
const list = await apiGet<any[]>(`http://localhost:${port}/v2/session?limit=10`, token);
|
|
968
|
+
if (!list.length) { nl(`${GRAY}No sessions${R}`); break; }
|
|
969
|
+
nl();
|
|
970
|
+
for (const s of list) {
|
|
971
|
+
const active = s.id === sessionId;
|
|
972
|
+
const dot = active ? `${GREEN}●${R}` : `${GRAY}○${R}`;
|
|
973
|
+
const t = new Date(s.time?.updated || Date.now()).toLocaleString();
|
|
974
|
+
nl(` ${dot} ${WHITE}${(s.title || "untitled").slice(0, 50).padEnd(50)}${R} ${GRAY}${t}${R}`);
|
|
975
|
+
}
|
|
976
|
+
nl();
|
|
977
|
+
} catch (e: any) { nl(`${RED}✗ ${e.message}${R}`); }
|
|
978
|
+
break;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
case "model":
|
|
982
|
+
if (args[0]) {
|
|
983
|
+
if (args[0] === "local") {
|
|
984
|
+
provider = process.env.DEFAULT_PROVIDER || "openai_compatible";
|
|
985
|
+
model = process.env.DEFAULT_MODEL || "local-model";
|
|
986
|
+
} else if (args[0].includes("/")) {
|
|
987
|
+
const i = args[0].indexOf("/");
|
|
988
|
+
provider = args[0].slice(0, i);
|
|
989
|
+
model = args[0].slice(i + 1);
|
|
990
|
+
} else {
|
|
991
|
+
model = args[0];
|
|
992
|
+
}
|
|
993
|
+
nl(`${GREEN}✓${R} ${WHITE}${provider}/${model}${R}`);
|
|
994
|
+
} else {
|
|
995
|
+
nl(`${GRAY}${provider}/${model}${R}`);
|
|
996
|
+
}
|
|
997
|
+
break;
|
|
998
|
+
|
|
999
|
+
case "provider":
|
|
1000
|
+
if (args[0]) {
|
|
1001
|
+
if (args[0] === "local") {
|
|
1002
|
+
provider = process.env.DEFAULT_PROVIDER || "openai_compatible";
|
|
1003
|
+
model = process.env.DEFAULT_MODEL || "local-model";
|
|
1004
|
+
nl(`${GREEN}✓${R} ${WHITE}${provider}/${model}${R}`);
|
|
1005
|
+
} else {
|
|
1006
|
+
provider = args[0];
|
|
1007
|
+
nl(`${GREEN}✓${R} ${WHITE}${provider}${R} ${GRAY}use /model <id> to set model${R}`);
|
|
1008
|
+
}
|
|
1009
|
+
} else {
|
|
1010
|
+
nl(`${GRAY}${provider}${R}`);
|
|
1011
|
+
}
|
|
1012
|
+
break;
|
|
1013
|
+
|
|
1014
|
+
case "providers": {
|
|
1015
|
+
const list = await fetchProviders(port, token);
|
|
1016
|
+
if (list.length) printProviders(list, provider);
|
|
1017
|
+
else nl(`${YELLOW}Could not fetch providers${R}`);
|
|
1018
|
+
break;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
case "clear":
|
|
1022
|
+
process.stdout.write(`${ESC}[2J${ESC}[H`);
|
|
1023
|
+
printLogo();
|
|
1024
|
+
break;
|
|
1025
|
+
|
|
1026
|
+
case "history":
|
|
1027
|
+
if (!history.length) nl(`${GRAY}No history${R}`);
|
|
1028
|
+
else history.slice(-10).forEach((h, i) => nl(` ${GRAY}${i + 1}.${R} ${h.slice(0, 80)}`));
|
|
1029
|
+
break;
|
|
1030
|
+
|
|
1031
|
+
case "exit": case "quit": case "q":
|
|
1032
|
+
nl(`\n${GRAY}Goodbye.${R}`);
|
|
1033
|
+
process.exit(0);
|
|
1034
|
+
break;
|
|
1035
|
+
|
|
1036
|
+
default:
|
|
1037
|
+
nl(`${YELLOW}Unknown: /${cmd}${R} ${GRAY}Type /help for available commands${R}`);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
rl.setPrompt(promptStr());
|
|
1041
|
+
rl.prompt();
|
|
1042
|
+
continue;
|
|
553
1043
|
}
|
|
554
1044
|
|
|
555
|
-
//
|
|
556
|
-
if (!
|
|
557
|
-
try {
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
1045
|
+
// ── Prompt to the agent ─────────────────────────────────────────────────
|
|
1046
|
+
if (!sessionId) {
|
|
1047
|
+
try { sessionId = await createSession(port, token, worktree, text.slice(0, 50)); }
|
|
1048
|
+
catch (e: any) {
|
|
1049
|
+
nl(`${RED}✗ ${e.message}${R}`);
|
|
1050
|
+
rl.setPrompt(promptStr());
|
|
1051
|
+
rl.prompt();
|
|
1052
|
+
continue;
|
|
562
1053
|
}
|
|
563
1054
|
}
|
|
564
1055
|
|
|
565
1056
|
// Resolve @file mentions to absolute paths
|
|
566
|
-
const
|
|
567
|
-
const abs = path.isAbsolute(
|
|
568
|
-
? filePath
|
|
569
|
-
: path.resolve(state.worktree, filePath);
|
|
1057
|
+
const resolved = text.replace(/@([\w./\-]+)/g, (match, fp) => {
|
|
1058
|
+
const abs = path.isAbsolute(fp) ? fp : path.resolve(worktree, fp);
|
|
570
1059
|
return fs.existsSync(abs) ? `@${abs}` : match;
|
|
571
1060
|
});
|
|
572
1061
|
|
|
573
|
-
|
|
574
|
-
renderer.print(resolvedText !== text ? `${text} ${DIM}(files resolved)${RESET}` : text);
|
|
575
|
-
renderer.separator("·");
|
|
576
|
-
renderer.print(`${GREEN}${BOLD}BoneCode${RESET} ${DIM}(${state.model})${RESET}`);
|
|
577
|
-
|
|
578
|
-
state.streaming = true;
|
|
579
|
-
const startMs = Date.now();
|
|
580
|
-
renderer.setStatus(`${YELLOW}⟳ thinking...${RESET}`);
|
|
581
|
-
|
|
582
|
-
await streamPrompt(
|
|
583
|
-
state,
|
|
584
|
-
resolvedText,
|
|
585
|
-
(delta) => {
|
|
586
|
-
// Strip markdown fences — they don't render well in terminals
|
|
587
|
-
const clean = delta.replace(/^```\w*\n?/gm, "").replace(/^```\n?/gm, "");
|
|
588
|
-
if (clean) renderer.delta(clean);
|
|
589
|
-
},
|
|
590
|
-
(info) => {
|
|
591
|
-
renderer.endStream();
|
|
592
|
-
state.streaming = false;
|
|
593
|
-
state.turnCount++;
|
|
594
|
-
|
|
595
|
-
const elapsed = ((Date.now() - startMs) / 1000).toFixed(1);
|
|
596
|
-
if (info.tokens) state.totalTokens += info.tokens;
|
|
597
|
-
|
|
598
|
-
if (info.error && info.error !== "interrupted") {
|
|
599
|
-
renderer.print(`${RED}✗ ${info.error}${RESET}`);
|
|
600
|
-
}
|
|
1062
|
+
renderUserMessage(text);
|
|
601
1063
|
|
|
602
|
-
|
|
1064
|
+
// Show "..." spinner so the user knows something is happening
|
|
1065
|
+
out(` ${GRAY}thinking...${R}`);
|
|
603
1066
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
1067
|
+
rl.pause();
|
|
1068
|
+
streaming = true;
|
|
1069
|
+
abort = new AbortController();
|
|
1070
|
+
|
|
1071
|
+
const t0 = Date.now();
|
|
1072
|
+
const result = await streamPrompt({
|
|
1073
|
+
port, token, sessionId,
|
|
1074
|
+
model, provider,
|
|
1075
|
+
message: resolved,
|
|
1076
|
+
worktree,
|
|
1077
|
+
abortSignal: abort.signal,
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
streaming = false;
|
|
1081
|
+
abort = null;
|
|
614
1082
|
|
|
615
|
-
|
|
1083
|
+
// Clear the thinking spinner if no output happened
|
|
1084
|
+
if (!result.text && !result.error) {
|
|
1085
|
+
clearLine();
|
|
1086
|
+
} else {
|
|
1087
|
+
// Make sure the next render starts on a clean line
|
|
1088
|
+
const cur = (process.stdout as any).cursor;
|
|
1089
|
+
if (cur && cur.col !== 0) out("\n");
|
|
1090
|
+
}
|
|
616
1091
|
|
|
617
|
-
|
|
618
|
-
|
|
1092
|
+
renderTurnEnd(model, result.elapsedMs, result.interrupted);
|
|
1093
|
+
|
|
1094
|
+
if (result.error && !result.interrupted) {
|
|
1095
|
+
nl(` ${RED}✗ ${result.error}${R}`);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
nl();
|
|
1099
|
+
|
|
1100
|
+
rl.resume();
|
|
1101
|
+
rl.setPrompt(promptStr());
|
|
1102
|
+
rl.prompt();
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
stdin.removeListener("keypress", onKeypress);
|
|
619
1106
|
}
|
|
620
1107
|
|
|
621
1108
|
// ─── Entry point ──────────────────────────────────────────────────────────────
|
|
622
1109
|
|
|
623
1110
|
export async function startInteractiveTUI(opts: {
|
|
624
|
-
port: number;
|
|
625
|
-
|
|
626
|
-
model: string;
|
|
627
|
-
provider: string;
|
|
628
|
-
worktree: string;
|
|
1111
|
+
port: number; token: string; model: string;
|
|
1112
|
+
provider: string; worktree: string;
|
|
629
1113
|
}): Promise<void> {
|
|
630
|
-
const
|
|
631
|
-
if (!
|
|
632
|
-
process.stderr.write(`${RED}
|
|
633
|
-
process.stderr.write(`${
|
|
1114
|
+
const ready = await waitForServer(opts.port, 30_000);
|
|
1115
|
+
if (!ready) {
|
|
1116
|
+
process.stderr.write(`${RED}BoneCode server not responding on port ${opts.port}${R}\n`);
|
|
1117
|
+
process.stderr.write(`${GRAY}Start with: bonecode serve${R}\n`);
|
|
634
1118
|
process.exit(1);
|
|
635
1119
|
}
|
|
636
1120
|
await runTUI(opts);
|