autark-cli 0.5.0 → 0.5.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.
Files changed (2) hide show
  1. package/autark.mjs +22 -18
  2. package/package.json +1 -1
package/autark.mjs CHANGED
@@ -831,48 +831,52 @@ async function mailLint(rest) {
831
831
  'the key insight', 'what this means is', 'the answer is', 'the trick is',
832
832
  'that\'s exactly', 'that\'s precisely', 'that\'s the whole point',
833
833
  ]
834
+ const WHY = {
835
+ 'structure-word': 'AI-fingerprint vocabulary. Humans don\'t write "structurally different" or "fundamentally" in cold email; LLMs reach for them when hedging or sounding profound. Describe the thing directly instead.',
836
+ 'em-dash': 'Em-dashes in cold outreach are a strong AI tell. Real people use commas, periods, or a new line break.',
837
+ 'body-url': 'URLs mid-paragraph read like an A/B test variant or a campaign send. The product link belongs in the signature anchor; if it\'s anywhere else, the recipient classifies the email as automated.',
838
+ 'too-many-questions': 'Asking >1 question is hedging. Pick the one question that, if answered, gives you the most signal. The rest can wait.',
839
+ 'compound-question': 'Compound a/b questions ("are you X or Y...") read as AI hedging across alternatives. Humans ask one specific thing. Drop the "or" and pick the more specific half.',
840
+ 'too-long': 'Cold outreach over 400 chars (~4-6 short lines) reads as a pitch deck, not a peer note. If you wrote more, you\'re explaining instead of asking. Cut to the single sharpest sentence.',
841
+ 'no-anchor-sig': 'Without a clickable name in the signature, the recipient has to type your name into Google to figure out who you are. The HTML <a href> / markdown [name](url) sig is the single biggest "who is this person?" mitigator. Plain "Kushal" alone is a tell.',
842
+ }
834
843
 
835
844
  const violations = []
836
845
  const lower = body.toLowerCase()
837
846
 
838
- // 1. structure-words
839
847
  for (const w of STRUCTURE_WORDS) {
840
- if (lower.includes(w)) violations.push({ rule: 'structure-word', detail: w })
848
+ if (lower.includes(w)) violations.push({ rule: 'structure-word', detail: w, why: WHY['structure-word'] })
841
849
  }
842
- // 2. em-dash
843
- if (/—/.test(body)) violations.push({ rule: 'em-dash', detail: 'use a period or comma instead' })
844
- // 3. URLs in body (allow only inside markdown anchor [text](url) or HTML <a href>)
850
+ if (/—/.test(body)) violations.push({ rule: 'em-dash', detail: 'em-dash detected', why: WHY['em-dash'] })
845
851
  const urlMatches = body.match(/https?:\/\/[^\s<>"')]+/g) || []
846
852
  for (const u of urlMatches) {
847
853
  const idx = body.indexOf(u)
848
854
  const before = body.slice(Math.max(0, idx - 40), idx)
849
- const after = body.slice(idx + u.length, idx + u.length + 10)
850
855
  const insideMdAnchor = /\]\(\s*$/.test(before)
851
856
  const insideHtmlAnchor = /href\s*=\s*["'][^"']*$/.test(before) || /<a\b[^>]*$/.test(before)
852
- if (!insideMdAnchor && !insideHtmlAnchor) violations.push({ rule: 'body-url', detail: u })
857
+ if (!insideMdAnchor && !insideHtmlAnchor) violations.push({ rule: 'body-url', detail: u, why: WHY['body-url'] })
853
858
  }
854
- // 4. multiple question marks (more than one ? in the body)
855
859
  const qmarks = (body.match(/\?/g) || []).length
856
- if (qmarks > 1) violations.push({ rule: 'too-many-questions', detail: `${qmarks} question marks; cold outreach gets one max` })
857
- // 5. compound question with "or" connecting clauses
860
+ if (qmarks > 1) violations.push({ rule: 'too-many-questions', detail: `${qmarks} question marks`, why: WHY['too-many-questions'] })
858
861
  const compoundQ = /\b(are|is|do|does|did|will|would|can|could|should|have|has)\b[^?]{0,200}\bor\b[^?]{0,200}\?/i
859
- if (compoundQ.test(body)) violations.push({ rule: 'compound-question', detail: 'a/b question with "or" splits attention; ask one thing' })
860
- // 6. body length (excluding signature heuristically)
862
+ if (compoundQ.test(body)) violations.push({ rule: 'compound-question', detail: 'a/b question with "or"', why: WHY['compound-question'] })
861
863
  const sigSplit = body.split(/\n\s*\n(?:best|thanks|cheers|—|-)[\s,]*\n/i)[0] || body
862
864
  const len = sigSplit.trim().length
863
- if (len > 400) violations.push({ rule: 'too-long', detail: `${len} chars of body; cold outreach should fit in ~400` })
864
- // 7. signature presence: should have `- <name>` and an anchor (md or html)
865
+ if (len > 400) violations.push({ rule: 'too-long', detail: `${len} chars`, why: WHY['too-long'] })
865
866
  const hasMdAnchor = /\[[^\]]+\]\(https?:\/\/[^)]+\)/.test(body)
866
867
  const hasHtmlAnchor = /<a\b[^>]*href\s*=\s*["']https?:\/\/[^"']+["'][^>]*>[^<]+<\/a>/i.test(body)
867
- if (!hasMdAnchor && !hasHtmlAnchor) {
868
- violations.push({ rule: 'no-anchor-sig', detail: 'signature must include a clickable link via [name](url) or <a href="url">name</a>' })
869
- }
868
+ if (!hasMdAnchor && !hasHtmlAnchor) violations.push({ rule: 'no-anchor-sig', detail: 'no clickable name in signature', why: WHY['no-anchor-sig'] })
870
869
 
871
870
  if (violations.length === 0) {
872
871
  console.log(JSON.stringify({ clean: true, length: len }, null, 2))
873
872
  return
874
873
  }
875
- console.error(JSON.stringify({ clean: false, length: len, violations }, null, 2))
874
+ console.error(JSON.stringify({
875
+ clean: false,
876
+ length: len,
877
+ violations,
878
+ next: 'Rewrite the draft so each violation\'s "why" is internalized, not just regex-fixed. If you can\'t make it pass after 2 rewrites, abstain — re-read ~/.claude/skills/outreach/SKILL.md AI-tells section and ask whether you should send this at all.',
879
+ }, null, 2))
876
880
  process.exit(1)
877
881
  }
878
882
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autark-cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "CLI for autark \u2014 hypothesis-driven product runbooks. Track products, hypotheses, runs, and actions from the terminal.",
5
5
  "type": "module",
6
6
  "license": "MIT",