@tiagojct/subsub 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/web/i18n.js CHANGED
@@ -3,7 +3,6 @@
3
3
 
4
4
  export const STRINGS = {
5
5
  en: {
6
- profile: "Profile",
7
6
  model: "Model",
8
7
  noModel: "Connect a model",
9
8
  noModelSend: "No model is connected yet. Choose a provider and save its key; your message is still in the box.",
@@ -20,7 +19,7 @@ export const STRINGS = {
20
19
  close: "Close",
21
20
  apply: "Apply {x}?",
22
21
  run: "Run {x}?",
23
- undo: "Undo this change?",
22
+ undoAsk: "Undo this change?",
24
23
  write: "Allow writing to {x}?",
25
24
  starbuckReport: "Write the Starbuck report to {x}?",
26
25
  approveHint: "Nothing changes until you select Yes.",
@@ -31,9 +30,7 @@ export const STRINGS = {
31
30
  toReviewHint: "Items that Sub-Sub tagged and that wait for your check (they carry the _agent tag). Select Library overview, or ask Sub-Sub to list them.",
32
31
  serversDown: "Not running: {x}",
33
32
  working: "Working",
34
- using: "Using: {x}",
35
33
  failed: "failed",
36
- result: "Result",
37
34
  open: "Open",
38
35
  stopped: "Stopped.",
39
36
  agentExit: "Sub-Sub stopped.",
@@ -91,7 +88,6 @@ export const STRINGS = {
91
88
  quitDone: "Sub-Sub is closed. You can close this tab.",
92
89
  earlier: "Earlier messages were summarised.",
93
90
  menu: "Menu",
94
- commands: "Commands",
95
91
  error: "Error",
96
92
  theme: "Theme",
97
93
  themeAuto: "System",
@@ -99,7 +95,6 @@ export const STRINGS = {
99
95
  themeDark: "Dark",
100
96
  themeToggle: "Toggle theme",
101
97
  layoutWide: "Wide",
102
- layoutFocus: "Focus",
103
98
  toggleWidth: "Toggle wide layout",
104
99
  copy: "Copy",
105
100
  copied: "Copied",
@@ -107,6 +102,26 @@ export const STRINGS = {
107
102
  searchConversations: "Search conversations",
108
103
  export: "Export",
109
104
  exportTitle: "Export conversation",
105
+ exportHeading: "Sub-Sub conversation",
106
+ exportUser: "You",
107
+ exportMode: "Mode",
108
+ notes: "Notes",
109
+ notesHint: "The notes in your Sub-Sub folder",
110
+ allNotes: "All notes",
111
+ properties: "Properties",
112
+ downloadCsv: "Download CSV",
113
+ filterNotes: "Filter notes",
114
+ noNotes: "No notes yet.",
115
+ openInApp: "Open in app",
116
+ librarySearch: "Search your library",
117
+ searching: "Searching...",
118
+ noItems: "Nothing found.",
119
+ readItem: "Read",
120
+ noteItem: "Note",
121
+ rename: "Rename",
122
+ renamePrompt: "A new name for this conversation",
123
+ trash: "Delete",
124
+ trashConfirm: "Move this conversation to the trash folder (~/.subsub/trash)? You can move it back from there.",
110
125
  confirmShortcutHint: "Y to approve, N to decline",
111
126
  starbuck: "Reference checks (Starbuck)",
112
127
  starbuckHint: "Checks that cited works exist, match their citation and were not retracted. Sub-Sub restarts when you change this.",
@@ -128,7 +143,6 @@ export const STRINGS = {
128
143
  groupLibrary: "Library",
129
144
  },
130
145
  pt: {
131
- profile: "Perfil",
132
146
  model: "Modelo",
133
147
  noModel: "Ligar um modelo",
134
148
  noModelSend: "Ainda não há um modelo ligado. Escolha um fornecedor e guarde a chave; a sua mensagem continua na caixa.",
@@ -145,7 +159,7 @@ export const STRINGS = {
145
159
  close: "Fechar",
146
160
  apply: "Confirmar: {x}?",
147
161
  run: "Executar {x}?",
148
- undo: "Desfazer esta alteração?",
162
+ undoAsk: "Desfazer esta alteração?",
149
163
  write: "Permitir escrever em {x}?",
150
164
  starbuckReport: "Escrever o relatório do Starbuck em {x}?",
151
165
  approveHint: "Nada muda até selecionar Sim.",
@@ -156,9 +170,7 @@ export const STRINGS = {
156
170
  toReviewHint: "Itens que o Sub-Sub etiquetou e que esperam a sua verificação (têm a etiqueta _agent). Selecione Resumo da biblioteca, ou peça ao Sub-Sub para os listar.",
157
171
  serversDown: "Parados: {x}",
158
172
  working: "A trabalhar",
159
- using: "A usar: {x}",
160
173
  failed: "falhou",
161
- result: "Resultado",
162
174
  open: "Abrir",
163
175
  stopped: "Parado.",
164
176
  agentExit: "O Sub-Sub parou.",
@@ -216,7 +228,6 @@ export const STRINGS = {
216
228
  quitDone: "O Sub-Sub está fechado. Pode fechar este separador.",
217
229
  earlier: "As mensagens anteriores foram resumidas.",
218
230
  menu: "Menu",
219
- commands: "Comandos",
220
231
  error: "Erro",
221
232
  theme: "Tema",
222
233
  themeAuto: "Sistema",
@@ -224,7 +235,6 @@ export const STRINGS = {
224
235
  themeDark: "Escuro",
225
236
  themeToggle: "Mudar tema",
226
237
  layoutWide: "Largo",
227
- layoutFocus: "Foco",
228
238
  toggleWidth: "Mudar largura do ecrã",
229
239
  copy: "Copiar",
230
240
  copied: "Copiado",
@@ -232,6 +242,26 @@ export const STRINGS = {
232
242
  searchConversations: "Procurar conversas",
233
243
  export: "Exportar",
234
244
  exportTitle: "Exportar conversa",
245
+ exportHeading: "Conversa do Sub-Sub",
246
+ exportUser: "Utilizador",
247
+ exportMode: "Modo",
248
+ notes: "Notas",
249
+ notesHint: "As notas da sua pasta do Sub-Sub",
250
+ allNotes: "Todas as notas",
251
+ properties: "Propriedades",
252
+ downloadCsv: "Transferir CSV",
253
+ filterNotes: "Filtrar notas",
254
+ noNotes: "Ainda não há notas.",
255
+ openInApp: "Abrir na aplicação",
256
+ librarySearch: "Pesquisar na biblioteca",
257
+ searching: "A pesquisar...",
258
+ noItems: "Nada encontrado.",
259
+ readItem: "Ler",
260
+ noteItem: "Nota",
261
+ rename: "Mudar o nome",
262
+ renamePrompt: "Um novo nome para esta conversa",
263
+ trash: "Apagar",
264
+ trashConfirm: "Mover esta conversa para a pasta do lixo (~/.subsub/trash)? Pode trazê-la de volta a partir daí.",
235
265
  confirmShortcutHint: "S para aprovar, N para recusar",
236
266
  starbuck: "Verificação de referências (Starbuck)",
237
267
  starbuckHint: "Verifica se as obras citadas existem, correspondem à citação e não foram retratadas. O Sub-Sub reinicia quando muda esta opção.",
@@ -255,23 +285,26 @@ export const STRINGS = {
255
285
  };
256
286
 
257
287
  /**
258
- * Buttons in the side panel, in two groups. `fill` puts the command in the message box; `send` runs it at once.
259
- * The Librarian sees the Library group only. `profiles` and `needs` limit a button to some profiles or add-ons.
288
+ * Buttons in the side panel, in two groups. A button puts the command in the message box; `send` runs it at once;
289
+ * `item` opens the library search, whose results have their own buttons.
290
+ * The Librarian sees the Library group only. `needs` limits a button to an add-on.
260
291
  */
261
292
  export const ACTIONS = {
262
293
  research: [
263
- { cmd: "lit", fill: true, en: "Literature review", pt: "Revisão da literatura" },
264
- { cmd: "compare", fill: true, profiles: ["scholar", "author", "editor"], en: "Compare sources", pt: "Comparar fontes" },
265
- { cmd: "lit-note", fill: true, en: "Literature note", pt: "Nota de leitura" },
266
- { cmd: "synthesis", fill: true, en: "Synthesis", pt: "Síntese" },
267
- { cmd: "gaps", fill: true, en: "What is missing", pt: "O que falta" },
268
- { cmd: "manuscript", fill: true, en: "Check a manuscript", pt: "Verificar um manuscrito" },
269
- { cmd: "verify", fill: true, needs: "starbuck", en: "Check references", pt: "Verificar referências" },
270
- { cmd: "review", fill: true, profiles: ["author", "editor"], en: "Review a manuscript", pt: "Rever um manuscrito" },
294
+ { cmd: "read", send: true, en: "Read a paper", pt: "Ler um artigo" },
295
+ { cmd: "lit", en: "Literature review", pt: "Revisão da literatura" },
296
+ { cmd: "lit reading list:", en: "Reading list", pt: "Lista de leitura" },
297
+ { cmd: "compare", en: "Compare sources", pt: "Comparar fontes" },
298
+ { cmd: "lit-note", item: true, en: "Literature note", pt: "Nota de literatura" },
299
+ { cmd: "synthesis", en: "Synthesis", pt: "Síntese" },
300
+ { cmd: "gaps", en: "What is missing", pt: "O que falta" },
301
+ { cmd: "manuscript", en: "Check a manuscript", pt: "Verificar um manuscrito" },
302
+ { cmd: "verify", needs: "starbuck", en: "Check references", pt: "Verificar referências" },
303
+ { cmd: "review", en: "Review a manuscript", pt: "Rever um manuscrito" },
271
304
  { cmd: "alert", send: true, en: "New publications", pt: "Novas publicações" },
272
- { cmd: "request-copy", fill: true, en: "Ask an author for a copy", pt: "Pedir uma cópia ao autor" },
273
- { cmd: "ai-statement", fill: true, en: "Statement on AI use", pt: "Declaração de uso de IA" },
274
- { cmd: "digest", send: true, profiles: ["scholar", "author", "editor"], en: "Digest of the week", pt: "Resumo da semana" },
305
+ { cmd: "request-copy", en: "Ask an author for a copy", pt: "Pedir uma cópia ao autor" },
306
+ { cmd: "ai-statement", en: "Statement on AI use", pt: "Declaração de uso de IA" },
307
+ { cmd: "digest", send: true, en: "Digest of the week", pt: "Resumo da semana" },
275
308
  ],
276
309
  library: [
277
310
  { cmd: "subsub", send: true, en: "Library overview", pt: "Resumo da biblioteca" },
@@ -280,6 +313,7 @@ export const ACTIONS = {
280
313
  { cmd: "import-queue", send: true, en: "Import the queue", pt: "Importar a fila" },
281
314
  { cmd: "history", send: true, en: "Recent changes", pt: "Alterações recentes" },
282
315
  { cmd: "undo", send: true, en: "Undo the last change", pt: "Desfazer a última alteração" },
316
+ { cmd: "feedback", en: "Report a problem", pt: "Comunicar um problema" },
283
317
  ],
284
318
  };
285
319
 
@@ -301,6 +335,7 @@ export const SUGGESTIONS = {
301
335
  export const TOOLS = {
302
336
  en: {
303
337
  find_items: "search the library",
338
+ get_figure: "cut a figure out of the PDF",
304
339
  standalone_items: "list files without a parent item",
305
340
  set_parent_items: "give files a parent item",
306
341
  find_reference: "look up a reference",
@@ -361,6 +396,7 @@ export const TOOLS = {
361
396
  },
362
397
  pt: {
363
398
  find_items: "pesquisar na biblioteca",
399
+ get_figure: "recortar uma figura do PDF",
364
400
  standalone_items: "listar ficheiros sem item principal",
365
401
  set_parent_items: "dar um item principal aos ficheiros",
366
402
  find_reference: "procurar uma referência",
package/web/index.html CHANGED
@@ -21,14 +21,7 @@
21
21
  </div>
22
22
  <div class="status">
23
23
  <span class="library" id="library"><span class="library-dot" aria-hidden="true"></span><span class="library-text"></span></span>
24
- <label class="profile wide-only"><span class="visually-hidden" data-i18n="profile">Profile</span>
25
- <select id="profile">
26
- <option value="reader">Reader</option>
27
- <option value="scholar">Scholar</option>
28
- <option value="author">Author</option>
29
- <option value="editor">Editor</option>
30
- </select>
31
- </label>
24
+ <button type="button" class="chip-btn" id="notes-btn" aria-controls="note-pane" aria-expanded="false" data-i18n="notes" data-i18n-title="notesHint">Notes</button>
32
25
  <button type="button" class="chip-btn model-btn wide-only" id="model-btn"></button>
33
26
  <button type="button" class="icon-btn wide-only" id="width-btn" aria-pressed="false"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h16M4 12l3.5-3.5M4 12l3.5 3.5M20 12l-3.5-3.5M20 12l-3.5 3.5"/></svg><span class="visually-hidden" id="width-btn-text">Wide</span></button>
34
27
  <button type="button" class="icon-btn" id="theme-btn"></button>
@@ -38,14 +31,6 @@
38
31
  <aside class="side" id="side">
39
32
  <button type="button" class="new-btn" id="new-btn"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg><span data-i18n="newConversation">New conversation</span></button>
40
33
  <section class="phone-only settings">
41
- <label><span data-i18n="profile">Profile</span>
42
- <select id="profile-side">
43
- <option value="reader">Reader</option>
44
- <option value="scholar">Scholar</option>
45
- <option value="author">Author</option>
46
- <option value="editor">Editor</option>
47
- </select>
48
- </label>
49
34
  <label><span data-i18n="model">Model</span>
50
35
  <button type="button" class="secondary model-btn" id="model-btn-side"></button>
51
36
  </label>
@@ -53,6 +38,11 @@
53
38
  <button type="button" class="secondary" id="theme-btn-side"></button>
54
39
  </label>
55
40
  </section>
41
+ <section>
42
+ <h2 data-i18n="groupLibrary">Library</h2>
43
+ <input type="search" id="library-search" class="search" data-i18n-placeholder="librarySearch" placeholder="Search your library" autocomplete="off">
44
+ <ul class="items" id="items" aria-live="polite"></ul>
45
+ </section>
56
46
  <section>
57
47
  <h2 data-i18n="actions">Actions</h2>
58
48
  <div class="actions" id="actions"></div>
@@ -91,6 +81,17 @@
91
81
  </div>
92
82
  </form>
93
83
  </main>
84
+
85
+ <aside class="note-pane" id="note-pane" hidden>
86
+ <div class="note-head">
87
+ <button type="button" class="link-btn" id="note-back" data-i18n="allNotes">All notes</button>
88
+ <span class="note-name" id="note-name"></span>
89
+ <button type="button" class="link-btn" id="note-app" data-i18n="openInApp" hidden>Open in app</button>
90
+ <button type="button" class="link-btn" id="note-close" data-i18n="close">Close</button>
91
+ </div>
92
+ <input type="search" id="note-filter" class="search" data-i18n-placeholder="filterNotes" placeholder="Filter notes" autocomplete="off">
93
+ <div class="note-body" id="note-body"></div>
94
+ </aside>
94
95
  </div>
95
96
 
96
97
  <dialog id="dialog" class="modal">
package/dist/profiles.js DELETED
@@ -1,96 +0,0 @@
1
- /**
2
- * Profiles: how much Sub-Sub does for the user. Named after the work, not the
3
- * person: a professor new to a field can choose Reader, a student can choose
4
- * Scholar. A profile is the user's own choice (/profile changes it), not a lock.
5
- *
6
- * - reader: explains each step; reading notes are quotes with page numbers and
7
- * questions; no summaries or syntheses; /lit gives a reading list;
8
- * no bulk library changes.
9
- * - scholar: literature notes, syntheses, searches, alerts; all library changes.
10
- * - author: scholar plus manuscript work: citation check, bibliography, comments
11
- * on the argument. Does not write the manuscript's paragraphs.
12
- * - editor: everything, including drafting text for a manuscript on request.
13
- */
14
- const BULK_LIBRARY = ["rename_tags", "remove_tags", "remove_automatic_tags", "trash_items", "update_fields", "repair_metadata"].map((n) => `zotero_${n}`);
15
- const MANUSCRIPT = ["scholar_check_manuscript", "scholar_export_bibliography"];
16
- export const PROFILE_SPECS = {
17
- reader: {
18
- name: "reader",
19
- label: "Reader",
20
- summary: "explains each step; reading notes are quotes and questions; /lit gives a reading list; no summaries; no bulk library changes",
21
- without: [...BULK_LIBRARY, ...MANUSCRIPT],
22
- withoutPrompts: ["compare", "review", "digest"],
23
- batch: 10,
24
- rules: [
25
- "Profile: Reader. {{User}} is learning to work with the literature and wants to do the reading and the writing.",
26
- "- Before each step, say in one or two lines what you will do and why.",
27
- "- A literature note is a reading note: the full reference, then direct quotes with page numbers (or section names), and under each quote one question for {{user}} to answer. Do not write summaries, syntheses, conclusions or paragraphs that {{user}} could hand in as their own text. When a prompt or the shared rules ask for a literature note or a synthesis, make a reading note instead and say so.",
28
- "- For searches, list works with one line each on what they study, not on what they conclude.",
29
- "- /lit gives a reading list, not a review: plan, search, screen and read as the prompt says, then write the note in the Reader form that the prompt gives (no answer, no agreement or disagreement, no conclusions).",
30
- "- If {{user}} asks for a summary, a synthesis or text for an assignment, say that the Reader profile does not write these, and offer quotes and questions. {{User}} can change the profile with /profile.",
31
- "- Tag review notes: at most 10 items. Explain each proposed tag in the Reason column.",
32
- ].join("\n"),
33
- },
34
- scholar: {
35
- name: "scholar",
36
- label: "Scholar",
37
- summary: "literature notes, syntheses, searches and alerts; all library changes",
38
- without: MANUSCRIPT,
39
- withoutPrompts: ["review"],
40
- batch: 25,
41
- rules: [
42
- "Profile: Scholar.",
43
- "- Write literature notes and syntheses from what you read, with a source for every claim.",
44
- "- Do not draft text for {{user}}'s manuscripts, theses or assignments. If asked, say that the Author and Editor profiles help with manuscripts (/profile).",
45
- "- Tag review notes: 25 items.",
46
- ].join("\n"),
47
- },
48
- author: {
49
- name: "author",
50
- label: "Author",
51
- summary: "Scholar plus manuscripts: citation check, bibliography, comments on the argument",
52
- without: [],
53
- withoutPrompts: [],
54
- batch: 25,
55
- rules: [
56
- "Profile: Author.",
57
- "- Write literature notes and syntheses from what you read, with a source for every claim.",
58
- "- Help with {{user}}'s manuscripts: check citations against the library, export the bibliography, and comment on the argument, the structure and the evidence that is missing, with the sources that could support each point.",
59
- "- Do not write the manuscript's paragraphs. Say what is missing and where the support is; {{user}} writes the text. The Editor profile drafts text (/profile).",
60
- "- Tag review notes: 25 items.",
61
- ].join("\n"),
62
- },
63
- editor: {
64
- name: "editor",
65
- label: "Editor",
66
- summary: "everything, including drafting manuscript text on request",
67
- without: [],
68
- withoutPrompts: [],
69
- batch: 25,
70
- rules: [
71
- "Profile: Editor. All tools.",
72
- "- Write literature notes and syntheses from what you read, with a source for every claim.",
73
- "- When {{user}} asks, draft text for a manuscript: cite library items as [@citekey], use only claims from sources you read, and mark drafted text clearly so that {{user}} can check it.",
74
- "- Tag review notes: 25 items unless {{user}} asks for more.",
75
- ].join("\n"),
76
- },
77
- };
78
- export function profileSpec(p) {
79
- return PROFILE_SPECS[p];
80
- }
81
- /** Why a prompt command does not run in this profile, or undefined when it does. */
82
- export function promptBlocked(text, profile) {
83
- const m = /^\/([\w-]+)(?:\s|$)/.exec(text.trim());
84
- if (!m || !PROFILE_SPECS[profile].withoutPrompts.includes(m[1]))
85
- return undefined;
86
- const allowed = Object.values(PROFILE_SPECS)
87
- .filter((s) => !s.withoutPrompts.includes(m[1]))
88
- .map((s) => s.label)
89
- .join(", ");
90
- return `/${m[1]} is not part of the ${PROFILE_SPECS[profile].label} profile (it is in: ${allowed}). Change the profile with /profile.`;
91
- }
92
- export function profileList(current) {
93
- return Object.values(PROFILE_SPECS)
94
- .map((s) => `${s.name === current ? "*" : " "} ${s.label.padEnd(8)} ${s.summary}`)
95
- .join("\n");
96
- }
package/src/profiles.ts DELETED
@@ -1,118 +0,0 @@
1
- /**
2
- * Profiles: how much Sub-Sub does for the user. Named after the work, not the
3
- * person: a professor new to a field can choose Reader, a student can choose
4
- * Scholar. A profile is the user's own choice (/profile changes it), not a lock.
5
- *
6
- * - reader: explains each step; reading notes are quotes with page numbers and
7
- * questions; no summaries or syntheses; /lit gives a reading list;
8
- * no bulk library changes.
9
- * - scholar: literature notes, syntheses, searches, alerts; all library changes.
10
- * - author: scholar plus manuscript work: citation check, bibliography, comments
11
- * on the argument. Does not write the manuscript's paragraphs.
12
- * - editor: everything, including drafting text for a manuscript on request.
13
- */
14
-
15
- import type { Profile } from "./config.ts";
16
-
17
- export interface ProfileSpec {
18
- name: Profile;
19
- label: string;
20
- summary: string;
21
- /** Tools this profile does not offer (full names). */
22
- without: string[];
23
- /** Prompt commands this profile does not run (names without the slash). */
24
- withoutPrompts: string[];
25
- /** Items per review note and per batch. */
26
- batch: number;
27
- /** Added to the system prompt. {{user}} is the user's name or "the user". */
28
- rules: string;
29
- }
30
-
31
- const BULK_LIBRARY = ["rename_tags", "remove_tags", "remove_automatic_tags", "trash_items", "update_fields", "repair_metadata"].map(
32
- (n) => `zotero_${n}`,
33
- );
34
- const MANUSCRIPT = ["scholar_check_manuscript", "scholar_export_bibliography"];
35
-
36
- export const PROFILE_SPECS: Record<Profile, ProfileSpec> = {
37
- reader: {
38
- name: "reader",
39
- label: "Reader",
40
- summary: "explains each step; reading notes are quotes and questions; /lit gives a reading list; no summaries; no bulk library changes",
41
- without: [...BULK_LIBRARY, ...MANUSCRIPT],
42
- withoutPrompts: ["compare", "review", "digest"],
43
- batch: 10,
44
- rules: [
45
- "Profile: Reader. {{User}} is learning to work with the literature and wants to do the reading and the writing.",
46
- "- Before each step, say in one or two lines what you will do and why.",
47
- "- A literature note is a reading note: the full reference, then direct quotes with page numbers (or section names), and under each quote one question for {{user}} to answer. Do not write summaries, syntheses, conclusions or paragraphs that {{user}} could hand in as their own text. When a prompt or the shared rules ask for a literature note or a synthesis, make a reading note instead and say so.",
48
- "- For searches, list works with one line each on what they study, not on what they conclude.",
49
- "- /lit gives a reading list, not a review: plan, search, screen and read as the prompt says, then write the note in the Reader form that the prompt gives (no answer, no agreement or disagreement, no conclusions).",
50
- "- If {{user}} asks for a summary, a synthesis or text for an assignment, say that the Reader profile does not write these, and offer quotes and questions. {{User}} can change the profile with /profile.",
51
- "- Tag review notes: at most 10 items. Explain each proposed tag in the Reason column.",
52
- ].join("\n"),
53
- },
54
- scholar: {
55
- name: "scholar",
56
- label: "Scholar",
57
- summary: "literature notes, syntheses, searches and alerts; all library changes",
58
- without: MANUSCRIPT,
59
- withoutPrompts: ["review"],
60
- batch: 25,
61
- rules: [
62
- "Profile: Scholar.",
63
- "- Write literature notes and syntheses from what you read, with a source for every claim.",
64
- "- Do not draft text for {{user}}'s manuscripts, theses or assignments. If asked, say that the Author and Editor profiles help with manuscripts (/profile).",
65
- "- Tag review notes: 25 items.",
66
- ].join("\n"),
67
- },
68
- author: {
69
- name: "author",
70
- label: "Author",
71
- summary: "Scholar plus manuscripts: citation check, bibliography, comments on the argument",
72
- without: [],
73
- withoutPrompts: [],
74
- batch: 25,
75
- rules: [
76
- "Profile: Author.",
77
- "- Write literature notes and syntheses from what you read, with a source for every claim.",
78
- "- Help with {{user}}'s manuscripts: check citations against the library, export the bibliography, and comment on the argument, the structure and the evidence that is missing, with the sources that could support each point.",
79
- "- Do not write the manuscript's paragraphs. Say what is missing and where the support is; {{user}} writes the text. The Editor profile drafts text (/profile).",
80
- "- Tag review notes: 25 items.",
81
- ].join("\n"),
82
- },
83
- editor: {
84
- name: "editor",
85
- label: "Editor",
86
- summary: "everything, including drafting manuscript text on request",
87
- without: [],
88
- withoutPrompts: [],
89
- batch: 25,
90
- rules: [
91
- "Profile: Editor. All tools.",
92
- "- Write literature notes and syntheses from what you read, with a source for every claim.",
93
- "- When {{user}} asks, draft text for a manuscript: cite library items as [@citekey], use only claims from sources you read, and mark drafted text clearly so that {{user}} can check it.",
94
- "- Tag review notes: 25 items unless {{user}} asks for more.",
95
- ].join("\n"),
96
- },
97
- };
98
-
99
- export function profileSpec(p: Profile): ProfileSpec {
100
- return PROFILE_SPECS[p];
101
- }
102
-
103
- /** Why a prompt command does not run in this profile, or undefined when it does. */
104
- export function promptBlocked(text: string, profile: Profile): string | undefined {
105
- const m = /^\/([\w-]+)(?:\s|$)/.exec(text.trim());
106
- if (!m || !PROFILE_SPECS[profile].withoutPrompts.includes(m[1])) return undefined;
107
- const allowed = Object.values(PROFILE_SPECS)
108
- .filter((s) => !s.withoutPrompts.includes(m[1]))
109
- .map((s) => s.label)
110
- .join(", ");
111
- return `/${m[1]} is not part of the ${PROFILE_SPECS[profile].label} profile (it is in: ${allowed}). Change the profile with /profile.`;
112
- }
113
-
114
- export function profileList(current?: Profile): string {
115
- return Object.values(PROFILE_SPECS)
116
- .map((s) => `${s.name === current ? "*" : " "} ${s.label.padEnd(8)} ${s.summary}`)
117
- .join("\n");
118
- }