@agent-pattern-labs/leads-rig 0.1.3

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 (87) hide show
  1. package/.claude/agents/general-free.md +39 -0
  2. package/.claude/agents/general-paid.md +20 -0
  3. package/.claude/agents/glm-minimal.md +9 -0
  4. package/.claude/iso-route.resolved.json +21 -0
  5. package/.claude/settings.json +3 -0
  6. package/.codex/config.toml +24 -0
  7. package/.cursor/iso-route.md +17 -0
  8. package/.cursor/mcp.json +19 -0
  9. package/.cursor/rules/agent-general-free.mdc +38 -0
  10. package/.cursor/rules/agent-general-paid.mdc +19 -0
  11. package/.cursor/rules/agent-glm-minimal.mdc +8 -0
  12. package/.cursor/rules/main.mdc +61 -0
  13. package/.mcp.json +19 -0
  14. package/.opencode/agents/general-free.md +48 -0
  15. package/.opencode/agents/general-paid.md +29 -0
  16. package/.opencode/agents/glm-minimal.md +19 -0
  17. package/.opencode/instructions.md +3 -0
  18. package/.opencode/skills/lead-harness.md +56 -0
  19. package/.opencode/skills/public-leads.md +56 -0
  20. package/.pi/prompts/lead-harness.md +54 -0
  21. package/.pi/prompts/public-leads.md +54 -0
  22. package/.pi/skills/general-free/SKILL.md +38 -0
  23. package/.pi/skills/general-paid/SKILL.md +19 -0
  24. package/.pi/skills/glm-minimal/SKILL.md +8 -0
  25. package/AGENTS.md +56 -0
  26. package/CLAUDE.md +56 -0
  27. package/LICENSE +21 -0
  28. package/README.md +61 -0
  29. package/batch/README.md +37 -0
  30. package/batch/batch-prompt.md +19 -0
  31. package/batch/batch-runner.sh +18 -0
  32. package/bin/create-leads-harness.mjs +178 -0
  33. package/bin/lead-harness.mjs +201 -0
  34. package/bin/sync.mjs +150 -0
  35. package/config/profile.example.yml +34 -0
  36. package/docs/ARCHITECTURE.md +85 -0
  37. package/docs/CONSTRUCTION.md +40 -0
  38. package/docs/README.md +5 -0
  39. package/docs/SETUP.md +89 -0
  40. package/examples/README.md +9 -0
  41. package/examples/sample-leads.json +57 -0
  42. package/iso/agents/general-free.md +50 -0
  43. package/iso/agents/general-paid.md +31 -0
  44. package/iso/agents/glm-minimal.md +21 -0
  45. package/iso/commands/lead-harness.md +59 -0
  46. package/iso/commands/public-leads.md +59 -0
  47. package/iso/config.json +11 -0
  48. package/iso/instructions.md +56 -0
  49. package/iso/instructions.opencode.md +3 -0
  50. package/iso/mcp.json +16 -0
  51. package/lib/leadharness-crawler.mjs +911 -0
  52. package/lib/leadharness-ingest.mjs +157 -0
  53. package/lib/leadharness-leads.mjs +574 -0
  54. package/models.yaml +32 -0
  55. package/modes/_shared.md +50 -0
  56. package/modes/batch.md +34 -0
  57. package/modes/crawl.md +25 -0
  58. package/modes/ingest.md +33 -0
  59. package/modes/pipeline.md +27 -0
  60. package/modes/reference-local-helpers.md +13 -0
  61. package/modes/review.md +15 -0
  62. package/modes/setup.md +25 -0
  63. package/opencode.json +43 -0
  64. package/package.json +186 -0
  65. package/scripts/batch-orchestrator.mjs +558 -0
  66. package/scripts/crawl.mjs +129 -0
  67. package/scripts/ingest.mjs +48 -0
  68. package/scripts/manifest.mjs +71 -0
  69. package/scripts/pipeline.mjs +118 -0
  70. package/scripts/validate-leads.mjs +69 -0
  71. package/templates/canon.json +26 -0
  72. package/templates/capabilities.json +23 -0
  73. package/templates/context.json +20 -0
  74. package/templates/contracts.json +72 -0
  75. package/templates/facts.json +13 -0
  76. package/templates/index.json +13 -0
  77. package/templates/lead-schema.json +143 -0
  78. package/templates/lineage.json +10 -0
  79. package/templates/migrations.json +4 -0
  80. package/templates/postflight.json +11 -0
  81. package/templates/preflight.json +19 -0
  82. package/templates/prioritize.json +10 -0
  83. package/templates/redact.json +15 -0
  84. package/templates/score.json +18 -0
  85. package/templates/states.yml +25 -0
  86. package/templates/timeline.json +10 -0
  87. package/verify-pipeline.mjs +123 -0
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from 'child_process';
4
+ import { createRequire } from 'module';
5
+ import { dirname, join, resolve } from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { existsSync } from 'fs';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const PKG_ROOT = resolve(__dirname, '..');
11
+ const PROJECT_DIR = process.env.PUBLIC_LEADS_PROJECT || process.env.LEAD_HARNESS_PROJECT || process.cwd();
12
+ const require = createRequire(import.meta.url);
13
+
14
+ const commands = {
15
+ crawl: 'scripts/crawl.mjs',
16
+ pipeline: 'scripts/pipeline.mjs',
17
+ validate: 'scripts/validate-leads.mjs',
18
+ manifest: 'scripts/manifest.mjs',
19
+ ingest: 'scripts/ingest.mjs',
20
+ batch: 'scripts/batch-orchestrator.mjs',
21
+ verify: 'verify-pipeline.mjs',
22
+ sync: 'bin/sync.mjs',
23
+ };
24
+
25
+ const helperAliases = {
26
+ 'trace:list': ['@razroo/iso-trace', 'list'],
27
+ 'trace:stats': ['@razroo/iso-trace', 'stats'],
28
+ 'trace:show': ['@razroo/iso-trace', 'show'],
29
+ 'guard:audit': ['@razroo/iso-guard', 'audit'],
30
+ 'guard:explain': ['@razroo/iso-guard', 'explain'],
31
+ 'ledger:status': ['@razroo/iso-ledger', 'status'],
32
+ 'ledger:rebuild': ['@razroo/iso-ledger', 'rebuild'],
33
+ 'ledger:verify': ['@razroo/iso-ledger', 'verify'],
34
+ 'capabilities:list': ['@razroo/iso-capabilities', 'list'],
35
+ 'capabilities:explain': ['@razroo/iso-capabilities', 'explain'],
36
+ 'capabilities:check': ['@razroo/iso-capabilities', 'check'],
37
+ 'context:list': ['@razroo/iso-context', 'list'],
38
+ 'context:explain': ['@razroo/iso-context', 'explain'],
39
+ 'context:plan': ['@razroo/iso-context', 'plan'],
40
+ 'context:check': ['@razroo/iso-context', 'check'],
41
+ 'context:render': ['@razroo/iso-context', 'render'],
42
+ 'cache:key': ['@razroo/iso-cache', 'key'],
43
+ 'cache:has': ['@razroo/iso-cache', 'has'],
44
+ 'cache:get': ['@razroo/iso-cache', 'get'],
45
+ 'cache:put': ['@razroo/iso-cache', 'put'],
46
+ 'cache:status': ['@razroo/iso-cache', 'status'],
47
+ 'cache:list': ['@razroo/iso-cache', 'list'],
48
+ 'cache:verify': ['@razroo/iso-cache', 'verify'],
49
+ 'cache:prune': ['@razroo/iso-cache', 'prune'],
50
+ 'index:build': ['@razroo/iso-index', 'build'],
51
+ 'index:status': ['@razroo/iso-index', 'status'],
52
+ 'index:query': ['@razroo/iso-index', 'query'],
53
+ 'index:has': ['@razroo/iso-index', 'has'],
54
+ 'index:verify': ['@razroo/iso-index', 'verify'],
55
+ 'facts:build': ['@razroo/iso-facts', 'build'],
56
+ 'facts:status': ['@razroo/iso-facts', 'status'],
57
+ 'facts:query': ['@razroo/iso-facts', 'query'],
58
+ 'facts:has': ['@razroo/iso-facts', 'has'],
59
+ 'facts:verify': ['@razroo/iso-facts', 'verify'],
60
+ 'score:compute': ['@razroo/iso-score', 'compute'],
61
+ 'score:verify': ['@razroo/iso-score', 'verify'],
62
+ 'score:check': ['@razroo/iso-score', 'check'],
63
+ 'score:gate': ['@razroo/iso-score', 'gate'],
64
+ 'score:compare': ['@razroo/iso-score', 'compare'],
65
+ 'score:explain': ['@razroo/iso-score', 'explain'],
66
+ 'canon:normalize': ['@razroo/iso-canon', 'normalize'],
67
+ 'canon:key': ['@razroo/iso-canon', 'key'],
68
+ 'canon:compare': ['@razroo/iso-canon', 'compare'],
69
+ 'canon:explain': ['@razroo/iso-canon', 'explain'],
70
+ 'preflight:plan': ['@razroo/iso-preflight', 'plan'],
71
+ 'preflight:check': ['@razroo/iso-preflight', 'check'],
72
+ 'preflight:explain': ['@razroo/iso-preflight', 'explain'],
73
+ 'postflight:status': ['@razroo/iso-postflight', 'status'],
74
+ 'postflight:check': ['@razroo/iso-postflight', 'check'],
75
+ 'postflight:explain': ['@razroo/iso-postflight', 'explain'],
76
+ 'timeline:status': ['@razroo/iso-timeline', 'status'],
77
+ 'timeline:build': ['@razroo/iso-timeline', 'build'],
78
+ 'timeline:plan': ['@razroo/iso-timeline', 'plan'],
79
+ 'timeline:due': ['@razroo/iso-timeline', 'due'],
80
+ 'timeline:check': ['@razroo/iso-timeline', 'check'],
81
+ 'timeline:verify': ['@razroo/iso-timeline', 'verify'],
82
+ 'prioritize:status': ['@razroo/iso-prioritize', 'status'],
83
+ 'prioritize:items': ['@razroo/iso-prioritize', 'items'],
84
+ 'prioritize:build': ['@razroo/iso-prioritize', 'build'],
85
+ 'prioritize:rank': ['@razroo/iso-prioritize', 'rank'],
86
+ 'prioritize:select': ['@razroo/iso-prioritize', 'select'],
87
+ 'prioritize:check': ['@razroo/iso-prioritize', 'check'],
88
+ 'prioritize:verify': ['@razroo/iso-prioritize', 'verify'],
89
+ 'lineage:status': ['@razroo/iso-lineage', 'status'],
90
+ 'lineage:record': ['@razroo/iso-lineage', 'record'],
91
+ 'lineage:check': ['@razroo/iso-lineage', 'check'],
92
+ 'lineage:stale': ['@razroo/iso-lineage', 'stale'],
93
+ 'lineage:verify': ['@razroo/iso-lineage', 'verify'],
94
+ 'redact:scan': ['@razroo/iso-redact', 'scan'],
95
+ 'redact:verify': ['@razroo/iso-redact', 'verify'],
96
+ 'redact:apply': ['@razroo/iso-redact', 'apply'],
97
+ 'migrate:plan': ['@razroo/iso-migrate', 'plan'],
98
+ 'migrate:apply': ['@razroo/iso-migrate', 'apply'],
99
+ 'migrate:check': ['@razroo/iso-migrate', 'check'],
100
+ };
101
+
102
+ const [, , cmd, ...rest] = process.argv;
103
+
104
+ if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') {
105
+ printHelp();
106
+ process.exit(cmd ? 0 : 1);
107
+ }
108
+
109
+ if (commands[cmd]) {
110
+ runScript(commands[cmd], rest);
111
+ }
112
+
113
+ if (helperAliases[cmd]) {
114
+ const [pkgName, subcommand] = helperAliases[cmd];
115
+ runIsoCli(pkgName, [subcommand, ...defaultIsoArgs(pkgName, subcommand, rest), ...rest]);
116
+ }
117
+
118
+ console.error(`Unknown command: ${cmd}\n`);
119
+ printHelp();
120
+ process.exit(1);
121
+
122
+ function runScript(relScript, args) {
123
+ const scriptPath = join(PKG_ROOT, relScript);
124
+ if (!existsSync(scriptPath)) {
125
+ console.error(`public-leads: script not found: ${relScript}`);
126
+ process.exit(1);
127
+ }
128
+
129
+ const result = spawnSync(process.execPath, [scriptPath, ...args], {
130
+ stdio: 'inherit',
131
+ cwd: PROJECT_DIR,
132
+ env: {
133
+ ...process.env,
134
+ PUBLIC_LEADS_PROJECT: PROJECT_DIR,
135
+ PUBLIC_LEADS_ROOT: PKG_ROOT,
136
+ LEAD_HARNESS_PROJECT: PROJECT_DIR,
137
+ LEAD_HARNESS_ROOT: PKG_ROOT,
138
+ },
139
+ });
140
+ process.exit(result.status ?? 1);
141
+ }
142
+
143
+ function runIsoCli(pkgName, args) {
144
+ const pkgJsonPath = require.resolve(`${pkgName}/package.json`);
145
+ const pkg = JSON.parse(require('fs').readFileSync(pkgJsonPath, 'utf8'));
146
+ const binRel = typeof pkg.bin === 'string'
147
+ ? pkg.bin
148
+ : Object.values(pkg.bin || {})[0];
149
+ if (!binRel) {
150
+ console.error(`public-leads: ${pkgName} does not expose a CLI bin`);
151
+ process.exit(1);
152
+ }
153
+ const cliPath = join(dirname(pkgJsonPath), binRel);
154
+ const result = spawnSync(process.execPath, [cliPath, ...args], {
155
+ stdio: 'inherit',
156
+ cwd: PROJECT_DIR,
157
+ env: process.env,
158
+ });
159
+ process.exit(result.status ?? 1);
160
+ }
161
+
162
+ function defaultIsoArgs(pkgName, subcommand, args) {
163
+ const defaults = [];
164
+ if (pkgName === '@razroo/iso-trace') {
165
+ if (!hasFlag(args, '--cwd')) defaults.push('--cwd', PROJECT_DIR);
166
+ if ((subcommand === 'list' || subcommand === 'stats') && !hasFlag(args, '--since')) {
167
+ defaults.push('--since', '7d');
168
+ }
169
+ }
170
+ return defaults;
171
+ }
172
+
173
+ function hasFlag(args, flag) {
174
+ return args.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
175
+ }
176
+
177
+ function printHelp() {
178
+ console.log(`public-leads — CLI for the @agent-pattern-labs/leads-rig package
179
+
180
+ Usage:
181
+ public-leads <command> [args...]
182
+
183
+ Core commands:
184
+ crawl Crawl public company pages and write a lead artifact
185
+ pipeline Crawl, validate, manifest, and optionally ingest leads
186
+ validate Validate lead artifacts against the local contract
187
+ manifest Build/update data/lead-manifest.json from lead artifacts
188
+ ingest Submit a validated payload to the configured ingest API
189
+ verify Run the full harness verification gate
190
+ sync Re-run consumer-project symlink sync
191
+
192
+ Helper command families:
193
+ trace:*, guard:*, ledger:*, capabilities:*, context:*, cache:*, index:*,
194
+ facts:*, score:*, canon:*, preflight:*, postflight:*, timeline:*,
195
+ prioritize:*, lineage:*,
196
+ redact:*, migrate:*
197
+
198
+ Legacy alias:
199
+ lead-harness <command> ...
200
+ `);
201
+ }
package/bin/sync.mjs ADDED
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ existsSync,
5
+ lstatSync,
6
+ mkdirSync,
7
+ readFileSync,
8
+ readlinkSync,
9
+ symlinkSync,
10
+ writeFileSync,
11
+ } from 'fs';
12
+ import { dirname, join, resolve, relative } from 'path';
13
+ import { fileURLToPath } from 'url';
14
+
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+ const PKG_ROOT = resolve(__dirname, '..');
17
+ const PROJECT_DIR = process.env.INIT_CWD || process.env.PUBLIC_LEADS_PROJECT || process.env.LEAD_HARNESS_PROJECT || process.cwd();
18
+
19
+ const pkgJsonPath = join(PROJECT_DIR, 'package.json');
20
+ if (existsSync(pkgJsonPath)) {
21
+ try {
22
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
23
+ if (new Set(['@agent-pattern-labs/leads-rig', '@agent-pattern-labs/public-leads-harness', 'public-leads-harness', 'leads-agentic-harness']).has(pkg.name) && PROJECT_DIR === PKG_ROOT) {
24
+ console.log('public-leads sync: skipping (running inside harness repo).');
25
+ process.exit(0);
26
+ }
27
+ } catch {
28
+ // Ignore malformed consumer package files; symlink sync can still run.
29
+ }
30
+ }
31
+
32
+ if (PROJECT_DIR === PKG_ROOT) {
33
+ console.log('public-leads sync: skipping (PROJECT_DIR == PKG_ROOT).');
34
+ process.exit(0);
35
+ }
36
+
37
+ const links = [
38
+ { src: '.cursor/mcp.json', dst: '.cursor/mcp.json' },
39
+ { src: '.cursor/rules/main.mdc', dst: '.cursor/rules/main.mdc' },
40
+ { src: '.cursor/rules/agent-general-free.mdc', dst: '.cursor/rules/agent-general-free.mdc' },
41
+ { src: '.cursor/rules/agent-general-paid.mdc', dst: '.cursor/rules/agent-general-paid.mdc' },
42
+ { src: '.cursor/rules/agent-glm-minimal.mdc', dst: '.cursor/rules/agent-glm-minimal.mdc' },
43
+ { src: '.cursor/iso-route.md', dst: '.cursor/iso-route.md' },
44
+ { src: '.mcp.json', dst: '.mcp.json' },
45
+ { src: '.claude/agents', dst: '.claude/agents' },
46
+ { src: '.claude/settings.json', dst: '.claude/settings.json' },
47
+ { src: '.claude/iso-route.resolved.json', dst: '.claude/iso-route.resolved.json' },
48
+ { src: '.codex/config.toml', dst: '.codex/config.toml' },
49
+ { src: '.opencode/instructions.md', dst: '.opencode/instructions.md' },
50
+ { src: '.opencode/skills/public-leads.md', dst: '.opencode/skills/public-leads.md' },
51
+ { src: '.opencode/skills/lead-harness.md', dst: '.opencode/skills/lead-harness.md' },
52
+ { src: '.opencode/agents', dst: '.opencode/agents' },
53
+ { src: '.pi/skills', dst: '.pi/skills' },
54
+ { src: '.pi/prompts', dst: '.pi/prompts' },
55
+ { src: 'models.yaml', dst: 'models.yaml' },
56
+ { src: 'modes', dst: 'modes' },
57
+ { src: 'templates', dst: 'templates' },
58
+ { src: 'batch/batch-prompt.md', dst: 'batch/batch-prompt.md' },
59
+ { src: 'batch/batch-runner.sh', dst: 'batch/batch-runner.sh' },
60
+ { src: 'batch/README.md', dst: 'batch/README.md' },
61
+ { src: 'AGENTS.md', dst: 'AGENTS.harness.md' },
62
+ { src: 'CLAUDE.md', dst: 'CLAUDE.harness.md' },
63
+ ];
64
+
65
+ let created = 0;
66
+ let skipped = 0;
67
+ let warned = 0;
68
+
69
+ for (const { src, dst } of links) {
70
+ const absSrc = join(PKG_ROOT, src);
71
+ const absDst = join(PROJECT_DIR, dst);
72
+
73
+ if (!existsSync(absSrc)) {
74
+ console.warn(` skip: ${src} not found in harness`);
75
+ skipped++;
76
+ continue;
77
+ }
78
+
79
+ const parent = dirname(absDst);
80
+ if (!existsSync(parent)) mkdirSync(parent, { recursive: true });
81
+
82
+ let stat = null;
83
+ try {
84
+ stat = lstatSync(absDst);
85
+ } catch {
86
+ // absent is expected
87
+ }
88
+
89
+ if (stat) {
90
+ if (stat.isSymbolicLink()) {
91
+ const current = readlinkSync(absDst);
92
+ const expected = relative(dirname(absDst), absSrc);
93
+ if (current === expected || resolve(dirname(absDst), current) === absSrc) {
94
+ skipped++;
95
+ continue;
96
+ }
97
+ console.warn(` warn: ${dst} points elsewhere (${current}) -- leaving alone`);
98
+ warned++;
99
+ continue;
100
+ }
101
+ console.warn(` warn: ${dst} already exists as a real file/dir -- leaving alone`);
102
+ warned++;
103
+ continue;
104
+ }
105
+
106
+ const relSrc = relative(dirname(absDst), absSrc);
107
+ const type = lstatSync(absSrc).isDirectory() ? 'dir' : 'file';
108
+ try {
109
+ symlinkSync(relSrc, absDst, type);
110
+ console.log(` linked: ${dst} -> ${relSrc}`);
111
+ created++;
112
+ } catch (error) {
113
+ console.error(` error: failed to symlink ${dst}: ${error.message}`);
114
+ warned++;
115
+ }
116
+ }
117
+
118
+ try {
119
+ if (ensureOpencodeInstructionRef()) created++;
120
+ } catch (error) {
121
+ console.warn(` warn: failed to patch opencode.json instructions: ${error.message}`);
122
+ warned++;
123
+ }
124
+
125
+ console.log(`\npublic-leads sync: ${created} created, ${skipped} skipped, ${warned} warnings (project: ${PROJECT_DIR})`);
126
+
127
+ function ensureOpencodeInstructionRef() {
128
+ const configPath = join(PROJECT_DIR, 'opencode.json');
129
+ if (!existsSync(configPath)) return false;
130
+
131
+ const raw = readFileSync(configPath, 'utf8');
132
+ const config = JSON.parse(raw);
133
+ const current = Array.isArray(config.instructions)
134
+ ? config.instructions.slice()
135
+ : config.instructions
136
+ ? [config.instructions]
137
+ : [];
138
+
139
+ const required = '.opencode/instructions.md';
140
+ if (current.includes(required)) return false;
141
+
142
+ const next = current.slice();
143
+ const anchor = next.indexOf('AGENTS.harness.md');
144
+ if (anchor === -1) next.unshift(required);
145
+ else next.splice(anchor + 1, 0, required);
146
+ config.instructions = [...new Set(next)];
147
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
148
+ console.log(` updated: opencode.json instructions += ${required}`);
149
+ return true;
150
+ }
@@ -0,0 +1,34 @@
1
+ # Lead ingest settings for the reference client.
2
+ # Prefer environment variables for secrets in shared terminals.
3
+
4
+ api:
5
+ # Cold Agent Leads local API default. Set target_project to a local
6
+ # cold-agent-leads checkout to read ADMIN_API_TOKEN/ADMIN_EMAILS from .env.
7
+ base_url: "http://localhost:8080"
8
+ ingest_path: "/api/lead-ingests"
9
+ target_project: ""
10
+ operator_email: "admin@example.com"
11
+ operator_email_header: "X-Admin-Email"
12
+ auth_header: "Authorization"
13
+ auth_scheme: "Bearer"
14
+ auth_token_env: "ADMIN_API_TOKEN"
15
+
16
+ crawl:
17
+ max_pages: 10
18
+ max_domains_per_batch: 50
19
+ concurrency: 2
20
+ user_agent: "PublicLeadsBot/0.1 (+https://example.com/public-leads)"
21
+
22
+ lead_policy:
23
+ min_confidence: 30
24
+ allowed_email_types:
25
+ - person
26
+ - role
27
+ - contact_path
28
+ required_fields:
29
+ - domain
30
+ - sourceUrl
31
+ - evidence
32
+ - emailType
33
+ - verificationStatus
34
+ - confidence
@@ -0,0 +1,85 @@
1
+ # Architecture
2
+
3
+ `@agent-pattern-labs/leads-rig` follows a package-oriented harness pattern.
4
+
5
+ ## Package Architecture
6
+
7
+ The harness repo is the npm package. Consumer projects install it, keep private data locally, and receive shared harness files through symlinks created by `bin/sync.mjs`.
8
+
9
+ ```text
10
+ consumer-project/
11
+ ├── package.json # depends on @agent-pattern-labs/leads-rig
12
+ ├── opencode.json # thin local config
13
+ ├── config/profile.yml # local API settings and secret env names
14
+ ├── data/domains.tsv # local target domains
15
+ ├── data/lead-manifest.json # generated, local
16
+ ├── batch/batch-input.tsv # local batch queue
17
+ ├── batch/batch-state.tsv # generated state
18
+ ├── batch/lead-results-*.json # generated lead artifacts
19
+ ├── AGENTS.md # local overrides
20
+ ├── AGENTS.harness.md # symlink to package AGENTS.md
21
+ ├── CLAUDE.harness.md # symlink to package CLAUDE.md
22
+ ├── modes/ # symlink to package modes
23
+ ├── templates/ # symlink to package templates
24
+ └── node_modules/@agent-pattern-labs/leads-rig/
25
+ ```
26
+
27
+ ## Source Of Truth
28
+
29
+ `iso/` is the source for multi-harness agent configuration. `npm run build:config` runs:
30
+
31
+ ```bash
32
+ iso-route build models.yaml --out .
33
+ iso-harness build
34
+ ```
35
+
36
+ This generates:
37
+
38
+ - `AGENTS.md` / `CLAUDE.md`
39
+ - `.opencode/agents/*`
40
+ - `.opencode/skills/public-leads.md`
41
+ - `.codex/config.toml`
42
+ - `.cursor/rules/*`
43
+ - `.pi/skills/*`
44
+ - `.pi/prompts/*`
45
+ - `.mcp.json`
46
+
47
+ Generated harness config is tracked for Git/source installs and refreshed at package time through `prepack`.
48
+
49
+ ## Runtime Flow
50
+
51
+ ```text
52
+ domain input
53
+ -> mode routing (/public-leads pipeline|crawl|batch)
54
+ -> deterministic bounded public crawler or bounded crawl workers
55
+ -> lead JSON artifacts
56
+ -> public-leads validate
57
+ -> public-leads manifest
58
+ -> public-leads ingest
59
+ -> configured lead store or review API
60
+ ```
61
+
62
+ ## Deterministic Helpers
63
+
64
+ | Helper | Purpose |
65
+ |---|---|
66
+ | `scripts/crawl.mjs` | Crawls bounded public company pages and writes lead artifacts |
67
+ | `scripts/pipeline.mjs` | Runs crawl, validation, manifest update, and optional ingest |
68
+ | `scripts/validate-leads.mjs` | Normalizes and validates JSON/JSONL lead artifacts |
69
+ | `scripts/manifest.mjs` | Records validated batches in `data/lead-manifest.json` |
70
+ | `scripts/ingest.mjs` | Posts to the configured ingest endpoint |
71
+ | `scripts/batch-orchestrator.mjs` | Durable batch runner using `@razroo/iso-orchestrator` |
72
+ | `verify-pipeline.mjs` | Repo/consumer health gate |
73
+
74
+ The package also carries the `@razroo/iso-*` helper ecosystem for trace, guard, ledger, cache, canon, context, preflight, postflight, prioritize, lineage, redact, migrate, score, and timeline commands.
75
+
76
+ ## Data Contract
77
+
78
+ `templates/lead-schema.json` defines the portable lead artifact and ingest payload:
79
+
80
+ - lead records
81
+ - crawl results
82
+ - page visits
83
+ - ingest requests
84
+
85
+ The validator intentionally accepts the same defaults the local runtime normalizes, but it fails missing source evidence, invalid URL fields, invalid `emailType`, invalid `verificationStatus`, invalid confidence, and person/role leads without email.
@@ -0,0 +1,40 @@
1
+ # Construction
2
+
3
+ `@agent-pattern-labs/leads-rig` follows the reusable agentic harness pattern used by Agent Pattern Labs: a published harness package owns shared behavior, while consumer projects own private inputs, generated artifacts, and local overrides.
4
+
5
+ ## Construction Map
6
+
7
+ | Pattern Area | This Harness |
8
+ |---|---|
9
+ | Package entry points | `bin/lead-harness.mjs`, `bin/create-leads-harness.mjs`, `bin/sync.mjs` |
10
+ | Source of truth | `iso/` instructions, subagents, commands, MCP declarations, and model routing |
11
+ | Runtime surfaces | `AGENTS.md`, `CLAUDE.md`, `.codex/`, `.claude/`, `.cursor/`, `.opencode/`, `.pi/`, `.mcp.json` |
12
+ | Workflow modes | `modes/_shared.md` plus `modes/{setup,crawl,pipeline,batch,ingest,review}.md` |
13
+ | Executable policy | `templates/lead-schema.json`, `templates/states.yml`, and `templates/*.json` helper policy files |
14
+ | Deterministic helpers | `scripts/crawl.mjs`, `scripts/pipeline.mjs`, `scripts/validate-leads.mjs`, `scripts/manifest.mjs`, `scripts/ingest.mjs`, `scripts/batch-orchestrator.mjs` |
15
+ | Consumer scaffold | `create-public-leads-harness <dir>` writes local config, input, output, and ignore files |
16
+ | Consumer sync | `public-leads sync` symlinks shared harness files from the installed package |
17
+ | Batch orchestration | `batch/batch-runner.sh` delegates to `@razroo/iso-orchestrator` with bounded parallelism |
18
+ | Verification gates | `npm run verify`, `npm run smoke:iso`, sample validate/manifest/ingest, `npm pack` |
19
+
20
+ ## Maintainer Rules
21
+
22
+ - Treat `iso/` as the source for shared agent instructions and generated runtime config.
23
+ - Keep private data in consumer projects under `config/`, `data/`, `batch/`, `reports/`, and `output/`.
24
+ - Add domain behavior through `modes/`, not by bloating always-loaded instructions.
25
+ - Prefer deterministic helpers in `scripts/` or `lib/` over prose-only workflow rules.
26
+ - Keep subagent roles narrow: procedural crawl work, quality reasoning, and small JSON transforms stay separate.
27
+ - Regenerate runtime config with `npm run build:config` after changing `iso/` or `models.yaml`.
28
+ - Run `npm run verify`, `npm run smoke:iso`, and package/install smoke tests before release.
29
+
30
+ ## Release Checklist
31
+
32
+ 1. Verify `config/profile.yml`, lead artifacts, reports, logs, and output files are gitignored.
33
+ 2. Run `npm run build:config` and inspect generated config diffs.
34
+ 3. Run `npm run verify` and `npm run smoke:iso`.
35
+ 4. Run `npx public-leads validate --input examples/sample-leads.json`.
36
+ 5. Run dry-run manifest and ingest checks against the sample artifact.
37
+ 6. Run `npm audit`.
38
+ 7. Run `npm pack` and confirm generated runtime surfaces are included.
39
+ 8. Scaffold a clean consumer project and test install-time sync from the packed package or source tree.
40
+ 9. Publish through GitHub Actions with `gh workflow run publish.yml --ref main`, or create a published release with `gh release create v0.1.3 --title v0.1.3 --generate-notes`.
package/docs/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Documentation
2
+
3
+ - [Architecture](ARCHITECTURE.md)
4
+ - [Construction](CONSTRUCTION.md)
5
+ - [Setup](SETUP.md)
package/docs/SETUP.md ADDED
@@ -0,0 +1,89 @@
1
+ # Setup
2
+
3
+ ## Harness Development
4
+
5
+ ```bash
6
+ npm install
7
+ npm run verify
8
+ npm run smoke:iso
9
+ ```
10
+
11
+ Build generated harness config locally:
12
+
13
+ ```bash
14
+ npm run build:config
15
+ ```
16
+
17
+ The generated config files are ignored in git. They are created for local smoke testing and during package packing.
18
+
19
+ ## Consumer Project
20
+
21
+ ```bash
22
+ npx -p @agent-pattern-labs/leads-rig create-public-leads-harness my-lead-project
23
+ cd my-lead-project
24
+ npm install
25
+ ```
26
+
27
+ Then edit:
28
+
29
+ - `config/profile.yml`
30
+ - `data/domains.tsv`
31
+
32
+ Use environment variables for secrets:
33
+
34
+ ```bash
35
+ export ADMIN_API_TOKEN=...
36
+ ```
37
+
38
+ ## Validate A Payload
39
+
40
+ ```bash
41
+ npx public-leads validate --input examples/sample-leads.json
42
+ ```
43
+
44
+ ## Crawl Domains
45
+
46
+ ```bash
47
+ npx public-leads crawl --input data/domains.tsv --out data/lead-results.json
48
+ npx public-leads pipeline --input data/domains.tsv --out data/lead-results.json
49
+ ```
50
+
51
+ ## Dry-Run Ingest
52
+
53
+ ```bash
54
+ npx public-leads ingest --input examples/sample-leads.json --dry-run --out output/sample-ingest.json
55
+ ```
56
+
57
+ ## Live Ingest
58
+
59
+ ```bash
60
+ npx public-leads pipeline --input data/domains.tsv --ingest --target-project /path/to/cold-agent-leads
61
+ npx public-leads ingest --input data/lead-results.json --target-project /path/to/cold-agent-leads
62
+ ```
63
+
64
+ Required request settings come from flags, environment, or `config/profile.yml`:
65
+
66
+ - `api.base_url`
67
+ - `api.ingest_path`
68
+ - `api.target_project`
69
+ - `api.operator_email`
70
+ - `api.operator_email_header`
71
+ - `api.auth_header`
72
+ - `api.auth_scheme`
73
+ - `api.auth_token_env`
74
+
75
+ ## Batch
76
+
77
+ Create `batch/batch-input.tsv`:
78
+
79
+ ```text
80
+ id domain company notes
81
+ 1 example.com Example Seed target
82
+ ```
83
+
84
+ Run:
85
+
86
+ ```bash
87
+ batch/batch-runner.sh --dry-run
88
+ batch/batch-runner.sh --parallel 2
89
+ ```
@@ -0,0 +1,9 @@
1
+ # Examples
2
+
3
+ `sample-leads.json` is a fictional artifact that matches the local lead-ingest
4
+ payload shape. Use it for local validation smoke tests:
5
+
6
+ ```bash
7
+ npx public-leads validate --input examples/sample-leads.json
8
+ npx public-leads ingest --input examples/sample-leads.json --dry-run --out output/sample-ingest.json
9
+ ```
@@ -0,0 +1,57 @@
1
+ {
2
+ "jobId": "sample-agentic-harness-job",
3
+ "domains": ["example.com"],
4
+ "leads": [
5
+ {
6
+ "company": "Example",
7
+ "domain": "example.com",
8
+ "websiteUrl": "https://example.com/",
9
+ "contactName": "Jane Founder",
10
+ "title": "Founder",
11
+ "email": "jane@example.com",
12
+ "emailType": "person",
13
+ "sourceUrl": "https://example.com/about",
14
+ "sourceLabel": "About",
15
+ "evidence": "Jane Founder is listed on the public About page with jane@example.com.",
16
+ "extractionMethod": "agentic_harness_public_page",
17
+ "verificationStatus": "unverified",
18
+ "confidence": 82,
19
+ "warnings": [],
20
+ "foundAt": "2026-05-15T00:00:00.000Z"
21
+ },
22
+ {
23
+ "company": "Example",
24
+ "domain": "example.com",
25
+ "websiteUrl": "https://example.com/",
26
+ "title": "Contact form",
27
+ "email": "",
28
+ "emailType": "contact_path",
29
+ "sourceUrl": "https://example.com/contact",
30
+ "sourceLabel": "Contact",
31
+ "evidence": "The public contact page contains a general inquiry form.",
32
+ "extractionMethod": "agentic_harness_contact_form",
33
+ "verificationStatus": "not_applicable",
34
+ "confidence": 35,
35
+ "warnings": [],
36
+ "foundAt": "2026-05-15T00:00:00.000Z"
37
+ }
38
+ ],
39
+ "results": [
40
+ {
41
+ "domain": "example.com",
42
+ "websiteUrl": "https://example.com/",
43
+ "leads": [],
44
+ "pages": [
45
+ {
46
+ "url": "https://example.com/about",
47
+ "title": "About Example",
48
+ "statusCode": 200,
49
+ "emailsFound": 1
50
+ }
51
+ ],
52
+ "warnings": [],
53
+ "completedAt": "2026-05-15T00:00:00.000Z"
54
+ }
55
+ ],
56
+ "errors": []
57
+ }