autark-cli 0.5.6 → 0.5.8
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/autark.mjs +194 -8
- package/package.json +1 -1
package/autark.mjs
CHANGED
|
@@ -25,7 +25,23 @@ const RAW_RUNTIME_BASE = process.env.AUTARK_RUNTIME_RAW_BASE
|
|
|
25
25
|
|| 'https://autark.sh/runtime'
|
|
26
26
|
const SKILL_NAMES = ['autark', 'plumcake', 'chrome-relay', 'email', 'outreach', 'email-finder']
|
|
27
27
|
const ECOSYSTEM_CLIS = ['autark-cli', 'plumcake-cli', 'chrome-relay']
|
|
28
|
-
const PROGRAM_FILES = ['start.md', 'double-down.md', 'check.md', 'followup.md']
|
|
28
|
+
const PROGRAM_FILES = ['start.md', 'find_leads.md', 'double-down.md', 'check.md', 'followup.md']
|
|
29
|
+
const LEAD_TEMPLATE = {
|
|
30
|
+
person: {
|
|
31
|
+
full_name: 'Itay Rosen',
|
|
32
|
+
primary_email: 'itay@example.com',
|
|
33
|
+
email_status: 'guessed',
|
|
34
|
+
handles: {
|
|
35
|
+
github: 'itayrosen',
|
|
36
|
+
twitter: 'itayrosen',
|
|
37
|
+
},
|
|
38
|
+
headline: 'Maintainer of parallel-browser-mcp',
|
|
39
|
+
source: 'github',
|
|
40
|
+
},
|
|
41
|
+
lead: {
|
|
42
|
+
angle: 'Maintains a browser automation MCP project, so he is likely to care about real-browser access for agents.',
|
|
43
|
+
},
|
|
44
|
+
}
|
|
29
45
|
|
|
30
46
|
// ============================================================ address reject
|
|
31
47
|
// Two address shapes are bad enough to refuse at the CLI boundary, regardless
|
|
@@ -208,6 +224,11 @@ async function main() {
|
|
|
208
224
|
if (command === 'unpause') return hypothesisPause(rest, 'active')
|
|
209
225
|
if (!command || command === '--help' || command === '-h') return hypothesisUsage()
|
|
210
226
|
}
|
|
227
|
+
if (group === 'lead') {
|
|
228
|
+
if (command === 'add') return leadAdd(rest)
|
|
229
|
+
if (command === 'template') return leadTemplate()
|
|
230
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') return leadUsage()
|
|
231
|
+
}
|
|
211
232
|
if (group === 'run') {
|
|
212
233
|
if (command === 'start') return runStart(rest)
|
|
213
234
|
if (command === 'finish') return runFinish(rest)
|
|
@@ -655,6 +676,69 @@ async function hypothesisStatus(rest) {
|
|
|
655
676
|
console.log(`${label} → ${result.status}`)
|
|
656
677
|
}
|
|
657
678
|
|
|
679
|
+
// =============================================================== v2 leads
|
|
680
|
+
|
|
681
|
+
async function leadAdd(rest) {
|
|
682
|
+
const opts = parseArgs(rest)
|
|
683
|
+
const payload = parseJsonValue(readValue(required(opts.input, '--input @lead.json')), '--input')
|
|
684
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) throw new Error('--input must be a JSON object')
|
|
685
|
+
if (!payload.lead || typeof payload.lead !== 'object' || !String(payload.lead.angle || '').trim()) {
|
|
686
|
+
throw new Error('lead.angle is required in --input JSON')
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
let hypId = opts['hypothesis-id'] || opts.hypothesis_id
|
|
690
|
+
if (!hypId) {
|
|
691
|
+
const ref = opts.hypothesis || opts._[0]
|
|
692
|
+
if (ref) {
|
|
693
|
+
const [productSlug, code] = splitHypothesisRef(ref)
|
|
694
|
+
const ctx = await api('GET', `/v1/context/${encodeURIComponent(productSlug)}/${encodeURIComponent(code)}`)
|
|
695
|
+
hypId = ctx.hypothesis?.id
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (!hypId) throw new Error('pass --hypothesis-id <id> or --hypothesis <slug/Hxx>')
|
|
699
|
+
|
|
700
|
+
const body = {
|
|
701
|
+
...payload,
|
|
702
|
+
source_run_id: opts['run-id'] || opts.run_id || opts.run || payload.source_run_id || undefined,
|
|
703
|
+
}
|
|
704
|
+
const result = await api('POST', `/v1/hypotheses/${encodeURIComponent(hypId)}/leads`, body)
|
|
705
|
+
console.log(JSON.stringify(result))
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function leadTemplate() {
|
|
709
|
+
printJson(LEAD_TEMPLATE)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function leadUsage() {
|
|
713
|
+
console.log(`autark lead
|
|
714
|
+
|
|
715
|
+
template
|
|
716
|
+
print a minimal JSON payload for lead add
|
|
717
|
+
|
|
718
|
+
add --hypothesis-id <id> --input @/tmp/lead.json [--run-id <id>]
|
|
719
|
+
add --hypothesis <slug>/<H01> --input @/tmp/lead.json [--run-id <id>]
|
|
720
|
+
|
|
721
|
+
lead add records one sourced lead: person identity + the angle connecting that
|
|
722
|
+
person to the hypothesis. It sends one ID-first request to the worker and
|
|
723
|
+
prints exactly one compact JSON object to stdout:
|
|
724
|
+
|
|
725
|
+
{"person_id":"...","lead_id":"...","person_existed":false,"lead_existed":false}
|
|
726
|
+
|
|
727
|
+
Minimum payload:
|
|
728
|
+
|
|
729
|
+
${JSON.stringify(LEAD_TEMPLATE, null, 2)}
|
|
730
|
+
|
|
731
|
+
Required:
|
|
732
|
+
- lead.angle
|
|
733
|
+
- enough person identity to dedupe: primary_email, a supported handle, or full_name
|
|
734
|
+
|
|
735
|
+
Optional person fields:
|
|
736
|
+
email_status, headline, source, bio, signals
|
|
737
|
+
|
|
738
|
+
find_leads should write /tmp/lead-N.json and call lead add with --input @file.
|
|
739
|
+
No outreach is sent and no touch rows are written.`)
|
|
740
|
+
}
|
|
741
|
+
|
|
658
742
|
// =============================================================== runs
|
|
659
743
|
|
|
660
744
|
async function runStart(rest) {
|
|
@@ -776,15 +860,31 @@ async function hypothesisPause(rest, status) {
|
|
|
776
860
|
async function feedbackRecord(rest) {
|
|
777
861
|
const opts = parseArgs(rest)
|
|
778
862
|
const ref = opts.hypothesis || opts._[0]
|
|
863
|
+
const text = readValue(required(opts.text || opts.message || opts.body, '--text "<feedback>"'))
|
|
864
|
+
|
|
865
|
+
// --product-id (or a bare product slug with no /Hxx) → product-level feedback.
|
|
866
|
+
const productId = opts['product-id'] || opts.product_id
|
|
867
|
+
const isProductSlug = ref && !opts['hypothesis-id'] && !opts.hypothesis_id && !/\/H\d{2}$/.test(ref)
|
|
868
|
+
if (productId || isProductSlug) {
|
|
869
|
+
let pid = productId
|
|
870
|
+
if (!pid) {
|
|
871
|
+
const ctx = await api('GET', `/v1/context/${encodeURIComponent(ref)}`)
|
|
872
|
+
pid = ctx.product?.id
|
|
873
|
+
}
|
|
874
|
+
if (!pid) throw new Error('product not found')
|
|
875
|
+
const result = await api('POST', `/v1/products/${encodeURIComponent(pid)}/feedback`, { text })
|
|
876
|
+
console.log(result.id)
|
|
877
|
+
return
|
|
878
|
+
}
|
|
879
|
+
|
|
779
880
|
let hypId = opts['hypothesis-id'] || opts.hypothesis_id
|
|
780
881
|
if (!hypId) {
|
|
781
|
-
if (!ref) throw new Error('pass product/Hxx or --hypothesis-id')
|
|
882
|
+
if (!ref) throw new Error('pass product, product/Hxx, --product-id, or --hypothesis-id')
|
|
782
883
|
const [productSlug, code] = splitHypothesisRef(ref)
|
|
783
884
|
const ctx = await api('GET', `/v1/context/${encodeURIComponent(productSlug)}/${encodeURIComponent(code)}`)
|
|
784
885
|
hypId = ctx.hypothesis?.id
|
|
785
886
|
}
|
|
786
887
|
if (!hypId) throw new Error('hypothesis not found')
|
|
787
|
-
const text = readValue(required(opts.text || opts.message || opts.body, '--text "<feedback>"'))
|
|
788
888
|
const body = { text }
|
|
789
889
|
if (opts['action-id'] || opts.action_id) body.action_id = opts['action-id'] || opts.action_id
|
|
790
890
|
const result = await api('POST', `/v1/hypotheses/${encodeURIComponent(hypId)}/feedback`, body)
|
|
@@ -799,7 +899,8 @@ async function feedbackDelete(rest) {
|
|
|
799
899
|
}
|
|
800
900
|
|
|
801
901
|
function feedbackUsage() {
|
|
802
|
-
console.log(`autark feedback record <product/Hxx> --text "<nudge>" [--action-id <id>]
|
|
902
|
+
console.log(`autark feedback record <product/Hxx> --text "<nudge>" [--action-id <id>] per-hypothesis nudge
|
|
903
|
+
autark feedback record <product> --text "<nudge>" product-level note (any hypothesis)
|
|
803
904
|
autark feedback delete <feedback-id>`)
|
|
804
905
|
}
|
|
805
906
|
|
|
@@ -886,7 +987,16 @@ async function mailLint(rest) {
|
|
|
886
987
|
const compoundQ = /\b(are|is|do|does|did|will|would|can|could|should|have|has)\b[^?]{0,200}\bor\b[^?]{0,200}\?/i
|
|
887
988
|
if (compoundQ.test(body)) violations.push({ rule: 'compound-question', detail: 'a/b question with "or"', why: WHY['compound-question'] })
|
|
888
989
|
const sigSplit = body.split(/\n\s*\n(?:best|thanks|cheers|—|-)[\s,]*\n/i)[0] || body
|
|
889
|
-
|
|
990
|
+
// Measure VISIBLE prose length, not raw markup. An <a href>/[text](url) signature
|
|
991
|
+
// anchor or any HTML wrapper must not eat into the 400-char content budget — only
|
|
992
|
+
// what the recipient actually reads counts.
|
|
993
|
+
const visibleText = sigSplit
|
|
994
|
+
.replace(/<a\b[^>]*>(.*?)<\/a>/gis, '$1') // <a ...>Kushal</a> -> Kushal
|
|
995
|
+
.replace(/\[([^\]]+)\]\(\s*https?:\/\/[^)]+\)/g, '$1') // [Kushal](url) -> Kushal
|
|
996
|
+
.replace(/<[^>]+>/g, '') // drop remaining html tags
|
|
997
|
+
.replace(/\s+/g, ' ') // collapse whitespace
|
|
998
|
+
.trim()
|
|
999
|
+
const len = visibleText.length
|
|
890
1000
|
if (len > 400) violations.push({ rule: 'too-long', detail: `${len} chars`, why: WHY['too-long'] })
|
|
891
1001
|
const hasMdAnchor = /\[[^\]]+\]\(https?:\/\/[^)]+\)/.test(body)
|
|
892
1002
|
const hasHtmlAnchor = /<a\b[^>]*href\s*=\s*["']https?:\/\/[^"']+["'][^>]*>[^<]+<\/a>/i.test(body)
|
|
@@ -1188,6 +1298,17 @@ function printProductContext(r) {
|
|
|
1188
1298
|
kv('product.url', r.product.url)
|
|
1189
1299
|
kv('product.visibility', r.product.visibility)
|
|
1190
1300
|
kv('product.hypothesis_count', (r.hypotheses || []).length)
|
|
1301
|
+
kv('product.person_count', r.product.person_count || (r.people || []).length || 0)
|
|
1302
|
+
kv('product.lead_count', r.product.lead_count || (r.leads || []).length || 0)
|
|
1303
|
+
kv('product.ready_lead_count', r.product.ready_lead_count || (r.leads || []).filter((l) => l.status === 'ready').length || 0)
|
|
1304
|
+
// Product-level operator feedback (newest first). Append-only + timestamped,
|
|
1305
|
+
// so each entry is a distinct nudge — re-read these every run.
|
|
1306
|
+
kv('product.feedback_count', (r.feedback || []).length)
|
|
1307
|
+
for (const f of (r.feedback || [])) {
|
|
1308
|
+
const shortF = String(f.id || '').slice(0, 8)
|
|
1309
|
+
kv(`product.feedback.${shortF}.created_at`, f.created_at)
|
|
1310
|
+
kv(`product.feedback.${shortF}.text`, f.text)
|
|
1311
|
+
}
|
|
1191
1312
|
for (const h of (r.hypotheses || [])) {
|
|
1192
1313
|
kv(`hypothesis.${h.code}.id`, h.id)
|
|
1193
1314
|
kv(`hypothesis.${h.code}.title`, h.title)
|
|
@@ -1195,6 +1316,8 @@ function printProductContext(r) {
|
|
|
1195
1316
|
kv(`hypothesis.${h.code}.run_count`, h.run_count)
|
|
1196
1317
|
kv(`hypothesis.${h.code}.action_count`, h.action_count)
|
|
1197
1318
|
kv(`hypothesis.${h.code}.reply_count`, h.reply_count)
|
|
1319
|
+
kv(`hypothesis.${h.code}.lead_count`, h.lead_count || (h.leads || []).length || 0)
|
|
1320
|
+
kv(`hypothesis.${h.code}.ready_lead_count`, h.ready_lead_count || (h.leads || []).filter((l) => l.status === 'ready').length || 0)
|
|
1198
1321
|
if (h.needs_human_count) kv(`hypothesis.${h.code}.needs_human_count`, h.needs_human_count)
|
|
1199
1322
|
const actions = (h.runs || []).flatMap((run) => run.actions || [])
|
|
1200
1323
|
const by = channelBreakdown(actions)
|
|
@@ -1203,12 +1326,29 @@ function printProductContext(r) {
|
|
|
1203
1326
|
kv(`hypothesis.${h.code}.channel.${ch}.replied`, by[ch].replied)
|
|
1204
1327
|
}
|
|
1205
1328
|
}
|
|
1329
|
+
for (const lead of (r.leads || [])) {
|
|
1330
|
+
const shortL = actionShortId(lead.id)
|
|
1331
|
+
kv(`lead.${shortL}.id`, lead.id)
|
|
1332
|
+
kv(`lead.${shortL}.status`, lead.status)
|
|
1333
|
+
kv(`lead.${shortL}.hypothesis_code`, lead.hypothesis_code)
|
|
1334
|
+
kv(`lead.${shortL}.person_name`, lead.person?.full_name)
|
|
1335
|
+
kv(`lead.${shortL}.person_email`, lead.person?.primary_email)
|
|
1336
|
+
kv(`lead.${shortL}.headline`, lead.person?.headline)
|
|
1337
|
+
kv(`lead.${shortL}.angle`, lead.angle)
|
|
1338
|
+
}
|
|
1206
1339
|
console.log('---')
|
|
1207
1340
|
// ---- narrative ----
|
|
1208
1341
|
console.log(`# ${r.product.slug} — ${r.product.name}\n`)
|
|
1209
1342
|
if (r.product.tagline) console.log(`> ${r.product.tagline}\n`)
|
|
1210
1343
|
console.log(`## Brief\n`)
|
|
1211
1344
|
console.log(r.product.brief?.trim() || '(no brief set — owner can add one at https://autark.sh)')
|
|
1345
|
+
if ((r.feedback || []).length > 0) {
|
|
1346
|
+
console.log(`\n## Operator feedback (${r.feedback.length}) — product-level, newest first\n`)
|
|
1347
|
+
console.log(`These are general operator nudges, not tied to one hypothesis. Treat the most recent as the freshest signal and let them re-shape your next move.\n`)
|
|
1348
|
+
for (const f of r.feedback) {
|
|
1349
|
+
console.log(`- (${f.created_at}) ${f.text}`)
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1212
1352
|
if (!r.hypotheses?.length) {
|
|
1213
1353
|
console.log(`\n## Hypotheses\n\n(none yet — create one with: autark hypothesis create --product-id <product_id> --md @hyp.md)`)
|
|
1214
1354
|
} else {
|
|
@@ -1220,7 +1360,18 @@ function printProductContext(r) {
|
|
|
1220
1360
|
.map((ch) => `${ch} ${by[ch].replied}/${by[ch].sent}`)
|
|
1221
1361
|
.join(' ')
|
|
1222
1362
|
const tail = channelSummary ? ` — replies/sends by channel: ${channelSummary}` : ''
|
|
1223
|
-
console.log(`- [${h.status}] ${h.code} — ${h.title} (runs: ${h.run_count}, actions: ${h.action_count || 0}, replies: ${h.reply_count || 0})${tail}`)
|
|
1363
|
+
console.log(`- [${h.status}] ${h.code} — ${h.title} (runs: ${h.run_count}, actions: ${h.action_count || 0}, replies: ${h.reply_count || 0}, leads: ${h.lead_count || 0})${tail}`)
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
if (r.leads?.length) {
|
|
1367
|
+
console.log(`\n## Recent leads (${r.leads.length})\n`)
|
|
1368
|
+
for (const lead of r.leads) {
|
|
1369
|
+
const person = lead.person || {}
|
|
1370
|
+
const label = person.full_name || person.primary_email || Object.entries(person.handles || {})[0]?.join(':') || lead.person_id || 'unknown person'
|
|
1371
|
+
const hyp = lead.hypothesis_code ? `${lead.hypothesis_code} ` : ''
|
|
1372
|
+
const headline = person.headline ? ` — ${person.headline}` : ''
|
|
1373
|
+
console.log(`- [${lead.status}] ${hyp}${label}${headline}`)
|
|
1374
|
+
if (lead.angle) console.log(` angle: ${lead.angle}`)
|
|
1224
1375
|
}
|
|
1225
1376
|
}
|
|
1226
1377
|
}
|
|
@@ -1233,6 +1384,8 @@ function printHypothesisContext(result) {
|
|
|
1233
1384
|
kv('hypothesis.code', result.hypothesis.code)
|
|
1234
1385
|
kv('hypothesis.title', result.hypothesis.title)
|
|
1235
1386
|
kv('hypothesis.status', result.hypothesis.status)
|
|
1387
|
+
kv('hypothesis.lead_count', result.hypothesis.lead_count || (result.leads || []).length || 0)
|
|
1388
|
+
kv('hypothesis.ready_lead_count', result.hypothesis.ready_lead_count || (result.leads || []).filter((l) => l.status === 'ready').length || 0)
|
|
1236
1389
|
// Feedback — operator nudges left on this hypothesis, oldest first. The
|
|
1237
1390
|
// worker returns them; previously the CLI silently dropped them, so agents
|
|
1238
1391
|
// never saw the operator's running corrections. Now surface them up top
|
|
@@ -1256,9 +1409,11 @@ function printHypothesisContext(result) {
|
|
|
1256
1409
|
}
|
|
1257
1410
|
for (const run of (result.runs || [])) {
|
|
1258
1411
|
const shortRun = actionShortId(run.id)
|
|
1412
|
+
const runLeads = (result.leads || []).filter((l) => l.source_run_id === run.id)
|
|
1259
1413
|
kv(`run.${shortRun}.id`, run.id)
|
|
1260
1414
|
kv(`run.${shortRun}.run_number`, run.run_number)
|
|
1261
1415
|
kv(`run.${shortRun}.started_at`, run.started_at)
|
|
1416
|
+
kv(`run.${shortRun}.lead_count`, runLeads.length)
|
|
1262
1417
|
if (run.finished_at) kv(`run.${shortRun}.finished_at`, run.finished_at)
|
|
1263
1418
|
for (const a of (run.actions || [])) {
|
|
1264
1419
|
const shortA = actionShortId(a.id)
|
|
@@ -1285,6 +1440,16 @@ function printHypothesisContext(result) {
|
|
|
1285
1440
|
}
|
|
1286
1441
|
}
|
|
1287
1442
|
}
|
|
1443
|
+
for (const lead of (result.leads || [])) {
|
|
1444
|
+
const shortL = actionShortId(lead.id)
|
|
1445
|
+
kv(`lead.${shortL}.id`, lead.id)
|
|
1446
|
+
kv(`lead.${shortL}.status`, lead.status)
|
|
1447
|
+
kv(`lead.${shortL}.source_run_id`, lead.source_run_id)
|
|
1448
|
+
kv(`lead.${shortL}.person_name`, lead.person?.full_name)
|
|
1449
|
+
kv(`lead.${shortL}.person_email`, lead.person?.primary_email)
|
|
1450
|
+
kv(`lead.${shortL}.headline`, lead.person?.headline)
|
|
1451
|
+
kv(`lead.${shortL}.angle`, lead.angle)
|
|
1452
|
+
}
|
|
1288
1453
|
console.log('---')
|
|
1289
1454
|
// ---- narrative ----
|
|
1290
1455
|
console.log(`# ${result.product.slug}/${result.hypothesis.code} — ${result.hypothesis.title}\n`)
|
|
@@ -1297,9 +1462,21 @@ function printHypothesisContext(result) {
|
|
|
1297
1462
|
console.log(`- [${when}]${tag} ${f.text}`)
|
|
1298
1463
|
}
|
|
1299
1464
|
}
|
|
1465
|
+
if (result.leads?.length) {
|
|
1466
|
+
console.log(`\n\n## Leads (${result.leads.length})\n`)
|
|
1467
|
+
for (const lead of result.leads) {
|
|
1468
|
+
const person = lead.person || {}
|
|
1469
|
+
const label = person.full_name || person.primary_email || Object.entries(person.handles || {})[0]?.join(':') || lead.person_id || 'unknown person'
|
|
1470
|
+
const headline = person.headline ? ` — ${person.headline}` : ''
|
|
1471
|
+
const runTag = lead.source_run_id ? ` (run ${actionShortId(lead.source_run_id)})` : ''
|
|
1472
|
+
console.log(`- [${lead.status}] ${label}${headline}${runTag}`)
|
|
1473
|
+
if (lead.angle) console.log(` angle: ${lead.angle}`)
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1300
1476
|
for (const run of (result.runs || [])) {
|
|
1477
|
+
const runLeads = (result.leads || []).filter((l) => l.source_run_id === run.id)
|
|
1301
1478
|
console.log(`\n\n## Run ${run.run_number} (${run.id})`)
|
|
1302
|
-
console.log(`started: ${run.started_at}${run.finished_at ? ` finished: ${run.finished_at}` : ' (in progress)'}`)
|
|
1479
|
+
console.log(`started: ${run.started_at}${run.finished_at ? ` finished: ${run.finished_at}` : ' (in progress)'}${runLeads.length ? ` leads sourced: ${runLeads.length}` : ''}`)
|
|
1303
1480
|
if (run.actions?.length) {
|
|
1304
1481
|
console.log(`\nActions:`)
|
|
1305
1482
|
for (const a of run.actions) {
|
|
@@ -1382,6 +1559,14 @@ function readValue(value) {
|
|
|
1382
1559
|
return String(value)
|
|
1383
1560
|
}
|
|
1384
1561
|
|
|
1562
|
+
function parseJsonValue(value, label = 'json') {
|
|
1563
|
+
try {
|
|
1564
|
+
return JSON.parse(value)
|
|
1565
|
+
} catch (e) {
|
|
1566
|
+
throw new Error(`${label} must be valid JSON: ${e.message}`)
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1385
1570
|
function listOpt(value, label) {
|
|
1386
1571
|
if (value === undefined || value === null || value === '') {
|
|
1387
1572
|
if (label) throw new Error(`${label} is required`)
|
|
@@ -1468,10 +1653,11 @@ function usage() {
|
|
|
1468
1653
|
product pause|unpause stop / resume cron on a product
|
|
1469
1654
|
hypothesis create|status create or update hypotheses
|
|
1470
1655
|
hypothesis pause|unpause stop / resume work on a hypothesis
|
|
1656
|
+
lead add|template record sourced v2 leads
|
|
1471
1657
|
run start|finish start / finish a run
|
|
1472
1658
|
log action record one outreach touch
|
|
1473
1659
|
action escalate flag an action for human attention
|
|
1474
|
-
feedback record|delete leave a free-text nudge on a hypothesis
|
|
1660
|
+
feedback record|delete leave a free-text nudge on a hypothesis or product
|
|
1475
1661
|
context [<slug>|...] pull product or hypothesis context
|
|
1476
1662
|
|
|
1477
1663
|
mail setup|send|reply|... send/read mail via your AgentMail inbox
|
package/package.json
CHANGED