token-pilot 0.19.2 → 0.23.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/hooks/hooks.json +30 -0
- package/.claude-plugin/plugin.json +2 -2
- package/CHANGELOG.md +165 -0
- package/README.md +194 -313
- package/dist/agents/tp-audit-scanner.md +49 -0
- package/dist/agents/tp-commit-writer.md +41 -0
- package/dist/agents/tp-dead-code-finder.md +43 -0
- package/dist/agents/tp-debugger.md +45 -0
- package/dist/agents/tp-history-explorer.md +43 -0
- package/dist/agents/tp-impact-analyzer.md +44 -0
- package/dist/agents/tp-migration-scout.md +43 -0
- package/dist/agents/tp-onboard.md +40 -0
- package/dist/agents/tp-pr-reviewer.md +41 -0
- package/dist/agents/tp-refactor-planner.md +42 -0
- package/dist/agents/tp-run.md +48 -0
- package/dist/agents/tp-session-restorer.md +47 -0
- package/dist/agents/tp-test-triage.md +40 -0
- package/dist/agents/tp-test-writer.md +46 -0
- package/dist/cli/agent-frontmatter.d.ts +48 -0
- package/dist/cli/agent-frontmatter.js +189 -0
- package/dist/cli/bless-agents.d.ts +65 -0
- package/dist/cli/bless-agents.js +307 -0
- package/dist/cli/claudeignore.d.ts +33 -0
- package/dist/cli/claudeignore.js +88 -0
- package/dist/cli/claudemd-hygiene.d.ts +26 -0
- package/dist/cli/claudemd-hygiene.js +43 -0
- package/dist/cli/doctor-drift.d.ts +31 -0
- package/dist/cli/doctor-drift.js +130 -0
- package/dist/cli/doctor-env-check.d.ts +25 -0
- package/dist/cli/doctor-env-check.js +91 -0
- package/dist/cli/install-agents.d.ts +108 -0
- package/dist/cli/install-agents.js +402 -0
- package/dist/cli/save-doc.d.ts +42 -0
- package/dist/cli/save-doc.js +145 -0
- package/dist/cli/scan-agents.d.ts +46 -0
- package/dist/cli/scan-agents.js +227 -0
- package/dist/cli/stats.d.ts +36 -0
- package/dist/cli/stats.js +131 -0
- package/dist/cli/typo-guard.d.ts +27 -0
- package/dist/cli/typo-guard.js +119 -0
- package/dist/cli/unbless-agents.d.ts +33 -0
- package/dist/cli/unbless-agents.js +85 -0
- package/dist/cli/uninstall-agents.d.ts +36 -0
- package/dist/cli/uninstall-agents.js +117 -0
- package/dist/config/defaults.d.ts +1 -1
- package/dist/config/defaults.js +14 -8
- package/dist/config/loader.d.ts +1 -1
- package/dist/config/loader.js +105 -11
- package/dist/core/context-registry.d.ts +16 -1
- package/dist/core/context-registry.js +60 -28
- package/dist/core/event-log.d.ts +79 -0
- package/dist/core/event-log.js +190 -0
- package/dist/core/session-registry.d.ts +43 -0
- package/dist/core/session-registry.js +113 -0
- package/dist/core/session-savings.d.ts +19 -0
- package/dist/core/session-savings.js +60 -0
- package/dist/handlers/session-budget.d.ts +32 -0
- package/dist/handlers/session-budget.js +61 -0
- package/dist/handlers/session-snapshot-persist.d.ts +22 -0
- package/dist/handlers/session-snapshot-persist.js +76 -0
- package/dist/hooks/adaptive-threshold.d.ts +27 -0
- package/dist/hooks/adaptive-threshold.js +46 -0
- package/dist/hooks/format-deny-message.d.ts +21 -0
- package/dist/hooks/format-deny-message.js +147 -0
- package/dist/hooks/installer.js +130 -31
- package/dist/hooks/path-safety.d.ts +16 -0
- package/dist/hooks/path-safety.js +34 -0
- package/dist/hooks/post-bash.d.ts +46 -0
- package/dist/hooks/post-bash.js +77 -0
- package/dist/hooks/post-task.d.ts +67 -0
- package/dist/hooks/post-task.js +136 -0
- package/dist/hooks/session-start.d.ts +45 -0
- package/dist/hooks/session-start.js +179 -0
- package/dist/hooks/summary-ast-index.d.ts +28 -0
- package/dist/hooks/summary-ast-index.js +122 -0
- package/dist/hooks/summary-head-tail.d.ts +15 -0
- package/dist/hooks/summary-head-tail.js +78 -0
- package/dist/hooks/summary-pipeline.d.ts +35 -0
- package/dist/hooks/summary-pipeline.js +63 -0
- package/dist/hooks/summary-regex.d.ts +14 -0
- package/dist/hooks/summary-regex.js +130 -0
- package/dist/hooks/summary-types.d.ts +29 -0
- package/dist/hooks/summary-types.js +9 -0
- package/dist/index.d.ts +15 -3
- package/dist/index.js +538 -149
- package/dist/integration/context-mode-detector.d.ts +7 -1
- package/dist/integration/context-mode-detector.js +51 -15
- package/dist/server/tool-definitions.d.ts +149 -0
- package/dist/server/tool-definitions.js +424 -202
- package/dist/server.d.ts +1 -1
- package/dist/server.js +456 -179
- package/dist/templates/agent-builder.d.ts +49 -0
- package/dist/templates/agent-builder.js +104 -0
- package/dist/types.d.ts +38 -4
- package/package.json +4 -2
- package/skills/stats/SKILL.md +13 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summary-generation pipeline for the deny-enhanced hook.
|
|
3
|
+
*
|
|
4
|
+
* Tries three parsers in order — ast-index subprocess → regex → head+tail —
|
|
5
|
+
* and returns the first result with non-empty signals. If every parser
|
|
6
|
+
* fails to produce useful output (or throws), the pipeline reports
|
|
7
|
+
* `pass-through`, at which point the handler lets the original Read
|
|
8
|
+
* proceed unmodified rather than emitting an empty / misleading denial.
|
|
9
|
+
*
|
|
10
|
+
* Each parser is injectable for tests. The default wiring uses the
|
|
11
|
+
* real implementations from sibling modules.
|
|
12
|
+
*/
|
|
13
|
+
import type { HookSummary } from "./summary-types.js";
|
|
14
|
+
export type PipelineTier = "ast-index" | "regex" | "head-tail";
|
|
15
|
+
export type PipelineResult = {
|
|
16
|
+
kind: "summary";
|
|
17
|
+
summary: HookSummary;
|
|
18
|
+
tier: PipelineTier;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "pass-through";
|
|
21
|
+
reason: string;
|
|
22
|
+
};
|
|
23
|
+
type AstIndexFn = (content: string, filePath: string) => Promise<HookSummary | null>;
|
|
24
|
+
type SyncSummaryFn = (content: string, filePath: string) => HookSummary;
|
|
25
|
+
export interface PipelineOptions {
|
|
26
|
+
/** Primary parser — ast-index subprocess. Returns null on soft fail. */
|
|
27
|
+
astIndex?: AstIndexFn;
|
|
28
|
+
/** Fallback parser — regex. Expected to always return a HookSummary. */
|
|
29
|
+
regex?: SyncSummaryFn;
|
|
30
|
+
/** Last-resort parser — head+tail. Expected to always return a HookSummary. */
|
|
31
|
+
headTail?: SyncSummaryFn;
|
|
32
|
+
}
|
|
33
|
+
export declare function runSummaryPipeline(content: string, filePath: string, options?: PipelineOptions): Promise<PipelineResult>;
|
|
34
|
+
export {};
|
|
35
|
+
//# sourceMappingURL=summary-pipeline.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Summary-generation pipeline for the deny-enhanced hook.
|
|
3
|
+
*
|
|
4
|
+
* Tries three parsers in order — ast-index subprocess → regex → head+tail —
|
|
5
|
+
* and returns the first result with non-empty signals. If every parser
|
|
6
|
+
* fails to produce useful output (or throws), the pipeline reports
|
|
7
|
+
* `pass-through`, at which point the handler lets the original Read
|
|
8
|
+
* proceed unmodified rather than emitting an empty / misleading denial.
|
|
9
|
+
*
|
|
10
|
+
* Each parser is injectable for tests. The default wiring uses the
|
|
11
|
+
* real implementations from sibling modules.
|
|
12
|
+
*/
|
|
13
|
+
import { parseAstIndexSummary } from "./summary-ast-index.js";
|
|
14
|
+
import { parseRegexSummary } from "./summary-regex.js";
|
|
15
|
+
import { parseHeadTailSummary } from "./summary-head-tail.js";
|
|
16
|
+
const defaultAstIndex = async (content, filePath) => parseAstIndexSummary(content, filePath);
|
|
17
|
+
const defaultRegex = parseRegexSummary;
|
|
18
|
+
const defaultHeadTail = parseHeadTailSummary;
|
|
19
|
+
function hasSignals(summary) {
|
|
20
|
+
return !!summary && summary.signals.length > 0;
|
|
21
|
+
}
|
|
22
|
+
async function tryAsync(fn) {
|
|
23
|
+
try {
|
|
24
|
+
return await fn();
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function trySync(fn) {
|
|
31
|
+
try {
|
|
32
|
+
return fn();
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function runSummaryPipeline(content, filePath, options = {}) {
|
|
39
|
+
const astIndex = options.astIndex ?? defaultAstIndex;
|
|
40
|
+
const regex = options.regex ?? defaultRegex;
|
|
41
|
+
const headTail = options.headTail ?? defaultHeadTail;
|
|
42
|
+
// Tier 1 — ast-index. Soft-fails on null or throw.
|
|
43
|
+
const astResult = await tryAsync(() => astIndex(content, filePath));
|
|
44
|
+
if (hasSignals(astResult)) {
|
|
45
|
+
return { kind: "summary", summary: astResult, tier: "ast-index" };
|
|
46
|
+
}
|
|
47
|
+
// Tier 2 — regex. Empty signals means "nothing useful here, try next".
|
|
48
|
+
const regexResult = trySync(() => regex(content, filePath));
|
|
49
|
+
if (hasSignals(regexResult)) {
|
|
50
|
+
return { kind: "summary", summary: regexResult, tier: "regex" };
|
|
51
|
+
}
|
|
52
|
+
// Tier 3 — head+tail. Always produces *something*, unless the parser
|
|
53
|
+
// itself crashes (in which case we pass-through).
|
|
54
|
+
const headTailResult = trySync(() => headTail(content, filePath));
|
|
55
|
+
if (hasSignals(headTailResult)) {
|
|
56
|
+
return { kind: "summary", summary: headTailResult, tier: "head-tail" };
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
kind: "pass-through",
|
|
60
|
+
reason: "all parsers returned empty or threw",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=summary-pipeline.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process regex-based structural summary parser.
|
|
3
|
+
*
|
|
4
|
+
* Fallback when the bundled ast-index binary is unavailable. Intentionally
|
|
5
|
+
* coarse: extracts imports, exports, and major top-level declarations per
|
|
6
|
+
* language using line-oriented regex. Never throws — worst case returns
|
|
7
|
+
* empty signals.
|
|
8
|
+
*
|
|
9
|
+
* Used by the hook summary pipeline (Phase 1 subtask 1.6).
|
|
10
|
+
*/
|
|
11
|
+
import type { HookSummary, SignalKind, SignalLine } from "./summary-types.js";
|
|
12
|
+
export type { HookSummary, SignalKind, SignalLine };
|
|
13
|
+
export declare function parseRegexSummary(content: string, filePath: string): HookSummary;
|
|
14
|
+
//# sourceMappingURL=summary-regex.d.ts.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process regex-based structural summary parser.
|
|
3
|
+
*
|
|
4
|
+
* Fallback when the bundled ast-index binary is unavailable. Intentionally
|
|
5
|
+
* coarse: extracts imports, exports, and major top-level declarations per
|
|
6
|
+
* language using line-oriented regex. Never throws — worst case returns
|
|
7
|
+
* empty signals.
|
|
8
|
+
*
|
|
9
|
+
* Used by the hook summary pipeline (Phase 1 subtask 1.6).
|
|
10
|
+
*/
|
|
11
|
+
const MAX_TEXT_LEN = 140;
|
|
12
|
+
const EXTENSIONS = {
|
|
13
|
+
ts: tsJsPattern(),
|
|
14
|
+
tsx: tsJsPattern(),
|
|
15
|
+
js: tsJsPattern(),
|
|
16
|
+
jsx: tsJsPattern(),
|
|
17
|
+
mjs: tsJsPattern(),
|
|
18
|
+
cjs: tsJsPattern(),
|
|
19
|
+
py: pythonPattern(),
|
|
20
|
+
go: goPattern(),
|
|
21
|
+
rs: rustPattern(),
|
|
22
|
+
};
|
|
23
|
+
function tsJsPattern() {
|
|
24
|
+
return {
|
|
25
|
+
// Covers ES modules and CommonJS: `import ...`, `const x = require(...)`
|
|
26
|
+
import: /^\s*(import\s|const\s+\w+\s*=\s*require\s*\()/,
|
|
27
|
+
// export keyword, module.exports, exports.foo =
|
|
28
|
+
export: /^\s*(export\s|module\.exports\s*=|exports\.\w+\s*=)/,
|
|
29
|
+
// top-level declarations not prefixed with export — function/class/interface/type/enum
|
|
30
|
+
declaration: /^\s*(async\s+)?(function|class|interface|type|enum)\s+\w+/,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function pythonPattern() {
|
|
34
|
+
return {
|
|
35
|
+
import: /^\s*(import\s|from\s+\S+\s+import\s)/,
|
|
36
|
+
declaration: /^\s*(async\s+)?(def\s+\w+|class\s+\w+)/,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function goPattern() {
|
|
40
|
+
return {
|
|
41
|
+
import: /^\s*import\s/,
|
|
42
|
+
declaration: /^\s*(func\s|type\s+\w+\s+(struct|interface|func))/,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function rustPattern() {
|
|
46
|
+
return {
|
|
47
|
+
import: /^\s*use\s/,
|
|
48
|
+
export: /^\s*pub\s+(fn|struct|trait|enum|type|mod|const|static)\s/,
|
|
49
|
+
declaration: /^\s*(async\s+)?(fn|struct|trait|enum|type|mod)\s+\w+/,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Derive the lower-case extension for a file path.
|
|
54
|
+
* Returns an empty string if the path has no dot.
|
|
55
|
+
*/
|
|
56
|
+
function extractExtension(filePath) {
|
|
57
|
+
const lastDot = filePath.lastIndexOf(".");
|
|
58
|
+
if (lastDot === -1 || lastDot === filePath.length - 1)
|
|
59
|
+
return "";
|
|
60
|
+
const ext = filePath.slice(lastDot + 1).toLowerCase();
|
|
61
|
+
// Guard against extensions containing path separators (e.g., "/foo.d/bar").
|
|
62
|
+
if (ext.includes("/") || ext.includes("\\"))
|
|
63
|
+
return "";
|
|
64
|
+
return ext;
|
|
65
|
+
}
|
|
66
|
+
function truncate(text) {
|
|
67
|
+
const trimmed = text.trim();
|
|
68
|
+
if (trimmed.length <= MAX_TEXT_LEN)
|
|
69
|
+
return trimmed;
|
|
70
|
+
return trimmed.slice(0, MAX_TEXT_LEN - 1) + "…";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Very rough token estimate mirroring the project-wide heuristic
|
|
74
|
+
* (see src/core/token-estimator.ts). Duplicated here to avoid a hard
|
|
75
|
+
* dependency in the hook hot-path.
|
|
76
|
+
*/
|
|
77
|
+
function estimateTokens(text) {
|
|
78
|
+
if (text.length === 0)
|
|
79
|
+
return 0;
|
|
80
|
+
const charEstimate = Math.ceil(text.length / 4);
|
|
81
|
+
const whitespaceRatio = (text.match(/\s/g)?.length ?? 0) / text.length;
|
|
82
|
+
const adjustment = 1 - whitespaceRatio * 0.3;
|
|
83
|
+
return Math.ceil(charEstimate * adjustment);
|
|
84
|
+
}
|
|
85
|
+
export function parseRegexSummary(content, filePath) {
|
|
86
|
+
const language = extractExtension(filePath);
|
|
87
|
+
const totalLines = content.split("\n").length;
|
|
88
|
+
const estimatedTokens = estimateTokens(content);
|
|
89
|
+
const patterns = EXTENSIONS[language];
|
|
90
|
+
if (!patterns) {
|
|
91
|
+
return {
|
|
92
|
+
signals: [],
|
|
93
|
+
totalLines,
|
|
94
|
+
estimatedTokens,
|
|
95
|
+
language,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const lines = content.split("\n");
|
|
99
|
+
const signals = [];
|
|
100
|
+
for (let i = 0; i < lines.length; i++) {
|
|
101
|
+
const line = lines[i];
|
|
102
|
+
if (!line || line.trim().length === 0)
|
|
103
|
+
continue;
|
|
104
|
+
// Classification order: import → export → declaration. First match wins.
|
|
105
|
+
let kind = null;
|
|
106
|
+
if (patterns.import?.test(line)) {
|
|
107
|
+
kind = "import";
|
|
108
|
+
}
|
|
109
|
+
else if (patterns.export?.test(line)) {
|
|
110
|
+
kind = "export";
|
|
111
|
+
}
|
|
112
|
+
else if (patterns.declaration?.test(line)) {
|
|
113
|
+
kind = "declaration";
|
|
114
|
+
}
|
|
115
|
+
if (kind !== null) {
|
|
116
|
+
signals.push({
|
|
117
|
+
line: i + 1,
|
|
118
|
+
kind,
|
|
119
|
+
text: truncate(line),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
signals,
|
|
125
|
+
totalLines,
|
|
126
|
+
estimatedTokens,
|
|
127
|
+
language,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=summary-regex.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the hook summary pipeline (Phase 1).
|
|
3
|
+
*
|
|
4
|
+
* Every parser in the fallback chain (ast-index subprocess, regex, head+tail)
|
|
5
|
+
* returns a HookSummary. The downstream formatter in handleHookRead renders
|
|
6
|
+
* this structure into the `permissionDecisionReason` body.
|
|
7
|
+
*/
|
|
8
|
+
export type SignalKind = "import" | "export" | "declaration" | "raw";
|
|
9
|
+
export interface SignalLine {
|
|
10
|
+
/** 1-based line number in the original source. */
|
|
11
|
+
line: number;
|
|
12
|
+
kind: SignalKind;
|
|
13
|
+
/** Trimmed source line, truncated to a parser-defined character cap. */
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
export interface HookSummary {
|
|
17
|
+
signals: SignalLine[];
|
|
18
|
+
totalLines: number;
|
|
19
|
+
estimatedTokens: number;
|
|
20
|
+
/** Lower-case extension without the dot; empty string if none. */
|
|
21
|
+
language: string;
|
|
22
|
+
/**
|
|
23
|
+
* Optional human-readable note explaining a non-standard summary
|
|
24
|
+
* (e.g. "parser unavailable — showing head+tail only"). Rendered by the
|
|
25
|
+
* formatter above the signals section.
|
|
26
|
+
*/
|
|
27
|
+
note?: string;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=summary-types.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the hook summary pipeline (Phase 1).
|
|
3
|
+
*
|
|
4
|
+
* Every parser in the fallback chain (ast-index subprocess, regex, head+tail)
|
|
5
|
+
* returns a HookSummary. The downstream formatter in handleHookRead renders
|
|
6
|
+
* this structure into the `permissionDecisionReason` body.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=summary-types.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import type { HookMode } from "./types.js";
|
|
2
3
|
export declare const CODE_EXTENSIONS: Set<string>;
|
|
3
4
|
export declare function getVersion(): string;
|
|
4
5
|
export declare function main(cliArgs?: string[]): Promise<void>;
|
|
5
6
|
export declare function startServer(cliArgs?: string[]): Promise<void>;
|
|
6
|
-
export
|
|
7
|
+
export interface HookReadAdaptiveOptions {
|
|
8
|
+
adaptiveThreshold?: boolean;
|
|
9
|
+
adaptiveBudgetTokens?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function handleHookRead(filePathArg?: string, mode?: HookMode, denyThreshold?: number, projectRoot?: string, adaptive?: HookReadAdaptiveOptions): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Pure implementation of the hook-read dispatch — returns the JSON payload
|
|
14
|
+
* to write to stdout, or null when we should pass-through (no output).
|
|
15
|
+
* Extracted for testability; the outer handleHookRead adds the process.exit
|
|
16
|
+
* wrapping.
|
|
17
|
+
*/
|
|
18
|
+
export declare function runHookReadDispatch(filePathArg: string | undefined, mode: HookMode, denyThresholdArg?: number, projectRootArg?: string, adaptive?: HookReadAdaptiveOptions): Promise<string | null>;
|
|
7
19
|
export declare function handleHookEdit(): void;
|
|
8
20
|
export declare function handleInstallHook(projectRoot: string): Promise<void>;
|
|
9
21
|
export declare function handleUninstallHook(projectRoot: string): Promise<void>;
|
|
@@ -11,8 +23,8 @@ export declare function handleInstallAstIndex(): Promise<void>;
|
|
|
11
23
|
export declare function handleDoctor(): Promise<void>;
|
|
12
24
|
export declare function handleInit(targetDir: string): Promise<void>;
|
|
13
25
|
export declare function checkNpmLatest(packageName: string): Promise<string | null>;
|
|
14
|
-
import type { TokenPilotConfig } from
|
|
15
|
-
import type { BinaryStatus } from
|
|
26
|
+
import type { TokenPilotConfig } from "./types.js";
|
|
27
|
+
import type { BinaryStatus } from "./ast-index/binary-manager.js";
|
|
16
28
|
export declare function checkAllUpdates(config: TokenPilotConfig, binaryStatus: BinaryStatus): Promise<void>;
|
|
17
29
|
export declare function printHelp(): void;
|
|
18
30
|
//# sourceMappingURL=index.d.ts.map
|