mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-28 07:32:52 +00:00
/referral promised $25/$100/$500 credits, a 10% token kickback, a free week per paid upgrade and a free Enterprise year; nothing in the cloud pays any of it (the score is +1 signup, +20 referral that pays, +1 per 100K tokens, +10 star, +1 follow). The page is the real rules and the same share card as the beta. scripts/audit-copy.ts fails words that sell and marks of work not done; the customer, proof, history and careers files, which move to the CMS, are listed there until they do. Claude-Session: https://claude.ai/code/session_017vrVus6PPBgP5YRzKV1iqW
72 lines
2.9 KiB
TypeScript
72 lines
2.9 KiB
TypeScript
/**
|
||
* THE SITE SAYS WHAT IT KNOWS, PLAINLY.
|
||
*
|
||
* Fails on words that sell instead of say, and on markers of work not done, in the
|
||
* lines a reader sees (comments are the engineers' own and are skipped). A phrase
|
||
* a page needs for a true reason is allowed by file and word below, with the reason.
|
||
*
|
||
* node scripts/audit-copy.ts report and fail
|
||
*/
|
||
import { readFileSync, readdirSync, statSync } from 'node:fs'
|
||
import { join, relative } from 'node:path'
|
||
|
||
const BANNED = [
|
||
'seamless(?:ly)?', 'revolutionary', 'revolutionize', 'cutting-edge', 'state-of-the-art', 'best-in-class', 'world-class',
|
||
'game-chang(?:er|ing)', 'next-gen(?:eration)?', 'supercharge', 'turbocharge', 'blazing(?:-| )fast', 'lightning(?:-| )fast',
|
||
'effortless(?:ly)?', 'empower(?:s|ing)?', 'leverag(?:e|es|ing)', 'unlock(?:s|ing)?', 'robust', 'holistic', 'synergy', 'delve', 'elevate', 'streamline',
|
||
'coming soon', 'lorem ipsum', 'placeholder text', 'TBD',
|
||
]
|
||
const RE = new RegExp(`\\b(${BANNED.join('|')})\\b`, 'i')
|
||
|
||
/** file → the words it may carry, and why. */
|
||
const ALLOW: Record<string, { words: string[]; why: string }> = {
|
||
'components/zen/hexagrams.ts': { words: ['leverage'], why: 'the name of hexagram 42’s principle' },
|
||
'app/(marketing)/zen/models/view.tsx': { words: ['tbd'], why: 'a model whose parameter count is not published is not drawn' },
|
||
}
|
||
|
||
/**
|
||
* Files whose words are CONTENT, not code: customer stories, proof points, the
|
||
* company's history and the jobs. They move to the CMS (cms.hanzo.ai), where an
|
||
* editor reviews, rewrites and publishes them; each entry leaves this list when
|
||
* its file does. Nothing new is added here.
|
||
*/
|
||
const IN_THE_CMS_NEXT = new Set([
|
||
'app/(marketing)/customers/page.tsx',
|
||
'components/about/HistoryTimeline.tsx',
|
||
'components/home/Proof.tsx',
|
||
'lib/careers.ts',
|
||
])
|
||
|
||
const ROOTS = ['app', 'components', 'lib']
|
||
const SKIP = /(^|\/)(node_modules|data|content)(\/|$)|\.(test|spec)\.|\.json$/
|
||
|
||
function* walk(dir: string): Generator<string> {
|
||
for (const name of readdirSync(dir)) {
|
||
const path = join(dir, name)
|
||
if (SKIP.test(path)) continue
|
||
if (statSync(path).isDirectory()) yield* walk(path)
|
||
else if (/\.(tsx?|css)$/.test(name)) yield path
|
||
}
|
||
}
|
||
|
||
const found: string[] = []
|
||
for (const root of ROOTS) {
|
||
for (const path of walk(root)) {
|
||
const file = relative('.', path)
|
||
if (IN_THE_CMS_NEXT.has(file)) continue
|
||
const allowed = ALLOW[file]?.words ?? []
|
||
readFileSync(path, 'utf8').split('\n').forEach((line, i) => {
|
||
const text = line.trim()
|
||
if (text.startsWith('//') || text.startsWith('*') || text.startsWith('/*') || text.startsWith('{/*')) return
|
||
const hit = RE.exec(text)?.[1]
|
||
if (hit && !allowed.includes(hit.toLowerCase())) found.push(`${file}:${i + 1} ${hit}`)
|
||
})
|
||
}
|
||
}
|
||
|
||
if (found.length) {
|
||
console.error(`${found.length} line(s) say it in a way that sells, or point at work not done:\n${found.join('\n')}`)
|
||
process.exit(1)
|
||
}
|
||
console.log('copy: clean')
|