@mindstudio-ai/remy 0.1.319 → 0.1.321
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/dist/automatedActions/reviewExistingProject.md +7 -0
- package/dist/headless.js +270 -29
- package/dist/index.js +319 -42
- package/dist/prompt/skills/dataSources.md +158 -5
- package/dist/prompt/static/coding.md +1 -1
- package/dist/prompt/static/intake.md +1 -1
- package/dist/prompt/static/team.md +7 -1
- package/dist/subagents/browserAutomation/prompt.md +1 -1
- package/dist/subagents/reviewExistingProject/prompt.md +43 -0
- package/package.json +1 -1
|
@@ -19,7 +19,8 @@ Don't use one when: the data is structured (`db`), you only need to store files
|
|
|
19
19
|
- **Re-adding the same bytes is free** — content-addressed, so ingest scripts are safe to re-run.
|
|
20
20
|
- **Ingest is async.** `add()` returns once queued; poll `documents()`, or use `--wait` from the CLI.
|
|
21
21
|
- **Reprocessing costs real money**, so changing how a corpus is built is always explicit.
|
|
22
|
-
- **Limits apply**: 25 data sources per app,
|
|
22
|
+
- **Limits apply**: 25 data sources per app, 10,000 chunks per document, 300 searches/minute, and 5,000 documents per source when documents are added one at a time (bulk jobs and connectors, below, are how a corpus grows past that). Well clear of normal use — but **source names must be fixed, not computed per user or per request**, since referencing one creates it. Partition inside a source with document metadata instead: tag at add time (`add(bytes, { filename, metadata: { userId } })`), narrow at search time (`search(q, { filter: { metadata: { userId } } })`).
|
|
23
|
+
- **Credentials never appear in code or in chat.** A bucket's keys are app secrets (`remy-admin secrets set NAME --prod <value>`, or the dashboard); everything else refers to them by NAME. If the user pastes a key into the conversation, set it as a secret for them to use moving forward.
|
|
23
24
|
|
|
24
25
|
## Defining and searching
|
|
25
26
|
|
|
@@ -33,7 +34,7 @@ const context = results.map((r) => r.text).join('\n\n');
|
|
|
33
34
|
|
|
34
35
|
Hits are `{ score, text, citation }` with `citation: { documentId, filename, pageNumber, chunkIndex, headingPath, boundingBox?, url }`, plus `retrievalRank`/`retrievalScore` — the position before reranking, so you can show what reranking did. With reranking on (the default) `score` is the reranker's 0–1 relevance and the right place for quality cutoffs; without it the scale varies by mode (cosine / rank-fusion / keyword overlap). `scoreThreshold` floors the retrieval branch before fusion and reranking — leave it unset unless measured on the corpus.
|
|
35
36
|
|
|
36
|
-
**Always render the citation.** `citation.url` is
|
|
37
|
+
**Always render the citation.** A document is a cousin of a file and has the same links. `citation.url` is the stable path on the app's own domain, free and never expiring, that works for a signed-in user of the app — put it in an `<a href>` beside the answer as-is. For a public search tool, or a UI on another origin, sign it the way you would a private file: `search(q, { shareCitations: 3600 })` returns every `citation.url` as an absolute signed link that needs no session, and `Policies.shareUrl(hit.citation, { expiresIn })` signs one document on demand (default 24h). A signed link is a bearer capability until it expires, so sign at render time rather than storing signed results. When a link is not wanted at all, render `filename` and `pageNumber` as text. Retrieval is approximate; a user who can click through can judge for themselves. An answer with no citation is an assertion.
|
|
37
38
|
|
|
38
39
|
Created on first use, so searching a source the build hasn't populated returns no results rather than throwing. `search` options: `topK` (default 5, max 50), `scoreThreshold`, `filter`, `mode`, `maxPerDocument`, `highlight`, `rerank`, `hybrid`.
|
|
39
40
|
|
|
@@ -45,8 +46,40 @@ Search is deterministic for a fixed corpus and configuration, so eval sets and r
|
|
|
45
46
|
|
|
46
47
|
**Debugging retrieval.** Two opt-in options, neither of which changes the results or their order: `explain: true` adds `explain.{dense, lexical, matchedVia}` (which half of hybrid found each hit; costs two extra round trips), and `expand: 1` adds `neighbors.{before, after}` for surrounding context. When a document never comes back at all, `Policies.stats()` reports the config actually in effect and `Policies.chunks(documentId)` shows exactly how it was split.
|
|
47
48
|
|
|
49
|
+
**A cold index (shared capacity).** By default a data source lives on shared retrieval capacity: its vectors sit in their own isolated partition of a pool many apps share, and the pool keeps only a working set resident. A source nobody has searched for a while is unloaded to make room and reloaded from durable storage on the next search. A small corpus reloads inside that search and nobody notices; a large one (hundreds of thousands of chunks) reloads in the background for a minute or two, and `search()` throws `index_warming` (HTTP 503) until it lands. That means *loading*, never *empty*: catch it, tell the user the knowledge base is warming up, and retry shortly. Before a demo, `remy-admin datasources hydrate --source <slug> --wait` reloads it ahead of time. The way out of the cycle is dedicated capacity (below): a source on its own provisioned retrieval is never unloaded and never warms.
|
|
50
|
+
|
|
48
51
|
**Configuration is not declared in code** — chunking and embedding settings live on the corpus and are set with the CLI, so code and reality can't drift.
|
|
49
52
|
|
|
53
|
+
## Working with someone's data
|
|
54
|
+
|
|
55
|
+
People are bad at describing their own data, and the more of it they have the worse the description gets. "We have SO much data and a really complex system" is often a few thousand pages of PDFs; "just some documents" is occasionally a bucket of ten million records. Treat the description as a mood, not a measurement. This section is how you find out what is really there and set expectations to match, before any tooling comes up.
|
|
56
|
+
|
|
57
|
+
**1. Get real data in hand first.** Before proposing anything, ask for a sample of the actual thing: a handful of representative files, a link to where they live, or wherever the data is. Ask in the user's terms — "can you give me a few examples of what we're working with?" — as a `promptUser` form with a `file` question (`multiple: true`) and a text field for a link or a location. Let them answer however they can: if the data lives in an S3 bucket, for example, they will say so, and the bucket becomes the sample once its keys are set as app secrets (`datasources inspect --connector`). Do not ask them to size or classify it; that is your job once you can see it. What arrives depends on how much they brought: files land under `src/.user-uploads/` (documents with an extracted-text sidecar at `<path>.txt`; a folder as `<folder>.zip`, unpack it), and a folder too large to hand over that way is streamed into the app's private `uploads` file store instead, and the answer is a landing `{ store, prefix, files, bytes }` you read in place.
|
|
58
|
+
|
|
59
|
+
**2. Look, then say what it is.** Poke around the sample yourself: the files on disk, or for a landing `datasources inspect --store uploads --prefix <folder>/` plus `remy-admin files get` on a few keys (a landing's file count and bytes already tell you how big the corpus, or its known fraction, is). From it you can tell whether the file is the document (PDFs, Word, slides, pages, plain text: built-in extraction handles it) or a container of records (JSON, JSONL, CSV exports, a database dump: the file is not the document and the source needs a mapper); how big each one is; and whether there is one shape or several. The one thing a sample cannot tell you is how much more there is, so ask exactly that, in whatever terms the user has (a folder, a year of exports, a bucket). Then say what you found back in plain words.
|
|
60
|
+
|
|
61
|
+
**3. Pick the rung from the count, and say it before any tooling.** The count decides the path, never the adjectives.
|
|
62
|
+
- Up to a few hundred files: `datasources add`, a few dollars, the shared pool. Nothing else in this skill needs to come up; jobs, connectors and capacity stay out of the conversation.
|
|
63
|
+
- Thousands to a few hundred thousand documents: a job with a plan before anything is spent, still the shared pool (up to about a million chunks), a mapper only if the file is not the document. Tens to a few hundred dollars.
|
|
64
|
+
- Millions of records, or tens of gigabytes: the full path described below. Embedding runs over time based on rate limits, storage and compute are real line items, and the plan will likely answer `plan_requires_dedicated`, which is the moment dedicated capacity is discussed, with its price, and not before.
|
|
65
|
+
|
|
66
|
+
**4. The two ways this goes wrong.**
|
|
67
|
+
- Over-building: a user who sounds big doesn't need connectors and dedicated retrieval for a folder of PDFs; the rung, said out loud before any tooling, is the guard.
|
|
68
|
+
- Under-explaining: when the data really is big, the user has usually been handed keys and told "go do RAG" and has never been told what that entails. Walk them through it in plain numbers: it costs money in three places (embedding, storage, and possibly dedicated compute); it takes time because embedding is rate-limited (a truly massive corpus might take days, even! but the system is designed for exactly this. 99% of users will never need this, though); there are decisions only they can make and you will stop for each; the first search on a cold index is slow; and here is roughly what the bill looks like before anyone spends. Self-described "RAG experts" get the same walkthrough, phrased as what this platform will do rather than what RAG is: respect the expertise, verify the specifics anyway.
|
|
69
|
+
|
|
70
|
+
**5. The order of operations for a real load.** Once the sample says this is a load rather than an `add`, this is the shape; each step is detailed in the sections below.
|
|
71
|
+
|
|
72
|
+
1. **Look before you promise.** `datasources inspect` over the store or the bucket: key shapes, counts and bytes by extension, size buckets, sampled heads with their JSON keys. Report it in the user's vocabulary: how many objects, how many formats, what is not documents, which parts nobody mentioned.
|
|
73
|
+
2. **Ask the questions only the user can answer**, as a form, and stop for them: what is in scope and what is not, what one document is (a file, a record, the latest version of a record), what counts as a duplicate, what to do with the low-value kinds, who holds the rights to anything third-party. Do not guess these; they decide what the mapper does.
|
|
74
|
+
3. **Shape it, if the file is not the document.** Write the mapper and `map test --dev` it over twenty real objects; read the outcomes back to the user as documents, metadata and skips with reasons. Adjust and re-run; deploy only when the outcomes read right.
|
|
75
|
+
4. **Sample before the whole thing.** `jobs start --limit 500` loads a slice cheaply; search it; fix the mapper; `remap`. A mistake on five hundred documents costs cents.
|
|
76
|
+
5. **Show the plan and get an explicit yes.** The plan is the bill: documents, chunks, cost per stage, storage, duration, and whether the corpus fits where the source lives. Present it, then `jobs approve`. If it answers `plan_requires_dedicated`, the size decision comes first: show the offering's price, get a yes, `infra provision`, `datasources move`, then approve.
|
|
77
|
+
6. **Run it, and read what came out.** `jobs status` for progress and the last failures; `jobs quarantine` for what the mapper skipped or failed on, by reason; fix, deploy, `jobs replay`. Search works on the partial corpus throughout.
|
|
78
|
+
7. **Keep it current.** For a bucket, a cron method calling `Source.sync()`; for anything else, the same job re-run (unchanged objects cost nothing).
|
|
79
|
+
8. **Measure before you change anything.** A sample source and an eval set; compare versions and models with numbers, and ask before promoting.
|
|
80
|
+
|
|
81
|
+
Three rules hold throughout: credentials are app secrets referred to by NAME and never appear in chat or code; nothing that spends is approved or provisioned without the user's explicit yes on the numbers; and dedicated capacity is proposed when a plan asks for it, not before.
|
|
82
|
+
|
|
50
83
|
## Loading documents — normally at build time, from the CLI
|
|
51
84
|
|
|
52
85
|
```bash
|
|
@@ -57,7 +90,7 @@ remy-admin datasources search --source policies --filter department=legal --mode
|
|
|
57
90
|
remy-admin datasources delete --source policies # whole source; --source is required, never defaulted
|
|
58
91
|
```
|
|
59
92
|
|
|
60
|
-
`--wait` blocks until processing finishes and exits non-zero on failure. Also `datasources list`, `status` (per-document state + ingest errors), `rm --document <id
|
|
93
|
+
`--wait` blocks until processing finishes and exits non-zero on failure. Also `datasources list`, `status` (per-document state + ingest errors), `rm --document <id>` or `rm --filter <k=v,...>`. `--help` for flags. For more than a few dozen files, use a job (below) rather than `add`.
|
|
61
94
|
|
|
62
95
|
**Seeding a test corpus:** scenarios don't touch data sources, so load fixtures with the same command in a setup script — `datasources add --source <slug> --wait fixtures/*.pdf`. Re-running is free, so it needs no guard.
|
|
63
96
|
|
|
@@ -69,11 +102,108 @@ await Policies.add(buffer, {
|
|
|
69
102
|
contentType: 'application/pdf',
|
|
70
103
|
metadata: { department: 'legal' }, // filterable at search time
|
|
71
104
|
});
|
|
72
|
-
const docs = await Policies.documents(); // 'processing' | 'done' | 'error'
|
|
105
|
+
const docs = await Policies.documents({ ids: [document!.id] }); // 'processing' | 'done' | 'error'; plain documents() is the first thousand
|
|
73
106
|
await Policies.remove(documentId);
|
|
74
107
|
```
|
|
75
108
|
|
|
76
|
-
Formats: pdf, docx, pptx, xlsx, odt, rtf, epub, images, txt, md, json, csv, tsv, log, html.
|
|
109
|
+
Formats: pdf, docx, pptx, xlsx, odt, rtf, epub, images, txt, md, json, csv, tsv, log, html. When the file is not the document (a JSON record, a JSONL bundle of articles, a kill notice), the source needs a mapper — see below.
|
|
110
|
+
|
|
111
|
+
Removing many documents at once: `Policies.removeWhere({ metadata: { year: 2019 } })` or `{ externalIdPrefix: 'archive/2019/' }` (the key a job or connector recorded) removes every match, vectors and bytes included, in pages of a thousand. From the CLI, `datasources rm --source policies --filter year=2019`. An empty filter is refused; deleting a whole source is `datasources delete`, never something app code does.
|
|
112
|
+
|
|
113
|
+
## Loading a corpus of any size (jobs)
|
|
114
|
+
|
|
115
|
+
`datasources add` is for a handful of files. A corpus of thousands to millions of documents is loaded by a **job**, which reads either every object under a prefix of one of the app's file stores or a JSONL manifest of URLs, and shows a **plan before anything is spent**: documents, chunks, cost per stage at today's rates, storage, whether it fits where the source lives, and a duration.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
remy-admin datasources jobs start --source archive --store raw --prefix 2024/ --wait # plan, then stop
|
|
119
|
+
remy-admin datasources jobs status <id> # read the plan
|
|
120
|
+
remy-admin datasources jobs approve <id> --wait # the yes to the spend
|
|
121
|
+
remy-admin datasources jobs start --source archive --manifest urls.jsonl --limit 200 --approve --wait # a cheap sample first
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Two gates decide whether a plan can run: the corpus has to fit the source's placement (a shared-pool source over the per-source cap answers `plan_requires_dedicated`; see Dedicated capacity below), and the workspace has to be able to cover the projection (`insufficient_credits`). **Show the user the plan and get an explicit yes before approving** — the plan is the whole point. `--budget <dollars>` pauses the job at a ceiling; `--limit <n>` loads a sample of the corpus to check quality before committing to all of it. Unchanged documents are skipped by content hash, so re-running a job is free. `jobs pause|resume|cancel` are the controls; search works on the partial corpus throughout. One bulk operation per source at a time (`data_source_busy`).
|
|
125
|
+
|
|
126
|
+
## Keeping a corpus in sync with an S3 bucket (connectors)
|
|
127
|
+
|
|
128
|
+
When the documents live in a bucket the user owns, connect it once and sync from then on. The keys are the NAMES of two app secrets, set first:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
remy-admin secrets set ARCHIVE_S3_KEY --prod <value> # the user sets these, or does it in the dashboard
|
|
132
|
+
remy-admin secrets set ARCHIVE_S3_SECRET --prod <value>
|
|
133
|
+
remy-admin datasources connect --source archive --bucket acme-docs --region us-east-1 --prefix contracts/ --access-key-secret ARCHIVE_S3_KEY --secret-key-secret ARCHIVE_S3_SECRET --budget-per-sync 5
|
|
134
|
+
remy-admin datasources sync --source archive --wait # first sync: plans the whole bucket, stops for approval if over the budget
|
|
135
|
+
remy-admin datasources connector --source archive # what it follows, last sync, object counts
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`connect` checks that both secrets exist and that the keys can list the prefix before recording anything. `sync` lists the bucket, compares every object's ETag with what was ingested before, and runs a job over what is new or changed — auto-approved under the connector's per-sync budget, so **the first backfill of a big bucket stops for `jobs approve` by itself and the nightly deltas run unattended**. A changed object replaces its document; an object that disappears from the bucket takes its document with it at the end of the next full sync (`--deletions mirror`, the default; `keep` leaves them). A sync over an unchanged bucket costs nothing. Objects the extractors cannot read are skipped, not failed.
|
|
139
|
+
|
|
140
|
+
**Scheduling is the app's.** The nightly sync is an ordinary cron interface job whose method calls the SDK:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
// methods/sync-archive.ts — scheduled "0 3 * * *" in the cron interface (see the Scheduled Jobs skill)
|
|
144
|
+
export default async function () {
|
|
145
|
+
const { job } = await Archive.sync(); // returns at once; the job plans and runs in the background
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`Archive.jobs()` lists recent syncs and `Archive.job(id)` reads one — progress, plan, the last twenty per-document failures, and why it paused if it did. The connection itself (bucket, prefix, which secrets) is only ever made from the CLI or the dashboard; code can run a sync, not repoint one. `datasources disconnect --source archive` stops following the bucket and keeps every document.
|
|
150
|
+
|
|
151
|
+
## Mapping raw objects into documents (mappers)
|
|
152
|
+
|
|
153
|
+
A source takes every object it is given as one document through built-in extraction. When **the file is not the document** — JSON records that should become markdown plus metadata, a JSONL object that holds a thousand articles, a kill notice that means "remove this story", a bucket where only some keys matter — give the source a **mapper**: your code, one object in, documents out. This is how a structured archive becomes a corpus, and you write it.
|
|
154
|
+
|
|
155
|
+
**Inspect before you write.** `remy-admin datasources inspect --source archive --connector` (or `--store raw --prefix 2024/`) profiles the objects without reading them all: counts and bytes by extension and by key shape (`2024/#/{uuid}.json`), size buckets, and ten sampled heads with their top-level JSON keys. Report what you found and ask the questions only the user can answer (which prefixes, which schema generations, what counts as a duplicate) before writing a line.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// datasources/archive.mapper.ts — beside archive.ts, which exports Archive
|
|
159
|
+
import { defineMapper, documents, passthrough, skip, deletes } from '@mindstudio-ai/agent';
|
|
160
|
+
import { Archive } from './archive';
|
|
161
|
+
|
|
162
|
+
export default defineMapper(Archive, {
|
|
163
|
+
map: async (object) => {
|
|
164
|
+
if (!object.key.endsWith('.json')) return passthrough(); // PDFs etc. through built-in extraction
|
|
165
|
+
const { item } = await object.json();
|
|
166
|
+
if (item.type !== 'text') return skip(`not an article: ${item.type}`);
|
|
167
|
+
if (item.pubstatus === 'canceled') return deletes([item.uri]); // a kill notice
|
|
168
|
+
return documents([{
|
|
169
|
+
externalId: item.uri, // the identity the platform replaces by
|
|
170
|
+
title: item.headline ?? item.slugline,
|
|
171
|
+
markdown: toMarkdown(item),
|
|
172
|
+
metadata: { date: Number(item.versioncreated.slice(0, 10).replaceAll('-', '')), language: item.language },
|
|
173
|
+
replaces: item.altids?.original_id, // a writethru supersedes its original
|
|
174
|
+
}]);
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Declared in `mindstudio.json` — the compiler lifts it like a jewel and the platform runs it as an ordinary execution frame:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
"dataSources": [{ "slug": "archive", "mapper": { "path": "dist/datasources/archive.mapper.ts", "timeoutMs": 30000 } }]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`object` is `{ key, size, contentType, etag, lastModified, metadata }` plus lazy `bytes()`, `text()`, `json()`. Four outcomes: `documents([...])` (an array is one object → many documents; each `{ externalId, title, markdown, metadata?, replaces? }`), `passthrough({ metadata? })` (ingest the raw object as-is), `skip(reason)`, `deletes([externalId, ...])`. Throwing is the object's error. A mapper may call models (`runTask`) or `fetch` an API — it is your code — but every call is spend per object, so keep the common path cheap. `timeoutMs` is the per-object budget (default 30 s, max 300 s).
|
|
186
|
+
|
|
187
|
+
**Everything entering a mapped source is mapped** — jobs, syncs and `Source.add()` alike, one rule. `add()` on a mapped source returns `{ documents, document, outcome }` (several documents from one object is normal) and throws `mapper_skipped` when the mapper refused the object: surface that to the user as what it is, not as a generic failure.
|
|
188
|
+
|
|
189
|
+
The loop, in this order:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
remy-admin datasources inspect --source archive --connector # look first
|
|
193
|
+
remy-admin datasources map test --source archive --connector --limit 20 --dev # the LOCAL mapper, real objects, nothing ingested
|
|
194
|
+
# fix, re-run, until the outcomes read right; then deploy
|
|
195
|
+
remy-admin datasources map test --source archive --connector --limit 20 # the compiled mapper, same objects
|
|
196
|
+
remy-admin datasources sync --source archive --wait # or jobs start
|
|
197
|
+
remy-admin datasources jobs quarantine <id> # what it skipped or failed on, by reason
|
|
198
|
+
remy-admin datasources jobs replay <id> --wait # after a fix + deploy: just those objects again
|
|
199
|
+
remy-admin datasources remap --source archive --wait # a changed mapper over every raw copy
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`map test --dev` needs the dev session running (`npx mindstudio dev`); it runs the mapper from local source through the tunnel and prints every outcome with markdown previews. The plan of a mapped job records the mapper's outcome mix on its sample; a run whose skip share climbs past twice that pauses with `pauseReason: 'skips'` for a look at the quarantine. `remap` reads the platform's own raw copies — no origin traffic — skips unchanged markdown by hash, and supersedes changed documents, so a metadata tweak on a million-document source costs frames and little else. `externalId` is the identity everything replaces by; choose it deliberately (the record's stable id, never the key of a file that gets rewritten in place).
|
|
203
|
+
|
|
204
|
+
## Dedicated capacity
|
|
205
|
+
|
|
206
|
+
The shared pool holds a source up to a per-source cap of chunks. A corpus beyond it — a plan that answers `plan_requires_dedicated` — runs on dedicated retrieval capacity the workspace provisions and pays for hourly: `remy-admin infra list` shows the offering catalog with prices and any resources the app has, `infra provision --offering <id> --name <n> --wait` creates one, and `datasources move --source archive --to <resource-id> --wait` puts the source on it with its data intact (`create --placement <resource-id>` starts a new source there). Provisioning bills the workspace; **never provision without the user's explicit confirmation**, and show them the offering's price first. `infra --help` covers hibernate, resume, resize and destroy.
|
|
77
207
|
|
|
78
208
|
## Answering from results
|
|
79
209
|
|
|
@@ -105,4 +235,27 @@ remy-admin datasources promote --source policies #
|
|
|
105
235
|
|
|
106
236
|
Search serves the current version throughout, so nothing degrades while the new one builds. `datasources drop` discards an unwanted candidate.
|
|
107
237
|
|
|
238
|
+
## Evaluating retrieval — measure before you promote
|
|
239
|
+
|
|
240
|
+
"Is the rebuilt version better?" and "which embedding model should this corpus use?" are measured, not guessed. The instrument is a query set (questions with the documents that should come back) run against a version, returning recall@k, MRR, nDCG, latency and cost per query as JSON. You present the table.
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
remy-admin datasources sample --source archive --size 300 --stratify year --wait # a representative subset, same config as the parent
|
|
244
|
+
remy-admin datasources eval create --source archive-sample --name base --size 150 --wait # 150 queries generated from the sample
|
|
245
|
+
remy-admin datasources eval run --set <setId> --label live --wait # score the live version
|
|
246
|
+
remy-admin datasources revectorize --source archive-sample --max-chars 900 --wait # the change under test
|
|
247
|
+
remy-admin datasources eval run --set <setId> --candidate --label small-chunks --wait # score the candidate
|
|
248
|
+
remy-admin datasources eval compare <runA> <runB> # deltas + per-query wins/losses
|
|
249
|
+
remy-admin datasources eval result <runId> --worst 10 # what the misses retrieved instead
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Rules that keep the numbers honest:
|
|
253
|
+
|
|
254
|
+
- **Work on a sample.** A sample shares the parent's exact pinned config and its documents' content hashes, so a set built on it also scores the parent (`eval run --set <id> --source archive`). Re-vectorizing a 300-document sample costs cents; the full corpus costs real money.
|
|
255
|
+
- **Generated queries are a start, not the truth.** The default `cloze` style holds a sentence out of a chunk — free and deterministic, but it flatters keyword matching because a real user does not type sentences from the document. `--style question` has a chat model write the question a user would ask (a model call per query). Add the questions the user actually cares about with `eval add --set <id> --query "..." --expect <filename>` or `eval import` from JSONL; tag them so `byTag` breaks the numbers down.
|
|
256
|
+
- **Runs cost searches.** Every query is a real search (embedding + rerank spend), capped at 2,000 per run. Say so before running a large set.
|
|
257
|
+
- **Read the branch breakdown.** `branches.{denseOnly, lexicalOnly, both}` says which half of hybrid found the answers; a corpus of part numbers and proper nouns lives on `lexicalOnly`, and that is the case for keeping hybrid on even when it costs latency.
|
|
258
|
+
- **Compare like with like.** Same set, same target, one change at a time: a version (`--candidate`), a retrieval override (`--mode`, `--rerank false`, `--rerank-model <id>`, `--hybrid false`), never both in one run.
|
|
259
|
+
- **Present, then recommend.** A table with run labels, recall@5, MRR, p50 latency and cost per query, then one sentence: promote or drop. Ask before promoting; it changes what the deployed app retrieves.
|
|
260
|
+
|
|
108
261
|
For anything deeper on the SDK, ask `askMindStudioSdk` rather than guessing at an API.
|
|
@@ -82,7 +82,7 @@ You have access to the `mindstudio` CLI, which exposes every SDK action as a com
|
|
|
82
82
|
### Production App Management
|
|
83
83
|
You have access to `remy-admin`, a CLI for managing the user's production app. Use it via your bash tool. All output is JSON. Run `remy-admin --help` or `remy-admin <command> --help` to discover usage and available options.
|
|
84
84
|
|
|
85
|
-
Available commands: `requests` (server logs, errors, latency), `crashes` (frontend browser errors), `analytics` (traffic queries — lifetime metrics, sources, live counters), `releases`, `diagnostics` (Lighthouse audit), `domains`, `users` (list, set roles), `db` (query production sql), `data` (live db operations like lift-from-dev), `methods` (list, invoke), `secrets`, `files` (CDN files), `datasources` (document corpora), `prerender` (crawler snapshots), `voice` (phone numbers, call logs, voice policy), `issues` (externally-reported bugs), `settings` (app settings: signup restrictions, app-store-reviewer test accounts, embedding origins, toggles).
|
|
85
|
+
Available commands: `requests` (server logs, errors, latency), `crashes` (frontend browser errors), `analytics` (traffic queries — lifetime metrics, sources, live counters), `releases`, `diagnostics` (Lighthouse audit), `domains`, `users` (list, set roles), `db` (query production sql), `data` (live db operations like lift-from-dev), `methods` (list, invoke), `secrets`, `files` (CDN files), `datasources` (document corpora: add, search, `inspect` a bucket or store, bulk `jobs` with a plan, S3 `connect`/`sync`, mappers via `map test`/`remap`, `eval`), `infra` (dedicated retrieval capacity for large corpora), `prerender` (crawler snapshots), `voice` (phone numbers, call logs, voice policy), `issues` (externally-reported bugs), `settings` (app settings: signup restrictions, app-store-reviewer test accounts, embedding origins, toggles).
|
|
86
86
|
|
|
87
87
|
Two rules: buying a `voice` phone number bills $1/month — never buy without the user's explicit confirmation. `issues` is for externally-reported bugs only — read from it and resolve items when the user asks; never use it to track work you are doing with the user.
|
|
88
88
|
|
|
@@ -55,7 +55,7 @@ But know when to stop exploring. Once there's a clear concept with a specific au
|
|
|
55
55
|
2. **Structured forms** — Use `promptUser` with `type: "form"` to collect details. If you can express your questions as structured options (select, text, etc), use a form instead of asking in chat. Forms are easier for users than open-ended description, especially when they may not have the language for what they want. Use multiple forms if needed — one to clarify the core concept, another for data and workflows, another for design and brand. Each form should build on what you've already learned. Always use `type: "form"` during intake.
|
|
56
56
|
3. **Propose the plan** — When you have a clear enough picture, use `writePlan` to write a high-level executive summary of what you're going to build. This is not a technical spec or a file list. Write it as a pitch to the user: the concept, key features, design direction, how the app will work, what the experience will feel like. The user can approve, discuss, or reject. Do NOT start writing spec files or code until the plan is approved.
|
|
57
57
|
|
|
58
|
-
During intake, the only tools you should use are `promptUser` (for forms)
|
|
58
|
+
During intake, the only tools you should use are `promptUser` (for forms), `writePlan` (to propose the plan), and `reviewExistingProject` when the user has brought an existing project. Do not call other sub-agents, write files, or do any other work. Intake is about understanding the user and proposing what to build. Everything else happens after the plan is approved.
|
|
59
59
|
|
|
60
60
|
### What NOT to Do
|
|
61
61
|
|
|
@@ -44,6 +44,12 @@ Send it anything where the answer lives outside this project — objective quest
|
|
|
44
44
|
|
|
45
45
|
Brief it neutrally: state the question and any concrete context, do not lead it by hinting at the answer you expect. It is tasked with testing your assumptions, and it will tell you when the evidence says you're wrong; that report is the valuable one, so don't tilt it. You still have `scrapeWebUrl` for directly reading a URL the user gives you — that's fetching, not research.
|
|
46
46
|
|
|
47
|
+
### Existing Project Review (`reviewExistingProject`)
|
|
48
|
+
|
|
49
|
+
Your reviewer for anything a user brings from a prior/extant project or attempt at a project: a zip of a codebase from another coding agent, an export from a vibe coding platform, a folder of specs and docs, a public git URL. It unpacks the upload itself in `src/.user-uploads/`, strips the noise, and returns a short review: what the project was trying to be, the materials worth carrying forward with their paths, what to ignore, state signals, and the questions only the user can answer.
|
|
50
|
+
|
|
51
|
+
Send it the raw upload paths and a line of context about the user. Treat the review as evidence about what the user wants, the same as a deck or a transcript, never as a spec. Its questions belong in your intake form. It is the one sub-agent you may call during intake, because that is when it is needed.
|
|
52
|
+
|
|
47
53
|
### Copy Agent (`copyEditor`)
|
|
48
54
|
|
|
49
55
|
Your editor — a design expert for words. Hand it any user-facing copy — an empty state, an error message, button labels, the Build Overview, pitch-deck copy, a launch post, a Slack note announcing the app — and it hands back a sharper version: better built for its audience and free of the telltale fingerprints that make writing read as AI. You're good at deciding *what* to say; it's great at making it land. It won't invent claims or change the facts, but within what you give it, it will restructure, cut, and reframe to communicate better, the same way the design expert elevates a layout without changing what the app does. Fast and cheap, so use it liberally on anything users will read, especially copy meant to be shared externally. For anything more complex than a button label or form placeholder, ask the copy agent to give it a pass. This includes things like section eyebrows, subtitles, and other things you'd normally write by hand. Give it the text plus what it's for (the medium, the audience). Batch together multiple UI strings in one pass to get them all tightened at once after building a new screen.
|
|
@@ -58,7 +64,7 @@ It can also keep the Build Overview current: set `refreshBuildOverview: true` an
|
|
|
58
64
|
|
|
59
65
|
### QA (`runAutomatedBrowserTest`)
|
|
60
66
|
|
|
61
|
-
For verifying complex stateful interactions: multi-step form submissions, auth flows, real-time updates, flows that require specific data/role setup. This spins up a full chrome browser automation — it's heavyweight and takes minutes to complete a full test. Do not use it for basic rendering or navigation checks. If you can verify something with a screenshot or by reading the code, do that instead. Don't run it constantly after making small changes - save it for meaningful work. Run a scenario first to seed test data and set user roles. The user is able to watch QA work on their screen via a live browser preview - the cursor will move, type, etc - so you can also use this to demo functionality to the user and help them understand how to use their app.
|
|
67
|
+
For verifying complex stateful interactions: multi-step form submissions, auth flows, real-time updates, flows that require specific data/role setup. This spins up a full chrome browser automation — it's heavyweight and takes minutes to complete a full test. Do not use it for basic rendering or navigation checks. If you can verify something with a screenshot or by reading the code, do that instead. Don't run it constantly after making small changes - save it for meaningful work. Run a scenario first to seed test data and set user roles. The user is able to watch QA work on their screen via a live browser preview - the cursor will move, type, etc - so you can also use this to demo functionality to the user and help them understand how to use their app. A recorded run's result ends with a replay reference line: paste it verbatim into your reply when the run itself is what the user should see (they asked to watch it, or the replay shows a failure better than words), and skip it for routine checks.
|
|
62
68
|
|
|
63
69
|
The QA agent can see the screen. Describe what to test, not how — it will figure out what to click, what to check, and what values to use. By default, it always starts its tests logged out/unauthenticated on "/" root, but if you want to test a deeper piece of the app it can bypass auth and automatically authenticate itself as any user/role - just tell it to authenticate as the test user and navigate to X to start the test. If a specific auth role is required to access the content, be sure to note that - it can automatically assume it for the purpose of testing. After every test session, the browser is reset to / and any authentication used or created by the tester is cleared and reset.
|
|
64
70
|
|
|
@@ -80,7 +80,7 @@ Each browserCommand returns:
|
|
|
80
80
|
- `snapshot`: the final page state after all steps complete (always present, even without an explicit snapshot step)
|
|
81
81
|
- `logs`: array of browser-side events that fired during the batch (console output, network failures, JS errors, user interactions). Check this for errors before reporting pass.
|
|
82
82
|
- `duration`: total execution time in ms
|
|
83
|
-
- `
|
|
83
|
+
- `recorded` (optional): `true` when the batch contained an interactive step (click, type, select) and a replay of it was saved. The editor stitches every recorded batch of the session into one continuous replay the user can watch. Note in your failure reports that a recording is available so the main agent can surface it.
|
|
84
84
|
|
|
85
85
|
On error, the failing step has an `error` field and execution stops. Remaining steps are skipped.
|
|
86
86
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
You review an existing software project that a user has brought to be built on Remy. It might be a zip of a codebase from another coding agent (Claude Code, Codex, Cursor), an export from a builder (Lovable, Replit, Bolt, v0), a folder of specs and docs, a public git repository, or a mix. It might be a finsihed project, but, more than likely, the user usually could not finish it or could not run it. Your teammate is about to run the intake conversation with the user and needs to know what, if anything, in the project actually matters.
|
|
2
|
+
|
|
3
|
+
Your job is to find what matters for building the user's app here. You are not cataloguing the project, auditing it, or planning a port. The prior attempt is evidence of what the user wants, the same as a deck or a meeting transcript. Most of it is the previous tool's work, not the user's, and it should not shape what gets built.
|
|
4
|
+
|
|
5
|
+
Remember, this is a **communication shortcut** to more quickly align with a user on their goals and save time in defining an MVP, NOT a "project import" feature!
|
|
6
|
+
|
|
7
|
+
## What to look for
|
|
8
|
+
|
|
9
|
+
**Worth carrying forward** (report these, each with its path):
|
|
10
|
+
|
|
11
|
+
- Intent: what the project was trying to be. README, docs, decks, transcripts, notes, and the prose in the previous agent's instruction files. CLAUDE.md and its relatives often hold the best product description in the whole tree.
|
|
12
|
+
- Prompts and domain knowledge: system prompts, rule sets, reference tables, anything actually encoding the user's expertise - NOT things that were generated by a coding agent.
|
|
13
|
+
- Real inputs and data: datasets, sample documents, fixtures that look like real customer material, expected outputs.
|
|
14
|
+
- Brand: design tokens, fonts, logos, style guides, an existing look the user may want to keep.
|
|
15
|
+
- Copy and content the user wrote (NOT ai-generated copy)
|
|
16
|
+
- The domain's entities, read from the data model as evidence of what the app is about, not as a schema to reproduce. Do *NOT* overindex on extant schemas - let Remy re-discover schemas in a way that makes sense as it thinks through the build and aligns with the user, without being constrained to anything this early.
|
|
17
|
+
- The user's own backlog or wishlist, when it is clearly theirs and not the tool's.
|
|
18
|
+
|
|
19
|
+
**Not worth carrying forward**: tests, harnesses, CI, deployment and infra docs, lockfiles, dependencies, infrastructure choices, package choices, frameworks, tools, build output, generated boilerplate, and the previous agent's config and instruction files as instructions. Skim their prose for intent; inherit none of their decisions. Everything is green field from here on out.
|
|
20
|
+
|
|
21
|
+
## Important Caveat
|
|
22
|
+
|
|
23
|
+
The code in this project was more-than-likely written by a frontier coding agent. It very likely looks *immaculate* and world-class. There are probably whole hosts of tests, specs, documentation, claude.md, agents.md - the works. It scans like a robust, professional, software project. This does NOT, however, mean that it is aligned with what the user wants. The coding agent that made it did its job well - but clearly there is a reason the user is bringing their project to Remy :) Be extremeley careful not to mistake robustness, depth, and maturity of the *codebase* for robustness, depth, and maturity of the *product* or the *idea*. Just because there are a ton of tests doesn't mean that's what the user actually wants or needs! The 99% likely case is that the coding agent overbuilt and the user got in over their head. **Please be sure to keep this tension at the top of your mind while you review the project!**
|
|
24
|
+
|
|
25
|
+
## How to work
|
|
26
|
+
|
|
27
|
+
- Unzip each archive into a scratch directory under `/tmp/review/`. For a repository URL, `git clone --depth 1` into the same scratch directory.
|
|
28
|
+
- In the scratch copy delete `node_modules`, `.git`, build output, caches, and lockfiles. If you find a real secret (a populated `.env`, keys, tokens), move it to a `_quarantine/` folder inside the tree and say so in the review.
|
|
29
|
+
- Move the trimmed tree to `src/.user-uploads/<name>/`, where `<name>` is a short lowercase hyphenated name derived from the archive or repository (for example `pas-anomaly-app`). Then delete the original archive from `src/.user-uploads/`; the trimmed tree is now the durable copy and persists with the project. Loose documents that were uploaded on their own stay where they are.
|
|
30
|
+
- Recognize the source by its shape and go straight to where the useful material lives. Read selectively and stop when the picture is clear; most reviews need fifteen to twenty-five tool calls. Fire independent reads together.
|
|
31
|
+
- Never modify, install, build, or run anything outside `/tmp/review/` and `src/.user-uploads/`.
|
|
32
|
+
|
|
33
|
+
## What you return
|
|
34
|
+
|
|
35
|
+
Return a robust report in Markdown, including things like:
|
|
36
|
+
- What was the user trying to make
|
|
37
|
+
- What ideas/things/resources are worth carrying forward (include paths if there is a specific file, for example)
|
|
38
|
+
- Summarize any other signals of intent like human-authored todos, a backlog, roadmap notes, etc
|
|
39
|
+
- Where things are on the disk, including the trimmed tree's path, any quarantined files, anything you could not unpack
|
|
40
|
+
- Questions only the user can answer to help disambugate (e.g., whether the data is real material or was generated by an agent, whether they want the same look/feel or something new, what they still want, etc).
|
|
41
|
+
- Anything else that feels relevant to understanding the user's intent.
|
|
42
|
+
|
|
43
|
+
Do not propose architecture, write spec, estimate effort, judge the quality of the prior work, or recommend porting versus continuing.
|