@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.
- package/.claude/agents/general-free.md +39 -0
- package/.claude/agents/general-paid.md +20 -0
- package/.claude/agents/glm-minimal.md +9 -0
- package/.claude/iso-route.resolved.json +21 -0
- package/.claude/settings.json +3 -0
- package/.codex/config.toml +24 -0
- package/.cursor/iso-route.md +17 -0
- package/.cursor/mcp.json +19 -0
- package/.cursor/rules/agent-general-free.mdc +38 -0
- package/.cursor/rules/agent-general-paid.mdc +19 -0
- package/.cursor/rules/agent-glm-minimal.mdc +8 -0
- package/.cursor/rules/main.mdc +61 -0
- package/.mcp.json +19 -0
- package/.opencode/agents/general-free.md +48 -0
- package/.opencode/agents/general-paid.md +29 -0
- package/.opencode/agents/glm-minimal.md +19 -0
- package/.opencode/instructions.md +3 -0
- package/.opencode/skills/lead-harness.md +56 -0
- package/.opencode/skills/public-leads.md +56 -0
- package/.pi/prompts/lead-harness.md +54 -0
- package/.pi/prompts/public-leads.md +54 -0
- package/.pi/skills/general-free/SKILL.md +38 -0
- package/.pi/skills/general-paid/SKILL.md +19 -0
- package/.pi/skills/glm-minimal/SKILL.md +8 -0
- package/AGENTS.md +56 -0
- package/CLAUDE.md +56 -0
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/batch/README.md +37 -0
- package/batch/batch-prompt.md +19 -0
- package/batch/batch-runner.sh +18 -0
- package/bin/create-leads-harness.mjs +178 -0
- package/bin/lead-harness.mjs +201 -0
- package/bin/sync.mjs +150 -0
- package/config/profile.example.yml +34 -0
- package/docs/ARCHITECTURE.md +85 -0
- package/docs/CONSTRUCTION.md +40 -0
- package/docs/README.md +5 -0
- package/docs/SETUP.md +89 -0
- package/examples/README.md +9 -0
- package/examples/sample-leads.json +57 -0
- package/iso/agents/general-free.md +50 -0
- package/iso/agents/general-paid.md +31 -0
- package/iso/agents/glm-minimal.md +21 -0
- package/iso/commands/lead-harness.md +59 -0
- package/iso/commands/public-leads.md +59 -0
- package/iso/config.json +11 -0
- package/iso/instructions.md +56 -0
- package/iso/instructions.opencode.md +3 -0
- package/iso/mcp.json +16 -0
- package/lib/leadharness-crawler.mjs +911 -0
- package/lib/leadharness-ingest.mjs +157 -0
- package/lib/leadharness-leads.mjs +574 -0
- package/models.yaml +32 -0
- package/modes/_shared.md +50 -0
- package/modes/batch.md +34 -0
- package/modes/crawl.md +25 -0
- package/modes/ingest.md +33 -0
- package/modes/pipeline.md +27 -0
- package/modes/reference-local-helpers.md +13 -0
- package/modes/review.md +15 -0
- package/modes/setup.md +25 -0
- package/opencode.json +43 -0
- package/package.json +186 -0
- package/scripts/batch-orchestrator.mjs +558 -0
- package/scripts/crawl.mjs +129 -0
- package/scripts/ingest.mjs +48 -0
- package/scripts/manifest.mjs +71 -0
- package/scripts/pipeline.mjs +118 -0
- package/scripts/validate-leads.mjs +69 -0
- package/templates/canon.json +26 -0
- package/templates/capabilities.json +23 -0
- package/templates/context.json +20 -0
- package/templates/contracts.json +72 -0
- package/templates/facts.json +13 -0
- package/templates/index.json +13 -0
- package/templates/lead-schema.json +143 -0
- package/templates/lineage.json +10 -0
- package/templates/migrations.json +4 -0
- package/templates/postflight.json +11 -0
- package/templates/preflight.json +19 -0
- package/templates/prioritize.json +10 -0
- package/templates/redact.json +15 -0
- package/templates/score.json +18 -0
- package/templates/states.yml +25 -0
- package/templates/timeline.json +10 -0
- 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
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
|
+
}
|