@oneie/claude 0.7.0 → 0.9.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/agents/abm-strategist.md +89 -0
- package/agents/ads-meta.md +97 -0
- package/agents/analyst.md +173 -0
- package/agents/architect.md +280 -0
- package/agents/brand-guardian.md +88 -0
- package/agents/brand-strategist.md +92 -0
- package/agents/campaign-content.md +90 -0
- package/agents/campaign-email.md +88 -0
- package/agents/campaign-sms.md +87 -0
- package/agents/campaign-social.md +87 -0
- package/agents/cco.md +99 -0
- package/agents/ceo.md +106 -0
- package/agents/chairman.md +120 -0
- package/agents/cmo.md +101 -0
- package/agents/community-greeter.md +89 -0
- package/agents/community-moderator.md +92 -0
- package/agents/compliance.md +92 -0
- package/agents/copywriter.md +100 -0
- package/agents/creative-strategist.md +95 -0
- package/agents/cro.md +98 -0
- package/agents/cto.md +267 -0
- package/agents/customer-interviewer.md +93 -0
- package/agents/customer-researcher.md +89 -0
- package/agents/customer-success-manager.md +90 -0
- package/agents/customer-trainer.md +90 -0
- package/agents/cxo.md +100 -0
- package/agents/demand-creator.md +89 -0
- package/agents/demo-mover.md +83 -0
- package/agents/demo-specialist.md +89 -0
- package/agents/demo-thai-family-law.md +87 -0
- package/agents/designer.md +112 -0
- package/agents/discovery-caller.md +98 -0
- package/agents/doctor.md +139 -0
- package/agents/educate-coach.md +100 -0
- package/agents/elevate-tutor.md +83 -0
- package/agents/email-lifecycle-marketer.md +91 -0
- package/agents/engage-specialist.md +99 -0
- package/agents/events-coordinator.md +90 -0
- package/agents/foundation-builder.md +94 -0
- package/agents/funnel-architect.md +89 -0
- package/agents/gift-creator.md +100 -0
- package/agents/google-ads.md +103 -0
- package/agents/guide.md +292 -0
- package/agents/helpdesk-dispatcher.md +88 -0
- package/agents/hook-specialist.md +91 -0
- package/agents/identify-optimizer.md +101 -0
- package/agents/implementer.md +305 -0
- package/agents/incident-commander.md +120 -0
- package/agents/insights-lead.md +93 -0
- package/agents/journey-runner.md +113 -0
- package/agents/linkedin-ads.md +123 -0
- package/agents/live-sales-chat.md +90 -0
- package/agents/market-researcher.md +88 -0
- package/agents/media-buyer.md +96 -0
- package/agents/movers-customer-researcher.md +95 -0
- package/agents/movers-foundation-builder.md +96 -0
- package/agents/movers-market-researcher.md +97 -0
- package/agents/movers-pricing-strategist.md +171 -0
- package/agents/nurture-architect.md +99 -0
- package/agents/offer-architect.md +92 -0
- package/agents/onboarder.md +222 -0
- package/agents/onboarding-specialist.md +92 -0
- package/agents/operations-dashboard.md +98 -0
- package/agents/perf-engineer.md +348 -0
- package/agents/playbook-writer.md +71 -0
- package/agents/plg-strategist.md +91 -0
- package/agents/positioning-architect.md +88 -0
- package/agents/press-officer.md +89 -0
- package/agents/pricing-strategist.md +94 -0
- package/agents/privacy-officer.md +92 -0
- package/agents/referral-manager.md +91 -0
- package/agents/refine-analyst.md +102 -0
- package/agents/release-manager.md +261 -0
- package/agents/renewals-upsell-rep.md +90 -0
- package/agents/review-engineer.md +327 -0
- package/agents/rewards-steward.md +82 -0
- package/agents/sales-call-coach.md +94 -0
- package/agents/sales-closer.md +98 -0
- package/agents/security-auditor.md +313 -0
- package/agents/sell-closer.md +101 -0
- package/agents/share-amplifier.md +99 -0
- package/agents/social-media-manager.md +92 -0
- package/agents/storyteller.md +250 -0
- package/agents/strategist.md +83 -0
- package/agents/strategy-aligner.md +94 -0
- package/agents/support-agent.md +90 -0
- package/agents/tagger.md +245 -0
- package/agents/tech-writer.md +216 -0
- package/agents/test-engineer.md +243 -0
- package/agents/tiktok-ads.md +105 -0
- package/agents/tracking-engineer.md +92 -0
- package/agents/upsell-strategist.md +101 -0
- package/agents/voice.md +85 -0
- package/agents/w4-tools.md +153 -0
- package/agents/w4-verify.md +20 -138
- package/agents/workflow-optimiser.md +84 -0
- package/commands/close.md +814 -160
- package/commands/create.md +2 -2
- package/commands/deploy.md +554 -175
- package/commands/do.md +119 -109
- package/commands/fast.md +67 -0
- package/commands/improve.md +2 -2
- package/commands/one.md +418 -0
- package/commands/see.md +1 -1
- package/commands/sync.md +1 -1
- package/commands/tasks.md +222 -0
- package/commands/vespio.md +110 -0
- package/commands/vespio.remote.md +227 -0
- package/hooks/hooks.json +25 -79
- package/hooks/lib/governor-escape-match.sh +88 -0
- package/hooks/lib/hook.sh +4 -13
- package/hooks/lib/signal.sh +9 -2
- package/hooks/scripts/branch-pin.sh +6 -0
- package/hooks/scripts/config-protect.sh +6 -0
- package/hooks/scripts/do-outcome-gate.sh +19 -2
- package/hooks/scripts/git-add-guard.sh +43 -2
- package/hooks/scripts/governor-escape.sh +96 -0
- package/hooks/scripts/load-guard.sh +127 -0
- package/hooks/scripts/post-edit-check.sh +36 -29
- package/hooks/scripts/session-start.sh +34 -80
- package/hooks/scripts/task-complete-verify.sh +46 -40
- package/package.json +1 -1
- package/rules/documentation.md +9 -0
- package/scripts/ad-copy-lint.sh +656 -0
- package/scripts/agentverse-audit.sh +256 -0
- package/scripts/asi-walk.sh +435 -0
- package/scripts/astro-build-cached.sh +143 -0
- package/scripts/blocks-manifest-cached.sh +100 -0
- package/scripts/blocks-manifest.mjs +497 -0
- package/scripts/blocks-ratchet.sh +116 -0
- package/scripts/blocks-render-probe.mjs +529 -0
- package/scripts/blocks-usage.mjs +334 -0
- package/scripts/chat-context-check.sh +89 -0
- package/scripts/close-metrics.sh +558 -0
- package/scripts/close-owner.sh +268 -0
- package/scripts/db-sync-lock-check.sh +116 -0
- package/scripts/deploy-dev.sh +35 -0
- package/scripts/deploy-emit.sh +298 -0
- package/scripts/deploy-gate-check.sh +128 -0
- package/scripts/deploy-ready.sh +78 -0
- package/scripts/deploy-record.sh +605 -0
- package/scripts/deploy.sh +1273 -0
- package/scripts/do-auto.sh +598 -48
- package/scripts/do-brief.sh +113 -0
- package/scripts/do-close.sh +1137 -0
- package/scripts/do-commit.sh +75 -0
- package/scripts/do-consumer-sweep.sh +18 -1
- package/scripts/do-cycle-shape-check.sh +160 -0
- package/scripts/do-decide.sh +476 -0
- package/scripts/do-derive-check.sh +436 -0
- package/scripts/do-fleet.sh +106 -28
- package/scripts/do-folder.sh +10 -1
- package/scripts/do-next.sh +106 -0
- package/scripts/do-orchestrate.sh +17 -5
- package/scripts/do-plan-json.mjs +201 -0
- package/scripts/do-plan-json.sh +8 -0
- package/scripts/do-preflight.sh +117 -0
- package/scripts/do-project.sh +157 -0
- package/scripts/do-prove-selftest.sh +108 -0
- package/scripts/do-prove.sh +295 -23
- package/scripts/do-rank.py +31 -1
- package/scripts/do-recon-cache.sh +7 -1
- package/scripts/do-recon-pack.sh +196 -0
- package/scripts/do-reconcile.sh +121 -4
- package/scripts/do-signal.sh +280 -23
- package/scripts/do-smoke.sh +18 -1
- package/scripts/do-test-gate.sh +80 -0
- package/scripts/do-tick.sh +102 -0
- package/scripts/do-tier.sh +6 -0
- package/scripts/do-triage.sh +182 -0
- package/scripts/do-ui-gate.sh +1 -1
- package/scripts/do-w4-gates.sh +451 -0
- package/scripts/do-walk.sh +12 -1
- package/scripts/env-sync.sh +173 -0
- package/scripts/factory-brief-check.sh +330 -0
- package/scripts/factory-check.sh +68 -14
- package/scripts/factory-close-check.sh +257 -0
- package/scripts/factory-emit.sh +211 -0
- package/scripts/factory-executor-check.mjs +353 -0
- package/scripts/factory-peak.sh +301 -0
- package/scripts/factory-repo.sh +118 -3
- package/scripts/factory-review-check.mjs +61 -0
- package/scripts/factory-ship.sh +61 -0
- package/scripts/factory-tasks-check.sh +18 -1
- package/scripts/factory-turn.sh +326 -0
- package/scripts/factory-walk.sh +396 -0
- package/scripts/factory-width.sh +57 -0
- package/scripts/fade-toxic.sh +4 -3
- package/scripts/fixtures/factory-brief-real.md +44 -0
- package/scripts/fixtures/triage-dupe.md +5 -0
- package/scripts/fleet-manifest.mjs +108 -0
- package/scripts/fleet-status.sh +110 -0
- package/scripts/full-suite-paths-check.sh +144 -0
- package/scripts/gate-reaper-check.sh +98 -0
- package/scripts/gate-reaper.sh +125 -0
- package/scripts/gate-run.sh +73 -0
- package/scripts/gc-content-check.sh +140 -0
- package/scripts/gen-dev-config.py +20 -0
- package/scripts/govern-bound-check.sh +60 -0
- package/scripts/govern-claims-check.sh +233 -0
- package/scripts/govern-mem-check.sh +290 -0
- package/scripts/governor-doors-check.sh +362 -0
- package/scripts/governor-escape-check.sh +171 -0
- package/scripts/health.sh +413 -0
- package/scripts/id-inventory.mjs +418 -0
- package/scripts/land.sh +551 -0
- package/scripts/lib/gc-finished.sh +77 -0
- package/scripts/lib/govern.sh +361 -0
- package/scripts/lib/govern.ts +756 -0
- package/scripts/lighthouse-run.sh +187 -0
- package/scripts/livekit-live-check.sh +61 -0
- package/scripts/livekit-ratchet.sh +46 -0
- package/scripts/load-guard-check.sh +49 -0
- package/scripts/machine-check.sh +102 -0
- package/scripts/machine-watch.sh +177 -0
- package/scripts/one-agents.mjs +415 -0
- package/scripts/one-resume.sh +78 -0
- package/scripts/orphan-baseline.json +182 -0
- package/scripts/orphan-modules.mjs +179 -0
- package/scripts/pr-body.sh +335 -0
- package/scripts/preview-fd-check.sh +289 -0
- package/scripts/promise-manifest.mjs +24 -1
- package/scripts/release.sh +322 -0
- package/scripts/roles-check.sh +946 -0
- package/scripts/sdk-build-cached.sh +64 -0
- package/scripts/signal-watch.sh +241 -0
- package/scripts/skills-publish.sh +94 -0
- package/scripts/speed-cache-check.sh +173 -0
- package/scripts/speed-check.mjs +907 -0
- package/scripts/speed-parity-check.sh +648 -0
- package/scripts/speed-waterfall-check.sh +355 -0
- package/scripts/substrate-env-parity.mjs +156 -0
- package/scripts/tasks-claim-race.mjs +108 -0
- package/scripts/tasks-loop.sh +185 -0
- package/scripts/test-cached.sh +255 -0
- package/scripts/test-full.sh +87 -0
- package/scripts/test-honesty.mjs +137 -0
- package/scripts/test-lanes.sh +166 -0
- package/scripts/test-speed.sh +94 -0
- package/scripts/triage-shape-check.sh +149 -0
- package/scripts/tsc-cached.sh +179 -0
- package/scripts/typedb-flake-check.sh +197 -0
- package/scripts/urls-lint.sh +15 -0
- package/scripts/verify-fast.sh +445 -0
- package/scripts/vespio-sync.sh +149 -0
- package/scripts/wf-check.mjs +104 -1
- package/scripts/worktree-preview.sh +879 -0
- package/scripts/worktree-up.sh +208 -0
- package/skills/livekit-agents/SKILL.md +285 -0
- package/skills/livekit-agents/references/freshness-rules.md +168 -0
- package/skills/shadcn/SKILL.md +1 -1
- package/skills/signal/SKILL.md +0 -1
- package/skills/voice/SKILL.md +94 -6
- package/skills/voice/corpus-check.sh +87 -0
- package/hooks/scripts/compact-hint.sh +0 -35
- package/hooks/scripts/gate-guard.sh +0 -83
- package/hooks/scripts/read-tracker.sh +0 -26
- package/hooks/scripts/session-end-verify.sh +0 -51
- package/hooks/scripts/stop-reflect.sh +0 -140
- package/hooks/scripts/sync-priority-todo.sh +0 -57
- package/hooks/scripts/sync-todo-docs.sh +0 -46
- package/hooks/scripts/tool-signal.sh +0 -48
- package/scripts/do-tasks-bridge.py +0 -366
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The generated id inventory — text/story-todo.md C1 deliverable, and the
|
|
3
|
+
// source `id:resolve` (C4) projects its metadata card from.
|
|
4
|
+
//
|
|
5
|
+
// Tony asked for seven id families. Seven hand-maintained registries is the
|
|
6
|
+
// measured 1% failure seven times over (text/story-factory-plan.md § 2), so
|
|
7
|
+
// there is ONE inventory with a generated `kind` discriminator. Every field
|
|
8
|
+
// here is READ from a source that already exists; nothing is hand-declared.
|
|
9
|
+
//
|
|
10
|
+
// Five kinds are counted from the repo. Two — `workflow` and `thing` — live in
|
|
11
|
+
// the graph, so they carry `provenance:"graph"`, `count:null` and a stated
|
|
12
|
+
// reason. `count:0` would be a lie: an unrun count is not a zero. The seventh
|
|
13
|
+
// kind is `thing`, the LOCKED dimension-3 word — never `object`
|
|
14
|
+
// (text/story-factory-plan.md § 2, § 0b).
|
|
15
|
+
//
|
|
16
|
+
// Two artifacts, one build, BOTH checked — the rule blocks-manifest.mjs states
|
|
17
|
+
// in its own header: an emitted file that nothing compares is a file that rots.
|
|
18
|
+
// one.ie/web/src/data/id-inventory.json the seven kinds and their entries
|
|
19
|
+
// one.ie/web/src/data/story.json the metadata card of the story family
|
|
20
|
+
//
|
|
21
|
+
// Usage:
|
|
22
|
+
// node .claude/scripts/id-inventory.mjs # write both files
|
|
23
|
+
// node .claude/scripts/id-inventory.mjs --check # diff, no write
|
|
24
|
+
|
|
25
|
+
import { execFileSync } from 'node:child_process'
|
|
26
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
27
|
+
import path from 'node:path'
|
|
28
|
+
import { fileURLToPath } from 'node:url'
|
|
29
|
+
|
|
30
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
31
|
+
const REPO_ROOT = path.resolve(__dirname, '../..')
|
|
32
|
+
const R = (...p) => path.join(REPO_ROOT, ...p)
|
|
33
|
+
|
|
34
|
+
const OUT_INVENTORY = R('one.ie/web/src/data/id-inventory.json')
|
|
35
|
+
const OUT_STORY = R('one.ie/web/src/data/story.json')
|
|
36
|
+
const STORY_MD = R('text/story.md')
|
|
37
|
+
|
|
38
|
+
const CHECK = process.argv.slice(2).includes('--check')
|
|
39
|
+
|
|
40
|
+
// ── the stamp ───────────────────────────────────────────────────────────────
|
|
41
|
+
// git HEAD, never wall-clock: a wall-clock stamp makes every regeneration a
|
|
42
|
+
// diff, so `--check` could never distinguish a stale artifact from a fresh one.
|
|
43
|
+
// It is DELIBERATELY excluded from the --check comparison (`stripStamp` below),
|
|
44
|
+
// the same way blocks-manifest.mjs's diffManifests never compares `measuredAt`.
|
|
45
|
+
// Comparing it would red this check on the very commit that lands the artifact:
|
|
46
|
+
// you generate at sha X, commit (minting sha Y), and X !== Y forever.
|
|
47
|
+
function headSha() {
|
|
48
|
+
try {
|
|
49
|
+
return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: REPO_ROOT, encoding: 'utf8' }).trim()
|
|
50
|
+
} catch {
|
|
51
|
+
return 'unknown'
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── frontmatter, the two fields every source here declares ──────────────────
|
|
56
|
+
// Deliberately not a YAML parser: `name` and `description` are single-line
|
|
57
|
+
// scalars in every SKILL.md and doc in this tree, and a dependency would put a
|
|
58
|
+
// build step in front of a script that must run from a bare checkout.
|
|
59
|
+
function frontmatter(file) {
|
|
60
|
+
const text = readFileSync(file, 'utf8')
|
|
61
|
+
if (!text.startsWith('---')) return {}
|
|
62
|
+
const end = text.indexOf('\n---', 3)
|
|
63
|
+
if (end === -1) return {}
|
|
64
|
+
const out = {}
|
|
65
|
+
for (const line of text.slice(4, end).split('\n')) {
|
|
66
|
+
const m = line.match(/^([a-zA-Z][a-zA-Z0-9_-]*):\s*(.*)$/)
|
|
67
|
+
if (!m) continue
|
|
68
|
+
let v = m[2].trim()
|
|
69
|
+
if (
|
|
70
|
+
(v.startsWith('"') && v.endsWith('"') && v.length > 1) ||
|
|
71
|
+
(v.startsWith("'") && v.endsWith("'") && v.length > 1)
|
|
72
|
+
) {
|
|
73
|
+
v = v.slice(1, -1)
|
|
74
|
+
}
|
|
75
|
+
if (v) out[m[1]] = v
|
|
76
|
+
}
|
|
77
|
+
return out
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ── kind: signal ────────────────────────────────────────────────────────────
|
|
81
|
+
// Counted the way signals-parity.ts counts — `Object.keys(RECEIVERS).length`
|
|
82
|
+
// off the registry itself (packages/sdk/scripts/signals-parity.ts:21-22), not a
|
|
83
|
+
// grep. A grep over receivers.ts counts whatever the current formatting is; the
|
|
84
|
+
// import counts what the system actually ships.
|
|
85
|
+
function signals() {
|
|
86
|
+
const runner = [
|
|
87
|
+
`import { RECEIVERS } from ${JSON.stringify(R('packages/sdk/src/receivers.ts'))}`,
|
|
88
|
+
'const out = Object.entries(RECEIVERS).map(([name, r]) => ({',
|
|
89
|
+
' name, summary: typeof r?.summary === "string" ? r.summary : "",',
|
|
90
|
+
' source: "packages/sdk/src/receivers.ts",',
|
|
91
|
+
'}))',
|
|
92
|
+
'out.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))',
|
|
93
|
+
'console.log(JSON.stringify(out))',
|
|
94
|
+
].join('\n')
|
|
95
|
+
const raw = execFileSync('bun', ['-e', runner], {
|
|
96
|
+
cwd: REPO_ROOT,
|
|
97
|
+
encoding: 'utf8',
|
|
98
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
99
|
+
})
|
|
100
|
+
return JSON.parse(raw.trim())
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ── kind: skill ─────────────────────────────────────────────────────────────
|
|
104
|
+
// `<dir>/<name>/SKILL.md`, ONE level deep, because that is what the running
|
|
105
|
+
// system treats as a skill: one.ie/web/src/lib/skill-catalog.ts:11 globs
|
|
106
|
+
// `../../ai/skills/*/SKILL.md`, skills-publish.sh:72 iterates `*/` and requires
|
|
107
|
+
// `$d/SKILL.md`, and asi-walk.sh:172 names the same shape. A recursive walk
|
|
108
|
+
// finds 325 SKILL.md under one.ie/ai/skills, but the 172 nested ones are not
|
|
109
|
+
// addressable as skill ids anywhere in the system — counting them would publish
|
|
110
|
+
// an id `id:resolve` could never resolve.
|
|
111
|
+
function skills() {
|
|
112
|
+
const out = []
|
|
113
|
+
for (const dir of ['one.ie/ai/skills', '.claude/skills']) {
|
|
114
|
+
const abs = R(dir)
|
|
115
|
+
if (!existsSync(abs)) continue
|
|
116
|
+
for (const name of readdirSync(abs).sort()) {
|
|
117
|
+
const file = path.join(abs, name, 'SKILL.md')
|
|
118
|
+
if (!existsSync(file)) continue
|
|
119
|
+
const fm = frontmatter(file)
|
|
120
|
+
out.push({
|
|
121
|
+
name: fm.name || name,
|
|
122
|
+
summary: fm.description || '',
|
|
123
|
+
source: `${dir}/${name}/SKILL.md`,
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return out
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── kind: component ─────────────────────────────────────────────────────────
|
|
131
|
+
// `jq '.count'`, NOT `jq 'length'` — the manifest is {measuredAt, count, blocks}
|
|
132
|
+
// so `length` is 3 and has been mistaken for the block count before
|
|
133
|
+
// (text/story-todo.md C1). The block vocabulary is itself generated, by
|
|
134
|
+
// .claude/scripts/blocks-manifest.mjs — this reads its artifact, never the
|
|
135
|
+
// Puck registry, so there is one generator per source.
|
|
136
|
+
function components() {
|
|
137
|
+
const file = R('one.ie/web/src/lib/puck/block-manifest.json')
|
|
138
|
+
const manifest = JSON.parse(readFileSync(file, 'utf8'))
|
|
139
|
+
const entries = Object.entries(manifest.blocks ?? {}).map(([name, b]) => ({
|
|
140
|
+
name,
|
|
141
|
+
summary: typeof b?.description === 'string' ? b.description : '',
|
|
142
|
+
source: 'one.ie/web/src/lib/puck/block-manifest.json',
|
|
143
|
+
}))
|
|
144
|
+
entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))
|
|
145
|
+
// The manifest's own declared count is the oracle. If the blocks object and
|
|
146
|
+
// the count disagree, the manifest is stale and this inventory must not
|
|
147
|
+
// launder that into a confident number of its own.
|
|
148
|
+
if (typeof manifest.count === 'number' && manifest.count !== entries.length) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`block-manifest.json is stale: count=${manifest.count} but blocks=${entries.length}. Run: node .claude/scripts/blocks-manifest.mjs`,
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
return entries
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ── kind: doc ───────────────────────────────────────────────────────────────
|
|
157
|
+
// The `docs` CONTENT COLLECTION, not text/. text/ is the plan-and-promise
|
|
158
|
+
// surface (a `doc` id addresses a published page); the collection is what
|
|
159
|
+
// one.ie actually renders (text/story-factory-plan.md § 2).
|
|
160
|
+
function docs() {
|
|
161
|
+
const dir = R('one.ie/web/src/content/docs')
|
|
162
|
+
if (!existsSync(dir)) return []
|
|
163
|
+
const out = []
|
|
164
|
+
for (const name of readdirSync(dir).sort()) {
|
|
165
|
+
if (!/\.mdx?$/.test(name)) continue
|
|
166
|
+
const slug = name.replace(/\.mdx?$/, '')
|
|
167
|
+
const fm = frontmatter(path.join(dir, name))
|
|
168
|
+
out.push({
|
|
169
|
+
name: slug,
|
|
170
|
+
title: fm.title || slug,
|
|
171
|
+
summary: fm.description || '',
|
|
172
|
+
source: `one.ie/web/src/content/docs/${name}`,
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
return out
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ── kind: function ──────────────────────────────────────────────────────────
|
|
179
|
+
// `fn:`, never `fun:` — text/story-factory-plan.md § 0b, the sibling board's
|
|
180
|
+
// ruling, which this file is read through.
|
|
181
|
+
//
|
|
182
|
+
// The name charclass MUST carry `-`: TypeQL fun names are hyphenated
|
|
183
|
+
// (`fun strong-reach(...)`, roles.tql:39) and a `[a-zA-Z0-9_]*` pattern silently
|
|
184
|
+
// undercounts by 44 across schema/ — measured. The `^\s*fun\s+<name>\s*(` anchor
|
|
185
|
+
// also keeps `entity funnel`, `owns funnel-slug` and every `# fun ...` comment
|
|
186
|
+
// out; verified by inspecting every non-matching line containing "fun".
|
|
187
|
+
const FUN_RE = /^[ \t]*fun[ \t]+([a-zA-Z_][a-zA-Z0-9_-]*)[ \t]*\(/
|
|
188
|
+
function functions() {
|
|
189
|
+
const dir = R('schema')
|
|
190
|
+
const out = []
|
|
191
|
+
for (const name of readdirSync(dir).sort()) {
|
|
192
|
+
if (!name.endsWith('.tql')) continue
|
|
193
|
+
const lines = readFileSync(path.join(dir, name), 'utf8').split('\n')
|
|
194
|
+
lines.forEach((line, i) => {
|
|
195
|
+
const m = line.match(FUN_RE)
|
|
196
|
+
if (!m) return
|
|
197
|
+
out.push({
|
|
198
|
+
name: m[1],
|
|
199
|
+
summary: '',
|
|
200
|
+
source: `schema/${name}:${i + 1}`,
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
return out
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ── the inventory ───────────────────────────────────────────────────────────
|
|
208
|
+
// Seven kinds, always seven, in the order text/story-factory-plan.md § 2 lists
|
|
209
|
+
// them. Five carry a number read from the repo; two carry null and say why.
|
|
210
|
+
const GRAPH_REASON_WORKFLOW =
|
|
211
|
+
'the graph, not the repo — workflows are rows the substrate holds, and a committed artifact cannot walk them. Count it through workflow:list, never here.'
|
|
212
|
+
const GRAPH_REASON_THING =
|
|
213
|
+
'dimension 3, the graph — a substrate query. `thing` is the LOCKED word and the catch-all kind; `object` is refused.'
|
|
214
|
+
|
|
215
|
+
function buildInventory() {
|
|
216
|
+
const counted = {
|
|
217
|
+
signal: signals(),
|
|
218
|
+
skill: skills(),
|
|
219
|
+
component: components(),
|
|
220
|
+
doc: docs(),
|
|
221
|
+
function: functions(),
|
|
222
|
+
}
|
|
223
|
+
const sources = {
|
|
224
|
+
signal: 'packages/sdk/src/receivers.ts',
|
|
225
|
+
skill: 'one.ie/ai/skills/*/SKILL.md + .claude/skills/*/SKILL.md',
|
|
226
|
+
component: "one.ie/web/src/lib/puck/block-manifest.json (jq '.count')",
|
|
227
|
+
doc: 'one.ie/web/src/content/docs',
|
|
228
|
+
function: 'schema/*.tql',
|
|
229
|
+
}
|
|
230
|
+
// The address is COMPOSED, never stored. An entry carries `name`; the id is
|
|
231
|
+
// `<id_prefix>:<name>` — and `id_prefix` is published here, bare and without
|
|
232
|
+
// its colon, so the grammar travels with the data instead of being retyped in
|
|
233
|
+
// the resolver.
|
|
234
|
+
//
|
|
235
|
+
// Storing the composed string is what this artifact must NOT do, and the
|
|
236
|
+
// reason is a law, not a lint: this repo's tags are BARE words, and
|
|
237
|
+
// one.ie/web/tests/unit/in/namespace-parity.test.ts greps src/ for any
|
|
238
|
+
// literal starting `(fn|ticket|kind|state|status|topic|lifecycle):`. A stored
|
|
239
|
+
// `fn:<name>` is indistinguishable from a classification tag — to that grep
|
|
240
|
+
// and to a human reading the file — so materialising 213 of them in src/data
|
|
241
|
+
// published 213 tag-shaped strings into a tree whose central law forbids
|
|
242
|
+
// them. Measured: the gate went red on exactly those 213.
|
|
243
|
+
//
|
|
244
|
+
// `signal` has no prefix: a receiver's name IS its address (`factory:size`),
|
|
245
|
+
// which is why null is the honest value rather than an empty string.
|
|
246
|
+
const idPrefix = { signal: null, skill: 'skill', component: 'component', doc: 'doc', function: 'fn' }
|
|
247
|
+
const kinds = [
|
|
248
|
+
...['signal', 'skill', 'component', 'doc', 'function'].map((kind) => ({
|
|
249
|
+
kind,
|
|
250
|
+
provenance: 'generated',
|
|
251
|
+
count: counted[kind].length,
|
|
252
|
+
id_prefix: idPrefix[kind],
|
|
253
|
+
source: sources[kind],
|
|
254
|
+
})),
|
|
255
|
+
{ kind: 'workflow', provenance: 'graph', count: null, id_prefix: 'workflow', source: 'the substrate', reason: GRAPH_REASON_WORKFLOW },
|
|
256
|
+
{ kind: 'thing', provenance: 'graph', count: null, id_prefix: 'thing', source: 'the substrate', reason: GRAPH_REASON_THING },
|
|
257
|
+
]
|
|
258
|
+
return { generated_at: headSha(), kinds, entries: counted }
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ── the story family card (text/story.md's `family` deliverable) ────────────
|
|
262
|
+
// The eleven paths are PARSED OUT of text/story.md's own accept line, never
|
|
263
|
+
// re-typed here. That accept is `ls <paths…> 2>/dev/null | wc -l` compared
|
|
264
|
+
// `-eq 11`; reading the list from the promise is what makes `jq '.files |
|
|
265
|
+
// length'` and that `-eq` unable to disagree. A hand-copied list is exactly the
|
|
266
|
+
// hand-declared metadata this whole rung exists to replace.
|
|
267
|
+
function familyPaths() {
|
|
268
|
+
const md = readFileSync(STORY_MD, 'utf8')
|
|
269
|
+
const line = md.split('\n').find((l) => l.includes('accept:') && /ls\s+text\/story\.md/.test(l))
|
|
270
|
+
if (!line) throw new Error(`could not find the family accept line in ${STORY_MD}`)
|
|
271
|
+
const m = line.match(/ls\s+((?:\S+\s+)*?)2>\/dev\/null/)
|
|
272
|
+
if (!m) throw new Error(`the family accept line is not the expected \`ls … 2>/dev/null\` shape`)
|
|
273
|
+
const paths = m[1].trim().split(/\s+/).filter(Boolean)
|
|
274
|
+
const declared = line.match(/-eq\s+(\d+)/)
|
|
275
|
+
if (declared && Number(declared[1]) !== paths.length) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`text/story.md's family accept disagrees with itself: it lists ${paths.length} paths and asserts -eq ${declared[1]}`,
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
return paths
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// One of the seven kinds for each family file, decided by a RULE, not by an
|
|
284
|
+
// eleven-row hand-written table — the same law the inventory above obeys.
|
|
285
|
+
//
|
|
286
|
+
// Nothing in the family maps to `signal`, and that is the honest answer rather
|
|
287
|
+
// than a gap: the family's signal side is `story:chain` and `id:resolve`, which
|
|
288
|
+
// live in packages/sdk/src/receivers.ts and are addressed as `story:chain`, not
|
|
289
|
+
// as a story-named file. Seven of the eleven land on `thing`, the dimension-3
|
|
290
|
+
// catch-all — a data artifact, a library, a page, a Move object, an agent and a
|
|
291
|
+
// test are all Things, and stretching one of them onto `component` or
|
|
292
|
+
// `function` would put a lie in the card `id:resolve` projects.
|
|
293
|
+
const KIND_RULES = [
|
|
294
|
+
[(p) => path.basename(p) === 'SKILL.md', 'skill', 'a SKILL.md is a skill'],
|
|
295
|
+
[(p) => p.endsWith('.tql') && /\/workflows\//.test(p), 'workflow', 'a .tql under */workflows/ is a workflow'],
|
|
296
|
+
[(p) => p.endsWith('.tql') && p.startsWith('schema/'), 'function', 'a .tql under schema/ declares funs'],
|
|
297
|
+
[(p) => p.endsWith('.tsx') && /\/components\//.test(p), 'component', 'a .tsx under */components/ is a component'],
|
|
298
|
+
[(p) => p.endsWith('.md') && p.startsWith('text/'), 'doc', 'a .md under text/ is a document'],
|
|
299
|
+
]
|
|
300
|
+
function kindOf(p) {
|
|
301
|
+
for (const [test, kind, rule] of KIND_RULES) if (test(p)) return { kind, rule }
|
|
302
|
+
return { kind: 'thing', rule: 'the dimension-3 catch-all — never `object`' }
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// This card is itself one of the eleven, and it is written LAST — so a plain
|
|
306
|
+
// existsSync said `exists:false` for `one.ie/web/src/data/story.json` at the
|
|
307
|
+
// instant that very file was being written, and the next --check went red
|
|
308
|
+
// reporting `committed=thing/false fresh=thing/true` forever (measured). The
|
|
309
|
+
// file's presence is what running this script guarantees, so the self-reference
|
|
310
|
+
// answers true. Every other path is probed on disk.
|
|
311
|
+
const SELF = path.relative(REPO_ROOT, OUT_STORY)
|
|
312
|
+
|
|
313
|
+
function buildStory() {
|
|
314
|
+
const paths = familyPaths()
|
|
315
|
+
const files = paths.map((p) => {
|
|
316
|
+
const { kind, rule } = kindOf(p)
|
|
317
|
+
return { path: p, kind, rule, exists: p === SELF ? true : existsSync(R(p)) }
|
|
318
|
+
})
|
|
319
|
+
return {
|
|
320
|
+
slug: 'story',
|
|
321
|
+
name: 'Story',
|
|
322
|
+
summary:
|
|
323
|
+
'One slug, every technology — the eleven files text/story.md promises, each the best of its own technology. A told story generates this family for the teller\'s own slug.',
|
|
324
|
+
source: 'text/story.md',
|
|
325
|
+
generated_at: headSha(),
|
|
326
|
+
count: files.length,
|
|
327
|
+
present: files.filter((f) => f.exists).length,
|
|
328
|
+
files,
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ── check ───────────────────────────────────────────────────────────────────
|
|
333
|
+
const stripStamp = (o) => {
|
|
334
|
+
const { generated_at: _drop, ...rest } = o
|
|
335
|
+
return rest
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function diffOne(label, outPath, fresh) {
|
|
339
|
+
if (!existsSync(outPath)) {
|
|
340
|
+
return [`- ${outPath} does not exist — run without --check first to seed it.`]
|
|
341
|
+
}
|
|
342
|
+
const committed = JSON.parse(readFileSync(outPath, 'utf8'))
|
|
343
|
+
if (JSON.stringify(stripStamp(committed)) === JSON.stringify(stripStamp(fresh))) return []
|
|
344
|
+
const lines = [`~ ${outPath} out of date — run: node .claude/scripts/id-inventory.mjs`]
|
|
345
|
+
// Say WHICH kind moved. A bare "out of date" makes the reader diff a 300KB
|
|
346
|
+
// file by hand, which is how a generated artifact stops being regenerated.
|
|
347
|
+
if (label === 'inventory') {
|
|
348
|
+
const byKind = (o) => Object.fromEntries((o.kinds ?? []).map((k) => [k.kind, k.count]))
|
|
349
|
+
const a = byKind(committed)
|
|
350
|
+
const b = byKind(fresh)
|
|
351
|
+
for (const kind of new Set([...Object.keys(a), ...Object.keys(b)])) {
|
|
352
|
+
if (a[kind] !== b[kind]) lines.push(` ~ ${kind}: committed=${a[kind]} fresh=${b[kind]}`)
|
|
353
|
+
}
|
|
354
|
+
// A kind whose COUNT is unchanged can still have drifted — a receiver
|
|
355
|
+
// whose summary was reworded moves no number. Report it, or the reader
|
|
356
|
+
// reads "out of date" against seven identical counts and assumes a bug.
|
|
357
|
+
for (const kind of Object.keys(fresh.entries ?? {})) {
|
|
358
|
+
const ca = (committed.entries ?? {})[kind]
|
|
359
|
+
const cb = (fresh.entries ?? {})[kind]
|
|
360
|
+
if (ca && cb && a[kind] === b[kind] && JSON.stringify(ca) !== JSON.stringify(cb)) {
|
|
361
|
+
lines.push(` ~ ${kind}: same count (${b[kind]}), different entries`)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
const byPath = (o) => Object.fromEntries((o.files ?? []).map((f) => [f.path, `${f.kind}/${f.exists}`]))
|
|
366
|
+
const a = byPath(committed)
|
|
367
|
+
const b = byPath(fresh)
|
|
368
|
+
for (const p of new Set([...Object.keys(a), ...Object.keys(b)])) {
|
|
369
|
+
if (a[p] !== b[p]) lines.push(` ~ ${p}: committed=${a[p] ?? '(absent)'} fresh=${b[p] ?? '(absent)'}`)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
// The per-item loops above key on a SUBSET of each file (a kind's count, a
|
|
373
|
+
// file's kind+exists). Anything outside that key — `rule`, `present`, `slug`,
|
|
374
|
+
// `summary`, `source`, `id_prefix` — differs without producing a line, and the
|
|
375
|
+
// reader gets a bare "out of date" header and diffs a 285KB file by hand.
|
|
376
|
+
// Whatever the comparator missed, name the top-level fields that moved.
|
|
377
|
+
if (lines.length === 1) {
|
|
378
|
+
const keys = new Set([...Object.keys(committed), ...Object.keys(fresh)])
|
|
379
|
+
const moved = [...keys]
|
|
380
|
+
.filter((k) => k !== 'generated_at')
|
|
381
|
+
.filter((k) => JSON.stringify(committed[k]) !== JSON.stringify(fresh[k]))
|
|
382
|
+
lines.push(
|
|
383
|
+
moved.length > 0
|
|
384
|
+
? ` ~ fields differ (below the per-item comparator): ${moved.join(', ')}`
|
|
385
|
+
: ` ~ differs only in key ORDER or in generated_at handling — regenerate`,
|
|
386
|
+
)
|
|
387
|
+
}
|
|
388
|
+
return lines
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function main() {
|
|
392
|
+
const inventory = buildInventory()
|
|
393
|
+
const story = buildStory()
|
|
394
|
+
|
|
395
|
+
if (CHECK) {
|
|
396
|
+
const diff = [
|
|
397
|
+
...diffOne('inventory', OUT_INVENTORY, inventory),
|
|
398
|
+
...diffOne('story', OUT_STORY, story),
|
|
399
|
+
]
|
|
400
|
+
if (diff.length > 0) {
|
|
401
|
+
console.error('id-inventory --check: drift detected')
|
|
402
|
+
for (const line of diff) console.error(line)
|
|
403
|
+
process.exit(1)
|
|
404
|
+
}
|
|
405
|
+
const counts = inventory.kinds.map((k) => `${k.kind}=${k.count ?? 'graph'}`).join(' ')
|
|
406
|
+
console.log(`id-inventory --check: ok (no drift) — kinds=${inventory.kinds.length} ${counts} story.files=${story.count} present=${story.present}`)
|
|
407
|
+
process.exit(0)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
mkdirSync(path.dirname(OUT_INVENTORY), { recursive: true })
|
|
411
|
+
writeFileSync(OUT_INVENTORY, JSON.stringify(inventory, null, 2) + '\n')
|
|
412
|
+
writeFileSync(OUT_STORY, JSON.stringify(story, null, 2) + '\n')
|
|
413
|
+
const counts = inventory.kinds.map((k) => `${k.kind}=${k.count ?? 'graph'}`).join(' ')
|
|
414
|
+
console.log(`wrote ${OUT_INVENTORY} — kinds=${inventory.kinds.length} ${counts}`)
|
|
415
|
+
console.log(`wrote ${OUT_STORY} — files=${story.count} present=${story.present}`)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
main()
|