dsh-tacit 0.4.0 → 0.5.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.
@@ -0,0 +1,62 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+
4
+ /**
5
+ * The identity of a workspace: the absolute directory with `..` resolved, a
6
+ * trailing separator stripped and symlinks followed when it exists on disk.
7
+ * Case is left alone. '' when there is no directory.
8
+ */
9
+ export function normalizeWorkspace(raw) {
10
+ if (typeof raw !== 'string' || raw.length === 0) return ''
11
+ const resolved = path.resolve(raw)
12
+ try {
13
+ return fs.realpathSync(resolved)
14
+ } catch {
15
+ return resolved
16
+ }
17
+ }
18
+
19
+ /** Whether `cwd` is `scope` itself or a directory inside it — by path segment, never by string prefix. */
20
+ export function workspaceContains(scope, cwd) {
21
+ if (typeof scope !== 'string' || scope.length === 0 || typeof cwd !== 'string') return false
22
+ if (cwd === scope) return true
23
+ const base = /[\\/]$/.test(scope) ? scope.slice(0, -1) : scope
24
+ return cwd.startsWith(base + '/') || cwd.startsWith(base + '\\')
25
+ }
26
+
27
+ /** The last path segment of a workspace directory — what a person calls the project. */
28
+ export function workspaceLabel(cwd) {
29
+ if (typeof cwd !== 'string' || cwd.length === 0) return ''
30
+ const parts = cwd.split(/[\\/]+/).filter((part) => part.length > 0)
31
+ return parts.length > 0 ? parts[parts.length - 1] : ''
32
+ }
33
+
34
+ /**
35
+ * A distinct label per workspace, in insertion order: the basename, extended by
36
+ * one more parent segment at a time (`a/web`, `b/web`) while two of them would
37
+ * read the same, up to the full path.
38
+ */
39
+ export function workspaceLabels(cwds) {
40
+ const distinct = [...new Set((Array.isArray(cwds) ? cwds : []).filter((cwd) => typeof cwd === 'string' && cwd.length > 0))]
41
+ const segments = new Map(distinct.map((cwd) => [cwd, cwd.split(/[\\/]+/).filter((part) => part.length > 0)]))
42
+ const depth = new Map(distinct.map((cwd) => [cwd, 1]))
43
+ const labelOf = (cwd) => segments.get(cwd).slice(-depth.get(cwd)).join('/')
44
+ for (;;) {
45
+ const groups = new Map()
46
+ for (const cwd of distinct) {
47
+ const label = labelOf(cwd)
48
+ if (groups.has(label)) groups.get(label).push(cwd)
49
+ else groups.set(label, [cwd])
50
+ }
51
+ let grew = false
52
+ for (const group of groups.values()) {
53
+ if (group.length < 2) continue
54
+ for (const cwd of group) {
55
+ if (depth.get(cwd) >= segments.get(cwd).length) continue
56
+ depth.set(cwd, depth.get(cwd) + 1)
57
+ grew = true
58
+ }
59
+ }
60
+ if (!grew) return new Map(distinct.map((cwd) => [cwd, labelOf(cwd)]))
61
+ }
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-tacit",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Tacit learns what you leave unsaid in your prompts — from messy turns and your own corrections, with zero clicks — and tells the agent how to compensate, on every turn, via a system-prompt section you can read and edit.",
5
5
  "author": "hackernotfound",
6
6
  "license": "MIT",