create-substrat 0.6.1 → 0.6.2
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
|
@@ -19,8 +19,13 @@
|
|
|
19
19
|
* This file lives in `.substrat/` — the tool-neutral home, next to `playbook.md` —
|
|
20
20
|
* not in `.claude/`. `.claude/settings.json` is a three-line adapter that runs it,
|
|
21
21
|
* and any other client that grows a session hook binds the same way. It also
|
|
22
|
-
* means the plugin distribution (#753)
|
|
23
|
-
* forking it.
|
|
22
|
+
* means the plugin distribution (#753) ships this script unchanged rather than
|
|
23
|
+
* forking it: `plugin/substrat/scripts/session-start.mjs` is emitted from this
|
|
24
|
+
* file byte-for-byte, and `pnpm lint:plugin --check` fails if the two diverge.
|
|
25
|
+
*
|
|
26
|
+
* The plugin copy is what reaches a project scaffolded before this hook existed.
|
|
27
|
+
* When a project owns its own copy, the plugin's stays silent — see
|
|
28
|
+
* `isPluginCopy()` below — so a scaffolded project announces itself once.
|
|
24
29
|
*
|
|
25
30
|
* ## Deliberately silent, and deliberately offline
|
|
26
31
|
*
|
|
@@ -33,8 +38,9 @@
|
|
|
33
38
|
*
|
|
34
39
|
* Opt out by creating `.substrat/no-session-context`.
|
|
35
40
|
*/
|
|
36
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
37
|
-
import { dirname, join } from 'node:path';
|
|
41
|
+
import { existsSync, readFileSync, realpathSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
42
|
+
import { dirname, join, relative, isAbsolute } from 'node:path';
|
|
43
|
+
import { fileURLToPath } from 'node:url';
|
|
38
44
|
|
|
39
45
|
const DOCS = 'https://substrat.net';
|
|
40
46
|
const KERNEL = '@substrat-run/kernel';
|
|
@@ -42,6 +48,33 @@ const KERNEL = '@substrat-run/kernel';
|
|
|
42
48
|
/** Where the project is. Claude Code sets this; fall back to the cwd it ran us in. */
|
|
43
49
|
const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
44
50
|
|
|
51
|
+
/** Where this script is. The two copies differ only in where they sit on disk. */
|
|
52
|
+
const self = fileURLToPath(import.meta.url);
|
|
53
|
+
|
|
54
|
+
/** Symlinks resolved, so a project reached through one is not mistaken for elsewhere. */
|
|
55
|
+
function real(path) {
|
|
56
|
+
try {
|
|
57
|
+
return realpathSync(path);
|
|
58
|
+
} catch {
|
|
59
|
+
return path;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* True when we are the plugin's copy rather than the project's own.
|
|
65
|
+
*
|
|
66
|
+
* Asked positionally instead of by a flag, because the two copies must stay
|
|
67
|
+
* byte-identical for `lint:plugin` to have anything to check: a copy that is
|
|
68
|
+
* invoked differently is a copy that can be edited differently. Both sides are
|
|
69
|
+
* resolved first — on macOS a project under `/tmp` is handed to us as
|
|
70
|
+
* `/private/tmp`, and an unresolved compare would read the project's own copy as
|
|
71
|
+
* foreign and silence the very hook that should speak.
|
|
72
|
+
*/
|
|
73
|
+
function isPluginCopy() {
|
|
74
|
+
const inside = relative(real(root), real(self));
|
|
75
|
+
return inside.startsWith('..') || isAbsolute(inside);
|
|
76
|
+
}
|
|
77
|
+
|
|
45
78
|
/** The marker: which kernel version this project last announced. */
|
|
46
79
|
const MARKER = join(root, '.substrat', '.docs-pin');
|
|
47
80
|
const OPT_OUT = join(root, '.substrat', 'no-session-context');
|
|
@@ -67,6 +100,11 @@ function main() {
|
|
|
67
100
|
// file to invent, and nothing to keep in sync.
|
|
68
101
|
if (!pkg?.substrat) silent();
|
|
69
102
|
|
|
103
|
+
// The project's own copy wins. It ships beside the playbook it points at, so it
|
|
104
|
+
// is the one that matches what is actually checked out here; the plugin's exists
|
|
105
|
+
// for projects scaffolded before this hook did, and must not double-announce.
|
|
106
|
+
if (isPluginCopy() && existsSync(join(root, '.substrat', 'hooks', 'session-start.mjs'))) silent();
|
|
107
|
+
|
|
70
108
|
if (existsSync(OPT_OUT)) silent();
|
|
71
109
|
|
|
72
110
|
// What is actually installed beats what package.json asked for: a caret range on
|