litcodex-ai 0.3.21 → 0.3.22
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/node_modules/@litcodex/lit-loop/directives/init-deep.md +10 -0
- package/node_modules/@litcodex/lit-loop/dist/codex-hook.js +2 -2
- package/node_modules/@litcodex/lit-loop/dist/directive.d.ts +6 -0
- package/node_modules/@litcodex/lit-loop/dist/directive.js +34 -0
- package/node_modules/@litcodex/lit-loop/dist/markers.d.ts +2 -0
- package/node_modules/@litcodex/lit-loop/dist/markers.js +2 -0
- package/node_modules/@litcodex/lit-loop/dist/modes.d.ts +5 -1
- package/node_modules/@litcodex/lit-loop/dist/modes.js +27 -1
- package/node_modules/@litcodex/lit-loop/dist/skill-mention-scope.d.ts +1 -0
- package/node_modules/@litcodex/lit-loop/dist/skill-mention-scope.js +16 -5
- package/node_modules/@litcodex/lit-loop/dist/trigger.d.ts +2 -2
- package/node_modules/@litcodex/lit-loop/dist/trigger.js +12 -13
- package/node_modules/@litcodex/lit-loop/package.json +1 -1
- package/package.json +29 -8
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<init-deep-mode>
|
|
2
|
+
|
|
3
|
+
**MANDATORY**: First user-visible line this turn MUST be exactly:
|
|
4
|
+
`🔥 INIT-DEEP ENABLED 🔥`
|
|
5
|
+
|
|
6
|
+
You are in init-deep: create or refresh sparse, evidence-backed repository guidance.
|
|
7
|
+
Read existing local instructions and the real directory structure before proposing edits.
|
|
8
|
+
Preserve secrets boundaries, ignored runtime state, and unrelated user changes.
|
|
9
|
+
|
|
10
|
+
</init-deep-mode>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// On activation the payload is the trusted bundled directive, loaded behind a FAIL-SILENT boundary:
|
|
18
18
|
// a directive-load error resolves to "" (→ noop), so a missing dist/directive.md degrades the hook
|
|
19
19
|
// to a silent no-op + exit 0 rather than throwing into the Codex host.
|
|
20
|
-
import {
|
|
20
|
+
import { loadDirectiveWithSkillBodyFrom } from "./directive.js";
|
|
21
21
|
import { shouldSuppressInjection } from "./guards.js";
|
|
22
22
|
import { modeForToken } from "./modes.js";
|
|
23
23
|
import { matchLitTrigger } from "./trigger.js";
|
|
@@ -125,7 +125,7 @@ export function applyPreToolUseCreateGoalGuard(input) {
|
|
|
125
125
|
*/
|
|
126
126
|
function loadDirectiveForModeFailSilent(mode) {
|
|
127
127
|
try {
|
|
128
|
-
return
|
|
128
|
+
return loadDirectiveWithSkillBodyFrom(mode.directivePath, mode.openMarker, mode.closeMarker, mode.skillName, mode.skillPath);
|
|
129
129
|
}
|
|
130
130
|
catch {
|
|
131
131
|
return "";
|
|
@@ -14,6 +14,12 @@ export declare class LitLoopDirectiveError extends Error {
|
|
|
14
14
|
* Normalization: CRLF→LF, lone CR→LF, then trim(). Fail-loud with a typed error (codes are generic).
|
|
15
15
|
*/
|
|
16
16
|
export declare function loadDirectiveFrom(resolvedPath: string, openMarker: string, closeMarker: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Load a mode directive and splice the matching bundled SKILL.md body inside the same wrapper.
|
|
19
|
+
* This keeps the small host-adapted directive as the safety envelope while satisfying the
|
|
20
|
+
* bare-invocation contract: the model receives the full installed skill text for the selected mode.
|
|
21
|
+
*/
|
|
22
|
+
export declare function loadDirectiveWithSkillBodyFrom(resolvedPath: string, openMarker: string, closeMarker: string, skillName: string, skillPath: string): string;
|
|
17
23
|
/**
|
|
18
24
|
* Read + normalize + validate the lit-loop directive at an explicit path. Test-only seam (S10
|
|
19
25
|
* §Test plan) so corrupt / CRLF / missing-file cases can be exercised without swapping the bundled
|
|
@@ -55,6 +55,40 @@ export function loadDirectiveFrom(resolvedPath, openMarker, closeMarker) {
|
|
|
55
55
|
}
|
|
56
56
|
return normalized;
|
|
57
57
|
}
|
|
58
|
+
function readNormalizedBody(resolvedPath, label) {
|
|
59
|
+
let raw;
|
|
60
|
+
try {
|
|
61
|
+
raw = readFileSync(resolvedPath, "utf8");
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
65
|
+
throw new LitLoopDirectiveError("LIT_LOOP_DIRECTIVE_UNREADABLE", `${label}: unreadable at ${resolvedPath}: ${detail}`, resolvedPath);
|
|
66
|
+
}
|
|
67
|
+
const normalized = raw.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
|
|
68
|
+
if (normalized.length === 0) {
|
|
69
|
+
throw new LitLoopDirectiveError("LIT_LOOP_DIRECTIVE_EMPTY", `${label}: empty after normalization at ${resolvedPath}`, resolvedPath);
|
|
70
|
+
}
|
|
71
|
+
return normalized;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Load a mode directive and splice the matching bundled SKILL.md body inside the same wrapper.
|
|
75
|
+
* This keeps the small host-adapted directive as the safety envelope while satisfying the
|
|
76
|
+
* bare-invocation contract: the model receives the full installed skill text for the selected mode.
|
|
77
|
+
*/
|
|
78
|
+
export function loadDirectiveWithSkillBodyFrom(resolvedPath, openMarker, closeMarker, skillName, skillPath) {
|
|
79
|
+
const directive = loadDirectiveFrom(resolvedPath, openMarker, closeMarker);
|
|
80
|
+
const skillBody = readNormalizedBody(skillPath, `skill body ${skillName}`);
|
|
81
|
+
const bodyBlock = [
|
|
82
|
+
"",
|
|
83
|
+
"# Installed skill body",
|
|
84
|
+
"",
|
|
85
|
+
`<litcodex-skill-body name="${skillName}">`,
|
|
86
|
+
skillBody,
|
|
87
|
+
"</litcodex-skill-body>",
|
|
88
|
+
"",
|
|
89
|
+
].join("\n");
|
|
90
|
+
return `${directive.slice(0, -closeMarker.length).trimEnd()}${bodyBlock}${closeMarker}`;
|
|
91
|
+
}
|
|
58
92
|
/**
|
|
59
93
|
* Read + normalize + validate the lit-loop directive at an explicit path. Test-only seam (S10
|
|
60
94
|
* §Test plan) so corrupt / CRLF / missing-file cases can be exercised without swapping the bundled
|
|
@@ -16,4 +16,6 @@ export declare const LIT_RECAP_DIRECTIVE_MARKER: "<lit-recap-mode>";
|
|
|
16
16
|
export declare const LIT_RECAP_DIRECTIVE_CLOSE: "</lit-recap-mode>";
|
|
17
17
|
export declare const HYPERPLAN_DIRECTIVE_MARKER: "<hyperplan-mode>";
|
|
18
18
|
export declare const HYPERPLAN_DIRECTIVE_CLOSE: "</hyperplan-mode>";
|
|
19
|
+
export declare const INIT_DEEP_DIRECTIVE_MARKER: "<init-deep-mode>";
|
|
20
|
+
export declare const INIT_DEEP_DIRECTIVE_CLOSE: "</init-deep-mode>";
|
|
19
21
|
export type LitLoopDirectiveMarker = typeof LIT_LOOP_DIRECTIVE_MARKER;
|
|
@@ -22,3 +22,5 @@ export const LIT_RECAP_DIRECTIVE_MARKER = "<lit-recap-mode>";
|
|
|
22
22
|
export const LIT_RECAP_DIRECTIVE_CLOSE = "</lit-recap-mode>";
|
|
23
23
|
export const HYPERPLAN_DIRECTIVE_MARKER = "<hyperplan-mode>";
|
|
24
24
|
export const HYPERPLAN_DIRECTIVE_CLOSE = "</hyperplan-mode>";
|
|
25
|
+
export const INIT_DEEP_DIRECTIVE_MARKER = "<init-deep-mode>";
|
|
26
|
+
export const INIT_DEEP_DIRECTIVE_CLOSE = "</init-deep-mode>";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LitTriggerToken } from "./trigger.js";
|
|
2
2
|
/** Lit-family modes. Multiple tokens may map to one mode (lit/litcodex/lit-loop → lit-loop). */
|
|
3
|
-
export type LitMode = "hyperplan" | "lit-loop" | "litwork" | "lit-plan" | "litgoal" | "review-work" | "litresearch" | "start-work" | "lit-recap";
|
|
3
|
+
export type LitMode = "hyperplan" | "init-deep" | "lit-loop" | "litwork" | "lit-plan" | "litgoal" | "review-work" | "litresearch" | "start-work" | "lit-recap";
|
|
4
4
|
/** Per-mode routing spec: its marker pair (guard) + resolved directive path (loader). */
|
|
5
5
|
export interface LitModeSpec {
|
|
6
6
|
readonly mode: LitMode;
|
|
@@ -8,6 +8,10 @@ export interface LitModeSpec {
|
|
|
8
8
|
readonly closeMarker: string;
|
|
9
9
|
/** Absolute path to the mode's directive.md, resolved from this module. */
|
|
10
10
|
readonly directivePath: string;
|
|
11
|
+
/** The bundled skill whose full SKILL.md body must be injected for this mode. */
|
|
12
|
+
readonly skillName: string;
|
|
13
|
+
/** Absolute path to the bundled skill body. */
|
|
14
|
+
readonly skillPath: string;
|
|
11
15
|
}
|
|
12
16
|
/** Token → mode spec. Frozen; the single source the hook router branches on. */
|
|
13
17
|
export declare const MODE_BY_TOKEN: Readonly<Record<LitTriggerToken, LitModeSpec>>;
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
//
|
|
15
15
|
// Imports markers + the trigger token type only (no cycle: hook → modes → {markers, trigger}).
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
|
-
import { HYPERPLAN_DIRECTIVE_CLOSE, HYPERPLAN_DIRECTIVE_MARKER, LIT_LOOP_DIRECTIVE_CLOSE, LIT_LOOP_DIRECTIVE_MARKER, LIT_PLAN_DIRECTIVE_CLOSE, LIT_PLAN_DIRECTIVE_MARKER, LIT_RECAP_DIRECTIVE_CLOSE, LIT_RECAP_DIRECTIVE_MARKER, LITGOAL_DIRECTIVE_CLOSE, LITGOAL_DIRECTIVE_MARKER, LITRESEARCH_DIRECTIVE_CLOSE, LITRESEARCH_DIRECTIVE_MARKER, LITWORK_DIRECTIVE_CLOSE, LITWORK_DIRECTIVE_MARKER, REVIEW_WORK_DIRECTIVE_CLOSE, REVIEW_WORK_DIRECTIVE_MARKER, START_WORK_DIRECTIVE_CLOSE, START_WORK_DIRECTIVE_MARKER, } from "./markers.js";
|
|
17
|
+
import { HYPERPLAN_DIRECTIVE_CLOSE, HYPERPLAN_DIRECTIVE_MARKER, INIT_DEEP_DIRECTIVE_CLOSE, INIT_DEEP_DIRECTIVE_MARKER, LIT_LOOP_DIRECTIVE_CLOSE, LIT_LOOP_DIRECTIVE_MARKER, LIT_PLAN_DIRECTIVE_CLOSE, LIT_PLAN_DIRECTIVE_MARKER, LIT_RECAP_DIRECTIVE_CLOSE, LIT_RECAP_DIRECTIVE_MARKER, LITGOAL_DIRECTIVE_CLOSE, LITGOAL_DIRECTIVE_MARKER, LITRESEARCH_DIRECTIVE_CLOSE, LITRESEARCH_DIRECTIVE_MARKER, LITWORK_DIRECTIVE_CLOSE, LITWORK_DIRECTIVE_MARKER, REVIEW_WORK_DIRECTIVE_CLOSE, REVIEW_WORK_DIRECTIVE_MARKER, START_WORK_DIRECTIVE_CLOSE, START_WORK_DIRECTIVE_MARKER, } from "./markers.js";
|
|
18
18
|
/** Resolve a path relative to this module (percent-decoding, space/#/Hangul-safe). */
|
|
19
19
|
function resolveFromHere(rel) {
|
|
20
20
|
return fileURLToPath(new URL(rel, import.meta.url));
|
|
@@ -25,6 +25,8 @@ const LIT_LOOP_SPEC = Object.freeze({
|
|
|
25
25
|
openMarker: LIT_LOOP_DIRECTIVE_MARKER,
|
|
26
26
|
closeMarker: LIT_LOOP_DIRECTIVE_CLOSE,
|
|
27
27
|
directivePath: resolveFromHere("../directive.md"),
|
|
28
|
+
skillName: "lit-loop",
|
|
29
|
+
skillPath: resolveFromHere("../../../skills/lit-loop/SKILL.md"),
|
|
28
30
|
});
|
|
29
31
|
/** Token → mode spec. Frozen; the single source the hook router branches on. */
|
|
30
32
|
export const MODE_BY_TOKEN = Object.freeze({
|
|
@@ -33,6 +35,16 @@ export const MODE_BY_TOKEN = Object.freeze({
|
|
|
33
35
|
openMarker: HYPERPLAN_DIRECTIVE_MARKER,
|
|
34
36
|
closeMarker: HYPERPLAN_DIRECTIVE_CLOSE,
|
|
35
37
|
directivePath: resolveFromHere("../directives/hyperplan.md"),
|
|
38
|
+
skillName: "hyperplan",
|
|
39
|
+
skillPath: resolveFromHere("../../../skills/hyperplan/SKILL.md"),
|
|
40
|
+
}),
|
|
41
|
+
"init-deep": Object.freeze({
|
|
42
|
+
mode: "init-deep",
|
|
43
|
+
openMarker: INIT_DEEP_DIRECTIVE_MARKER,
|
|
44
|
+
closeMarker: INIT_DEEP_DIRECTIVE_CLOSE,
|
|
45
|
+
directivePath: resolveFromHere("../directives/init-deep.md"),
|
|
46
|
+
skillName: "init-deep",
|
|
47
|
+
skillPath: resolveFromHere("../../../skills/init-deep/SKILL.md"),
|
|
36
48
|
}),
|
|
37
49
|
"lit-loop": LIT_LOOP_SPEC,
|
|
38
50
|
litcodex: LIT_LOOP_SPEC,
|
|
@@ -42,42 +54,56 @@ export const MODE_BY_TOKEN = Object.freeze({
|
|
|
42
54
|
openMarker: LITWORK_DIRECTIVE_MARKER,
|
|
43
55
|
closeMarker: LITWORK_DIRECTIVE_CLOSE,
|
|
44
56
|
directivePath: resolveFromHere("../directives/litwork.md"),
|
|
57
|
+
skillName: "litwork",
|
|
58
|
+
skillPath: resolveFromHere("../../../skills/litwork/SKILL.md"),
|
|
45
59
|
}),
|
|
46
60
|
"lit-plan": Object.freeze({
|
|
47
61
|
mode: "lit-plan",
|
|
48
62
|
openMarker: LIT_PLAN_DIRECTIVE_MARKER,
|
|
49
63
|
closeMarker: LIT_PLAN_DIRECTIVE_CLOSE,
|
|
50
64
|
directivePath: resolveFromHere("../directives/lit-plan.md"),
|
|
65
|
+
skillName: "lit-plan",
|
|
66
|
+
skillPath: resolveFromHere("../../../skills/lit-plan/SKILL.md"),
|
|
51
67
|
}),
|
|
52
68
|
litgoal: Object.freeze({
|
|
53
69
|
mode: "litgoal",
|
|
54
70
|
openMarker: LITGOAL_DIRECTIVE_MARKER,
|
|
55
71
|
closeMarker: LITGOAL_DIRECTIVE_CLOSE,
|
|
56
72
|
directivePath: resolveFromHere("../directives/litgoal.md"),
|
|
73
|
+
skillName: "litgoal",
|
|
74
|
+
skillPath: resolveFromHere("../../../skills/litgoal/SKILL.md"),
|
|
57
75
|
}),
|
|
58
76
|
"review-work": Object.freeze({
|
|
59
77
|
mode: "review-work",
|
|
60
78
|
openMarker: REVIEW_WORK_DIRECTIVE_MARKER,
|
|
61
79
|
closeMarker: REVIEW_WORK_DIRECTIVE_CLOSE,
|
|
62
80
|
directivePath: resolveFromHere("../directives/review-work.md"),
|
|
81
|
+
skillName: "review-work",
|
|
82
|
+
skillPath: resolveFromHere("../../../skills/review-work/SKILL.md"),
|
|
63
83
|
}),
|
|
64
84
|
litresearch: Object.freeze({
|
|
65
85
|
mode: "litresearch",
|
|
66
86
|
openMarker: LITRESEARCH_DIRECTIVE_MARKER,
|
|
67
87
|
closeMarker: LITRESEARCH_DIRECTIVE_CLOSE,
|
|
68
88
|
directivePath: resolveFromHere("../directives/litresearch.md"),
|
|
89
|
+
skillName: "litresearch",
|
|
90
|
+
skillPath: resolveFromHere("../../../skills/litresearch/SKILL.md"),
|
|
69
91
|
}),
|
|
70
92
|
"start-work": Object.freeze({
|
|
71
93
|
mode: "start-work",
|
|
72
94
|
openMarker: START_WORK_DIRECTIVE_MARKER,
|
|
73
95
|
closeMarker: START_WORK_DIRECTIVE_CLOSE,
|
|
74
96
|
directivePath: resolveFromHere("../directives/start-work.md"),
|
|
97
|
+
skillName: "start-work",
|
|
98
|
+
skillPath: resolveFromHere("../../../skills/start-work/SKILL.md"),
|
|
75
99
|
}),
|
|
76
100
|
"lit-recap": Object.freeze({
|
|
77
101
|
mode: "lit-recap",
|
|
78
102
|
openMarker: LIT_RECAP_DIRECTIVE_MARKER,
|
|
79
103
|
closeMarker: LIT_RECAP_DIRECTIVE_CLOSE,
|
|
80
104
|
directivePath: resolveFromHere("../directives/lit-recap.md"),
|
|
105
|
+
skillName: "lit-recap",
|
|
106
|
+
skillPath: resolveFromHere("../../../skills/lit-recap/SKILL.md"),
|
|
81
107
|
}),
|
|
82
108
|
});
|
|
83
109
|
/** The mode spec for a matched token. Total over the token union (every token has a spec). */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { LitTriggerToken } from "./trigger.js";
|
|
2
2
|
export declare function isCodexSkillMentionToken(prompt: string, tokenIndex: number): boolean;
|
|
3
3
|
export declare function scopedTokenForBareLit(prompt: string, tokenIndex: number): LitTriggerToken | null;
|
|
4
|
+
export declare function suppressesBareLitAfterCodexSkillMention(prompt: string, tokenIndex: number): boolean;
|
|
@@ -13,19 +13,28 @@ export function isCodexSkillMentionToken(prompt, tokenIndex) {
|
|
|
13
13
|
return mention.startsWith("$") && mention.includes(":") && tokenIndex > start;
|
|
14
14
|
}
|
|
15
15
|
export function scopedTokenForBareLit(prompt, tokenIndex) {
|
|
16
|
-
|
|
16
|
+
const skillName = nearestLitcodexSkillNameBefore(prompt, tokenIndex);
|
|
17
|
+
if (skillName === null || suppressesBareLitAfterCodexSkillMention(prompt, tokenIndex)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return tokenForSkillName(skillName);
|
|
21
|
+
}
|
|
22
|
+
export function suppressesBareLitAfterCodexSkillMention(prompt, tokenIndex) {
|
|
23
|
+
return nearestLitcodexSkillNameBefore(prompt, tokenIndex) === "start-work";
|
|
24
|
+
}
|
|
25
|
+
function nearestLitcodexSkillNameBefore(prompt, tokenIndex) {
|
|
26
|
+
let scopedSkillName = null;
|
|
17
27
|
for (const match of prompt.slice(0, tokenIndex).matchAll(CODEX_SKILL_MENTION_PATTERN)) {
|
|
18
28
|
const pluginName = match[1]?.toLowerCase();
|
|
19
29
|
const skillName = match[2]?.toLowerCase();
|
|
20
30
|
if (pluginName !== LITCODEX_PLUGIN_NAME || skillName === undefined) {
|
|
21
31
|
continue;
|
|
22
32
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
scopedToken = token;
|
|
33
|
+
if (tokenForSkillName(skillName) !== null) {
|
|
34
|
+
scopedSkillName = skillName;
|
|
26
35
|
}
|
|
27
36
|
}
|
|
28
|
-
return
|
|
37
|
+
return scopedSkillName;
|
|
29
38
|
}
|
|
30
39
|
function tokenForSkillName(skillName) {
|
|
31
40
|
switch (skillName) {
|
|
@@ -41,6 +50,8 @@ function tokenForSkillName(skillName) {
|
|
|
41
50
|
return "litresearch";
|
|
42
51
|
case "lit-recap":
|
|
43
52
|
return "lit-recap";
|
|
53
|
+
case "init-deep":
|
|
54
|
+
return "init-deep";
|
|
44
55
|
case "start-work":
|
|
45
56
|
return "start-work";
|
|
46
57
|
case "review-work":
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The accepted bounded lit-family tokens, longest-first (ordering is load-bearing — see below). */
|
|
2
|
-
export type LitTriggerToken = "hyperplan" | "start-work" | "review-work" | "litresearch" | "lit-recap" | "lit-loop" | "lit-plan" | "litcodex" | "litgoal" | "litwork" | "lit";
|
|
2
|
+
export type LitTriggerToken = "hyperplan" | "init-deep" | "start-work" | "review-work" | "litresearch" | "lit-recap" | "lit-loop" | "lit-plan" | "litcodex" | "litgoal" | "litwork" | "lit";
|
|
3
3
|
/**
|
|
4
4
|
* Frozen tuple of the accepted tokens in match-priority (longest-first) order.
|
|
5
5
|
* Exported so tests and the mode router (modes.ts) can enumerate without re-deriving.
|
|
@@ -14,7 +14,7 @@ export declare const LIT_TRIGGER_TOKENS: readonly LitTriggerToken[];
|
|
|
14
14
|
*
|
|
15
15
|
* MUST NOT carry the /g or /y flag: a global regex retains `lastIndex` between calls and would
|
|
16
16
|
* make `.test()` return alternating results for the same input. Longest-first alternation
|
|
17
|
-
* (`hyperplan|review-work|litresearch|lit-recap|lit-loop|lit-plan|litcodex|litrecap|litgoal|litwork|recap|리캡|lit`)
|
|
17
|
+
* (`hyperplan|init-deep|start-work|review-work|litresearch|lit-recap|lit-loop|lit-plan|litcodex|litrecap|litgoal|litwork|recap|리캡|lit`)
|
|
18
18
|
* guarantees a longer family token wins over a bare `lit` at the same start; the trailing-`-`
|
|
19
19
|
* lookahead means `lit work` (space) is a bare `lit` while `litwork` (glued) is the work mode.
|
|
20
20
|
* The `litrecap` / `recap` / `리캡` alternatives are ROUTING ALIASES normalized to the canonical
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Single source of truth for deciding whether a user prompt activates the LitCodex `lit-loop`
|
|
4
4
|
// workflow. Pure, side-effect-free, Unicode-aware. Accepts the bounded tokens `lit`, `lit-loop`,
|
|
5
5
|
// and `litcodex` (case-insensitive), plus sibling mode tokens that are safe to inject directly.
|
|
6
|
-
// `start-work
|
|
7
|
-
//
|
|
6
|
+
// Bare `start-work ...` routes to the safe blocked handoff with the installed skill body, while
|
|
7
|
+
// scoped Codex skill mentions such as `$litcodex:start-work ...` stay on the native skill surface.
|
|
8
8
|
// Recap aliases (`litrecap` / `recap` / `리캡`) normalize to the single `lit-recap` token. Rejects
|
|
9
9
|
// every substring collision (`split`, `literal`, `litmus`, `lithium`, `glitter`, `flit`, `slit`,
|
|
10
10
|
// `litter`, `recapture`, …) while respecting Korean (Hangul) and English boundaries.
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
// This module imports NOTHING from `state-store`, `directive`, `guards`, `markers`, or
|
|
13
13
|
// `codex-hook` (one-way: hook → trigger, never the reverse). It reads no files, touches no state,
|
|
14
14
|
// emits no hook JSON, and never mutates the prompt. (A3 C9 flat layout; A2 §2.1 export surface.)
|
|
15
|
-
import { isCodexSkillMentionToken, scopedTokenForBareLit } from "./skill-mention-scope.js";
|
|
15
|
+
import { isCodexSkillMentionToken, scopedTokenForBareLit, suppressesBareLitAfterCodexSkillMention, } from "./skill-mention-scope.js";
|
|
16
16
|
/**
|
|
17
17
|
* Frozen tuple of the accepted tokens in match-priority (longest-first) order.
|
|
18
18
|
* Exported so tests and the mode router (modes.ts) can enumerate without re-deriving.
|
|
19
19
|
*/
|
|
20
20
|
export const LIT_TRIGGER_TOKENS = Object.freeze([
|
|
21
21
|
"hyperplan",
|
|
22
|
+
"init-deep",
|
|
22
23
|
"start-work",
|
|
23
24
|
"review-work",
|
|
24
25
|
"litresearch",
|
|
@@ -39,14 +40,14 @@ export const LIT_TRIGGER_TOKENS = Object.freeze([
|
|
|
39
40
|
*
|
|
40
41
|
* MUST NOT carry the /g or /y flag: a global regex retains `lastIndex` between calls and would
|
|
41
42
|
* make `.test()` return alternating results for the same input. Longest-first alternation
|
|
42
|
-
* (`hyperplan|review-work|litresearch|lit-recap|lit-loop|lit-plan|litcodex|litrecap|litgoal|litwork|recap|리캡|lit`)
|
|
43
|
+
* (`hyperplan|init-deep|start-work|review-work|litresearch|lit-recap|lit-loop|lit-plan|litcodex|litrecap|litgoal|litwork|recap|리캡|lit`)
|
|
43
44
|
* guarantees a longer family token wins over a bare `lit` at the same start; the trailing-`-`
|
|
44
45
|
* lookahead means `lit work` (space) is a bare `lit` while `litwork` (glued) is the work mode.
|
|
45
46
|
* The `litrecap` / `recap` / `리캡` alternatives are ROUTING ALIASES normalized to the canonical
|
|
46
47
|
* `lit-recap` token by `matchLitTrigger` (they are not members of the token union).
|
|
47
48
|
* No nested quantifiers / no backreferences ⇒ ReDoS-free.
|
|
48
49
|
*/
|
|
49
|
-
export const LIT_TRIGGER_PATTERN = /(?:^|[^\p{L}\p{N}_])(hyperplan|review-work|litresearch|lit-recap|lit-loop|lit-plan|litcodex|litrecap|litgoal|litwork|recap|리캡|lit)(?![\p{L}\p{N}_-])/iu;
|
|
50
|
+
export const LIT_TRIGGER_PATTERN = /(?:^|[^\p{L}\p{N}_])(hyperplan|init-deep|start-work|review-work|litresearch|lit-recap|lit-loop|lit-plan|litcodex|litrecap|litgoal|litwork|recap|리캡|lit)(?![\p{L}\p{N}_-])/iu;
|
|
50
51
|
const NOT_A_STRING = "lit trigger: prompt must be a string";
|
|
51
52
|
/**
|
|
52
53
|
* Returns true iff `prompt` contains at least one bounded lit trigger.
|
|
@@ -72,11 +73,8 @@ export function matchLitTrigger(prompt) {
|
|
|
72
73
|
if (typeof prompt !== "string") {
|
|
73
74
|
throw new TypeError(NOT_A_STRING);
|
|
74
75
|
}
|
|
75
|
-
if (isLeadingStartWorkInvocation(prompt)) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
76
|
const searchable = maskMarkdownCode(prompt);
|
|
79
|
-
const scan = /(?:^|[^\p{L}\p{N}_])(hyperplan|review-work|litresearch|lit-recap|lit-loop|lit-plan|litcodex|litrecap|litgoal|litwork|recap|리캡|lit)(?![\p{L}\p{N}_-])/giu;
|
|
77
|
+
const scan = /(?:^|[^\p{L}\p{N}_])(hyperplan|init-deep|start-work|review-work|litresearch|lit-recap|lit-loop|lit-plan|litcodex|litrecap|litgoal|litwork|recap|리캡|lit)(?![\p{L}\p{N}_-])/giu;
|
|
80
78
|
for (const m of searchable.matchAll(scan)) {
|
|
81
79
|
const raw = m[1];
|
|
82
80
|
if (raw === undefined) {
|
|
@@ -101,6 +99,9 @@ export function matchLitTrigger(prompt) {
|
|
|
101
99
|
if (phrase !== null) {
|
|
102
100
|
return phrase;
|
|
103
101
|
}
|
|
102
|
+
if (raw.toLowerCase() === "lit" && suppressesBareLitAfterCodexSkillMention(prompt, index)) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
104
105
|
const scopedToken = raw.toLowerCase() === "lit" ? scopedTokenForBareLit(prompt, index) : null;
|
|
105
106
|
if (scopedToken !== null) {
|
|
106
107
|
return { token: scopedToken, raw: prompt.slice(index, index + raw.length), index };
|
|
@@ -109,13 +110,10 @@ export function matchLitTrigger(prompt) {
|
|
|
109
110
|
}
|
|
110
111
|
return null;
|
|
111
112
|
}
|
|
112
|
-
function isLeadingStartWorkInvocation(prompt) {
|
|
113
|
-
return /^(?:\$litcodex:|\$)?start-work(?:$|\s)/iu.test(prompt.trimStart());
|
|
114
|
-
}
|
|
115
113
|
function naturalPhraseAfterLit(prompt, searchable, index, rawLength) {
|
|
116
114
|
const start = index + rawLength;
|
|
117
115
|
const rest = searchable.slice(start);
|
|
118
|
-
const m = /^(\s+)(start\s+work|hyperplan|plan|review|research|goal|recap)(?![\p{L}\p{N}_-])/iu.exec(rest);
|
|
116
|
+
const m = /^(\s+)(start\s+work|hyperplan|init-deep|plan|review|research|goal|recap)(?![\p{L}\p{N}_-])/iu.exec(rest);
|
|
119
117
|
if (m === null || m[1] === undefined || m[2] === undefined) {
|
|
120
118
|
return null;
|
|
121
119
|
}
|
|
@@ -123,6 +121,7 @@ function naturalPhraseAfterLit(prompt, searchable, index, rawLength) {
|
|
|
123
121
|
const tokenByPhrase = {
|
|
124
122
|
"start work": "start-work",
|
|
125
123
|
hyperplan: "hyperplan",
|
|
124
|
+
"init-deep": "init-deep",
|
|
126
125
|
plan: "lit-plan",
|
|
127
126
|
review: "review-work",
|
|
128
127
|
research: "litresearch",
|
package/package.json
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "litcodex-ai",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22",
|
|
4
4
|
"description": "Codex loop harness installer. Run `npx litcodex-ai install` to set up the LitCodex Codex platform: the bare `lit` hook and the durable lit-loop runtime.",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"codex",
|
|
7
|
+
"litcodex",
|
|
8
|
+
"lit-loop",
|
|
9
|
+
"ai-agents",
|
|
10
|
+
"orchestration"
|
|
11
|
+
],
|
|
6
12
|
"author": "LitCodex Authors",
|
|
7
13
|
"license": "MIT",
|
|
8
|
-
"publishConfig": {
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/wjgoarxiv/litcodex.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/wjgoarxiv/litcodex/issues"
|
|
23
|
+
},
|
|
11
24
|
"homepage": "https://github.com/wjgoarxiv/litcodex#readme",
|
|
12
25
|
"type": "module",
|
|
13
26
|
"bin": {
|
|
14
27
|
"litcodex": "bin/litcodex.js"
|
|
15
28
|
},
|
|
16
|
-
"files": [
|
|
29
|
+
"files": [
|
|
30
|
+
"bin",
|
|
31
|
+
"dist",
|
|
32
|
+
"model-catalog.json",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
17
36
|
"dependencies": {
|
|
18
|
-
"@litcodex/lit-loop": "0.3.
|
|
37
|
+
"@litcodex/lit-loop": "0.3.22"
|
|
19
38
|
},
|
|
20
|
-
"bundledDependencies": [
|
|
39
|
+
"bundledDependencies": [
|
|
40
|
+
"@litcodex/lit-loop"
|
|
41
|
+
],
|
|
21
42
|
"scripts": {
|
|
22
43
|
"build": "tsc -p tsconfig.build.json",
|
|
23
44
|
"typecheck": "tsc --noEmit",
|