@useshifu/coding-harness 0.3.35 → 0.3.36
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/README.md +10 -10
- package/bin/onboarding.js +16 -15
- package/bin/shifu-harness.js +1 -1
- package/package.json +1 -1
- package/skills/shifu-onboarding/SKILL.md +5 -3
- package/wizard/brand-mark.png +0 -0
- package/wizard/fonts/ibm-plex-mono-OFL.txt +93 -0
- package/wizard/fonts/ibm-plex-mono.ttf +0 -0
- package/wizard/fonts/plus-jakarta-sans-OFL.txt +93 -0
- package/wizard/fonts/plus-jakarta-sans.ttf +0 -0
- package/wizard/index.html +62 -17
- package/wizard/wizard.css +78 -1
- package/wizard/wizard.js +15 -14
package/README.md
CHANGED
|
@@ -280,30 +280,30 @@ The companion endpoint is used only when the authenticated connection advertises
|
|
|
280
280
|
|
|
281
281
|
Use only compatible preparation writers on a shared configuration directory. An older runner can read and transport the unchanged base queue, but cannot enforce new preparation holds. Stop its preparation schedule or use a compatible rollback build. Never delete checkpoints or replay accepted history to repair missing continuity.
|
|
282
282
|
|
|
283
|
-
##
|
|
283
|
+
## Set up Shifu
|
|
284
284
|
|
|
285
|
-
|
|
285
|
+
Use the `shifu-onboarding` skill in your coding assistant, or open Shifu setup yourself:
|
|
286
286
|
|
|
287
287
|
```sh
|
|
288
|
-
npx --yes @useshifu/coding-harness@0.3.
|
|
288
|
+
npx --yes @useshifu/coding-harness@0.3.36 onboard --harness codex
|
|
289
289
|
```
|
|
290
290
|
|
|
291
|
-
Choose `codex`, `claude_code`, `opencode`, or `cursor`. Run this on the local macOS/Linux device whose coding history you want to connect. A remote/headless assistant cannot open this device's
|
|
291
|
+
Choose `codex`, `claude_code`, `opencode`, or `cursor`. Run this on the local macOS/Linux device whose coding history you want to connect. A remote/headless assistant cannot open this device's setup page: run the command in your own local terminal instead. The launcher prints a local URL if opening the browser fails. That URL carries a per-launch capability in its fragment; do not share it. It is not your Shifu connection key.
|
|
292
292
|
|
|
293
|
-
The
|
|
293
|
+
The setup page collects initial history (15 days, 30 days, custom 1–36500 days, or start from now), independent growth-feedback and decision-alignment choices, and shows examples. Saved settings and the original history cutoff are reused. Keys go directly from the setup page to the local runner, never through chat. Existing connections are verified and reused. Review explicitly authorizes preparation and upload of the selected coding history; source text uses your configured coding-model provider. The one-time backfill does not change an existing manual approval policy for future work. Folder-file uploads are separate from this coding-history backfill.
|
|
294
294
|
|
|
295
295
|
Accepted ranges use existing checkpoints and exact frozen payloads. Initial discovery freezes session boundaries for this run; later activity follows ordinary sync. Closing the browser does not stop the detached worker. The scheduled runner resumes unfinished ranges on its next tick; **Resume unfinished backfill** retries immediately. Older unsynced turns outside the saved window remain blocked rather than silently expanding scope. A zero-day selection skips historical work. Empty history is reported honestly, not counted as captured context.
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
Setup checks saved-work availability before offering **Copy a question**. Reload the assistant as instructed and paste that short prompt. The non-secret run reference only identifies local state; it never grants account access. The skill checks status without reinstalling, reasking choices or replaying accepted history:
|
|
298
298
|
|
|
299
299
|
```sh
|
|
300
|
-
npx --yes @useshifu/coding-harness@0.3.
|
|
300
|
+
npx --yes @useshifu/coding-harness@0.3.36 onboard --harness codex --status --run <reference>
|
|
301
301
|
```
|
|
302
302
|
|
|
303
303
|
Only after explicit user confirmation that the sourced answer helped may the assistant add `--helpful`. The backend requires saved evidence and a real context retrieval before recording activation. Assistant usefulness telemetry is not user confirmation.
|
|
304
304
|
|
|
305
|
-
To change choices after a completed, skipped or empty run, use **Change
|
|
305
|
+
To change choices after a completed, skipped or empty run, use **Change preferences**, or explicitly launch with `--new`. This preserves accepted sync checkpoints. An active selection cannot be discarded with `--new`. If a key was revoked, replace it through the existing interactive `connect` flow first (which resets local checkpoints), then use `onboard --new` to bind the changed connection. A key replacement or package upgrade alone does not authorize another backfill.
|
|
306
306
|
|
|
307
|
-
|
|
307
|
+
The setup page and skill ship in package `0.3.36`, with the same light theme and bundled fonts as the dashboard. The dashboard intentionally has no onboarding entry points. The existing progress API remains compatible. No package is published merely by changing this checkout.
|
|
308
308
|
|
|
309
|
-
For an unfinished selection that you explicitly want to leave, **Change
|
|
309
|
+
For an unfinished selection that you explicitly want to leave, **Change preferences** asks for confirmation and preserves accepted records/checkpoints. The equivalent terminal operation is `onboard --harness codex --new --abandon`. Ordinary `--new` refuses to discard an active run. If you explicitly resolve an older blocked prefix through the existing backfill workflow, Resume rechecks only the frozen blocked targets and can finish without importing later appended turns.
|
package/bin/onboarding.js
CHANGED
|
@@ -28,7 +28,7 @@ function fingerprint(config) { return config?.token ? createHash('sha256').updat
|
|
|
28
28
|
function load(runtime, runId) {
|
|
29
29
|
const state = read(file(runtime));
|
|
30
30
|
if (runId && (!ID.test(runId) || state?.runId !== runId)) throw new Error('This setup run is not available on this device.');
|
|
31
|
-
if (state && state.connectionFingerprint && state.connectionFingerprint !== fingerprint(runtime.readConfig())) throw new Error('The connection changed.
|
|
31
|
+
if (state && state.connectionFingerprint && state.connectionFingerprint !== fingerprint(runtime.readConfig())) throw new Error('The connection changed. Ask your assistant to start Shifu setup again.');
|
|
32
32
|
return state;
|
|
33
33
|
}
|
|
34
34
|
function save(runtime, state) {
|
|
@@ -58,7 +58,7 @@ function projection(state) {
|
|
|
58
58
|
}
|
|
59
59
|
function safeStatus(runtime, state) {
|
|
60
60
|
return state ? { ...projection(state), contextAvailable: Boolean(state.contextAvailable), retrievalAvailable: Boolean(state.retrievalAvailable), configured: Boolean(runtime.readConfig()), authorized: state.authorized === true,
|
|
61
|
-
reloadRequired: state.connectionStatus === 'reload_required', resumePrompt: `
|
|
61
|
+
reloadRequired: state.connectionStatus === 'reload_required', resumePrompt: `Use my saved Shifu setup (reference ${state.runId}). Check progress with the shifu-onboarding skill, then answer using my work context: What decisions and constraints should I remember from my recent work?` } : { configured: Boolean(runtime.readConfig()), authorized: false };
|
|
62
62
|
}
|
|
63
63
|
async function publish(runtime, state) {
|
|
64
64
|
const config = runtime.readConfig();
|
|
@@ -153,17 +153,18 @@ async function startServer(runtime, state, { port=0 }={}) {
|
|
|
153
153
|
let origin, busy=false;
|
|
154
154
|
const server = http.createServer(async (request,response) => {
|
|
155
155
|
response.setHeader('Cache-Control','no-store'); response.setHeader('Referrer-Policy','no-referrer'); response.setHeader('X-Content-Type-Options','nosniff');
|
|
156
|
-
response.setHeader('Content-Security-Policy',"default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src '
|
|
156
|
+
response.setHeader('Content-Security-Policy',"default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self'; font-src 'self'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'");
|
|
157
157
|
const send = (status,value) => { response.writeHead(status,{'Content-Type':'application/json'}); response.end(JSON.stringify(value)); };
|
|
158
|
-
if (request.headers.host !== new URL(origin).host || (request.headers.origin && request.headers.origin !== origin)) return send(403,{error:'
|
|
158
|
+
if (request.headers.host !== new URL(origin).host || (request.headers.origin && request.headers.origin !== origin)) return send(403,{error:'This page could not be verified. Reopen Shifu setup from your assistant.'});
|
|
159
159
|
const url = new URL(request.url,origin);
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
const assets = { '/': ['index.html', 'text/html'], '/wizard.js': ['wizard.js', 'text/javascript'], '/wizard.css': ['wizard.css', 'text/css'], '/brand-mark.png': ['brand-mark.png', 'image/png'], '/fonts/plus-jakarta-sans.ttf': ['fonts/plus-jakarta-sans.ttf', 'font/ttf'], '/fonts/ibm-plex-mono.ttf': ['fonts/ibm-plex-mono.ttf', 'font/ttf'] };
|
|
161
|
+
if (request.method === 'GET' && Object.hasOwn(assets, url.pathname)) {
|
|
162
|
+
const [asset, contentType] = assets[url.pathname];
|
|
163
|
+
response.writeHead(200,{'Content-Type':contentType});
|
|
163
164
|
return response.end(fs.readFileSync(path.join(runtime.assets,asset)));
|
|
164
165
|
}
|
|
165
166
|
const supplied = Buffer.from(String(request.headers['x-shifu-wizard'] || ''));
|
|
166
|
-
if (supplied.length !== token.length || !timingSafeEqual(supplied,Buffer.from(token))) return send(401,{error:'Reopen
|
|
167
|
+
if (supplied.length !== token.length || !timingSafeEqual(supplied,Buffer.from(token))) return send(401,{error:'Reopen Shifu setup from your assistant.'});
|
|
167
168
|
try {
|
|
168
169
|
if (request.method === 'GET' && url.pathname === '/api/status') {
|
|
169
170
|
const current = load(runtime,state.runId); await publish(runtime,current); return send(200,safeStatus(runtime,current));
|
|
@@ -171,7 +172,7 @@ async function startServer(runtime, state, { port=0 }={}) {
|
|
|
171
172
|
if (request.method === 'POST' && url.pathname === '/api/apply') {
|
|
172
173
|
if (busy) return send(409,{error:'Setup is already running.'});
|
|
173
174
|
const body = await bodyJSON(request); busy=true;
|
|
174
|
-
try { const result=await runtime.setupLock(async()=>{state=load(runtime,state.runId);await apply(runtime,state,body);});if(result?.skipped)return send(409,{error:"
|
|
175
|
+
try { const result=await runtime.setupLock(async()=>{state=load(runtime,state.runId);await apply(runtime,state,body);});if(result?.skipped)return send(409,{error:"Shifu setup is already open in another window."}); } finally { busy=false; }
|
|
175
176
|
return send(200,safeStatus(runtime,state));
|
|
176
177
|
}
|
|
177
178
|
if (request.method === 'POST' && url.pathname === '/api/new') {
|
|
@@ -189,7 +190,7 @@ async function startServer(runtime, state, { port=0 }={}) {
|
|
|
189
190
|
if (!state.authorized) return send(409,{error:'Approve setup first.'});
|
|
190
191
|
spawnWorker(runtime,state); return send(202,safeStatus(runtime,state));
|
|
191
192
|
}
|
|
192
|
-
return send(404,{error:'
|
|
193
|
+
return send(404,{error:'This setup action is unavailable.'});
|
|
193
194
|
} catch (error) {
|
|
194
195
|
// Never relay provider or connector errors: those can contain source text or credentials.
|
|
195
196
|
return send(400,{error: ['Choose a valid history window and both feedback options.','The connection key format is invalid.','Unsupported setup option.','This run has a fixed history selection. Finish it before changing the history window.'].includes(error.message) ? error.message : 'Setup could not finish. Your accepted work is preserved; check the connection and retry.'});
|
|
@@ -210,13 +211,13 @@ async function main(runtime, options={}) {
|
|
|
210
211
|
if (!state?.authorized) throw new Error('Verify setup before confirming usefulness.');
|
|
211
212
|
const config=runtime.readConfig();
|
|
212
213
|
const response=await fetch(`${config.apiUrl}/v1/connectors/coding-sessions/onboarding/feedback`,{method:'POST',headers:{Authorization:`Bearer ${config.token}`,'Content-Type':'application/json'},body:JSON.stringify({harness:runtime.harness,runId:state.runId,action:'helpful'}),signal:AbortSignal.timeout(10000)});
|
|
213
|
-
if (!response.ok) throw new Error('Shifu could not
|
|
214
|
+
if (!response.ok) throw new Error('Shifu could not save your feedback.');
|
|
214
215
|
return console.log(JSON.stringify({helpful:true,runId:state.runId}));
|
|
215
216
|
}
|
|
216
|
-
if (options.worker) { if (!state?.authorized) throw new Error('
|
|
217
|
+
if (options.worker) { if (!state?.authorized) throw new Error('Confirm your preferences before syncing history.'); return resumeBackfill(runtime); }
|
|
217
218
|
if (!state) {
|
|
218
219
|
const result=await runtime.setupLock(async()=>{state=load(runtime)||initial(runtime);});
|
|
219
|
-
if(result?.skipped)throw new Error("
|
|
220
|
+
if(result?.skipped)throw new Error("Shifu setup is already opening. Try again in a moment.");
|
|
220
221
|
}
|
|
221
222
|
if (options.serve) {
|
|
222
223
|
const instance=await startServer(runtime,state);
|
|
@@ -237,8 +238,8 @@ async function main(runtime, options={}) {
|
|
|
237
238
|
child.on('error',()=>{});child.unref();
|
|
238
239
|
for(let attempt=0;attempt<30;attempt+=1){ await new Promise(resolve=>setTimeout(resolve,100)); const next=read(file(runtime,'wizard'));if(next?.runId===state.runId && next.pid===child.pid){url=next.url;break;} }
|
|
239
240
|
}
|
|
240
|
-
if(!url) throw new Error('Could not
|
|
241
|
+
if(!url) throw new Error('Could not open Shifu setup. Run this command on your Mac or Linux computer.');
|
|
241
242
|
if(process.env.SHIFU_ONBOARD_NO_BROWSER!=='1') execFile(process.platform==='darwin'?'open':'xdg-open',[url],()=>{});
|
|
242
|
-
console.log(JSON.stringify({runId:state.runId,wizardUrl:url,message:'Open
|
|
243
|
+
console.log(JSON.stringify({runId:state.runId,wizardUrl:url,message:'Open Shifu setup and choose your preferences. Enter your connection key there, never in chat. When ready, return to your assistant with the question provided.'}));
|
|
243
244
|
}
|
|
244
245
|
module.exports={main,startServer,initial,load,projection,safeStatus,validateChoices,apply,resumeBackfill};
|
package/bin/shifu-harness.js
CHANGED
|
@@ -47,7 +47,7 @@ const MAX_TURNS_PER_SEGMENT = 12;
|
|
|
47
47
|
const MAX_TITLE_LENGTH = 160;
|
|
48
48
|
const MAX_WORK_DETAIL_LENGTH = 600;
|
|
49
49
|
const CONFIG_VERSION = 2;
|
|
50
|
-
const RUNNER_VERSION = "0.3.
|
|
50
|
+
const RUNNER_VERSION = "0.3.36";
|
|
51
51
|
const REDACTION_VERSION = 5;
|
|
52
52
|
const LEGACY_REDACTION_VERSION = 4;
|
|
53
53
|
const MAX_PREPARATIONS_PER_RUN = 4;
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: shifu-onboarding
|
|
3
|
-
description: Set up or resume Shifu through its
|
|
3
|
+
description: Set up or resume Shifu through its setup page; check progress and continue to a first useful answer.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Identify the current supported coding tool; ask if unclear. Launch `npx --yes @useshifu/coding-harness onboard --harness codex`. Open
|
|
6
|
+
Identify the current supported coding tool; ask if unclear. Launch `npx --yes @useshifu/coding-harness onboard --harness codex`. Open the returned setup URL; never ask for a connection key in chat or read private connection files. The runner owns settings, connection checks and approved backfill. Do not generate HTML, inspect unrelated projects, force sync, or repeat selected choices.
|
|
7
|
+
|
|
8
|
+
Call the experience “Shifu setup”. Give only the next action in plain language. Keep wizard, server, capability and checkpoint details out of user-facing replies. Before starting history processing, explain the selected history and model-account usage. Keep connection keys out of chat.
|
|
7
9
|
|
|
8
10
|
On a resume prompt, run `npx --yes @useshifu/coding-harness onboard --harness codex --status --run <reference>`. A reference is not a credential. Reuse saved state. If a reload is required and Shifu tools are unavailable in this chat, explain the tool-specific reload instructions and have the user paste the same prompt in a new chat. OpenCode 2 may require `opencode service stop` before reopening; never stop an active service automatically.
|
|
9
11
|
|
|
10
|
-
Once tools are available and context is ready, retrieve context for the user's question and answer with sources. Ask whether it helped. Only after an explicit positive answer, run `npx --yes @useshifu/coding-harness onboard --harness codex --run <reference> --helpful`. Never treat assistant usefulness telemetry as user confirmation. Zero or empty history is a valid setup result, not proof of captured work. Resume unfinished work through the
|
|
12
|
+
Once tools are available and context is ready, retrieve context for the user's question and answer with sources. Ask whether it helped. Only after an explicit positive answer, run `npx --yes @useshifu/coding-harness onboard --harness codex --run <reference> --helpful`. Never treat assistant usefulness telemetry as user confirmation. Zero or empty history is a valid setup result, not proof of captured work. Resume unfinished work through the setup page; preserve the selected cutoff and accepted checkpoints.
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
|
|
5
|
+
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Copyright 2020 The Plus Jakarta Sans Project Authors (https://github.com/tokotype/PlusJakartaSans)
|
|
2
|
+
|
|
3
|
+
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
|
4
|
+
This license is copied below, and is also available with a FAQ at:
|
|
5
|
+
http://scripts.sil.org/OFL
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
-----------------------------------------------------------
|
|
9
|
+
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
|
10
|
+
-----------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
PREAMBLE
|
|
13
|
+
The goals of the Open Font License (OFL) are to stimulate worldwide
|
|
14
|
+
development of collaborative font projects, to support the font creation
|
|
15
|
+
efforts of academic and linguistic communities, and to provide a free and
|
|
16
|
+
open framework in which fonts may be shared and improved in partnership
|
|
17
|
+
with others.
|
|
18
|
+
|
|
19
|
+
The OFL allows the licensed fonts to be used, studied, modified and
|
|
20
|
+
redistributed freely as long as they are not sold by themselves. The
|
|
21
|
+
fonts, including any derivative works, can be bundled, embedded,
|
|
22
|
+
redistributed and/or sold with any software provided that any reserved
|
|
23
|
+
names are not used by derivative works. The fonts and derivatives,
|
|
24
|
+
however, cannot be released under any other type of license. The
|
|
25
|
+
requirement for fonts to remain under this license does not apply
|
|
26
|
+
to any document created using the fonts or their derivatives.
|
|
27
|
+
|
|
28
|
+
DEFINITIONS
|
|
29
|
+
"Font Software" refers to the set of files released by the Copyright
|
|
30
|
+
Holder(s) under this license and clearly marked as such. This may
|
|
31
|
+
include source files, build scripts and documentation.
|
|
32
|
+
|
|
33
|
+
"Reserved Font Name" refers to any names specified as such after the
|
|
34
|
+
copyright statement(s).
|
|
35
|
+
|
|
36
|
+
"Original Version" refers to the collection of Font Software components as
|
|
37
|
+
distributed by the Copyright Holder(s).
|
|
38
|
+
|
|
39
|
+
"Modified Version" refers to any derivative made by adding to, deleting,
|
|
40
|
+
or substituting -- in part or in whole -- any of the components of the
|
|
41
|
+
Original Version, by changing formats or by porting the Font Software to a
|
|
42
|
+
new environment.
|
|
43
|
+
|
|
44
|
+
"Author" refers to any designer, engineer, programmer, technical
|
|
45
|
+
writer or other person who contributed to the Font Software.
|
|
46
|
+
|
|
47
|
+
PERMISSION & CONDITIONS
|
|
48
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
49
|
+
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
|
50
|
+
redistribute, and sell modified and unmodified copies of the Font
|
|
51
|
+
Software, subject to the following conditions:
|
|
52
|
+
|
|
53
|
+
1) Neither the Font Software nor any of its individual components,
|
|
54
|
+
in Original or Modified Versions, may be sold by itself.
|
|
55
|
+
|
|
56
|
+
2) Original or Modified Versions of the Font Software may be bundled,
|
|
57
|
+
redistributed and/or sold with any software, provided that each copy
|
|
58
|
+
contains the above copyright notice and this license. These can be
|
|
59
|
+
included either as stand-alone text files, human-readable headers or
|
|
60
|
+
in the appropriate machine-readable metadata fields within text or
|
|
61
|
+
binary files as long as those fields can be easily viewed by the user.
|
|
62
|
+
|
|
63
|
+
3) No Modified Version of the Font Software may use the Reserved Font
|
|
64
|
+
Name(s) unless explicit written permission is granted by the corresponding
|
|
65
|
+
Copyright Holder. This restriction only applies to the primary font name as
|
|
66
|
+
presented to the users.
|
|
67
|
+
|
|
68
|
+
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
|
69
|
+
Software shall not be used to promote, endorse or advertise any
|
|
70
|
+
Modified Version, except to acknowledge the contribution(s) of the
|
|
71
|
+
Copyright Holder(s) and the Author(s) or with their explicit written
|
|
72
|
+
permission.
|
|
73
|
+
|
|
74
|
+
5) The Font Software, modified or unmodified, in part or in whole,
|
|
75
|
+
must be distributed entirely under this license, and must not be
|
|
76
|
+
distributed under any other license. The requirement for fonts to
|
|
77
|
+
remain under this license does not apply to any document created
|
|
78
|
+
using the Font Software.
|
|
79
|
+
|
|
80
|
+
TERMINATION
|
|
81
|
+
This license becomes null and void if any of the above conditions are
|
|
82
|
+
not met.
|
|
83
|
+
|
|
84
|
+
DISCLAIMER
|
|
85
|
+
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
86
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
|
87
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
|
88
|
+
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
|
89
|
+
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
90
|
+
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
|
91
|
+
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
92
|
+
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
|
93
|
+
OTHER DEALINGS IN THE FONT SOFTWARE.
|
|
Binary file
|
package/wizard/index.html
CHANGED
|
@@ -1,18 +1,63 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
|
-
<html lang="en"
|
|
3
|
-
<
|
|
4
|
-
<
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
<div id="
|
|
15
|
-
<
|
|
16
|
-
<
|
|
17
|
-
<
|
|
18
|
-
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Connect Shifu</title>
|
|
7
|
+
<link rel="stylesheet" href="/wizard.css">
|
|
8
|
+
<script src="/wizard.js" defer></script>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<main>
|
|
12
|
+
<header><img src="/brand-mark.png" alt=""><span>Shifu</span><small>Setup</small></header>
|
|
13
|
+
<section>
|
|
14
|
+
<div id="welcome"><h1>Set up Shifu</h1><p class="intro">Choose what to remember and how you’d like Shifu to help.</p></div>
|
|
15
|
+
<form id="settings">
|
|
16
|
+
<fieldset>
|
|
17
|
+
<legend class="sr-only">Your preferences</legend>
|
|
18
|
+
<label class="label" for="history">How much recent work should Shifu remember?</label>
|
|
19
|
+
<select id="history"><option value="15">Last 15 days · recommended</option><option value="30">Last 30 days</option><option value="custom">Choose a number of days</option><option value="0">Start from now</option></select>
|
|
20
|
+
<label id="custom-label" hidden>Number of days<input id="custom" type="number" min="1" max="36500" step="1" value="7"></label>
|
|
21
|
+
<p class="example">Include recent decisions and lessons. More history uses more of your model account.</p>
|
|
22
|
+
<fieldset class="option"><legend>Feedback on your work</legend>
|
|
23
|
+
<label><input type="radio" name="growth" value="on" checked>On</label><label><input type="radio" name="growth" value="off">Off</label>
|
|
24
|
+
<p class="example">For example: “Adding a success measure could make this plan easier to evaluate.”</p>
|
|
25
|
+
</fieldset>
|
|
26
|
+
<fieldset class="option"><legend>Reminders about past decisions</legend>
|
|
27
|
+
<label><input type="radio" name="alignment" value="on" checked>On</label><label><input type="radio" name="alignment" value="off">Off</label>
|
|
28
|
+
<p class="example">For example: “Earlier notes set a 30-minute window. Has that changed?”</p>
|
|
29
|
+
</fieldset>
|
|
30
|
+
<p class="note">Your past work stays searchable with either option off.</p>
|
|
31
|
+
<div id="key-entry">
|
|
32
|
+
<label class="label" for="key">Your <span id="harness"></span> connection key</label>
|
|
33
|
+
<input id="key" type="password" autocomplete="off" spellcheck="false" placeholder="Paste your Shifu connection key">
|
|
34
|
+
<p class="note"><a href="https://app.useshifu.com/#/sources" target="_blank" rel="noreferrer">Get a key from Sources</a>, then paste it here. Keep it out of chat.</p>
|
|
35
|
+
</div>
|
|
36
|
+
<div id="saved-key" class="note" hidden>Your saved connection is ready to use.
|
|
37
|
+
<details><summary>Need to replace your key?</summary><p>Ask your assistant to reconnect Shifu. Enter the new key in the secure prompt, never in chat. Replacing a connection can reset local sync progress; work already saved in Shifu remains.</p></details>
|
|
38
|
+
</div>
|
|
39
|
+
</fieldset>
|
|
40
|
+
<button type="submit">Continue</button>
|
|
41
|
+
</form>
|
|
42
|
+
<div id="review" hidden>
|
|
43
|
+
<h1>Review your choices</h1><dl id="summary"></dl><p id="scope" class="note"></p>
|
|
44
|
+
<p class="note">Your current sync settings stay the same. New connections sync automatically every hour.</p>
|
|
45
|
+
<p class="note">Your coding tool’s model provider prepares the selected text. Shifu receives redacted records, but redaction can miss sensitive details. History may include multiple projects.</p>
|
|
46
|
+
<div class="actions"><button id="apply" type="button">Connect and sync</button><button id="edit" class="secondary" type="button">Back</button></div>
|
|
47
|
+
</div>
|
|
48
|
+
<div id="progress" hidden>
|
|
49
|
+
<h1 id="progress-title">Connecting Shifu…</h1><dl id="progress-facts"></dl><p id="progress-note" class="note"></p>
|
|
50
|
+
<div class="actions"><button id="resume" type="button" hidden>Continue syncing</button><button id="copy" type="button" hidden>Copy a question</button><button id="change" class="secondary" type="button" hidden>Change preferences</button></div>
|
|
51
|
+
<p id="handoff" class="note" hidden>Return to your assistant and paste the question. If Shifu isn’t available there yet, start a new chat first.</p>
|
|
52
|
+
<textarea id="prompt" readonly hidden aria-label="Question to copy"></textarea>
|
|
53
|
+
</div>
|
|
54
|
+
<p id="message" role="status" aria-live="polite"></p>
|
|
55
|
+
<details class="privacy"><summary>Your data and model usage</summary>
|
|
56
|
+
<p>Choosing preferences uses no model tokens. Preparing your selected history uses your coding tool’s model account; cost depends on how much work you include.</p>
|
|
57
|
+
<p>Files you add to the Shifu folder upload as provided for server processing. Keep unapproved drafts outside that folder.</p>
|
|
58
|
+
<p>Disconnecting stops future access. Deleting saved work is a separate beta request. <a href="https://www.useshifu.com/data" target="_blank" rel="noreferrer">Data handling</a> · <a href="https://www.useshifu.com/privacy" target="_blank" rel="noreferrer">Privacy</a></p>
|
|
59
|
+
</details>
|
|
60
|
+
</section>
|
|
61
|
+
</main>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
package/wizard/wizard.css
CHANGED
|
@@ -1 +1,78 @@
|
|
|
1
|
-
|
|
1
|
+
@font-face {
|
|
2
|
+
font-family: 'Plus Jakarta Sans';
|
|
3
|
+
src: url('/fonts/plus-jakarta-sans.ttf') format('truetype');
|
|
4
|
+
font-weight: 200 800;
|
|
5
|
+
font-display: swap;
|
|
6
|
+
}
|
|
7
|
+
@font-face {
|
|
8
|
+
font-family: 'IBM Plex Mono';
|
|
9
|
+
src: url('/fonts/ibm-plex-mono.ttf') format('truetype');
|
|
10
|
+
font-weight: 400;
|
|
11
|
+
font-display: swap;
|
|
12
|
+
}
|
|
13
|
+
:root {
|
|
14
|
+
color-scheme: light;
|
|
15
|
+
--app-canvas: #fcfaf8;
|
|
16
|
+
--app-panel: #ffffff;
|
|
17
|
+
--app-subtle: #fff1e8;
|
|
18
|
+
--app-border: #e7dcd3;
|
|
19
|
+
--app-text: #362b25;
|
|
20
|
+
--app-muted: #716256;
|
|
21
|
+
--app-accent: #bc4617;
|
|
22
|
+
--app-accent-text: #ffffff;
|
|
23
|
+
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
24
|
+
font-size: 14px;
|
|
25
|
+
line-height: 1.5;
|
|
26
|
+
letter-spacing: -.015em;
|
|
27
|
+
color: var(--app-text);
|
|
28
|
+
background: var(--app-canvas);
|
|
29
|
+
-webkit-font-smoothing: antialiased;
|
|
30
|
+
}
|
|
31
|
+
* { box-sizing: border-box; }
|
|
32
|
+
body { min-width: 320px; margin: 0; padding: 32px 20px; }
|
|
33
|
+
main { max-width: 640px; margin: auto; border: 1px solid var(--app-border); border-radius: 6px; background: var(--app-panel); }
|
|
34
|
+
header { display: flex; align-items: center; gap: 10px; padding: 18px 28px; border-bottom: 1px solid var(--app-border); font-weight: 650; }
|
|
35
|
+
header img { width: 28px; height: 28px; }
|
|
36
|
+
header small { margin-left: auto; color: var(--app-muted); font: 11px 'IBM Plex Mono', monospace; text-transform: uppercase; letter-spacing: .04em; }
|
|
37
|
+
section { padding: 28px; }
|
|
38
|
+
h1 { margin: 0 0 10px; font-size: 26px; line-height: 1.2; font-weight: 550; letter-spacing: -.035em; }
|
|
39
|
+
h2 { font-size: 18px; font-weight: 550; }
|
|
40
|
+
p { margin: 8px 0 14px; }
|
|
41
|
+
.intro, .note, .example { color: var(--app-muted); }
|
|
42
|
+
.intro { font-size: 14px; margin-bottom: 24px; }
|
|
43
|
+
.note, .example, details { font-size: 12px; line-height: 1.6; }
|
|
44
|
+
fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
|
|
45
|
+
legend, .label { font-size: 14px; font-weight: 550; }
|
|
46
|
+
.label { display: block; margin: 18px 0 8px; }
|
|
47
|
+
select, input[type=password], input[type=number], textarea { width: 100%; min-height: 44px; padding: 10px 12px; border: 1px solid var(--app-border); border-radius: 6px; background: var(--app-panel); color: var(--app-text); font: inherit; }
|
|
48
|
+
.option { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--app-border); }
|
|
49
|
+
.option legend { float: left; width: 100%; margin-bottom: 4px; }
|
|
50
|
+
.option label { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; margin-right: 20px; }
|
|
51
|
+
input[type=radio] { width: 16px; height: 16px; accent-color: var(--app-accent); }
|
|
52
|
+
.example { margin: 4px 0 0; }
|
|
53
|
+
button { min-height: 44px; margin-top: 18px; padding: 10px 16px; border: 1px solid transparent; border-radius: 6px; background: var(--app-accent); color: var(--app-accent-text); font: inherit; font-size: 13px; font-weight: 550; cursor: pointer; }
|
|
54
|
+
button:hover { filter: brightness(.95); }
|
|
55
|
+
button:disabled { opacity: .6; cursor: wait; }
|
|
56
|
+
.secondary { border-color: var(--app-border); background: transparent; color: var(--app-text); }
|
|
57
|
+
.actions { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
58
|
+
a, summary { color: var(--app-accent); }
|
|
59
|
+
summary { min-height: 32px; padding: 6px 0; cursor: pointer; }
|
|
60
|
+
.privacy { margin-top: 24px; padding-top: 14px; border-top: 1px solid var(--app-border); }
|
|
61
|
+
dl { display: grid; gap: 12px; }
|
|
62
|
+
dl div { display: grid; grid-template-columns: 130px minmax(0, 1fr); gap: 12px; }
|
|
63
|
+
dt { color: var(--app-muted); }
|
|
64
|
+
dd { margin: 0; overflow-wrap: anywhere; }
|
|
65
|
+
#message { margin: 14px 0 0; color: var(--app-accent); font-size: 12px; }
|
|
66
|
+
#message:empty { display: none; }
|
|
67
|
+
textarea { min-height: 120px; margin-top: 14px; font: 12px/1.6 'IBM Plex Mono', monospace; }
|
|
68
|
+
:focus-visible { outline: 2px solid var(--app-accent); outline-offset: 3px; }
|
|
69
|
+
[hidden] { display: none !important; }
|
|
70
|
+
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
71
|
+
@media (max-width: 480px) {
|
|
72
|
+
body { padding: 16px 12px; }
|
|
73
|
+
section { padding: 22px 18px; }
|
|
74
|
+
header { padding: 16px 18px; }
|
|
75
|
+
h1 { font-size: 24px; }
|
|
76
|
+
dl div { grid-template-columns: 100px minmax(0, 1fr); }
|
|
77
|
+
input[type=password], input[type=number], select { font-size: 16px; }
|
|
78
|
+
}
|
package/wizard/wizard.js
CHANGED
|
@@ -11,14 +11,15 @@
|
|
|
11
11
|
function settings(value){
|
|
12
12
|
el('history').value=['0','15','30'].includes(String(value.historyDays))?String(value.historyDays):'custom';el('custom').value=value.historyDays||7;el('custom-label').hidden=el('history').value!=='custom';
|
|
13
13
|
document.querySelector(`input[name=growth][value=${value.growthFeedback?'on':'off'}]`).checked=true;document.querySelector(`input[name=alignment][value=${value.decisionAlignment?'on':'off'}]`).checked=true;
|
|
14
|
-
el('key-entry').hidden=value.configured;el('saved-key').hidden=!value.configured;el('harness').textContent=names[value.harness]||value.harness;
|
|
14
|
+
el('key-entry').hidden=value.configured;el('saved-key').hidden=!value.configured;el('harness').textContent=names[value.harness]||value.harness;
|
|
15
15
|
}
|
|
16
16
|
function renderProgress(){
|
|
17
|
-
el('settings').hidden=true;el('review').hidden=true;el('progress').hidden=false;
|
|
18
|
-
const labels={pending:'
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
el('welcome').hidden=true;el('settings').hidden=true;el('review').hidden=true;el('progress').hidden=false;
|
|
18
|
+
const labels={pending:'Ready to sync your recent work',running:'Syncing your recent work',partial:'Some work is saved; sync will continue',blocked:'Some older work needs your attention',failed:'Sync needs a retry',complete:'Your selected history is saved',skipped:'Ready for new work',empty:'No recent work found in this time range'};
|
|
19
|
+
const connections={pending:'Connecting',verified:'Connected',reload_required:'Connected · start a new chat',failed:'Reconnect required'};
|
|
20
|
+
el('progress-title').textContent=state.contextAvailable?'Shifu is ready to help.':labels[state.backfillStatus]||'Setup is continuing.';
|
|
21
|
+
facts('progress-facts',[['Assistant',names[state.harness]||state.harness],['Connection',connections[state.connectionStatus]||'Checking connection'],['History',labels[state.backfillStatus]||'Checking your recent work']]);
|
|
22
|
+
el('progress-note').textContent=state.lastErrorCode?'Work already saved is kept. Try again, or ask your assistant to help reconnect Shifu.':state.backfillStatus==='skipped'?'Your next captured work can become context for future questions. No past work was imported.':'You can close this page and keep working. Sync continues in the background and resumes without repeating saved work.';
|
|
22
23
|
el('resume').hidden=!['failed','partial','blocked','pending'].includes(state.backfillStatus);el('copy').hidden=!state.contextAvailable;el('handoff').hidden=!state.contextAvailable;
|
|
23
24
|
el('prompt').value=state.resumePrompt||'';el('change').hidden=state.backfillStatus==='running';
|
|
24
25
|
}
|
|
@@ -27,21 +28,21 @@
|
|
|
27
28
|
event.preventDefault();const days=el('history').value==='custom'?Number(el('custom').value):Number(el('history').value);
|
|
28
29
|
if(!Number.isSafeInteger(days)||days<0||days>36500||(el('history').value==='custom'&&days===0)){el('message').textContent='Choose a positive whole number of custom days, or Start from now.';return;}
|
|
29
30
|
choices={historyDays:days,growthFeedback:document.querySelector('input[name=growth]:checked').value==='on',decisionAlignment:document.querySelector('input[name=alignment]:checked').value==='on'};
|
|
30
|
-
facts('summary',[['History',days?`Last ${days} days`:'Start from now'],['
|
|
31
|
-
el('scope').textContent=days?`
|
|
32
|
-
el('apply').textContent=days?'Connect and
|
|
31
|
+
facts('summary',[['History',days?`Last ${days} days`:'Start from now'],['Feedback',choices.growthFeedback?'On':'Off'],['Decision reminders',choices.decisionAlignment?'On':'Off']]);
|
|
32
|
+
el('scope').textContent=days?`Include the last ${days} days of work. Preparing this history uses your coding tool’s model account; cost depends on the amount of work.`:'Connect your assistant and start with new work. Past work will not be imported.';
|
|
33
|
+
el('apply').textContent=days?'Connect and sync':'Connect Shifu';el('welcome').hidden=true;el('settings').hidden=true;el('review').hidden=false;el('message').textContent='';
|
|
33
34
|
});
|
|
34
|
-
el('edit').addEventListener('click',()=>{el('review').hidden=true;el('settings').hidden=false;});
|
|
35
|
+
el('edit').addEventListener('click',()=>{el('welcome').hidden=false;el('review').hidden=true;el('settings').hidden=false;});
|
|
35
36
|
el('apply').addEventListener('click',async()=>{
|
|
36
37
|
if(busy)return;busy=true;el('apply').disabled=true;el('message').textContent='Verifying your connection and configuring Shifu…';
|
|
37
38
|
const key=el('key').value.trim();el('key').value='';
|
|
38
39
|
try{state=await api('apply',{...choices,...(!state.configured?{key}:{})});renderProgress();el('message').textContent='';}
|
|
39
|
-
catch(error){el('message').textContent=error.message;el('review').hidden=true;el('settings').hidden=false;}
|
|
40
|
+
catch(error){el('message').textContent=error.message;el('welcome').hidden=false;el('review').hidden=true;el('settings').hidden=false;}
|
|
40
41
|
finally{busy=false;el('apply').disabled=false;}
|
|
41
42
|
});
|
|
42
|
-
el('change').addEventListener('click',async()=>{try{const abandon=!['complete','skipped','empty'].includes(state.backfillStatus);if(abandon&&!window.confirm('
|
|
43
|
-
el('resume').addEventListener('click',async()=>{try{state=await api('resume',{});renderProgress();el('message').textContent='
|
|
44
|
-
el('copy').addEventListener('click',async()=>{try{await navigator.clipboard.writeText(state.resumePrompt);el('message').textContent='
|
|
43
|
+
el('change').addEventListener('click',async()=>{try{const abandon=!['complete','skipped','empty'].includes(state.backfillStatus);if(abandon&&!window.confirm('Change your history selection? Work already saved will stay in Shifu. This stops the current selection; no older work will be added automatically.'))return;state=await api('new',{abandon});settings(state);el('welcome').hidden=false;el('progress').hidden=true;el('settings').hidden=false;}catch(error){el('message').textContent=error.message;}});
|
|
44
|
+
el('resume').addEventListener('click',async()=>{try{state=await api('resume',{});renderProgress();el('message').textContent='Sync is continuing. Work already saved will not be sent again.';}catch(error){el('message').textContent=error.message;}});
|
|
45
|
+
el('copy').addEventListener('click',async()=>{try{await navigator.clipboard.writeText(state.resumePrompt);el('message').textContent='Question copied. Return to your assistant and paste it there. Start a new chat if Shifu is not available yet.';}catch{el('prompt').hidden=false;el('prompt').focus();el('prompt').select();el('message').textContent='Copy the question below and paste it into your assistant.';}});
|
|
45
46
|
async function refresh(){if(busy||document.hidden)return;try{state=await api('status');if(state.authorized)renderProgress();}catch(error){el('message').textContent=error.message;}}
|
|
46
47
|
api('status').then(value=>{state=value;settings(value);if(value.authorized)renderProgress();}).catch(error=>{el('settings').hidden=true;el('message').textContent=error.message;});
|
|
47
48
|
setInterval(refresh,5000);document.addEventListener('visibilitychange',()=>{if(!document.hidden)refresh();});
|