@litfamily/litclaude 1.0.5 → 1.0.7
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 +9 -5
- package/README.md +11 -38
- package/README_ko-KR.md +11 -37
- package/docs/assets/cover-motion.webp +0 -0
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/privacy.md +12 -17
- package/package.json +3 -1
- package/plugins/litclaude/.claude-plugin/plugin.json +1 -1
- package/plugins/litclaude/bin/litclaude-hook.js +23 -60
- package/plugins/litclaude/bin/litclaude-mcp.js +1 -1
- package/plugins/litclaude/hooks/hooks.json +4 -4
- package/plugins/litclaude/lib/canonical-skill-catalog.mjs +0 -1
- package/plugins/litclaude/lib/canonical-skill-resources.mjs +5 -27
- package/plugins/litclaude/lib/lit-plan-persistence.mjs +2 -1
- package/plugins/litclaude/lib/litgoal/paths.mjs +10 -8
- package/plugins/litclaude/lib/project-state-root.mjs +22 -0
- package/plugins/litclaude/lib/rules/session-state.mjs +2 -1
- package/plugins/litclaude/lib/start-work-cli.mjs +3 -3
- package/plugins/litclaude/lib/wikify-knowledge-cli.mjs +3 -1
- package/plugins/litclaude/lib/wikify-knowledge.mjs +6 -5
- package/plugins/litclaude/scripts/scaffold-plan.mjs +9 -3
- package/plugins/litclaude/skills/lit-team/scripts/team.mjs +4 -3
- package/scripts/prepublish-test-gate.mjs +26 -0
- package/plugins/litclaude/lib/skill-loop/apply.mjs +0 -391
- package/plugins/litclaude/lib/skill-loop/authority.mjs +0 -129
- package/plugins/litclaude/lib/skill-loop/cli.mjs +0 -67
- package/plugins/litclaude/lib/skill-loop/common.mjs +0 -280
- package/plugins/litclaude/lib/skill-loop/config.mjs +0 -91
- package/plugins/litclaude/lib/skill-loop/curator.mjs +0 -670
- package/plugins/litclaude/lib/skill-loop/hook-runtime.mjs +0 -188
- package/plugins/litclaude/lib/skill-loop/hook-state.mjs +0 -484
- package/plugins/litclaude/lib/skill-loop/ledger.mjs +0 -834
- package/plugins/litclaude/lib/skill-loop/mutation-transaction.mjs +0 -529
- package/plugins/litclaude/lib/skill-loop/proposal-reader.mjs +0 -26
- package/plugins/litclaude/lib/skill-loop/proposal-record.mjs +0 -104
- package/plugins/litclaude/lib/skill-loop/proposal-store.mjs +0 -187
- package/plugins/litclaude/lib/skill-loop/proposals.mjs +0 -26
- package/plugins/litclaude/lib/skill-loop/review-runner.mjs +0 -13
- package/plugins/litclaude/lib/skill-loop/review-transaction.mjs +0 -358
- package/plugins/litclaude/lib/skill-loop/review.mjs +0 -223
- package/plugins/litclaude/lib/skill-loop/usage.mjs +0 -218
- package/plugins/litclaude/lib/skill-observer.mjs +0 -693
- package/plugins/litclaude/skills/skill-observer/SKILL.md +0 -155
- package/plugins/litclaude/skills/skill-observer/references/review-contract.md +0 -77
- package/plugins/litclaude/skills/skill-observer/scripts/skill-loop.mjs +0 -10
|
@@ -22,32 +22,13 @@ export const canonicalSkillResourceManifest = Object.freeze(
|
|
|
22
22
|
["lib/rules/matcher.mjs", "83334358a035a9fedf3d7970b2454825d4ca8d0082b5a8779efc8ad8fdeecd5b"],
|
|
23
23
|
["lib/rules/ordering.mjs", "c6da1ef0b228b8f0932b54422d2bc9744eb6bfbb8f5bb70b30b8f1a798765d1c"],
|
|
24
24
|
["lib/rules/scanner.mjs", "c62d672a66af25f29fe1d05b09288583425a2bf55f20d788f81d1578035f79f9"],
|
|
25
|
-
["lib/rules/session-state.mjs", "
|
|
25
|
+
["lib/rules/session-state.mjs", "ad660cadf147b102437da95a771bae19ee2140bf39c58915b8cb498e8d04f2af"],
|
|
26
26
|
["lib/secret-shapes.mjs", "73733bb5927d6d1e00c529acc847b79d8664d10f94100bcc7452824615ab87ba"],
|
|
27
27
|
["lib/secure-path-read.mjs", "c2991d0279f7399c3022c78babe28e653260f2a2e9ff4a5df692883ccf8807fd"],
|
|
28
|
-
["lib/skill-loop/apply.mjs", "c00aa7a40d76cb5390ac7c13af4a2eda4ff8d090f10c2a4f1e4a72ac158d3ac9"],
|
|
29
|
-
["lib/skill-loop/authority.mjs", "41911124a5141480538bbd9887c4d5cec2ea38552ed03c05bd3d3412f0c11b1d"],
|
|
30
|
-
["lib/skill-loop/cli.mjs", "333135a39c36e7eab6b10d5a3859dce68ba98b7c83bbf36144d53b19f4bdc1f4"],
|
|
31
|
-
["lib/skill-loop/common.mjs", "c65eec46eaad369b989337876960dcba9a0f25f40320862dc1c4a298a7e7a730"],
|
|
32
|
-
["lib/skill-loop/config.mjs", "833e73f74ffdf43068a95aa6c50dc29e9080b0549871bdc0a5669a01c752cba6"],
|
|
33
|
-
["lib/skill-loop/curator.mjs", "6776eb46fb0c1f79a2a217f495b2195e54cac044f61dfe9298447c9b2bd2de76"],
|
|
34
|
-
["lib/skill-loop/hook-runtime.mjs", "34af32eb44e04aed2f452dcc01556f4f113b9e4896bcdb633402c4e273763006"],
|
|
35
|
-
["lib/skill-loop/hook-state.mjs", "cbe544247069ee004acb389f70ef7d9f25f0afd8a5df9c5567c365bf47989938"],
|
|
36
|
-
["lib/skill-loop/ledger.mjs", "5367b62c21f8bad2e8efb34caa9e2a5c79de85d9d762d7a583cc66ea9da325e5"],
|
|
37
|
-
["lib/skill-loop/mutation-transaction.mjs", "485b871b3cc5e8c3234df9bf0a84f240fddc4f91dbff94607d1e046248bf33f9"],
|
|
38
|
-
["lib/skill-loop/proposal-reader.mjs", "b979d2a848eef091fabb001ce2e8a8a4fd8dc6e16e874add0f34dc99b3575af4"],
|
|
39
|
-
["lib/skill-loop/proposal-record.mjs", "352888ab9d0b7235414ca9ce068c119fb2230d1abe46bcff602d55feff0552bc"],
|
|
40
|
-
["lib/skill-loop/proposal-store.mjs", "07e06e035a8fd5b58baf41fd6df244f120bb14a857ea6b98f0d71f362dd4c358"],
|
|
41
|
-
["lib/skill-loop/proposals.mjs", "bc0a880b149c8293065d458eaf464ba79e08da93b1912c695239af8cbb8cedec"],
|
|
42
|
-
["lib/skill-loop/review-runner.mjs", "fcd4f1c122b3c50934358c376cda544808c0286793e1e882fee72277258d4c7c"],
|
|
43
|
-
["lib/skill-loop/review-transaction.mjs", "a74f21f6d24014d1ade5c6bf760d7452ed491df4471f7d14a8ef3423028db4c3"],
|
|
44
|
-
["lib/skill-loop/review.mjs", "90118466b694bc56aa605e4eeb1fbd34ce57367c4075adbb52d2ec2f2182bc45"],
|
|
45
|
-
["lib/skill-loop/usage.mjs", "9a80b2979014c46bdd9527140ef193abacd3a1b4eafd458f32315d5902aa2b99"],
|
|
46
|
-
["lib/skill-observer.mjs", "13466c511a870cb4809e327c8a9181cdba17da9883ef05995f6ca8c698a925b6"],
|
|
47
28
|
["lib/strict-json.mjs", "d941f327aabce5ffc212770852cfbc7e08dd03471e4e9bc9560eff54f06d489a"],
|
|
48
|
-
["lib/wikify-knowledge-cli.mjs", "
|
|
49
|
-
["lib/wikify-knowledge.mjs", "
|
|
50
|
-
["scripts/scaffold-plan.mjs", "
|
|
29
|
+
["lib/wikify-knowledge-cli.mjs", "6835f8d6d98a3dd26266409366c23b2da437c0cd41e63537f0f200667655132b"],
|
|
30
|
+
["lib/wikify-knowledge.mjs", "00dc8509d007484105dab87c1562d1657ef0f01ac8840a7d2f18898768d375b3"],
|
|
31
|
+
["scripts/scaffold-plan.mjs", "b171ffccdaff8b9a586fe65590a154eeb663e7359fd34b6ee0e942d452381d2a"],
|
|
51
32
|
["skills/autoconference/SKILL.md", "3731208dc3838d4d2916fb01da0c8cb63cca36342c2ffb5770770b3332d06cd3"],
|
|
52
33
|
["skills/autoresearch/SKILL.md", "2b69c53bade3851cfb9c770bce80afe5f9e75b9629719e9ba95a4b0045eaf5b3"],
|
|
53
34
|
["skills/browser-drive/scripts/capability-probe.mjs", "e767912bc034eaf9081256d8aa2f2bf63c7d9d29e39cb5e56f390e872df794f7"],
|
|
@@ -187,12 +168,9 @@ export const canonicalSkillResourceManifest = Object.freeze(
|
|
|
187
168
|
["skills/lit-comprehend/references/micro-worlds.md", "92fc1401fc14c5bcbbeded00ff45819aacd2ead50b86f8a0f45985d19461bfa4"],
|
|
188
169
|
["skills/lit-comprehend/scripts/verify-explainer.mjs", "837a6fe18e2fea8ed015c8e024b36f9f7e2ebb7f6abe433fc69b1db60a152994"],
|
|
189
170
|
["skills/lit-team/SKILL.md", "6d4ca726d1240db7692ccc95802ab6abfd45588147ec663dafa1b3b233a08a13"],
|
|
190
|
-
["skills/lit-team/scripts/team.mjs", "
|
|
171
|
+
["skills/lit-team/scripts/team.mjs", "a0be61f09cc1c10989b12709135ac4b99260496f840dcf58514478082e239fe7"],
|
|
191
172
|
["skills/litresearch/ATTRIBUTION.md", "1ed8b43d48a13d7fc106cbfd86bc1bac0316529bdac4e2e38043fafeae2a8e15"],
|
|
192
173
|
["skills/litwork/SKILL.md", "18d91a20d882d72f9795cfeb8d46a3e07c773d66853d51aa965cdcec5513bea3"],
|
|
193
|
-
["skills/skill-observer/SKILL.md", "a67b72d877c45f519711f59e285302e1c1c8afbdda85009df07f1cf30a1a5d57"],
|
|
194
|
-
["skills/skill-observer/references/review-contract.md", "f55dc8334f3b8d8498c84d57725fa03af8fbd689b2dd7cce25e49931629ee54f"],
|
|
195
|
-
["skills/skill-observer/scripts/skill-loop.mjs", "36915455a8f22a03975108d48dea83e8f6aa827936f9fdac3c09eca3fec9072d"],
|
|
196
174
|
["skills/visual-qa/SKILL.md", "089d26a14800b461cfd628b4789f676308d5e09fe13e9e24632b8fd6305c8517"],
|
|
197
175
|
["skills/visual-qa/references/capture-playbook.md", "92cad16893118c9e878cabbb49dc2187049ec6e759fcc4e6006d79bdae998689"],
|
|
198
176
|
["skills/visual-qa/references/complete-contract.md", "8dd878975eb386b8b5d0b5439ed7bc50de9c4e3ee2aeff6d553c4299c5b30cb4"],
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { join, resolve } from "node:path";
|
|
8
8
|
|
|
9
|
+
import { resolveProjectStateRoot } from "./project-state-root.mjs";
|
|
9
10
|
import { findProjectRoot } from "./rules/discovery.mjs";
|
|
10
11
|
|
|
11
12
|
export const LIT_PLAN_MAX_BLOCKS = 2;
|
|
@@ -15,7 +16,7 @@ export const LIT_PLAN_BLOCK_REASON = "lit-plan must persist plans/<slug>.md (run
|
|
|
15
16
|
const MTIME_TOLERANCE_MS = 1000;
|
|
16
17
|
const TASK_ROW = /^- \[ \] \d+\./mu;
|
|
17
18
|
|
|
18
|
-
export const litPlanTurnPath = (cwd =
|
|
19
|
+
export const litPlanTurnPath = (cwd = resolveProjectStateRoot()) => join(resolveProjectStateRoot(cwd), ".litclaude", "lit-plan", "turn.json");
|
|
19
20
|
|
|
20
21
|
const readTurn = (cwd) => {
|
|
21
22
|
const path = litPlanTurnPath(cwd);
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { join
|
|
1
|
+
import { join } from "node:path";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export const
|
|
6
|
-
export const
|
|
7
|
-
export const
|
|
8
|
-
export const
|
|
3
|
+
import { resolveProjectStateRoot } from "../project-state-root.mjs";
|
|
4
|
+
|
|
5
|
+
export const litgoalStateDir = (cwd = resolveProjectStateRoot()) => join(resolveProjectStateRoot(cwd), ".litclaude", "litgoal");
|
|
6
|
+
export const litgoalBriefPath = (cwd = resolveProjectStateRoot()) => join(litgoalStateDir(cwd), "brief.md");
|
|
7
|
+
export const litgoalGoalsPath = (cwd = resolveProjectStateRoot()) => join(litgoalStateDir(cwd), "goals.json");
|
|
8
|
+
export const litgoalLedgerPath = (cwd = resolveProjectStateRoot()) => join(litgoalStateDir(cwd), "ledger.jsonl");
|
|
9
|
+
export const litgoalLockDir = (cwd = resolveProjectStateRoot()) => join(litgoalStateDir(cwd), ".lock");
|
|
10
|
+
export const litgoalSessionsDir = (cwd = resolveProjectStateRoot()) => join(litgoalStateDir(cwd), "sessions");
|
|
9
11
|
// Where a completed goal is preserved when a fresh session opens new state (drift 6a).
|
|
10
12
|
// The session id is filename-sanitized because it arrives from the host.
|
|
11
|
-
export const litgoalSessionArchivePath = (cwd =
|
|
13
|
+
export const litgoalSessionArchivePath = (cwd = resolveProjectStateRoot(), sessionId = "unknown") =>
|
|
12
14
|
join(litgoalSessionsDir(cwd), `${String(sessionId).replace(/[^A-Za-z0-9_-]/gu, "").slice(0, 64) || "unknown"}.json`);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { findProjectRoot } from "./rules/discovery.mjs";
|
|
5
|
+
|
|
6
|
+
const outermostGitRoot = (startDir) => {
|
|
7
|
+
let root = null;
|
|
8
|
+
let current = startDir;
|
|
9
|
+
for (;;) {
|
|
10
|
+
if (existsSync(join(current, ".git"))) root = current;
|
|
11
|
+
const parent = dirname(current);
|
|
12
|
+
if (parent === current) return root;
|
|
13
|
+
current = parent;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// State placement is intentionally separate from nearest-marker rule discovery: a nested
|
|
18
|
+
// package.json is a valid rules boundary, but .litclaude belongs at the outer Git root.
|
|
19
|
+
export const resolveProjectStateRoot = (startDir = process.cwd()) => {
|
|
20
|
+
const start = resolve(startDir);
|
|
21
|
+
return outermostGitRoot(start) ?? findProjectRoot(start) ?? start;
|
|
22
|
+
};
|
|
@@ -23,6 +23,7 @@ import { isAbsolute, join, relative, resolve } from "node:path";
|
|
|
23
23
|
import { acquireOwnerLock, OwnerLockError, releaseOwnerLock } from "../owner-lock.mjs";
|
|
24
24
|
import { POST_COMPACT_REINJECTION_BUDGET } from "./constants.mjs";
|
|
25
25
|
import { pathIdentity, samePathIdentity } from "../secure-path-read.mjs";
|
|
26
|
+
import { resolveProjectStateRoot } from "../project-state-root.mjs";
|
|
26
27
|
|
|
27
28
|
const MAX_TRACKED_KEYS = 500;
|
|
28
29
|
const MAX_STATE_BYTES = 256 * 1024;
|
|
@@ -42,7 +43,7 @@ const isSameOrChildPath = (parentPath, childPath) => {
|
|
|
42
43
|
const emptyState = () => ({ injected: [], postCompactCount: 0 });
|
|
43
44
|
|
|
44
45
|
const safeStateDirectory = (projectRoot, { create }) => {
|
|
45
|
-
const root =
|
|
46
|
+
const root = resolveProjectStateRoot(projectRoot);
|
|
46
47
|
const rootStats = lstatSync(root);
|
|
47
48
|
if (rootStats.isSymbolicLink() || !rootStats.isDirectory()) throw new Error("unsafe project root");
|
|
48
49
|
const realRoot = realpathSync.native(root);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveProjectStateRoot } from "./project-state-root.mjs";
|
|
2
2
|
import {
|
|
3
3
|
cancelStartWork,
|
|
4
4
|
completeStartWork,
|
|
@@ -141,7 +141,7 @@ const emitError = (io, error, command) => {
|
|
|
141
141
|
return status;
|
|
142
142
|
};
|
|
143
143
|
|
|
144
|
-
export const runStartWorkCli = (argv, io = { stdout: process.stdout, stderr: process.stderr }, defaultRoot =
|
|
144
|
+
export const runStartWorkCli = (argv, io = { stdout: process.stdout, stderr: process.stderr }, defaultRoot = resolveProjectStateRoot()) => {
|
|
145
145
|
const [command, ...args] = argv;
|
|
146
146
|
if (!command || command === "--help" || command === "-h") {
|
|
147
147
|
io.stdout.write(helpText);
|
|
@@ -149,7 +149,7 @@ export const runStartWorkCli = (argv, io = { stdout: process.stdout, stderr: pro
|
|
|
149
149
|
}
|
|
150
150
|
try {
|
|
151
151
|
assertExactOptions(command, args);
|
|
152
|
-
const root =
|
|
152
|
+
const root = resolveProjectStateRoot(value(args, "--root") ?? defaultRoot);
|
|
153
153
|
const result = execute(root, command, args);
|
|
154
154
|
io.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
155
155
|
return 0;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
+
|
|
3
|
+
import { resolveProjectStateRoot } from "./project-state-root.mjs";
|
|
2
4
|
import {
|
|
3
5
|
captureKnowledgeEvent,
|
|
4
6
|
queryKnowledge,
|
|
@@ -116,7 +118,7 @@ const emitError = (io, error, command, outputJson) => {
|
|
|
116
118
|
export const runWikifyKnowledgeCli = (
|
|
117
119
|
argv,
|
|
118
120
|
io = { stdout: process.stdout, stderr: process.stderr },
|
|
119
|
-
defaultRoot =
|
|
121
|
+
defaultRoot = resolveProjectStateRoot(),
|
|
120
122
|
) => {
|
|
121
123
|
const [command, ...args] = argv;
|
|
122
124
|
if (!command || command === "--help" || command === "-h") {
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from "./secure-path-read.mjs";
|
|
26
26
|
import { decodeStrictUtf8, parseStrictJson } from "./strict-json.mjs";
|
|
27
27
|
import { containsSecret, URI_USERINFO_PATTERN } from "./secret-shapes.mjs";
|
|
28
|
+
import { resolveProjectStateRoot } from "./project-state-root.mjs";
|
|
28
29
|
|
|
29
30
|
const MAX_LEDGER_BYTES = 4 * 1024 * 1024;
|
|
30
31
|
const MAX_TEXT_BYTES = 512;
|
|
@@ -116,7 +117,7 @@ const deterministicId = (kind, text, provenance, evidence) => {
|
|
|
116
117
|
};
|
|
117
118
|
|
|
118
119
|
const canonicalRoot = (root) => {
|
|
119
|
-
const candidate =
|
|
120
|
+
const candidate = resolveProjectStateRoot(root ?? process.cwd());
|
|
120
121
|
try {
|
|
121
122
|
const details = lstatSync(candidate);
|
|
122
123
|
if (details.isSymbolicLink() || !details.isDirectory()) throw new Error("invalid root");
|
|
@@ -137,7 +138,7 @@ const ensurePrivateDirectory = (path) => {
|
|
|
137
138
|
mkdirSync(path, { mode: 0o700 });
|
|
138
139
|
};
|
|
139
140
|
|
|
140
|
-
export const knowledgeDirectory = (root =
|
|
141
|
+
export const knowledgeDirectory = (root = resolveProjectStateRoot()) => {
|
|
141
142
|
const canonical = canonicalRoot(root);
|
|
142
143
|
const litDirectory = join(canonical, ".litclaude");
|
|
143
144
|
const directory = join(litDirectory, "knowledge");
|
|
@@ -150,8 +151,8 @@ export const knowledgeDirectory = (root = process.cwd()) => {
|
|
|
150
151
|
}
|
|
151
152
|
return directory;
|
|
152
153
|
};
|
|
153
|
-
export const knowledgeClaimsPath = (root =
|
|
154
|
-
export const knowledgeSettingsPath = (root =
|
|
154
|
+
export const knowledgeClaimsPath = (root = resolveProjectStateRoot()) => join(knowledgeDirectory(root), "claims.jsonl");
|
|
155
|
+
export const knowledgeSettingsPath = (root = resolveProjectStateRoot()) => join(knowledgeDirectory(root), "settings.json");
|
|
155
156
|
|
|
156
157
|
const prepareKnowledgeDirectory = (root) => {
|
|
157
158
|
const canonical = canonicalRoot(root);
|
|
@@ -510,7 +511,7 @@ const normalizeEvent = (event, surface) => {
|
|
|
510
511
|
};
|
|
511
512
|
};
|
|
512
513
|
|
|
513
|
-
export const readCaptureSettings = (root =
|
|
514
|
+
export const readCaptureSettings = (root = resolveProjectStateRoot()) => {
|
|
514
515
|
return withClaimsLock(root, (directory) => {
|
|
515
516
|
const path = join(directory, "settings.json");
|
|
516
517
|
const snapshot = assertRegularStateFile(path);
|
|
@@ -30,6 +30,7 @@ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
|
30
30
|
import { fileURLToPath } from "node:url";
|
|
31
31
|
|
|
32
32
|
import { parsePlanTaskRows, planLinesOutsideFences } from "../lib/plan-task-rows.mjs";
|
|
33
|
+
import { resolveProjectStateRoot } from "../lib/project-state-root.mjs";
|
|
33
34
|
import { readRegularStable, samePathIdentity } from "../lib/secure-path-read.mjs";
|
|
34
35
|
|
|
35
36
|
/** Canonical plan section headers, in order. */
|
|
@@ -107,8 +108,9 @@ export function parseArgs(argv) {
|
|
|
107
108
|
|
|
108
109
|
/** Confine a write to `.litclaude/drafts/` or `plans/`, `.md` only. */
|
|
109
110
|
export function resolveSafePlanPath(cwd, relPath) {
|
|
110
|
-
const
|
|
111
|
-
const
|
|
111
|
+
const workspaceRoot = resolveProjectStateRoot(cwd);
|
|
112
|
+
const resolved = resolve(workspaceRoot, relPath);
|
|
113
|
+
const rel = relative(workspaceRoot, resolved);
|
|
112
114
|
if (rel.startsWith("..") || isAbsolute(rel)) {
|
|
113
115
|
throw new Error(`refused: path escapes the workspace root: ${relPath}`);
|
|
114
116
|
}
|
|
@@ -804,6 +806,7 @@ export async function writeGuarded(
|
|
|
804
806
|
onParentState,
|
|
805
807
|
} = {},
|
|
806
808
|
) {
|
|
809
|
+
cwd = resolveProjectStateRoot(cwd);
|
|
807
810
|
const target = resolveSafePlanPath(cwd, relPath);
|
|
808
811
|
const ownInterruption = interruption ?? createInterruptionContext();
|
|
809
812
|
let parentState;
|
|
@@ -874,6 +877,7 @@ export async function writeGuarded(
|
|
|
874
877
|
}
|
|
875
878
|
|
|
876
879
|
export async function scaffold(cwd, { slug, intent, reset = false, force = false, draftOnly = false, reviewRequired = false }) {
|
|
880
|
+
cwd = resolveProjectStateRoot(cwd);
|
|
877
881
|
const interruption = createInterruptionContext();
|
|
878
882
|
const created = [];
|
|
879
883
|
const parentStates = new Set();
|
|
@@ -1007,6 +1011,7 @@ export function checkPlanStructure(text) {
|
|
|
1007
1011
|
}
|
|
1008
1012
|
|
|
1009
1013
|
async function runCheck(checkPath, cwd) {
|
|
1014
|
+
cwd = resolveProjectStateRoot(cwd);
|
|
1010
1015
|
const target = resolveSafePlanPath(cwd, checkPath);
|
|
1011
1016
|
const read = readRegularStable(resolve(cwd), target, undefined, { maxBytes: MAX_PLAN_BYTES });
|
|
1012
1017
|
if (read.failure) {
|
|
@@ -1023,7 +1028,8 @@ async function runCheck(checkPath, cwd) {
|
|
|
1023
1028
|
return 1;
|
|
1024
1029
|
}
|
|
1025
1030
|
|
|
1026
|
-
export async function runScaffoldPlanCli(argv = process.argv, cwd =
|
|
1031
|
+
export async function runScaffoldPlanCli(argv = process.argv, cwd = resolveProjectStateRoot()) {
|
|
1032
|
+
cwd = resolveProjectStateRoot(cwd);
|
|
1027
1033
|
const parsed = parseArgs(argv);
|
|
1028
1034
|
if (parsed.mode === "check") {
|
|
1029
1035
|
return runCheck(parsed.checkPath, cwd);
|
|
@@ -38,6 +38,7 @@ import { isAbsolute, join, relative, resolve } from "node:path";
|
|
|
38
38
|
import { pathToFileURL } from "node:url";
|
|
39
39
|
|
|
40
40
|
import { acquireOwnerLock, OwnerLockError, releaseOwnerLock } from "../../../lib/owner-lock.mjs";
|
|
41
|
+
import { resolveProjectStateRoot } from "../../../lib/project-state-root.mjs";
|
|
41
42
|
import { pathIdentity, samePathIdentity } from "../../../lib/secure-path-read.mjs";
|
|
42
43
|
|
|
43
44
|
const MIN_MEMBERS = 2;
|
|
@@ -61,7 +62,7 @@ const requireTeamId = (teamId) => {
|
|
|
61
62
|
return teamId;
|
|
62
63
|
};
|
|
63
64
|
|
|
64
|
-
export const teamsRoot = (cwd =
|
|
65
|
+
export const teamsRoot = (cwd = resolveProjectStateRoot()) => join(resolveProjectStateRoot(cwd), ".litclaude", "teams");
|
|
65
66
|
export const teamDir = (cwd, teamId) => join(teamsRoot(cwd), requireTeamId(teamId));
|
|
66
67
|
export const teamStatePath = (cwd, teamId) => join(teamDir(cwd, teamId), "team.json");
|
|
67
68
|
|
|
@@ -87,7 +88,7 @@ const isSameOrChildPath = (parentPath, childPath) => {
|
|
|
87
88
|
};
|
|
88
89
|
|
|
89
90
|
const safeDirectory = (cwd, components, { create }) => {
|
|
90
|
-
const root =
|
|
91
|
+
const root = resolveProjectStateRoot(cwd);
|
|
91
92
|
const rootStats = lstatSync(root);
|
|
92
93
|
if (rootStats.isSymbolicLink() || !rootStats.isDirectory()) throw new TeamError("workspace root is not a safe directory", 65);
|
|
93
94
|
const realRoot = realpathSync.native(root);
|
|
@@ -379,7 +380,7 @@ const archive = (cwd, args) => {
|
|
|
379
380
|
|
|
380
381
|
const commands = { init, "add-member": addMember, prompt, report, block, status, archive };
|
|
381
382
|
|
|
382
|
-
export const runTeamCli = (argv, io = { stdout: process.stdout, stderr: process.stderr }, cwd =
|
|
383
|
+
export const runTeamCli = (argv, io = { stdout: process.stdout, stderr: process.stderr }, cwd = resolveProjectStateRoot()) => {
|
|
383
384
|
const [command, ...rest] = argv;
|
|
384
385
|
if (!command || command === "--help" || command === "-h") {
|
|
385
386
|
io.stdout.write(`Usage: team.mjs <${Object.keys(commands).join("|")}> [...args]\n`);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
const env = Object.fromEntries(
|
|
4
|
+
Object.entries(process.env).filter(([key]) => key.toLowerCase() !== "npm_config_dry_run"),
|
|
5
|
+
);
|
|
6
|
+
const npmExecPath = process.env.npm_execpath;
|
|
7
|
+
const result = npmExecPath
|
|
8
|
+
? spawnSync(process.execPath, [npmExecPath, "test"], { env, stdio: "inherit" })
|
|
9
|
+
: spawnSync(process.platform === "win32" ? "npm.cmd" : "npm", ["test"], {
|
|
10
|
+
env,
|
|
11
|
+
shell: process.platform === "win32",
|
|
12
|
+
stdio: "inherit",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
if (result.error) {
|
|
16
|
+
console.error(`prepublish test gate could not start npm test: ${result.error.message}`);
|
|
17
|
+
process.exitCode = 1;
|
|
18
|
+
} else if (result.signal) {
|
|
19
|
+
console.error(`prepublish test gate interrupted by ${result.signal}`);
|
|
20
|
+
process.exitCode = 1;
|
|
21
|
+
} else if (Number.isInteger(result.status)) {
|
|
22
|
+
process.exitCode = result.status;
|
|
23
|
+
} else {
|
|
24
|
+
console.error("prepublish test gate received a non-integer npm test exit status");
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
}
|