autark-cli 0.5.8 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/autark.mjs +168 -122
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
CLI for [autark](https://autark.sh) — hypothesis-driven product runbooks.
|
|
4
4
|
|
|
5
|
-
A product has hypotheses. Each hypothesis
|
|
5
|
+
A product has hypotheses. Each hypothesis frames leads — deduped people with a concrete angle, sourced by runs. The dashboard shows it all.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -29,7 +29,7 @@ autark context chrome-relay
|
|
|
29
29
|
|
|
30
30
|
autark hypothesis create --product-id <product_id> --md @./H01.md --code H01 --title "..."
|
|
31
31
|
autark run start --hypothesis-id <hypothesis_id>
|
|
32
|
-
autark
|
|
32
|
+
autark lead add --hypothesis-id <hypothesis_id> --run-id <run_id> --input @/tmp/lead.json
|
|
33
33
|
autark run finish --run-id <run_id> --narrative @./narrative.md
|
|
34
34
|
```
|
|
35
35
|
|
package/autark.mjs
CHANGED
|
@@ -24,6 +24,7 @@ const CREDS_PATH = process.env.AUTARK_CREDENTIALS || path.join(AUTARK_HOME, 'cre
|
|
|
24
24
|
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
|
+
const SKILL_AGENTS = ['claude-code', 'codex', 'pi', 'opencode']
|
|
27
28
|
const ECOSYSTEM_CLIS = ['autark-cli', 'plumcake-cli', 'chrome-relay']
|
|
28
29
|
const PROGRAM_FILES = ['start.md', 'find_leads.md', 'double-down.md', 'check.md', 'followup.md']
|
|
29
30
|
const LEAD_TEMPLATE = {
|
|
@@ -226,23 +227,23 @@ async function main() {
|
|
|
226
227
|
}
|
|
227
228
|
if (group === 'lead') {
|
|
228
229
|
if (command === 'add') return leadAdd(rest)
|
|
230
|
+
if (command === 'list') return leadList(rest)
|
|
231
|
+
if (command === 'show') return leadShow(rest)
|
|
232
|
+
if (command === 'status') return leadStatus(rest)
|
|
229
233
|
if (command === 'template') return leadTemplate()
|
|
230
234
|
if (!command || command === 'help' || command === '--help' || command === '-h') return leadUsage()
|
|
231
235
|
}
|
|
236
|
+
if (group === 'touch') {
|
|
237
|
+
if (command === 'add') return touchAdd(rest)
|
|
238
|
+
if (command === 'mute') return touchMute(rest, true)
|
|
239
|
+
if (command === 'unmute') return touchMute(rest, false)
|
|
240
|
+
if (!command || command === '--help' || command === '-h') return touchUsage()
|
|
241
|
+
}
|
|
232
242
|
if (group === 'run') {
|
|
233
243
|
if (command === 'start') return runStart(rest)
|
|
234
244
|
if (command === 'finish') return runFinish(rest)
|
|
235
245
|
if (!command || command === '--help' || command === '-h') return runUsage()
|
|
236
246
|
}
|
|
237
|
-
if (group === 'log') {
|
|
238
|
-
if (command === 'action') return logAction(rest)
|
|
239
|
-
if (!command || command === '--help' || command === '-h') return logUsage()
|
|
240
|
-
}
|
|
241
|
-
if (group === 'action') {
|
|
242
|
-
if (command === 'escalate') return actionEscalate(rest)
|
|
243
|
-
if (command === 'unescalate') return actionEscalate(rest, { clear: true })
|
|
244
|
-
if (!command || command === '--help' || command === '-h') return actionUsage()
|
|
245
|
-
}
|
|
246
247
|
if (group === 'feedback') {
|
|
247
248
|
if (command === 'record') return feedbackRecord(rest)
|
|
248
249
|
if (command === 'delete') return feedbackDelete(rest)
|
|
@@ -540,7 +541,13 @@ async function refreshRuntimeFiles() {
|
|
|
540
541
|
async function refreshSkills() {
|
|
541
542
|
// skills CLI switched from `-s name,name` to positional `name name`.
|
|
542
543
|
// The old comma-separated form silently matches nothing.
|
|
543
|
-
|
|
544
|
+
// Target only the agents we actually run — without -a the skills CLI
|
|
545
|
+
// sprays all ~71 known hosts and reports noisy failures for hosts that
|
|
546
|
+
// reject global installs (e.g. PromptScript).
|
|
547
|
+
// Like the skill names, agents must be repeated flags — `-a a,b` is read
|
|
548
|
+
// as one (invalid) agent name.
|
|
549
|
+
const agentFlags = SKILL_AGENTS.flatMap((a) => ['-a', a])
|
|
550
|
+
const res = await spawnSync('pnpm', ['dlx', '--silent', 'skills', 'add', 'kiluazen/kstack', ...SKILL_NAMES, ...agentFlags, '-y', '-g'])
|
|
544
551
|
if (res.status !== 0) throw new Error(`kstack skills install failed`)
|
|
545
552
|
}
|
|
546
553
|
|
|
@@ -705,6 +712,99 @@ async function leadAdd(rest) {
|
|
|
705
712
|
console.log(JSON.stringify(result))
|
|
706
713
|
}
|
|
707
714
|
|
|
715
|
+
async function leadStatus(rest) {
|
|
716
|
+
const opts = parseArgs(rest)
|
|
717
|
+
const id = required(opts['lead-id'] || opts.lead_id || opts._[0], 'lead id')
|
|
718
|
+
const status = required(opts.status, '--status (sourced|ready|contacted|replied|done|dead)')
|
|
719
|
+
const result = await api('PATCH', `/v1/leads/${encodeURIComponent(id)}/status`, { status })
|
|
720
|
+
console.log(JSON.stringify(result))
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// The agent's front door: one compact line per lead, id first, filtered by
|
|
724
|
+
// status. "Who replied?" = `autark lead list <slug> --status replied`.
|
|
725
|
+
async function leadList(rest) {
|
|
726
|
+
const opts = parseArgs(rest)
|
|
727
|
+
const product = required(opts.product || opts._[0], 'product slug')
|
|
728
|
+
const status = opts.status || ''
|
|
729
|
+
const qs = `?product=${encodeURIComponent(product)}${status ? `&status=${encodeURIComponent(status)}` : ''}`
|
|
730
|
+
const data = await api('GET', `/v1/leads${qs}`)
|
|
731
|
+
if (opts.json) { console.log(JSON.stringify(data, null, 2)); return }
|
|
732
|
+
if (!data.leads.length) { console.log(`no leads${status ? ` with status ${status}` : ''} in ${product}`); return }
|
|
733
|
+
for (const l of data.leads) {
|
|
734
|
+
const who = [l.full_name, l.email && `<${l.email}>`].filter(Boolean).join(' ')
|
|
735
|
+
console.log(`${l.lead_id} ${l.status}${l.hypothesis ? ` ${l.hypothesis}` : ''} ${who}`)
|
|
736
|
+
if (l.replied_at) console.log(` replied_at=${l.replied_at}${l.last_in_at ? ` last_in=${l.last_in_at}` : ''}`)
|
|
737
|
+
if (l.thread_ref) console.log(` thread=${l.thread_ref} inbox=${l.inbox_ref || ''}`)
|
|
738
|
+
if (l.angle) console.log(` angle: ${l.angle.length > 140 ? l.angle.slice(0, 140) + '…' : l.angle}`)
|
|
739
|
+
}
|
|
740
|
+
console.error(`${data.count} lead(s). Next: autark lead show <id> · autark mail thread <thread> · autark mail reply --lead-id <id>`)
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
async function leadShow(rest) {
|
|
744
|
+
const opts = parseArgs(rest)
|
|
745
|
+
const id = required(opts['lead-id'] || opts.lead_id || opts._[0], 'lead id')
|
|
746
|
+
const lead = await api('GET', `/v1/leads/${encodeURIComponent(id)}`)
|
|
747
|
+
console.log(JSON.stringify(lead, null, 2))
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// muted=true marks an inbound touch as a non-reply (ticket bot, autoresponder).
|
|
751
|
+
// The worker recomputes the lead verdict in the same write and the reply sweep
|
|
752
|
+
// skips muted touches, so the judgment sticks. --action-id targets a v1 action
|
|
753
|
+
// row instead (legacy bridge; the replies tab filters those at render).
|
|
754
|
+
async function touchMute(rest, muted) {
|
|
755
|
+
const opts = parseArgs(rest)
|
|
756
|
+
const actionId = opts['action-id'] || opts.action_id
|
|
757
|
+
if (actionId) {
|
|
758
|
+
const result = await api('PATCH', `/v1/actions/${encodeURIComponent(actionId)}/mute`, { muted })
|
|
759
|
+
console.log(JSON.stringify(result))
|
|
760
|
+
return
|
|
761
|
+
}
|
|
762
|
+
const id = required(opts['touch-id'] || opts.touch_id || opts._[0], 'touch id (or --action-id for v1 rows)')
|
|
763
|
+
const result = await api('PATCH', `/v1/touches/${encodeURIComponent(id)}/mute`, { muted })
|
|
764
|
+
console.log(JSON.stringify(result))
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
async function touchAdd(rest) {
|
|
768
|
+
const opts = parseArgs(rest)
|
|
769
|
+
const leadId = required(opts['lead-id'] || opts.lead_id, '--lead-id')
|
|
770
|
+
const channel = required(opts.channel, '--channel')
|
|
771
|
+
const result = await api('POST', `/v1/leads/${encodeURIComponent(leadId)}/touches`, {
|
|
772
|
+
channel,
|
|
773
|
+
direction: opts.direction === 'in' ? 'in' : 'out',
|
|
774
|
+
thread_ref: opts['thread-ref'] || opts.thread_ref || undefined,
|
|
775
|
+
inbox_ref: opts['inbox-ref'] || opts.inbox_ref || undefined,
|
|
776
|
+
summary: opts.summary || undefined,
|
|
777
|
+
occurred_at: opts['occurred-at'] || opts.occurred_at || undefined,
|
|
778
|
+
status: opts.status || undefined,
|
|
779
|
+
})
|
|
780
|
+
console.log(JSON.stringify(result))
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function touchUsage() {
|
|
784
|
+
console.log(`autark touch
|
|
785
|
+
|
|
786
|
+
add --lead-id <id> --channel <c>
|
|
787
|
+
[--direction out|in] # default out
|
|
788
|
+
[--thread-ref <ref>] # AgentMail thread id or public URL of YOUR comment
|
|
789
|
+
[--summary "<one line>"]
|
|
790
|
+
[--occurred-at <iso>]
|
|
791
|
+
[--status <s>] # override the automatic advance
|
|
792
|
+
|
|
793
|
+
One row per outreach interaction, recorded against an explicit lead id —
|
|
794
|
+
the CLI never guesses which lead an interaction belongs to. Writing the
|
|
795
|
+
touch advances lead.status in the same transaction (out: ready→contacted,
|
|
796
|
+
in: →replied) unless --status overrides it.
|
|
797
|
+
|
|
798
|
+
Email is a special case: \`autark mail send --lead-id <id>\` records the
|
|
799
|
+
touch automatically — do not call \`touch add\` for the same send.
|
|
800
|
+
|
|
801
|
+
mute <touch-id> mark an inbound touch as a non-reply (bot,
|
|
802
|
+
autoresponder); lead verdict recomputes and
|
|
803
|
+
the reply sweep respects the judgment
|
|
804
|
+
mute --action-id <id> same, for a legacy v1 action row
|
|
805
|
+
unmute <touch-id> reverse it`)
|
|
806
|
+
}
|
|
807
|
+
|
|
708
808
|
function leadTemplate() {
|
|
709
809
|
printJson(LEAD_TEMPLATE)
|
|
710
810
|
}
|
|
@@ -718,6 +818,19 @@ function leadUsage() {
|
|
|
718
818
|
add --hypothesis-id <id> --input @/tmp/lead.json [--run-id <id>]
|
|
719
819
|
add --hypothesis <slug>/<H01> --input @/tmp/lead.json [--run-id <id>]
|
|
720
820
|
|
|
821
|
+
list <slug> [--status replied|contacted|ready|...] [--json]
|
|
822
|
+
the front door: one line per lead, ids first, newest replies on top.
|
|
823
|
+
"who replied?" = lead list <slug> --status replied
|
|
824
|
+
|
|
825
|
+
show <lead-id>
|
|
826
|
+
person + bet + status + the ordered touch log with ids — run this
|
|
827
|
+
when handed a lead link, before replying or muting
|
|
828
|
+
|
|
829
|
+
status <lead-id> --status sourced|ready|contacted|replied|done|dead
|
|
830
|
+
explicit status write — normally NOT needed: sending via
|
|
831
|
+
\`autark mail send --lead-id\` or \`autark touch add\` advances
|
|
832
|
+
status automatically in the same transaction
|
|
833
|
+
|
|
721
834
|
lead add records one sourced lead: person identity + the angle connecting that
|
|
722
835
|
person to the hypothesis. It sends one ID-first request to the worker and
|
|
723
836
|
prints exactly one compact JSON object to stdout:
|
|
@@ -763,59 +876,6 @@ async function runFinish(rest) {
|
|
|
763
876
|
console.log(`${result.id} finished_at=${result.finished_at}`)
|
|
764
877
|
}
|
|
765
878
|
|
|
766
|
-
// ============================================================ actions
|
|
767
|
-
|
|
768
|
-
async function logAction(rest) {
|
|
769
|
-
const opts = parseArgs(rest)
|
|
770
|
-
const run = required(opts['run-id'] || opts.run_id || opts.run, '--run-id')
|
|
771
|
-
const channel = required(opts.channel, '--channel')
|
|
772
|
-
const title = required(opts.title, '--title')
|
|
773
|
-
const needsHuman = Boolean(opts['needs-human'] || opts.needs_human || opts.escalate)
|
|
774
|
-
const escalationReason = opts['escalation-reason'] || opts.escalation_reason || opts.reason || undefined
|
|
775
|
-
|
|
776
|
-
// Merge `our_comment_id` into metadata if passed as a sugar flag. This is
|
|
777
|
-
// the per-channel anchor the reply-state cron uses to detect engagement on
|
|
778
|
-
// OUR specific comment (vs the parent we were replying to). Always pass
|
|
779
|
-
// it when you posted a comment somewhere we can re-fetch — github issue/PR
|
|
780
|
-
// comments, gists, HN replies, Substack comments, Reddit comments.
|
|
781
|
-
const meta = opts.metadata ? JSON.parse(readValue(opts.metadata)) : undefined
|
|
782
|
-
const ourCommentId = opts['our-comment-id'] || opts.our_comment_id
|
|
783
|
-
const metadata = ourCommentId
|
|
784
|
-
? { ...(meta || {}), our_comment_id: String(ourCommentId) }
|
|
785
|
-
: meta
|
|
786
|
-
|
|
787
|
-
const result = await api('POST', `/v1/runs/${encodeURIComponent(run)}/actions`, {
|
|
788
|
-
channel,
|
|
789
|
-
title,
|
|
790
|
-
url: opts.url || undefined,
|
|
791
|
-
agentmail_thread_id: opts['agentmail-thread-id'] || opts['thread-id'] || opts.thread_id || undefined,
|
|
792
|
-
agentmail_inbox_id: opts['agentmail-inbox-id'] || opts.inbox_id || opts.agentmail_inbox_id || undefined,
|
|
793
|
-
recipient: opts.recipient || undefined,
|
|
794
|
-
metadata,
|
|
795
|
-
needs_human: needsHuman || undefined,
|
|
796
|
-
escalation_reason: needsHuman ? escalationReason : undefined,
|
|
797
|
-
occurred_at: opts['occurred-at'] || opts.occurred_at || undefined,
|
|
798
|
-
})
|
|
799
|
-
console.log(result.id)
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
// ============================================================ action escalate
|
|
803
|
-
|
|
804
|
-
async function actionEscalate(rest, { clear = false } = {}) {
|
|
805
|
-
const opts = parseArgs(rest)
|
|
806
|
-
const id = required(opts['action-id'] || opts.action_id || opts._[0], 'action id')
|
|
807
|
-
const reason = opts.reason || opts._.slice(1).join(' ') || undefined
|
|
808
|
-
const body = { needs_human: !clear }
|
|
809
|
-
if (!clear) body.reason = reason || ''
|
|
810
|
-
const result = await api('PATCH', `/v1/actions/${encodeURIComponent(id)}/escalate`, body)
|
|
811
|
-
console.log(`${result.id} needs_human=${result.needs_human}${result.escalation_reason ? ` reason="${result.escalation_reason}"` : ''}`)
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
function actionUsage() {
|
|
815
|
-
console.log(`autark action escalate <action-id> [--reason "<one-line why>"]
|
|
816
|
-
autark action unescalate <action-id>`)
|
|
817
|
-
}
|
|
818
|
-
|
|
819
879
|
// ============================================================ product pause
|
|
820
880
|
|
|
821
881
|
async function productPause(rest, fixedStatus) {
|
|
@@ -1061,7 +1121,8 @@ async function mailSend(rest) {
|
|
|
1061
1121
|
response: result,
|
|
1062
1122
|
})
|
|
1063
1123
|
noteAutoLog(action, opts)
|
|
1064
|
-
|
|
1124
|
+
const touch = await maybeLogMailTouch(opts, { kind: 'send', subject, response: result })
|
|
1125
|
+
printJson({ ...result, autark_action_id: action?.id, autark_touch_id: touch?.touch_id, autark_lead_status: touch?.status })
|
|
1065
1126
|
}
|
|
1066
1127
|
|
|
1067
1128
|
async function mailReply(rest, mode) {
|
|
@@ -1069,6 +1130,10 @@ async function mailReply(rest, mode) {
|
|
|
1069
1130
|
const creds = requireAgentmailCredentials()
|
|
1070
1131
|
const messageId = required(opts['message-id'] || opts.message_id || opts._[0], '--message-id')
|
|
1071
1132
|
const body = mailBody(opts)
|
|
1133
|
+
// Reply addresses the SENDER of the target message. Following up on your
|
|
1134
|
+
// own last message therefore self-sends unless you override the recipient.
|
|
1135
|
+
// --to forces who receives the reply (the follow-up case).
|
|
1136
|
+
if (opts.to !== undefined) body.to = listOpt(opts.to, '--to')
|
|
1072
1137
|
const endpoint = mode === 'reply-all' ? 'reply-all' : 'reply'
|
|
1073
1138
|
const result = await agentmailRequest('POST', `/inboxes/${encodeURIComponent(creds.inboxId)}/messages/${encodeURIComponent(messageId)}/${endpoint}`, body)
|
|
1074
1139
|
const action = await maybeLogMailAction(opts, {
|
|
@@ -1079,7 +1144,8 @@ async function mailReply(rest, mode) {
|
|
|
1079
1144
|
metadata: { message_id: messageId },
|
|
1080
1145
|
})
|
|
1081
1146
|
noteAutoLog(action, opts)
|
|
1082
|
-
|
|
1147
|
+
const touch = await maybeLogMailTouch(opts, { kind: mode, subject: opts.subject, response: result })
|
|
1148
|
+
printJson({ ...result, autark_action_id: action?.id, autark_touch_id: touch?.touch_id, autark_lead_status: touch?.status })
|
|
1083
1149
|
}
|
|
1084
1150
|
|
|
1085
1151
|
async function mailForward(rest) {
|
|
@@ -1097,17 +1163,18 @@ async function mailForward(rest) {
|
|
|
1097
1163
|
metadata: { message_id: messageId },
|
|
1098
1164
|
})
|
|
1099
1165
|
noteAutoLog(action, opts)
|
|
1100
|
-
|
|
1166
|
+
const touch = await maybeLogMailTouch(opts, { kind: 'forward', subject: opts.subject, response: result })
|
|
1167
|
+
printJson({ ...result, autark_action_id: action?.id, autark_touch_id: touch?.touch_id, autark_lead_status: touch?.status })
|
|
1101
1168
|
}
|
|
1102
1169
|
|
|
1103
|
-
// Explicit human-readable cue so agents (and humans) see that the
|
|
1104
|
-
//
|
|
1105
|
-
//
|
|
1106
|
-
//
|
|
1170
|
+
// Explicit human-readable cue so agents (and humans) see that the send was
|
|
1171
|
+
// recorded against the run — no separate logging step is needed or possible.
|
|
1172
|
+
// Stays on stderr so JSON consumers piping stdout aren't affected. Only
|
|
1173
|
+
// fires when --run-id was passed and the log succeeded.
|
|
1107
1174
|
function noteAutoLog(action, opts) {
|
|
1108
1175
|
if (!action?.id) return
|
|
1109
1176
|
const runId = opts['run-id'] || opts.run_id || opts.run
|
|
1110
|
-
console.error(`autark:
|
|
1177
|
+
console.error(`autark: send recorded as ${action.id} on run ${runId} — nothing else to log`)
|
|
1111
1178
|
}
|
|
1112
1179
|
|
|
1113
1180
|
async function mailThreads(rest) {
|
|
@@ -1181,6 +1248,28 @@ function mailBody(opts, base = {}) {
|
|
|
1181
1248
|
return body
|
|
1182
1249
|
}
|
|
1183
1250
|
|
|
1251
|
+
// v2: when --lead-id is passed, the send is recorded as a touch on that lead
|
|
1252
|
+
// and the lead's status advances (ready → contacted) in the same worker
|
|
1253
|
+
// transact. The send IS the bookkeeping — no separate status command needed.
|
|
1254
|
+
async function maybeLogMailTouch(opts, { kind, subject, response }) {
|
|
1255
|
+
const leadId = opts['lead-id'] || opts.lead_id
|
|
1256
|
+
if (!leadId) return null
|
|
1257
|
+
const threadId = response?.thread_id
|
|
1258
|
+
if (!threadId) throw new Error('AgentMail response missing thread_id; refusing to log email touch')
|
|
1259
|
+
// inbox_ref tells the reply sweep which inbox owns the thread — stamped
|
|
1260
|
+
// from the sending credentials so it survives later inbox rotation.
|
|
1261
|
+
const creds = requireAgentmailCredentials()
|
|
1262
|
+
const touch = await api('POST', `/v1/leads/${encodeURIComponent(leadId)}/touches`, {
|
|
1263
|
+
channel: 'email',
|
|
1264
|
+
direction: 'out',
|
|
1265
|
+
thread_ref: threadId,
|
|
1266
|
+
inbox_ref: creds.inboxId,
|
|
1267
|
+
summary: subject ? `${kind}: ${subject}` : kind,
|
|
1268
|
+
})
|
|
1269
|
+
console.error(`autark: touch ${touch.touch_id} on lead ${touch.lead_id} — status ${touch.status}${touch.status_changed ? ' (advanced)' : ''}`)
|
|
1270
|
+
return touch
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1184
1273
|
async function maybeLogMailAction(opts, { kind, defaultTitle, recipient, subject, response, metadata = {} }) {
|
|
1185
1274
|
const runId = opts['run-id'] || opts.run_id || opts.run
|
|
1186
1275
|
if (!runId) return null
|
|
@@ -1616,10 +1705,12 @@ function mailUsage() {
|
|
|
1616
1705
|
console.log(`autark mail
|
|
1617
1706
|
|
|
1618
1707
|
setup --prefix <name> [--force]
|
|
1619
|
-
send --to <email[,email]> --subject <s> [--text @body.txt] [--html @body.html] [--cc <e>] [--bcc <e>] [--reply-to <e>] [--label <l>] [--attachment <a>] [--run-id <id>] [--dry-run]
|
|
1620
|
-
reply --message-id <id> [--text @reply.txt] [--html @reply.html] [--run-id <id>]
|
|
1621
|
-
|
|
1622
|
-
|
|
1708
|
+
send --to <email[,email]> --subject <s> [--text @body.txt] [--html @body.html] [--cc <e>] [--bcc <e>] [--reply-to <e>] [--label <l>] [--attachment <a>] [--run-id <id>] [--lead-id <id>] [--dry-run]
|
|
1709
|
+
reply --message-id <id> [--to <email>] [--text @reply.txt] [--html @reply.html] [--run-id <id>] [--lead-id <id>]
|
|
1710
|
+
--to overrides the recipient — REQUIRED when following up on your
|
|
1711
|
+
own message (plain reply answers its sender: you)
|
|
1712
|
+
reply-all --message-id <id> [--text @reply.txt] [--html @reply.html] [--run-id <id>] [--lead-id <id>]
|
|
1713
|
+
forward --message-id <id> --to <email> [--text @body.txt] [--html @body.html] [--run-id <id>] [--lead-id <id>]
|
|
1623
1714
|
threads [--limit N]
|
|
1624
1715
|
thread <thread_id>
|
|
1625
1716
|
messages [--limit N]
|
|
@@ -1653,10 +1744,9 @@ function usage() {
|
|
|
1653
1744
|
product pause|unpause stop / resume cron on a product
|
|
1654
1745
|
hypothesis create|status create or update hypotheses
|
|
1655
1746
|
hypothesis pause|unpause stop / resume work on a hypothesis
|
|
1656
|
-
lead add|
|
|
1747
|
+
lead list|add|show|status the sheet: list by status / record / inspect / set
|
|
1748
|
+
touch add|mute|unmute record an interaction / judge a non-reply
|
|
1657
1749
|
run start|finish start / finish a run
|
|
1658
|
-
log action record one outreach touch
|
|
1659
|
-
action escalate flag an action for human attention
|
|
1660
1750
|
feedback record|delete leave a free-text nudge on a hypothesis or product
|
|
1661
1751
|
context [<slug>|...] pull product or hypothesis context
|
|
1662
1752
|
|
|
@@ -1709,50 +1799,6 @@ function runUsage() {
|
|
|
1709
1799
|
→ seals the run with the narrative blob`)
|
|
1710
1800
|
}
|
|
1711
1801
|
|
|
1712
|
-
function logUsage() {
|
|
1713
|
-
console.log(`autark log
|
|
1714
|
-
|
|
1715
|
-
action --run-id <id> --channel <c> --title <t>
|
|
1716
|
-
[--url <u>] # github / reddit / hn / blog / gist / linkedin
|
|
1717
|
-
[--agentmail-thread-id <uuid>] # email
|
|
1718
|
-
[--agentmail-inbox-id <email>] # email — defaults to your inbox
|
|
1719
|
-
[--recipient <email>] # email
|
|
1720
|
-
[--our-comment-id <id>] # see "Always capture our_comment_id" below
|
|
1721
|
-
[--metadata @./meta.json] # any other channel-specific extras
|
|
1722
|
-
[--escalate --reason "<why>"] # flag this action as needs_human
|
|
1723
|
-
|
|
1724
|
-
One row per external touch. Body content stays in AgentMail/GitHub/etc;
|
|
1725
|
-
this just records the pointer + the title agents see on the dashboard.
|
|
1726
|
-
|
|
1727
|
-
Log the URL of YOUR comment, not the parent
|
|
1728
|
-
|
|
1729
|
-
The reply-state cron parses the comment id out of the --url you log to
|
|
1730
|
-
detect "did anyone reply to you?". Most channels' URLs already contain
|
|
1731
|
-
the id; you just have to log the permalink of your OWN comment, not the
|
|
1732
|
-
issue/post URL you replied to.
|
|
1733
|
-
|
|
1734
|
-
github (issue or PR comment):
|
|
1735
|
-
use the response's html_url, ends in #issuecomment-<id>
|
|
1736
|
-
NEW_URL=$(gh api repos/<o>/<r>/issues/<n>/comments \\
|
|
1737
|
-
-X POST -f body=@body.md --jq .html_url)
|
|
1738
|
-
|
|
1739
|
-
gist:
|
|
1740
|
-
ends in #gistcomment-<id>
|
|
1741
|
-
|
|
1742
|
-
hn:
|
|
1743
|
-
.../item?id=<your_new_item_id> (your id, not the parent's)
|
|
1744
|
-
|
|
1745
|
-
reddit:
|
|
1746
|
-
.../r/<sub>/comments/<thread>/<title>/<your_comment_id>/
|
|
1747
|
-
|
|
1748
|
-
substack:
|
|
1749
|
-
Substack permalinks don't always include the comment id, so for
|
|
1750
|
-
this channel use --our-comment-id <id> from the response body.
|
|
1751
|
-
|
|
1752
|
-
Escape hatch: --our-comment-id <id> tucks the id into metadata. Use it
|
|
1753
|
-
when the URL pattern can't encode the comment (Substack, edge HN cases).`)
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
1802
|
function contextUsage() {
|
|
1757
1803
|
console.log(`autark context
|
|
1758
1804
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autark-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CLI for autark
|
|
3
|
+
"version": "0.7.1",
|
|
4
|
+
"description": "CLI for autark — hypothesis-driven product runbooks. Track products, hypotheses, runs, and actions from the terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|