job-forge 2.14.18 → 2.14.20
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/.cursor/rules/main.mdc +8 -2
- package/.opencode/skills/job-forge.md +8 -0
- package/AGENTS.md +8 -2
- package/CLAUDE.md +8 -2
- package/README.md +6 -4
- package/bin/job-forge.mjs +62 -0
- package/docs/ARCHITECTURE.md +4 -0
- package/docs/CUSTOMIZATION.md +8 -0
- package/docs/MODEL-ROUTING.md +4 -0
- package/docs/README.md +1 -1
- package/docs/SETUP.md +2 -0
- package/iso/commands/job-forge.md +8 -0
- package/iso/instructions.md +8 -2
- package/lib/jobforge-capabilities.mjs +55 -0
- package/lib/jobforge-context.mjs +55 -0
- package/package.json +12 -1
- package/scripts/capabilities.mjs +205 -0
- package/scripts/context.mjs +180 -0
- package/templates/capabilities.json +110 -0
- package/templates/context.json +283 -0
package/.cursor/rules/main.mdc
CHANGED
|
@@ -65,12 +65,18 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
65
65
|
- [D9] Treat `templates/contracts.json` as the source of truth for machine-readable artifacts. Prefer `npx job-forge tracker-line ... --write` for tracker additions; if emitting TSV manually, inspect `npx iso-contract explain jobforge.tracker-row --contracts templates/contracts.json` first. `merge` and `verify` enforce the tracker-row contract locally.
|
|
66
66
|
why: deterministic code owns the exact tracker TSV/table shape; repeated prose gets re-tokenized and agents occasionally misremember it
|
|
67
67
|
|
|
68
|
+
- [D10] Treat `templates/capabilities.json` as the source of truth for role capability boundaries. Use `npx job-forge capabilities:explain <role>` or `npx job-forge capabilities:check <role> ...` when changing or validating subagent tool/MCP/filesystem/command permissions; do not paste the full capability matrix into task prompts.
|
|
69
|
+
why: executable local policy prevents role-permission drift without adding MCP/tool-schema tokens or loading a capability matrix into the shared prefix
|
|
70
|
+
|
|
71
|
+
- [D11] Treat `templates/context.json` as the source of truth for mode/reference context bundles. Use `npx job-forge context:plan <mode>` or `npx job-forge context:check <mode>` when changing or validating what a mode loads; do not paste the full context matrix into prompts.
|
|
72
|
+
why: deterministic context bundles prevent reference-file drift and accidental token bloat without adding MCP/tool-schema tokens
|
|
73
|
+
|
|
68
74
|
## Procedure
|
|
69
75
|
|
|
70
76
|
1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
|
|
71
77
|
2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
72
|
-
3. Read the active mode file [D3]; decide inline vs delegated work [D1].
|
|
73
|
-
4. Prepare Geometra dispatches: cleanup [H3], ledger prefilter when present [D8], dedupe [H2], location filter [D5], routing [D2], proxy prompt hygiene [H8].
|
|
78
|
+
3. Read the active mode file [D3]; use context bundle checks when changing context loads [D11]; decide inline vs delegated work [D1].
|
|
79
|
+
4. Prepare Geometra dispatches: cleanup [H3], ledger prefilter when present [D8], dedupe [H2], location filter [D5], routing [D2, D10], proxy prompt hygiene [H8].
|
|
74
80
|
5. Dispatch at most 2 tasks per round [H1]; wait for final outcomes, not just task ids [H5b].
|
|
75
81
|
6. Keep multi-job form-filling out of the orchestrator [H4].
|
|
76
82
|
7. Cross-check subagent facts against authoritative files [H7].
|
|
@@ -76,6 +76,14 @@ Local workflow ledger (terminal, outside opencode):
|
|
|
76
76
|
Artifact contracts (terminal, outside opencode):
|
|
77
77
|
npx iso-contract explain jobforge.tracker-row --contracts templates/contracts.json
|
|
78
78
|
npx job-forge tracker-line ... --write # renders + validates tracker TSV locally
|
|
79
|
+
|
|
80
|
+
Role capabilities (terminal, outside opencode):
|
|
81
|
+
npx job-forge capabilities:explain general-free
|
|
82
|
+
npx job-forge capabilities:check general-free --tool browser --mcp geometra --filesystem write
|
|
83
|
+
|
|
84
|
+
Context bundles (terminal, outside opencode):
|
|
85
|
+
npx job-forge context:plan apply
|
|
86
|
+
npx job-forge context:check apply --budget 23000
|
|
79
87
|
```
|
|
80
88
|
|
|
81
89
|
---
|
package/AGENTS.md
CHANGED
|
@@ -60,12 +60,18 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
60
60
|
- [D9] Treat `templates/contracts.json` as the source of truth for machine-readable artifacts. Prefer `npx job-forge tracker-line ... --write` for tracker additions; if emitting TSV manually, inspect `npx iso-contract explain jobforge.tracker-row --contracts templates/contracts.json` first. `merge` and `verify` enforce the tracker-row contract locally.
|
|
61
61
|
why: deterministic code owns the exact tracker TSV/table shape; repeated prose gets re-tokenized and agents occasionally misremember it
|
|
62
62
|
|
|
63
|
+
- [D10] Treat `templates/capabilities.json` as the source of truth for role capability boundaries. Use `npx job-forge capabilities:explain <role>` or `npx job-forge capabilities:check <role> ...` when changing or validating subagent tool/MCP/filesystem/command permissions; do not paste the full capability matrix into task prompts.
|
|
64
|
+
why: executable local policy prevents role-permission drift without adding MCP/tool-schema tokens or loading a capability matrix into the shared prefix
|
|
65
|
+
|
|
66
|
+
- [D11] Treat `templates/context.json` as the source of truth for mode/reference context bundles. Use `npx job-forge context:plan <mode>` or `npx job-forge context:check <mode>` when changing or validating what a mode loads; do not paste the full context matrix into prompts.
|
|
67
|
+
why: deterministic context bundles prevent reference-file drift and accidental token bloat without adding MCP/tool-schema tokens
|
|
68
|
+
|
|
63
69
|
## Procedure
|
|
64
70
|
|
|
65
71
|
1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
|
|
66
72
|
2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
67
|
-
3. Read the active mode file [D3]; decide inline vs delegated work [D1].
|
|
68
|
-
4. Prepare Geometra dispatches: cleanup [H3], ledger prefilter when present [D8], dedupe [H2], location filter [D5], routing [D2], proxy prompt hygiene [H8].
|
|
73
|
+
3. Read the active mode file [D3]; use context bundle checks when changing context loads [D11]; decide inline vs delegated work [D1].
|
|
74
|
+
4. Prepare Geometra dispatches: cleanup [H3], ledger prefilter when present [D8], dedupe [H2], location filter [D5], routing [D2, D10], proxy prompt hygiene [H8].
|
|
69
75
|
5. Dispatch at most 2 tasks per round [H1]; wait for final outcomes, not just task ids [H5b].
|
|
70
76
|
6. Keep multi-job form-filling out of the orchestrator [H4].
|
|
71
77
|
7. Cross-check subagent facts against authoritative files [H7].
|
package/CLAUDE.md
CHANGED
|
@@ -60,12 +60,18 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
60
60
|
- [D9] Treat `templates/contracts.json` as the source of truth for machine-readable artifacts. Prefer `npx job-forge tracker-line ... --write` for tracker additions; if emitting TSV manually, inspect `npx iso-contract explain jobforge.tracker-row --contracts templates/contracts.json` first. `merge` and `verify` enforce the tracker-row contract locally.
|
|
61
61
|
why: deterministic code owns the exact tracker TSV/table shape; repeated prose gets re-tokenized and agents occasionally misremember it
|
|
62
62
|
|
|
63
|
+
- [D10] Treat `templates/capabilities.json` as the source of truth for role capability boundaries. Use `npx job-forge capabilities:explain <role>` or `npx job-forge capabilities:check <role> ...` when changing or validating subagent tool/MCP/filesystem/command permissions; do not paste the full capability matrix into task prompts.
|
|
64
|
+
why: executable local policy prevents role-permission drift without adding MCP/tool-schema tokens or loading a capability matrix into the shared prefix
|
|
65
|
+
|
|
66
|
+
- [D11] Treat `templates/context.json` as the source of truth for mode/reference context bundles. Use `npx job-forge context:plan <mode>` or `npx job-forge context:check <mode>` when changing or validating what a mode loads; do not paste the full context matrix into prompts.
|
|
67
|
+
why: deterministic context bundles prevent reference-file drift and accidental token bloat without adding MCP/tool-schema tokens
|
|
68
|
+
|
|
63
69
|
## Procedure
|
|
64
70
|
|
|
65
71
|
1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
|
|
66
72
|
2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
67
|
-
3. Read the active mode file [D3]; decide inline vs delegated work [D1].
|
|
68
|
-
4. Prepare Geometra dispatches: cleanup [H3], ledger prefilter when present [D8], dedupe [H2], location filter [D5], routing [D2], proxy prompt hygiene [H8].
|
|
73
|
+
3. Read the active mode file [D3]; use context bundle checks when changing context loads [D11]; decide inline vs delegated work [D1].
|
|
74
|
+
4. Prepare Geometra dispatches: cleanup [H3], ledger prefilter when present [D8], dedupe [H2], location filter [D5], routing [D2, D10], proxy prompt hygiene [H8].
|
|
69
75
|
5. Dispatch at most 2 tasks per round [H1]; wait for final outcomes, not just task ids [H5b].
|
|
70
76
|
6. Keep multi-job form-filling out of the orchestrator [H4].
|
|
71
77
|
7. Cross-check subagent facts against authoritative files [H7].
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ The scaffolded `opencode.json` already has three MCPs wired up — they launch a
|
|
|
31
31
|
- **Gmail** — reads replies from recruiters
|
|
32
32
|
- **state-trace** — typed working memory for cross-session context (resumed batches, recent decisions, repeated portal quirks). Install once with `python3 -m pip install "state-trace[mcp]"`; the MCP command is `state-trace-mcp`.
|
|
33
33
|
|
|
34
|
-
JobForge also keeps MCP-free local workflow state: `templates/contracts.json` defines tracker/apply artifact shapes via `@razroo/iso-contract`, and `.jobforge-ledger/events.jsonl` records deterministic duplicate/status events via `@razroo/iso-ledger`.
|
|
34
|
+
JobForge also keeps MCP-free local workflow state: `templates/contracts.json` defines tracker/apply artifact shapes via `@razroo/iso-contract`, `templates/capabilities.json` defines role capability boundaries via `@razroo/iso-capabilities`, `templates/context.json` defines deterministic mode/reference bundles via `@razroo/iso-context`, and `.jobforge-ledger/events.jsonl` records deterministic duplicate/status events via `@razroo/iso-ledger`. None of these add always-on prompt or tool-schema tokens.
|
|
35
35
|
|
|
36
36
|
`npm install` also materializes symlinks for every supported agent harness — OpenCode, Cursor, Claude Code, and Codex — so you can run `opencode`, `cursor`, `claude`, or `codex` in the same project and each picks up the shared MCP config and instructions.
|
|
37
37
|
|
|
@@ -78,7 +78,7 @@ JobForge turns opencode into a full job search command center. Instead of manual
|
|
|
78
78
|
| **Durable Batch Orchestration** | `batch-runner.sh` uses `@razroo/iso-orchestrator` for resumable bundle execution, bounded fan-out, mutexed state writes, and workflow records in `.jobforge-runs/`. |
|
|
79
79
|
| **Pipeline Integrity** | Automated merge, dedup, status normalization, health checks |
|
|
80
80
|
| **Cost-Aware Agent Routing** | Three subagents (`@general-free`, `@general-paid`, `@glm-minimal`) with per-task tool surfaces. On OpenCode, JobForge pins all tiers to `opencode-go/deepseek-v4-flash` so application runs avoid overloaded free-model pools. See [Subagent Routing in AGENTS.md](AGENTS.md) for the task-to-agent mapping. |
|
|
81
|
-
| **Trace + Telemetry + Guard + Contract + Ledger** | `job-forge trace:*` exposes local OpenCode transcripts, `job-forge telemetry:*` summarizes runs, `job-forge guard:*` audits deterministic policy rules, `templates/contracts.json` enforces artifact shape with `iso-contract`,
|
|
81
|
+
| **Trace + Telemetry + Guard + Contract + Ledger + Capabilities + Context** | `job-forge trace:*` exposes local OpenCode transcripts, `job-forge telemetry:*` summarizes runs, `job-forge guard:*` audits deterministic policy rules, `templates/contracts.json` enforces artifact shape with `iso-contract`, `job-forge ledger:*` queries append-only workflow state, `job-forge capabilities:*` checks role boundaries, and `job-forge context:*` plans mode/reference context bundles without MCP/tool-schema overhead. |
|
|
82
82
|
| **Token Cost Visibility** | `job-forge tokens --days 1` for per-session breakdown; `job-forge session-report --since-minutes 60 --log` to flag sessions over budget and append history to `data/token-usage.tsv`. Auto-logged after every batch run. |
|
|
83
83
|
|
|
84
84
|
## Usage
|
|
@@ -162,7 +162,7 @@ my-search/
|
|
|
162
162
|
├── .opencode/skills/job-forge.md # → skill router
|
|
163
163
|
├── .opencode/agents/ # → @general-free, @general-paid, @glm-minimal
|
|
164
164
|
├── modes/ # → _shared.md + skill modes
|
|
165
|
-
├── templates/ # → states.yml, portals.example.yml, cv-template.html
|
|
165
|
+
├── templates/ # → states.yml, portals.example.yml, cv-template.html, capabilities.json, context.json
|
|
166
166
|
├── batch/batch-prompt.md # → batch worker prompt
|
|
167
167
|
├── batch/batch-runner.sh # → parallel orchestrator
|
|
168
168
|
│
|
|
@@ -188,13 +188,15 @@ JobForge/
|
|
|
188
188
|
│ ├── sync.mjs # postinstall: creates symlinks in consumer project
|
|
189
189
|
│ └── create-job-forge.mjs # scaffolder
|
|
190
190
|
├── modes/ # _shared.md + 16 skill modes
|
|
191
|
-
├── templates/ # cv-template.html, portals.example.yml, states.yml
|
|
191
|
+
├── templates/ # cv-template.html, portals.example.yml, states.yml, capabilities.json, context.json
|
|
192
192
|
├── config/profile.example.yml # template for consumer's profile.yml
|
|
193
193
|
├── batch/{batch-prompt.md,batch-runner.sh} # batch orchestrator
|
|
194
194
|
├── scripts/
|
|
195
195
|
│ ├── batch-orchestrator.mjs # iso-orchestrator-backed batch control loop
|
|
196
196
|
│ ├── tracker-line.mjs # iso-contract-backed tracker TSV renderer
|
|
197
197
|
│ ├── ledger.mjs # iso-ledger-backed workflow-state CLI
|
|
198
|
+
│ ├── capabilities.mjs # iso-capabilities-backed role policy CLI
|
|
199
|
+
│ ├── context.mjs # iso-context-backed context bundle CLI
|
|
198
200
|
│ ├── token-usage-report.mjs # opencode cost analyzer
|
|
199
201
|
│ └── release/check-source.mjs # version gate for npm publish
|
|
200
202
|
├── tracker-lib.mjs / merge-tracker.mjs / dedup-tracker.mjs / verify-pipeline.mjs
|
package/bin/job-forge.mjs
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
* telemetry:* Summarize JobForge pipeline status from traces + tracker files
|
|
22
22
|
* guard:* Audit JobForge trace policy with iso-guard
|
|
23
23
|
* ledger:* Query local deterministic workflow state via iso-ledger
|
|
24
|
+
* capabilities:* Query role capability policy via iso-capabilities
|
|
25
|
+
* context:* Query/render deterministic context bundles via iso-context
|
|
24
26
|
* sync Re-run the harness symlink sync (bin/sync.mjs)
|
|
25
27
|
* help, --help Show this message
|
|
26
28
|
*/
|
|
@@ -84,6 +86,23 @@ const ledgerAliases = {
|
|
|
84
86
|
'ledger:path': 'path',
|
|
85
87
|
};
|
|
86
88
|
|
|
89
|
+
const capabilitiesAliases = {
|
|
90
|
+
'capabilities:list': 'list',
|
|
91
|
+
'capabilities:explain': 'explain',
|
|
92
|
+
'capabilities:check': 'check',
|
|
93
|
+
'capabilities:render': 'render',
|
|
94
|
+
'capabilities:path': 'path',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const contextAliases = {
|
|
98
|
+
'context:list': 'list',
|
|
99
|
+
'context:explain': 'explain',
|
|
100
|
+
'context:plan': 'plan',
|
|
101
|
+
'context:check': 'check',
|
|
102
|
+
'context:render': 'render',
|
|
103
|
+
'context:path': 'path',
|
|
104
|
+
};
|
|
105
|
+
|
|
87
106
|
const [, , cmd, ...rest] = process.argv;
|
|
88
107
|
|
|
89
108
|
function printHelp() {
|
|
@@ -114,6 +133,15 @@ Commands:
|
|
|
114
133
|
ledger:rebuild Rebuild .jobforge-ledger/events.jsonl from tracker/pipeline files
|
|
115
134
|
ledger:has Check URL or company+role state without loading tracker files
|
|
116
135
|
ledger:verify Validate the local workflow ledger
|
|
136
|
+
capabilities:list List JobForge role capability policies
|
|
137
|
+
capabilities:explain Explain one role capability policy
|
|
138
|
+
capabilities:check Validate requested tool/MCP/command/fs/network access
|
|
139
|
+
capabilities:render Render compact role guidance for an agent harness
|
|
140
|
+
context:list List JobForge context bundles
|
|
141
|
+
context:explain Explain one context bundle
|
|
142
|
+
context:plan Estimate files/tokens for one context bundle
|
|
143
|
+
context:check Fail if a context bundle exceeds its budget
|
|
144
|
+
context:render Render context bundle content as markdown/json
|
|
117
145
|
sync Re-create harness symlinks in the current project
|
|
118
146
|
|
|
119
147
|
Deterministic helpers (prefer these over LLM-derived values):
|
|
@@ -143,6 +171,10 @@ Pass --help after a command to see its own flags, e.g.:
|
|
|
143
171
|
job-forge guard:audit
|
|
144
172
|
job-forge guard:explain
|
|
145
173
|
job-forge ledger:has --company "Acme" --role "Staff Engineer" --status Applied
|
|
174
|
+
job-forge capabilities:explain general-free
|
|
175
|
+
job-forge capabilities:check general-free --tool browser --mcp geometra --command "npx job-forge merge" --filesystem write
|
|
176
|
+
job-forge context:plan apply
|
|
177
|
+
job-forge context:check apply --budget 23000
|
|
146
178
|
|
|
147
179
|
Project directory resolves to $JOB_FORGE_PROJECT or cwd.`);
|
|
148
180
|
}
|
|
@@ -212,6 +244,36 @@ if (cmd === 'ledger' || ledgerAliases[cmd]) {
|
|
|
212
244
|
process.exit(result.status ?? 1);
|
|
213
245
|
}
|
|
214
246
|
|
|
247
|
+
if (cmd === 'capabilities' || capabilitiesAliases[cmd]) {
|
|
248
|
+
const capabilitiesArgs = cmd === 'capabilities'
|
|
249
|
+
? (rest.length === 0 ? ['help'] : rest)
|
|
250
|
+
: [capabilitiesAliases[cmd], ...rest];
|
|
251
|
+
|
|
252
|
+
const scriptPath = join(PKG_ROOT, 'scripts/capabilities.mjs');
|
|
253
|
+
const result = spawnSync(process.execPath, [scriptPath, ...capabilitiesArgs], {
|
|
254
|
+
stdio: 'inherit',
|
|
255
|
+
cwd: PROJECT_DIR,
|
|
256
|
+
env: process.env,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
process.exit(result.status ?? 1);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (cmd === 'context' || contextAliases[cmd]) {
|
|
263
|
+
const contextArgs = cmd === 'context'
|
|
264
|
+
? (rest.length === 0 ? ['help'] : rest)
|
|
265
|
+
: [contextAliases[cmd], ...rest];
|
|
266
|
+
|
|
267
|
+
const scriptPath = join(PKG_ROOT, 'scripts/context.mjs');
|
|
268
|
+
const result = spawnSync(process.execPath, [scriptPath, ...contextArgs], {
|
|
269
|
+
stdio: 'inherit',
|
|
270
|
+
cwd: PROJECT_DIR,
|
|
271
|
+
env: process.env,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
process.exit(result.status ?? 1);
|
|
275
|
+
}
|
|
276
|
+
|
|
215
277
|
const rel = commands[cmd];
|
|
216
278
|
if (!rel) {
|
|
217
279
|
console.error(`Unknown command: ${cmd}\n`);
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -163,6 +163,7 @@ data/pipeline.md → Pending URLs and `local:jds/...` inbox (see modes/p
|
|
|
163
163
|
.jobforge-ledger/events.jsonl → Append-only workflow events for cheap local duplicate/status checks
|
|
164
164
|
jds/*.md → Saved job descriptions referenced from the pipeline (`local:jds/{file}`)
|
|
165
165
|
templates/states.yml → Canonical status values
|
|
166
|
+
templates/context.json → Deterministic mode/reference context bundle policy
|
|
166
167
|
templates/cv-template.html → PDF generation template
|
|
167
168
|
examples/*.md → Fictional layouts only (not read by scripts; see examples/README.md)
|
|
168
169
|
```
|
|
@@ -175,6 +176,8 @@ Create `data/pipeline.md` when you start using the URL inbox (`/job-forge pipeli
|
|
|
175
176
|
- PDFs: `cv-candidate-{company-slug}-{YYYY-MM-DD}.pdf`
|
|
176
177
|
- Tracker TSVs: `batch/tracker-additions/{num}-{company-slug}.tsv` (one file per evaluation; merged files move under `batch/tracker-additions/merged/`; shape enforced by `templates/contracts.json`)
|
|
177
178
|
- Ledger: `.jobforge-ledger/events.jsonl` (created by `job-forge ledger:rebuild`, `tracker-line --write`, or `merge`; gitignored personal state)
|
|
179
|
+
- Capabilities: `templates/capabilities.json` (role boundary policy inspected with `job-forge capabilities:*`)
|
|
180
|
+
- Context: `templates/context.json` (mode/reference file bundles inspected with `job-forge context:*`)
|
|
178
181
|
|
|
179
182
|
## Pipeline Integrity
|
|
180
183
|
|
|
@@ -216,6 +219,7 @@ Scripts maintain data consistency. In a consumer project they're invoked via the
|
|
|
216
219
|
| `scripts/telemetry.mjs` | `npx job-forge telemetry:status` / `telemetry:show` | JobForge operational telemetry derived from OpenCode traces plus tracker TSV state |
|
|
217
220
|
| `scripts/guard.mjs` | `npx job-forge guard:audit` / `guard:explain` | Deterministic `@razroo/iso-guard` policy audits over local OpenCode traces |
|
|
218
221
|
| `scripts/ledger.mjs` | `npx job-forge ledger:status` / `ledger:has` / `ledger:rebuild` | Deterministic `@razroo/iso-ledger` state over tracker, TSV, and pipeline files |
|
|
222
|
+
| `scripts/context.mjs` | `npx job-forge context:list` / `context:plan` / `context:check` / `context:render` | Deterministic `@razroo/iso-context` mode/reference context bundle planning and rendering |
|
|
219
223
|
| `tracker-lib.mjs` | _(library)_ | Shared helpers for reading/writing day-based tracker files — imported by merge/dedup/verify/normalize |
|
|
220
224
|
| `bin/sync.mjs` | `npx job-forge sync` | Creates the harness symlinks in a consumer project (also runs as `postinstall`) |
|
|
221
225
|
| `bin/create-job-forge.mjs` | `npx create-job-forge <dir>` | Scaffolds a new personal project |
|
package/docs/CUSTOMIZATION.md
CHANGED
|
@@ -142,6 +142,14 @@ npx job-forge ledger:verify
|
|
|
142
142
|
|
|
143
143
|
Machine-readable artifact shapes live in `templates/contracts.json` and are enforced by `@razroo/iso-contract`. `job-forge tracker-line` renders tracker additions through the `jobforge.tracker-row` contract, `merge` validates pending TSV/table rows before writing tracker files, and `verify` validates existing tracker rows against the same contract. Custom forks can extend `templates/contracts.json`, but keep the tracker status enum aligned with `templates/states.yml`.
|
|
144
144
|
|
|
145
|
+
## JobForge role capabilities
|
|
146
|
+
|
|
147
|
+
Role capability boundaries live in `templates/capabilities.json` and are enforced locally by `@razroo/iso-capabilities`. Use `job-forge capabilities:explain <role>` to inspect a role and `job-forge capabilities:check <role> ...` to validate a tool, MCP, command, filesystem, or network boundary before changing agent frontmatter. Custom forks can extend the policy, but keep it aligned with `.opencode/agents/` and the routing rules in `iso/instructions.md`.
|
|
148
|
+
|
|
149
|
+
## JobForge context bundles
|
|
150
|
+
|
|
151
|
+
Mode/reference context bundles live in `templates/context.json` and are planned locally by `@razroo/iso-context`. Use `job-forge context:plan <mode>` to see the files and estimated tokens, `job-forge context:check <mode>` to fail on budget drift, and `job-forge context:render <mode>` when you intentionally need a compact markdown or JSON context bundle. This is not an MCP and does not add tool-schema tokens; rendered context only consumes prompt tokens when a workflow deliberately asks for it.
|
|
152
|
+
|
|
145
153
|
## JobForge guard audits
|
|
146
154
|
|
|
147
155
|
Guard audits run deterministic `@razroo/iso-guard` policies over the same local OpenCode traces. The default policy lives at `templates/guards/jobforge-baseline.yaml` and checks rules that are reliable from transcript data, including max two task dispatches per assistant message, no task-status polling via `task`, no raw proxy configuration in task prompts, and no child session task recursion.
|
package/docs/MODEL-ROUTING.md
CHANGED
|
@@ -53,6 +53,10 @@ The orchestrator can only dispatch to these three agents. Accidental self-calls
|
|
|
53
53
|
```
|
|
54
54
|
Disables ~30 MCP tool schemas globally; each agent re-enables only what it needs in its own `.opencode/agents/<name>.md` frontmatter. Saves ~2-3K input tokens per request in the orchestrator.
|
|
55
55
|
|
|
56
|
+
`templates/capabilities.json` is the executable source for intended role boundaries. Inspect it with `job-forge capabilities:explain general-free`, or validate a boundary with `job-forge capabilities:check general-free --tool browser --mcp geometra --command "npx job-forge merge" --filesystem write --network restricted`. Native harness frontmatter still enforces permissions where supported; `iso-capabilities` gives JobForge a local audit/check contract for changes and reviews.
|
|
57
|
+
|
|
58
|
+
`templates/context.json` is the executable source for intended mode/reference context loads. Inspect a bundle with `job-forge context:plan apply`, fail CI or local reviews on drift with `job-forge context:check apply`, or render the bundle only when a workflow deliberately needs it. `iso-context` is local CLI/library policy, not an MCP, so it avoids tool-schema overhead and prevents accidental "load every mode file" prompt bloat.
|
|
59
|
+
|
|
56
60
|
**3. Thinking budgets** (`reasoningEffort` in agent frontmatter):
|
|
57
61
|
- `@general-free`: `minimal` — procedural work shouldn't need chain-of-thought
|
|
58
62
|
- `@general-paid`: `medium` — writing quality benefits from thinking
|
package/docs/README.md
CHANGED
|
@@ -31,7 +31,7 @@ The harness exposes a single CLI (`job-forge`) installed as a `bin` entry. In a
|
|
|
31
31
|
|
|
32
32
|
| What you need | Where to read |
|
|
33
33
|
|---------------|---------------|
|
|
34
|
-
| Full command list (`verify`, `merge`, `dedup`, `normalize`, `pdf`, `sync-check`, `tokens`, `trace`, `telemetry`, `guard`, `ledger`, `sync`). | [SETUP.md — Tracker and scripts (terminal)](SETUP.md#tracker-and-scripts-terminal). |
|
|
34
|
+
| Full command list (`verify`, `merge`, `dedup`, `normalize`, `pdf`, `sync-check`, `tokens`, `trace`, `telemetry`, `guard`, `ledger`, `context`, `sync`). | [SETUP.md — Tracker and scripts (terminal)](SETUP.md#tracker-and-scripts-terminal). |
|
|
35
35
|
| What each harness `.mjs` script does. | [ARCHITECTURE.md — Pipeline integrity](ARCHITECTURE.md#pipeline-integrity) and the scripts table underneath. |
|
|
36
36
|
| Batch runner, TSV layout, and `batch/tracker-additions/` merge flow. | [batch/README.md](../batch/README.md). |
|
|
37
37
|
| PR gate for harness contributions (`npm run verify` + `npm run build:dashboard`). | [CONTRIBUTING.md — Development](../CONTRIBUTING.md#development). |
|
package/docs/SETUP.md
CHANGED
|
@@ -126,6 +126,8 @@ From your project root, these commands maintain the tracker and pipeline checks.
|
|
|
126
126
|
| Pipeline health check | `npx job-forge verify` | `npm run verify` |
|
|
127
127
|
| Merge `batch/tracker-additions/*.tsv` into the tracker | `npx job-forge merge` | `npm run merge` |
|
|
128
128
|
| Inspect tracker row contract | `npx iso-contract explain jobforge.tracker-row --contracts templates/contracts.json` | _(none)_ |
|
|
129
|
+
| Inspect role capabilities | `npx job-forge capabilities:explain general-free` | `npm run capabilities:explain -- general-free` |
|
|
130
|
+
| Inspect context bundle budget | `npx job-forge context:plan apply` | `npm run context:plan -- apply` |
|
|
129
131
|
| Map status column to canonical labels | `npx job-forge normalize` | `npm run normalize` |
|
|
130
132
|
| Merge duplicate company/role rows | `npx job-forge dedup` | `npm run dedup` |
|
|
131
133
|
| Generate ATS-optimized CV PDF | `npx job-forge pdf` | `npm run pdf` |
|
|
@@ -79,6 +79,14 @@ Local workflow ledger (terminal, outside opencode):
|
|
|
79
79
|
Artifact contracts (terminal, outside opencode):
|
|
80
80
|
npx iso-contract explain jobforge.tracker-row --contracts templates/contracts.json
|
|
81
81
|
npx job-forge tracker-line ... --write # renders + validates tracker TSV locally
|
|
82
|
+
|
|
83
|
+
Role capabilities (terminal, outside opencode):
|
|
84
|
+
npx job-forge capabilities:explain general-free
|
|
85
|
+
npx job-forge capabilities:check general-free --tool browser --mcp geometra --filesystem write
|
|
86
|
+
|
|
87
|
+
Context bundles (terminal, outside opencode):
|
|
88
|
+
npx job-forge context:plan apply
|
|
89
|
+
npx job-forge context:check apply --budget 23000
|
|
82
90
|
```
|
|
83
91
|
|
|
84
92
|
---
|
package/iso/instructions.md
CHANGED
|
@@ -60,12 +60,18 @@ AI-powered job search pipeline: scans portals, evaluates offers, generates CVs v
|
|
|
60
60
|
- [D9] Treat `templates/contracts.json` as the source of truth for machine-readable artifacts. Prefer `npx job-forge tracker-line ... --write` for tracker additions; if emitting TSV manually, inspect `npx iso-contract explain jobforge.tracker-row --contracts templates/contracts.json` first. `merge` and `verify` enforce the tracker-row contract locally.
|
|
61
61
|
why: deterministic code owns the exact tracker TSV/table shape; repeated prose gets re-tokenized and agents occasionally misremember it
|
|
62
62
|
|
|
63
|
+
- [D10] Treat `templates/capabilities.json` as the source of truth for role capability boundaries. Use `npx job-forge capabilities:explain <role>` or `npx job-forge capabilities:check <role> ...` when changing or validating subagent tool/MCP/filesystem/command permissions; do not paste the full capability matrix into task prompts.
|
|
64
|
+
why: executable local policy prevents role-permission drift without adding MCP/tool-schema tokens or loading a capability matrix into the shared prefix
|
|
65
|
+
|
|
66
|
+
- [D11] Treat `templates/context.json` as the source of truth for mode/reference context bundles. Use `npx job-forge context:plan <mode>` or `npx job-forge context:check <mode>` when changing or validating what a mode loads; do not paste the full context matrix into prompts.
|
|
67
|
+
why: deterministic context bundles prevent reference-file drift and accidental token bloat without adding MCP/tool-schema tokens
|
|
68
|
+
|
|
63
69
|
## Procedure
|
|
64
70
|
|
|
65
71
|
1. Check `cv.md`, `profile.yml`, and `portals.yml`; onboard if any file is missing.
|
|
66
72
|
2. Pick and name the mode from **Routing** [D6]. No match → ask; do not guess.
|
|
67
|
-
3. Read the active mode file [D3]; decide inline vs delegated work [D1].
|
|
68
|
-
4. Prepare Geometra dispatches: cleanup [H3], ledger prefilter when present [D8], dedupe [H2], location filter [D5], routing [D2], proxy prompt hygiene [H8].
|
|
73
|
+
3. Read the active mode file [D3]; use context bundle checks when changing context loads [D11]; decide inline vs delegated work [D1].
|
|
74
|
+
4. Prepare Geometra dispatches: cleanup [H3], ledger prefilter when present [D8], dedupe [H2], location filter [D5], routing [D2, D10], proxy prompt hygiene [H8].
|
|
69
75
|
5. Dispatch at most 2 tasks per round [H1]; wait for final outcomes, not just task ids [H5b].
|
|
70
76
|
6. Keep multi-job form-filling out of the orchestrator [H4].
|
|
71
77
|
7. Cross-check subagent facts against authoritative files [H7].
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { dirname, join, resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import {
|
|
5
|
+
checkRoleCapability,
|
|
6
|
+
formatCheckResult,
|
|
7
|
+
formatResolvedRole,
|
|
8
|
+
loadCapabilityPolicy,
|
|
9
|
+
renderRole,
|
|
10
|
+
resolveRole,
|
|
11
|
+
roleNames,
|
|
12
|
+
} from '@razroo/iso-capabilities';
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const PKG_ROOT = resolve(__dirname, '..');
|
|
16
|
+
export const CAPABILITIES_RELATIVE_PATH = 'templates/capabilities.json';
|
|
17
|
+
|
|
18
|
+
export function resolveProjectDir(projectDir = process.env.JOB_FORGE_PROJECT || process.cwd()) {
|
|
19
|
+
return projectDir;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function jobForgeCapabilitiesPath(projectDir = resolveProjectDir()) {
|
|
23
|
+
const projectPath = join(projectDir, CAPABILITIES_RELATIVE_PATH);
|
|
24
|
+
if (existsSync(projectPath)) return projectPath;
|
|
25
|
+
return join(PKG_ROOT, CAPABILITIES_RELATIVE_PATH);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function loadJobForgeCapabilityPolicy(projectDir = resolveProjectDir()) {
|
|
29
|
+
const path = jobForgeCapabilitiesPath(projectDir);
|
|
30
|
+
return loadCapabilityPolicy(JSON.parse(readFileSync(path, 'utf-8')));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function listJobForgeCapabilityRoles(projectDir = resolveProjectDir()) {
|
|
34
|
+
return roleNames(loadJobForgeCapabilityPolicy(projectDir));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveJobForgeCapabilityRole(name, projectDir = resolveProjectDir()) {
|
|
38
|
+
return resolveRole(loadJobForgeCapabilityPolicy(projectDir), name);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function checkJobForgeCapability(name, request, projectDir = resolveProjectDir()) {
|
|
42
|
+
return checkRoleCapability(loadJobForgeCapabilityPolicy(projectDir), name, request);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function formatJobForgeCapabilityCheck(result) {
|
|
46
|
+
return formatCheckResult(result);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function formatJobForgeCapabilityRole(role) {
|
|
50
|
+
return formatResolvedRole(role);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function renderJobForgeCapabilityRole(role, target = 'markdown') {
|
|
54
|
+
return renderRole(role, target);
|
|
55
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { dirname, join, resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import {
|
|
5
|
+
bundleNames,
|
|
6
|
+
formatContextPlan,
|
|
7
|
+
formatResolvedContextBundle,
|
|
8
|
+
loadContextPolicy,
|
|
9
|
+
planContext,
|
|
10
|
+
renderContextPlan,
|
|
11
|
+
resolveContextBundle,
|
|
12
|
+
} from '@razroo/iso-context';
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const PKG_ROOT = resolve(__dirname, '..');
|
|
16
|
+
export const CONTEXT_RELATIVE_PATH = 'templates/context.json';
|
|
17
|
+
|
|
18
|
+
export function resolveProjectDir(projectDir = process.env.JOB_FORGE_PROJECT || process.cwd()) {
|
|
19
|
+
return projectDir;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function jobForgeContextPath(projectDir = resolveProjectDir()) {
|
|
23
|
+
const projectPath = join(projectDir, CONTEXT_RELATIVE_PATH);
|
|
24
|
+
if (existsSync(projectPath)) return projectPath;
|
|
25
|
+
return join(PKG_ROOT, CONTEXT_RELATIVE_PATH);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function loadJobForgeContextPolicy(projectDir = resolveProjectDir()) {
|
|
29
|
+
const path = jobForgeContextPath(projectDir);
|
|
30
|
+
return loadContextPolicy(JSON.parse(readFileSync(path, 'utf-8')));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function listJobForgeContextBundles(projectDir = resolveProjectDir()) {
|
|
34
|
+
return bundleNames(loadJobForgeContextPolicy(projectDir));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveJobForgeContextBundle(name, projectDir = resolveProjectDir()) {
|
|
38
|
+
return resolveContextBundle(loadJobForgeContextPolicy(projectDir), name);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function planJobForgeContextBundle(name, options = {}, projectDir = resolveProjectDir()) {
|
|
42
|
+
return planContext(loadJobForgeContextPolicy(projectDir), name, options);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function formatJobForgeContextPlan(plan) {
|
|
46
|
+
return formatContextPlan(plan);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function formatJobForgeContextBundle(bundle) {
|
|
50
|
+
return formatResolvedContextBundle(bundle);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function renderJobForgeContextPlan(plan, target = 'markdown') {
|
|
54
|
+
return renderContextPlan(plan, target);
|
|
55
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "job-forge",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.20",
|
|
4
4
|
"description": "AI-powered job search pipeline built on opencode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,6 +32,15 @@
|
|
|
32
32
|
"ledger:verify": "node bin/job-forge.mjs ledger:verify",
|
|
33
33
|
"ledger:has": "node bin/job-forge.mjs ledger:has",
|
|
34
34
|
"ledger:query": "node bin/job-forge.mjs ledger:query",
|
|
35
|
+
"capabilities:list": "node bin/job-forge.mjs capabilities:list",
|
|
36
|
+
"capabilities:explain": "node bin/job-forge.mjs capabilities:explain",
|
|
37
|
+
"capabilities:check": "node bin/job-forge.mjs capabilities:check",
|
|
38
|
+
"capabilities:render": "node bin/job-forge.mjs capabilities:render",
|
|
39
|
+
"context:list": "node bin/job-forge.mjs context:list",
|
|
40
|
+
"context:explain": "node bin/job-forge.mjs context:explain",
|
|
41
|
+
"context:plan": "node bin/job-forge.mjs context:plan",
|
|
42
|
+
"context:check": "node bin/job-forge.mjs context:check",
|
|
43
|
+
"context:render": "node bin/job-forge.mjs context:render",
|
|
35
44
|
"plan": "iso plan .",
|
|
36
45
|
"lint:agentmd": "agentmd lint iso/instructions.md",
|
|
37
46
|
"lint:modes": "isolint lint modes/",
|
|
@@ -96,6 +105,8 @@
|
|
|
96
105
|
"node": ">=20.6.0"
|
|
97
106
|
},
|
|
98
107
|
"dependencies": {
|
|
108
|
+
"@razroo/iso-capabilities": "^0.1.0",
|
|
109
|
+
"@razroo/iso-context": "^0.1.0",
|
|
99
110
|
"@razroo/iso-contract": "^0.1.0",
|
|
100
111
|
"@razroo/iso-guard": "^0.1.0",
|
|
101
112
|
"@razroo/iso-ledger": "^0.1.0",
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { PROJECT_DIR } from '../tracker-lib.mjs';
|
|
4
|
+
import {
|
|
5
|
+
checkJobForgeCapability,
|
|
6
|
+
formatJobForgeCapabilityCheck,
|
|
7
|
+
formatJobForgeCapabilityRole,
|
|
8
|
+
jobForgeCapabilitiesPath,
|
|
9
|
+
listJobForgeCapabilityRoles,
|
|
10
|
+
renderJobForgeCapabilityRole,
|
|
11
|
+
resolveJobForgeCapabilityRole,
|
|
12
|
+
} from '../lib/jobforge-capabilities.mjs';
|
|
13
|
+
|
|
14
|
+
const USAGE = `job-forge capabilities - deterministic role capability policy
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
job-forge capabilities:list [--json]
|
|
18
|
+
job-forge capabilities:explain <role> [--json]
|
|
19
|
+
job-forge capabilities:check <role> [--tool <name>] [--mcp <name>] [--command <cmd>] [--filesystem read|write] [--network off|restricted|on] [--json]
|
|
20
|
+
job-forge capabilities:render <role> [--target markdown|claude|codex|cursor|opencode|json] [--json]
|
|
21
|
+
job-forge capabilities:path
|
|
22
|
+
|
|
23
|
+
The policy is templates/capabilities.json. It is local project state, not an
|
|
24
|
+
MCP and not prompt context.`;
|
|
25
|
+
|
|
26
|
+
const [cmd = 'help', ...rawArgs] = process.argv.slice(2);
|
|
27
|
+
const { positional, opts } = parseArgs(rawArgs);
|
|
28
|
+
|
|
29
|
+
if (opts.help || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
30
|
+
console.log(USAGE);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
if (cmd === 'path') {
|
|
36
|
+
console.log(jobForgeCapabilitiesPath(PROJECT_DIR));
|
|
37
|
+
} else if (cmd === 'list') {
|
|
38
|
+
list(opts);
|
|
39
|
+
} else if (cmd === 'explain') {
|
|
40
|
+
explain(positional, opts);
|
|
41
|
+
} else if (cmd === 'check') {
|
|
42
|
+
check(positional, opts);
|
|
43
|
+
} else if (cmd === 'render') {
|
|
44
|
+
render(positional, opts);
|
|
45
|
+
} else {
|
|
46
|
+
console.error(`unknown capabilities command "${cmd}"\n`);
|
|
47
|
+
console.error(USAGE);
|
|
48
|
+
process.exit(2);
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function parseArgs(args) {
|
|
56
|
+
const positional = [];
|
|
57
|
+
const opts = {
|
|
58
|
+
json: false,
|
|
59
|
+
help: false,
|
|
60
|
+
target: 'markdown',
|
|
61
|
+
tools: [],
|
|
62
|
+
mcp: [],
|
|
63
|
+
commands: [],
|
|
64
|
+
filesystem: [],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < args.length; i++) {
|
|
68
|
+
const arg = args[i];
|
|
69
|
+
if (arg === '--json') {
|
|
70
|
+
opts.json = true;
|
|
71
|
+
} else if (arg === '--tool') {
|
|
72
|
+
opts.tools.push(valueAfter(args, ++i, '--tool'));
|
|
73
|
+
} else if (arg.startsWith('--tool=')) {
|
|
74
|
+
opts.tools.push(arg.slice('--tool='.length));
|
|
75
|
+
} else if (arg === '--mcp') {
|
|
76
|
+
opts.mcp.push(valueAfter(args, ++i, '--mcp'));
|
|
77
|
+
} else if (arg.startsWith('--mcp=')) {
|
|
78
|
+
opts.mcp.push(arg.slice('--mcp='.length));
|
|
79
|
+
} else if (arg === '--command') {
|
|
80
|
+
opts.commands.push(valueAfter(args, ++i, '--command'));
|
|
81
|
+
} else if (arg.startsWith('--command=')) {
|
|
82
|
+
opts.commands.push(arg.slice('--command='.length));
|
|
83
|
+
} else if (arg === '--filesystem') {
|
|
84
|
+
opts.filesystem.push(parseFilesystem(valueAfter(args, ++i, '--filesystem')));
|
|
85
|
+
} else if (arg.startsWith('--filesystem=')) {
|
|
86
|
+
opts.filesystem.push(parseFilesystem(arg.slice('--filesystem='.length)));
|
|
87
|
+
} else if (arg === '--network') {
|
|
88
|
+
opts.network = parseNetwork(valueAfter(args, ++i, '--network'));
|
|
89
|
+
} else if (arg.startsWith('--network=')) {
|
|
90
|
+
opts.network = parseNetwork(arg.slice('--network='.length));
|
|
91
|
+
} else if (arg === '--target') {
|
|
92
|
+
opts.target = parseTarget(valueAfter(args, ++i, '--target'));
|
|
93
|
+
} else if (arg.startsWith('--target=')) {
|
|
94
|
+
opts.target = parseTarget(arg.slice('--target='.length));
|
|
95
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
96
|
+
opts.help = true;
|
|
97
|
+
} else if (arg.startsWith('--')) {
|
|
98
|
+
throw new Error(`unknown flag "${arg}"`);
|
|
99
|
+
} else {
|
|
100
|
+
positional.push(arg);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { positional, opts };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function valueAfter(values, index, flag) {
|
|
108
|
+
const value = values[index];
|
|
109
|
+
if (!value || value.startsWith('--')) throw new Error(`${flag} requires a value`);
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function list(opts) {
|
|
114
|
+
const names = listJobForgeCapabilityRoles(PROJECT_DIR);
|
|
115
|
+
if (opts.json) {
|
|
116
|
+
console.log(JSON.stringify(names, null, 2));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
console.log(names.join('\n'));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function explain(positional, opts) {
|
|
123
|
+
const role = readRole(positional);
|
|
124
|
+
if (opts.json) {
|
|
125
|
+
console.log(JSON.stringify(role, null, 2));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
console.log(formatJobForgeCapabilityRole(role));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function check(positional, opts) {
|
|
132
|
+
const roleName = positional[0];
|
|
133
|
+
if (!roleName) throw new Error('missing role name');
|
|
134
|
+
const request = requestFromOptions(opts);
|
|
135
|
+
if (!hasRequest(request)) {
|
|
136
|
+
throw new Error('capabilities:check requires at least one --tool, --mcp, --command, --filesystem, or --network');
|
|
137
|
+
}
|
|
138
|
+
const result = checkJobForgeCapability(roleName, request, PROJECT_DIR);
|
|
139
|
+
if (opts.json) {
|
|
140
|
+
console.log(JSON.stringify(result, null, 2));
|
|
141
|
+
} else {
|
|
142
|
+
console.log(formatJobForgeCapabilityCheck(result));
|
|
143
|
+
}
|
|
144
|
+
process.exit(result.ok ? 0 : 1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function render(positional, opts) {
|
|
148
|
+
const role = readRole(positional);
|
|
149
|
+
const text = renderJobForgeCapabilityRole(role, opts.target);
|
|
150
|
+
if (opts.json) {
|
|
151
|
+
console.log(JSON.stringify({ target: opts.target, role, text }, null, 2));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
console.log(text);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function readRole(positional) {
|
|
158
|
+
const roleName = positional[0];
|
|
159
|
+
if (!roleName) throw new Error('missing role name');
|
|
160
|
+
return resolveJobForgeCapabilityRole(roleName, PROJECT_DIR);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function requestFromOptions(opts) {
|
|
164
|
+
return {
|
|
165
|
+
tools: opts.tools.length ? opts.tools : undefined,
|
|
166
|
+
mcp: opts.mcp.length ? opts.mcp : undefined,
|
|
167
|
+
commands: opts.commands.length ? opts.commands : undefined,
|
|
168
|
+
filesystem: opts.filesystem.length ? opts.filesystem : undefined,
|
|
169
|
+
network: opts.network,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function hasRequest(request) {
|
|
174
|
+
return Boolean(
|
|
175
|
+
request.tools?.length ||
|
|
176
|
+
request.mcp?.length ||
|
|
177
|
+
request.commands?.length ||
|
|
178
|
+
request.filesystem?.length ||
|
|
179
|
+
request.network,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function parseFilesystem(value) {
|
|
184
|
+
if (value === 'read' || value === 'write') return value;
|
|
185
|
+
throw new Error('--filesystem must be read or write');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function parseNetwork(value) {
|
|
189
|
+
if (value === 'off' || value === 'restricted' || value === 'on') return value;
|
|
190
|
+
throw new Error('--network must be off, restricted, or on');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function parseTarget(value) {
|
|
194
|
+
if (
|
|
195
|
+
value === 'markdown' ||
|
|
196
|
+
value === 'claude' ||
|
|
197
|
+
value === 'codex' ||
|
|
198
|
+
value === 'cursor' ||
|
|
199
|
+
value === 'opencode' ||
|
|
200
|
+
value === 'json'
|
|
201
|
+
) {
|
|
202
|
+
return value;
|
|
203
|
+
}
|
|
204
|
+
throw new Error('--target must be markdown, claude, codex, cursor, opencode, or json');
|
|
205
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { PROJECT_DIR } from '../tracker-lib.mjs';
|
|
4
|
+
import {
|
|
5
|
+
formatJobForgeContextBundle,
|
|
6
|
+
formatJobForgeContextPlan,
|
|
7
|
+
jobForgeContextPath,
|
|
8
|
+
listJobForgeContextBundles,
|
|
9
|
+
planJobForgeContextBundle,
|
|
10
|
+
renderJobForgeContextPlan,
|
|
11
|
+
resolveJobForgeContextBundle,
|
|
12
|
+
} from '../lib/jobforge-context.mjs';
|
|
13
|
+
|
|
14
|
+
const USAGE = `job-forge context - deterministic mode/reference context policy
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
job-forge context:list [--json]
|
|
18
|
+
job-forge context:explain <bundle> [--json]
|
|
19
|
+
job-forge context:plan <bundle> [--root <dir>] [--budget N] [--chars-per-token N] [--json]
|
|
20
|
+
job-forge context:check <bundle> [--root <dir>] [--budget N] [--chars-per-token N] [--json]
|
|
21
|
+
job-forge context:render <bundle> [--root <dir>] [--target markdown|json] [--json]
|
|
22
|
+
job-forge context:path
|
|
23
|
+
|
|
24
|
+
The policy is templates/context.json. It is local project policy, not an MCP
|
|
25
|
+
and not always-loaded prompt context.`;
|
|
26
|
+
|
|
27
|
+
const [cmd = 'help', ...rawArgs] = process.argv.slice(2);
|
|
28
|
+
const { positional, opts } = parseArgs(rawArgs);
|
|
29
|
+
|
|
30
|
+
if (opts.help || cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
31
|
+
console.log(USAGE);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
if (cmd === 'path') {
|
|
37
|
+
console.log(jobForgeContextPath(PROJECT_DIR));
|
|
38
|
+
} else if (cmd === 'list') {
|
|
39
|
+
list(opts);
|
|
40
|
+
} else if (cmd === 'explain') {
|
|
41
|
+
explain(positional, opts);
|
|
42
|
+
} else if (cmd === 'plan') {
|
|
43
|
+
plan(positional, opts, false);
|
|
44
|
+
} else if (cmd === 'check') {
|
|
45
|
+
check(positional, opts);
|
|
46
|
+
} else if (cmd === 'render') {
|
|
47
|
+
render(positional, opts);
|
|
48
|
+
} else {
|
|
49
|
+
console.error(`unknown context command "${cmd}"\n`);
|
|
50
|
+
console.error(USAGE);
|
|
51
|
+
process.exit(2);
|
|
52
|
+
}
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function parseArgs(args) {
|
|
59
|
+
const positional = [];
|
|
60
|
+
const opts = {
|
|
61
|
+
json: false,
|
|
62
|
+
help: false,
|
|
63
|
+
target: 'markdown',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < args.length; i++) {
|
|
67
|
+
const arg = args[i];
|
|
68
|
+
if (arg === '--json') {
|
|
69
|
+
opts.json = true;
|
|
70
|
+
} else if (arg === '--root') {
|
|
71
|
+
opts.root = valueAfter(args, ++i, '--root');
|
|
72
|
+
} else if (arg.startsWith('--root=')) {
|
|
73
|
+
opts.root = arg.slice('--root='.length);
|
|
74
|
+
} else if (arg === '--budget' || arg === '--token-budget') {
|
|
75
|
+
opts.tokenBudget = parsePositiveInteger(valueAfter(args, ++i, arg), arg);
|
|
76
|
+
} else if (arg.startsWith('--budget=')) {
|
|
77
|
+
opts.tokenBudget = parsePositiveInteger(arg.slice('--budget='.length), '--budget');
|
|
78
|
+
} else if (arg.startsWith('--token-budget=')) {
|
|
79
|
+
opts.tokenBudget = parsePositiveInteger(arg.slice('--token-budget='.length), '--token-budget');
|
|
80
|
+
} else if (arg === '--chars-per-token') {
|
|
81
|
+
opts.charsPerToken = parsePositiveInteger(valueAfter(args, ++i, '--chars-per-token'), '--chars-per-token');
|
|
82
|
+
} else if (arg.startsWith('--chars-per-token=')) {
|
|
83
|
+
opts.charsPerToken = parsePositiveInteger(arg.slice('--chars-per-token='.length), '--chars-per-token');
|
|
84
|
+
} else if (arg === '--target') {
|
|
85
|
+
opts.target = parseTarget(valueAfter(args, ++i, '--target'));
|
|
86
|
+
} else if (arg.startsWith('--target=')) {
|
|
87
|
+
opts.target = parseTarget(arg.slice('--target='.length));
|
|
88
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
89
|
+
opts.help = true;
|
|
90
|
+
} else if (arg.startsWith('--')) {
|
|
91
|
+
throw new Error(`unknown flag "${arg}"`);
|
|
92
|
+
} else {
|
|
93
|
+
positional.push(arg);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { positional, opts };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function valueAfter(values, index, flag) {
|
|
101
|
+
const value = values[index];
|
|
102
|
+
if (!value || value.startsWith('--')) throw new Error(`${flag} requires a value`);
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function list(opts) {
|
|
107
|
+
const names = listJobForgeContextBundles(PROJECT_DIR);
|
|
108
|
+
if (opts.json) {
|
|
109
|
+
console.log(JSON.stringify(names, null, 2));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
console.log(names.join('\n'));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function explain(positional, opts) {
|
|
116
|
+
const bundle = readBundle(positional);
|
|
117
|
+
if (opts.json) {
|
|
118
|
+
console.log(JSON.stringify(bundle, null, 2));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
console.log(formatJobForgeContextBundle(bundle));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function plan(positional, opts, includeContent) {
|
|
125
|
+
const bundleName = readBundleName(positional);
|
|
126
|
+
const result = planJobForgeContextBundle(bundleName, planOptions(opts, includeContent), PROJECT_DIR);
|
|
127
|
+
if (opts.json) {
|
|
128
|
+
console.log(JSON.stringify(result, null, 2));
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
console.log(formatJobForgeContextPlan(result));
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function check(positional, opts) {
|
|
136
|
+
const result = plan(positional, opts, false);
|
|
137
|
+
process.exit(result.ok ? 0 : 1);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function render(positional, opts) {
|
|
141
|
+
const bundleName = readBundleName(positional);
|
|
142
|
+
const result = planJobForgeContextBundle(bundleName, planOptions(opts, true), PROJECT_DIR);
|
|
143
|
+
const text = renderJobForgeContextPlan(result, opts.target);
|
|
144
|
+
if (opts.json) {
|
|
145
|
+
console.log(JSON.stringify({ target: opts.target, plan: result, text }, null, 2));
|
|
146
|
+
} else {
|
|
147
|
+
console.log(text);
|
|
148
|
+
}
|
|
149
|
+
process.exit(result.ok ? 0 : 1);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function readBundle(positional) {
|
|
153
|
+
return resolveJobForgeContextBundle(readBundleName(positional), PROJECT_DIR);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function readBundleName(positional) {
|
|
157
|
+
const bundleName = positional[0];
|
|
158
|
+
if (!bundleName) throw new Error('missing bundle name');
|
|
159
|
+
return bundleName;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function planOptions(opts, includeContent) {
|
|
163
|
+
return {
|
|
164
|
+
root: opts.root || PROJECT_DIR,
|
|
165
|
+
includeContent,
|
|
166
|
+
tokenBudget: opts.tokenBudget,
|
|
167
|
+
charsPerToken: opts.charsPerToken,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function parseTarget(value) {
|
|
172
|
+
if (value === 'markdown' || value === 'json') return value;
|
|
173
|
+
throw new Error('--target must be markdown or json');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function parsePositiveInteger(value, flag) {
|
|
177
|
+
const number = Number(value);
|
|
178
|
+
if (!Number.isInteger(number) || number <= 0) throw new Error(`${flag} must be a positive integer`);
|
|
179
|
+
return number;
|
|
180
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
{
|
|
2
|
+
"roles": [
|
|
3
|
+
{
|
|
4
|
+
"name": "jobforge-base",
|
|
5
|
+
"description": "Safe local JobForge role for reading project state and running deterministic verification commands.",
|
|
6
|
+
"tools": ["read", "search", "shell"],
|
|
7
|
+
"mcp": [],
|
|
8
|
+
"commands": {
|
|
9
|
+
"allow": [
|
|
10
|
+
"npx job-forge verify",
|
|
11
|
+
"npx job-forge ledger:*",
|
|
12
|
+
"npx job-forge capabilities:*",
|
|
13
|
+
"npx job-forge context:*",
|
|
14
|
+
"rg *"
|
|
15
|
+
],
|
|
16
|
+
"deny": [
|
|
17
|
+
"rm -rf *"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"filesystem": "read-only",
|
|
21
|
+
"network": "off",
|
|
22
|
+
"notes": [
|
|
23
|
+
"Do not paste secrets from config/profile.yml into prompts, traces, or summaries.",
|
|
24
|
+
"Prefer deterministic JobForge commands over re-reading growing tracker files when a local command can answer the question."
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "orchestrator",
|
|
29
|
+
"description": "Primary session role that routes work, checks policy, and delegates tool-heavy browser/application work.",
|
|
30
|
+
"extends": "jobforge-base",
|
|
31
|
+
"tools": ["task"],
|
|
32
|
+
"commands": {
|
|
33
|
+
"allow": [
|
|
34
|
+
"npx job-forge merge",
|
|
35
|
+
"npx job-forge guard:*",
|
|
36
|
+
"npx job-forge telemetry:*",
|
|
37
|
+
"npx job-forge trace:*"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"filesystem": "read-only",
|
|
41
|
+
"network": "off",
|
|
42
|
+
"notes": [
|
|
43
|
+
"Delegate multi-job Geometra work to subagents; do not run browser-heavy application loops inline.",
|
|
44
|
+
"Use capabilities checks when changing or auditing role boundaries."
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "general-free",
|
|
49
|
+
"description": "Procedural worker for application form fills, TSV writes, merges, verification, OTP retrieval, and portal metadata extraction.",
|
|
50
|
+
"extends": "jobforge-base",
|
|
51
|
+
"tools": ["browser", "write"],
|
|
52
|
+
"mcp": ["geometra", "gmail"],
|
|
53
|
+
"commands": {
|
|
54
|
+
"allow": [
|
|
55
|
+
"npx job-forge tracker-line *",
|
|
56
|
+
"npx job-forge merge",
|
|
57
|
+
"npx job-forge verify",
|
|
58
|
+
"npx job-forge ledger:*",
|
|
59
|
+
"npx job-forge capabilities:*"
|
|
60
|
+
],
|
|
61
|
+
"deny": [
|
|
62
|
+
"task *"
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"filesystem": "project-write",
|
|
66
|
+
"network": "restricted",
|
|
67
|
+
"notes": [
|
|
68
|
+
"Use for procedural, tool-heavy work where output shape is validated by JobForge commands.",
|
|
69
|
+
"Do not spawn nested tasks."
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "general-paid",
|
|
74
|
+
"description": "Quality-sensitive writing and evaluation worker for reports, cover letters, interview stories, and outreach.",
|
|
75
|
+
"extends": "jobforge-base",
|
|
76
|
+
"tools": ["write"],
|
|
77
|
+
"commands": {
|
|
78
|
+
"allow": [
|
|
79
|
+
"npx job-forge render-report-header *",
|
|
80
|
+
"npx job-forge pdf",
|
|
81
|
+
"npx job-forge tracker-line *",
|
|
82
|
+
"npx job-forge verify"
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"filesystem": "project-write",
|
|
86
|
+
"network": "restricted",
|
|
87
|
+
"notes": [
|
|
88
|
+
"Use for narrative quality, personalization, and scoring explanations rather than browser automation."
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "glm-minimal",
|
|
93
|
+
"description": "Narrow one-shot extractor/classifier role for small structured transforms.",
|
|
94
|
+
"extends": "jobforge-base",
|
|
95
|
+
"tools": [],
|
|
96
|
+
"commands": {
|
|
97
|
+
"allow": [
|
|
98
|
+
"npx job-forge slugify *",
|
|
99
|
+
"npx job-forge today",
|
|
100
|
+
"npx job-forge next-num"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"filesystem": "read-only",
|
|
104
|
+
"network": "off",
|
|
105
|
+
"notes": [
|
|
106
|
+
"Keep input small and output structured; no browser, MCP, or nested task work."
|
|
107
|
+
]
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
{
|
|
2
|
+
"defaults": {
|
|
3
|
+
"tokenBudget": 12000,
|
|
4
|
+
"charsPerToken": 4
|
|
5
|
+
},
|
|
6
|
+
"bundles": [
|
|
7
|
+
{
|
|
8
|
+
"name": "base",
|
|
9
|
+
"description": "Shared JobForge orchestration contract.",
|
|
10
|
+
"tokenBudget": 4000,
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "iso/instructions.md",
|
|
14
|
+
"maxTokens": 3500,
|
|
15
|
+
"notes": [
|
|
16
|
+
"Shared contract; mode/reference files should be selected through narrower bundles."
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "shared",
|
|
23
|
+
"extends": "base",
|
|
24
|
+
"description": "Shared scoring model plus JobForge orchestration contract.",
|
|
25
|
+
"tokenBudget": 8000,
|
|
26
|
+
"files": [
|
|
27
|
+
{
|
|
28
|
+
"path": "modes/_shared.md",
|
|
29
|
+
"maxTokens": 4500,
|
|
30
|
+
"notes": [
|
|
31
|
+
"Loaded for evaluation-style modes that need archetypes, scoring, and profile references."
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "auto-pipeline",
|
|
38
|
+
"extends": "shared",
|
|
39
|
+
"description": "Default JD/URL pipeline context.",
|
|
40
|
+
"tokenBudget": 12000,
|
|
41
|
+
"files": [
|
|
42
|
+
{
|
|
43
|
+
"path": "modes/auto-pipeline.md",
|
|
44
|
+
"maxTokens": 2000
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"path": "modes/reference-setup.md",
|
|
48
|
+
"maxTokens": 2600,
|
|
49
|
+
"required": false,
|
|
50
|
+
"notes": [
|
|
51
|
+
"Load when onboarding, tracker layout, or setup rules are the blocker."
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "offer",
|
|
58
|
+
"extends": "shared",
|
|
59
|
+
"description": "Single-offer evaluation context.",
|
|
60
|
+
"tokenBudget": 11000,
|
|
61
|
+
"files": [
|
|
62
|
+
{
|
|
63
|
+
"path": "modes/offer.md",
|
|
64
|
+
"maxTokens": 2400
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"path": "modes/_shared-calibration.md",
|
|
68
|
+
"maxTokens": 2500,
|
|
69
|
+
"required": false,
|
|
70
|
+
"notes": [
|
|
71
|
+
"Load when score calibration or anchor examples are needed."
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "compare",
|
|
78
|
+
"extends": "shared",
|
|
79
|
+
"description": "Offer comparison context.",
|
|
80
|
+
"tokenBudget": 8500,
|
|
81
|
+
"files": [
|
|
82
|
+
{
|
|
83
|
+
"path": "modes/compare.md",
|
|
84
|
+
"maxTokens": 600
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "contact",
|
|
90
|
+
"extends": "shared",
|
|
91
|
+
"description": "LinkedIn outreach context.",
|
|
92
|
+
"tokenBudget": 9000,
|
|
93
|
+
"files": [
|
|
94
|
+
{
|
|
95
|
+
"path": "modes/contact.md",
|
|
96
|
+
"maxTokens": 1200
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"name": "pdf",
|
|
102
|
+
"extends": "shared",
|
|
103
|
+
"description": "CV/PDF generation context.",
|
|
104
|
+
"tokenBudget": 10000,
|
|
105
|
+
"files": [
|
|
106
|
+
{
|
|
107
|
+
"path": "modes/pdf.md",
|
|
108
|
+
"maxTokens": 2200
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "apply",
|
|
114
|
+
"extends": "shared",
|
|
115
|
+
"description": "Application form-fill context with Geometra recovery rules.",
|
|
116
|
+
"tokenBudget": 23000,
|
|
117
|
+
"files": [
|
|
118
|
+
{
|
|
119
|
+
"path": "modes/apply.md",
|
|
120
|
+
"maxTokens": 7800
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"path": "modes/reference-geometra.md",
|
|
124
|
+
"maxTokens": 5600
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"path": "modes/reference-portals.md",
|
|
128
|
+
"maxTokens": 2500,
|
|
129
|
+
"required": false,
|
|
130
|
+
"notes": [
|
|
131
|
+
"Load when OTP, proxy, or portal configuration is the blocker."
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"name": "pipeline",
|
|
138
|
+
"extends": "shared",
|
|
139
|
+
"description": "Pending URL inbox context.",
|
|
140
|
+
"tokenBudget": 12000,
|
|
141
|
+
"files": [
|
|
142
|
+
{
|
|
143
|
+
"path": "modes/pipeline.md",
|
|
144
|
+
"maxTokens": 1500
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"path": "modes/reference-setup.md",
|
|
148
|
+
"maxTokens": 2600,
|
|
149
|
+
"required": false
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"name": "scan",
|
|
155
|
+
"extends": "shared",
|
|
156
|
+
"description": "Portal scanning context.",
|
|
157
|
+
"tokenBudget": 18000,
|
|
158
|
+
"files": [
|
|
159
|
+
{
|
|
160
|
+
"path": "modes/scan.md",
|
|
161
|
+
"maxTokens": 6500
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"path": "modes/reference-portals.md",
|
|
165
|
+
"maxTokens": 2500
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"name": "batch",
|
|
171
|
+
"extends": "shared",
|
|
172
|
+
"description": "Batch processing and durable runner context.",
|
|
173
|
+
"tokenBudget": 16000,
|
|
174
|
+
"files": [
|
|
175
|
+
{
|
|
176
|
+
"path": "modes/batch.md",
|
|
177
|
+
"maxTokens": 1500
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"path": "batch/batch-prompt.md",
|
|
181
|
+
"maxTokens": 4800,
|
|
182
|
+
"required": false,
|
|
183
|
+
"notes": [
|
|
184
|
+
"Needed when invoking or auditing the standalone batch runner prompt."
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"path": "modes/reference-setup.md",
|
|
189
|
+
"maxTokens": 2600,
|
|
190
|
+
"required": false
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"name": "tracker",
|
|
196
|
+
"extends": "base",
|
|
197
|
+
"description": "Tracker/status context with setup reference.",
|
|
198
|
+
"tokenBudget": 7000,
|
|
199
|
+
"files": [
|
|
200
|
+
{
|
|
201
|
+
"path": "modes/tracker.md",
|
|
202
|
+
"maxTokens": 500
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"path": "modes/reference-setup.md",
|
|
206
|
+
"maxTokens": 2600
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"name": "deep",
|
|
212
|
+
"extends": "base",
|
|
213
|
+
"description": "Company research context.",
|
|
214
|
+
"tokenBudget": 5500,
|
|
215
|
+
"files": [
|
|
216
|
+
{
|
|
217
|
+
"path": "modes/deep.md",
|
|
218
|
+
"maxTokens": 1200
|
|
219
|
+
}
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"name": "training",
|
|
224
|
+
"extends": "base",
|
|
225
|
+
"description": "Course/certification evaluation context.",
|
|
226
|
+
"tokenBudget": 4500,
|
|
227
|
+
"files": [
|
|
228
|
+
{
|
|
229
|
+
"path": "modes/training.md",
|
|
230
|
+
"maxTokens": 400
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "project",
|
|
236
|
+
"extends": "base",
|
|
237
|
+
"description": "Portfolio project evaluation context.",
|
|
238
|
+
"tokenBudget": 4500,
|
|
239
|
+
"files": [
|
|
240
|
+
{
|
|
241
|
+
"path": "modes/project.md",
|
|
242
|
+
"maxTokens": 400
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"name": "followup",
|
|
248
|
+
"extends": "base",
|
|
249
|
+
"description": "Follow-up triage context.",
|
|
250
|
+
"tokenBudget": 5500,
|
|
251
|
+
"files": [
|
|
252
|
+
{
|
|
253
|
+
"path": "modes/followup.md",
|
|
254
|
+
"maxTokens": 1000
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"name": "rejection",
|
|
260
|
+
"extends": "base",
|
|
261
|
+
"description": "Rejection logging and pattern review context.",
|
|
262
|
+
"tokenBudget": 5500,
|
|
263
|
+
"files": [
|
|
264
|
+
{
|
|
265
|
+
"path": "modes/rejection.md",
|
|
266
|
+
"maxTokens": 1100
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"name": "negotiation",
|
|
272
|
+
"extends": "base",
|
|
273
|
+
"description": "Offer negotiation context.",
|
|
274
|
+
"tokenBudget": 6500,
|
|
275
|
+
"files": [
|
|
276
|
+
{
|
|
277
|
+
"path": "modes/negotiation.md",
|
|
278
|
+
"maxTokens": 1600
|
|
279
|
+
}
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
}
|