@intentic/claude-plugin 1.317.0 → 1.319.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 +2 -8
- package/README.md +4 -7
- package/bin/fileq +2 -2
- package/dist/fileq.mjs +90493 -88470
- package/dist/post-bash.mjs +0 -6
- package/dist/prompt-submit.mjs +2 -63
- package/dist/session-start.mjs +4 -27
- package/dist/stats.mjs +13 -77
- package/generated/fileq-NOTICE +208 -0
- package/generated/skills/field-notes/SKILL.md +3 -1
- package/generated/skills/fileq/SKILL.md +43 -8
- package/generated/skills/iq/SKILL.md +6 -0
- package/hooks/hooks.json +1 -11
- package/package.json +4 -4
- package/dist/post-edit.mjs +0 -252
|
@@ -33,6 +33,12 @@ iq "where do we enforce the secrets floor?"
|
|
|
33
33
|
|
|
34
34
|
Every answer opens with a capsule (`answer:`, `candidates:`, `more:`). Read that and stop — do not pipe through `head`; `--budget` already caps output. Scope with `--in`, `--repo`, `--lang`, `--glob`. Wrong grep habits (`iq search`, `iq ask`) are rewritten to `q`; use canonical forms next time. Full verb list: `iq --help`.
|
|
35
35
|
|
|
36
|
+
A question's `answer:` line ends with a verdict:
|
|
37
|
+
|
|
38
|
+
- `confident` — the top result stands out. Read it and stop.
|
|
39
|
+
- `ambiguous` — the top files scored close. The answer is probably among them and the `candidates:`; compare those rather than searching again.
|
|
40
|
+
- `weak` — no result scored as a likely answer, so what you asked about probably does not exist in this workspace. Stop and say so, or rephrase once in the code's own words (the name you would expect it to have). A second `weak` means it is not here. Do not read on through the candidates or fall back to grep hunting for it.
|
|
41
|
+
|
|
36
42
|
**Session recall:** `iq sessions grab "topic"` for ranked excerpts from past sessions; `iq sessions files "topic"` for files those sessions touched. Verify load-bearing hits against current code.
|
|
37
43
|
|
|
38
44
|
**Another conversation**, rather than what past sessions touched, is the `agents` CLI: `agents show <handle>` answers one whole (its task, where it got to, branch, worktree, delta, record) from any spelling of its name — id, branch, id prefix, session id, or title words. `agents ls` is the fleet, `agents find '<text>'` is who said a phrase. Never search `/history` by hand for one.
|
package/hooks/hooks.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "intentic: session context (map, field notes, iq teaching), Bash output cleaning
|
|
2
|
+
"description": "intentic: session context (map, field notes, iq teaching), Bash output cleaning. Each script reads its own /config switch and answers nothing when it is off. Without node on PATH the tool hooks stand down silently and each session opens with one line saying why nothing ran.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"SessionStart": [
|
|
5
5
|
{
|
|
@@ -33,16 +33,6 @@
|
|
|
33
33
|
"timeout": 15
|
|
34
34
|
}
|
|
35
35
|
]
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"matcher": "Write|Edit|MultiEdit|NotebookEdit",
|
|
39
|
-
"hooks": [
|
|
40
|
-
{
|
|
41
|
-
"type": "command",
|
|
42
|
-
"command": "command -v node >/dev/null 2>&1 && node \"${CLAUDE_PLUGIN_ROOT}/dist/post-edit.mjs\"; exit 0",
|
|
43
|
-
"timeout": 10
|
|
44
|
-
}
|
|
45
|
-
]
|
|
46
36
|
}
|
|
47
37
|
]
|
|
48
38
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentic/claude-plugin",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "The intentic Claude Code plugin: trimmed Bash output, a project map and field notes at session start, iq search and fileq teaching,
|
|
3
|
+
"version": "1.319.0",
|
|
4
|
+
"description": "The intentic Claude Code plugin: trimmed Bash output, a project map and field notes at session start, iq search and fileq teaching, and a /intentic:stats report of what each saved",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@intentic/agent-context": "0.0.0",
|
|
43
|
-
"@intentic/fileq": "1.
|
|
44
|
-
"@intentic/iq": "
|
|
43
|
+
"@intentic/fileq": "1.319.0",
|
|
44
|
+
"@intentic/iq": "1.319.0",
|
|
45
45
|
"@intentic/output-cleaners": "0.0.0",
|
|
46
46
|
"@intentic/testing": "0.0.0",
|
|
47
47
|
"@intentic/tsconfig": "0.0.0",
|
package/dist/post-edit.mjs
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { createRequire as __intenticRequire } from 'node:module'; const require = __intenticRequire(import.meta.url);
|
|
2
|
-
|
|
3
|
-
// src/runtime.ts
|
|
4
|
-
import { spawn } from "node:child_process";
|
|
5
|
-
import { accessSync, closeSync, constants, mkdirSync, openSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
-
import { delimiter, dirname, join } from "node:path";
|
|
7
|
-
var pluginRoot = (env = process.env) => env["CLAUDE_PLUGIN_ROOT"] ?? dirname(import.meta.dirname);
|
|
8
|
-
var startDetached = (command, args, options) => {
|
|
9
|
-
mkdirSync(dirname(options.log), { recursive: true });
|
|
10
|
-
const out = openSync(options.log, "w");
|
|
11
|
-
try {
|
|
12
|
-
const child = spawn(command, args, { cwd: options.cwd, detached: true, stdio: ["ignore", out, out], env: { ...options.env ?? process.env } });
|
|
13
|
-
child.on("error", () => void 0);
|
|
14
|
-
child.unref();
|
|
15
|
-
} finally {
|
|
16
|
-
closeSync(out);
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
var projectSlug = (project) => project.replace(/[^a-zA-Z0-9]/g, "-");
|
|
20
|
-
|
|
21
|
-
// src/features.ts
|
|
22
|
-
var OPTIONS = {
|
|
23
|
-
output_cleaners: {
|
|
24
|
-
type: "boolean",
|
|
25
|
-
title: "Trim Bash output",
|
|
26
|
-
description: "Clean what a successful command printed before Claude reads it (progress bars, install chatter, repeated lines, the middle of huge logs), keep the full text retrievable with `retrieve-output`, and ledger what each cleaner saved.",
|
|
27
|
-
default: true
|
|
28
|
-
},
|
|
29
|
-
cleaners: {
|
|
30
|
-
type: "string",
|
|
31
|
-
title: "Cleaners",
|
|
32
|
-
description: "Which cleaners run, as the sandbox spells it: empty for all, `-cap,-wide` to switch some off, `git,pnpm` to allow only those. `/intentic:stats` lists them by what they saved.",
|
|
33
|
-
default: ""
|
|
34
|
-
},
|
|
35
|
-
output_holdout: {
|
|
36
|
-
type: "number",
|
|
37
|
-
title: "Untrimmed share",
|
|
38
|
-
description: "Share of commands left untrimmed, so the saving can be measured against a real comparison and not only computed. 0 turns the comparison off; the exact per-command saving is reported either way.",
|
|
39
|
-
default: 0,
|
|
40
|
-
min: 0,
|
|
41
|
-
max: 0.5
|
|
42
|
-
},
|
|
43
|
-
project_map: {
|
|
44
|
-
type: "boolean",
|
|
45
|
-
title: "Project map",
|
|
46
|
-
description: "Open each session with a map of the project it starts in: its areas, what each is for, and where Claude is standing. Worked out from the tree every time, never stored, so it cannot go stale.",
|
|
47
|
-
default: true
|
|
48
|
-
},
|
|
49
|
-
field_notes: {
|
|
50
|
-
type: "boolean",
|
|
51
|
-
title: "Field notes",
|
|
52
|
-
description: "Open each session with this project's field notes: the traps past sessions hit and the commands that really verify. Written by `/intentic:field-notes` from your own sessions here; nothing is sent until you run it once.",
|
|
53
|
-
default: true
|
|
54
|
-
},
|
|
55
|
-
iq: {
|
|
56
|
-
type: "boolean",
|
|
57
|
-
title: "iq search",
|
|
58
|
-
description: "Teach Claude the `iq` code search CLI and recall past sessions on a matching prompt. Needs `npm i -g @intentic/iq`; without it this switch does nothing and says so once.",
|
|
59
|
-
default: true
|
|
60
|
-
},
|
|
61
|
-
fileq: {
|
|
62
|
-
type: "boolean",
|
|
63
|
-
title: "fileq",
|
|
64
|
-
description: "Let Claude read documents, PDFs, spreadsheets, slides, notebooks, images, audio and archives as markdown with the bundled `fileq` CLI. Off makes the command refuse; its one-line skill description stays listed, since a plugin cannot hide its own skills.",
|
|
65
|
-
default: true
|
|
66
|
-
},
|
|
67
|
-
shadows: {
|
|
68
|
-
type: "boolean",
|
|
69
|
-
title: "Document shadows",
|
|
70
|
-
description: "Keep a markdown rendering of every document in the project up to date in the background (a sweep at session start and every ten minutes, and each binary file Claude writes), so a read finds one ready. Costs background CPU on a document-heavy project.",
|
|
71
|
-
default: false
|
|
72
|
-
},
|
|
73
|
-
holdout: {
|
|
74
|
-
type: "number",
|
|
75
|
-
title: "Measured share",
|
|
76
|
-
description: "Share of sessions opened without the map, the field notes and the iq teaching, so `/intentic:stats` can show what each changed. 0 sends them to every session and measures nothing.",
|
|
77
|
-
default: 0.1,
|
|
78
|
-
min: 0,
|
|
79
|
-
max: 0.5
|
|
80
|
-
}
|
|
81
|
-
};
|
|
82
|
-
var asBoolean = (raw, fallback) => {
|
|
83
|
-
const value = raw?.trim().toLowerCase();
|
|
84
|
-
if (value === "true" || value === "1" || value === "yes" || value === "on") {
|
|
85
|
-
return true;
|
|
86
|
-
}
|
|
87
|
-
if (value === "false" || value === "0" || value === "no" || value === "off") {
|
|
88
|
-
return false;
|
|
89
|
-
}
|
|
90
|
-
return fallback;
|
|
91
|
-
};
|
|
92
|
-
var asShare = (raw, option) => {
|
|
93
|
-
const value = raw === void 0 || raw.trim() === "" ? Number.NaN : Number(raw);
|
|
94
|
-
return Number.isFinite(value) ? Math.min(option.max, Math.max(option.min, value)) : option.default;
|
|
95
|
-
};
|
|
96
|
-
var optionEnvName = (key) => `CLAUDE_PLUGIN_OPTION_${key.toUpperCase()}`;
|
|
97
|
-
var readOptions = (env = process.env) => {
|
|
98
|
-
const entries = Object.keys(OPTIONS).map((key) => {
|
|
99
|
-
const option = OPTIONS[key];
|
|
100
|
-
const raw = env[optionEnvName(key)];
|
|
101
|
-
if (option.type === "boolean") {
|
|
102
|
-
return [key, asBoolean(raw, option.default)];
|
|
103
|
-
}
|
|
104
|
-
if (option.type === "number") {
|
|
105
|
-
return [key, asShare(raw, option)];
|
|
106
|
-
}
|
|
107
|
-
return [key, raw ?? option.default];
|
|
108
|
-
});
|
|
109
|
-
return Object.fromEntries(entries);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// src/hook-io.ts
|
|
113
|
-
import { homedir } from "node:os";
|
|
114
|
-
import { join as join2 } from "node:path";
|
|
115
|
-
var readStdin = async () => {
|
|
116
|
-
const chunks = [];
|
|
117
|
-
for await (const chunk of process.stdin) {
|
|
118
|
-
chunks.push(chunk);
|
|
119
|
-
}
|
|
120
|
-
return Buffer.concat(chunks).toString("utf8");
|
|
121
|
-
};
|
|
122
|
-
var parseHookInput = (text) => {
|
|
123
|
-
try {
|
|
124
|
-
const value = JSON.parse(text);
|
|
125
|
-
return typeof value === "object" && value !== null ? value : void 0;
|
|
126
|
-
} catch {
|
|
127
|
-
return void 0;
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
var runHook = async (handle) => {
|
|
131
|
-
try {
|
|
132
|
-
const raw = await readStdin();
|
|
133
|
-
const input = parseHookInput(raw);
|
|
134
|
-
if (input === void 0) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const answer = await handle(input, raw);
|
|
138
|
-
if (typeof answer === "string") {
|
|
139
|
-
process.stdout.write(answer);
|
|
140
|
-
} else if (answer !== void 0) {
|
|
141
|
-
process.stdout.write(`${JSON.stringify(answer)}
|
|
142
|
-
`);
|
|
143
|
-
}
|
|
144
|
-
} catch (error) {
|
|
145
|
-
process.stderr.write(`intentic: ${error instanceof Error ? error.message : String(error)}
|
|
146
|
-
`);
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
var dataDir = (env = process.env) => env["CLAUDE_PLUGIN_DATA"] ?? join2(env["CLAUDE_CONFIG_DIR"] ?? join2(homedir(), ".claude"), "plugins", "data", "intentic-intentic");
|
|
150
|
-
var projectDir = (input, env = process.env) => env["CLAUDE_PROJECT_DIR"] ?? input.cwd ?? process.cwd();
|
|
151
|
-
|
|
152
|
-
// src/shadows.ts
|
|
153
|
-
import { relative, isAbsolute, join as join3 } from "node:path";
|
|
154
|
-
|
|
155
|
-
// ../fileq/src/lib/formats.ts
|
|
156
|
-
import { extname } from "node:path";
|
|
157
|
-
var EXTENSION_FORMAT = {
|
|
158
|
-
".docx": "docx",
|
|
159
|
-
".xlsx": "xlsx",
|
|
160
|
-
".pptx": "pptx",
|
|
161
|
-
".odt": "odt",
|
|
162
|
-
".ott": "odt",
|
|
163
|
-
// a template is the same document with a different intent
|
|
164
|
-
".ods": "ods",
|
|
165
|
-
".ots": "ods",
|
|
166
|
-
".odp": "odp",
|
|
167
|
-
".otp": "odp",
|
|
168
|
-
".odg": "odp",
|
|
169
|
-
// a drawing is a presentation of one page
|
|
170
|
-
".rtf": "rtf",
|
|
171
|
-
".epub": "epub",
|
|
172
|
-
".ipynb": "ipynb",
|
|
173
|
-
// JSON: no magic bytes name it, so the extension is the whole recognition.
|
|
174
|
-
".cpuprofile": "profile",
|
|
175
|
-
// V8 profiles are JSON too: named by extension alone, like a notebook.
|
|
176
|
-
".heapprofile": "profile",
|
|
177
|
-
".pdf": "pdf",
|
|
178
|
-
".png": "image",
|
|
179
|
-
".jpg": "image",
|
|
180
|
-
".jpeg": "image",
|
|
181
|
-
".gif": "image",
|
|
182
|
-
".webp": "image",
|
|
183
|
-
".tif": "image",
|
|
184
|
-
".tiff": "image",
|
|
185
|
-
".heic": "image",
|
|
186
|
-
".avif": "image",
|
|
187
|
-
".mp3": "media",
|
|
188
|
-
".wav": "media",
|
|
189
|
-
".flac": "media",
|
|
190
|
-
".ogg": "media",
|
|
191
|
-
".opus": "media",
|
|
192
|
-
".aac": "media",
|
|
193
|
-
".m4a": "media",
|
|
194
|
-
".mp4": "media",
|
|
195
|
-
".mov": "media",
|
|
196
|
-
".webm": "media",
|
|
197
|
-
".mkv": "media",
|
|
198
|
-
".html": "html",
|
|
199
|
-
".htm": "html",
|
|
200
|
-
// Archives, including the three zip-underneath package formats (a jar, a war and a wheel are each a zip with a
|
|
201
|
-
// manifest); a double extension like `.tar.gz` is recognized by its last one, which is the compressor.
|
|
202
|
-
".zip": "archive",
|
|
203
|
-
".jar": "archive",
|
|
204
|
-
".war": "archive",
|
|
205
|
-
".whl": "archive",
|
|
206
|
-
".tar": "archive",
|
|
207
|
-
".tgz": "archive",
|
|
208
|
-
".gz": "archive",
|
|
209
|
-
".bz2": "archive",
|
|
210
|
-
".xz": "archive",
|
|
211
|
-
".zst": "archive",
|
|
212
|
-
".7z": "archive",
|
|
213
|
-
".rar": "archive"
|
|
214
|
-
};
|
|
215
|
-
var isCandidatePath = (path) => extname(path).toLowerCase() in EXTENSION_FORMAT;
|
|
216
|
-
|
|
217
|
-
// src/shadows.ts
|
|
218
|
-
var SWEEP_EVERY_MS = 10 * 6e4;
|
|
219
|
-
var fileq = (env) => join3(pluginRoot(env), "dist", "fileq.mjs");
|
|
220
|
-
var logOf = (data, project, what) => join3(data, "shadows", `${projectSlug(project)}.${what}.log`);
|
|
221
|
-
var fileqEnv = (env, project) => ({ ...env, WORKSPACE_ROOT: project });
|
|
222
|
-
var derivablePath = (project, file) => {
|
|
223
|
-
const rel = isAbsolute(file) ? relative(project, file) : file;
|
|
224
|
-
return rel === "" || rel.startsWith("..") || isAbsolute(rel) || !isCandidatePath(rel) ? void 0 : rel.split("\\").join("/");
|
|
225
|
-
};
|
|
226
|
-
var deriveNow = (data, project, rel, env = process.env) => {
|
|
227
|
-
startDetached(process.execPath, [fileq(env), "derive", rel], { cwd: project, log: logOf(data, project, "derive"), env: fileqEnv(env, project) });
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// src/post-edit.ts
|
|
231
|
-
var PATH_KEYS = ["file_path", "notebook_path"];
|
|
232
|
-
var postEdit = (input, env = process.env) => {
|
|
233
|
-
if (!readOptions(env).shadows) {
|
|
234
|
-
return void 0;
|
|
235
|
-
}
|
|
236
|
-
const project = projectDir(input, env);
|
|
237
|
-
for (const key of PATH_KEYS) {
|
|
238
|
-
const file = input.tool_input?.[key];
|
|
239
|
-
const rel = typeof file === "string" ? derivablePath(project, file) : void 0;
|
|
240
|
-
if (rel !== void 0) {
|
|
241
|
-
deriveNow(dataDir(env), project, rel, env);
|
|
242
|
-
return void 0;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return void 0;
|
|
246
|
-
};
|
|
247
|
-
if (process.argv[1]?.endsWith("post-edit.mjs")) {
|
|
248
|
-
await runHook((input) => postEdit(input));
|
|
249
|
-
}
|
|
250
|
-
export {
|
|
251
|
-
postEdit
|
|
252
|
-
};
|