qiksy 1.2.2 → 1.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qiksy",
3
- "version": "1.2.2",
3
+ "version": "1.3.1",
4
4
  "description": "Qiksy \u2014 one local engine for every Qiksy tool (Work Finder, Travel Assistant, Client Finder). Runs on your own Claude; nothing leaves your machine.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -78,11 +78,12 @@ RULES:
78
78
  - Skip these companies (already found earlier): ${excludeCompanies.length ? excludeCompanies.join(', ') : 'none'}.
79
79
  - Find AT LEAST ${n} distinct openings at distinct companies — more is better. There is no upper limit: return every match you can confirm in this pass.
80
80
  - SCORE THE FIT: for every vacancy set "match" to an INTEGER 0-100 — how well THIS role fits the candidate above (stack & seniority overlap, remote eligibility from ${profile?.location || 'Ukraine'}, engagement type). Be honest and discriminating: reserve 90+ for a strong direct match, 60-75 for a plausible partial fit, below 50 for a stretch. The "notes" field should justify the number in one line.
81
+ - FIRST FIVE, ONE AT A TIME. Do not gather a batch before reporting. Take the first promising link, WebFetch it, and the MOMENT it checks out print its VACANCY line — before you open the next one. Repeat until five are out. Only after that may you work in parallel and report in groups. The person is watching an empty screen until the first line appears, and five minutes of nothing reads as broken (measured 2026-07-26: first results at ~7 min).
81
82
  - WORK FAST but do not stop early: broad WebSearch queries first, then WebFetch the promising pages. Streaming a confirmed find costs nothing extra, so stream as you go.
82
83
  - TIME BUDGET: up to ~12 minutes for this pass. Stop when you genuinely run out of NEW companies, not when you reach a number.
83
84
  ${wave > 1 ? `- THIS IS SWEEP #${wave}. Everything the earlier sweeps found is in the exclusion list above and must not come back. Asking the same question again returns the same answers, so change the ANGLE: different search phrasings and role synonyms, different ATS hosts, different regions and countries, smaller and less-known companies, older-but-still-open postings, adjacent titles the candidate also fits. If a whole angle turns up nothing new, say so and finish the pass — an honest empty sweep ends the search cleanly.` : ''}
84
85
 
85
- PROGRESS STREAMING: the user watches results arrive live. As soon as you have CONFIRMED one vacancy (page fetched, fresh, geography checked), immediately print a single line:
86
+ PROGRESS STREAMING — THIS IS NOT OPTIONAL AND NOT A SUMMARY STEP: the user is watching a live screen and sees nothing until you print. Never hold findings back to report them together at the end; a vacancy you confirmed but have not printed is invisible to them. As soon as you have CONFIRMED one vacancy (page fetched, fresh, geography checked), immediately print a single line:
86
87
  VACANCY: {"company":...} ← one-line JSON, exact same fields as the array items below
87
88
  …then continue searching. Stream every confirmed finding this way.
88
89
 
@@ -33,7 +33,11 @@ function defaults() {
33
33
  re-read every pass. Three days was aggressive enough to hide most of the
34
34
  market; two weeks is still "current" and leaves something to find. */
35
35
  freshnessFrom: 0,
36
- freshnessDays: 14,
36
+ /* Seven days, not fourteen (owner decision 2026-07-26). A wider window mostly
37
+ buys older postings that nobody answers any more, at the cost of the agent
38
+ re-reading them — slower AND more of the person's Claude quota for less. The
39
+ window is a setting, so "dig deeper" stays one control away. */
40
+ freshnessDays: 7,
37
41
  /* How many sweeps at most. Unbounded would spend the user's whole Claude
38
42
  quota in one night; two barren sweeps in a row also end it early. */
39
43
  maxSweeps: 6,
@@ -212,8 +212,21 @@ function startSearch(position, run = null) {
212
212
  const sweep = async (wave) => {
213
213
  const exclude = db.vacancies.filter(v => v.positionId === position.id).map(v => v.company);
214
214
  const before = added;
215
+ /* The FIRST wave is deliberately smaller. Measured on a live run 2026-07-26: the
216
+ first finds landed after ~7 minutes and the next 20 minutes added two more —
217
+ so the person was made to wait for a tail they could have been working through.
218
+ A short first wave gets them a usable list fast; the rest keeps arriving behind
219
+ them, and the UI now says so. Later waves go back to full size. */
220
+ const perWave = wave === 1
221
+ ? Math.min(12, db.settings.resultsPerSearch ?? 20)
222
+ : (db.settings.resultsPerSearch ?? 20);
223
+ const waveSettings = { ...db.settings, resultsPerSearch: perWave };
224
+ // The app shows "wave 2 of 6" from these — a log line in Russian could not be
225
+ // translated, and a run with no visible progress reads as a hang.
226
+ job.wave = wave;
227
+ job.waves = maxSweeps;
215
228
  const r = await runClaude({
216
- prompt: searchPrompt(position, profile, exclude, db.settings, { wave, waves: maxSweeps, canNarrow: effectivePlanId() !== 'free' }),
229
+ prompt: searchPrompt(position, profile, exclude, waveSettings, { wave, waves: maxSweeps, canNarrow: effectivePlanId() !== 'free' }),
217
230
  allowedTools: ['WebSearch', 'WebFetch'],
218
231
  withBrowser: !!db.settings.searchViaBrowser,
219
232
  model: db.settings.model,