@wenathlan/devthink 2.0.14 → 2.0.19
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/CHANGELOG.md +60 -0
- package/{dist/bridge.d.ts → bridge.d.ts} +1 -1
- package/bridge.d.ts.map +1 -0
- package/bridge.js +1877 -0
- package/bun.d.ts.map +1 -0
- package/bun.js +52329 -0
- package/caps/background.json +712 -0
- package/caps/cli.json +360 -0
- package/caps/library.json +3611 -0
- package/caps/mcp.json +126 -0
- package/caps/pagebridge.json +377 -0
- package/caps/popup.json +51 -0
- package/caps/sidepanel.json +522 -0
- package/checksums.txt +142 -0
- package/cli.d.ts.map +1 -0
- package/{dist/cli.js → cli.js} +2 -2
- package/companion.d.ts.map +1 -0
- package/companion.js +389 -0
- package/crossbrowser.d.ts.map +1 -0
- package/crossbrowser.js +1191 -0
- package/dashdone.d.ts.map +1 -0
- package/dashdone.js +368 -0
- package/deno.d.ts.map +1 -0
- package/deno.js +52325 -0
- package/devthink.umd.js +52346 -0
- package/fixtures/bun.ts +7 -0
- package/fixtures/cjs.js +6 -0
- package/fixtures/deno.ts +6 -0
- package/fixtures/example-org-pagestate.json +22 -0
- package/fixtures/importers/automa-workflow.json +18 -0
- package/fixtures/importers/selenium-side.json +20 -0
- package/fixtures/importers/tabular-plan.csv +7 -0
- package/fixtures/importers/uivision-macro.json +10 -0
- package/fixtures/importers/v1-plan.json +13 -0
- package/fixtures/mcp-client.mjs +14 -0
- package/fixtures/pages/checkout.html +192 -0
- package/fixtures/pages/dashboard.html +110 -0
- package/fixtures/pages/feed.html +113 -0
- package/fixtures/pages/product-grid.html +150 -0
- package/fixtures/plans/form-inventory-plan.json +11 -0
- package/fixtures/plans/release-notes-plan.json +11 -0
- package/fixtures/recipes/agentscompete.json +11 -0
- package/fixtures/recipes/agentsforms.json +11 -0
- package/fixtures/recipes/agentsmonitor.json +11 -0
- package/fixtures/recipes/agentsreview.json +29 -0
- package/fixtures/recipes/agentswarm.json +11 -0
- package/fixtures/recipes/fillcalendar.json +12 -0
- package/fixtures/recipes/fillcheckout.json +13 -0
- package/fixtures/recipes/filldependentdropdowns.json +12 -0
- package/fixtures/recipes/fillfromcsv.json +13 -0
- package/fixtures/recipes/fillloginform.json +12 -0
- package/fixtures/recipes/fillmultistepwizard.json +13 -0
- package/fixtures/recipes/fillregistration.json +12 -0
- package/fixtures/recipes/fillsearchfilters.json +12 -0
- package/fixtures/recipes/filltypeahead.json +11 -0
- package/fixtures/recipes/monitoralerts.json +12 -0
- package/fixtures/recipes/monitoravailability.json +11 -0
- package/fixtures/recipes/monitornetworkquiet.json +12 -0
- package/fixtures/recipes/monitorpagechanges.json +11 -0
- package/fixtures/recipes/monitorpricechange.json +11 -0
- package/fixtures/recipes/monitorschedule.json +12 -0
- package/fixtures/recipes/scrapedynamicprices.json +13 -0
- package/fixtures/recipes/scrapeimagegallery.json +12 -0
- package/fixtures/recipes/scrapeinfinitefeed.json +13 -0
- package/fixtures/recipes/scrapenestedlists.json +12 -0
- package/fixtures/recipes/scrapeproductgrid.json +12 -0
- package/fixtures/recipes/scrapereviews.json +12 -0
- package/fixtures/recipes/scrapesearchresults.json +12 -0
- package/fixtures/recipes/scrapetablepagination.json +12 -0
- package/fixtures/recipes/testaccessibility.json +34 -0
- package/fixtures/recipes/testconsoleerrors.json +12 -0
- package/fixtures/recipes/testdeeplinks.json +12 -0
- package/fixtures/recipes/testformvalidation.json +13 -0
- package/fixtures/recipes/testlayoutshift.json +12 -0
- package/fixtures/recipes/testlinkcheck.json +12 -0
- package/fixtures/recipes/testloadtiming.json +12 -0
- package/fixtures/recipes/testvisualdiff.json +12 -0
- package/fixtures/workflows/changelog-digest-workflow.json +13 -0
- package/fixtures/workflows/form-survey-workflow.json +13 -0
- package/gallery.json +706 -0
- package/hardening.d.ts.map +1 -0
- package/hardening.js +188 -0
- package/headless.d.ts.map +1 -0
- package/headless.js +11522 -0
- package/http.d.ts.map +1 -0
- package/http.js +2165 -0
- package/index.cjs +54682 -0
- package/index.d.ts.map +1 -0
- package/index.js +52310 -0
- package/index.neutral.js +46343 -0
- package/{web/manifest.json → manifest.json} +1 -1
- package/mcp.d.ts.map +1 -0
- package/mcp.js +20515 -0
- package/memory.d.ts.map +1 -0
- package/{memory.ts → memory.js} +2310 -3834
- package/nativehost.template.json +10 -0
- package/node.cjs +54686 -0
- package/node.d.ts.map +1 -0
- package/pack.d.ts.map +1 -0
- package/pack.js +805 -0
- package/package.json +83 -176
- package/policy.d.ts.map +1 -0
- package/{policy.ts → policy.js} +4679 -5073
- package/progress.d.ts.map +1 -0
- package/progress.js +645 -0
- package/protocol.d.ts.map +1 -0
- package/protocol.js +8334 -0
- package/schemas/audit.schema.json +226 -0
- package/schemas/capability.schema.json +113 -0
- package/schemas/envelope.schema.json +162 -0
- package/schemas/memory.schema.json +51 -0
- package/schemas/observation.schema.json +113 -0
- package/schemas/plan.schema.json +208 -0
- package/schemas/progress.schema.json +126 -0
- package/schemas/proposal.schema.json +68 -0
- package/schemas/session.schema.json +82 -0
- package/schemas/tool.schema.json +94 -0
- package/server.d.ts.map +1 -0
- package/server.js +28877 -0
- package/umd-example.html +18 -0
- package/umd.d.ts.map +1 -0
- package/accounts.ts +0 -2087
- package/agent.ts +0 -2331
- package/antigravity.ts +0 -2139
- package/apifreeze.ts +0 -4146
- package/auth.ts +0 -642
- package/background.ts +0 -16837
- package/bridge.ts +0 -2080
- package/build.ts +0 -54
- package/bun.ts +0 -26
- package/capture.ts +0 -1614
- package/checksecrets.ts +0 -72
- package/cjs.ts +0 -9
- package/cli.ts +0 -3500
- package/commands.ts +0 -1101
- package/companion.ts +0 -450
- package/compatibility.ts +0 -37
- package/config.ts +0 -3137
- package/constants.ts +0 -943
- package/core.ts +0 -1473
- package/crossbrowser.ts +0 -1359
- package/dashdone.ts +0 -605
- package/data.ts +0 -943
- package/database.ts +0 -133
- package/debug.ts +0 -850
- package/deno.ts +0 -27
- package/devthink.ts +0 -707
- package/dist/accounts.d.ts +0 -409
- package/dist/agent.d.ts +0 -837
- package/dist/antigravity.d.ts +0 -380
- package/dist/apifreeze.d.ts +0 -113
- package/dist/auth.d.ts +0 -224
- package/dist/capture.d.ts +0 -595
- package/dist/cjs.d.ts +0 -6
- package/dist/commands.d.ts +0 -310
- package/dist/compatibility.d.ts +0 -17
- package/dist/config.d.ts +0 -1853
- package/dist/constants.d.ts +0 -637
- package/dist/core.d.ts +0 -361
- package/dist/data.d.ts +0 -330
- package/dist/database.d.ts +0 -24
- package/dist/debug.d.ts +0 -270
- package/dist/engine.d.ts +0 -74
- package/dist/environments.d.ts +0 -244
- package/dist/evidence.d.ts +0 -277
- package/dist/export.d.ts +0 -311
- package/dist/fingerprint.d.ts +0 -594
- package/dist/flow.d.ts +0 -300
- package/dist/gates.d.ts +0 -103
- package/dist/identity.d.ts +0 -51
- package/dist/ids.d.ts +0 -7
- package/dist/llm.d.ts +0 -652
- package/dist/models.d.ts +0 -1807
- package/dist/net.d.ts +0 -325
- package/dist/neutral.d.ts +0 -41
- package/dist/oauth.d.ts +0 -703
- package/dist/page.d.ts +0 -1166
- package/dist/perf.d.ts +0 -481
- package/dist/plan.d.ts +0 -134
- package/dist/project.d.ts +0 -395
- package/dist/providers.d.ts +0 -37
- package/dist/quota.d.ts +0 -453
- package/dist/recovery.d.ts +0 -621
- package/dist/request.d.ts +0 -1492
- package/dist/run.d.ts +0 -779
- package/dist/runtime.d.ts +0 -212
- package/dist/search.d.ts +0 -375
- package/dist/security.d.ts +0 -534
- package/dist/serve.d.ts +0 -136
- package/dist/session.d.ts +0 -296
- package/dist/storage.d.ts +0 -53
- package/dist/streaming.d.ts +0 -1119
- package/dist/swarm.d.ts +0 -328
- package/dist/tools.d.ts +0 -170
- package/dist/types.d.ts +0 -8357
- package/dist/utils.d.ts +0 -82
- package/dist/version.d.ts +0 -7
- package/dist/versionregistry.d.ts +0 -406
- package/dist/views.d.ts +0 -579
- package/dist/workbenchsession.d.ts +0 -69
- package/dist/workflow.d.ts +0 -649
- package/docs/readme.md +0 -92
- package/docs/schemas/antigravity.json +0 -192
- package/engine.ts +0 -1524
- package/environments.ts +0 -1005
- package/evidence.ts +0 -643
- package/export.ts +0 -889
- package/fingerprint.ts +0 -1458
- package/flow.ts +0 -1190
- package/gates.ts +0 -307
- package/gateway.ts +0 -34
- package/hardening.ts +0 -191
- package/headless.ts +0 -369
- package/http.ts +0 -2110
- package/identity.ts +0 -246
- package/ids.ts +0 -26
- package/index.ts +0 -369
- package/llm.ts +0 -2682
- package/mcp.ts +0 -1835
- package/models.ts +0 -3805
- package/modes.ts +0 -43
- package/net.ts +0 -1214
- package/neutral.ts +0 -362
- package/node.ts +0 -26
- package/oauth.ts +0 -2216
- package/offscreen.ts +0 -170
- package/pack.ts +0 -956
- package/page.ts +0 -7824
- package/pagebridge.ts +0 -1116
- package/perf.ts +0 -957
- package/plan.ts +0 -672
- package/plugin.ts +0 -76
- package/prisma.config.ts +0 -27
- package/progress.ts +0 -1308
- package/project.ts +0 -1291
- package/protocol.ts +0 -4187
- package/providers.ts +0 -246
- package/quota.ts +0 -1887
- package/recovery.ts +0 -2433
- package/request.ts +0 -4482
- package/run.ts +0 -2135
- package/runtime.ts +0 -526
- package/search.ts +0 -1760
- package/security.ts +0 -1673
- package/serve.ts +0 -419
- package/server.ts +0 -5538
- package/session.ts +0 -1162
- package/storage.ts +0 -182
- package/streaming.ts +0 -4101
- package/swarm.ts +0 -884
- package/sync.ts +0 -56
- package/tests/agentcert.test.ts +0 -128
- package/tests/agentfleet.test.ts +0 -214
- package/tests/agentreview.test.ts +0 -241
- package/tests/agentstream.test.ts +0 -335
- package/tests/agentwork.test.ts +0 -356
- package/tests/apifreeze.test.ts +0 -290
- package/tests/apimap.test.ts +0 -112
- package/tests/approvalgate.test.ts +0 -175
- package/tests/artifactmanifest.test.ts +0 -204
- package/tests/attentionfeed.test.ts +0 -163
- package/tests/backgroundruns.test.ts +0 -100
- package/tests/batchscheduling.test.ts +0 -131
- package/tests/blackboard.test.ts +0 -238
- package/tests/bridge.test.ts +0 -1088
- package/tests/browsercoverage.test.ts +0 -134
- package/tests/browserpolyfills.test.ts +0 -124
- package/tests/build.mjs +0 -1260
- package/tests/clientauth.test.ts +0 -223
- package/tests/clitools.test.ts +0 -578
- package/tests/confirmgates.test.ts +0 -190
- package/tests/containerpack.test.ts +0 -143
- package/tests/controlflow.test.ts +0 -2120
- package/tests/coordination.test.ts +0 -345
- package/tests/copyscreen.test.ts +0 -139
- package/tests/core.test.ts +0 -417
- package/tests/costcert.test.ts +0 -95
- package/tests/crossbrowser.test.ts +0 -115
- package/tests/cspaudit.test.ts +0 -79
- package/tests/dashdone.test.ts +0 -615
- package/tests/datagrid.test.ts +0 -137
- package/tests/doccheck.test.ts +0 -107
- package/tests/dombatch.test.ts +0 -109
- package/tests/emulation.test.ts +0 -282
- package/tests/environments.test.ts +0 -259
- package/tests/evidenceviews.test.ts +0 -88
- package/tests/exporttools.test.ts +0 -185
- package/tests/firefoxprep.test.ts +0 -134
- package/tests/flowlibrary.test.ts +0 -242
- package/tests/flowrun.test.ts +0 -303
- package/tests/forensics.test.ts +0 -439
- package/tests/gateway.test.ts +0 -939
- package/tests/headlesslib.test.ts +0 -171
- package/tests/httpstream.test.ts +0 -277
- package/tests/immutablelog.test.ts +0 -157
- package/tests/inboundguard.test.ts +0 -177
- package/tests/lazyload.test.ts +0 -72
- package/tests/librarymodes.test.ts +0 -623
- package/tests/llm.test.ts +0 -696
- package/tests/manifest.test.ts +0 -226
- package/tests/maskinputs.test.ts +0 -150
- package/tests/matrixverify.test.ts +0 -111
- package/tests/mavenpack.test.ts +0 -75
- package/tests/mcpserver.test.ts +0 -704
- package/tests/memory.test.ts +0 -4155
- package/tests/memorycare.test.ts +0 -230
- package/tests/migrateplan.test.ts +0 -349
- package/tests/minimization.test.ts +0 -414
- package/tests/modelroute.test.ts +0 -112
- package/tests/multiagent.test.ts +0 -440
- package/tests/nativebridge.test.ts +0 -807
- package/tests/navigation.test.ts +0 -284
- package/tests/nugetpack.test.ts +0 -83
- package/tests/orchestration.test.ts +0 -449
- package/tests/originpolicy.test.ts +0 -405
- package/tests/outputcompare.test.ts +0 -97
- package/tests/package.test.ts +0 -91
- package/tests/pagebridge.test.ts +0 -5909
- package/tests/pentest.test.ts +0 -75
- package/tests/perfrecords.test.ts +0 -257
- package/tests/permdiff.test.ts +0 -85
- package/tests/phishguard.test.ts +0 -128
- package/tests/pickerviews.test.ts +0 -155
- package/tests/pipelines.test.ts +0 -381
- package/tests/planlint.test.ts +0 -240
- package/tests/planreview.test.ts +0 -383
- package/tests/platformtargets.test.ts +0 -56
- package/tests/policy.test.ts +0 -8284
- package/tests/portability.test.ts +0 -96
- package/tests/progress.test.ts +0 -1036
- package/tests/promptlibrary.test.ts +0 -116
- package/tests/protocol.test.ts +0 -4793
- package/tests/publishingpipeline.test.ts +0 -314
- package/tests/quickactions.test.ts +0 -164
- package/tests/readiness.test.ts +0 -78
- package/tests/recipes.test.ts +0 -163
- package/tests/redactshots.test.ts +0 -216
- package/tests/registernvidiakeys.ts +0 -51
- package/tests/relayserve.test.ts +0 -304
- package/tests/releasenotes.test.ts +0 -276
- package/tests/resilience.test.ts +0 -467
- package/tests/resourceaware.test.ts +0 -187
- package/tests/runbudget.test.ts +0 -125
- package/tests/runhistory.test.ts +0 -299
- package/tests/runreplay.test.ts +0 -113
- package/tests/runresume.test.ts +0 -184
- package/tests/runstate.test.ts +0 -288
- package/tests/runtimeadapters.test.ts +0 -251
- package/tests/safariskeleton.test.ts +0 -115
- package/tests/sandboxframe.test.ts +0 -196
- package/tests/sbom.test.ts +0 -105
- package/tests/secretvault.test.ts +0 -188
- package/tests/selcache.test.ts +0 -111
- package/tests/sessioninterface.test.ts +0 -791
- package/tests/sessions.test.ts +0 -591
- package/tests/siteprefs.test.ts +0 -228
- package/tests/snapshotdelta.test.ts +0 -165
- package/tests/statusviews.test.ts +0 -237
- package/tests/stepmeter.test.ts +0 -200
- package/tests/streamparse.test.ts +0 -163
- package/tests/stylepruning.test.ts +0 -90
- package/tests/surfaces.test.ts +0 -282
- package/tests/sweep.test.ts +0 -78
- package/tests/syncbridge.test.ts +0 -164
- package/tests/taskqueue.test.ts +0 -206
- package/tests/toolcalls.test.ts +0 -336
- package/tests/toolcatalog.test.ts +0 -201
- package/tests/torture-agent.test.ts +0 -706
- package/tests/torture-auth.test.ts +0 -461
- package/tests/torture-commands.test.ts +0 -895
- package/tests/torture-data.test.ts +0 -923
- package/tests/torture-environments.test.ts +0 -285
- package/tests/torture-export.test.ts +0 -263
- package/tests/torture-http.test.ts +0 -1379
- package/tests/torture-llm.test.ts +0 -808
- package/tests/torture-memory.test.ts +0 -1084
- package/tests/torture-plan.test.ts +0 -829
- package/tests/torture-run.test.ts +0 -626
- package/tests/torture-session.test.ts +0 -928
- package/tests/torture-swarm.test.ts +0 -1245
- package/tests/torture-tools.test.ts +0 -964
- package/tests/torture-workflow.test.ts +0 -2354
- package/tests/tourviews.test.ts +0 -52
- package/tests/transparency.test.ts +0 -147
- package/tests/trigger.test.ts +0 -941
- package/tests/v1sunset.test.ts +0 -428
- package/tests/virtlist.test.ts +0 -94
- package/tests/vision.test.ts +0 -540
- package/tests/vsixpack.test.ts +0 -145
- package/tests/webapi.test.ts +0 -523
- package/tests/workflow.test.ts +0 -1785
- package/tests/workfloweditor.test.ts +0 -759
- package/tests/xpipack.test.ts +0 -128
- package/tools.ts +0 -734
- package/types.ts +0 -9908
- package/umd.ts +0 -9
- package/utils.ts +0 -249
- package/version.ts +0 -8
- package/versionregistry.ts +0 -1310
- package/views.ts +0 -2153
- package/vitest.config.ts +0 -12
- package/web/App.tsx +0 -78
- package/web/README.md +0 -47
- package/web/capacitor.config.ts +0 -46
- package/web/const.ts +0 -19
- package/web/control.shell.tsx +0 -66
- package/web/dashboardpage.ts +0 -554
- package/web/db.ts +0 -493
- package/web/design.html +0 -541
- package/web/gatewayview/Gateway.tsx +0 -345
- package/web/gatewayview/config.ts +0 -1042
- package/web/gatewayview/definition.ts +0 -174
- package/web/icons.ts +0 -15
- package/web/optionspage.ts +0 -691
- package/web/popup.ts +0 -1651
- package/web/schema.prisma +0 -176
- package/web/sidepanel.ts +0 -7941
- package/web/sitemanifest.json +0 -13
- package/web/terminal-ui.tsx +0 -622
- package/web/transparencypage.ts +0 -541
- package/web/vite.config.ts +0 -39
- package/web/workspace.ts +0 -23
- package/workbenchmemory.ts +0 -104
- package/workbenchsession.ts +0 -304
- package/workflow.ts +0 -4495
- /package/{dist/bun.d.ts → bun.d.ts} +0 -0
- /package/{dist/cli.d.ts → cli.d.ts} +0 -0
- /package/{dist/companion.d.ts → companion.d.ts} +0 -0
- /package/{dist/crossbrowser.d.ts → crossbrowser.d.ts} +0 -0
- /package/{dist/dashdone.d.ts → dashdone.d.ts} +0 -0
- /package/{dist/deno.d.ts → deno.d.ts} +0 -0
- /package/{dist/hardening.d.ts → hardening.d.ts} +0 -0
- /package/{dist/headless.d.ts → headless.d.ts} +0 -0
- /package/{dist/http.d.ts → http.d.ts} +0 -0
- /package/{dist/index.d.ts → index.d.ts} +0 -0
- /package/{dist/mcp.d.ts → mcp.d.ts} +0 -0
- /package/{dist/memory.d.ts → memory.d.ts} +0 -0
- /package/{dist/node.d.ts → node.d.ts} +0 -0
- /package/{dist/pack.d.ts → pack.d.ts} +0 -0
- /package/{dist/policy.d.ts → policy.d.ts} +0 -0
- /package/{dist/progress.d.ts → progress.d.ts} +0 -0
- /package/{dist/protocol.d.ts → protocol.d.ts} +0 -0
- /package/{dist/server.d.ts → server.d.ts} +0 -0
- /package/{dist/umd.d.ts → umd.d.ts} +0 -0
package/web/sitemanifest.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "devthink site",
|
|
3
|
-
"shortname": "devthink site",
|
|
4
|
-
"servercontract": 1,
|
|
5
|
-
"capabilities": {
|
|
6
|
-
"version": 1,
|
|
7
|
-
"operations": ["sessioncreate", "sessionjoin", "eventpost", "eventstream"],
|
|
8
|
-
"events": ["chat", "planproposal", "planreview", "progress"],
|
|
9
|
-
"heartbeat": true,
|
|
10
|
-
"multiplex": true
|
|
11
|
-
},
|
|
12
|
-
"staticonly": true
|
|
13
|
-
}
|
package/web/terminal-ui.tsx
DELETED
|
@@ -1,622 +0,0 @@
|
|
|
1
|
-
/** Design: DevThink v1.1.15 — Ink shares the compact local workspace grammar, identity, destinations, settings and command language with the paired React workbench. The plain ANSI renderer family of the former root ui.ts lives here too: the web design room owns the whole terminal design (CLI, pages, TV, Android), so the pure string renderers coexist with the Ink components. */
|
|
2
|
-
import { Box, Text, render, useApp, useInput, useWindowSize } from "ink";
|
|
3
|
-
import { useMemo, useState } from "react";
|
|
4
|
-
import { listProviders } from "../providers.js";
|
|
5
|
-
import { listSessions, type Session } from "../workbenchsession.js";
|
|
6
|
-
import { readPreferences, savePreference } from "../storage.js";
|
|
7
|
-
import { getIdentity, pairingStatus } from "../identity.js";
|
|
8
|
-
import type { DevThinkConfig, DevThinkPaths } from "../config.js";
|
|
9
|
-
import type { ChatEvent } from "../streaming.js";
|
|
10
|
-
import {
|
|
11
|
-
isWorkspaceDestination,
|
|
12
|
-
workspaceDestination,
|
|
13
|
-
workspaceDestinations,
|
|
14
|
-
type WorkspaceDestination,
|
|
15
|
-
} from "./workspace.js";
|
|
16
|
-
|
|
17
|
-
const categories = ["features", "bugs", "refactor", "snippets", "tasks", "notes", "all"] as const;
|
|
18
|
-
const categoryGlyphs: Record<(typeof categories)[number], string> = {
|
|
19
|
-
features: "ϟ",
|
|
20
|
-
bugs: "⊗",
|
|
21
|
-
refactor: "◌",
|
|
22
|
-
snippets: "◫",
|
|
23
|
-
tasks: "☑",
|
|
24
|
-
notes: "□",
|
|
25
|
-
all: "◉",
|
|
26
|
-
};
|
|
27
|
-
const entryArt = [
|
|
28
|
-
" +++++",
|
|
29
|
-
" +++++++++++++++",
|
|
30
|
-
" ++++++ ++++++",
|
|
31
|
-
" +++++ +++++",
|
|
32
|
-
" +++++ +++++",
|
|
33
|
-
" +++++ +++++",
|
|
34
|
-
" ++++++ ++++++",
|
|
35
|
-
" +++++++++++++",
|
|
36
|
-
" +++++",
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
type Runtime = { config: DevThinkConfig; paths: DevThinkPaths };
|
|
40
|
-
type Execute = (
|
|
41
|
-
prompt: string,
|
|
42
|
-
current: Session | undefined,
|
|
43
|
-
onEvent: (event: ChatEvent) => void,
|
|
44
|
-
) => Promise<Session | undefined>;
|
|
45
|
-
type Entry = { role: "user" | "devthink" | "error"; title: string; body: string };
|
|
46
|
-
|
|
47
|
-
function clip(value: string, width: number): string {
|
|
48
|
-
return value.length > width ? `${value.slice(0, Math.max(0, width - 1))}…` : value;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function TerminalEntry({
|
|
52
|
-
draft,
|
|
53
|
-
provider,
|
|
54
|
-
userId,
|
|
55
|
-
width,
|
|
56
|
-
}: {
|
|
57
|
-
draft: string;
|
|
58
|
-
provider: string;
|
|
59
|
-
userId: string;
|
|
60
|
-
width: number;
|
|
61
|
-
}) {
|
|
62
|
-
return (
|
|
63
|
-
<Box
|
|
64
|
-
flexDirection="column"
|
|
65
|
-
width="100%"
|
|
66
|
-
height="100%"
|
|
67
|
-
minHeight={20}
|
|
68
|
-
paddingX={2}
|
|
69
|
-
paddingY={1}
|
|
70
|
-
justifyContent="space-between"
|
|
71
|
-
>
|
|
72
|
-
<Box justifyContent="space-between">
|
|
73
|
-
<Text color="#807970">✦ DEVTHINK</Text>
|
|
74
|
-
<Text color="#8ab4f8">{userId}</Text>
|
|
75
|
-
</Box>
|
|
76
|
-
<Box flexDirection="column" alignItems="center">
|
|
77
|
-
<Box flexDirection="column" alignItems="center">
|
|
78
|
-
{entryArt.map((line, index) => (
|
|
79
|
-
<Text key={`${line}-${index}`} color="#382c23">
|
|
80
|
-
{line}
|
|
81
|
-
</Text>
|
|
82
|
-
))}
|
|
83
|
-
</Box>
|
|
84
|
-
<Box marginTop={-1}>
|
|
85
|
-
<Text color="#ff5f00" bold>
|
|
86
|
-
DEVTHINK
|
|
87
|
-
</Text>
|
|
88
|
-
</Box>
|
|
89
|
-
<Box marginTop={1}>
|
|
90
|
-
<Text color="#746f66">begin with one intention</Text>
|
|
91
|
-
</Box>
|
|
92
|
-
<Box marginTop={2}>
|
|
93
|
-
<Text color="#282724">┌{"─".repeat(Math.max(16, width - 2))}┐</Text>
|
|
94
|
-
</Box>
|
|
95
|
-
<Text color="#e8e1d7">
|
|
96
|
-
│ <Text color="#ff5f00">›_ </Text>
|
|
97
|
-
<Text color={draft ? "#e8e1d7" : "#746f66"}>{clip(draft || "Ask anything…", Math.max(12, width - 22))}</Text>
|
|
98
|
-
<Text color="#8ab4f8"> {provider}</Text>
|
|
99
|
-
<Text color="#ff5f00"> [open]</Text> │
|
|
100
|
-
</Text>
|
|
101
|
-
<Text color="#282724">└{"─".repeat(Math.max(16, width - 2))}┘</Text>
|
|
102
|
-
</Box>
|
|
103
|
-
<Text color="#746f66">enter opens a local workspace · esc exits</Text>
|
|
104
|
-
</Box>
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function projectRows(runtime: Runtime): string[] {
|
|
109
|
-
const projects = new Map<string, { title: string; sessions: number }>();
|
|
110
|
-
for (const session of listSessions(runtime.paths))
|
|
111
|
-
projects.set(session.workspaceId, {
|
|
112
|
-
title: session.title,
|
|
113
|
-
sessions: (projects.get(session.workspaceId)?.sessions || 0) + 1,
|
|
114
|
-
});
|
|
115
|
-
return [...projects.entries()].map(
|
|
116
|
-
([id, project]) =>
|
|
117
|
-
`${id} ${project.sessions} ${project.sessions === 1 ? "session" : "sessions"} ${project.title}`,
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function viewRows(view: WorkspaceDestination, runtime: Runtime, session: Session | undefined): string[] {
|
|
122
|
-
if (view === "history")
|
|
123
|
-
return listSessions(runtime.paths).map((item) => `${item.id} ${item.title}`) || ["No saved sessions."];
|
|
124
|
-
if (view === "projects") return projectRows(runtime) || ["No local projects."];
|
|
125
|
-
if (view === "providers")
|
|
126
|
-
return listProviders().map((provider) => `${provider.id} ${provider.protocol} ${provider.env}`);
|
|
127
|
-
if (view === "usage") {
|
|
128
|
-
const sessions = listSessions(runtime.paths);
|
|
129
|
-
return [
|
|
130
|
-
`workspaces ${new Set(sessions.map((item) => item.workspaceId)).size}`,
|
|
131
|
-
`sessions ${sessions.length}`,
|
|
132
|
-
`tabs ${sessions.reduce((total, item) => total + item.tabs.length, 0)}`,
|
|
133
|
-
`messages ${sessions.reduce((total, item) => total + item.messages.length, 0)}`,
|
|
134
|
-
];
|
|
135
|
-
}
|
|
136
|
-
if (view === "routes")
|
|
137
|
-
return [
|
|
138
|
-
"GET /health",
|
|
139
|
-
"GET /providers",
|
|
140
|
-
"GET /workspaces",
|
|
141
|
-
"GET /usage",
|
|
142
|
-
"GET /preferences",
|
|
143
|
-
"PATCH /preferences",
|
|
144
|
-
"POST /sessions",
|
|
145
|
-
"POST /chat",
|
|
146
|
-
];
|
|
147
|
-
if (view === "settings") {
|
|
148
|
-
const preferences = Object.fromEntries(
|
|
149
|
-
Object.entries(readPreferences(runtime.paths)).map(([key, preference]) => [key, preference.value]),
|
|
150
|
-
);
|
|
151
|
-
const identity = getIdentity(runtime.paths);
|
|
152
|
-
const pairing = pairingStatus(runtime.paths);
|
|
153
|
-
const sessions = listSessions(runtime.paths);
|
|
154
|
-
return [
|
|
155
|
-
`userId ${identity.userId}`,
|
|
156
|
-
`deviceId ${identity.deviceId}`,
|
|
157
|
-
`browser sessions ${pairing.activeSessions}`,
|
|
158
|
-
`database cli-owned-sqlite · ${new Set(sessions.map((session) => session.workspaceId)).size} workspaces · ${sessions.length} sessions`,
|
|
159
|
-
`provider ${runtime.config.activeProvider || runtime.config.provider || "not configured"}`,
|
|
160
|
-
`theme ${preferences.theme || "dark"}`,
|
|
161
|
-
`railMode ${preferences.railMode || "auto"}`,
|
|
162
|
-
`interfaceZoom ${preferences.interfaceZoom || "100"}`,
|
|
163
|
-
"inspect all: devthink config settings",
|
|
164
|
-
"edit identity: devthink identity --id <10-15 lowercase chars>",
|
|
165
|
-
"edit: /settings set theme dark|light",
|
|
166
|
-
"edit: /settings set railMode always|auto|off",
|
|
167
|
-
"edit: /settings set interfaceZoom 80-150",
|
|
168
|
-
"credentials remain in ~/.config/devthink/auth.json",
|
|
169
|
-
];
|
|
170
|
-
}
|
|
171
|
-
if (session) return [`workspace ${session.workspaceId}`, `session ${session.id}`, `tab ${session.activeTabId}`];
|
|
172
|
-
return ["Describe the next feature, bug or piece of work below."];
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function TerminalWorkspace({ runtime, execute, version }: { runtime: Runtime; execute: Execute; version: string }) {
|
|
176
|
-
const { exit } = useApp();
|
|
177
|
-
const { columns } = useWindowSize();
|
|
178
|
-
const [entered, setEntered] = useState(false);
|
|
179
|
-
const [active, setActive] = useState<(typeof categories)[number]>("all");
|
|
180
|
-
const [view, setView] = useState<WorkspaceDestination>("chat");
|
|
181
|
-
const [entries, setEntries] = useState<Entry[]>([]);
|
|
182
|
-
const [session, setSession] = useState<Session>();
|
|
183
|
-
const [draft, setDraft] = useState("");
|
|
184
|
-
const [pending, setPending] = useState(false);
|
|
185
|
-
const [preferenceRevision, setPreferenceRevision] = useState(0);
|
|
186
|
-
const provider = runtime.config.activeProvider || runtime.config.provider || "local";
|
|
187
|
-
const identity = getIdentity(runtime.paths);
|
|
188
|
-
const model = runtime.config.activeModel || runtime.config.model || "not configured";
|
|
189
|
-
const compact = columns < 100;
|
|
190
|
-
const terminalWidth = Math.max(38, columns - (compact ? 2 : 6));
|
|
191
|
-
const tabLine = compact
|
|
192
|
-
? workspaceDestinations.map((destination) => destination.glyph).join(" ")
|
|
193
|
-
: workspaceDestinations
|
|
194
|
-
.map(
|
|
195
|
-
(destination) =>
|
|
196
|
-
`${destination.id === view ? "[" : ""}${destination.glyph} ${destination.label}${destination.id === view ? "]" : ""}`,
|
|
197
|
-
)
|
|
198
|
-
.join(" ");
|
|
199
|
-
const rows = useMemo(() => viewRows(view, runtime, session), [preferenceRevision, runtime, session, view]);
|
|
200
|
-
|
|
201
|
-
function openView(next: WorkspaceDestination) {
|
|
202
|
-
setView(next);
|
|
203
|
-
setEntries((current) => [
|
|
204
|
-
...current,
|
|
205
|
-
{ role: "devthink", title: workspaceDestination(next).label, body: rows.join(" · ") },
|
|
206
|
-
]);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async function submit() {
|
|
210
|
-
const prompt = draft.trim();
|
|
211
|
-
if (!prompt || pending) return;
|
|
212
|
-
if (prompt === "/exit" || prompt === "/quit") return exit();
|
|
213
|
-
if (!entered) {
|
|
214
|
-
setEntries([
|
|
215
|
-
{ role: "user", title: "first intention", body: prompt },
|
|
216
|
-
{
|
|
217
|
-
role: "devthink",
|
|
218
|
-
title: "local workspace ready",
|
|
219
|
-
body: "The first command opened a local DevThink session. Use arrows or /view to inspect local history, projects, providers, usage, routes and settings.",
|
|
220
|
-
},
|
|
221
|
-
]);
|
|
222
|
-
setDraft("");
|
|
223
|
-
setEntered(true);
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
if (prompt === "/clear") {
|
|
227
|
-
setEntries([]);
|
|
228
|
-
setDraft("");
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
if (prompt === "/new") {
|
|
232
|
-
setSession(undefined);
|
|
233
|
-
setEntries([]);
|
|
234
|
-
setView("chat");
|
|
235
|
-
setDraft("");
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
const viewMatch = prompt.match(/^\/(?:view\s+)?(chat|history|projects|providers|usage|routes|settings)$/);
|
|
239
|
-
if (viewMatch && isWorkspaceDestination(viewMatch[1])) {
|
|
240
|
-
openView(viewMatch[1]);
|
|
241
|
-
setDraft("");
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
if (prompt === "/sessions") {
|
|
245
|
-
openView("history");
|
|
246
|
-
setDraft("");
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const preferenceMatch = prompt.match(/^\/(?:settings\s+)?set\s+(theme|railMode|interfaceZoom)\s+(\S+)$/);
|
|
250
|
-
if (preferenceMatch) {
|
|
251
|
-
const [, key, value] = preferenceMatch;
|
|
252
|
-
const valid =
|
|
253
|
-
(key === "theme" && ["dark", "light"].includes(value)) ||
|
|
254
|
-
(key === "railMode" && ["always", "auto", "off"].includes(value)) ||
|
|
255
|
-
(key === "interfaceZoom" && /^(?:[8-9][0-9]|1[0-4][0-9]|150)$/.test(value));
|
|
256
|
-
if (!valid)
|
|
257
|
-
setEntries((current) => [
|
|
258
|
-
...current,
|
|
259
|
-
{
|
|
260
|
-
role: "error",
|
|
261
|
-
title: "settings",
|
|
262
|
-
body: "Use theme dark|light, railMode always|auto|off, or interfaceZoom 80-150.",
|
|
263
|
-
},
|
|
264
|
-
]);
|
|
265
|
-
else {
|
|
266
|
-
savePreference(runtime.paths, key, value);
|
|
267
|
-
setPreferenceRevision((current) => current + 1);
|
|
268
|
-
setView("settings");
|
|
269
|
-
setEntries((current) => [
|
|
270
|
-
...current,
|
|
271
|
-
{ role: "devthink", title: "settings", body: `${key} updated to ${value}.` },
|
|
272
|
-
]);
|
|
273
|
-
}
|
|
274
|
-
setDraft("");
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
if (prompt === "/help") {
|
|
278
|
-
setEntries((current) => [
|
|
279
|
-
...current,
|
|
280
|
-
{
|
|
281
|
-
role: "devthink",
|
|
282
|
-
title: "command reference",
|
|
283
|
-
body: "/view chat|history|projects|providers|usage|routes|settings /settings set <key> <value> /new /clear /models /modes /sessions /exit",
|
|
284
|
-
},
|
|
285
|
-
]);
|
|
286
|
-
setDraft("");
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
setView("chat");
|
|
290
|
-
setEntries((current) => [
|
|
291
|
-
...current,
|
|
292
|
-
{ role: "user", title: active, body: prompt },
|
|
293
|
-
{ role: "devthink", title: "connecting", body: "waiting for local gateway events…" },
|
|
294
|
-
]);
|
|
295
|
-
setDraft("");
|
|
296
|
-
setPending(true);
|
|
297
|
-
let partial = "";
|
|
298
|
-
try {
|
|
299
|
-
const next = await execute(prompt, session, (event) => {
|
|
300
|
-
if (event.type === "text" || event.type === "reasoning") partial += event.text;
|
|
301
|
-
setEntries((current) =>
|
|
302
|
-
current.map((entry, index) =>
|
|
303
|
-
index === current.length - 1
|
|
304
|
-
? {
|
|
305
|
-
...entry,
|
|
306
|
-
title: event.type === "start" ? "streaming" : "devthink",
|
|
307
|
-
body: partial || "connecting to local provider…",
|
|
308
|
-
}
|
|
309
|
-
: entry,
|
|
310
|
-
),
|
|
311
|
-
);
|
|
312
|
-
});
|
|
313
|
-
setSession(next);
|
|
314
|
-
setEntries((current) =>
|
|
315
|
-
current.map((entry, index) =>
|
|
316
|
-
index === current.length - 1
|
|
317
|
-
? {
|
|
318
|
-
...entry,
|
|
319
|
-
title: "devthink",
|
|
320
|
-
body: partial || next?.messages.at(-1)?.content || "the provider returned no text.",
|
|
321
|
-
}
|
|
322
|
-
: entry,
|
|
323
|
-
),
|
|
324
|
-
);
|
|
325
|
-
} catch (error) {
|
|
326
|
-
setEntries((current) =>
|
|
327
|
-
current.map((entry, index) =>
|
|
328
|
-
index === current.length - 1
|
|
329
|
-
? {
|
|
330
|
-
role: "error",
|
|
331
|
-
title: "gateway error",
|
|
332
|
-
body: error instanceof Error ? error.message : "request could not be completed",
|
|
333
|
-
}
|
|
334
|
-
: entry,
|
|
335
|
-
),
|
|
336
|
-
);
|
|
337
|
-
} finally {
|
|
338
|
-
setPending(false);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
useInput((input, key) => {
|
|
343
|
-
if (key.escape || (key.ctrl && input === "c")) return exit();
|
|
344
|
-
if (key.return) return void submit();
|
|
345
|
-
if (key.backspace || key.delete) return setDraft((current) => current.slice(0, -1));
|
|
346
|
-
if (entered && key.upArrow)
|
|
347
|
-
return setView(
|
|
348
|
-
(current) =>
|
|
349
|
-
workspaceDestinations[
|
|
350
|
-
(workspaceDestinations.findIndex((item) => item.id === current) + workspaceDestinations.length - 1) %
|
|
351
|
-
workspaceDestinations.length
|
|
352
|
-
].id,
|
|
353
|
-
);
|
|
354
|
-
if (entered && key.downArrow)
|
|
355
|
-
return setView(
|
|
356
|
-
(current) =>
|
|
357
|
-
workspaceDestinations[
|
|
358
|
-
(workspaceDestinations.findIndex((item) => item.id === current) + 1) % workspaceDestinations.length
|
|
359
|
-
].id,
|
|
360
|
-
);
|
|
361
|
-
if (entered && view === "chat" && (key.leftArrow || key.tab))
|
|
362
|
-
return setActive(
|
|
363
|
-
(current) => categories[(categories.indexOf(current) + categories.length - 1) % categories.length],
|
|
364
|
-
);
|
|
365
|
-
if (entered && view === "chat" && key.rightArrow)
|
|
366
|
-
return setActive((current) => categories[(categories.indexOf(current) + 1) % categories.length]);
|
|
367
|
-
if (!key.ctrl && !key.meta && input) setDraft((current) => current + input);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
if (!entered)
|
|
371
|
-
return <TerminalEntry draft={draft} provider={provider} userId={identity.userId} width={terminalWidth} />;
|
|
372
|
-
|
|
373
|
-
return (
|
|
374
|
-
<Box flexDirection="column" width="100%" height="100%" minHeight={20} paddingX={compact ? 1 : 3} paddingY={1}>
|
|
375
|
-
<Box justifyContent="space-between">
|
|
376
|
-
<Text color="#a19a91">
|
|
377
|
-
✦ DEVTHINK <Text color="#746f66">/ {workspaceDestination(view).label}</Text>
|
|
378
|
-
</Text>
|
|
379
|
-
<Text color="#8ab4f8">◉ {identity.userId}</Text>
|
|
380
|
-
</Box>
|
|
381
|
-
<Text color="#282724">{"─".repeat(terminalWidth)}</Text>
|
|
382
|
-
<Text color="#746f66">{clip(tabLine, terminalWidth)}</Text>
|
|
383
|
-
<Box marginTop={1}>
|
|
384
|
-
<Text color="#e8e1d7">
|
|
385
|
-
{workspaceDestination(view).glyph} {workspaceDestination(view).label.toUpperCase()}{" "}
|
|
386
|
-
<Text color="#746f66">· {clip(model, 22)}</Text>
|
|
387
|
-
</Text>
|
|
388
|
-
</Box>
|
|
389
|
-
<Box
|
|
390
|
-
flexDirection="column"
|
|
391
|
-
flexGrow={1}
|
|
392
|
-
justifyContent={entries.length && view === "chat" ? "flex-start" : "center"}
|
|
393
|
-
alignItems={entries.length && view === "chat" ? "flex-start" : "center"}
|
|
394
|
-
>
|
|
395
|
-
{view !== "chat" ? (
|
|
396
|
-
rows.map((row, index) => (
|
|
397
|
-
<Text key={`${view}-${index}`} color={index === 0 ? "#e8e1d7" : "#746f66"}>
|
|
398
|
-
▎ {clip(row, terminalWidth - 4)}
|
|
399
|
-
</Text>
|
|
400
|
-
))
|
|
401
|
-
) : entries.length ? (
|
|
402
|
-
entries.map((entry, index) => (
|
|
403
|
-
<Box key={`${entry.title}-${index}`} flexDirection="column" marginBottom={1} paddingLeft={1}>
|
|
404
|
-
<Box>
|
|
405
|
-
<Text color={entry.role === "user" ? "#ff5f00" : entry.role === "error" ? "#ff6b6b" : "#8ab4f8"}>
|
|
406
|
-
▎ {entry.title.toUpperCase()}
|
|
407
|
-
</Text>
|
|
408
|
-
</Box>
|
|
409
|
-
<Box>
|
|
410
|
-
<Text color="#e8e1d7">{clip(entry.body, terminalWidth - 6)}</Text>
|
|
411
|
-
</Box>
|
|
412
|
-
</Box>
|
|
413
|
-
))
|
|
414
|
-
) : (
|
|
415
|
-
<Box flexDirection="column" alignItems="center">
|
|
416
|
-
<Text color="#4b4842">Nothing is active yet.</Text>
|
|
417
|
-
<Text color="#625c54">Describe the next feature, bug or piece of work below.</Text>
|
|
418
|
-
<Box marginTop={2}>
|
|
419
|
-
<Text color="#ff5f00" bold>
|
|
420
|
-
DEVTHINK
|
|
421
|
-
</Text>
|
|
422
|
-
</Box>
|
|
423
|
-
</Box>
|
|
424
|
-
)}
|
|
425
|
-
</Box>
|
|
426
|
-
<Text color="#282724">┌{"─".repeat(Math.max(16, terminalWidth - 2))}┐</Text>
|
|
427
|
-
<Text color="#e8e1d7">
|
|
428
|
-
│ <Text color="#ff5f00">›_ </Text>
|
|
429
|
-
<Text color={draft ? "#e8e1d7" : "#746f66"}>
|
|
430
|
-
{clip(
|
|
431
|
-
draft ||
|
|
432
|
-
(view === "chat"
|
|
433
|
-
? `Ask DevThink about ${active === "all" ? "the work…" : `${active}…`}`
|
|
434
|
-
: `Type /view chat to return…`),
|
|
435
|
-
Math.max(12, terminalWidth - 20),
|
|
436
|
-
)}
|
|
437
|
-
</Text>
|
|
438
|
-
<Text color="#8ab4f8"> {provider}</Text>
|
|
439
|
-
<Text color="#ff5f00"> [run]</Text> │
|
|
440
|
-
</Text>
|
|
441
|
-
<Text color="#282724">└{"─".repeat(Math.max(16, terminalWidth - 2))}┘</Text>
|
|
442
|
-
<Text color="#746f66">↑↓ destinations · ←→ categories · /help commands · v{version} · esc exit</Text>
|
|
443
|
-
</Box>
|
|
444
|
-
);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
export async function startTerminalWorkspace(runtime: Runtime, version: string, execute: Execute): Promise<void> {
|
|
448
|
-
const app = render(<TerminalWorkspace runtime={runtime} version={version} execute={execute} />, {
|
|
449
|
-
exitOnCtrlC: false,
|
|
450
|
-
});
|
|
451
|
-
await app.waitUntilExit();
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/* ── The plain ANSI renderer family (the ui.ts merge): pure string renderers of
|
|
455
|
-
the DevThink Orbital Signal Room — canonical ANSI art, orange intent and
|
|
456
|
-
orb-blue connection states. The CLI imports these for the non-Ink fallback
|
|
457
|
-
path and every plain-text surface; they are pure functions returning
|
|
458
|
-
strings, so they coexist with the Ink components above. ── */
|
|
459
|
-
|
|
460
|
-
/** The ANSI palette: background, panel, text and muted chrome with orange intent, orb-blue connections and signal accents. */
|
|
461
|
-
export const colors = {
|
|
462
|
-
background: "\u001b[38;5;234m",
|
|
463
|
-
panel: "\u001b[38;5;240m",
|
|
464
|
-
text: "\u001b[38;5;255m",
|
|
465
|
-
muted: "\u001b[38;5;246m",
|
|
466
|
-
cyan: "\u001b[38;5;117m",
|
|
467
|
-
blue: "\u001b[38;5;111m",
|
|
468
|
-
orange: "\u001b[38;5;208m",
|
|
469
|
-
orangeSoft: "\u001b[38;5;215m",
|
|
470
|
-
green: "\u001b[38;5;151m",
|
|
471
|
-
red: "\u001b[38;5;167m",
|
|
472
|
-
yellow: "\u001b[38;5;222m",
|
|
473
|
-
reset: "\u001b[0m",
|
|
474
|
-
bold: "\u001b[1m",
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
/** The spinner frames of the streaming indicator. */
|
|
478
|
-
const frames = ["◐", "◓", "◑", "◒"];
|
|
479
|
-
|
|
480
|
-
/** Canonical DevThink ANSI mark transcribed from docs/logo.md. */
|
|
481
|
-
const canonicalMark = String.raw`
|
|
482
|
-
+
|
|
483
|
-
+
|
|
484
|
-
-+
|
|
485
|
-
++
|
|
486
|
-
++
|
|
487
|
-
+++
|
|
488
|
-
-++
|
|
489
|
-
++++-
|
|
490
|
-
+++++++++
|
|
491
|
-
+-+++++++++++++--
|
|
492
|
-
--++++++-++++++++++++
|
|
493
|
-
+++++++-+ +++ +--++++++-
|
|
494
|
-
-+++++++-+ +++ -+-+++++++
|
|
495
|
-
++++++++++ -+++ --++++++++
|
|
496
|
-
++++++++-+ ++++ --+++++++-
|
|
497
|
-
++++++++++ ++++ --+++++++-
|
|
498
|
-
---++++++- -++++ -++++++--+
|
|
499
|
-
++++++++-- +++++ ++++++++--
|
|
500
|
-
+--+++++-- +++++ +++++++---
|
|
501
|
-
++++++++- +++++ +-+++++++
|
|
502
|
-
-+++++-- -++++- --++++++
|
|
503
|
-
-+++++- +++++- +++++++
|
|
504
|
-
-++++++ -+++++- +++++++
|
|
505
|
-
-++++++ ++++++- +++++++
|
|
506
|
-
-++++++ +++++++- +++++++
|
|
507
|
-
-++++++ ++++++++- +++++++
|
|
508
|
-
-++++++ -++++++++++-+ +++++++
|
|
509
|
-
-++++++ -+++++++++++++- +++++++
|
|
510
|
-
-++++++ -+++++++++++++++++ +++++++
|
|
511
|
-
-++++++ -++++++++++++++++++++ +++++++
|
|
512
|
-
-++++++ -++++++++++++++++++++++ +++++++
|
|
513
|
-
-++++++ -+++++++++++++++++++++++++- +++++++
|
|
514
|
-
-++++++ +++++++++++++++++-+--++++++++-- +++++++
|
|
515
|
-
-++++++ +++++++++++++++ -+++++++++ +++++++
|
|
516
|
-
-++++++ +++++++++++-++ --++++++-+ +-++++
|
|
517
|
-
-++++++ ++++++++++-+- +--++++-- --++
|
|
518
|
-
-++++++ +--++++++++++ ---++++-+ +-
|
|
519
|
-
-++++++ ++-++++++++ ++++++++
|
|
520
|
-
-++++++ -+++++-+-- +-++-+
|
|
521
|
-
-+++++++++++++ -++++++
|
|
522
|
-
-++++++++ +-+++-+++-
|
|
523
|
-
-+++++++++++ ++++++++- +---+
|
|
524
|
-
--++-+ -++++++-++ ---++++++++ +-++
|
|
525
|
-
++++ +-+++++++ -++++++-+ ++
|
|
526
|
-
-++ ++++++++-- ++++++++-+- +
|
|
527
|
-
+-++++++-+ --++++++++
|
|
528
|
-
++-+++++++ ++++++++-+
|
|
529
|
-
+-++++++++ --++++++++
|
|
530
|
-
++++++++++ +--++++++-
|
|
531
|
-
--+++++++- ++++++++++
|
|
532
|
-
-+++++++-++++--++++++++
|
|
533
|
-
+-++++++++++++++-
|
|
534
|
-
--++++++++-
|
|
535
|
-
+-++-`;
|
|
536
|
-
|
|
537
|
-
/** Wraps a value in an ANSI color code and resets after it. */
|
|
538
|
-
function color(code: string, value: string): string {
|
|
539
|
-
return `${code}${value}${colors.reset}`;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/** Strips ANSI escape sequences so visible width can be measured. */
|
|
543
|
-
function stripAnsi(value: string): string {
|
|
544
|
-
return value.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "");
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/** Pads a possibly-colored value to a visible width. */
|
|
548
|
-
function pad(value: string, width: number): string {
|
|
549
|
-
const visible = stripAnsi(value);
|
|
550
|
-
return visible.length >= width
|
|
551
|
-
? value.slice(0, Math.max(0, width - Math.max(0, visible.length - width)))
|
|
552
|
-
: value + " ".repeat(width - visible.length);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/** Renders the banner: the canonical ANSI mark, the version and the local workspace signal line. */
|
|
556
|
-
export function banner(version: string): string {
|
|
557
|
-
const mark = canonicalMark
|
|
558
|
-
.split("\n")
|
|
559
|
-
.map((line) => color(colors.orange, line))
|
|
560
|
-
.join("\n");
|
|
561
|
-
const title = color(colors.orange + colors.bold, "DEVTHINK");
|
|
562
|
-
const signal = color(colors.blue, "◆ local workspace");
|
|
563
|
-
return [
|
|
564
|
-
color(colors.muted, "╭────────────────────────────── signal workspace ──────────────────────────────╮"),
|
|
565
|
-
mark,
|
|
566
|
-
` ${title} ${color(colors.muted, `v${version}`)} ${signal}`,
|
|
567
|
-
` ${color(colors.muted, "route the work · keep provider credentials local · ctrl+p commands")}`,
|
|
568
|
-
color(colors.muted, "╰────────────────────────────────────────────────────────────────────────────────╯"),
|
|
569
|
-
].join("\n");
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/** Renders a titled content box with the workspace panel chrome. */
|
|
573
|
-
export function box(title: string, content: string, width = 76): string {
|
|
574
|
-
const innerWidth = Math.max(20, width - 4);
|
|
575
|
-
const top = `╭─ ${color(colors.orange, title)} ${color(colors.muted, "─".repeat(Math.max(0, innerWidth - title.length - 3)))}╮`;
|
|
576
|
-
const body = content.split("\n").map((line) => `│ ${color(colors.blue, "▎")} ${pad(line, innerWidth - 2)} │`);
|
|
577
|
-
return [top, ...body, `╰${"─".repeat(width - 2)}╯`].join("\n");
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/** Renders the one-line provider/model/mode status bar. */
|
|
581
|
-
export function statusBar(provider: string, model: string, mode: string): string {
|
|
582
|
-
const providerLabel = provider || "not configured";
|
|
583
|
-
const modelLabel = model || "not configured";
|
|
584
|
-
return [
|
|
585
|
-
color(colors.orange, "◆"),
|
|
586
|
-
color(colors.text, providerLabel),
|
|
587
|
-
color(colors.muted, "/"),
|
|
588
|
-
color(colors.blue, modelLabel),
|
|
589
|
-
color(colors.muted, "·"),
|
|
590
|
-
color(colors.cyan, mode),
|
|
591
|
-
].join(" ");
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/** Renders one streaming spinner frame with its message. */
|
|
595
|
-
export function spinnerFrame(index: number, message: string): string {
|
|
596
|
-
return `${color(colors.blue, frames[index % frames.length])} ${color(colors.muted, "stream")} ${message}`;
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/** Renders one chat event as plain ANSI text. */
|
|
600
|
-
export function formatEvent(event: ChatEvent): string {
|
|
601
|
-
if (event.type === "text") return event.text;
|
|
602
|
-
if (event.type === "reasoning") return `${color(colors.blue, "·")} ${color(colors.muted, event.text)}`;
|
|
603
|
-
if (event.type === "error") return color(colors.red, event.message);
|
|
604
|
-
if (event.type === "finish") return "";
|
|
605
|
-
if (event.type === "start")
|
|
606
|
-
return `${color(colors.blue, "◆")} ${color(colors.text, "connected")} ${color(colors.muted, `${event.provider} / ${event.model}`)}`;
|
|
607
|
-
return `${color(colors.orange, "→")} ${color(colors.blue, event.name)} ${color(colors.muted, JSON.stringify(event.input))}`;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
/** Filters a suggestion list down to the values matching the input, capped at a limit. */
|
|
611
|
-
export function suggestions(input: string, values: string[], limit = 8): string[] {
|
|
612
|
-
const query = input.trim().toLowerCase();
|
|
613
|
-
if (!query) return values.slice(0, limit);
|
|
614
|
-
return values.filter((value) => value.toLowerCase().includes(query)).slice(0, limit);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
/** Renders a config record as aligned key/value lines. */
|
|
618
|
-
export function formatConfig(config: Record<string, unknown>): string {
|
|
619
|
-
return Object.entries(config)
|
|
620
|
-
.map(([key, value]) => `${color(colors.muted, pad(key, 20))} ${String(value)}`)
|
|
621
|
-
.join("\n");
|
|
622
|
-
}
|