@mindfoldhq/trellis 0.5.0-beta.14 → 0.5.0-beta.16
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 +5 -5
- package/dist/cli/index.js +1 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +24 -20
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +44 -13
- package/dist/commands/update.js.map +1 -1
- package/dist/configurators/claude.js +1 -1
- package/dist/configurators/claude.js.map +1 -1
- package/dist/configurators/codebuddy.js +1 -1
- package/dist/configurators/codebuddy.js.map +1 -1
- package/dist/configurators/codex.d.ts.map +1 -1
- package/dist/configurators/codex.js +3 -6
- package/dist/configurators/codex.js.map +1 -1
- package/dist/configurators/copilot.d.ts.map +1 -1
- package/dist/configurators/copilot.js +4 -11
- package/dist/configurators/copilot.js.map +1 -1
- package/dist/configurators/cursor.js +1 -1
- package/dist/configurators/cursor.js.map +1 -1
- package/dist/configurators/droid.js +1 -1
- package/dist/configurators/droid.js.map +1 -1
- package/dist/configurators/gemini.d.ts.map +1 -1
- package/dist/configurators/gemini.js +1 -3
- package/dist/configurators/gemini.js.map +1 -1
- package/dist/configurators/index.d.ts.map +1 -1
- package/dist/configurators/index.js +24 -38
- package/dist/configurators/index.js.map +1 -1
- package/dist/configurators/kiro.js +1 -1
- package/dist/configurators/kiro.js.map +1 -1
- package/dist/configurators/opencode.d.ts.map +1 -1
- package/dist/configurators/opencode.js +4 -1
- package/dist/configurators/opencode.js.map +1 -1
- package/dist/configurators/pi.d.ts +3 -0
- package/dist/configurators/pi.d.ts.map +1 -0
- package/dist/configurators/pi.js +39 -0
- package/dist/configurators/pi.js.map +1 -0
- package/dist/configurators/qoder.d.ts.map +1 -1
- package/dist/configurators/qoder.js +1 -3
- package/dist/configurators/qoder.js.map +1 -1
- package/dist/configurators/shared.d.ts +2 -4
- package/dist/configurators/shared.d.ts.map +1 -1
- package/dist/configurators/shared.js +6 -9
- package/dist/configurators/shared.js.map +1 -1
- package/dist/migrations/manifests/0.5.0-beta.15.json +116 -0
- package/dist/migrations/manifests/0.5.0-beta.16.json +9 -0
- package/dist/templates/claude/agents/trellis-research.md +1 -1
- package/dist/templates/claude/settings.json +0 -4
- package/dist/templates/codebuddy/agents/trellis-research.md +1 -1
- package/dist/templates/codex/agents/trellis-check.toml +0 -16
- package/dist/templates/codex/agents/trellis-implement.toml +0 -16
- package/dist/templates/codex/agents/trellis-research.toml +3 -2
- package/dist/templates/codex/hooks/session-start.py +51 -21
- package/dist/templates/codex/skills/start/SKILL.md +1 -1
- package/dist/templates/copilot/hooks/session-start.py +51 -21
- package/dist/templates/copilot/prompts/start.prompt.md +1 -1
- package/dist/templates/cursor/agents/trellis-check.md +1 -1
- package/dist/templates/cursor/agents/trellis-implement.md +1 -1
- package/dist/templates/cursor/agents/trellis-research.md +2 -2
- package/dist/templates/cursor/hooks.json +7 -1
- package/dist/templates/droid/droids/trellis-research.md +1 -1
- package/dist/templates/extract.d.ts +6 -0
- package/dist/templates/extract.d.ts.map +1 -1
- package/dist/templates/extract.js +14 -0
- package/dist/templates/extract.js.map +1 -1
- package/dist/templates/gemini/agents/trellis-research.md +1 -1
- package/dist/templates/kiro/agents/trellis-research.json +1 -1
- package/dist/templates/markdown/agents.md +11 -12
- package/dist/templates/markdown/gitignore.txt +3 -0
- package/dist/templates/opencode/agents/trellis-check.md +1 -1
- package/dist/templates/opencode/agents/trellis-implement.md +1 -1
- package/dist/templates/opencode/agents/trellis-research.md +2 -2
- package/dist/templates/opencode/lib/trellis-context.js +100 -13
- package/dist/templates/opencode/plugins/inject-subagent-context.js +54 -4
- package/dist/templates/opencode/plugins/inject-workflow-state.js +48 -25
- package/dist/templates/opencode/plugins/session-start.js +29 -16
- package/dist/templates/pi/agents/trellis-check.md +28 -0
- package/dist/templates/pi/agents/trellis-implement.md +33 -0
- package/dist/templates/pi/agents/trellis-research.md +25 -0
- package/dist/templates/pi/extensions/trellis/index.ts.txt +549 -0
- package/dist/templates/pi/index.d.ts +5 -0
- package/dist/templates/pi/index.d.ts.map +1 -0
- package/dist/templates/pi/index.js +12 -0
- package/dist/templates/pi/index.js.map +1 -0
- package/dist/templates/pi/settings.json +12 -0
- package/dist/templates/qoder/agents/trellis-research.md +1 -1
- package/dist/templates/shared-hooks/index.d.ts +31 -0
- package/dist/templates/shared-hooks/index.d.ts.map +1 -1
- package/dist/templates/shared-hooks/index.js +59 -0
- package/dist/templates/shared-hooks/index.js.map +1 -1
- package/dist/templates/shared-hooks/inject-shell-session-context.py +180 -0
- package/dist/templates/shared-hooks/inject-subagent-context.py +128 -26
- package/dist/templates/shared-hooks/inject-workflow-state.py +99 -62
- package/dist/templates/shared-hooks/session-start.py +139 -24
- package/dist/templates/trellis/gitignore.txt +3 -0
- package/dist/templates/trellis/index.d.ts +1 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +2 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/common/__init__.py +8 -0
- package/dist/templates/trellis/scripts/common/active_task.py +593 -0
- package/dist/templates/trellis/scripts/common/cli_adapter.py +43 -8
- package/dist/templates/trellis/scripts/common/paths.py +61 -58
- package/dist/templates/trellis/scripts/common/session_context.py +12 -0
- package/dist/templates/trellis/scripts/common/task_store.py +6 -8
- package/dist/templates/trellis/scripts/task.py +59 -17
- package/dist/templates/trellis/workflow.md +30 -26
- package/dist/types/ai-tools.d.ts +3 -3
- package/dist/types/ai-tools.d.ts.map +1 -1
- package/dist/types/ai-tools.js +16 -0
- package/dist/types/ai-tools.js.map +1 -1
- package/dist/utils/posix.d.ts +13 -0
- package/dist/utils/posix.d.ts.map +1 -0
- package/dist/utils/posix.js +15 -0
- package/dist/utils/posix.js.map +1 -0
- package/dist/utils/template-fetcher.d.ts +22 -6
- package/dist/utils/template-fetcher.d.ts.map +1 -1
- package/dist/utils/template-fetcher.js +405 -27
- package/dist/utils/template-fetcher.js.map +1 -1
- package/dist/utils/template-hash.d.ts +22 -3
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +80 -18
- package/dist/utils/template-hash.js.map +1 -1
- package/package.json +1 -1
- package/dist/templates/shared-hooks/statusline.py +0 -219
|
@@ -3,18 +3,37 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Stores SHA256 hashes of template files at install time.
|
|
5
5
|
* Used to determine if users have modified templates.
|
|
6
|
+
*
|
|
7
|
+
* Cross-platform contract:
|
|
8
|
+
* - All hash dictionary keys are POSIX style (`/`), never `\`.
|
|
9
|
+
* - Hashes are computed against LF-normalized content, so CRLF and LF
|
|
10
|
+
* versions of the same logical content produce the same hash.
|
|
11
|
+
* - Persisted JSON uses schema `{ __version: 2, hashes: { ... } }`. Old
|
|
12
|
+
* flat-format files (no `__version`) are silently discarded; the hash
|
|
13
|
+
* set is regenerated on next init/update.
|
|
6
14
|
*/
|
|
7
15
|
import type { TemplateHashes } from "../types/migration.js";
|
|
8
16
|
/**
|
|
9
|
-
* Compute SHA256 hash of content
|
|
17
|
+
* Compute SHA256 hash of content.
|
|
18
|
+
*
|
|
19
|
+
* Normalizes line endings (CRLF -> LF) before hashing so that the same
|
|
20
|
+
* logical content yields the same hash across platforms.
|
|
10
21
|
*/
|
|
11
22
|
export declare function computeHash(content: string): string;
|
|
12
23
|
/**
|
|
13
|
-
* Load stored template hashes
|
|
24
|
+
* Load stored template hashes.
|
|
25
|
+
*
|
|
26
|
+
* Returns `{}` for: missing file, invalid JSON, or legacy flat-format
|
|
27
|
+
* files (no `__version`). Legacy files are silently dropped; the hash
|
|
28
|
+
* set is regenerated by subsequent init/update logic. This handles two
|
|
29
|
+
* Windows compatibility issues at once: backslash keys and CRLF-based
|
|
30
|
+
* hashes both differ from the v2 contract.
|
|
14
31
|
*/
|
|
15
32
|
export declare function loadHashes(cwd: string): TemplateHashes;
|
|
16
33
|
/**
|
|
17
|
-
* Save template hashes
|
|
34
|
+
* Save template hashes.
|
|
35
|
+
*
|
|
36
|
+
* Always writes the v2 schema with POSIX-normalized keys.
|
|
18
37
|
*/
|
|
19
38
|
export declare function saveHashes(cwd: string, hashes: TemplateHashes): void;
|
|
20
39
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-hash.d.ts","sourceRoot":"","sources":["../../src/utils/template-hash.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"template-hash.d.ts","sourceRoot":"","sources":["../../src/utils/template-hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAe5D;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGnD;AAoBD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CA6BtD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAOpE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAQ1E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAU1E;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAKlE;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,IAAI,CASN;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,cAAc,GACrB,OAAO,CAsBT;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,GACtB,OAAO,CAST;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,EAAE,EACvB,MAAM,EAAE,cAAc,GACrB,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAQtB;AAuED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAsBpD"}
|
|
@@ -3,19 +3,34 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Stores SHA256 hashes of template files at install time.
|
|
5
5
|
* Used to determine if users have modified templates.
|
|
6
|
+
*
|
|
7
|
+
* Cross-platform contract:
|
|
8
|
+
* - All hash dictionary keys are POSIX style (`/`), never `\`.
|
|
9
|
+
* - Hashes are computed against LF-normalized content, so CRLF and LF
|
|
10
|
+
* versions of the same logical content produce the same hash.
|
|
11
|
+
* - Persisted JSON uses schema `{ __version: 2, hashes: { ... } }`. Old
|
|
12
|
+
* flat-format files (no `__version`) are silently discarded; the hash
|
|
13
|
+
* set is regenerated on next init/update.
|
|
6
14
|
*/
|
|
7
15
|
import { createHash } from "node:crypto";
|
|
8
16
|
import fs from "node:fs";
|
|
9
17
|
import path from "node:path";
|
|
10
18
|
import { DIR_NAMES } from "../constants/paths.js";
|
|
11
19
|
import { ALL_MANAGED_DIRS } from "../configurators/index.js";
|
|
20
|
+
import { toPosix } from "./posix.js";
|
|
12
21
|
/** File name for storing template hashes */
|
|
13
22
|
const HASHES_FILE = ".template-hashes.json";
|
|
23
|
+
/** Current schema version for `.template-hashes.json` */
|
|
24
|
+
const HASHES_SCHEMA_VERSION = 2;
|
|
14
25
|
/**
|
|
15
|
-
* Compute SHA256 hash of content
|
|
26
|
+
* Compute SHA256 hash of content.
|
|
27
|
+
*
|
|
28
|
+
* Normalizes line endings (CRLF -> LF) before hashing so that the same
|
|
29
|
+
* logical content yields the same hash across platforms.
|
|
16
30
|
*/
|
|
17
31
|
export function computeHash(content) {
|
|
18
|
-
|
|
32
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
33
|
+
return createHash("sha256").update(normalized, "utf-8").digest("hex");
|
|
19
34
|
}
|
|
20
35
|
/**
|
|
21
36
|
* Get path to the hashes file
|
|
@@ -24,7 +39,23 @@ function getHashesPath(cwd) {
|
|
|
24
39
|
return path.join(cwd, DIR_NAMES.WORKFLOW, HASHES_FILE);
|
|
25
40
|
}
|
|
26
41
|
/**
|
|
27
|
-
*
|
|
42
|
+
* Normalize all keys of a hashes record to POSIX form.
|
|
43
|
+
*/
|
|
44
|
+
function normalizeHashKeys(hashes) {
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const [key, value] of Object.entries(hashes)) {
|
|
47
|
+
out[toPosix(key)] = value;
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Load stored template hashes.
|
|
53
|
+
*
|
|
54
|
+
* Returns `{}` for: missing file, invalid JSON, or legacy flat-format
|
|
55
|
+
* files (no `__version`). Legacy files are silently dropped; the hash
|
|
56
|
+
* set is regenerated by subsequent init/update logic. This handles two
|
|
57
|
+
* Windows compatibility issues at once: backslash keys and CRLF-based
|
|
58
|
+
* hashes both differ from the v2 contract.
|
|
28
59
|
*/
|
|
29
60
|
export function loadHashes(cwd) {
|
|
30
61
|
const hashesPath = getHashesPath(cwd);
|
|
@@ -33,18 +64,37 @@ export function loadHashes(cwd) {
|
|
|
33
64
|
}
|
|
34
65
|
try {
|
|
35
66
|
const content = fs.readFileSync(hashesPath, "utf-8");
|
|
36
|
-
|
|
67
|
+
const parsed = JSON.parse(content);
|
|
68
|
+
if (parsed !== null &&
|
|
69
|
+
typeof parsed === "object" &&
|
|
70
|
+
"__version" in parsed &&
|
|
71
|
+
parsed.__version === HASHES_SCHEMA_VERSION &&
|
|
72
|
+
"hashes" in parsed &&
|
|
73
|
+
typeof parsed.hashes === "object" &&
|
|
74
|
+
parsed.hashes !== null) {
|
|
75
|
+
const stored = parsed;
|
|
76
|
+
// Defensive: enforce POSIX keys even if a writer slipped through.
|
|
77
|
+
return normalizeHashKeys(stored.hashes);
|
|
78
|
+
}
|
|
79
|
+
// Legacy flat format (no __version) or unknown shape: discard.
|
|
80
|
+
return {};
|
|
37
81
|
}
|
|
38
82
|
catch {
|
|
39
83
|
return {};
|
|
40
84
|
}
|
|
41
85
|
}
|
|
42
86
|
/**
|
|
43
|
-
* Save template hashes
|
|
87
|
+
* Save template hashes.
|
|
88
|
+
*
|
|
89
|
+
* Always writes the v2 schema with POSIX-normalized keys.
|
|
44
90
|
*/
|
|
45
91
|
export function saveHashes(cwd, hashes) {
|
|
46
92
|
const hashesPath = getHashesPath(cwd);
|
|
47
|
-
|
|
93
|
+
const payload = {
|
|
94
|
+
__version: HASHES_SCHEMA_VERSION,
|
|
95
|
+
hashes: normalizeHashKeys(hashes),
|
|
96
|
+
};
|
|
97
|
+
fs.writeFileSync(hashesPath, JSON.stringify(payload, null, 2));
|
|
48
98
|
}
|
|
49
99
|
/**
|
|
50
100
|
* Update hashes for specific files
|
|
@@ -55,7 +105,7 @@ export function saveHashes(cwd, hashes) {
|
|
|
55
105
|
export function updateHashes(cwd, files) {
|
|
56
106
|
const hashes = loadHashes(cwd);
|
|
57
107
|
for (const [relativePath, content] of files) {
|
|
58
|
-
hashes[relativePath] = computeHash(content);
|
|
108
|
+
hashes[toPosix(relativePath)] = computeHash(content);
|
|
59
109
|
}
|
|
60
110
|
saveHashes(cwd, hashes);
|
|
61
111
|
}
|
|
@@ -69,7 +119,7 @@ export function updateHashFromFile(cwd, relativePath) {
|
|
|
69
119
|
}
|
|
70
120
|
const content = fs.readFileSync(fullPath, "utf-8");
|
|
71
121
|
const hashes = loadHashes(cwd);
|
|
72
|
-
hashes[relativePath] = computeHash(content);
|
|
122
|
+
hashes[toPosix(relativePath)] = computeHash(content);
|
|
73
123
|
saveHashes(cwd, hashes);
|
|
74
124
|
}
|
|
75
125
|
/**
|
|
@@ -77,7 +127,8 @@ export function updateHashFromFile(cwd, relativePath) {
|
|
|
77
127
|
*/
|
|
78
128
|
export function removeHash(cwd, relativePath) {
|
|
79
129
|
const hashes = loadHashes(cwd);
|
|
80
|
-
const
|
|
130
|
+
const key = toPosix(relativePath);
|
|
131
|
+
const { [key]: _, ...rest } = hashes;
|
|
81
132
|
saveHashes(cwd, rest);
|
|
82
133
|
}
|
|
83
134
|
/**
|
|
@@ -85,9 +136,11 @@ export function removeHash(cwd, relativePath) {
|
|
|
85
136
|
*/
|
|
86
137
|
export function renameHash(cwd, oldPath, newPath) {
|
|
87
138
|
const hashes = loadHashes(cwd);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
139
|
+
const oldKey = toPosix(oldPath);
|
|
140
|
+
const newKey = toPosix(newPath);
|
|
141
|
+
if (hashes[oldKey]) {
|
|
142
|
+
const { [oldKey]: oldValue, ...rest } = hashes;
|
|
143
|
+
rest[newKey] = oldValue;
|
|
91
144
|
saveHashes(cwd, rest);
|
|
92
145
|
}
|
|
93
146
|
}
|
|
@@ -105,8 +158,11 @@ export function isTemplateModified(cwd, relativePath, hashes) {
|
|
|
105
158
|
if (!fs.existsSync(fullPath)) {
|
|
106
159
|
return false;
|
|
107
160
|
}
|
|
108
|
-
// If we don't have a stored hash, assume it's modified (conservative)
|
|
109
|
-
|
|
161
|
+
// If we don't have a stored hash, assume it's modified (conservative).
|
|
162
|
+
// Lookup by POSIX key — hashes loaded via loadHashes are already
|
|
163
|
+
// normalized, but the caller may have built `hashes` themselves.
|
|
164
|
+
const posixKey = toPosix(relativePath);
|
|
165
|
+
const storedHash = hashes[posixKey] ?? hashes[relativePath];
|
|
110
166
|
if (!storedHash) {
|
|
111
167
|
return true;
|
|
112
168
|
}
|
|
@@ -162,22 +218,26 @@ const EXCLUDE_FROM_HASH = [
|
|
|
162
218
|
"workspace/", // Workspace files (user data)
|
|
163
219
|
"tasks/", // Task files (user data)
|
|
164
220
|
".current-task", // Current task marker (file, not directory)
|
|
165
|
-
"spec/", // User-customized spec files
|
|
221
|
+
".trellis/spec/", // User-customized spec files
|
|
166
222
|
".backup-", // Backup directories
|
|
167
223
|
];
|
|
168
224
|
/**
|
|
169
225
|
* Check if a path should be excluded from hash tracking
|
|
170
226
|
*/
|
|
171
227
|
function shouldExcludeFromHash(relativePath) {
|
|
228
|
+
const normalizedPath = toPosix(relativePath);
|
|
172
229
|
for (const pattern of EXCLUDE_FROM_HASH) {
|
|
173
|
-
if (
|
|
230
|
+
if (normalizedPath.includes(pattern)) {
|
|
174
231
|
return true;
|
|
175
232
|
}
|
|
176
233
|
}
|
|
177
234
|
return false;
|
|
178
235
|
}
|
|
179
236
|
/**
|
|
180
|
-
* Recursively collect all files in a directory
|
|
237
|
+
* Recursively collect all files in a directory.
|
|
238
|
+
*
|
|
239
|
+
* Returned paths are POSIX-normalized so they can be used directly as
|
|
240
|
+
* hash dictionary keys regardless of host OS.
|
|
181
241
|
*/
|
|
182
242
|
function collectFiles(cwd, dir, relativeTo = "") {
|
|
183
243
|
const fullDir = path.join(cwd, dir);
|
|
@@ -195,7 +255,7 @@ function collectFiles(cwd, dir, relativeTo = "") {
|
|
|
195
255
|
files.push(...collectFiles(cwd, relativePath, relativeTo));
|
|
196
256
|
}
|
|
197
257
|
else if (entry.isFile()) {
|
|
198
|
-
files.push(relativePath);
|
|
258
|
+
files.push(toPosix(relativePath));
|
|
199
259
|
}
|
|
200
260
|
}
|
|
201
261
|
return files;
|
|
@@ -216,6 +276,8 @@ export function initializeHashes(cwd) {
|
|
|
216
276
|
for (const dir of TEMPLATE_DIRS) {
|
|
217
277
|
const files = collectFiles(cwd, dir);
|
|
218
278
|
for (const relativePath of files) {
|
|
279
|
+
// `relativePath` is POSIX (collectFiles normalizes); reconstruct an
|
|
280
|
+
// OS-native path for the actual fs read.
|
|
219
281
|
const fullPath = path.join(cwd, relativePath);
|
|
220
282
|
try {
|
|
221
283
|
const content = fs.readFileSync(fullPath, "utf-8");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-hash.js","sourceRoot":"","sources":["../../src/utils/template-hash.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"template-hash.js","sourceRoot":"","sources":["../../src/utils/template-hash.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Trellis StatusLine — project-level status display for Claude Code.
|
|
5
|
-
|
|
6
|
-
Reads Claude Code session JSON from stdin + Trellis task data from filesystem.
|
|
7
|
-
Outputs 1-2 lines:
|
|
8
|
-
With active task: [P1] Task title (status) + info line
|
|
9
|
-
Without task: info line only
|
|
10
|
-
Info line: model · ctx% · branch · duration · developer · tasks · rate limits
|
|
11
|
-
"""
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import json
|
|
15
|
-
import re
|
|
16
|
-
import subprocess
|
|
17
|
-
import sys
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
|
|
20
|
-
# Fix: Windows Python defaults to GBK encoding, which corrupts UTF-8
|
|
21
|
-
# characters like the middle dot (·). Wrap stdout/stderr with UTF-8.
|
|
22
|
-
if sys.platform == "win32":
|
|
23
|
-
for stream in (sys.stdout, sys.stderr):
|
|
24
|
-
reconfigure = getattr(stream, "reconfigure", None)
|
|
25
|
-
if callable(reconfigure):
|
|
26
|
-
reconfigure(encoding="utf-8", errors="replace")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def _read_text(path: Path) -> str:
|
|
30
|
-
try:
|
|
31
|
-
return path.read_text(encoding="utf-8").strip()
|
|
32
|
-
except (FileNotFoundError, PermissionError, OSError):
|
|
33
|
-
return ""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def _read_json(path: Path) -> dict:
|
|
37
|
-
text = _read_text(path)
|
|
38
|
-
if not text:
|
|
39
|
-
return {}
|
|
40
|
-
try:
|
|
41
|
-
return json.loads(text)
|
|
42
|
-
except (json.JSONDecodeError, ValueError):
|
|
43
|
-
return {}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def _normalize_task_ref(task_ref: str) -> str:
|
|
47
|
-
normalized = task_ref.strip()
|
|
48
|
-
if not normalized:
|
|
49
|
-
return ""
|
|
50
|
-
|
|
51
|
-
path_obj = Path(normalized)
|
|
52
|
-
if path_obj.is_absolute():
|
|
53
|
-
return str(path_obj)
|
|
54
|
-
|
|
55
|
-
normalized = normalized.replace("\\", "/")
|
|
56
|
-
while normalized.startswith("./"):
|
|
57
|
-
normalized = normalized[2:]
|
|
58
|
-
|
|
59
|
-
if normalized.startswith("tasks/"):
|
|
60
|
-
return f".trellis/{normalized}"
|
|
61
|
-
|
|
62
|
-
return normalized
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def _resolve_task_dir(trellis_dir: Path, task_ref: str) -> Path:
|
|
66
|
-
normalized = _normalize_task_ref(task_ref)
|
|
67
|
-
path_obj = Path(normalized)
|
|
68
|
-
if path_obj.is_absolute():
|
|
69
|
-
return path_obj
|
|
70
|
-
if normalized.startswith(".trellis/"):
|
|
71
|
-
return trellis_dir.parent / path_obj
|
|
72
|
-
return trellis_dir / "tasks" / path_obj
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def _find_trellis_dir() -> Path | None:
|
|
76
|
-
"""Walk up from cwd to find .trellis/ directory."""
|
|
77
|
-
current = Path.cwd()
|
|
78
|
-
for parent in [current, *current.parents]:
|
|
79
|
-
candidate = parent / ".trellis"
|
|
80
|
-
if candidate.is_dir():
|
|
81
|
-
return candidate
|
|
82
|
-
return None
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def _get_current_task(trellis_dir: Path) -> dict | None:
|
|
86
|
-
"""Load current task info. Returns dict with title/status/priority or None."""
|
|
87
|
-
task_ref = _normalize_task_ref(_read_text(trellis_dir / ".current-task"))
|
|
88
|
-
if not task_ref:
|
|
89
|
-
return None
|
|
90
|
-
|
|
91
|
-
# Resolve task directory
|
|
92
|
-
task_path = _resolve_task_dir(trellis_dir, task_ref)
|
|
93
|
-
task_data = _read_json(task_path / "task.json")
|
|
94
|
-
if not task_data:
|
|
95
|
-
return None
|
|
96
|
-
|
|
97
|
-
return {
|
|
98
|
-
"title": task_data.get("title") or task_data.get("name") or "unknown",
|
|
99
|
-
"status": task_data.get("status", "unknown"),
|
|
100
|
-
"priority": task_data.get("priority", "P2"),
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _count_active_tasks(trellis_dir: Path) -> int:
|
|
105
|
-
"""Count non-archived task directories with valid task.json."""
|
|
106
|
-
tasks_dir = trellis_dir / "tasks"
|
|
107
|
-
if not tasks_dir.is_dir():
|
|
108
|
-
return 0
|
|
109
|
-
count = 0
|
|
110
|
-
for d in tasks_dir.iterdir():
|
|
111
|
-
if d.is_dir() and d.name != "archive" and (d / "task.json").is_file():
|
|
112
|
-
count += 1
|
|
113
|
-
return count
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def _get_developer(trellis_dir: Path) -> str:
|
|
117
|
-
content = _read_text(trellis_dir / ".developer")
|
|
118
|
-
if not content:
|
|
119
|
-
return "unknown"
|
|
120
|
-
for line in content.splitlines():
|
|
121
|
-
if line.startswith("name="):
|
|
122
|
-
return line[5:].strip()
|
|
123
|
-
return content.splitlines()[0].strip() or "unknown"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def _get_git_branch() -> str:
|
|
127
|
-
try:
|
|
128
|
-
result = subprocess.run(
|
|
129
|
-
["git", "branch", "--show-current"],
|
|
130
|
-
capture_output=True, text=True, timeout=3,
|
|
131
|
-
)
|
|
132
|
-
return result.stdout.strip() if result.returncode == 0 else ""
|
|
133
|
-
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
134
|
-
return ""
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def _format_ctx_size(size: int) -> str:
|
|
138
|
-
if size >= 1_000_000:
|
|
139
|
-
return f"{size // 1_000_000}M"
|
|
140
|
-
if size >= 1_000:
|
|
141
|
-
return f"{size // 1_000}K"
|
|
142
|
-
return str(size)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def _format_duration(ms: int) -> str:
|
|
146
|
-
secs = ms // 1000
|
|
147
|
-
hours, remainder = divmod(secs, 3600)
|
|
148
|
-
mins = remainder // 60
|
|
149
|
-
if hours > 0:
|
|
150
|
-
return f"{hours}h{mins}m"
|
|
151
|
-
return f"{mins}m"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def main() -> None:
|
|
155
|
-
# Read Claude Code session JSON from stdin
|
|
156
|
-
try:
|
|
157
|
-
cc_data = json.loads(sys.stdin.read())
|
|
158
|
-
except (json.JSONDecodeError, ValueError):
|
|
159
|
-
cc_data = {}
|
|
160
|
-
|
|
161
|
-
trellis_dir = _find_trellis_dir()
|
|
162
|
-
SEP = " \033[90m·\033[0m "
|
|
163
|
-
|
|
164
|
-
# --- Trellis data ---
|
|
165
|
-
task = _get_current_task(trellis_dir) if trellis_dir else None
|
|
166
|
-
dev = _get_developer(trellis_dir) if trellis_dir else ""
|
|
167
|
-
task_count = _count_active_tasks(trellis_dir) if trellis_dir else 0
|
|
168
|
-
|
|
169
|
-
# --- CC session data ---
|
|
170
|
-
model = cc_data.get("model", {}).get("display_name", "?")
|
|
171
|
-
ctx_pct = int(cc_data.get("context_window", {}).get("used_percentage") or 0)
|
|
172
|
-
ctx_size = _format_ctx_size(cc_data.get("context_window", {}).get("context_window_size") or 0)
|
|
173
|
-
duration = _format_duration(cc_data.get("cost", {}).get("total_duration_ms") or 0)
|
|
174
|
-
branch = _get_git_branch()
|
|
175
|
-
|
|
176
|
-
# Avoid "Opus 4.6 (1M context) (1M)"
|
|
177
|
-
if re.search(r"\d+[KMG]\b", model, re.IGNORECASE):
|
|
178
|
-
model_label = model
|
|
179
|
-
else:
|
|
180
|
-
model_label = f"{model} ({ctx_size})"
|
|
181
|
-
|
|
182
|
-
# Context % with color
|
|
183
|
-
if ctx_pct >= 90:
|
|
184
|
-
ctx_color = "\033[31m"
|
|
185
|
-
elif ctx_pct >= 70:
|
|
186
|
-
ctx_color = "\033[33m"
|
|
187
|
-
else:
|
|
188
|
-
ctx_color = "\033[32m"
|
|
189
|
-
|
|
190
|
-
# Build info line: model · ctx · branch · duration · dev · tasks [· rate limits]
|
|
191
|
-
parts = [
|
|
192
|
-
model_label,
|
|
193
|
-
f"ctx {ctx_color}{ctx_pct}%\033[0m",
|
|
194
|
-
]
|
|
195
|
-
if branch:
|
|
196
|
-
parts.append(f"\033[35m{branch}\033[0m")
|
|
197
|
-
parts.append(duration)
|
|
198
|
-
if dev:
|
|
199
|
-
parts.append(f"\033[32m{dev}\033[0m")
|
|
200
|
-
if task_count:
|
|
201
|
-
parts.append(f"{task_count} task(s)")
|
|
202
|
-
|
|
203
|
-
five_hr = cc_data.get("rate_limits", {}).get("five_hour", {}).get("used_percentage")
|
|
204
|
-
if five_hr is not None:
|
|
205
|
-
parts.append(f"5h {int(five_hr)}%")
|
|
206
|
-
seven_day = cc_data.get("rate_limits", {}).get("seven_day", {}).get("used_percentage")
|
|
207
|
-
if seven_day is not None:
|
|
208
|
-
parts.append(f"7d {int(seven_day)}%")
|
|
209
|
-
|
|
210
|
-
info_line = SEP.join(parts)
|
|
211
|
-
|
|
212
|
-
# Output: task line (only if active) + info line
|
|
213
|
-
if task:
|
|
214
|
-
print(f"\033[36m[{task['priority']}]\033[0m {task['title']} \033[33m({task['status']})\033[0m")
|
|
215
|
-
print(info_line)
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if __name__ == "__main__":
|
|
219
|
-
main()
|