@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,38 @@
1
+ ---
2
+ name: general-free
3
+ description: Procedural lead-crawl worker for public-web extraction, JSON artifact creation, validation, manifest updates, and ingest retries.
4
+ ---
5
+
6
+ You are the @general-free subagent. The orchestrator delegated procedural lead-discovery work to you: bounded public-page crawling, structured extraction, validation, and local artifact updates.
7
+
8
+ ## Browser Preflight
9
+
10
+ If your task uses Geometra, start with:
11
+
12
+ ```
13
+ geometra_list_sessions()
14
+ geometra_disconnect({ closeBrowser: true })
15
+ geometra_connect({
16
+ pageUrl: "<the assigned URL>",
17
+ isolated: true,
18
+ headless: true,
19
+ slowMo: 250,
20
+ stealth: true
21
+ })
22
+ ```
23
+
24
+ Skip this only if the task explicitly says to attach to an existing session.
25
+
26
+ ## Do
27
+
28
+ - Crawl only public, high-signal company pages.
29
+ - Extract emails, role inboxes, contact forms, public names/titles, source URL, evidence, and warnings.
30
+ - Emit JSON/JSONL matching `templates/lead-schema.json`.
31
+ - Run `npx public-leads validate --input <file>` before returning a result.
32
+ - Return file paths and terminal status, not a prose-only summary.
33
+
34
+ ## Do Not
35
+
36
+ - Guess email patterns, scrape private/authenticated pages, send outreach, or invent source evidence.
37
+ - Spawn or check other tasks.
38
+ - Paste secrets from config or environment.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: general-paid
3
+ description: Quality-sensitive lead research worker for ICP fit, company context, and nuanced review notes.
4
+ ---
5
+
6
+ You are the @general-paid subagent. Use judgment for ICP fit, lead quality review, company context, and evidence quality. Keep final outputs tied to source URLs and local artifacts.
7
+
8
+ ## Do
9
+
10
+ - Research target-company context when the orchestrator requests quality review.
11
+ - Explain why a lead is high/medium/low value using evidence already captured in artifacts.
12
+ - Improve source-backed evidence snippets without fabricating facts.
13
+ - Validate any edited artifact with `npx public-leads validate --input <file>`.
14
+
15
+ ## Do Not
16
+
17
+ - Replace deterministic validation with prose.
18
+ - Send outreach or generate cold emails unless the user explicitly asks for copy outside the crawl workflow.
19
+ - Spawn or check other tasks.
@@ -0,0 +1,8 @@
1
+ ---
2
+ name: glm-minimal
3
+ description: Narrow JSON extractor for small lead snippets and schema transforms. Not for multi-step crawling.
4
+ ---
5
+
6
+ You are the @glm-minimal subagent. Return the requested small structured output only.
7
+
8
+ If the orchestrator asks for JSON, return JSON without markdown fences. If you cannot complete the transform, return `{"error":"<one-sentence reason>"}`.
package/AGENTS.md ADDED
@@ -0,0 +1,56 @@
1
+ # Agent: public-leads-harness
2
+
3
+ Agentic public-web lead discovery harness. It crawls company domains, extracts reviewable public-source contacts, validates artifacts against the local ingest contract, and can submit completed batches to a configured ingest API.
4
+
5
+ ## Hard Limits
6
+
7
+ - [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt.
8
+ - [H2] Respect robots.txt, terms, rate limits, and site intent. If a page blocks crawling or is not public, record a warning and move on.
9
+ - [H3] Never send outreach from this harness. The output is reviewable lead records and ingest-ready artifacts, not email campaigns.
10
+ - [H4] Use file-backed artifacts before ingest. A subagent prose summary is not enough; write validated JSON/JSONL first, then ingest.
11
+ - [H5] Do not paste API tokens, proxy credentials, or cookies into task prompts or summaries. Reference `config/profile.yml` or environment variables by name only.
12
+ - [H6] For batch domain crawling, dispatch at most 2 subagents per round and wait for final artifact paths before launching the next round.
13
+ - [H7] Before using browser/MCP crawling in a batch round, run Geometra cleanup: `geometra_list_sessions()` then `geometra_disconnect({ closeBrowser: true })`.
14
+
15
+ ## Defaults
16
+
17
+ - [D1] Use `@general-free` for procedural crawl/extract/validate work, `@general-paid` for quality-sensitive ICP research and lead-fit reasoning, and `@glm-minimal` for narrow JSON transforms.
18
+ - [D2] Use deterministic local helpers when they can validate, normalize, manifest, preflight, postflight, or ingest artifacts.
19
+ - [D3] Read the active mode file before executing. Mode files own output shapes and gates.
20
+ - [D4] Prefer official company websites and high-signal pages: home, contact, about, team, leadership, people, press, blog, careers, and legal pages.
21
+ - [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`.
22
+
23
+ ## Procedure
24
+
25
+ 1. Check for `config/profile.yml` and `data/domains.tsv` or `data/pipeline.md`; onboard if missing.
26
+ 2. Pick and name the mode from Routing. If no mode fits, ask.
27
+ 3. Read `modes/_shared.md` plus `modes/{mode}.md` unless the mode says otherwise.
28
+ 4. Normalize domains and dedupe against prior artifacts.
29
+ 5. Crawl bounded, public pages only. Preserve warnings and page visits.
30
+ 6. Validate lead artifacts with `npx public-leads validate`.
31
+ 7. Create or update the manifest with `npx public-leads manifest`.
32
+ 8. Ingest only validated payloads with `npx public-leads ingest`.
33
+ 9. Run `npx public-leads verify` before ending a batch workflow.
34
+
35
+ ## Routing
36
+
37
+ | If the user... | Mode |
38
+ |---|---|
39
+ | wants to crawl domains | `crawl` |
40
+ | gives a domain list or asks for leads | `pipeline` |
41
+ | asks to submit/store leads | `ingest` |
42
+ | asks to inspect lead state | `review` |
43
+ | asks for batch processing | `batch` |
44
+ | asks for setup | `setup` |
45
+ | otherwise | Ask which mode fits |
46
+
47
+ ## Output
48
+
49
+ Output shape is mode-specific. Keep user-facing status terse and ground claims in files, command output, or ingest responses.
50
+
51
+ ## Reference
52
+
53
+ - `modes/{mode}.md` for active workflow instructions.
54
+ - `templates/lead-schema.json` for the lead artifact contract.
55
+ - `templates/states.yml` for canonical run and ingest states.
56
+ - `docs/ARCHITECTURE.md` for harness architecture.
package/CLAUDE.md ADDED
@@ -0,0 +1,56 @@
1
+ # Agent: public-leads-harness
2
+
3
+ Agentic public-web lead discovery harness. It crawls company domains, extracts reviewable public-source contacts, validates artifacts against the local ingest contract, and can submit completed batches to a configured ingest API.
4
+
5
+ ## Hard Limits
6
+
7
+ - [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt.
8
+ - [H2] Respect robots.txt, terms, rate limits, and site intent. If a page blocks crawling or is not public, record a warning and move on.
9
+ - [H3] Never send outreach from this harness. The output is reviewable lead records and ingest-ready artifacts, not email campaigns.
10
+ - [H4] Use file-backed artifacts before ingest. A subagent prose summary is not enough; write validated JSON/JSONL first, then ingest.
11
+ - [H5] Do not paste API tokens, proxy credentials, or cookies into task prompts or summaries. Reference `config/profile.yml` or environment variables by name only.
12
+ - [H6] For batch domain crawling, dispatch at most 2 subagents per round and wait for final artifact paths before launching the next round.
13
+ - [H7] Before using browser/MCP crawling in a batch round, run Geometra cleanup: `geometra_list_sessions()` then `geometra_disconnect({ closeBrowser: true })`.
14
+
15
+ ## Defaults
16
+
17
+ - [D1] Use `@general-free` for procedural crawl/extract/validate work, `@general-paid` for quality-sensitive ICP research and lead-fit reasoning, and `@glm-minimal` for narrow JSON transforms.
18
+ - [D2] Use deterministic local helpers when they can validate, normalize, manifest, preflight, postflight, or ingest artifacts.
19
+ - [D3] Read the active mode file before executing. Mode files own output shapes and gates.
20
+ - [D4] Prefer official company websites and high-signal pages: home, contact, about, team, leadership, people, press, blog, careers, and legal pages.
21
+ - [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`.
22
+
23
+ ## Procedure
24
+
25
+ 1. Check for `config/profile.yml` and `data/domains.tsv` or `data/pipeline.md`; onboard if missing.
26
+ 2. Pick and name the mode from Routing. If no mode fits, ask.
27
+ 3. Read `modes/_shared.md` plus `modes/{mode}.md` unless the mode says otherwise.
28
+ 4. Normalize domains and dedupe against prior artifacts.
29
+ 5. Crawl bounded, public pages only. Preserve warnings and page visits.
30
+ 6. Validate lead artifacts with `npx public-leads validate`.
31
+ 7. Create or update the manifest with `npx public-leads manifest`.
32
+ 8. Ingest only validated payloads with `npx public-leads ingest`.
33
+ 9. Run `npx public-leads verify` before ending a batch workflow.
34
+
35
+ ## Routing
36
+
37
+ | If the user... | Mode |
38
+ |---|---|
39
+ | wants to crawl domains | `crawl` |
40
+ | gives a domain list or asks for leads | `pipeline` |
41
+ | asks to submit/store leads | `ingest` |
42
+ | asks to inspect lead state | `review` |
43
+ | asks for batch processing | `batch` |
44
+ | asks for setup | `setup` |
45
+ | otherwise | Ask which mode fits |
46
+
47
+ ## Output
48
+
49
+ Output shape is mode-specific. Keep user-facing status terse and ground claims in files, command output, or ingest responses.
50
+
51
+ ## Reference
52
+
53
+ - `modes/{mode}.md` for active workflow instructions.
54
+ - `templates/lead-schema.json` for the lead artifact contract.
55
+ - `templates/states.yml` for canonical run and ingest states.
56
+ - `docs/ARCHITECTURE.md` for harness architecture.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Razroo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @agent-pattern-labs/leads-rig
2
+
3
+ Agentic public-web lead discovery harness for portable, source-backed lead artifacts. It uses a package-oriented harness shape with an installable npm package, `iso/` source for multi-agent config, mode files, deterministic local helpers, batch orchestration, and a verifier gate.
4
+
5
+ ## What It Does
6
+
7
+ - Crawls public company websites through agent workflows.
8
+ - Produces source-backed lead artifacts matching the local lead-ingest contract.
9
+ - Validates lead JSON/JSONL before database handoff.
10
+ - Builds a local manifest for batch auditability.
11
+ - Submits validated payloads to a configured ingest API.
12
+ - Ships OpenCode, Claude Code, Cursor, Codex, and Pi harness config from one `iso/` source.
13
+
14
+ ## Core Commands
15
+
16
+ ```bash
17
+ npm install
18
+ npm run verify
19
+ npm run smoke:iso
20
+
21
+ npx -p @agent-pattern-labs/leads-rig create-public-leads-harness my-lead-project
22
+ npx public-leads crawl --domains example.com --out data/lead-results.json
23
+ npx public-leads pipeline --input data/domains.tsv --ingest --target-project /path/to/cold-agent-leads
24
+ npx public-leads validate --input examples/sample-leads.json
25
+ npx public-leads ingest --input examples/sample-leads.json --dry-run --out output/sample-ingest.json
26
+ batch/batch-runner.sh --help
27
+ ```
28
+
29
+ ## Reference Ingest Handoff
30
+
31
+ The default reference client posts to the Cold Agent Leads ingest API:
32
+
33
+ ```http
34
+ POST /api/lead-ingests
35
+ Authorization: Bearer $ADMIN_API_TOKEN
36
+ X-Admin-Email: admin@example.com
37
+ Content-Type: application/json
38
+ ```
39
+
40
+ All endpoint details are configurable through `config/profile.yml`, CLI flags, environment variables, or `--target-project /path/to/cold-agent-leads` for reading that app's `.env` at runtime. Payloads contain `jobId`, `domains`, `leads`, `results`, and `errors`, with the schema defined in `templates/lead-schema.json`.
41
+
42
+ Legacy aliases `lead-harness` and `create-leads-harness` remain available for compatibility.
43
+
44
+ ## Publishing
45
+
46
+ The npm package publishes as `@agent-pattern-labs/leads-rig` with public scoped access. The GitHub Actions publish workflow expects `NPM_TOKEN` to be available as a repo or organization secret.
47
+
48
+ ```bash
49
+ gh workflow run publish.yml --ref main
50
+ gh run watch
51
+ ```
52
+
53
+ You can also publish by creating a GitHub release for the package version:
54
+
55
+ ```bash
56
+ gh release create v0.1.3 --title v0.1.3 --generate-notes
57
+ ```
58
+
59
+ The package version must not already exist on npm.
60
+
61
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/CONSTRUCTION.md](docs/CONSTRUCTION.md), [docs/SETUP.md](docs/SETUP.md), and [batch/README.md](batch/README.md).
@@ -0,0 +1,37 @@
1
+ # Batch Lead Discovery
2
+
3
+ `batch/` contains the durable runner for processing many company domains with headless agent workers.
4
+
5
+ ## Input
6
+
7
+ Create `batch/batch-input.tsv`:
8
+
9
+ ```text
10
+ id domain company notes
11
+ 1 example.com Example Seed target
12
+ 2 example.org Example Org Official site
13
+ ```
14
+
15
+ ## Commands
16
+
17
+ ```bash
18
+ batch/batch-runner.sh --dry-run
19
+ batch/batch-runner.sh --parallel 2
20
+ batch/batch-runner.sh --runner codex --parallel 2
21
+ ```
22
+
23
+ `--parallel` is capped at `2` to keep browser-heavy work bounded. Worker CLI permission-bypass flags are disabled by default; pass `--allow-unsafe-workers` only in a trusted local workspace when you explicitly want the old unsafe behavior.
24
+
25
+ The runner writes:
26
+
27
+ - `batch/batch-state.tsv`
28
+ - `batch/logs/*.log`
29
+ - `batch/lead-results-*.json`
30
+ - `.public-leads-runs/` durable workflow records
31
+
32
+ After a run:
33
+
34
+ ```bash
35
+ npm run verify
36
+ npx public-leads ingest --input batch/lead-results-1.json --dry-run
37
+ ```
@@ -0,0 +1,19 @@
1
+ # Lead Harness Batch Worker
2
+
3
+ You are processing assigned public company domains for a source-backed lead discovery workflow.
4
+
5
+ Follow `modes/_shared.md` and `modes/crawl.md`.
6
+
7
+ For each assigned domain:
8
+
9
+ 1. Crawl only bounded public official-company pages.
10
+ 2. Extract source-backed public leads.
11
+ 3. Write `batch/lead-results-{id}.json` matching `templates/lead-schema.json`.
12
+ 4. Run `npx public-leads validate --input batch/lead-results-{id}.json`.
13
+ 5. End with one JSON status line:
14
+
15
+ ```json
16
+ {"id":"<id>","status":"completed|failed","domain":"example.com","leadCount":0,"artifact":"batch/lead-results-<id>.json","error":null}
17
+ ```
18
+
19
+ Do not send outreach. Do not infer or brute-force emails. Do not paste secrets.
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ PROJECT_DIR="${PUBLIC_LEADS_PROJECT:-${LEAD_HARNESS_PROJECT:-$(cd "$SCRIPT_DIR/.." && pwd)}}"
6
+
7
+ SOURCE="${BASH_SOURCE[0]}"
8
+ while [[ -L "$SOURCE" ]]; do
9
+ SOURCE_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
10
+ SOURCE="$(readlink "$SOURCE")"
11
+ [[ "$SOURCE" != /* ]] && SOURCE="$SOURCE_DIR/$SOURCE"
12
+ done
13
+ HARNESS_BATCH_DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
14
+ HARNESS_DIR="$(cd "$HARNESS_BATCH_DIR/.." && pwd)"
15
+
16
+ export PUBLIC_LEADS_PROJECT="$PROJECT_DIR"
17
+ export LEAD_HARNESS_PROJECT="$PROJECT_DIR"
18
+ exec node "$HARNESS_DIR/scripts/batch-orchestrator.mjs" "$@"
@@ -0,0 +1,178 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { copyFileSync, existsSync, mkdirSync, writeFileSync } from 'fs';
4
+ import { basename, dirname, join, resolve } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const PKG_ROOT = resolve(__dirname, '..');
9
+
10
+ const args = process.argv.slice(2);
11
+ const FORCE = args.includes('--force');
12
+ const HELP = args.includes('--help') || args.includes('-h');
13
+ const positional = args.filter((arg) => !arg.startsWith('--'));
14
+
15
+ if (HELP || positional.length === 0) {
16
+ console.log(`create-public-leads-harness — scaffold a new lead discovery project
17
+
18
+ Usage:
19
+ npx -p @agent-pattern-labs/leads-rig create-public-leads-harness <dir> [--force]
20
+
21
+ After scaffolding:
22
+ cd <dir>
23
+ npm install
24
+ edit config/profile.yml and data/domains.tsv
25
+ opencode
26
+ `);
27
+ process.exit(HELP ? 0 : 1);
28
+ }
29
+
30
+ const targetDir = resolve(positional[0]);
31
+ const name = basename(targetDir);
32
+
33
+ console.log(`\nScaffolding lead harness project in ${targetDir}\n`);
34
+ if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true });
35
+
36
+ write('package.json', JSON.stringify({
37
+ name,
38
+ version: '0.1.0',
39
+ private: true,
40
+ scripts: {
41
+ sync: 'public-leads sync',
42
+ verify: 'public-leads verify',
43
+ crawl: 'public-leads crawl',
44
+ pipeline: 'public-leads pipeline',
45
+ validate: 'public-leads validate',
46
+ manifest: 'public-leads manifest',
47
+ ingest: 'public-leads ingest',
48
+ 'trace:list': 'public-leads trace:list',
49
+ 'trace:stats': 'public-leads trace:stats',
50
+ 'trace:show': 'public-leads trace:show',
51
+ 'update-harness': 'npm install @agent-pattern-labs/leads-rig@latest && public-leads sync',
52
+ },
53
+ dependencies: {
54
+ '@agent-pattern-labs/leads-rig': '^0.1.3',
55
+ },
56
+ engines: {
57
+ node: '>=20.6.0',
58
+ },
59
+ }, null, 2) + '\n');
60
+
61
+ write('opencode.json', JSON.stringify({
62
+ $schema: 'https://opencode.ai/config.json',
63
+ model: 'opencode-go/deepseek-v4-flash',
64
+ small_model: 'opencode-go/deepseek-v4-flash',
65
+ instructions: [
66
+ 'AGENTS.harness.md',
67
+ '.opencode/instructions.md',
68
+ 'templates/lead-schema.json',
69
+ 'templates/states.yml',
70
+ 'modes/_shared.md',
71
+ ],
72
+ mcp: {
73
+ geometra: {
74
+ type: 'local',
75
+ command: ['npx', '-y', '@geometra/mcp@1.61.3'],
76
+ enabled: true,
77
+ },
78
+ },
79
+ permission: {
80
+ task: {
81
+ 'general-free': 'allow',
82
+ 'general-paid': 'allow',
83
+ 'glm-minimal': 'allow',
84
+ },
85
+ },
86
+ tools: {
87
+ 'geometra_*': false,
88
+ },
89
+ }, null, 2) + '\n');
90
+
91
+ write('AGENTS.md', `# AGENTS — ${name}
92
+
93
+ This is a consumer project for the @agent-pattern-labs/leads-rig package. Keep private configuration, target domains, and local artifacts here; the shared harness files are symlinked from \`node_modules/@agent-pattern-labs/leads-rig\` after \`npm install\`.
94
+
95
+ ## Local Files
96
+
97
+ | What | Where |
98
+ |---|---|
99
+ | Harness rules | \`AGENTS.harness.md\` |
100
+ | Domain input | \`data/domains.tsv\` or \`data/pipeline.md\` |
101
+ | Lead artifacts | \`data/lead-results.json\`, \`batch/lead-results*.json\` |
102
+ | Manifest | \`data/lead-manifest.json\` |
103
+ | Ingest response | \`data/ingest-response.json\` |
104
+ | Config | \`config/profile.yml\` |
105
+ `);
106
+
107
+ copy('config/profile.example.yml', 'config/profile.yml');
108
+ write('data/domains.tsv', `domain\tcompany\tnotes
109
+ example.com\tExample\tSeed target
110
+ `);
111
+ write('data/pipeline.md', `# Lead Pipeline
112
+
113
+ - [ ] example.com | Example | Seed target
114
+ `);
115
+ write('reports/.gitkeep', '');
116
+ write('output/.gitkeep', '');
117
+ write('batch/logs/.gitkeep', '');
118
+ write('.gitignore', `node_modules/
119
+ .env
120
+ .env.*
121
+ config/profile.yml
122
+ data/*.json
123
+ data/*.jsonl
124
+ data/runs/
125
+ batch/*.json
126
+ batch/*.jsonl
127
+ batch/logs/*
128
+ !batch/logs/.gitkeep
129
+ reports/*
130
+ !reports/.gitkeep
131
+ output/*
132
+ !output/.gitkeep
133
+ `);
134
+ write('README.md', `# ${name}
135
+
136
+ Lead discovery project powered by \`@agent-pattern-labs/leads-rig\`.
137
+
138
+ ## Start
139
+
140
+ \`\`\`bash
141
+ npm install
142
+ npm run verify
143
+ opencode
144
+ \`\`\`
145
+
146
+ Update \`config/profile.yml\` with your ingest API settings and add domains to \`data/domains.tsv\` or \`data/pipeline.md\`.
147
+ `);
148
+
149
+ console.log('\nNext: npm install, then edit config/profile.yml and data/domains.tsv.\n');
150
+
151
+ function write(rel, content, { overwrite = FORCE } = {}) {
152
+ const abs = join(targetDir, rel);
153
+ if (existsSync(abs) && !overwrite) {
154
+ console.log(` skip: ${rel} (exists)`);
155
+ return;
156
+ }
157
+ const parent = dirname(abs);
158
+ if (!existsSync(parent)) mkdirSync(parent, { recursive: true });
159
+ writeFileSync(abs, content, 'utf8');
160
+ console.log(` create: ${rel}`);
161
+ }
162
+
163
+ function copy(srcRel, dstRel, { overwrite = FORCE } = {}) {
164
+ const src = join(PKG_ROOT, srcRel);
165
+ const abs = join(targetDir, dstRel);
166
+ if (!existsSync(src)) {
167
+ console.log(` skip: ${dstRel} (template ${srcRel} missing)`);
168
+ return;
169
+ }
170
+ if (existsSync(abs) && !overwrite) {
171
+ console.log(` skip: ${dstRel} (exists)`);
172
+ return;
173
+ }
174
+ const parent = dirname(abs);
175
+ if (!existsSync(parent)) mkdirSync(parent, { recursive: true });
176
+ copyFileSync(src, abs);
177
+ console.log(` create: ${dstRel}`);
178
+ }