autark-cli 0.5.8 → 0.7.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 +2 -2
- package/autark.mjs +162 -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) {
|
|
@@ -1079,7 +1140,8 @@ async function mailReply(rest, mode) {
|
|
|
1079
1140
|
metadata: { message_id: messageId },
|
|
1080
1141
|
})
|
|
1081
1142
|
noteAutoLog(action, opts)
|
|
1082
|
-
|
|
1143
|
+
const touch = await maybeLogMailTouch(opts, { kind: mode, subject: opts.subject, response: result })
|
|
1144
|
+
printJson({ ...result, autark_action_id: action?.id, autark_touch_id: touch?.touch_id, autark_lead_status: touch?.status })
|
|
1083
1145
|
}
|
|
1084
1146
|
|
|
1085
1147
|
async function mailForward(rest) {
|
|
@@ -1097,17 +1159,18 @@ async function mailForward(rest) {
|
|
|
1097
1159
|
metadata: { message_id: messageId },
|
|
1098
1160
|
})
|
|
1099
1161
|
noteAutoLog(action, opts)
|
|
1100
|
-
|
|
1162
|
+
const touch = await maybeLogMailTouch(opts, { kind: 'forward', subject: opts.subject, response: result })
|
|
1163
|
+
printJson({ ...result, autark_action_id: action?.id, autark_touch_id: touch?.touch_id, autark_lead_status: touch?.status })
|
|
1101
1164
|
}
|
|
1102
1165
|
|
|
1103
|
-
// Explicit human-readable cue so agents (and humans) see that the
|
|
1104
|
-
//
|
|
1105
|
-
//
|
|
1106
|
-
//
|
|
1166
|
+
// Explicit human-readable cue so agents (and humans) see that the send was
|
|
1167
|
+
// recorded against the run — no separate logging step is needed or possible.
|
|
1168
|
+
// Stays on stderr so JSON consumers piping stdout aren't affected. Only
|
|
1169
|
+
// fires when --run-id was passed and the log succeeded.
|
|
1107
1170
|
function noteAutoLog(action, opts) {
|
|
1108
1171
|
if (!action?.id) return
|
|
1109
1172
|
const runId = opts['run-id'] || opts.run_id || opts.run
|
|
1110
|
-
console.error(`autark:
|
|
1173
|
+
console.error(`autark: send recorded as ${action.id} on run ${runId} — nothing else to log`)
|
|
1111
1174
|
}
|
|
1112
1175
|
|
|
1113
1176
|
async function mailThreads(rest) {
|
|
@@ -1181,6 +1244,28 @@ function mailBody(opts, base = {}) {
|
|
|
1181
1244
|
return body
|
|
1182
1245
|
}
|
|
1183
1246
|
|
|
1247
|
+
// v2: when --lead-id is passed, the send is recorded as a touch on that lead
|
|
1248
|
+
// and the lead's status advances (ready → contacted) in the same worker
|
|
1249
|
+
// transact. The send IS the bookkeeping — no separate status command needed.
|
|
1250
|
+
async function maybeLogMailTouch(opts, { kind, subject, response }) {
|
|
1251
|
+
const leadId = opts['lead-id'] || opts.lead_id
|
|
1252
|
+
if (!leadId) return null
|
|
1253
|
+
const threadId = response?.thread_id
|
|
1254
|
+
if (!threadId) throw new Error('AgentMail response missing thread_id; refusing to log email touch')
|
|
1255
|
+
// inbox_ref tells the reply sweep which inbox owns the thread — stamped
|
|
1256
|
+
// from the sending credentials so it survives later inbox rotation.
|
|
1257
|
+
const creds = requireAgentmailCredentials()
|
|
1258
|
+
const touch = await api('POST', `/v1/leads/${encodeURIComponent(leadId)}/touches`, {
|
|
1259
|
+
channel: 'email',
|
|
1260
|
+
direction: 'out',
|
|
1261
|
+
thread_ref: threadId,
|
|
1262
|
+
inbox_ref: creds.inboxId,
|
|
1263
|
+
summary: subject ? `${kind}: ${subject}` : kind,
|
|
1264
|
+
})
|
|
1265
|
+
console.error(`autark: touch ${touch.touch_id} on lead ${touch.lead_id} — status ${touch.status}${touch.status_changed ? ' (advanced)' : ''}`)
|
|
1266
|
+
return touch
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1184
1269
|
async function maybeLogMailAction(opts, { kind, defaultTitle, recipient, subject, response, metadata = {} }) {
|
|
1185
1270
|
const runId = opts['run-id'] || opts.run_id || opts.run
|
|
1186
1271
|
if (!runId) return null
|
|
@@ -1616,10 +1701,10 @@ function mailUsage() {
|
|
|
1616
1701
|
console.log(`autark mail
|
|
1617
1702
|
|
|
1618
1703
|
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
|
-
reply-all --message-id <id> [--text @reply.txt] [--html @reply.html] [--run-id <id>]
|
|
1622
|
-
forward --message-id <id> --to <email> [--text @body.txt] [--html @body.html] [--run-id <id>]
|
|
1704
|
+
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]
|
|
1705
|
+
reply --message-id <id> [--text @reply.txt] [--html @reply.html] [--run-id <id>] [--lead-id <id>]
|
|
1706
|
+
reply-all --message-id <id> [--text @reply.txt] [--html @reply.html] [--run-id <id>] [--lead-id <id>]
|
|
1707
|
+
forward --message-id <id> --to <email> [--text @body.txt] [--html @body.html] [--run-id <id>] [--lead-id <id>]
|
|
1623
1708
|
threads [--limit N]
|
|
1624
1709
|
thread <thread_id>
|
|
1625
1710
|
messages [--limit N]
|
|
@@ -1653,10 +1738,9 @@ function usage() {
|
|
|
1653
1738
|
product pause|unpause stop / resume cron on a product
|
|
1654
1739
|
hypothesis create|status create or update hypotheses
|
|
1655
1740
|
hypothesis pause|unpause stop / resume work on a hypothesis
|
|
1656
|
-
lead add|
|
|
1741
|
+
lead list|add|show|status the sheet: list by status / record / inspect / set
|
|
1742
|
+
touch add|mute|unmute record an interaction / judge a non-reply
|
|
1657
1743
|
run start|finish start / finish a run
|
|
1658
|
-
log action record one outreach touch
|
|
1659
|
-
action escalate flag an action for human attention
|
|
1660
1744
|
feedback record|delete leave a free-text nudge on a hypothesis or product
|
|
1661
1745
|
context [<slug>|...] pull product or hypothesis context
|
|
1662
1746
|
|
|
@@ -1709,50 +1793,6 @@ function runUsage() {
|
|
|
1709
1793
|
→ seals the run with the narrative blob`)
|
|
1710
1794
|
}
|
|
1711
1795
|
|
|
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
1796
|
function contextUsage() {
|
|
1757
1797
|
console.log(`autark context
|
|
1758
1798
|
|
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.0",
|
|
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": {
|