analyzthis_design 2.4.0 → 2.4.1

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/HOW-TO-USE.md CHANGED
@@ -313,21 +313,39 @@ Each persona reads the session state, so you do not need to repeat the task.
313
313
 
314
314
  ## 8. After the run — accept, reject, evolve
315
315
 
316
- ### Mark an output accepted
316
+ ### Designer path (remember this)
317
317
 
318
- ```bash
319
- npx analyzthis_design session accept --persona arjun
318
+ In chat, after `/zara` or any persona:
319
+
320
+ ```
321
+ /accept yes
322
+ ```
323
+
324
+ or
325
+
326
+ ```
327
+ /accept no — too much motion on the daily table
320
328
  ```
321
329
 
322
- ### Reject with correction
330
+ No terminal flags. `/accept` writes keep or skip for **local** evolution.
331
+
332
+ To send a correction to the published package:
333
+
334
+ ```
335
+ /share
336
+ ```
337
+
338
+ Then **yes** if the preview looks right. Redacted — no folder names, emails, or keys.
339
+
340
+ ### CLI (same thing, if you prefer the terminal)
323
341
 
324
342
  ```bash
325
- npx analyzthis_design session accept --persona arjun --reject \
326
- --comment "Invented tokens not in our DS" \
327
- --correction "Use --color-primary and spacing-4 from tokens.css" \
328
- --rating 2 --tags invented_tokens,missed_ds
343
+ npx analyzthis_design accept --keep --persona arjun
344
+ npx analyzthis_design accept --fix --persona arjun --because "Invented tokens use tokens.css"
329
345
  ```
330
346
 
347
+ The older `session accept` / `feedback record` flags still work.
348
+
331
349
  ### Make the team learn
332
350
 
333
351
  ```bash
package/README.md CHANGED
@@ -4,6 +4,12 @@ A set of AI design personas and a task-first evaluation framework that plugs int
4
4
 
5
5
  Install once. Run structured UX critiques, multi-phase ideation, and task-grounded screen reviews — directly inside your AI chat. **No external LLM API keys required** for CLI orchestrator runs: **`/devi`** voices each persona from your host IDE (Cursor, Claude, etc.).
6
6
 
7
+ ## v2.4.1 — `/accept` and `/share` (designer keep / skip / send)
8
+
9
+ After `/zara` (or any persona), type `/accept yes` or `/accept no` plus one sentence. No CLI flags. Writes keep or skip for **local** evolution. Slash-only notes still count.
10
+
11
+ To send a correction to the **published** package: `/share` (preview) then `/share yes`. Redacted. Uses the HTTP endpoint if configured; otherwise a GitHub issue on joshirishi/analyzthis_design.
12
+
7
13
  ## v2.4.0 — Claude-path efficiency (1 Sep 2026)
8
14
 
9
15
  Same SHIP / REVISE / BLOCK. Fewer tokens on Claude Code and Claude Desktop. Not Caveman-speak. Not a proxy.
@@ -48,7 +54,7 @@ Pass `--global` to opt into the legacy merged behavior (read `config.sources` an
48
54
 
49
55
  Use `npx analyzthis_design run-unchunked` for the legacy single-pass orchestrator.
50
56
 
51
- **npm:** [analyzthis_design](https://www.npmjs.com/package/analyzthis_design) · **Current version:** 2.4.0 · **Step-by-step guide:** [HOW-TO-USE.md](./HOW-TO-USE.md)
57
+ **npm:** [analyzthis_design](https://www.npmjs.com/package/analyzthis_design) · **Current version:** 2.4.1 · **Step-by-step guide:** [HOW-TO-USE.md](./HOW-TO-USE.md)
52
58
 
53
59
  ---
54
60
 
@@ -149,6 +155,9 @@ Invoke critique personas for targeted, already-grounded questions. For **wirefra
149
155
  | `/knowledge-bank` | Auto-populated from your connected vault (or from Kavi collect). All personas read this first. |
150
156
  | `/design-reference` | CSV reference data (colors, typography, UX guidelines, stacks, …) |
151
157
  | `/collect-knowledge` | Alias for `/kavi` |
158
+ | `/accept` | Keep or skip the last persona note (yes / no). Local evolution — no CLI flags. |
159
+ | `/share` | Send a correction to the package (preview, then yes). Redacted. |
160
+ | `/receipt` | Inferred token receipt for this project. Not a bill. |
152
161
 
153
162
  ---
154
163
 
@@ -742,6 +751,10 @@ npx analyzthis_design status
742
751
 
743
752
  # Session (agentic)
744
753
  npx analyzthis_design session init|show|reset [--project id] [--all]
754
+ npx analyzthis_design accept --keep [--persona zara] # designer keep (slash: /accept yes)
755
+ npx analyzthis_design accept --fix --because "one sentence" # designer skip (slash: /accept no)
756
+ npx analyzthis_design share # preview notes for the package
757
+ npx analyzthis_design share --send # send (slash: /share yes)
745
758
  npx analyzthis_design session accept --persona <id> [--reject] [--comment "..."] [--correction "..."] [--rating 1-5] [--tags a,b]
746
759
 
747
760
  # Persona feedback (v1.16)
@@ -313,21 +313,39 @@ Each persona reads the session state, so you do not need to repeat the task.
313
313
 
314
314
  ## 8. After the run — accept, reject, evolve
315
315
 
316
- ### Mark an output accepted
316
+ ### Designer path (remember this)
317
317
 
318
- ```bash
319
- npx analyzthis_design session accept --persona arjun
318
+ In chat, after `/zara` or any persona:
319
+
320
+ ```
321
+ /accept yes
322
+ ```
323
+
324
+ or
325
+
326
+ ```
327
+ /accept no — too much motion on the daily table
320
328
  ```
321
329
 
322
- ### Reject with correction
330
+ No terminal flags. `/accept` writes keep or skip for **local** evolution.
331
+
332
+ To send a correction to the published package:
333
+
334
+ ```
335
+ /share
336
+ ```
337
+
338
+ Then **yes** if the preview looks right. Redacted — no folder names, emails, or keys.
339
+
340
+ ### CLI (same thing, if you prefer the terminal)
323
341
 
324
342
  ```bash
325
- npx analyzthis_design session accept --persona arjun --reject \
326
- --comment "Invented tokens not in our DS" \
327
- --correction "Use --color-primary and spacing-4 from tokens.css" \
328
- --rating 2 --tags invented_tokens,missed_ds
343
+ npx analyzthis_design accept --keep --persona arjun
344
+ npx analyzthis_design accept --fix --persona arjun --because "Invented tokens use tokens.css"
329
345
  ```
330
346
 
347
+ The older `session accept` / `feedback record` flags still work.
348
+
331
349
  ### Make the team learn
332
350
 
333
351
  ```bash
package/dist/README.md CHANGED
@@ -4,6 +4,12 @@ A set of AI design personas and a task-first evaluation framework that plugs int
4
4
 
5
5
  Install once. Run structured UX critiques, multi-phase ideation, and task-grounded screen reviews — directly inside your AI chat. **No external LLM API keys required** for CLI orchestrator runs: **`/devi`** voices each persona from your host IDE (Cursor, Claude, etc.).
6
6
 
7
+ ## v2.4.1 — `/accept` and `/share` (designer keep / skip / send)
8
+
9
+ After `/zara` (or any persona), type `/accept yes` or `/accept no` plus one sentence. No CLI flags. Writes keep or skip for **local** evolution. Slash-only notes still count.
10
+
11
+ To send a correction to the **published** package: `/share` (preview) then `/share yes`. Redacted. Uses the HTTP endpoint if configured; otherwise a GitHub issue on joshirishi/analyzthis_design.
12
+
7
13
  ## v2.4.0 — Claude-path efficiency (1 Sep 2026)
8
14
 
9
15
  Same SHIP / REVISE / BLOCK. Fewer tokens on Claude Code and Claude Desktop. Not Caveman-speak. Not a proxy.
@@ -48,7 +54,7 @@ Pass `--global` to opt into the legacy merged behavior (read `config.sources` an
48
54
 
49
55
  Use `npx analyzthis_design run-unchunked` for the legacy single-pass orchestrator.
50
56
 
51
- **npm:** [analyzthis_design](https://www.npmjs.com/package/analyzthis_design) · **Current version:** 2.4.0 · **Step-by-step guide:** [HOW-TO-USE.md](./HOW-TO-USE.md)
57
+ **npm:** [analyzthis_design](https://www.npmjs.com/package/analyzthis_design) · **Current version:** 2.4.1 · **Step-by-step guide:** [HOW-TO-USE.md](./HOW-TO-USE.md)
52
58
 
53
59
  ---
54
60
 
@@ -149,6 +155,9 @@ Invoke critique personas for targeted, already-grounded questions. For **wirefra
149
155
  | `/knowledge-bank` | Auto-populated from your connected vault (or from Kavi collect). All personas read this first. |
150
156
  | `/design-reference` | CSV reference data (colors, typography, UX guidelines, stacks, …) |
151
157
  | `/collect-knowledge` | Alias for `/kavi` |
158
+ | `/accept` | Keep or skip the last persona note (yes / no). Local evolution — no CLI flags. |
159
+ | `/share` | Send a correction to the package (preview, then yes). Redacted. |
160
+ | `/receipt` | Inferred token receipt for this project. Not a bill. |
152
161
 
153
162
  ---
154
163
 
@@ -742,6 +751,10 @@ npx analyzthis_design status
742
751
 
743
752
  # Session (agentic)
744
753
  npx analyzthis_design session init|show|reset [--project id] [--all]
754
+ npx analyzthis_design accept --keep [--persona zara] # designer keep (slash: /accept yes)
755
+ npx analyzthis_design accept --fix --because "one sentence" # designer skip (slash: /accept no)
756
+ npx analyzthis_design share # preview notes for the package
757
+ npx analyzthis_design share --send # send (slash: /share yes)
745
758
  npx analyzthis_design session accept --persona <id> [--reject] [--comment "..."] [--correction "..."] [--rating 1-5] [--tags a,b]
746
759
 
747
760
  # Persona feedback (v1.16)
package/dist/bin/cli.js CHANGED
@@ -60,6 +60,10 @@ Usage:
60
60
  session accept Mark a persona's last output accepted, for training export
61
61
  (--persona <id>, optional --reject to unmark)
62
62
  Optional feedback: --comment, --correction, --rating 1-5, --tags a,b
63
+ accept Designer keep / skip (same as /accept). --keep or --fix
64
+ Optional: --persona, --because "one sentence", --liked (keep, not shipped)
65
+ share Designer send-to-package (same as /share). Preview, then --send
66
+ Optional: --because "one sentence", --persona
63
67
 
64
68
  ── Persona feedback commands ─────────────────────────────────
65
69
  feedback record Record unhappiness or a correction for a persona output
@@ -220,6 +224,10 @@ Usage:
220
224
  npx analyzthis_design devi respond --run ~/.analyzthis_design/runs/... --step 001-arjun --file out.md
221
225
  npx analyzthis_design run-unchunked --task "Review this screen" --provider anthropic
222
226
 
227
+ npx analyzthis_design accept --keep --persona zara
228
+ npx analyzthis_design accept --fix --persona zara --because "too much motion on the table"
229
+ npx analyzthis_design share
230
+ npx analyzthis_design share --send
223
231
  npx analyzthis_design session accept --persona arjun
224
232
  npx analyzthis_design session accept --persona arjun --reject \\
225
233
  --comment "Invented tokens not in our DS" \\
@@ -322,6 +330,11 @@ const budgetVal = getFlag('budget');
322
330
  const sequentialFlag = flags.includes('--sequential');
323
331
  const maxChunksVal = getFlag('max-chunks');
324
332
  const unchunkedFlag = flags.includes('--unchunked');
333
+ const keepFlag = flags.includes('--keep');
334
+ const fixFlag = flags.includes('--fix') || flags.includes('--no');
335
+ const likedFlag = flags.includes('--liked');
336
+ const becauseVal = getFlag('because');
337
+ const sendFlag = flags.includes('--send');
325
338
 
326
339
  // ─── Commands ────────────────────────────────────────────────────────────────
327
340
 
@@ -993,6 +1006,123 @@ switch (cmd) {
993
1006
  break;
994
1007
  }
995
1008
 
1009
+ case 'accept': {
1010
+ const accept = require('../lib/accept');
1011
+ const wantKeep = keepFlag || yesFlag;
1012
+ const wantFix = fixFlag;
1013
+ try {
1014
+ if (wantKeep && wantFix) {
1015
+ console.error('\n ✗ Use --keep or --fix, not both.\n');
1016
+ process.exit(1);
1017
+ }
1018
+ if (!wantKeep && !wantFix) {
1019
+ const peek = accept.preview({ project: projectVal });
1020
+ console.log('\n Keep the last note?');
1021
+ console.log(' Yes: npx analyzthis_design accept --keep' + (peek.persona ? ` --persona ${peek.persona}` : ''));
1022
+ console.log(' No: npx analyzthis_design accept --fix --because "one sentence"');
1023
+ if (peek.persona) console.log(` Last persona: ${peek.persona}`);
1024
+ else console.log(' Last persona: not sure — add --persona zara (or arjun, meera, …)');
1025
+ console.log('');
1026
+ break;
1027
+ }
1028
+ if (wantKeep) {
1029
+ const result = accept.keep({
1030
+ project: projectVal,
1031
+ persona: personaVal,
1032
+ comment: commentVal || becauseVal || '',
1033
+ shipped: !likedFlag,
1034
+ note: commentVal || '',
1035
+ });
1036
+ console.log(`\n✅ Kept ${result.persona}'s note.` + (result.shipped ? ' Marked as shipped for evolution.' : ' Saved as liked (not shipped).') + '\n');
1037
+ break;
1038
+ }
1039
+ const result = accept.fix({
1040
+ project: projectVal,
1041
+ persona: personaVal,
1042
+ because: becauseVal || commentVal || '',
1043
+ correction: correctionVal || '',
1044
+ rating: ratingVal,
1045
+ tags: parseFeedbackTags(tagsVal),
1046
+ });
1047
+ console.log(`\n✅ Logged your fix for ${result.persona}. The team can learn from it.\n`);
1048
+ } catch (err) {
1049
+ if (err.code === 'NEED_PERSONA') {
1050
+ console.error('\n ✗ Which note? Add --persona zara (or arjun, meera, priya, noor, anuj, raj).\n');
1051
+ process.exit(1);
1052
+ }
1053
+ if (err.code === 'NEED_BECAUSE') {
1054
+ console.error('\n ✗ Add one sentence: --because "what was wrong or what you did instead"\n');
1055
+ process.exit(1);
1056
+ }
1057
+ console.error(`\n ✗ ${err.message}\n`);
1058
+ process.exit(1);
1059
+ }
1060
+
1061
+ break;
1062
+ }
1063
+
1064
+ case 'share': {
1065
+ const share = require('../lib/share');
1066
+ const acceptMod = require('../lib/accept');
1067
+ const wantSend = sendFlag || yesFlag;
1068
+ (async () => {
1069
+ try {
1070
+ if (!wantSend) {
1071
+ if (becauseVal || commentVal) {
1072
+ acceptMod.fix({
1073
+ project: projectVal,
1074
+ persona: personaVal,
1075
+ because: becauseVal || commentVal,
1076
+ correction: correctionVal || becauseVal || commentVal,
1077
+ });
1078
+ }
1079
+ const peek = share.preview({ project: projectVal, persona: personaVal, all: allFlag });
1080
+ if (!peek.count) {
1081
+ console.log('\n Nothing to send yet. After a critique: /accept no plus one sentence, then /share.\n');
1082
+ return;
1083
+ }
1084
+ console.log(`\n Ready to send ${peek.count} note(s) to the package (redacted — no folders, emails, or keys).`);
1085
+ peek.rows.slice(0, 5).forEach((r) => {
1086
+ console.log(` • ${r.persona}: ${(r.comment || r.correction || '').slice(0, 100)}`);
1087
+ });
1088
+ if (peek.rows.length > 5) console.log(` … and ${peek.rows.length - 5} more`);
1089
+ console.log(peek.hasHttp ? ' Path: configured HTTP endpoint.' : ' Path: GitHub issue on joshirishi/analyzthis_design.');
1090
+ console.log(' Send? Run: npx analyzthis_design share --send\n');
1091
+ return;
1092
+ }
1093
+ const result = await share.send({
1094
+ project: projectVal,
1095
+ persona: personaVal,
1096
+ because: becauseVal || commentVal || '',
1097
+ correction: correctionVal || '',
1098
+ all: allFlag,
1099
+ });
1100
+ if (result.via === 'github-link') {
1101
+ console.log(`\n ${result.message}`);
1102
+ console.log(` ${result.url}\n`);
1103
+ return;
1104
+ }
1105
+ console.log(`\n✅ ${result.message}` + (result.url ? `\n ${result.url}` : '') + '\n');
1106
+ } catch (err) {
1107
+ if (err.code === 'NOTHING') {
1108
+ console.error('\n ✗ Nothing to send yet. /accept no plus one sentence, then /share.\n');
1109
+ process.exit(1);
1110
+ }
1111
+ if (err.code === 'NEED_PERSONA') {
1112
+ console.error('\n ✗ Which note? Add --persona zara (or arjun, meera, priya, noor, anuj, raj).\n');
1113
+ process.exit(1);
1114
+ }
1115
+ if (err.code === 'NEED_BECAUSE') {
1116
+ console.error('\n ✗ Add one sentence: --because "what was wrong or what you did instead"\n');
1117
+ process.exit(1);
1118
+ }
1119
+ console.error(`\n ✗ ${err.message}\n`);
1120
+ process.exit(1);
1121
+ }
1122
+ })();
1123
+ break;
1124
+ }
1125
+
996
1126
  // ── Persona feedback (v1.16) ────────────────────────────────────────────
997
1127
 
998
1128
  case 'feedback': {
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Designer keep / skip for evolution.
5
+ * Slash /accept talks in yes/no. This module writes the session flags.
6
+ */
7
+
8
+ var session = require('./session');
9
+ var feedback = require('./feedback');
10
+ var outcome = require('./outcome');
11
+
12
+ var PERSONA_IDS = session.PERSONA_IDS;
13
+
14
+ function resolvePersona(opts) {
15
+ var raw = String(opts.persona || '').toLowerCase().trim();
16
+ if (raw && PERSONA_IDS.indexOf(raw) === -1) {
17
+ var err = new Error('Unknown persona. Use: ' + PERSONA_IDS.join(', '));
18
+ err.code = 'BAD_PERSONA';
19
+ throw err;
20
+ }
21
+ var id = raw || session.inferLastPersona({ project: opts.project });
22
+ if (!id) {
23
+ var need = new Error('NEED_PERSONA');
24
+ need.code = 'NEED_PERSONA';
25
+ throw need;
26
+ }
27
+ return id;
28
+ }
29
+
30
+ function keep(opts) {
31
+ opts = opts || {};
32
+ var persona = resolvePersona(opts);
33
+ session.ensurePersonaOutput({
34
+ project: opts.project,
35
+ persona: persona,
36
+ text: opts.note,
37
+ });
38
+ session.markAccepted({ project: opts.project, persona: persona, accepted: true });
39
+ if (opts.comment) {
40
+ feedback.recordFeedback({
41
+ project: opts.project,
42
+ persona: persona,
43
+ satisfied: true,
44
+ rating: 5,
45
+ comment: opts.comment,
46
+ correction: '',
47
+ markRejected: false,
48
+ });
49
+ }
50
+ var shipped = opts.shipped !== false;
51
+ if (shipped) {
52
+ outcome.confirmOutcome({ project: opts.project, persona: persona, outcome: 'shipped' });
53
+ }
54
+ return { persona: persona, kept: true, shipped: shipped };
55
+ }
56
+
57
+ function fix(opts) {
58
+ opts = opts || {};
59
+ var persona = resolvePersona(opts);
60
+ var because = String(opts.because || opts.comment || '').trim();
61
+ var correction = String(opts.correction || because).trim();
62
+ if (!because) {
63
+ var need = new Error('NEED_BECAUSE');
64
+ need.code = 'NEED_BECAUSE';
65
+ throw need;
66
+ }
67
+ session.ensurePersonaOutput({
68
+ project: opts.project,
69
+ persona: persona,
70
+ text: opts.note,
71
+ });
72
+ feedback.recordFeedback({
73
+ project: opts.project,
74
+ persona: persona,
75
+ satisfied: false,
76
+ rating: opts.rating != null ? Number(opts.rating) : 2,
77
+ comment: because,
78
+ correction: correction,
79
+ tags: opts.tags || ['other'],
80
+ markRejected: true,
81
+ });
82
+ outcome.confirmOutcome({ project: opts.project, persona: persona, outcome: 'revised' });
83
+ return { persona: persona, kept: false, because: because };
84
+ }
85
+
86
+ function preview(opts) {
87
+ opts = opts || {};
88
+ var state = session.show({ project: opts.project });
89
+ return {
90
+ persona: session.inferLastPersona({ project: opts.project }),
91
+ hasSession: !!state,
92
+ };
93
+ }
94
+
95
+ module.exports = { keep, fix, preview, PERSONA_IDS };
@@ -316,6 +316,8 @@ module.exports = {
316
316
  resolveSubmitConfig,
317
317
  submitFeedback,
318
318
  submitStatus,
319
+ collectUnsentEntries,
320
+ markEntriesSubmitted,
319
321
  loadConsent,
320
322
  saveConsent,
321
323
  revokeConsent,
@@ -34,6 +34,8 @@ const PROJECT_COMMANDS = [
34
34
  'getting-started',
35
35
  'devi',
36
36
  'receipt',
37
+ 'accept',
38
+ 'share',
37
39
  ];
38
40
 
39
41
  const SKILLS = [
@@ -62,6 +64,8 @@ const SKILLS = [
62
64
  'design-reference',
63
65
  'knowledge-bank',
64
66
  'receipt',
67
+ 'accept',
68
+ 'share',
65
69
  ];
66
70
 
67
71
  function getPackageVersion() {
@@ -129,6 +133,9 @@ function printWelcomeBanner(targetId, log = console.log) {
129
133
  log(` ${p}design-critic 4-persona critique + composite score`);
130
134
  log(` ${p}deliberation-protocol adversarial review rules (objections, Raj)`);
131
135
  log(` ${p}devi host LLM: voice personas when CLI has no API keys`);
136
+ log(` ${p}accept keep or skip the last note (yes / no) — local evolution`);
137
+ log(` ${p}share send a correction to the package (preview, then yes)`);
138
+ log(` ${p}receipt inferred tokens for this project (not a bill)`);
132
139
 
133
140
  log('');
134
141
  log(' CLI (host mode — no API keys):');
@@ -268,9 +268,12 @@ var TOOLS = [
268
268
  inputSchema: {
269
269
  type: 'object',
270
270
  properties: {
271
- action: { type: 'string', enum: ['show', 'init', 'reset', 'accept'], description: 'show: display current state; init: create fresh; reset: delete; accept: mark a persona output as accepted' },
272
- persona: { type: 'string', description: 'Persona ID (for accept action)' },
273
- reject: { type: 'boolean', description: 'Reject instead of accept (for accept action)' },
271
+ action: { type: 'string', enum: ['show', 'init', 'reset', 'accept'], description: 'show: display current state; init: create fresh; reset: delete; accept: keep or skip a persona note (designer /accept)' },
272
+ persona: { type: 'string', description: 'Persona ID (for accept action). Optional — last persona if omitted.' },
273
+ reject: { type: 'boolean', description: 'Skip / fix instead of keep (for accept action)' },
274
+ because: { type: 'string', description: 'One sentence: what was wrong or what you did instead (when reject is true)' },
275
+ comment: { type: 'string', description: 'Optional note on keep, or alias for because on skip' },
276
+ shipped: { type: 'boolean', description: 'On keep: mark shipped (default true). False = liked only.' },
274
277
  },
275
278
  required: ['action'],
276
279
  },
@@ -591,9 +594,18 @@ function handleSession(args) {
591
594
  return { result: 'Session reset.' };
592
595
  }
593
596
  if (action === 'accept') {
594
- if (!args.persona) return { error: 'persona is required for accept' };
595
- var result = session.markAccepted({ persona: args.persona, accepted: !args.reject });
596
- return { result: JSON.stringify(result) };
597
+ var accept = require('./accept');
598
+ if (args.reject) {
599
+ return { result: JSON.stringify(accept.fix({
600
+ persona: args.persona,
601
+ because: args.because || args.comment || '',
602
+ })) };
603
+ }
604
+ return { result: JSON.stringify(accept.keep({
605
+ persona: args.persona,
606
+ comment: args.comment || '',
607
+ shipped: args.shipped !== false,
608
+ })) };
597
609
  }
598
610
  } catch (e) {
599
611
  return { error: e.message };
@@ -0,0 +1,102 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Inferred token receipts for Claude-path (slash / MCP) turns.
5
+ * Never call this a bill. Never multiply into a monthly dollar. Never say verified.
6
+ */
7
+
8
+ var session = require('./session');
9
+
10
+ var HOST_TURNS_CAP = 40;
11
+ var CHARS_PER_TOKEN = 4;
12
+
13
+ function inferTokens(text) {
14
+ return Math.ceil(String(text || '').length / CHARS_PER_TOKEN);
15
+ }
16
+
17
+ function formatFooter(inferredIn) {
18
+ return (
19
+ '\n\n---\nInferred context this turn: ' +
20
+ inferredIn +
21
+ ' tokens (not a bill). Do not invent a dollar figure.'
22
+ );
23
+ }
24
+
25
+ function wrapPayload(text, meta) {
26
+ var body = String(text || '');
27
+ var inferredIn = inferTokens(body);
28
+ try {
29
+ session.logHostTurn({
30
+ path: (meta && meta.path) || 'mcp',
31
+ persona: (meta && meta.persona) || '',
32
+ inferred_in: inferredIn,
33
+ project: meta && meta.project,
34
+ verdicts: (meta && meta.verdicts) || 0,
35
+ });
36
+ } catch (e) {
37
+ // Session write is best-effort — never fail a tool call for a receipt.
38
+ }
39
+ return body + formatFooter(inferredIn);
40
+ }
41
+
42
+ function tokensPerVerdict(state) {
43
+ if (!state || !state.metrics) return null;
44
+ var m = state.metrics;
45
+ var host = (m.host_turns || []).reduce(function (sum, turn) {
46
+ return sum + (turn.inferred_in || 0);
47
+ }, 0);
48
+ var cliIn = m.input_tokens_est || 0;
49
+ var totalIn = host + cliIn;
50
+ var verdicts = m.verdicts_this_run || 0;
51
+ if (!verdicts && (m.experts_run || []).length) verdicts = 1;
52
+ if (!verdicts && (m.host_turns || []).length) verdicts = 1;
53
+ if (!verdicts || !totalIn) return null;
54
+ return Math.round(totalIn / verdicts);
55
+ }
56
+
57
+ function formatReport(state) {
58
+ if (!state) {
59
+ return 'No session found. Run: npx analyzthis_design session init';
60
+ }
61
+ var m = state.metrics || {};
62
+ var turns = m.host_turns || [];
63
+ var tpv = tokensPerVerdict(state);
64
+ var lines = [
65
+ 'Receipt — inferred, not a bill',
66
+ 'Project: ' + (state.project_id || ''),
67
+ 'CLI est. tokens: ' + (m.input_tokens_est || 0) + ' in / ' + (m.output_tokens_est || 0) + ' out',
68
+ 'Host turns logged: ' + turns.length,
69
+ ];
70
+ turns.slice(-8).forEach(function (turn) {
71
+ lines.push(
72
+ ' ' +
73
+ (turn.at || '') +
74
+ ' ' +
75
+ (turn.path || '') +
76
+ ' ' +
77
+ (turn.persona || '-') +
78
+ ' ' +
79
+ (turn.inferred_in || 0) +
80
+ ' tok'
81
+ );
82
+ });
83
+ if (tpv != null) lines.push('Tokens per verdict: ' + tpv + ' (inferred + CLI est. input / verdicts)');
84
+ else lines.push('Tokens per verdict: n/a — no host turn or CLI run yet');
85
+ lines.push('You cannot know Claude’s real usage from slash alone. Do not invent a dollar figure.');
86
+ return lines.join('\n');
87
+ }
88
+
89
+ function report(opts) {
90
+ var state = session.show({ project: opts && opts.project });
91
+ return formatReport(state);
92
+ }
93
+
94
+ module.exports = {
95
+ inferTokens: inferTokens,
96
+ formatFooter: formatFooter,
97
+ wrapPayload: wrapPayload,
98
+ tokensPerVerdict: tokensPerVerdict,
99
+ formatReport: formatReport,
100
+ report: report,
101
+ HOST_TURNS_CAP: HOST_TURNS_CAP,
102
+ };
@@ -177,8 +177,58 @@ function listProjects() {
177
177
  * lightweight feedback flag the future LoRA training-pair export reads.
178
178
  * No-ops if the persona hasn't produced an output yet.
179
179
  */
180
+ const PERSONA_IDS = ['arjun', 'meera', 'priya', 'zara', 'noor', 'anuj', 'raj', 'kavi'];
181
+
182
+ function inferLastPersona({ project } = {}) {
183
+ const state = show({ project: project || getProjectId() });
184
+ if (!state) return null;
185
+ const turns = (state.metrics && state.metrics.host_turns) || [];
186
+ for (let i = turns.length - 1; i >= 0; i--) {
187
+ const id = turns[i] && turns[i].persona;
188
+ if (id && PERSONA_IDS.indexOf(id) !== -1) return id;
189
+ }
190
+ const outputs = state.persona_outputs || {};
191
+ let best = null;
192
+ let bestAt = '';
193
+ Object.keys(outputs).forEach((id) => {
194
+ const at = (outputs[id] && outputs[id].at) || '';
195
+ if (at >= bestAt) {
196
+ bestAt = at;
197
+ best = id;
198
+ }
199
+ });
200
+ if (best) return best;
201
+ const experts = state.routing_decision && state.routing_decision.experts;
202
+ if (Array.isArray(experts) && experts[0]) return experts[0];
203
+ return null;
204
+ }
205
+
206
+ // Slash / MCP notes often never write persona_outputs. Create a stub so keep/fix still works.
207
+ function ensurePersonaOutput({ project, persona, text } = {}) {
208
+ const projectId = project || getProjectId();
209
+ let state = show({ project: projectId });
210
+ if (!state) {
211
+ init({ project: projectId });
212
+ state = show({ project: projectId });
213
+ }
214
+ const outputs = { ...(state.persona_outputs || {}) };
215
+ let created = false;
216
+ if (!outputs[persona]) {
217
+ outputs[persona] = {
218
+ text: text || '(slash — designer marked this note; full text was in chat)',
219
+ at: new Date().toISOString(),
220
+ accepted: null,
221
+ source: 'slash',
222
+ };
223
+ created = true;
224
+ state = update({ project: projectId, patch: { persona_outputs: outputs } });
225
+ }
226
+ return { projectId, state, created };
227
+ }
228
+
180
229
  function markAccepted({ project, persona, accepted = true } = {}) {
181
230
  const projectId = project || getProjectId();
231
+ ensurePersonaOutput({ project: projectId, persona });
182
232
  const state = show({ project: projectId });
183
233
  if (!state || !state.persona_outputs || !state.persona_outputs[persona]) {
184
234
  return { updated: false, reason: `No output recorded for persona "${persona}" in this session yet.` };
@@ -213,5 +263,6 @@ function logHostTurn({ project, path: turnPath, persona, inferred_in, verdicts =
213
263
  }
214
264
 
215
265
  module.exports = {
216
- getProjectId, sessionPath, sessionDir, init, show, reset, update, listProjects, markAccepted, logHostTurn, SESSIONS_ROOT,
266
+ getProjectId, sessionPath, sessionDir, init, show, reset, update, listProjects,
267
+ markAccepted, ensurePersonaOutput, inferLastPersona, PERSONA_IDS, logHostTurn, SESSIONS_ROOT,
217
268
  };
@@ -0,0 +1,131 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Designer share — send local corrections back to the published package.
5
+ * Slash /share talks in yes/no. HTTP submit if configured; else GitHub issue.
6
+ */
7
+
8
+ var { spawnSync } = require('child_process');
9
+ var accept = require('./accept');
10
+ var submit = require('./feedback-submit');
11
+
12
+ var REPO = 'joshirishi/analyzthis_design';
13
+ var ISSUE_NEW = 'https://github.com/' + REPO + '/issues/new';
14
+
15
+ function preview(opts) {
16
+ opts = opts || {};
17
+ var cfg = submit.resolveSubmitConfig();
18
+ var entries = submit.collectUnsentEntries({
19
+ project: opts.project,
20
+ all: !!opts.all,
21
+ persona: opts.persona,
22
+ });
23
+ var rows = entries.map(function (e) {
24
+ var payload = submit.entryToSubmitPayload(e, cfg);
25
+ return {
26
+ id: e.id,
27
+ persona: payload.persona,
28
+ comment: payload.user_comment,
29
+ correction: payload.assistant_preferred,
30
+ };
31
+ });
32
+ return {
33
+ count: rows.length,
34
+ rows: rows,
35
+ hasHttp: !!(cfg.url && cfg.anonKey),
36
+ endpoint: cfg.url || '',
37
+ consent: !!(submit.loadConsent() && submit.loadConsent().opted_in),
38
+ };
39
+ }
40
+
41
+ function formatIssueBody(rows) {
42
+ var lines = [
43
+ 'Shared from `/share` (designer keep/skip). Redacted. No paths, emails, or keys.',
44
+ '',
45
+ ];
46
+ rows.forEach(function (r, i) {
47
+ lines.push('### ' + (i + 1) + '. ' + r.persona);
48
+ if (r.comment) lines.push('**What was wrong:** ' + r.comment);
49
+ if (r.correction) lines.push('**What we did instead:** ' + r.correction);
50
+ lines.push('');
51
+ });
52
+ return lines.join('\n');
53
+ }
54
+
55
+ function issueWebUrl(title, body) {
56
+ return ISSUE_NEW
57
+ + '?labels=persona-feedback'
58
+ + '&title=' + encodeURIComponent(title)
59
+ + '&body=' + encodeURIComponent(body);
60
+ }
61
+
62
+ function tryGithubIssue(title, body) {
63
+ var created = spawnSync(
64
+ 'gh',
65
+ ['issue', 'create', '--repo', REPO, '--title', title, '--label', 'persona-feedback', '--body', body],
66
+ { encoding: 'utf8', timeout: 20000 }
67
+ );
68
+ if (created.status === 0 && created.stdout) {
69
+ return { ok: true, url: created.stdout.trim() };
70
+ }
71
+ return { ok: false, error: (created.stderr || created.stdout || 'gh failed').trim() };
72
+ }
73
+
74
+ async function send(opts) {
75
+ opts = opts || {};
76
+ var because = String(opts.because || opts.comment || '').trim();
77
+ if (because) {
78
+ accept.fix({
79
+ project: opts.project,
80
+ persona: opts.persona,
81
+ because: because,
82
+ correction: opts.correction || because,
83
+ });
84
+ }
85
+
86
+ var peek = preview(opts);
87
+ if (!peek.count) {
88
+ var empty = new Error('NOTHING');
89
+ empty.code = 'NOTHING';
90
+ throw empty;
91
+ }
92
+
93
+ if (peek.hasHttp) {
94
+ var result = await submit.submitFeedback({
95
+ project: opts.project,
96
+ all: !!opts.all,
97
+ persona: opts.persona,
98
+ yes: true,
99
+ });
100
+ return {
101
+ sent: result.submitted || 0,
102
+ via: 'http',
103
+ url: result.endpoint || '',
104
+ message: result.message,
105
+ };
106
+ }
107
+
108
+ var title = '[feedback] ' + peek.rows[0].persona + ': ' + (peek.rows[0].comment || 'correction').slice(0, 72);
109
+ var body = formatIssueBody(peek.rows);
110
+ var gh = tryGithubIssue(title, body);
111
+ if (gh.ok) {
112
+ submit.markEntriesSubmitted(peek.rows.map(function (r) { return r.id; }));
113
+ return {
114
+ sent: peek.count,
115
+ via: 'github',
116
+ url: gh.url,
117
+ message: 'Opened a GitHub issue so the package can learn.',
118
+ };
119
+ }
120
+
121
+ return {
122
+ sent: 0,
123
+ via: 'github-link',
124
+ url: issueWebUrl(title, body),
125
+ title: title,
126
+ body: body,
127
+ message: 'Could not open GitHub from here. Use the link — nothing secret is in it.',
128
+ };
129
+ }
130
+
131
+ module.exports = { preview, send, formatIssueBody, issueWebUrl, REPO };
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: accept
3
+ description: Keep or skip the last persona note for evolution. Designer types /accept then yes or no — no CLI flags to remember.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Accept — keep or skip
8
+
9
+ You help a designer mark the last persona note (Zara, Arjun, …) so the team can evolve. They should never type `npx` flags.
10
+
11
+ ## What to do
12
+
13
+ 1. Read the words after `/accept` (and this chat).
14
+ 2. Decide keep vs skip:
15
+ - **Keep:** yes, keep, ship, good, like, 👍
16
+ - **Skip:** no, skip, fix, wrong, reject, 👎
17
+ 3. If they did not say keep or skip, ask **once** in plain language:
18
+
19
+ > Keep this note? Reply **yes** (we’re taking it) or **no** plus one sentence.
20
+
21
+ 4. Infer the persona from this chat (`/zara`, `/arjun`, …). If unclear, ask **once**:
22
+
23
+ > Was this Zara, Arjun, Meera, Priya, Noor, Anuj, or Raj?
24
+
25
+ 5. On **skip**, you need one sentence (what was wrong, or what they did instead). If missing, ask **once**. Then stop asking.
26
+ 6. Run the CLI yourself. Do not ask them to copy commands.
27
+
28
+ **Keep:**
29
+
30
+ ```bash
31
+ npx analyzthis_design accept --keep --persona zara
32
+ ```
33
+
34
+ **Skip:**
35
+
36
+ ```bash
37
+ npx analyzthis_design accept --fix --persona zara --because "one sentence from the designer"
38
+ ```
39
+
40
+ Change `zara` to the persona they used. If they already named it in `/accept yes zara`, pass that.
41
+
42
+ 7. Reply in **one or two short sentences**. Example: “Saved. Zara’s note is marked keep.” or “Saved. We logged your fix so the team can learn.” After a skip, you may mention they can type `/share` to send it to the package. No JSON. No dollar figures. No flag tutorial unless the command failed.
43
+
44
+ ## If the command failed
45
+
46
+ Say what happened in plain language. If it asks which persona or for one sentence, ask the designer that — still no flags.
47
+
48
+ ## Do not
49
+
50
+ - Invent a monthly cost or a verified token bill
51
+ - Apply evolve patches (`evolve --apply`) from this command
52
+ - Edit product code
@@ -36,6 +36,9 @@ This runs **Kavi** — scans your repo, builds an Obsidian vault, and syncs a **
36
36
  | Set visual direction with references + team debate | `/mood-board` | Collect web/DS references, tag, deliberate, converge |
37
37
  | Inspect the chunked execution planner | `/chunk-planner` | See how tasks are split into model-routed chunks |
38
38
  | Run legacy single-pass orchestrator | `/run-unchunked` | Skip planner overhead for quick single-expert tasks |
39
+ | Keep or skip the last persona note | `/accept` | Yes or no (plus one sentence if no). Local evolution — no CLI flags. |
40
+ | Send a correction to the package | `/share` | Preview, then yes. Redacted. GitHub or HTTP — no CLI flags. |
41
+ | See inferred tokens after a critique | `/receipt` | Not a bill. Not a monthly dollar. |
39
42
  | Make the team learn from accepted outputs | `npx analyzthis_design evolve --extract` | Harvests lessons + proposes prompt/reference/router patches |
40
43
  | Track whether a persona's advice actually shipped | `npx analyzthis_design outcome --confirm` | Labels outcome: shipped / revised / blocked / missed |
41
44
 
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: share
3
+ description: Send a correction back to the analyzthis_design package. Designer types /share then yes — no CLI flags. Preview first. Redacted. Not a bill.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Share — send a note back to the package
8
+
9
+ You help a designer send a correction to the **published** team (npm package / GitHub). Local `/accept` stays on their machine until they share.
10
+
11
+ They should never type `npx` flags.
12
+
13
+ ## What to do
14
+
15
+ 1. Read the words after `/share` (and this chat).
16
+ 2. If they gave a sentence (what was wrong / what they did instead) and have not `/accept`’d yet, treat that as the note to share.
17
+ 3. Always **preview first** unless they already said **yes** on a preview you just showed:
18
+
19
+ ```bash
20
+ npx analyzthis_design share
21
+ ```
22
+
23
+ 4. Show the preview in plain language. Example:
24
+
25
+ > Ready to send **1** note to the package (Zara): “too much motion on the daily table.”
26
+ > This is redacted — no folder names, emails, or keys.
27
+ > Send it? Reply **yes** or **no**.
28
+
29
+ 5. **no** / cancel / skip → stop. Say “Not sent. It stays on your machine.”
30
+ 6. **yes** / send / share → run:
31
+
32
+ ```bash
33
+ npx analyzthis_design share --send
34
+ ```
35
+
36
+ If they included a new sentence and a persona, add `--because "..."` and `--persona zara` (change the name). Do not ask them to type those flags.
37
+
38
+ 7. Reply in **one or two short sentences**. If the command printed a GitHub URL, include that link. No JSON dump. No dollar figures.
39
+
40
+ ## If nothing is waiting
41
+
42
+ Say: “Nothing to send yet. After a critique, `/accept no` plus one sentence, then `/share`.”
43
+ If they already typed a sentence in this `/share`, run preview with that sentence recorded (the CLI `--because` path) and continue from step 4.
44
+
45
+ ## If the command failed
46
+
47
+ Plain language only. If it asks which persona or for one sentence, ask that once — still no flags.
48
+
49
+ ## Do not
50
+
51
+ - Send without a yes (unless they typed `/share yes` on a preview you already showed)
52
+ - Invent that HTTP or GitHub succeeded
53
+ - Apply evolve patches
54
+ - Edit product code
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "analyzthis_design",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "8 AI design personas — v2.2 project-scoped knowledge bank (no cross-project vault entanglement), v2.0 chunked execution with frontier planner + free/cheap chunk models, adversarial deliberation loops, opt-in community feedback, Kavi knowledge collection, DesignSpec producer path, wireframe skills, UX critique, Agent Skills for Cursor, Claude, Codex, Grok, Windsurf. Plain source — no obfuscation, no auto-install.",
5
5
  "keywords": [
6
6
  "cursor",
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: accept
3
+ description: Keep or skip the last persona note for evolution. Designer types /accept then yes or no — no CLI flags to remember.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Accept — keep or skip
8
+
9
+ You help a designer mark the last persona note (Zara, Arjun, …) so the team can evolve. They should never type `npx` flags.
10
+
11
+ ## What to do
12
+
13
+ 1. Read the words after `/accept` (and this chat).
14
+ 2. Decide keep vs skip:
15
+ - **Keep:** yes, keep, ship, good, like, 👍
16
+ - **Skip:** no, skip, fix, wrong, reject, 👎
17
+ 3. If they did not say keep or skip, ask **once** in plain language:
18
+
19
+ > Keep this note? Reply **yes** (we’re taking it) or **no** plus one sentence.
20
+
21
+ 4. Infer the persona from this chat (`/zara`, `/arjun`, …). If unclear, ask **once**:
22
+
23
+ > Was this Zara, Arjun, Meera, Priya, Noor, Anuj, or Raj?
24
+
25
+ 5. On **skip**, you need one sentence (what was wrong, or what they did instead). If missing, ask **once**. Then stop asking.
26
+ 6. Run the CLI yourself. Do not ask them to copy commands.
27
+
28
+ **Keep:**
29
+
30
+ ```bash
31
+ npx analyzthis_design accept --keep --persona zara
32
+ ```
33
+
34
+ **Skip:**
35
+
36
+ ```bash
37
+ npx analyzthis_design accept --fix --persona zara --because "one sentence from the designer"
38
+ ```
39
+
40
+ Change `zara` to the persona they used. If they already named it in `/accept yes zara`, pass that.
41
+
42
+ 7. Reply in **one or two short sentences**. Example: “Saved. Zara’s note is marked keep.” or “Saved. We logged your fix so the team can learn.” After a skip, you may mention they can type `/share` to send it to the package. No JSON. No dollar figures. No flag tutorial unless the command failed.
43
+
44
+ ## If the command failed
45
+
46
+ Say what happened in plain language. If it asks which persona or for one sentence, ask the designer that — still no flags.
47
+
48
+ ## Do not
49
+
50
+ - Invent a monthly cost or a verified token bill
51
+ - Apply evolve patches (`evolve --apply`) from this command
52
+ - Edit product code
@@ -36,6 +36,9 @@ This runs **Kavi** — scans your repo, builds an Obsidian vault, and syncs a **
36
36
  | Set visual direction with references + team debate | `/mood-board` | Collect web/DS references, tag, deliberate, converge |
37
37
  | Inspect the chunked execution planner | `/chunk-planner` | See how tasks are split into model-routed chunks |
38
38
  | Run legacy single-pass orchestrator | `/run-unchunked` | Skip planner overhead for quick single-expert tasks |
39
+ | Keep or skip the last persona note | `/accept` | Yes or no (plus one sentence if no). Local evolution — no CLI flags. |
40
+ | Send a correction to the package | `/share` | Preview, then yes. Redacted. GitHub or HTTP — no CLI flags. |
41
+ | See inferred tokens after a critique | `/receipt` | Not a bill. Not a monthly dollar. |
39
42
  | Make the team learn from accepted outputs | `npx analyzthis_design evolve --extract` | Harvests lessons + proposes prompt/reference/router patches |
40
43
  | Track whether a persona's advice actually shipped | `npx analyzthis_design outcome --confirm` | Labels outcome: shipped / revised / blocked / missed |
41
44
 
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: share
3
+ description: Send a correction back to the analyzthis_design package. Designer types /share then yes — no CLI flags. Preview first. Redacted. Not a bill.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Share — send a note back to the package
8
+
9
+ You help a designer send a correction to the **published** team (npm package / GitHub). Local `/accept` stays on their machine until they share.
10
+
11
+ They should never type `npx` flags.
12
+
13
+ ## What to do
14
+
15
+ 1. Read the words after `/share` (and this chat).
16
+ 2. If they gave a sentence (what was wrong / what they did instead) and have not `/accept`’d yet, treat that as the note to share.
17
+ 3. Always **preview first** unless they already said **yes** on a preview you just showed:
18
+
19
+ ```bash
20
+ npx analyzthis_design share
21
+ ```
22
+
23
+ 4. Show the preview in plain language. Example:
24
+
25
+ > Ready to send **1** note to the package (Zara): “too much motion on the daily table.”
26
+ > This is redacted — no folder names, emails, or keys.
27
+ > Send it? Reply **yes** or **no**.
28
+
29
+ 5. **no** / cancel / skip → stop. Say “Not sent. It stays on your machine.”
30
+ 6. **yes** / send / share → run:
31
+
32
+ ```bash
33
+ npx analyzthis_design share --send
34
+ ```
35
+
36
+ If they included a new sentence and a persona, add `--because "..."` and `--persona zara` (change the name). Do not ask them to type those flags.
37
+
38
+ 7. Reply in **one or two short sentences**. If the command printed a GitHub URL, include that link. No JSON dump. No dollar figures.
39
+
40
+ ## If nothing is waiting
41
+
42
+ Say: “Nothing to send yet. After a critique, `/accept no` plus one sentence, then `/share`.”
43
+ If they already typed a sentence in this `/share`, run preview with that sentence recorded (the CLI `--because` path) and continue from step 4.
44
+
45
+ ## If the command failed
46
+
47
+ Plain language only. If it asks which persona or for one sentence, ask that once — still no flags.
48
+
49
+ ## Do not
50
+
51
+ - Send without a yes (unless they typed `/share yes` on a preview you already showed)
52
+ - Invent that HTTP or GitHub succeeded
53
+ - Apply evolve patches
54
+ - Edit product code