@jitera/connect 1.5.0 → 2.0.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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +20 -13
- package/content/instructions.md +7 -4
- package/content/session-start-unbound.md +12 -0
- package/content/session-start.md +14 -5
- package/dist/bin/statusline.js +6 -1
- package/dist/bin/statusline.js.map +1 -1
- package/dist/hook-io.d.ts +2 -0
- package/dist/hook-io.d.ts.map +1 -1
- package/dist/hook-io.js +3 -0
- package/dist/hook-io.js.map +1 -1
- package/dist/hooks/session-start.js +19 -5
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/stop.js +7 -1
- package/dist/hooks/stop.js.map +1 -1
- package/dist/hooks/user-prompt-submit.js +75 -20
- package/dist/hooks/user-prompt-submit.js.map +1 -1
- package/dist/project-marker.d.ts.map +1 -1
- package/dist/project-marker.js +14 -2
- package/dist/project-marker.js.map +1 -1
- package/dist/statusline.d.ts +2 -1
- package/dist/statusline.d.ts.map +1 -1
- package/dist/statusline.js +6 -1
- package/dist/statusline.js.map +1 -1
- package/package.json +1 -1
- package/skills/jitera-context/SKILL.md +22 -0
- package/skills/jitera-memory/SKILL.md +14 -2
- package/templates/AGENTS.md.tmpl +10 -4
package/README.md
CHANGED
|
@@ -60,19 +60,26 @@ CLAUDE.md above a repo leaks into every project underneath it.
|
|
|
60
60
|
## what applies where
|
|
61
61
|
|
|
62
62
|
the api key is global: one per user, per machine, stored by `login --install`.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
`.jitera.json`
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
it says who you are, not which project you are in.
|
|
64
|
+
|
|
65
|
+
`.jitera.json` says which project. without it a repo is unbound, and the hooks
|
|
66
|
+
do nothing at all there: no context is gathered, no checkpoint is asked for, and
|
|
67
|
+
no call reaches the server. a repo you have not run `init` in is left alone,
|
|
68
|
+
which is why init is per repo and login is per machine.
|
|
69
|
+
|
|
70
|
+
an unbound session is not silent about it. session start tells the assistant the
|
|
71
|
+
repo is unbound and to suggest `npx @jitera/connect init`, and the status line
|
|
72
|
+
shows the same thing.
|
|
73
|
+
|
|
74
|
+
sessions also check the marker against the plugin. a teammate who has not signed
|
|
75
|
+
in yet gets the exact login command for the right environment. a teammate
|
|
76
|
+
pointed at a different environment gets a warning instead of quietly reading the
|
|
77
|
+
wrong project.
|
|
78
|
+
|
|
79
|
+
the proxy and the hooks send the project it names as an `X-Jitera-Project`
|
|
80
|
+
header, so several repos on one machine can talk to different projects through a
|
|
81
|
+
single key. older deployments bind the key itself to one project; there the
|
|
82
|
+
marker still decides whether the hooks run, and makes a mismatch visible.
|
|
76
83
|
|
|
77
84
|
## seeing your keys
|
|
78
85
|
|
package/content/instructions.md
CHANGED
|
@@ -3,16 +3,19 @@ memory, documentation, and source. Treat it as the source of truth for this
|
|
|
3
3
|
project, not an optional lookup.
|
|
4
4
|
|
|
5
5
|
Before answering a question about this project, and before planning or
|
|
6
|
-
implementing anything, call `
|
|
7
|
-
|
|
6
|
+
implementing anything, call `gather_jitera_context` with the task in the user's
|
|
7
|
+
own words. It recalls memory and searches documentation and source in one pass,
|
|
8
|
+
across every agent in the project. An empty result is normal, not an error.
|
|
9
|
+
Call it again whenever the work reaches something you have not gathered context
|
|
10
|
+
for; it is not a once-per-session lookup.
|
|
8
11
|
|
|
9
12
|
Before writing a spec, design, or plan, search `documents/` with
|
|
10
13
|
`resource_search` and read any match with `resource_read`. Update the existing
|
|
11
14
|
document rather than creating a second one.
|
|
12
15
|
|
|
13
16
|
When a decision is made, a constraint is discovered, or the user corrects you,
|
|
14
|
-
persist it with `remember_jitera_memory`. Recall the entity first
|
|
15
|
-
full attribute list — entities are keyed on name plus type, so writing blind
|
|
17
|
+
persist it with `remember_jitera_memory`. Recall the entity first with
|
|
18
|
+
`recall_jitera_memory` and re-send its full attribute list — entities are keyed on name plus type, so writing blind
|
|
16
19
|
creates duplicates and replaces facts instead of adding them.
|
|
17
20
|
|
|
18
21
|
Do not store anything already recorded in the repository or its git history.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
An API key is configured, but this repository is not bound to a project.
|
|
2
|
+
No project context is loaded and the MCP tools have nothing to read.
|
|
3
|
+
Do not call them until this repository is bound.
|
|
4
|
+
|
|
5
|
+
If the user asks about project context, documentation, or shared memory, or
|
|
6
|
+
wants this repository bound to one, tell them to run this at the repository root:
|
|
7
|
+
|
|
8
|
+
npx @jitera/connect init
|
|
9
|
+
|
|
10
|
+
That writes a .jitera.json recording which deployment and project this
|
|
11
|
+
repository belongs to, alongside AGENTS.md and CLAUDE.md for the rest of the
|
|
12
|
+
team. Once it exists, project context loads by itself at the start of a session.
|
package/content/session-start.md
CHANGED
|
@@ -2,10 +2,19 @@ Project context tools are configured for this workspace: project documentation,
|
|
|
2
2
|
source, and a shared project memory over MCP.
|
|
3
3
|
|
|
4
4
|
Before your first substantive answer in this session, and before planning or
|
|
5
|
-
implementing anything, call `
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
implementing anything, call `gather_jitera_context` with the task in the user's
|
|
6
|
+
own words. An empty result is normal and is not an error.
|
|
7
|
+
|
|
8
|
+
If the tools are missing from your tool list or a call fails, say so and
|
|
9
|
+
continue without stored context instead of guessing.
|
|
10
|
+
|
|
11
|
+
Keep using it as the work moves. This is not a once-per-session lookup: call it
|
|
12
|
+
again whenever you reach an area you have not gathered context for yet, an
|
|
13
|
+
unfamiliar name, or a decision about how something should work. Use
|
|
14
|
+
`resource_search` and `resource_read` when you want one specific file rather
|
|
15
|
+
than the whole picture.
|
|
8
16
|
|
|
9
17
|
As decisions are made, constraints are discovered, or you are corrected, persist
|
|
10
|
-
them with `remember_jitera_memory`. Recall the entity first
|
|
11
|
-
attribute list, so you update it
|
|
18
|
+
them with `remember_jitera_memory`. Recall the entity first with
|
|
19
|
+
`recall_jitera_memory` and re-send its full attribute list, so you update it
|
|
20
|
+
rather than creating a duplicate.
|
package/dist/bin/statusline.js
CHANGED
|
@@ -13,5 +13,10 @@ catch {
|
|
|
13
13
|
const dir = input.workspace?.project_dir ?? input.workspace?.current_dir ?? input.cwd;
|
|
14
14
|
const marker = dir ? readProjectMarker(dir) : undefined;
|
|
15
15
|
const status = readSessionStatus(input.session_id);
|
|
16
|
-
process.stdout.write(`${renderStatusLine({
|
|
16
|
+
process.stdout.write(`${renderStatusLine({
|
|
17
|
+
status,
|
|
18
|
+
markerEnvironment: marker?.environment,
|
|
19
|
+
// No directory to inspect is not the same as an unbound repository.
|
|
20
|
+
bound: dir ? marker !== undefined : undefined,
|
|
21
|
+
})}\n`);
|
|
17
22
|
//# sourceMappingURL=statusline.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusline.js","sourceRoot":"","sources":["../../src/bin/statusline.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAQpD,IAAI,KAAK,GAAgB,EAAE,CAAC;AAC5B,IAAI,CAAC;IACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAgB,CAAC;AAC7D,CAAC;AAAC,MAAM,CAAC;IACP,KAAK,GAAG,EAAE,CAAC;AACb,CAAC;AAED,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,WAAW,IAAI,KAAK,CAAC,SAAS,EAAE,WAAW,IAAI,KAAK,CAAC,GAAG,CAAC;AACtF,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxD,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAEnD,OAAO,CAAC,MAAM,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"statusline.js","sourceRoot":"","sources":["../../src/bin/statusline.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAQpD,IAAI,KAAK,GAAgB,EAAE,CAAC;AAC5B,IAAI,CAAC;IACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAgB,CAAC;AAC7D,CAAC;AAAC,MAAM,CAAC;IACP,KAAK,GAAG,EAAE,CAAC;AACb,CAAC;AAED,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,EAAE,WAAW,IAAI,KAAK,CAAC,SAAS,EAAE,WAAW,IAAI,KAAK,CAAC,GAAG,CAAC;AACtF,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACxD,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;AAEnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,gBAAgB,CAAC;IAClB,MAAM;IACN,iBAAiB,EAAE,MAAM,EAAE,WAAW;IACtC,oEAAoE;IACpE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS;CAC9C,CAAC,IAAI,CACP,CAAC"}
|
package/dist/hook-io.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface SessionStartInput extends HookInputBase {
|
|
|
9
9
|
readonly source?: "startup" | "resume" | "clear" | "compact" | "fork";
|
|
10
10
|
}
|
|
11
11
|
export interface UserPromptSubmitInput extends HookInputBase {
|
|
12
|
+
readonly prompt?: string;
|
|
12
13
|
readonly prompt_text?: string;
|
|
13
14
|
}
|
|
14
15
|
export interface StopInput extends HookInputBase {
|
|
@@ -18,5 +19,6 @@ export interface StopInput extends HookInputBase {
|
|
|
18
19
|
}
|
|
19
20
|
export type HookInput = SessionStartInput & UserPromptSubmitInput & StopInput;
|
|
20
21
|
export declare function readHookInput(fd?: number): HookInput;
|
|
22
|
+
export declare function promptText(input: UserPromptSubmitInput): string;
|
|
21
23
|
export declare function emitContext(event: HookEventName, additionalContext: string): void;
|
|
22
24
|
//# sourceMappingURL=hook-io.d.ts.map
|
package/dist/hook-io.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-io.d.ts","sourceRoot":"","sources":["../src/hook-io.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,kBAAkB,GAClB,MAAM,GACN,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;CACvE;AAED,MAAM,WAAW,qBAAsB,SAAQ,aAAa;
|
|
1
|
+
{"version":3,"file":"hook-io.d.ts","sourceRoot":"","sources":["../src/hook-io.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,kBAAkB,GAClB,MAAM,GACN,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;CACvE;AAED,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAI1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,SAAU,SAAQ,aAAa;IAC9C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAC1C;AAED,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG,qBAAqB,GAAG,SAAS,CAAC;AAE9E,wBAAgB,aAAa,CAAC,EAAE,GAAE,MAAU,GAAG,SAAS,CAMvD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAE/D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAIjF"}
|
package/dist/hook-io.js
CHANGED
|
@@ -7,6 +7,9 @@ export function readHookInput(fd = 0) {
|
|
|
7
7
|
return {};
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
+
export function promptText(input) {
|
|
11
|
+
return (input.prompt ?? input.prompt_text ?? "").trim();
|
|
12
|
+
}
|
|
10
13
|
export function emitContext(event, additionalContext) {
|
|
11
14
|
process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: event, additionalContext } }));
|
|
12
15
|
}
|
package/dist/hook-io.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-io.js","sourceRoot":"","sources":["../src/hook-io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"hook-io.js","sourceRoot":"","sources":["../src/hook-io.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAoCvC,MAAM,UAAU,aAAa,CAAC,EAAE,GAAW,CAAC;IAC1C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,CAAc,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAA4B;IACrD,OAAO,CAAC,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAoB,EAAE,iBAAyB;IACzE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,IAAI,CAAC,SAAS,CAAC,EAAE,kBAAkB,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,CAAC,CACpF,CAAC;AACJ,CAAC"}
|
|
@@ -12,7 +12,16 @@ const input = readHookInput();
|
|
|
12
12
|
// teach the model to call tools that are not there.
|
|
13
13
|
const apiKey = (process.env["CLAUDE_PLUGIN_OPTION_JITERA_API_KEY"] ?? "").trim();
|
|
14
14
|
const configuredEnvironment = (process.env["CLAUDE_PLUGIN_OPTION_ENVIRONMENT"] ?? "").trim();
|
|
15
|
-
|
|
15
|
+
// A committed .jitera.json binds the repository to a deployment and project.
|
|
16
|
+
// Read it first: which directive applies depends on whether this repo is bound.
|
|
17
|
+
const marker = readProjectMarker(input.cwd ?? process.cwd());
|
|
18
|
+
// Three states, because "connected" and "bound to a project" are different
|
|
19
|
+
// things: without a binding the tools would have no project to read.
|
|
20
|
+
const file = !apiKey
|
|
21
|
+
? "session-start-unconfigured.md"
|
|
22
|
+
: marker
|
|
23
|
+
? "session-start.md"
|
|
24
|
+
: "session-start-unbound.md";
|
|
16
25
|
let directive;
|
|
17
26
|
try {
|
|
18
27
|
directive = readFileSync(join(CONTENT_ROOT, file), "utf8").trim();
|
|
@@ -20,10 +29,6 @@ try {
|
|
|
20
29
|
catch {
|
|
21
30
|
process.exit(0);
|
|
22
31
|
}
|
|
23
|
-
// A committed .jitera.json binds the repository to a deployment, so teammates
|
|
24
|
-
// who have not connected yet get the exact command, and a mismatched plugin
|
|
25
|
-
// configuration is called out instead of silently reading the wrong project.
|
|
26
|
-
const marker = readProjectMarker(input.cwd ?? process.cwd());
|
|
27
32
|
writeSessionStatus(input.session_id, {
|
|
28
33
|
configured: Boolean(apiKey),
|
|
29
34
|
environment: configuredEnvironment || "studio",
|
|
@@ -42,5 +47,14 @@ if (marker?.environment) {
|
|
|
42
47
|
`Project context may come from the wrong deployment. Suggest rebinding with:\n\n ${login}`;
|
|
43
48
|
}
|
|
44
49
|
}
|
|
50
|
+
// `init` writes a marker with no project when nobody was signed in to pick one.
|
|
51
|
+
// A project-scoped key still works, but a user-level key cannot infer a project
|
|
52
|
+
// and every call quietly fails, so name the gap rather than let it dead-end.
|
|
53
|
+
if (apiKey && marker && !marker.project) {
|
|
54
|
+
directive +=
|
|
55
|
+
`\n\nNote: this repository's .jitera.json records no project. A user-level ` +
|
|
56
|
+
`API key cannot infer one, so project context may not load. Suggest finishing ` +
|
|
57
|
+
`the binding with:\n\n npx @jitera/connect init --project=<uuid>`;
|
|
58
|
+
}
|
|
45
59
|
emitContext("SessionStart", directive);
|
|
46
60
|
//# sourceMappingURL=session-start.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../src/hooks/session-start.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAE1F,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;AAE9B,gFAAgF;AAChF,+EAA+E;AAC/E,oDAAoD;AACpD,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACjF,MAAM,qBAAqB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7F,MAAM,
|
|
1
|
+
{"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../src/hooks/session-start.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAE1F,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;AAE9B,gFAAgF;AAChF,+EAA+E;AAC/E,oDAAoD;AACpD,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACjF,MAAM,qBAAqB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7F,6EAA6E;AAC7E,gFAAgF;AAChF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAE7D,2EAA2E;AAC3E,qEAAqE;AACrE,MAAM,IAAI,GAAG,CAAC,MAAM;IAClB,CAAC,CAAC,+BAA+B;IACjC,CAAC,CAAC,MAAM;QACN,CAAC,CAAC,kBAAkB;QACpB,CAAC,CAAC,0BAA0B,CAAC;AAEjC,IAAI,SAAiB,CAAC;AACtB,IAAI,CAAC;IACH,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AACpE,CAAC;AAAC,MAAM,CAAC;IACP,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,kBAAkB,CAAC,KAAK,CAAC,UAAU,EAAE;IACnC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC;IAC3B,WAAW,EAAE,qBAAqB,IAAI,QAAQ;CAC/C,CAAC,CAAC;AAEH,IAAI,MAAM,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,KAAK,GAAG,mCAAmC,MAAM,CAAC,WAAW,YAAY,CAAC;IAChF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,SAAS;YACP,8DAA8D;gBAC9D,iBAAiB,MAAM,CAAC,WAAW,8CAA8C,KAAK,EAAE,CAAC;IAC7F,CAAC;SAAM,IAAI,qBAAqB,IAAI,qBAAqB,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC;QACjF,SAAS;YACP,gEAAgE;gBAChE,IAAI,MAAM,CAAC,WAAW,wCAAwC,qBAAqB,KAAK;gBACxF,sFAAsF,KAAK,EAAE,CAAC;IAClG,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,gFAAgF;AAChF,6EAA6E;AAC7E,IAAI,MAAM,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACxC,SAAS;QACP,4EAA4E;YAC5E,+EAA+E;YAC/E,oEAAoE,CAAC;AACzE,CAAC;AAED,WAAW,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC"}
|
package/dist/hooks/stop.js
CHANGED
|
@@ -3,13 +3,19 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { join, dirname } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { emitContext, readHookInput } from "../hook-io.js";
|
|
6
|
+
import { readProjectMarker } from "../project-marker.js";
|
|
6
7
|
const CONTENT_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "content");
|
|
7
8
|
const CHECKPOINT_EVERY_TURNS = 5;
|
|
8
|
-
const
|
|
9
|
+
const input = readHookInput();
|
|
10
|
+
const turn = input.turn_number;
|
|
9
11
|
if (!Number.isInteger(turn) || turn < 1)
|
|
10
12
|
process.exit(0);
|
|
11
13
|
if (turn % CHECKPOINT_EVERY_TURNS !== 0)
|
|
12
14
|
process.exit(0);
|
|
15
|
+
// The checkpoint asks the model to write to project memory, which needs a
|
|
16
|
+
// project. Unbound repositories get nothing, same as the other hooks.
|
|
17
|
+
if (!readProjectMarker(input.cwd ?? process.cwd()))
|
|
18
|
+
process.exit(0);
|
|
13
19
|
let directive;
|
|
14
20
|
try {
|
|
15
21
|
directive = readFileSync(join(CONTENT_ROOT, "checkpoint.md"), "utf8").trim();
|
package/dist/hooks/stop.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/hooks/stop.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/hooks/stop.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;AAC1F,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAEjC,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;AAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC;AAC/B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAK,IAAe,GAAG,CAAC;IAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACrE,IAAK,IAAe,GAAG,sBAAsB,KAAK,CAAC;IAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAErE,0EAA0E;AAC1E,sEAAsE;AACtE,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEpE,IAAI,SAAiB,CAAC;AACtB,IAAI,CAAC;IACH,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC;AAAC,MAAM,CAAC;IACP,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC"}
|
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { callTool, McpCallError } from "../mcp-client.js";
|
|
3
3
|
import { discoverDeployment } from "../discovery.js";
|
|
4
|
-
import { emitContext, readHookInput } from "../hook-io.js";
|
|
4
|
+
import { emitContext, promptText, readHookInput } from "../hook-io.js";
|
|
5
5
|
import { readProjectMarker } from "../project-marker.js";
|
|
6
6
|
import { claimOnce } from "../session-marker.js";
|
|
7
7
|
import { writeSessionStatus } from "../session-status.js";
|
|
8
8
|
const MAX_CONTEXT_CHARS = 6000;
|
|
9
|
+
// The composite tool searches memory, documents and source concurrently, so it
|
|
10
|
+
// is slower than a bare recall. This hook blocks the user's prompt, so the
|
|
11
|
+
// ceiling stays close to the old one rather than tracking the server's own
|
|
12
|
+
// per-section timeout.
|
|
13
|
+
const GATHER_TIMEOUT_MS = 10000;
|
|
9
14
|
const RECALL_TIMEOUT_MS = 8000;
|
|
15
|
+
// Keywords are extracted from the task, so a pasted stack trace or diff adds
|
|
16
|
+
// upload on a path that blocks the prompt without adding signal.
|
|
17
|
+
const MAX_TASK_CHARS = 2000;
|
|
10
18
|
const input = readHookInput();
|
|
11
|
-
if (!claimOnce(input.session_id, "recall"))
|
|
12
|
-
process.exit(0);
|
|
13
19
|
const apiKey = process.env["CLAUDE_PLUGIN_OPTION_JITERA_API_KEY"] ?? "";
|
|
14
20
|
if (!apiKey)
|
|
15
21
|
process.exit(0);
|
|
22
|
+
// Without .jitera.json this repository is not bound to a project, so there is
|
|
23
|
+
// no project whose context could be gathered. Bail before discovery rather
|
|
24
|
+
// than spend a network round trip guessing at a project nobody chose; session
|
|
25
|
+
// start is where the user is told how to bind it.
|
|
26
|
+
const marker = readProjectMarker(input.cwd ?? process.cwd());
|
|
27
|
+
if (!marker)
|
|
28
|
+
process.exit(0);
|
|
29
|
+
// Claimed only once there is something to claim it for: burning the session's
|
|
30
|
+
// single gather on an unbound repo would mean running init mid-session never
|
|
31
|
+
// took effect.
|
|
32
|
+
if (!claimOnce(input.session_id, "recall"))
|
|
33
|
+
process.exit(0);
|
|
16
34
|
let url;
|
|
17
35
|
const override = process.env["JITERA_MCP_URL"];
|
|
18
36
|
if (override) {
|
|
@@ -31,24 +49,60 @@ else {
|
|
|
31
49
|
process.exit(0);
|
|
32
50
|
}
|
|
33
51
|
}
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
const transport = { url, apiKey, projectUuid: marker.project };
|
|
53
|
+
const task = promptText(input).slice(0, MAX_TASK_CHARS);
|
|
54
|
+
// The blind recall serves two purposes: it is what deployments predating the
|
|
55
|
+
// composite tool answer, and it is the right call when there is no prompt text
|
|
56
|
+
// to gather for. It stays query-less on purpose — a server without the widening
|
|
57
|
+
// ladder can return less for a query than for no query.
|
|
58
|
+
const recall = () => callTool({
|
|
59
|
+
...transport,
|
|
60
|
+
name: "recall_jitera_memory",
|
|
61
|
+
args: {},
|
|
62
|
+
timeoutMs: RECALL_TIMEOUT_MS,
|
|
63
|
+
});
|
|
64
|
+
// The fallback exists for deployments that predate the composite tool, so it
|
|
65
|
+
// is worth a second round trip only when the tool is genuinely absent. Falling
|
|
66
|
+
// back on a timeout would stack RECALL_TIMEOUT_MS on top of GATHER_TIMEOUT_MS
|
|
67
|
+
// while the user's prompt is blocked.
|
|
68
|
+
function toolIsMissing(error) {
|
|
69
|
+
if (!(error instanceof McpCallError))
|
|
70
|
+
return false;
|
|
71
|
+
const detail = error.detail;
|
|
72
|
+
if (detail?.code === -32601 || detail?.code === -32602)
|
|
73
|
+
return true;
|
|
74
|
+
return /unknown tool|tool not found|method not found/i.test(error.message);
|
|
45
75
|
}
|
|
46
|
-
|
|
76
|
+
function giveUp(error) {
|
|
47
77
|
const message = error instanceof McpCallError ? error.message : String(error);
|
|
48
78
|
writeSessionStatus(input.session_id, { recallError: message });
|
|
49
|
-
process.stderr.write(`jitera-connect: could not load project
|
|
79
|
+
process.stderr.write(`jitera-connect: could not load project context: ${message}\n`);
|
|
50
80
|
process.exit(0);
|
|
51
81
|
}
|
|
82
|
+
let memory;
|
|
83
|
+
const startedAt = Date.now();
|
|
84
|
+
try {
|
|
85
|
+
// Gathering for an empty task makes the server ask for one, and that answer
|
|
86
|
+
// would be injected as though it were project context.
|
|
87
|
+
memory = task
|
|
88
|
+
? await callTool({
|
|
89
|
+
...transport,
|
|
90
|
+
name: "gather_jitera_context",
|
|
91
|
+
args: { task, budget: MAX_CONTEXT_CHARS },
|
|
92
|
+
timeoutMs: GATHER_TIMEOUT_MS,
|
|
93
|
+
})
|
|
94
|
+
: await recall();
|
|
95
|
+
}
|
|
96
|
+
catch (gatherError) {
|
|
97
|
+
if (!task || !toolIsMissing(gatherError))
|
|
98
|
+
giveUp(gatherError);
|
|
99
|
+
try {
|
|
100
|
+
memory = await recall();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
giveUp(error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
52
106
|
writeSessionStatus(input.session_id, {
|
|
53
107
|
recallMs: Date.now() - startedAt,
|
|
54
108
|
recallChars: memory.length,
|
|
@@ -57,9 +111,10 @@ writeSessionStatus(input.session_id, {
|
|
|
57
111
|
if (!memory)
|
|
58
112
|
process.exit(0);
|
|
59
113
|
const body = memory.length > MAX_CONTEXT_CHARS
|
|
60
|
-
? `${memory.slice(0, MAX_CONTEXT_CHARS)}\n\n[truncated, call
|
|
114
|
+
? `${memory.slice(0, MAX_CONTEXT_CHARS)}\n\n[truncated, call gather_jitera_context for the rest]`
|
|
61
115
|
: memory;
|
|
62
|
-
emitContext("UserPromptSubmit", `
|
|
63
|
-
`Treat this as current.
|
|
64
|
-
`and persist
|
|
116
|
+
emitContext("UserPromptSubmit", `Project context for this workspace, gathered at session start:\n\n${body}\n\n` +
|
|
117
|
+
`Treat this as current. It was gathered for the first prompt of this session, so call ` +
|
|
118
|
+
`gather_jitera_context yourself when the task moves on to a different area, and persist ` +
|
|
119
|
+
`new decisions with remember_jitera_memory.`);
|
|
65
120
|
//# sourceMappingURL=user-prompt-submit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-prompt-submit.js","sourceRoot":"","sources":["../../src/hooks/user-prompt-submit.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"user-prompt-submit.js","sourceRoot":"","sources":["../../src/hooks/user-prompt-submit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-marker.d.ts","sourceRoot":"","sources":["../src/project-marker.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAM9C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,
|
|
1
|
+
{"version":3,"file":"project-marker.d.ts","sourceRoot":"","sources":["../src/project-marker.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAM9C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAsClF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,EACrB,MAAM,UAAQ,GACb,iBAAiB,CAuBnB"}
|
package/dist/project-marker.js
CHANGED
|
@@ -16,8 +16,15 @@ export function readProjectMarker(startDir) {
|
|
|
16
16
|
catch {
|
|
17
17
|
return undefined;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// `null` parses cleanly and then throws on property access, and a array
|
|
20
|
+
// or a bare scalar is not a marker either. Malformed reads as unbound,
|
|
21
|
+
// the same as unparseable, because hooks must never break a session.
|
|
22
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const fields = parsed;
|
|
26
|
+
const environment = fields["environment"];
|
|
27
|
+
const project = fields["project"];
|
|
21
28
|
return {
|
|
22
29
|
path,
|
|
23
30
|
...(typeof environment === "string" && SAFE_ENVIRONMENT.test(environment)
|
|
@@ -26,6 +33,11 @@ export function readProjectMarker(startDir) {
|
|
|
26
33
|
...(typeof project === "string" ? { project } : {}),
|
|
27
34
|
};
|
|
28
35
|
}
|
|
36
|
+
// Never look past the repository root. `init` writes the marker there, so
|
|
37
|
+
// a stray .jitera.json in a parent directory (or $HOME) would otherwise
|
|
38
|
+
// bind every repository beneath it to a project nobody chose for them.
|
|
39
|
+
if (existsSync(join(dir, ".git")))
|
|
40
|
+
return undefined;
|
|
29
41
|
const parent = dirname(dir);
|
|
30
42
|
if (parent === dir)
|
|
31
43
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-marker.js","sourceRoot":"","sources":["../src/project-marker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,CAAC,MAAM,eAAe,GAAG,cAAc,CAAC;AAE9C,8EAA8E;AAC9E,2CAA2C;AAC3C,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAWhD,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,IAAI,GAAG,GAAG,QAAQ,CAAC;IACnB,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QACxC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,IAAI,
|
|
1
|
+
{"version":3,"file":"project-marker.js","sourceRoot":"","sources":["../src/project-marker.ts"],"names":[],"mappings":"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"}
|
package/dist/statusline.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { SessionStatus } from "./session-status.ts";
|
|
|
2
2
|
export interface StatusLineInput {
|
|
3
3
|
readonly status?: SessionStatus | undefined;
|
|
4
4
|
readonly markerEnvironment?: string | undefined;
|
|
5
|
+
readonly bound?: boolean | undefined;
|
|
5
6
|
}
|
|
6
|
-
export declare function renderStatusLine({ status, markerEnvironment }: StatusLineInput): string;
|
|
7
|
+
export declare function renderStatusLine({ status, markerEnvironment, bound, }: StatusLineInput): string;
|
|
7
8
|
//# sourceMappingURL=statusline.d.ts.map
|
package/dist/statusline.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusline.d.ts","sourceRoot":"","sources":["../src/statusline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOzD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"statusline.d.ts","sourceRoot":"","sources":["../src/statusline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOzD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAGhD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACtC;AAUD,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,iBAAiB,EACjB,KAAK,GACN,EAAE,eAAe,GAAG,MAAM,CAwB1B"}
|
package/dist/statusline.js
CHANGED
|
@@ -8,11 +8,16 @@ function compactChars(chars) {
|
|
|
8
8
|
function compactMs(ms) {
|
|
9
9
|
return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${ms}ms`;
|
|
10
10
|
}
|
|
11
|
-
export function renderStatusLine({ status, markerEnvironment }) {
|
|
11
|
+
export function renderStatusLine({ status, markerEnvironment, bound, }) {
|
|
12
12
|
if (!status)
|
|
13
13
|
return `${DIM}○${RESET} jitera`;
|
|
14
14
|
if (!status.configured)
|
|
15
15
|
return `${DIM}○${RESET} jitera · not connected`;
|
|
16
|
+
// Connected, but this repository is not bound to a project, so the hooks
|
|
17
|
+
// deliberately do nothing here. Carry the command that fixes it.
|
|
18
|
+
if (bound === false) {
|
|
19
|
+
return `${YELLOW}○${RESET} jitera · not initialised · npx @jitera/connect init`;
|
|
20
|
+
}
|
|
16
21
|
const dot = status.recallError ? `${YELLOW}●${RESET}` : `${GREEN}●${RESET}`;
|
|
17
22
|
const parts = [`${dot} jitera`];
|
|
18
23
|
if (status.environment)
|
package/dist/statusline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusline.js","sourceRoot":"","sources":["../src/statusline.ts"],"names":[],"mappings":"AAEA,MAAM,KAAK,GAAG,OAAO,CAAC;AACtB,MAAM,MAAM,GAAG,OAAO,CAAC;AACvB,MAAM,GAAG,GAAG,MAAM,CAAC;AACnB,MAAM,KAAK,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"statusline.js","sourceRoot":"","sources":["../src/statusline.ts"],"names":[],"mappings":"AAEA,MAAM,KAAK,GAAG,OAAO,CAAC;AACtB,MAAM,MAAM,GAAG,OAAO,CAAC;AACvB,MAAM,GAAG,GAAG,MAAM,CAAC;AACnB,MAAM,KAAK,GAAG,MAAM,CAAC;AAUrB,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC;AACxE,CAAC;AAED,SAAS,SAAS,CAAC,EAAU;IAC3B,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAC/B,MAAM,EACN,iBAAiB,EACjB,KAAK,GACW;IAChB,IAAI,CAAC,MAAM;QAAE,OAAO,GAAG,GAAG,IAAI,KAAK,SAAS,CAAC;IAC7C,IAAI,CAAC,MAAM,CAAC,UAAU;QAAE,OAAO,GAAG,GAAG,IAAI,KAAK,yBAAyB,CAAC;IACxE,yEAAyE;IACzE,iEAAiE;IACjE,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;QACpB,OAAO,GAAG,MAAM,IAAI,KAAK,sDAAsD,CAAC;IAClF,CAAC;IAED,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,KAAK,EAAE,CAAC;IAC5E,MAAM,KAAK,GAAG,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC;IAChC,IAAI,MAAM,CAAC,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAEvD,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC9B,CAAC;SAAM,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;QACzF,KAAK,CAAC,IAAI,CAAC,UAAU,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACzF,CAAC;IAED,IAAI,iBAAiB,IAAI,MAAM,CAAC,WAAW,IAAI,iBAAiB,KAAK,MAAM,CAAC,WAAW,EAAE,CAAC;QACxF,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,cAAc,iBAAiB,GAAG,KAAK,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC"}
|
package/package.json
CHANGED
|
@@ -17,6 +17,24 @@ Three virtual domains:
|
|
|
17
17
|
| `sources/` | Project source code | no |
|
|
18
18
|
| `uploads/` | Uploaded files | no |
|
|
19
19
|
|
|
20
|
+
## Start with the whole picture
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
gather_jitera_context(task="how do refunds get issued", budget=6000)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Recalls project memory and searches documentation and source in one call,
|
|
27
|
+
running the widening fallbacks itself: memory falls back to every stored entity
|
|
28
|
+
when keywords match nothing, and search falls back to fuzzy matching when exact
|
|
29
|
+
matching is dry. It also lists the project's agents, so a recalled fact can be
|
|
30
|
+
attributed. Use it before planning or answering, then narrow with the tools
|
|
31
|
+
below.
|
|
32
|
+
|
|
33
|
+
It is not a once-per-session lookup. Call it again whenever the work reaches
|
|
34
|
+
something you have not gathered context for yet: a subsystem you have not
|
|
35
|
+
touched, a name you do not recognise, or a decision about how something should
|
|
36
|
+
work. Checking costs one call; guessing costs the rest of the session.
|
|
37
|
+
|
|
20
38
|
## Searching
|
|
21
39
|
|
|
22
40
|
```
|
|
@@ -27,6 +45,10 @@ resource_search(pattern="documents/**/*.md", content="refund", limit=30, offset=
|
|
|
27
45
|
domain prefix to search every domain: `resource_search(pattern="**/checkout*.*")`
|
|
28
46
|
- `content` — a grep-style match inside files, supporting boolean expressions:
|
|
29
47
|
`content="(api OR rest) AND controller"`
|
|
48
|
+
- `mode` — how `content` is matched. `default` is a case-insensitive substring;
|
|
49
|
+
`regex` treats `content` as a case-insensitive regular expression; `full-text`
|
|
50
|
+
matches fuzzily by trigram similarity, for when you are unsure of the exact
|
|
51
|
+
wording. `pattern` is always a glob, whatever the mode
|
|
30
52
|
- Results are grouped by domain. Open a match with `resource_read`
|
|
31
53
|
|
|
32
54
|
Search before reading. Do not guess paths.
|
|
@@ -31,17 +31,29 @@ failed.
|
|
|
31
31
|
|
|
32
32
|
### When the first recall comes back thin
|
|
33
33
|
|
|
34
|
-
`
|
|
35
|
-
|
|
34
|
+
`gather_jitera_context` runs this ladder for you and says when it widened, so
|
|
35
|
+
prefer it for a first look. Reach for `recall_jitera_memory` directly when you
|
|
36
|
+
want memory alone, and work down the ladder yourself:
|
|
36
37
|
|
|
37
38
|
1. `recall_jitera_memory(query="<specific terms from the task>")`
|
|
38
39
|
2. `recall_jitera_memory(query="<the subsystem or domain>")`
|
|
39
40
|
3. `recall_jitera_memory()` with no query — returns every stored entity
|
|
40
41
|
|
|
42
|
+
Keywords are combined with OR, so *more* terms match more entities. There is no
|
|
43
|
+
narrower rung below step 1 — when it comes back empty, widen rather than
|
|
44
|
+
refine.
|
|
45
|
+
|
|
41
46
|
Do not conclude that memory is empty until step 3 returns nothing. Results
|
|
42
47
|
containing `[[id]]` references mean related entities exist, and a broader query
|
|
43
48
|
will surface them.
|
|
44
49
|
|
|
50
|
+
### Whose memory you are reading
|
|
51
|
+
|
|
52
|
+
Each agent in a project writes to its own partition. A project-level key reads
|
|
53
|
+
across all of them, so a recalled fact may have been written by any agent;
|
|
54
|
+
`list_jitera_agents` names them. A key pinned to one agent sees only that
|
|
55
|
+
agent's own partition.
|
|
56
|
+
|
|
45
57
|
## Remember when knowledge is created
|
|
46
58
|
|
|
47
59
|
Persist with `remember_jitera_memory(name, type, attrs)` when:
|
package/templates/AGENTS.md.tmpl
CHANGED
|
@@ -4,16 +4,22 @@
|
|
|
4
4
|
This repository is connected to a {{BRAND}} project. Documentation, source, and a
|
|
5
5
|
shared project memory are available through the `jitera` MCP server.
|
|
6
6
|
|
|
7
|
-
Before planning or implementing anything,
|
|
8
|
-
|
|
7
|
+
Before planning or implementing anything, and before answering a question about
|
|
8
|
+
this project, call `gather_jitera_context` with the task in your own words. It
|
|
9
|
+
recalls project memory and searches documentation and source in one pass. An
|
|
10
|
+
empty result is normal and is not an error.
|
|
11
|
+
|
|
12
|
+
Keep using it as the work moves. It is not a once-per-session lookup: call it
|
|
13
|
+
again whenever you reach an area you have not gathered context for yet.
|
|
9
14
|
|
|
10
15
|
Before writing a spec or design, search `documents/` with `resource_search` and
|
|
11
16
|
read any match with `resource_read`. Update the existing document rather than
|
|
12
17
|
creating a second one.
|
|
13
18
|
|
|
14
19
|
When a decision is made, a constraint is discovered, or you are corrected,
|
|
15
|
-
persist it with `remember_jitera_memory`. Recall the entity first
|
|
16
|
-
full attribute list, because entities are
|
|
20
|
+
persist it with `remember_jitera_memory`. Recall the entity first with
|
|
21
|
+
`recall_jitera_memory` and re-send its full attribute list, because entities are
|
|
22
|
+
keyed on name plus type.
|
|
17
23
|
|
|
18
24
|
`documents/` is readable and writable. `sources/` and `uploads/` are read-only.
|
|
19
25
|
|