trackly-cli 0.5.2 → 0.6.0
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/README.md +2 -1
- package/bin/trackly +19 -3
- package/docs/trackly-tools.md +2 -1
- package/mcp/server.js +10 -2
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ trackly jobs # List jobs
|
|
|
44
44
|
trackly jobs --remote # Filter remote jobs (sets usStates=REMOTE)
|
|
45
45
|
trackly jobs --region us # Filter by region (us, non_us, all, or a region tag: europe, canada, remote, ...)
|
|
46
46
|
trackly jobs --job-type internship # Filter by employment type (full_time, internship, all)
|
|
47
|
+
trackly jobs --work-arrangement hybrid,remote # Filter by work arrangement independently
|
|
47
48
|
trackly jobs --function product # Filter by function
|
|
48
49
|
trackly jobs --company 243 # Filter by company ID
|
|
49
50
|
trackly job 1234 # Get job details
|
|
@@ -152,7 +153,7 @@ Then use natural language in any of these clients:
|
|
|
152
153
|
|
|
153
154
|
| Tool | Description |
|
|
154
155
|
|------|-------------|
|
|
155
|
-
| trackly_search_jobs | Search and filter jobs by function, company,
|
|
156
|
+
| trackly_search_jobs | Search and filter jobs by function, company, region, employment type, work arrangement, and status |
|
|
156
157
|
| trackly_get_job | Get full details for a specific job |
|
|
157
158
|
| trackly_search_companies | Semantic company search |
|
|
158
159
|
| trackly_list_companies | List all tracked companies |
|
package/bin/trackly
CHANGED
|
@@ -57,7 +57,7 @@ const COMMAND_FLAGS = {
|
|
|
57
57
|
login: [],
|
|
58
58
|
logout: [],
|
|
59
59
|
whoami: [],
|
|
60
|
-
jobs: ['function', 'company', 'region', 'job-type', 'remote', 'status', 'sort', 'limit', 'offset', 'keywords', 'location', 'modality'],
|
|
60
|
+
jobs: ['function', 'company', 'region', 'job-type', 'work-arrangement', 'remote', 'status', 'sort', 'limit', 'offset', 'keywords', 'location', 'modality'],
|
|
61
61
|
job: [],
|
|
62
62
|
companies: ['limit', 'offset'],
|
|
63
63
|
search: [],
|
|
@@ -86,6 +86,7 @@ const STATUS_LABELS = {
|
|
|
86
86
|
saved: 'saved',
|
|
87
87
|
dismissed: 'dismissed',
|
|
88
88
|
};
|
|
89
|
+
const WORK_ARRANGEMENT_VALUES = ['remote', 'hybrid', 'in_person', 'unspecified'];
|
|
89
90
|
|
|
90
91
|
function parseArgs(args) {
|
|
91
92
|
const result = { _: [], command: null };
|
|
@@ -262,7 +263,7 @@ function hasJobListFilters(args) {
|
|
|
262
263
|
// (which then raises the migration error) instead of cmdJob(42) silently ignoring
|
|
263
264
|
// the filter. See PR #13 Cursor Bugbot finding.
|
|
264
265
|
const deprecated = Object.keys(DEPRECATED_JOB_FLAGS);
|
|
265
|
-
const active = ['function', 'company', 'keywords', 'limit', 'region', 'job-type', 'remote', 'offset', 'sort', 'status'];
|
|
266
|
+
const active = ['function', 'company', 'keywords', 'limit', 'region', 'job-type', 'work-arrangement', 'remote', 'offset', 'sort', 'status'];
|
|
266
267
|
return [...active, ...deprecated].some((key) => args[key] !== undefined);
|
|
267
268
|
}
|
|
268
269
|
|
|
@@ -506,11 +507,24 @@ async function cmdJobs(args) {
|
|
|
506
507
|
failNow('--remote cannot be combined with --region. Use one or the other (e.g. --region remote).');
|
|
507
508
|
}
|
|
508
509
|
|
|
510
|
+
let workArrangements;
|
|
511
|
+
if (args['work-arrangement'] !== undefined) {
|
|
512
|
+
if (typeof args['work-arrangement'] !== 'string' || !args['work-arrangement'].trim()) {
|
|
513
|
+
failNow('--work-arrangement requires a comma-separated value: remote, hybrid, in_person, unspecified.');
|
|
514
|
+
}
|
|
515
|
+
workArrangements = [...new Set(args['work-arrangement'].split(',').map((value) => value.trim()))];
|
|
516
|
+
const invalid = workArrangements.filter((value) => !WORK_ARRANGEMENT_VALUES.includes(value));
|
|
517
|
+
if (invalid.length > 0) {
|
|
518
|
+
failNow(`Invalid --work-arrangement value "${invalid.join(',')}". Use: ${WORK_ARRANGEMENT_VALUES.join(', ')}.`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
509
522
|
const params = new URLSearchParams();
|
|
510
523
|
if (args.function) params.set('jobFunction', args.function);
|
|
511
524
|
if (args.company) params.set('companyId', args.company);
|
|
512
525
|
if (args.region) params.set('locationFilter', args.region);
|
|
513
526
|
if (args['job-type']) params.set('jobModality', args['job-type']);
|
|
527
|
+
if (workArrangements) params.set('workArrangements', workArrangements.join(','));
|
|
514
528
|
if (args.remote) params.set('usStates', 'REMOTE');
|
|
515
529
|
if (args.status) params.set('status', args.status);
|
|
516
530
|
if (args.sort) params.set('sort', args.sort);
|
|
@@ -994,7 +1008,7 @@ ${color('bold', 'Commands:')}
|
|
|
994
1008
|
${color('cyan', 'login')} Authenticate via Google OAuth
|
|
995
1009
|
${color('cyan', 'logout')} Clear credentials
|
|
996
1010
|
${color('cyan', 'whoami')} Show current user
|
|
997
|
-
${color('cyan', 'jobs')} List jobs (--function, --company <id>, --region <tag>, --job-type <type>, --remote, --status, --sort, --limit, --offset, --keywords)
|
|
1011
|
+
${color('cyan', 'jobs')} List jobs (--function, --company <id>, --region <tag>, --job-type <type>, --work-arrangement <values>, --remote, --status, --sort, --limit, --offset, --keywords)
|
|
998
1012
|
${color('cyan', 'job <id>')} Get full job detail
|
|
999
1013
|
${color('cyan', 'jobs <id>')} Alias for job <id>
|
|
1000
1014
|
${color('cyan', 'companies')} List companies
|
|
@@ -1036,6 +1050,8 @@ ${color('bold', 'Filter values:')}
|
|
|
1036
1050
|
Use a comma-separated list of region tags for multiple regions;
|
|
1037
1051
|
us/non_us/all cannot be combined.
|
|
1038
1052
|
--job-type full_time, internship, all (employment type, not work-location)
|
|
1053
|
+
--work-arrangement
|
|
1054
|
+
remote, hybrid, in_person, unspecified (comma-separated)
|
|
1039
1055
|
--remote Flag (no value). Filters for remote jobs (equivalent to usStates=REMOTE).
|
|
1040
1056
|
--status new, applying, applied_confirmed, check_later, not_interested, all
|
|
1041
1057
|
(filters by YOUR application pipeline state)
|
package/docs/trackly-tools.md
CHANGED
|
@@ -72,7 +72,8 @@ You can also pass `TRACKLY_API_KEY` as an environment variable for one-off runs.
|
|
|
72
72
|
- `locationFilter`: pass ONE of
|
|
73
73
|
- a single scalar: `us`, `non_us`, `all`, or a region tag (`europe`, `latam`, `middle_east`, `asia`, `africa`, `canada`, `oceania`, `remote`, `unknown`), OR
|
|
74
74
|
- an array of region tags for multi-region (e.g. `["europe", "canada"]`). The array form excludes `us` — combining `us` with other tags in an array causes the backend to silently drop the others. To get "not US" use the scalar `non_us` alone.
|
|
75
|
-
- `jobModality`: `full_time`, `internship`, `all` — employment type, NOT work
|
|
75
|
+
- `jobModality`: `full_time`, `internship`, `all` — employment type, NOT work arrangement.
|
|
76
|
+
- `workArrangements`: one or more of `remote`, `hybrid`, `in_person`, `unspecified`. This is an independent axis and combines with region, employment type, and function filters.
|
|
76
77
|
- `remote` (boolean): filter to remote jobs only. Maps to `usStates=REMOTE`.
|
|
77
78
|
- `status`: your application pipeline state. Values: `new`, `applied_confirmed`, `check_later`, `not_interested`, `all`. Older `applying` requests are accepted by the backend as a private compatibility alias for `check_later`, but new clients must not emit it.
|
|
78
79
|
- `sort`: `newest` (default) or `match` (highest match score first; requires a resume on file). Backend rejects the deprecated values `oldest` and `company` with HTTP 400.
|
package/mcp/server.js
CHANGED
|
@@ -40,6 +40,10 @@ const STATUS_VALUES = ['new', 'applied_confirmed', 'check_later', 'not_intereste
|
|
|
40
40
|
// `jobModality` enum matches `jobscout.ts:2870-2875`. Employment type, NOT work-location.
|
|
41
41
|
const JOB_MODALITIES = ['full_time', 'internship', 'all'];
|
|
42
42
|
|
|
43
|
+
// Independent from geography and employment type. Matches the backend's
|
|
44
|
+
// workArrangements query contract and job_postings constraint.
|
|
45
|
+
const WORK_ARRANGEMENTS = ['remote', 'hybrid', 'in_person', 'unspecified'];
|
|
46
|
+
|
|
43
47
|
// `sort` enum matches backend handler at `jobscout.ts:3053` — NOT the pre-fix
|
|
44
48
|
// `newest|oldest|company` (backend rejects oldest/company with HTTP 400).
|
|
45
49
|
const SORT_VALUES = ['newest', 'match'];
|
|
@@ -126,7 +130,7 @@ function createServer() {
|
|
|
126
130
|
|
|
127
131
|
server.tool(
|
|
128
132
|
'trackly_search_jobs',
|
|
129
|
-
'Search and filter job postings. Returns matching jobs with title, company, location, and structured fields. Use companyId to filter jobs at a specific company (get companyId from trackly_search_companies first).
|
|
133
|
+
'Search and filter job postings. Returns matching jobs with title, company, location, and structured fields. Use companyId to filter jobs at a specific company (get companyId from trackly_search_companies first). Work arrangement is independent from region and employment type: use workArrangements for remote, hybrid, in-person, or unspecified classifications.',
|
|
130
134
|
{
|
|
131
135
|
function: z.enum(JOB_FUNCTIONS).optional().describe('Job function filter. One of: ' + JOB_FUNCTIONS.join(', ')),
|
|
132
136
|
companyId: z.number().optional().describe('Filter jobs by company ID (get from trackly_search_companies)'),
|
|
@@ -138,7 +142,10 @@ function createServer() {
|
|
|
138
142
|
"Region tag filter. Pass ONE of: (a) a single scalar from 'us', 'non_us', 'all', or a REGION_TAGS value ('europe', 'latam', 'middle_east', 'asia', 'africa', 'canada', 'oceania', 'remote', 'unknown'); or (b) an array of region tags for multi-region (e.g. ['europe', 'canada']). The array form excludes 'us' — combining 'us' with other tags causes the backend to silently drop the others. For 'not US' use the scalar 'non_us' alone."
|
|
139
143
|
),
|
|
140
144
|
jobModality: z.enum(JOB_MODALITIES).optional().describe(
|
|
141
|
-
|
|
145
|
+
'Employment type (NOT work arrangement). full_time = full-time roles, internship = internships, all = both. Use workArrangements for remote, hybrid, or in-person classification.'
|
|
146
|
+
),
|
|
147
|
+
workArrangements: z.array(z.enum(WORK_ARRANGEMENTS)).min(1).max(4).optional().describe(
|
|
148
|
+
'Work arrangement filter, independent from geography and employment type. Values: remote, hybrid, in_person, unspecified. Multiple values use OR semantics.'
|
|
142
149
|
),
|
|
143
150
|
remote: z.boolean().optional().describe('Filter to remote jobs only (maps to usStates=REMOTE).'),
|
|
144
151
|
status: z.enum(STATUS_VALUES).optional().describe(
|
|
@@ -168,6 +175,7 @@ function createServer() {
|
|
|
168
175
|
qs.set('locationFilter', value);
|
|
169
176
|
}
|
|
170
177
|
if (params.jobModality !== undefined) qs.set('jobModality', params.jobModality);
|
|
178
|
+
if (params.workArrangements !== undefined) qs.set('workArrangements', params.workArrangements.join(','));
|
|
171
179
|
if (params.remote === true) qs.set('usStates', 'REMOTE');
|
|
172
180
|
if (params.status !== undefined) qs.set('status', params.status);
|
|
173
181
|
if (params.sort !== undefined) qs.set('sort', params.sort);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trackly-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"mcpName": "io.github.trackly-app/trackly",
|
|
5
5
|
"description": "AI job search CLI + hosted MCP server with OAuth. 128K+ jobs, 1,900+ companies, 40+ ATS. Works with Claude, ChatGPT, Cursor, Windsurf, Codex via hosted streamable-http or local stdio.",
|
|
6
6
|
"bin": {
|
package/server.json
CHANGED
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
]
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
|
-
"version": "0.
|
|
20
|
+
"version": "0.6.0",
|
|
21
21
|
"packages": [
|
|
22
22
|
{
|
|
23
23
|
"registryType": "npm",
|
|
24
24
|
"identifier": "trackly-cli",
|
|
25
|
-
"version": "0.
|
|
25
|
+
"version": "0.6.0",
|
|
26
26
|
"runtimeHint": "npx",
|
|
27
27
|
"runtimeArguments": [
|
|
28
28
|
{
|