analyzthis_design 2.1.2 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -6
- package/dist/README.md +30 -6
- package/dist/bin/cli.js +36 -20
- package/dist/lib/collect.js +8 -4
- package/dist/lib/knowledge.js +196 -44
- package/dist/lib/source-discovery.js +6 -3
- package/package.json +2 -3
- package/dist/supabase/deliberation-config.example.json +0 -15
- package/dist/supabase/feedback-config.example.json +0 -7
- package/dist/supabase/migrations/001_persona_feedback.sql +0 -54
- package/supabase/deliberation-config.example.json +0 -15
- package/supabase/feedback-config.example.json +0 -7
- package/supabase/migrations/001_persona_feedback.sql +0 -54
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.2 — project-scoped knowledge bank
|
|
8
|
+
|
|
9
|
+
Knowledge sources are now scoped per project by default. `collect`, `connect`, `sync`, `disconnect`, and `status` operate on the project derived from your current working directory, and the built `knowledge-bank` skill is written into that project's local skills directory (`<project>/.claude/skills/knowledge-bank/SKILL.md`, `<project>/.cursor/skills/...`, etc.). Invoking a skill from one project never reads another project's vaults.
|
|
10
|
+
|
|
11
|
+
Pass `--global` to opt into the legacy merged behavior (read `config.sources` and write into `~/.claude/skills/...`). Use `--global` only when you deliberately want cross-project blending.
|
|
12
|
+
|
|
7
13
|
## v2.0 — chunked execution by default
|
|
8
14
|
|
|
9
15
|
`npx analyzthis_design run --task "..."` now uses a **frontier planner + cheap chunk models**:
|
|
@@ -15,7 +21,7 @@ Install once. Run structured UX critiques, multi-phase ideation, and task-ground
|
|
|
15
21
|
|
|
16
22
|
Use `npx analyzthis_design run-unchunked` for the legacy single-pass orchestrator.
|
|
17
23
|
|
|
18
|
-
**npm:** [analyzthis_design](https://www.npmjs.com/package/analyzthis_design) · **Current version:** 2.
|
|
24
|
+
**npm:** [analyzthis_design](https://www.npmjs.com/package/analyzthis_design) · **Current version:** 2.2.0 · **Step-by-step guide:** [HOW-TO-USE.md](./HOW-TO-USE.md)
|
|
19
25
|
|
|
20
26
|
---
|
|
21
27
|
|
|
@@ -536,12 +542,14 @@ npx analyzthis_design feedback status
|
|
|
536
542
|
|
|
537
543
|
**What does NOT get sent:** project paths, repo names, emails, API keys, full source trees.
|
|
538
544
|
|
|
539
|
-
**Maintainer setup (
|
|
545
|
+
**Maintainer setup (vendor-neutral HTTP endpoint):**
|
|
540
546
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
547
|
+
The `feedback submit` client is a plain HTTPS POST with an `apikey` header — it works with any REST endpoint that accepts anonymous inserts, not only Supabase. A reference schema (with row-level security for insert-only anon access) lives in [`supabase/migrations/001_persona_feedback.sql`](./supabase/migrations/001_persona_feedback.sql) in the repo. That folder is **not** shipped in the npm package, so installing `analyzthis_design` does not pull a Supabase-branded folder into `node_modules`.
|
|
548
|
+
|
|
549
|
+
1. Stand up any HTTP endpoint that accepts anonymous JSON inserts (Supabase with RLS is one option; a small Cloudflare Worker or a self-hosted Postgres + thin API work too).
|
|
550
|
+
2. If you use the reference schema, run `supabase/migrations/001_persona_feedback.sql` in your SQL editor.
|
|
551
|
+
3. Copy the endpoint URL and anon key into `~/.analyzthis_design/config.json` under `"feedback"` (or set env vars `ANALYZTHIS_FEEDBACK_URL` + `ANALYZTHIS_FEEDBACK_ANON_KEY`).
|
|
552
|
+
4. Read submissions from your endpoint's dashboard.
|
|
545
553
|
|
|
546
554
|
Users can also file GitHub issues via **Persona feedback** template if they prefer not to use CLI submit.
|
|
547
555
|
|
|
@@ -869,6 +877,22 @@ skills/
|
|
|
869
877
|
|
|
870
878
|
---
|
|
871
879
|
|
|
880
|
+
## Marketing site
|
|
881
|
+
|
|
882
|
+
The public lab site lives in `website/` and is deployed on Vercel as **analyzthis-lab** (site only — not the npm package). Copy now names two doors: lab products for people who already use Cursor or Claude, and custom teams that arrive in a firm’s existing tools. The Vercel project root is `website/` with no build step — do not set Output Directory to `public`.
|
|
883
|
+
|
|
884
|
+
- Live: [https://analyzthis-lab.vercel.app](https://analyzthis-lab.vercel.app)
|
|
885
|
+
- Design team deep-dive: [https://analyzthis-lab.vercel.app/design](https://analyzthis-lab.vercel.app/design) (`website/design.html`)
|
|
886
|
+
- Form test (does not write to Airtable): add `?form=mock` to the URL
|
|
887
|
+
- Leads go to Airtable through `website/api/lead.js`. Each step is saved; unfinished fills are marked `partial`.
|
|
888
|
+
- Booking uses Google Calendar: [https://calendar.app.google/KtKJ7hCAx1duA8m58](https://calendar.app.google/KtKJ7hCAx1duA8m58)
|
|
889
|
+
- Form views are counted first-party (no cookies, no third-party pixel). They are only stored if you set the optional `AIRTABLE_EVENTS_TABLE` env var to a separate Airtable table; without it the count is discarded and the Leads table is never touched.
|
|
890
|
+
- There is exactly one privacy disclosure, in the `.privacy-note` directly above the inquiry form. If you change what the site collects, update that sentence — do not add a second notice elsewhere on the page.
|
|
891
|
+
- Header layout: the real `logo.svg` sits left, nav links centre, one orange CTA right. No webfont is loaded, so the wordmark must stay an SVG.
|
|
892
|
+
- The hero is tuned so the primary CTA and trust ticks stay above the fold at 1440×800 — check that before changing hero padding or the `h1` clamp.
|
|
893
|
+
|
|
894
|
+
---
|
|
895
|
+
|
|
872
896
|
## License
|
|
873
897
|
|
|
874
898
|
MIT — [Rishikesh Joshi](https://github.com/rishikeshjoshi)
|
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.2 — project-scoped knowledge bank
|
|
8
|
+
|
|
9
|
+
Knowledge sources are now scoped per project by default. `collect`, `connect`, `sync`, `disconnect`, and `status` operate on the project derived from your current working directory, and the built `knowledge-bank` skill is written into that project's local skills directory (`<project>/.claude/skills/knowledge-bank/SKILL.md`, `<project>/.cursor/skills/...`, etc.). Invoking a skill from one project never reads another project's vaults.
|
|
10
|
+
|
|
11
|
+
Pass `--global` to opt into the legacy merged behavior (read `config.sources` and write into `~/.claude/skills/...`). Use `--global` only when you deliberately want cross-project blending.
|
|
12
|
+
|
|
7
13
|
## v2.0 — chunked execution by default
|
|
8
14
|
|
|
9
15
|
`npx analyzthis_design run --task "..."` now uses a **frontier planner + cheap chunk models**:
|
|
@@ -15,7 +21,7 @@ Install once. Run structured UX critiques, multi-phase ideation, and task-ground
|
|
|
15
21
|
|
|
16
22
|
Use `npx analyzthis_design run-unchunked` for the legacy single-pass orchestrator.
|
|
17
23
|
|
|
18
|
-
**npm:** [analyzthis_design](https://www.npmjs.com/package/analyzthis_design) · **Current version:** 2.
|
|
24
|
+
**npm:** [analyzthis_design](https://www.npmjs.com/package/analyzthis_design) · **Current version:** 2.2.0 · **Step-by-step guide:** [HOW-TO-USE.md](./HOW-TO-USE.md)
|
|
19
25
|
|
|
20
26
|
---
|
|
21
27
|
|
|
@@ -536,12 +542,14 @@ npx analyzthis_design feedback status
|
|
|
536
542
|
|
|
537
543
|
**What does NOT get sent:** project paths, repo names, emails, API keys, full source trees.
|
|
538
544
|
|
|
539
|
-
**Maintainer setup (
|
|
545
|
+
**Maintainer setup (vendor-neutral HTTP endpoint):**
|
|
540
546
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
547
|
+
The `feedback submit` client is a plain HTTPS POST with an `apikey` header — it works with any REST endpoint that accepts anonymous inserts, not only Supabase. A reference schema (with row-level security for insert-only anon access) lives in [`supabase/migrations/001_persona_feedback.sql`](./supabase/migrations/001_persona_feedback.sql) in the repo. That folder is **not** shipped in the npm package, so installing `analyzthis_design` does not pull a Supabase-branded folder into `node_modules`.
|
|
548
|
+
|
|
549
|
+
1. Stand up any HTTP endpoint that accepts anonymous JSON inserts (Supabase with RLS is one option; a small Cloudflare Worker or a self-hosted Postgres + thin API work too).
|
|
550
|
+
2. If you use the reference schema, run `supabase/migrations/001_persona_feedback.sql` in your SQL editor.
|
|
551
|
+
3. Copy the endpoint URL and anon key into `~/.analyzthis_design/config.json` under `"feedback"` (or set env vars `ANALYZTHIS_FEEDBACK_URL` + `ANALYZTHIS_FEEDBACK_ANON_KEY`).
|
|
552
|
+
4. Read submissions from your endpoint's dashboard.
|
|
545
553
|
|
|
546
554
|
Users can also file GitHub issues via **Persona feedback** template if they prefer not to use CLI submit.
|
|
547
555
|
|
|
@@ -869,6 +877,22 @@ skills/
|
|
|
869
877
|
|
|
870
878
|
---
|
|
871
879
|
|
|
880
|
+
## Marketing site
|
|
881
|
+
|
|
882
|
+
The public lab site lives in `website/` and is deployed on Vercel as **analyzthis-lab** (site only — not the npm package). Copy now names two doors: lab products for people who already use Cursor or Claude, and custom teams that arrive in a firm’s existing tools. The Vercel project root is `website/` with no build step — do not set Output Directory to `public`.
|
|
883
|
+
|
|
884
|
+
- Live: [https://analyzthis-lab.vercel.app](https://analyzthis-lab.vercel.app)
|
|
885
|
+
- Design team deep-dive: [https://analyzthis-lab.vercel.app/design](https://analyzthis-lab.vercel.app/design) (`website/design.html`)
|
|
886
|
+
- Form test (does not write to Airtable): add `?form=mock` to the URL
|
|
887
|
+
- Leads go to Airtable through `website/api/lead.js`. Each step is saved; unfinished fills are marked `partial`.
|
|
888
|
+
- Booking uses Google Calendar: [https://calendar.app.google/KtKJ7hCAx1duA8m58](https://calendar.app.google/KtKJ7hCAx1duA8m58)
|
|
889
|
+
- Form views are counted first-party (no cookies, no third-party pixel). They are only stored if you set the optional `AIRTABLE_EVENTS_TABLE` env var to a separate Airtable table; without it the count is discarded and the Leads table is never touched.
|
|
890
|
+
- There is exactly one privacy disclosure, in the `.privacy-note` directly above the inquiry form. If you change what the site collects, update that sentence — do not add a second notice elsewhere on the page.
|
|
891
|
+
- Header layout: the real `logo.svg` sits left, nav links centre, one orange CTA right. No webfont is loaded, so the wordmark must stay an SVG.
|
|
892
|
+
- The hero is tuned so the primary CTA and trust ticks stay above the fold at 1440×800 — check that before changing hero padding or the `h1` clamp.
|
|
893
|
+
|
|
894
|
+
---
|
|
895
|
+
|
|
872
896
|
## License
|
|
873
897
|
|
|
874
898
|
MIT — [Rishikesh Joshi](https://github.com/rishikeshjoshi)
|
package/dist/bin/cli.js
CHANGED
|
@@ -33,14 +33,20 @@ Usage:
|
|
|
33
33
|
welcome Print getting-started banner for your AI tool (--target)
|
|
34
34
|
|
|
35
35
|
── Knowledge bank commands ──────────────────────────────────
|
|
36
|
-
connect Register a vault or knowledge folder as a source
|
|
37
|
-
sync Read
|
|
38
|
-
disconnect Remove a source from
|
|
39
|
-
status Show
|
|
36
|
+
connect Register a vault or knowledge folder as a source (scoped to cwd project)
|
|
37
|
+
sync Read this project's sources and build the knowledge-bank skill
|
|
38
|
+
disconnect Remove a source from this project's registry
|
|
39
|
+
status Show this project's sources and last sync time
|
|
40
40
|
collect Kavi: scan codebase → Obsidian vault → enrich → sync knowledge bank
|
|
41
41
|
(--vault path, --dry-run, --no-enrich, --no-web, --no-discover,
|
|
42
42
|
--web-limit N, --target cursor|claude|codex|grok|windsurf|agents|all)
|
|
43
43
|
|
|
44
|
+
Scope flags (connect/sync/disconnect/status):
|
|
45
|
+
--project <id> Explicit project id (overrides cwd-derived id)
|
|
46
|
+
--global Use the legacy merged pool (config.sources → ~/.claude/skills/...)
|
|
47
|
+
instead of per-project scoping. Only use when you want
|
|
48
|
+
multiple projects' notes blended together.
|
|
49
|
+
|
|
44
50
|
── Design spec commands ─────────────────────────────────────
|
|
45
51
|
spec show Print design_spec from session state
|
|
46
52
|
spec validate Validate a DesignSpec JSON file (--file) or session
|
|
@@ -61,7 +67,7 @@ Usage:
|
|
|
61
67
|
feedback list List feedback entries for this project (--all for every project)
|
|
62
68
|
feedback export Export rejected + correction JSONL pairs for training
|
|
63
69
|
(--persona <id>, --all, --output <path>, --include-positive)
|
|
64
|
-
feedback submit Opt-in: send anonymized corrections to community store (
|
|
70
|
+
feedback submit Opt-in: send anonymized corrections to a community store (HTTP endpoint)
|
|
65
71
|
(--yes skip prompt, --dry-run preview, --all unsent across projects)
|
|
66
72
|
feedback status Show submit consent, endpoint config, unsent count
|
|
67
73
|
feedback revoke Revoke opt-in consent for community submit
|
|
@@ -175,9 +181,11 @@ Usage:
|
|
|
175
181
|
npx analyzthis_design connect --vault ~/Documents/MyVault
|
|
176
182
|
npx analyzthis_design connect --vault ~/docs --tags design,brand,product
|
|
177
183
|
npx analyzthis_design connect --vault ~/vault --include Design,Research
|
|
178
|
-
npx analyzthis_design sync # sync to Cursor
|
|
179
|
-
npx analyzthis_design sync --target all # sync to all tools
|
|
180
|
-
npx analyzthis_design
|
|
184
|
+
npx analyzthis_design sync # sync to Cursor (project-scoped)
|
|
185
|
+
npx analyzthis_design sync --target all # sync to all tools (project-scoped)
|
|
186
|
+
npx analyzthis_design sync --global # legacy: blend all projects into ~/.claude/skills/
|
|
187
|
+
npx analyzthis_design status # show this project's sources
|
|
188
|
+
npx analyzthis_design status --global # show the legacy merged pool
|
|
181
189
|
npx analyzthis_design disconnect --vault ~/Documents/MyVault
|
|
182
190
|
|
|
183
191
|
npx analyzthis_design session init # start a new session for this repo
|
|
@@ -255,6 +263,7 @@ const vaultVal = getFlag('vault');
|
|
|
255
263
|
const tagsVal = getFlag('tags');
|
|
256
264
|
const includeVal = getFlag('include');
|
|
257
265
|
const projectVal = getFlag('project');
|
|
266
|
+
const globalFlag = flags.includes('--global');
|
|
258
267
|
const allFlag = flags.includes('--all');
|
|
259
268
|
const urlVal = getFlag('url');
|
|
260
269
|
const queryVal = getFlag('query');
|
|
@@ -352,8 +361,10 @@ switch (cmd) {
|
|
|
352
361
|
try {
|
|
353
362
|
const tags = tagsVal ? tagsVal.split(',').map(t => t.trim()) : [];
|
|
354
363
|
const include = includeVal ? includeVal.split(',').map(t => t.trim()) : [];
|
|
355
|
-
const abs = connect({ vaultPath: vaultVal, tags, include });
|
|
364
|
+
const abs = connect({ vaultPath: vaultVal, tags, include, project: projectVal, global: globalFlag });
|
|
365
|
+
const scopeLabel = globalFlag ? 'global (merged across projects)' : (projectVal || `project derived from ${path.resolve(process.cwd())}`);
|
|
356
366
|
console.log(`\n✅ Connected: ${abs}`);
|
|
367
|
+
console.log(` Scope: ${scopeLabel}`);
|
|
357
368
|
if (tags.length) console.log(` Tags filter: ${tags.join(', ')}`);
|
|
358
369
|
if (include.length) console.log(` Folder filter: ${include.join(', ')}`);
|
|
359
370
|
console.log(`\n Run "npx analyzthis_design sync" to build the knowledge bank.\n`);
|
|
@@ -369,7 +380,7 @@ switch (cmd) {
|
|
|
369
380
|
console.error('\n ✗ --vault is required. Example: npx analyzthis_design disconnect --vault ~/Documents/MyVault\n');
|
|
370
381
|
process.exit(1);
|
|
371
382
|
}
|
|
372
|
-
disconnect(vaultVal);
|
|
383
|
+
disconnect(vaultVal, { project: projectVal, global: globalFlag });
|
|
373
384
|
console.log(`\n🗑 Disconnected: ${path.resolve(vaultVal)}\n`);
|
|
374
385
|
break;
|
|
375
386
|
}
|
|
@@ -381,9 +392,10 @@ switch (cmd) {
|
|
|
381
392
|
console.error(`\n ✗ Unknown target "${targetVal}". Choose: ${ALL_TARGET_IDS.join(', ')}, all\n`);
|
|
382
393
|
process.exit(1);
|
|
383
394
|
}
|
|
384
|
-
|
|
395
|
+
const scopeLabel = globalFlag ? 'global (merged across projects)' : (projectVal || `project derived from ${path.resolve(process.cwd())}`);
|
|
396
|
+
console.log(`\n⏳ Syncing knowledge bank (${scopeLabel})...\n`);
|
|
385
397
|
try {
|
|
386
|
-
const result = sync({ targets });
|
|
398
|
+
const result = sync({ targets, project: projectVal, global: globalFlag });
|
|
387
399
|
if (result.message) {
|
|
388
400
|
console.log(` ⚠ ${result.message}\n`);
|
|
389
401
|
} else {
|
|
@@ -399,18 +411,22 @@ switch (cmd) {
|
|
|
399
411
|
}
|
|
400
412
|
|
|
401
413
|
case 'status': {
|
|
402
|
-
const
|
|
403
|
-
if (!
|
|
404
|
-
|
|
414
|
+
const view = status({ project: projectVal, global: globalFlag });
|
|
415
|
+
if (!view.sources || view.sources.length === 0) {
|
|
416
|
+
const scopeHint = view.scope === 'global'
|
|
417
|
+
? 'Run: npx analyzthis_design connect --vault /path/to/vault --global'
|
|
418
|
+
: 'Run: npx analyzthis_design collect (from inside the project) or npx analyzthis_design connect --vault /path/to/vault';
|
|
419
|
+
console.log(`\n No knowledge sources connected for this ${view.scope} scope.\n ${scopeHint}\n`);
|
|
405
420
|
} else {
|
|
406
|
-
|
|
407
|
-
|
|
421
|
+
const scopeLabel = view.scope === 'global' ? 'global (merged across projects)' : `project: ${view.projectId}`;
|
|
422
|
+
console.log(`\n📚 Knowledge bank sources (${view.sources.length}) — ${scopeLabel}:\n`);
|
|
423
|
+
for (const s of view.sources) {
|
|
408
424
|
console.log(` • ${s.path}`);
|
|
409
|
-
if (s.tags.length) console.log(` Tags: ${s.tags.join(', ')}`);
|
|
410
|
-
if (s.include.length) console.log(` Folders: ${s.include.join(', ')}`);
|
|
425
|
+
if (s.tags && s.tags.length) console.log(` Tags: ${s.tags.join(', ')}`);
|
|
426
|
+
if (s.include && s.include.length) console.log(` Folders: ${s.include.join(', ')}`);
|
|
411
427
|
console.log(` Added: ${s.addedAt}`);
|
|
412
428
|
}
|
|
413
|
-
if (
|
|
429
|
+
if (view.lastSync) console.log(`\n Last sync: ${view.lastSync}`);
|
|
414
430
|
console.log('');
|
|
415
431
|
}
|
|
416
432
|
break;
|
package/dist/lib/collect.js
CHANGED
|
@@ -767,22 +767,26 @@ async function collect(opts = {}) {
|
|
|
767
767
|
}
|
|
768
768
|
}
|
|
769
769
|
|
|
770
|
-
// Auto-connect discovered sources + Kavi vault, then sync knowledge bank
|
|
771
|
-
|
|
770
|
+
// Auto-connect discovered sources + Kavi vault, then sync knowledge bank.
|
|
771
|
+
// Scoped to the current project so this project's vault never bleeds into
|
|
772
|
+
// another project's knowledge bank (the v2.2 project-scoping fix).
|
|
773
|
+
console.log(`\n⏳ Wiring knowledge bank (project: ${projectId})...`);
|
|
772
774
|
const connectResult = discoverSources
|
|
773
775
|
? connectDiscoveredSources({
|
|
774
776
|
discoveries: sourceDiscoveries,
|
|
775
777
|
kaviVaultPath: vaultPath,
|
|
776
778
|
config,
|
|
777
779
|
autoConnect: config.collect?.auto_connect_discovered !== false,
|
|
780
|
+
project: projectId,
|
|
781
|
+
cwd,
|
|
778
782
|
})
|
|
779
783
|
: { connected: [], skipped: [] };
|
|
780
784
|
if (connectResult.connected.length > 0) {
|
|
781
785
|
console.log(` Auto-connected ${connectResult.connected.length} source(s):`);
|
|
782
786
|
for (const p of connectResult.connected) console.log(` • ${p}`);
|
|
783
787
|
}
|
|
784
|
-
knowledge.connect({ vaultPath, tags: [], include: [] });
|
|
785
|
-
const syncResult = knowledge.sync({ targets });
|
|
788
|
+
knowledge.connect({ vaultPath, tags: [], include: [], project: projectId, cwd });
|
|
789
|
+
const syncResult = knowledge.sync({ targets, project: projectId, cwd });
|
|
786
790
|
cache.invalidatePrefix('kb:');
|
|
787
791
|
|
|
788
792
|
if (syncResult.message) {
|
package/dist/lib/knowledge.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
const fs
|
|
5
|
-
const path
|
|
6
|
-
const os
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const crypto = require('crypto');
|
|
7
8
|
|
|
8
9
|
const CONFIG_DIR = path.join(os.homedir(), '.analyzthis_design');
|
|
9
10
|
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
@@ -32,9 +33,14 @@ const CATEGORIES = {
|
|
|
32
33
|
// ─── Config helpers ──────────────────────────────────────────────────────────
|
|
33
34
|
|
|
34
35
|
function loadConfig() {
|
|
35
|
-
if (!fs.existsSync(CONFIG_FILE)) return { sources: [] };
|
|
36
|
-
try {
|
|
37
|
-
|
|
36
|
+
if (!fs.existsSync(CONFIG_FILE)) return { sources: [], projects: {} };
|
|
37
|
+
try {
|
|
38
|
+
const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
39
|
+
if (!cfg.sources) cfg.sources = [];
|
|
40
|
+
if (!cfg.projects) cfg.projects = {};
|
|
41
|
+
return cfg;
|
|
42
|
+
}
|
|
43
|
+
catch { return { sources: [], projects: {} }; }
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
function saveConfig(config) {
|
|
@@ -42,6 +48,79 @@ function saveConfig(config) {
|
|
|
42
48
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
43
49
|
}
|
|
44
50
|
|
|
51
|
+
// ─── Project scoping ─────────────────────────────────────────────────────────
|
|
52
|
+
// Knowledge sources are scoped per project by default. Each project's sources
|
|
53
|
+
// live under config.projects[projectId].sources and the master knowledge-bank
|
|
54
|
+
// is written into that project's local skills directory (e.g.
|
|
55
|
+
// <projectRoot>/.claude/skills/knowledge-bank/SKILL.md), so invoking a skill
|
|
56
|
+
// from one project never reads another project's vaults.
|
|
57
|
+
//
|
|
58
|
+
// Pass { global: true } (or --global on the CLI) to opt into the legacy merged
|
|
59
|
+
// behavior: read config.sources and write into ~/.claude/skills/... This is the
|
|
60
|
+
// only path that ever blends multiple projects' notes together.
|
|
61
|
+
|
|
62
|
+
function resolveProjectScope({ project, global, cwd } = {}) {
|
|
63
|
+
// Explicit --global wins: read the legacy merged pool, write to global skills dirs.
|
|
64
|
+
if (global) return { scope: 'global', projectId: null, projectRoot: null };
|
|
65
|
+
|
|
66
|
+
// --project <id> (or a caller-provided project id): use that id, rooted at cwd.
|
|
67
|
+
let projectId = project;
|
|
68
|
+
let projectRoot = path.resolve(cwd || process.cwd());
|
|
69
|
+
|
|
70
|
+
// Default: auto-derive a project id from cwd, the same way session.js does.
|
|
71
|
+
if (!projectId) {
|
|
72
|
+
const abs = path.resolve(cwd || process.cwd());
|
|
73
|
+
const slug = path.basename(abs).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'project';
|
|
74
|
+
const hash = crypto.createHash('sha1').update(abs).digest('hex').slice(0, 8);
|
|
75
|
+
projectId = `${slug}-${hash}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { scope: 'project', projectId, projectRoot };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Return the sources array for a resolved scope, creating the project entry
|
|
82
|
+
// on first use. For global scope, returns the legacy top-level config.sources.
|
|
83
|
+
function scopedSources(config, scopeInfo, { create = false } = {}) {
|
|
84
|
+
if (scopeInfo.scope === 'global') return config.sources;
|
|
85
|
+
if (!config.projects[scopeInfo.projectId] && create) {
|
|
86
|
+
config.projects[scopeInfo.projectId] = { sources: [], addedAt: new Date().toISOString() };
|
|
87
|
+
}
|
|
88
|
+
const proj = config.projects[scopeInfo.projectId];
|
|
89
|
+
return proj ? proj.sources : null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Per-tool skill roots, expressed relative to a project root, mirroring the
|
|
93
|
+
// global TARGETS layout in platforms.js. Used when writing project-local
|
|
94
|
+
// knowledge-bank files so each project's skills load only inside that project.
|
|
95
|
+
const PROJECT_REL_TARGETS = [
|
|
96
|
+
{ id: 'cursor', rel: ['.cursor', 'skills'], layout: 'dir' },
|
|
97
|
+
{ id: 'claude', rel: ['.claude', 'skills'], layout: 'dir' },
|
|
98
|
+
{ id: 'claude-cmds', rel: ['.claude', 'commands'], layout: 'flat' },
|
|
99
|
+
{ id: 'codex', rel: ['.codex', 'skills'], layout: 'dir' },
|
|
100
|
+
{ id: 'grok', rel: ['.grok', 'skills'], layout: 'dir' },
|
|
101
|
+
{ id: 'windsurf', rel: ['.codeium', 'windsurf', 'skills'], layout: 'dir' },
|
|
102
|
+
{ id: 'agents', rel: ['.agents', 'skills'], layout: 'dir' },
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
function projectTargets(projectRoot, requestedTargets) {
|
|
106
|
+
// requestedTargets is the resolved list from resolveTargets (e.g. ['cursor'],
|
|
107
|
+
// ['claude'], or ALL_TARGET_IDS). claude-cmds is always paired with claude.
|
|
108
|
+
const want = new Set(requestedTargets);
|
|
109
|
+
const out = [];
|
|
110
|
+
for (const t of PROJECT_REL_TARGETS) {
|
|
111
|
+
if (t.id === 'claude-cmds') {
|
|
112
|
+
if (want.has('claude')) out.push(t);
|
|
113
|
+
} else if (want.has(t.id)) {
|
|
114
|
+
out.push(t);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return out.map((t) => ({
|
|
118
|
+
id: t.id,
|
|
119
|
+
root: path.join(projectRoot, ...t.rel),
|
|
120
|
+
layout: t.layout,
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
|
|
45
124
|
// ─── Vault reading ───────────────────────────────────────────────────────────
|
|
46
125
|
|
|
47
126
|
// Recursively collect all .md files under a directory
|
|
@@ -103,29 +182,54 @@ function categorize(title, tags, body) {
|
|
|
103
182
|
|
|
104
183
|
/**
|
|
105
184
|
* Register a vault or folder as a knowledge source.
|
|
185
|
+
*
|
|
186
|
+
* By default the source is scoped to the project derived from cwd, so it only
|
|
187
|
+
* feeds critiques run from that project. Pass { global: true } to register it
|
|
188
|
+
* in the legacy merged pool (config.sources) instead.
|
|
189
|
+
*
|
|
106
190
|
* Options:
|
|
107
191
|
* include — array of sub-folder prefixes to include, e.g. ['Design', 'Brand']
|
|
108
192
|
* tags — array of tags to filter by, e.g. ['ux', 'design', 'brand']
|
|
193
|
+
* project — explicit project id (overrides cwd-derived id)
|
|
194
|
+
* global — when true, register in the legacy merged pool instead
|
|
195
|
+
* cwd — working directory used to derive the project id (default: process.cwd())
|
|
109
196
|
*/
|
|
110
|
-
function connect({ vaultPath, include = [], tags = [] }) {
|
|
197
|
+
function connect({ vaultPath, include = [], tags = [], project, global = false, cwd } = {}) {
|
|
111
198
|
const abs = path.resolve(vaultPath);
|
|
112
199
|
if (!fs.existsSync(abs)) throw new Error(`Path does not exist: ${abs}`);
|
|
113
200
|
|
|
114
201
|
const config = loadConfig();
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
202
|
+
const scopeInfo = resolveProjectScope({ project, global, cwd });
|
|
203
|
+
const sources = scopedSources(config, scopeInfo, { create: true });
|
|
204
|
+
// Replace any existing entry with the same path within this scope
|
|
205
|
+
const filtered = sources.filter(s => s.path !== abs);
|
|
206
|
+
filtered.push({ path: abs, include, tags, addedAt: new Date().toISOString() });
|
|
207
|
+
if (scopeInfo.scope === 'global') {
|
|
208
|
+
config.sources = filtered;
|
|
209
|
+
} else {
|
|
210
|
+
config.projects[scopeInfo.projectId].sources = filtered;
|
|
211
|
+
}
|
|
118
212
|
saveConfig(config);
|
|
119
213
|
return abs;
|
|
120
214
|
}
|
|
121
215
|
|
|
122
216
|
/**
|
|
123
217
|
* Remove a vault/folder from the knowledge sources list.
|
|
218
|
+
* Accepts the same { project, global, cwd } options as connect() to target
|
|
219
|
+
* the right scope.
|
|
124
220
|
*/
|
|
125
|
-
function disconnect(vaultPath) {
|
|
221
|
+
function disconnect(vaultPath, opts = {}) {
|
|
126
222
|
const abs = path.resolve(vaultPath);
|
|
127
223
|
const config = loadConfig();
|
|
128
|
-
|
|
224
|
+
const scopeInfo = resolveProjectScope(opts);
|
|
225
|
+
const sources = scopedSources(config, scopeInfo);
|
|
226
|
+
if (!sources) return; // nothing in this scope yet
|
|
227
|
+
const filtered = sources.filter(s => s.path !== abs);
|
|
228
|
+
if (scopeInfo.scope === 'global') {
|
|
229
|
+
config.sources = filtered;
|
|
230
|
+
} else if (config.projects[scopeInfo.projectId]) {
|
|
231
|
+
config.projects[scopeInfo.projectId].sources = filtered;
|
|
232
|
+
}
|
|
129
233
|
saveConfig(config);
|
|
130
234
|
}
|
|
131
235
|
|
|
@@ -133,19 +237,38 @@ function disconnect(vaultPath) {
|
|
|
133
237
|
* Read all connected sources, filter notes, build knowledge-bank.md,
|
|
134
238
|
* and copy it to all requested target AI tool directories.
|
|
135
239
|
*
|
|
136
|
-
*
|
|
240
|
+
* By default the bank is scoped to the project derived from cwd: it reads
|
|
241
|
+
* only that project's sources and writes into that project's local skills
|
|
242
|
+
* directory (<projectRoot>/.claude/skills/knowledge-bank/SKILL.md, etc.), so
|
|
243
|
+
* invoking a skill from one project never pulls in another project's vaults.
|
|
244
|
+
*
|
|
245
|
+
* Pass { global: true } (or --global) to opt into the legacy merged behavior:
|
|
246
|
+
* read config.sources and write into ~/.claude/skills/... so multiple
|
|
247
|
+
* projects' notes blend together. Use this only when you deliberately want
|
|
248
|
+
* cross-project blending.
|
|
249
|
+
*
|
|
250
|
+
* targets — array of tool names: 'cursor', 'claude', 'codex', 'grok',
|
|
251
|
+
* 'windsurf', 'agents', or ['all']
|
|
252
|
+
* project — explicit project id (overrides cwd-derived id)
|
|
253
|
+
* global — when true, use the legacy merged pool
|
|
254
|
+
* cwd — working directory used to derive the project id
|
|
137
255
|
*/
|
|
138
|
-
function sync({ targets = ['cursor'] } = {}) {
|
|
256
|
+
function sync({ targets = ['cursor'], project, global = false, cwd } = {}) {
|
|
139
257
|
const config = loadConfig();
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
258
|
+
const scopeInfo = resolveProjectScope({ project, global, cwd });
|
|
259
|
+
const sources = scopedSources(config, scopeInfo) || [];
|
|
260
|
+
|
|
261
|
+
if (!sources || sources.length === 0) {
|
|
262
|
+
const hint = scopeInfo.scope === 'global'
|
|
263
|
+
? 'No sources connected. Run: npx analyzthis_design connect --vault /path/to/vault --global'
|
|
264
|
+
: `No sources connected for project "${scopeInfo.projectId}". Run: npx analyzthis_design collect (from inside the project) or npx analyzthis_design connect --vault /path/to/vault`;
|
|
265
|
+
return { synced: 0, message: hint };
|
|
143
266
|
}
|
|
144
267
|
|
|
145
268
|
const sections = { prd: [], brand: [], product: [], design: [], research: [], tech: [], web: [], other: [] };
|
|
146
269
|
let totalFiles = 0;
|
|
147
270
|
|
|
148
|
-
for (const source of
|
|
271
|
+
for (const source of sources) {
|
|
149
272
|
const files = readMarkdownFiles(source.path);
|
|
150
273
|
|
|
151
274
|
for (const filePath of files) {
|
|
@@ -184,7 +307,7 @@ function sync({ targets = ['cursor'] } = {}) {
|
|
|
184
307
|
} catch { /* no session / research available — fine */ }
|
|
185
308
|
|
|
186
309
|
// Build the knowledge-bank markdown
|
|
187
|
-
const sourceList =
|
|
310
|
+
const sourceList = sources.map(s => s.path).join(', ');
|
|
188
311
|
const date = new Date().toISOString().split('T')[0];
|
|
189
312
|
|
|
190
313
|
// PRD/stories listed first — ux-story-gate reads this section in Phase 0
|
|
@@ -199,6 +322,10 @@ function sync({ targets = ['cursor'] } = {}) {
|
|
|
199
322
|
{ key: 'other', heading: '## Additional Context' },
|
|
200
323
|
];
|
|
201
324
|
|
|
325
|
+
const scopeLabel = scopeInfo.scope === 'global'
|
|
326
|
+
? 'global (merged across projects)'
|
|
327
|
+
: `project: ${scopeInfo.projectId}`;
|
|
328
|
+
|
|
202
329
|
let md = `---
|
|
203
330
|
name: knowledge-bank
|
|
204
331
|
description: Personal knowledge bank — takes precedence over all built-in persona defaults.
|
|
@@ -208,6 +335,7 @@ disable-model-invocation: true
|
|
|
208
335
|
# Knowledge Bank
|
|
209
336
|
|
|
210
337
|
> Last synced: ${date}
|
|
338
|
+
> Scope: ${scopeLabel}
|
|
211
339
|
> Sources: ${sourceList}
|
|
212
340
|
> Files loaded: ${totalFiles}
|
|
213
341
|
|
|
@@ -232,53 +360,63 @@ disable-model-invocation: true
|
|
|
232
360
|
md += `_No matching files found. Check your --tags or --include filters, or remove filters to include all notes._\n`;
|
|
233
361
|
}
|
|
234
362
|
|
|
235
|
-
// Write to the package's own skills/knowledge-bank/SKILL.md
|
|
363
|
+
// Write to the package's own skills/knowledge-bank/SKILL.md (source of truth)
|
|
236
364
|
fs.mkdirSync(path.dirname(KNOWLEDGE_SKILL), { recursive: true });
|
|
237
365
|
fs.writeFileSync(KNOWLEDGE_SKILL, md);
|
|
238
366
|
|
|
239
367
|
// Build per-persona knowledge slices (priority + fallback)
|
|
240
368
|
try {
|
|
241
369
|
const session = require('./session');
|
|
242
|
-
const projectId = session.getProjectId();
|
|
370
|
+
const projectId = scopeInfo.scope === 'project' ? scopeInfo.projectId : session.getProjectId();
|
|
243
371
|
writePersonaSlices(sections, sectionDefs, projectId);
|
|
244
372
|
} catch {
|
|
245
373
|
// sessions unavailable — skip slices
|
|
246
374
|
}
|
|
247
375
|
|
|
248
|
-
// Copy knowledge-bank into every requested platform
|
|
376
|
+
// Copy knowledge-bank into every requested platform.
|
|
377
|
+
// Project-scoped → write into <projectRoot>/.{tool}/skills/... (project-local,
|
|
378
|
+
// loaded only when the skill is invoked from inside that project).
|
|
379
|
+
// Global-scoped → write into ~/.{tool}/skills/... (legacy merged behavior).
|
|
249
380
|
const { TARGETS, resolveTargets } = require('./platforms');
|
|
250
381
|
const copiedTo = [];
|
|
251
382
|
const resolved = targets.includes('all')
|
|
252
383
|
? resolveTargets('all')
|
|
253
384
|
: targets.filter((t) => TARGETS[t]);
|
|
254
385
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
386
|
+
const destinations = scopeInfo.scope === 'project'
|
|
387
|
+
? projectTargets(scopeInfo.projectRoot, resolved)
|
|
388
|
+
: resolved.map((id) => {
|
|
389
|
+
const t = TARGETS[id];
|
|
390
|
+
const list = [{ root: t.root, layout: t.layout, id }];
|
|
391
|
+
if (t.also) list.push({ root: t.also.root, layout: t.also.layout, id: `${id}-cmds` });
|
|
392
|
+
return list;
|
|
393
|
+
}).flat();
|
|
394
|
+
|
|
395
|
+
for (const dest of destinations) {
|
|
396
|
+
fs.mkdirSync(dest.root, { recursive: true });
|
|
397
|
+
if (dest.layout === 'dir') {
|
|
398
|
+
const skillDir = path.join(dest.root, 'knowledge-bank');
|
|
399
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
400
|
+
fs.copyFileSync(KNOWLEDGE_SKILL, path.join(skillDir, 'SKILL.md'));
|
|
401
|
+
} else {
|
|
402
|
+
fs.copyFileSync(KNOWLEDGE_SKILL, path.join(dest.root, 'knowledge-bank.md'));
|
|
270
403
|
}
|
|
404
|
+
copiedTo.push(`${dest.id} → ${dest.root}`);
|
|
271
405
|
}
|
|
272
406
|
|
|
273
|
-
// Persist lastSync timestamp
|
|
274
|
-
|
|
407
|
+
// Persist lastSync timestamp on the scoped config entry
|
|
408
|
+
if (scopeInfo.scope === 'global') {
|
|
409
|
+
config.lastSync = new Date().toISOString();
|
|
410
|
+
} else if (config.projects[scopeInfo.projectId]) {
|
|
411
|
+
config.projects[scopeInfo.projectId].lastSync = new Date().toISOString();
|
|
412
|
+
}
|
|
275
413
|
saveConfig(config);
|
|
276
414
|
|
|
277
415
|
// Knowledge bank content just changed for every project — drop any cached
|
|
278
416
|
// knowledge-bank slices so the next run re-reads the fresh sync.
|
|
279
417
|
try { require('./cache').invalidatePrefix('kb:'); } catch { /* cache module unavailable — fine */ }
|
|
280
418
|
|
|
281
|
-
return { synced: totalFiles, copiedTo };
|
|
419
|
+
return { synced: totalFiles, copiedTo, scope: scopeLabel };
|
|
282
420
|
}
|
|
283
421
|
|
|
284
422
|
function loadManifest(id) {
|
|
@@ -376,10 +514,24 @@ function getPersonaSliceForPrompt(state, personaId) {
|
|
|
376
514
|
}
|
|
377
515
|
|
|
378
516
|
/**
|
|
379
|
-
* Return
|
|
517
|
+
* Return the config view for the resolved scope.
|
|
518
|
+
* Accepts the same { project, global, cwd } options as connect/sync.
|
|
519
|
+
* Returns { scope, projectId, sources, lastSync } so the CLI can print
|
|
520
|
+
* a scope-aware status without leaking other projects' sources.
|
|
380
521
|
*/
|
|
381
|
-
function status() {
|
|
382
|
-
|
|
522
|
+
function status(opts = {}) {
|
|
523
|
+
const config = loadConfig();
|
|
524
|
+
const scopeInfo = resolveProjectScope(opts);
|
|
525
|
+
const sources = scopedSources(config, scopeInfo) || [];
|
|
526
|
+
const lastSync = scopeInfo.scope === 'global'
|
|
527
|
+
? config.lastSync
|
|
528
|
+
: (config.projects[scopeInfo.projectId] && config.projects[scopeInfo.projectId].lastSync);
|
|
529
|
+
return {
|
|
530
|
+
scope: scopeInfo.scope,
|
|
531
|
+
projectId: scopeInfo.projectId,
|
|
532
|
+
sources,
|
|
533
|
+
lastSync,
|
|
534
|
+
};
|
|
383
535
|
}
|
|
384
536
|
|
|
385
|
-
module.exports = { connect, disconnect, sync, status, buildPersonaSlice, writePersonaSlices, readPersonaSlice, getPersonaSliceForPrompt, KNOWLEDGE_SLICE_ROOT };
|
|
537
|
+
module.exports = { connect, disconnect, sync, status, buildPersonaSlice, writePersonaSlices, readPersonaSlice, getPersonaSliceForPrompt, KNOWLEDGE_SLICE_ROOT, resolveProjectScope };
|
|
@@ -449,9 +449,12 @@ function writeSourceManifestNotes({ discoveries, vaultPath, dryRun }) {
|
|
|
449
449
|
}
|
|
450
450
|
|
|
451
451
|
/**
|
|
452
|
-
* Auto-connect discovered sources (high/medium confidence) into
|
|
452
|
+
* Auto-connect discovered sources (high/medium confidence) into the project's
|
|
453
|
+
* scoped sources (config.projects[projectId].sources). Pass { project, cwd }
|
|
454
|
+
* so the scope matches the collect() call site; falls back to cwd-derived
|
|
455
|
+
* scoping inside knowledge.connect when omitted.
|
|
453
456
|
*/
|
|
454
|
-
function connectDiscoveredSources({ discoveries, kaviVaultPath, config, autoConnect = true }) {
|
|
457
|
+
function connectDiscoveredSources({ discoveries, kaviVaultPath, config, autoConnect = true, project, cwd }) {
|
|
455
458
|
const connected = [];
|
|
456
459
|
const skipped = [];
|
|
457
460
|
if (!autoConnect) return { connected, skipped };
|
|
@@ -469,7 +472,7 @@ function connectDiscoveredSources({ discoveries, kaviVaultPath, config, autoConn
|
|
|
469
472
|
continue;
|
|
470
473
|
}
|
|
471
474
|
try {
|
|
472
|
-
knowledge.connect({ vaultPath: d.path, tags: [], include: [] });
|
|
475
|
+
knowledge.connect({ vaultPath: d.path, tags: [], include: [], project, cwd });
|
|
473
476
|
connected.push(d.path);
|
|
474
477
|
} catch (err) {
|
|
475
478
|
skipped.push({ path: d.path, reason: err.message });
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "analyzthis_design",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "8 AI design personas — 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.",
|
|
3
|
+
"version": "2.2.0",
|
|
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",
|
|
7
7
|
"cursor-skill",
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
"dist/",
|
|
34
34
|
"skills/",
|
|
35
35
|
"agents/",
|
|
36
|
-
"supabase/",
|
|
37
36
|
".github/",
|
|
38
37
|
"scripts/validate-csvs.js",
|
|
39
38
|
"README.md",
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"deliberation": {
|
|
3
|
-
"satisfaction_threshold": 0.4,
|
|
4
|
-
"max_rounds": 3,
|
|
5
|
-
"parallel_pairs": [["noor", "anuj"], ["meera", "priya"]],
|
|
6
|
-
"objection_token_cap": 600,
|
|
7
|
-
"prior_output_chars_first": 800,
|
|
8
|
-
"prior_output_chars_rebuttal": 400,
|
|
9
|
-
"prior_output_chars_produce": 1200,
|
|
10
|
-
"context_pack_chars_objection": 1500,
|
|
11
|
-
"context_pack_chars_produce": 3000,
|
|
12
|
-
"escalate_to_raj_after_round": 2,
|
|
13
|
-
"default_mode": "adversarial"
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
-- Persona feedback — anonymous community corrections for analyzthis_design
|
|
2
|
-
-- Run in Supabase SQL editor or via supabase db push
|
|
3
|
-
|
|
4
|
-
create table if not exists public.persona_feedback (
|
|
5
|
-
id uuid primary key default gen_random_uuid(),
|
|
6
|
-
created_at timestamptz not null default now(),
|
|
7
|
-
|
|
8
|
-
install_id text not null,
|
|
9
|
-
package_version text,
|
|
10
|
-
feedback_id text,
|
|
11
|
-
|
|
12
|
-
persona text not null,
|
|
13
|
-
satisfied boolean not null default false,
|
|
14
|
-
rating smallint check (rating is null or (rating >= 1 and rating <= 5)),
|
|
15
|
-
tags text[] not null default '{}',
|
|
16
|
-
|
|
17
|
-
user_comment text,
|
|
18
|
-
assistant_rejected text,
|
|
19
|
-
assistant_preferred text,
|
|
20
|
-
task_summary text,
|
|
21
|
-
problem_type text,
|
|
22
|
-
mode text,
|
|
23
|
-
recorded_at timestamptz
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
create index if not exists persona_feedback_persona_idx on public.persona_feedback (persona);
|
|
27
|
-
create index if not exists persona_feedback_created_at_idx on public.persona_feedback (created_at desc);
|
|
28
|
-
create index if not exists persona_feedback_tags_idx on public.persona_feedback using gin (tags);
|
|
29
|
-
|
|
30
|
-
alter table public.persona_feedback enable row level security;
|
|
31
|
-
|
|
32
|
-
-- Anonymous clients may INSERT only (no read/update/delete for anon)
|
|
33
|
-
drop policy if exists "anon_insert_persona_feedback" on public.persona_feedback;
|
|
34
|
-
create policy "anon_insert_persona_feedback"
|
|
35
|
-
on public.persona_feedback
|
|
36
|
-
for insert
|
|
37
|
-
to anon
|
|
38
|
-
with check (
|
|
39
|
-
char_length(coalesce(user_comment, '')) <= 2000
|
|
40
|
-
and char_length(coalesce(assistant_rejected, '')) <= 4000
|
|
41
|
-
and char_length(coalesce(assistant_preferred, '')) <= 4000
|
|
42
|
-
and persona ~ '^[a-z][a-z0-9_-]{0,31}$'
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
-- Service role (dashboard) can read everything — use Supabase dashboard or service key
|
|
46
|
-
drop policy if exists "service_read_persona_feedback" on public.persona_feedback;
|
|
47
|
-
create policy "service_read_persona_feedback"
|
|
48
|
-
on public.persona_feedback
|
|
49
|
-
for select
|
|
50
|
-
to service_role
|
|
51
|
-
using (true);
|
|
52
|
-
|
|
53
|
-
comment on table public.persona_feedback is
|
|
54
|
-
'Anonymous opt-in persona correction data from analyzthis_design CLI (feedback submit).';
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"deliberation": {
|
|
3
|
-
"satisfaction_threshold": 0.4,
|
|
4
|
-
"max_rounds": 3,
|
|
5
|
-
"parallel_pairs": [["noor", "anuj"], ["meera", "priya"]],
|
|
6
|
-
"objection_token_cap": 600,
|
|
7
|
-
"prior_output_chars_first": 800,
|
|
8
|
-
"prior_output_chars_rebuttal": 400,
|
|
9
|
-
"prior_output_chars_produce": 1200,
|
|
10
|
-
"context_pack_chars_objection": 1500,
|
|
11
|
-
"context_pack_chars_produce": 3000,
|
|
12
|
-
"escalate_to_raj_after_round": 2,
|
|
13
|
-
"default_mode": "adversarial"
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
-- Persona feedback — anonymous community corrections for analyzthis_design
|
|
2
|
-
-- Run in Supabase SQL editor or via supabase db push
|
|
3
|
-
|
|
4
|
-
create table if not exists public.persona_feedback (
|
|
5
|
-
id uuid primary key default gen_random_uuid(),
|
|
6
|
-
created_at timestamptz not null default now(),
|
|
7
|
-
|
|
8
|
-
install_id text not null,
|
|
9
|
-
package_version text,
|
|
10
|
-
feedback_id text,
|
|
11
|
-
|
|
12
|
-
persona text not null,
|
|
13
|
-
satisfied boolean not null default false,
|
|
14
|
-
rating smallint check (rating is null or (rating >= 1 and rating <= 5)),
|
|
15
|
-
tags text[] not null default '{}',
|
|
16
|
-
|
|
17
|
-
user_comment text,
|
|
18
|
-
assistant_rejected text,
|
|
19
|
-
assistant_preferred text,
|
|
20
|
-
task_summary text,
|
|
21
|
-
problem_type text,
|
|
22
|
-
mode text,
|
|
23
|
-
recorded_at timestamptz
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
create index if not exists persona_feedback_persona_idx on public.persona_feedback (persona);
|
|
27
|
-
create index if not exists persona_feedback_created_at_idx on public.persona_feedback (created_at desc);
|
|
28
|
-
create index if not exists persona_feedback_tags_idx on public.persona_feedback using gin (tags);
|
|
29
|
-
|
|
30
|
-
alter table public.persona_feedback enable row level security;
|
|
31
|
-
|
|
32
|
-
-- Anonymous clients may INSERT only (no read/update/delete for anon)
|
|
33
|
-
drop policy if exists "anon_insert_persona_feedback" on public.persona_feedback;
|
|
34
|
-
create policy "anon_insert_persona_feedback"
|
|
35
|
-
on public.persona_feedback
|
|
36
|
-
for insert
|
|
37
|
-
to anon
|
|
38
|
-
with check (
|
|
39
|
-
char_length(coalesce(user_comment, '')) <= 2000
|
|
40
|
-
and char_length(coalesce(assistant_rejected, '')) <= 4000
|
|
41
|
-
and char_length(coalesce(assistant_preferred, '')) <= 4000
|
|
42
|
-
and persona ~ '^[a-z][a-z0-9_-]{0,31}$'
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
-- Service role (dashboard) can read everything — use Supabase dashboard or service key
|
|
46
|
-
drop policy if exists "service_read_persona_feedback" on public.persona_feedback;
|
|
47
|
-
create policy "service_read_persona_feedback"
|
|
48
|
-
on public.persona_feedback
|
|
49
|
-
for select
|
|
50
|
-
to service_role
|
|
51
|
-
using (true);
|
|
52
|
-
|
|
53
|
-
comment on table public.persona_feedback is
|
|
54
|
-
'Anonymous opt-in persona correction data from analyzthis_design CLI (feedback submit).';
|