job-forge 2.14.26 → 2.14.28

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.
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync } from 'fs';
4
+ import { isAbsolute, relative, resolve } from 'path';
5
+ import {
6
+ formatConfigSummary,
7
+ formatPreflightPlan,
8
+ loadPreflightConfig,
9
+ parseJson,
10
+ planPreflight,
11
+ } from '@razroo/iso-preflight';
12
+ import { PROJECT_DIR } from '../tracker-lib.mjs';
13
+ import {
14
+ jobForgePreflightConfigPath,
15
+ planJobForgePreflight,
16
+ readJobForgePreflightConfig,
17
+ } from '../lib/jobforge-preflight.mjs';
18
+
19
+ const USAGE = `job-forge preflight - deterministic dispatch planning for JobForge
20
+
21
+ Usage:
22
+ job-forge preflight:plan --candidates <file> [--workflow jobforge.apply] [--json]
23
+ job-forge preflight:check --candidates <file> [--workflow jobforge.apply] [--json]
24
+ job-forge preflight:explain [--json]
25
+ job-forge preflight:path
26
+
27
+ Candidate files are JSON arrays, or objects with a candidates array. The policy
28
+ is templates/preflight.json. This is local project state, not an MCP and not
29
+ prompt context.`;
30
+
31
+ const [cmd = 'help', ...rawArgs] = process.argv.slice(2);
32
+ const opts = parseArgs(rawArgs);
33
+
34
+ if (opts.help || cmd === 'help' || cmd === '--help' || cmd === '-h') {
35
+ console.log(USAGE);
36
+ process.exit(0);
37
+ }
38
+
39
+ try {
40
+ if (cmd === 'path') {
41
+ console.log(configPath(opts));
42
+ } else if (cmd === 'plan' || cmd === 'check') {
43
+ runPlan(cmd, opts);
44
+ } else if (cmd === 'explain') {
45
+ explain(opts);
46
+ } else {
47
+ console.error(`unknown preflight command "${cmd}"\n`);
48
+ console.error(USAGE);
49
+ process.exit(2);
50
+ }
51
+ } catch (error) {
52
+ console.error(error instanceof Error ? error.message : String(error));
53
+ process.exit(1);
54
+ }
55
+
56
+ function parseArgs(args) {
57
+ const opts = {
58
+ json: false,
59
+ help: false,
60
+ };
61
+
62
+ for (let i = 0; i < args.length; i++) {
63
+ const arg = args[i];
64
+ if (arg === '--json') {
65
+ opts.json = true;
66
+ } else if (arg === '--candidates' || arg === '-c') {
67
+ opts.candidates = valueAfter(args, ++i, arg);
68
+ } else if (arg.startsWith('--candidates=')) {
69
+ opts.candidates = arg.slice('--candidates='.length);
70
+ } else if (arg === '--workflow') {
71
+ opts.workflow = valueAfter(args, ++i, '--workflow');
72
+ } else if (arg.startsWith('--workflow=')) {
73
+ opts.workflow = arg.slice('--workflow='.length);
74
+ } else if (arg === '--config') {
75
+ opts.config = valueAfter(args, ++i, '--config');
76
+ } else if (arg.startsWith('--config=')) {
77
+ opts.config = arg.slice('--config='.length);
78
+ } else if (arg === '--help' || arg === '-h') {
79
+ opts.help = true;
80
+ } else {
81
+ throw new Error(`unknown flag "${arg}"`);
82
+ }
83
+ }
84
+
85
+ return opts;
86
+ }
87
+
88
+ function runPlan(mode, opts) {
89
+ if (!opts.candidates) throw new Error(`${mode} requires --candidates <file>`);
90
+ const candidates = readJsonFile(resolveInputPath(opts.candidates));
91
+ const result = opts.config
92
+ ? planPreflight(readConfig(opts), candidates, { workflow: opts.workflow })
93
+ : planJobForgePreflight(candidates, { workflow: opts.workflow }, PROJECT_DIR);
94
+
95
+ if (opts.json) {
96
+ console.log(JSON.stringify(result, null, 2));
97
+ } else {
98
+ console.log(formatPreflightPlan(result, mode));
99
+ }
100
+
101
+ if (mode === 'check' && !result.ok) process.exit(1);
102
+ }
103
+
104
+ function explain(opts) {
105
+ const config = readConfig(opts);
106
+ if (opts.json) {
107
+ console.log(JSON.stringify(config, null, 2));
108
+ return;
109
+ }
110
+ console.log(`config: ${relativePath(configPath(opts))}`);
111
+ console.log(formatConfigSummary(config));
112
+ }
113
+
114
+ function readConfig(opts) {
115
+ if (opts.config) {
116
+ const path = resolveInputPath(opts.config);
117
+ return loadPreflightConfig(readJsonFile(path));
118
+ }
119
+ return readJobForgePreflightConfig(PROJECT_DIR);
120
+ }
121
+
122
+ function configPath(opts) {
123
+ return opts.config ? resolveInputPath(opts.config) : jobForgePreflightConfigPath(PROJECT_DIR);
124
+ }
125
+
126
+ function readJsonFile(path) {
127
+ return parseJson(readFileSync(path, 'utf8'), path);
128
+ }
129
+
130
+ function valueAfter(values, index, flag) {
131
+ const value = values[index];
132
+ if (!value || value.startsWith('--')) throw new Error(`${flag} requires a value`);
133
+ return value;
134
+ }
135
+
136
+ function resolveInputPath(path) {
137
+ return isAbsolute(path) ? path : resolve(PROJECT_DIR, path);
138
+ }
139
+
140
+ function relativePath(path) {
141
+ return relative(PROJECT_DIR, path) || '.';
142
+ }
@@ -0,0 +1,65 @@
1
+ {
2
+ "version": 1,
3
+ "profiles": [
4
+ {
5
+ "name": "jobforge",
6
+ "url": {
7
+ "dropHash": true,
8
+ "stripWww": true,
9
+ "stripQueryParams": [
10
+ "utm_*",
11
+ "gh_src",
12
+ "source",
13
+ "ref",
14
+ "referrer",
15
+ "lever-source",
16
+ "ashby_jid"
17
+ ]
18
+ },
19
+ "company": {
20
+ "aliases": {
21
+ "open ai": "openai",
22
+ "anthropic pbc": "anthropic",
23
+ "google llc": "google",
24
+ "meta platforms": "meta"
25
+ },
26
+ "suffixes": [
27
+ "inc",
28
+ "incorporated",
29
+ "llc",
30
+ "ltd",
31
+ "limited",
32
+ "corp",
33
+ "corporation",
34
+ "company",
35
+ "co",
36
+ "pbc",
37
+ "plc"
38
+ ]
39
+ },
40
+ "role": {
41
+ "aliases": {
42
+ "fdse": "forward deployed software engineer",
43
+ "fullstack": "full stack",
44
+ "ml": "machine learning",
45
+ "sr": "senior",
46
+ "swe": "software engineer"
47
+ },
48
+ "stopWords": [
49
+ "hybrid",
50
+ "new york",
51
+ "onsite",
52
+ "remote",
53
+ "san francisco",
54
+ "united states",
55
+ "usa",
56
+ "us"
57
+ ]
58
+ },
59
+ "match": {
60
+ "strong": 0.92,
61
+ "possible": 0.78
62
+ }
63
+ }
64
+ ]
65
+ }
@@ -13,6 +13,8 @@
13
13
  "npx job-forge context:*",
14
14
  "npx job-forge cache:*",
15
15
  "npx job-forge index:*",
16
+ "npx job-forge canon:*",
17
+ "npx job-forge preflight:*",
16
18
  "rg *"
17
19
  ],
18
20
  "deny": [
@@ -36,7 +38,8 @@
36
38
  "npx job-forge merge",
37
39
  "npx job-forge guard:*",
38
40
  "npx job-forge telemetry:*",
39
- "npx job-forge trace:*"
41
+ "npx job-forge trace:*",
42
+ "npx job-forge preflight:*"
40
43
  ]
41
44
  },
42
45
  "filesystem": "read-only",
@@ -60,7 +63,9 @@
60
63
  "npx job-forge ledger:*",
61
64
  "npx job-forge capabilities:*",
62
65
  "npx job-forge cache:*",
63
- "npx job-forge index:*"
66
+ "npx job-forge index:*",
67
+ "npx job-forge canon:*",
68
+ "npx job-forge preflight:*"
64
69
  ],
65
70
  "deny": [
66
71
  "task *"
@@ -100,6 +105,7 @@
100
105
  "commands": {
101
106
  "allow": [
102
107
  "npx job-forge slugify *",
108
+ "npx job-forge canon:*",
103
109
  "npx job-forge today",
104
110
  "npx job-forge next-num"
105
111
  ]
@@ -33,6 +33,13 @@
33
33
  "index:has": "job-forge index:has",
34
34
  "index:query": "job-forge index:query",
35
35
  "index:explain": "job-forge index:explain",
36
+ "canon:normalize": "job-forge canon:normalize",
37
+ "canon:key": "job-forge canon:key",
38
+ "canon:compare": "job-forge canon:compare",
39
+ "canon:explain": "job-forge canon:explain",
40
+ "preflight:plan": "job-forge preflight:plan",
41
+ "preflight:check": "job-forge preflight:check",
42
+ "preflight:explain": "job-forge preflight:explain",
36
43
  "migrate:plan": "job-forge migrate:plan",
37
44
  "migrate:apply": "job-forge migrate:apply",
38
45
  "migrate:check": "job-forge migrate:check",
@@ -0,0 +1,59 @@
1
+ {
2
+ "version": 1,
3
+ "workflows": [
4
+ {
5
+ "name": "jobforge.apply",
6
+ "description": "Plan safe JobForge application dispatch rounds before Geometra/task work starts.",
7
+ "roundSize": 2,
8
+ "idFact": "id",
9
+ "conflictFact": "companyRoleKey",
10
+ "requiredFacts": [
11
+ "id",
12
+ "company",
13
+ "role",
14
+ "companyRoleKey",
15
+ "url",
16
+ "score"
17
+ ],
18
+ "sourceRequiredFacts": [
19
+ "company",
20
+ "role",
21
+ "companyRoleKey",
22
+ "url",
23
+ "score"
24
+ ],
25
+ "requireGateSources": true,
26
+ "gatePolicy": {
27
+ "skipStatuses": [
28
+ "skip",
29
+ "skipped"
30
+ ],
31
+ "blockStatuses": [
32
+ "block",
33
+ "blocked",
34
+ "fail",
35
+ "failed"
36
+ ]
37
+ },
38
+ "preSteps": [
39
+ {
40
+ "id": "geometra-cleanup",
41
+ "label": "Disconnect stale Geometra sessions before dispatch",
42
+ "command": "geometra_list_sessions && geometra_disconnect({ closeBrowser: true })"
43
+ }
44
+ ],
45
+ "postSteps": [
46
+ {
47
+ "id": "merge",
48
+ "label": "Merge tracker TSV outcomes",
49
+ "command": "npx job-forge merge"
50
+ },
51
+ {
52
+ "id": "verify",
53
+ "label": "Verify tracker integrity",
54
+ "command": "npx job-forge verify"
55
+ }
56
+ ]
57
+ }
58
+ ]
59
+ }