@nitra/cursor 3.6.0 → 3.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.6.1] - 2026-06-01
4
+
5
+ ### Fixed
6
+
7
+ - flow: resolveArtifact обирає артефакт за mtime + пріоритетом slug гілки замість лексикографічного (spec/plan фіксували не той doc при кількох на одну дату)
8
+
3
9
  ## [3.6.0] - 2026-06-01
4
10
 
5
11
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitra/cursor",
3
- "version": "3.6.0",
3
+ "version": "3.6.1",
4
4
  "description": "CLI для завантаження cursor-правил (префікс n-) у локальний репозиторій",
5
5
  "keywords": [
6
6
  "cli",
@@ -6,24 +6,36 @@
6
6
  * Лінки front-matter (`spec.plan`/`plan.spec`/`plan.flow`) пише сам агент за
7
7
  * контрактом `flow.mdc` — тут лише ВЕРИФІКАЦІЯ (мутатора `trace link` нема).
8
8
  */
9
- import { existsSync, readdirSync } from 'node:fs'
9
+ import { existsSync, readdirSync, statSync } from 'node:fs'
10
10
  import { join } from 'node:path'
11
11
 
12
12
  import { runTraceCli } from '../trace.mjs'
13
13
 
14
14
  /**
15
- * Найсвіжіший `docs/<kind>/*.md` (лексикографічно дата у префіксі назви).
15
+ * Резолвить артефакт у `docs/<kind>/`. Пріоритет: файли, чия назва містить
16
+ * хвіст гілки (slug, напр. `flow-gate` з `claude/flow-gate`); серед них (або
17
+ * серед усіх, якщо збігу нема) — **найсвіжіший за mtime**. Лексикографічний
18
+ * вибір був хибним при кількох артефактах на одну дату (виявлено dogfood'ом).
16
19
  * @param {string} cwd корінь worktree
17
20
  * @param {'specs' | 'plans'} kind підкаталог `docs`
21
+ * @param {string} [branch] гілка задачі — для пріоритету за slug
18
22
  * @returns {string | null} абсолютний шлях або null, якщо каталог/файли відсутні
19
23
  */
20
- export function resolveArtifact(cwd, kind) {
24
+ export function resolveArtifact(cwd, kind, branch) {
21
25
  const dir = join(cwd, 'docs', kind)
22
26
  if (!existsSync(dir)) return null
23
- const md = readdirSync(dir)
24
- .filter(f => f.endsWith('.md'))
25
- .toSorted()
26
- return md.length > 0 ? join(dir, md.at(-1)) : null
27
+ const md = readdirSync(dir).filter(f => f.endsWith('.md'))
28
+ if (md.length === 0) return null
29
+
30
+ const slug = branch ? branch.split('/').pop() : null
31
+ const matched = slug ? md.filter(f => f.includes(slug)) : []
32
+ const pool = matched.length > 0 ? matched : md
33
+
34
+ const best = pool
35
+ .map(f => ({ f, mtime: statSync(join(dir, f)).mtimeMs }))
36
+ .toSorted((a, b) => a.mtime - b.mtime || (a.f < b.f ? -1 : 1))
37
+ .at(-1)
38
+ return join(dir, best.f)
27
39
  }
28
40
 
29
41
  /** Маркер критерію приймання в рядку кроку (порівняння — case-insensitive). */
@@ -35,7 +35,7 @@ export async function plan(rest, deps = {}) {
35
35
  log('plan: дизайн ще не зафіксовано — рекомендовано спершу `flow spec` (не блокує)')
36
36
  }
37
37
 
38
- const doc = rest.find(a => a.endsWith('.md')) ?? resolveArtifact(cwd, 'plans')
38
+ const doc = rest.find(a => a.endsWith('.md')) ?? resolveArtifact(cwd, 'plans', state.branch)
39
39
  let steps
40
40
  if (rest.includes('--panel')) {
41
41
  let runner = deps.runner
@@ -48,7 +48,7 @@ export async function spec(rest, deps = {}) {
48
48
  }
49
49
  }
50
50
 
51
- const doc = rest.find(a => a.endsWith('.md')) ?? resolveArtifact(cwd, 'specs')
51
+ const doc = rest.find(a => a.endsWith('.md')) ?? resolveArtifact(cwd, 'specs', state.branch)
52
52
  if (!doc || !existsSync(doc)) {
53
53
  log('spec: нема docs/specs/<date>-<slug>.md — спершу пройди brainstorm (див. flow.mdc)')
54
54
  return 1