context-brake 1.0.0 → 1.1.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/README.md +31 -41
- package/dist/assets/runtime/antigravity-cli-hook.mjs +68 -23
- package/dist/assets/runtime/claude-code-hook.mjs +133 -35
- package/dist/assets/runtime/claude-code-mod.mjs +165 -59
- package/dist/assets/runtime/claude-code-statusline.mjs +41 -9
- package/dist/assets/runtime/codex-cli-hook.mjs +71 -23
- package/dist/assets/runtime/cursor-hook.mjs +71 -23
- package/dist/assets/runtime/github-copilot-cli-hook.mjs +71 -23
- package/dist/assets/runtime/omp-extension.js +70 -23
- package/dist/assets/runtime/omp-restart.js +41 -9
- package/dist/assets/runtime/opencode-plugin.js +70 -23
- package/dist/assets/runtime/pi-extension.js +73 -23
- package/dist/assets/runtime/pi-restart.js +41 -9
- package/dist/src/cli/assistant/assistant-context.js +7 -0
- package/dist/src/cli/assistant/assistant-questions.js +5 -4
- package/dist/src/cli/assistant/assistant-session.js +3 -1
- package/dist/src/cli/assistant/invocation-facts.d.ts +5 -0
- package/dist/src/cli/assistant/invocation-facts.js +6 -0
- package/dist/src/cli/assistant/questions-misc.d.ts +1 -0
- package/dist/src/cli/assistant/questions-misc.js +3 -2
- package/dist/src/cli/assistant/questions-skill.d.ts +21 -0
- package/dist/src/cli/assistant/questions-skill.js +45 -0
- package/dist/src/cli/assistant/questions-snapshot.d.ts +3 -1
- package/dist/src/cli/assistant/questions-snapshot.js +21 -26
- package/dist/src/cli/assistant/questions-trigger.d.ts +6 -0
- package/dist/src/cli/assistant/questions-trigger.js +56 -0
- package/dist/src/cli/assistant/skill-options.d.ts +20 -0
- package/dist/src/cli/assistant/skill-options.js +40 -0
- package/dist/src/cli/assistant/snapshot-specs.d.ts +7 -1
- package/dist/src/cli/assistant/snapshot-specs.js +17 -7
- package/dist/src/cli/assistant/summary-rows.d.ts +5 -0
- package/dist/src/cli/assistant/summary-rows.js +47 -0
- package/dist/src/cli/assistant/summary.d.ts +2 -1
- package/dist/src/cli/assistant/summary.js +21 -30
- package/dist/src/cli/assistant/types.d.ts +15 -0
- package/dist/src/cli/commands/doctor.js +2 -1
- package/dist/src/cli/commands/remove.js +1 -1
- package/dist/src/cli/init-arguments.js +5 -4
- package/dist/src/cli/init-flow.js +1 -1
- package/dist/src/cli/init-option-rules.d.ts +5 -0
- package/dist/src/cli/init-option-rules.js +16 -0
- package/dist/src/cli/output/card.d.ts +8 -0
- package/dist/src/cli/output/card.js +30 -0
- package/dist/src/cli/output/doctor-mode-text.js +8 -2
- package/dist/src/cli/output/install-rows.d.ts +6 -0
- package/dist/src/cli/output/install-rows.js +31 -0
- package/dist/src/cli/output/install-text.d.ts +4 -0
- package/dist/src/cli/output/install-text.js +26 -0
- package/dist/src/cli/output/style.d.ts +22 -0
- package/dist/src/cli/output/style.js +27 -0
- package/dist/src/cli/output/text.d.ts +1 -2
- package/dist/src/cli/output/text.js +1 -28
- package/dist/src/core/contracts/adapter.d.ts +2 -1
- package/dist/src/core/contracts/configuration.d.ts +2 -1
- package/dist/src/core/contracts/configuration.js +2 -10
- package/dist/src/core/contracts/context-reading.d.ts +16 -0
- package/dist/src/core/contracts/context-reading.js +20 -0
- package/dist/src/core/contracts/context-window-report.d.ts +8 -1
- package/dist/src/core/contracts/context-window-report.js +3 -1
- package/dist/src/core/contracts/diagnostics.d.ts +21 -0
- package/dist/src/core/contracts/diagnostics.js +2 -1
- package/dist/src/core/contracts/manifest.d.ts +16 -0
- package/dist/src/core/contracts/manifest.js +6 -0
- package/dist/src/core/contracts/runtime.d.ts +2 -0
- package/dist/src/core/contracts/skill-catalog.d.ts +11 -0
- package/dist/src/core/contracts/skill-catalog.js +1 -0
- package/dist/src/core/contracts/skill-invocation.d.ts +3 -0
- package/dist/src/core/contracts/skill-invocation.js +1 -0
- package/dist/src/core/contracts/skill-name.d.ts +5 -0
- package/dist/src/core/contracts/skill-name.js +31 -0
- package/dist/src/core/services/brake-engine.js +4 -3
- package/dist/src/core/services/doctor-report-extras.d.ts +2 -0
- package/dist/src/core/services/doctor-report-extras.js +22 -5
- package/dist/src/core/services/doctor-service.d.ts +2 -0
- package/dist/src/core/services/doctor-service.js +1 -1
- package/dist/src/core/services/failure-policy.js +1 -1
- package/dist/src/core/services/injection-policy.d.ts +1 -0
- package/dist/src/core/services/injection-policy.js +1 -1
- package/dist/src/core/services/installation-adapters.d.ts +2 -1
- package/dist/src/core/services/installation-adapters.js +3 -1
- package/dist/src/core/services/installation-builder.js +3 -2
- package/dist/src/core/services/installation-service.js +1 -1
- package/dist/src/core/services/manifest-change.d.ts +2 -1
- package/dist/src/core/services/manifest-change.js +2 -1
- package/dist/src/core/services/session-reset-handler.js +1 -1
- package/dist/src/core/services/skill-candidates.d.ts +16 -0
- package/dist/src/core/services/skill-candidates.js +14 -0
- package/dist/src/core/services/skill-invocation.d.ts +4 -0
- package/dist/src/core/services/skill-invocation.js +12 -0
- package/dist/src/core/services/skill-ranking.d.ts +7 -0
- package/dist/src/core/services/skill-ranking.js +25 -0
- package/dist/src/core/services/snapshot-merge.d.ts +1 -0
- package/dist/src/core/services/snapshot-merge.js +12 -5
- package/dist/src/core/services/trigger-text.d.ts +5 -0
- package/dist/src/core/services/trigger-text.js +4 -0
- package/dist/src/core/services/zone-guidance.d.ts +13 -2
- package/dist/src/core/services/zone-guidance.js +14 -8
- package/dist/src/infrastructure/harnesses/antigravity-cli/runtime.js +2 -1
- package/dist/src/infrastructure/harnesses/antigravity-cli/skill-folders.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/antigravity-cli/skill-folders.js +1 -0
- package/dist/src/infrastructure/harnesses/antigravity-cli/skill-invocation.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/antigravity-cli/skill-invocation.js +2 -0
- package/dist/src/infrastructure/harnesses/claude-code/adapter.js +6 -4
- package/dist/src/infrastructure/harnesses/claude-code/auto-restart-diagnostics.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/claude-code/auto-restart-diagnostics.js +2 -2
- package/dist/src/infrastructure/harnesses/claude-code/auto-restart-planner.d.ts +5 -1
- package/dist/src/infrastructure/harnesses/claude-code/auto-restart-planner.js +6 -6
- package/dist/src/infrastructure/harnesses/claude-code/capabilities.js +1 -1
- package/dist/src/infrastructure/harnesses/claude-code/context-readings-scan.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/claude-code/context-readings-scan.js +21 -0
- package/dist/src/infrastructure/harnesses/claude-code/context-source.d.ts +10 -0
- package/dist/src/infrastructure/harnesses/claude-code/context-source.js +16 -0
- package/dist/src/infrastructure/harnesses/claude-code/mod/context-reading.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/claude-code/mod/context-reading.js +67 -0
- package/dist/src/infrastructure/harnesses/claude-code/mod/host.d.ts +9 -0
- package/dist/src/infrastructure/harnesses/claude-code/mod/mod-info.d.ts +1 -1
- package/dist/src/infrastructure/harnesses/claude-code/mod/mod-info.js +1 -1
- package/dist/src/infrastructure/harnesses/claude-code/mod/mod-log.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/claude-code/mod/mod-log.js +3 -0
- package/dist/src/infrastructure/harnesses/claude-code/mod/register.js +6 -1
- package/dist/src/infrastructure/harnesses/claude-code/mod-reading.d.ts +6 -0
- package/dist/src/infrastructure/harnesses/claude-code/mod-reading.js +37 -0
- package/dist/src/infrastructure/harnesses/claude-code/planner.d.ts +2 -1
- package/dist/src/infrastructure/harnesses/claude-code/planner.js +4 -4
- package/dist/src/infrastructure/harnesses/claude-code/project-root.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/claude-code/project-root.js +5 -0
- package/dist/src/infrastructure/harnesses/claude-code/runtime.js +12 -7
- package/dist/src/infrastructure/harnesses/claude-code/skill-folders.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/claude-code/skill-folders.js +1 -0
- package/dist/src/infrastructure/harnesses/claude-code/skill-invocation.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/claude-code/skill-invocation.js +2 -0
- package/dist/src/infrastructure/harnesses/claude-code/statusline-context-window.js +15 -2
- package/dist/src/infrastructure/harnesses/claude-code/statusline-diagnostics.js +3 -3
- package/dist/src/infrastructure/harnesses/claude-code/statusline-migration.d.ts +3 -0
- package/dist/src/infrastructure/harnesses/claude-code/statusline-migration.js +23 -0
- package/dist/src/infrastructure/harnesses/claude-code/statusline-planner.d.ts +2 -1
- package/dist/src/infrastructure/harnesses/claude-code/statusline-planner.js +7 -4
- package/dist/src/infrastructure/harnesses/claude-code/user-settings.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/claude-code/user-settings.js +11 -0
- package/dist/src/infrastructure/harnesses/codex-cli/adapter.js +5 -4
- package/dist/src/infrastructure/harnesses/codex-cli/runtime.js +2 -1
- package/dist/src/infrastructure/harnesses/codex-cli/skill-folders.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/codex-cli/skill-folders.js +1 -0
- package/dist/src/infrastructure/harnesses/codex-cli/skill-invocation.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/codex-cli/skill-invocation.js +2 -0
- package/dist/src/infrastructure/harnesses/common/config-origin.d.ts +10 -0
- package/dist/src/infrastructure/harnesses/common/config-origin.js +52 -0
- package/dist/src/infrastructure/harnesses/cursor/adapter.js +5 -4
- package/dist/src/infrastructure/harnesses/cursor/runtime.js +2 -1
- package/dist/src/infrastructure/harnesses/cursor/skill-folders.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/cursor/skill-folders.js +1 -0
- package/dist/src/infrastructure/harnesses/cursor/skill-invocation.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/cursor/skill-invocation.js +2 -0
- package/dist/src/infrastructure/harnesses/github-copilot-cli/runtime.js +2 -1
- package/dist/src/infrastructure/harnesses/github-copilot-cli/skill-folders.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/github-copilot-cli/skill-folders.js +1 -0
- package/dist/src/infrastructure/harnesses/github-copilot-cli/skill-invocation.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/github-copilot-cli/skill-invocation.js +2 -0
- package/dist/src/infrastructure/harnesses/oh-my-pi/runtime.js +2 -1
- package/dist/src/infrastructure/harnesses/oh-my-pi/skill-folders.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/oh-my-pi/skill-folders.js +1 -0
- package/dist/src/infrastructure/harnesses/oh-my-pi/skill-invocation.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/oh-my-pi/skill-invocation.js +2 -0
- package/dist/src/infrastructure/harnesses/opencode/runtime.js +2 -1
- package/dist/src/infrastructure/harnesses/opencode/skill-folders.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/opencode/skill-folders.js +1 -0
- package/dist/src/infrastructure/harnesses/opencode/skill-invocation.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/opencode/skill-invocation.js +2 -0
- package/dist/src/infrastructure/harnesses/pi/runtime.js +2 -1
- package/dist/src/infrastructure/harnesses/pi/skill-folders.d.ts +1 -0
- package/dist/src/infrastructure/harnesses/pi/skill-folders.js +1 -0
- package/dist/src/infrastructure/harnesses/pi/skill-invocation.d.ts +2 -0
- package/dist/src/infrastructure/harnesses/pi/skill-invocation.js +2 -0
- package/dist/src/infrastructure/harnesses/skill-folders.d.ts +3 -0
- package/dist/src/infrastructure/harnesses/skill-folders.js +21 -0
- package/dist/src/infrastructure/harnesses/skill-invocations.d.ts +4 -0
- package/dist/src/infrastructure/harnesses/skill-invocations.js +21 -0
- package/dist/src/infrastructure/runtime/runtime-paths.js +2 -1
- package/dist/src/infrastructure/skills/frontmatter.d.ts +5 -0
- package/dist/src/infrastructure/skills/frontmatter.js +25 -0
- package/dist/src/infrastructure/skills/skill-catalog-reader.d.ts +4 -0
- package/dist/src/infrastructure/skills/skill-catalog-reader.js +78 -0
- package/dist/src/infrastructure/skills/skill-catalog.d.ts +2 -0
- package/dist/src/infrastructure/skills/skill-catalog.js +5 -0
- package/dist/src/infrastructure/storage/change-applier.d.ts +1 -0
- package/dist/src/infrastructure/storage/change-applier.js +2 -1
- package/dist/src/infrastructure/storage/json-document-editor.js +2 -1
- package/dist/src/infrastructure/storage/json-node-removal.d.ts +2 -0
- package/dist/src/infrastructure/storage/json-node-removal.js +70 -0
- package/dist/src/infrastructure/storage/json-span-utils.d.ts +1 -1
- package/dist/src/infrastructure/storage/json-span-utils.js +1 -48
- package/docs/telemetry-block.md +3 -1
- package/package.json +1 -1
- package/schemas/context-brake.config.schema.json +9 -4
- package/schemas/doctor-report.schema.json +51 -0
|
@@ -440,8 +440,23 @@ export declare const doctorReportSchema: z.ZodObject<{
|
|
|
440
440
|
YELLOW: "YELLOW";
|
|
441
441
|
RED: "RED";
|
|
442
442
|
}>;
|
|
443
|
+
triggerPercentage: z.ZodOptional<z.ZodNumber>;
|
|
443
444
|
command: z.ZodNullable<z.ZodString>;
|
|
444
445
|
resumeCommand: z.ZodNullable<z.ZodString>;
|
|
446
|
+
invocations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
447
|
+
harness: z.ZodEnum<{
|
|
448
|
+
cursor: "cursor";
|
|
449
|
+
"claude-code": "claude-code";
|
|
450
|
+
"codex-cli": "codex-cli";
|
|
451
|
+
"github-copilot-cli": "github-copilot-cli";
|
|
452
|
+
opencode: "opencode";
|
|
453
|
+
pi: "pi";
|
|
454
|
+
"oh-my-pi": "oh-my-pi";
|
|
455
|
+
"antigravity-cli": "antigravity-cli";
|
|
456
|
+
}>;
|
|
457
|
+
command: z.ZodString;
|
|
458
|
+
resumeCommand: z.ZodNullable<z.ZodString>;
|
|
459
|
+
}, z.core.$strict>>>;
|
|
445
460
|
}, z.core.$strict>>;
|
|
446
461
|
contextWindow: z.ZodOptional<z.ZodObject<{
|
|
447
462
|
bridge: z.ZodEnum<{
|
|
@@ -449,8 +464,14 @@ export declare const doctorReportSchema: z.ZodObject<{
|
|
|
449
464
|
installed: "installed";
|
|
450
465
|
absent: "absent";
|
|
451
466
|
}>;
|
|
467
|
+
mod: z.ZodEnum<{
|
|
468
|
+
installed: "installed";
|
|
469
|
+
absent: "absent";
|
|
470
|
+
reporting: "reporting";
|
|
471
|
+
}>;
|
|
452
472
|
source: z.ZodEnum<{
|
|
453
473
|
contextWindowCeiling: "contextWindowCeiling";
|
|
474
|
+
mod: "mod";
|
|
454
475
|
statusline: "statusline";
|
|
455
476
|
}>;
|
|
456
477
|
lastWindowTokens: z.ZodNullable<z.ZodNumber>;
|
|
@@ -20,7 +20,8 @@ const harnessPlan = z.object({ harness: z.enum(HARNESS_IDS), outcome: z.enum(['p
|
|
|
20
20
|
const outcome = z.object({ path: z.string(), status: z.enum(['planned', 'applied', 'unchanged', 'skipped', 'failed']), detail: z.string().nullable() }).strict();
|
|
21
21
|
const plan = z.object({ schemaVersion: z.literal(1), projectRoot: z.string(), changes: z.array(fileChange), conflicts: z.array(conflict), harnesses: z.array(harnessPlan), requiresConfirmation: z.boolean() }).strict();
|
|
22
22
|
export const installReportSchema = z.object({ schemaVersion: z.literal(1), command: z.enum(['init', 'remove']), mode: z.enum(['dry_run', 'applied']), status: z.enum(['success', 'warnings', 'errors']), exitCode: z.union([z.literal(0), z.literal(1), z.literal(2)]), detections: z.array(detection), plan, outcomes: z.array(outcome), findings: z.array(finding) }).strict();
|
|
23
|
-
const
|
|
23
|
+
const invocationReport = z.object({ harness: z.enum(HARNESS_IDS), command: z.string(), resumeCommand: z.string().nullable() }).strict();
|
|
24
|
+
const snapshotReport = z.object({ triggerZone: z.enum(SNAPSHOT_TRIGGER_ZONES), triggerPercentage: z.optional(z.number().int().min(1).max(100)), command: z.string().nullable(), resumeCommand: z.string().nullable(), invocations: z.optional(z.array(invocationReport)) }).strict();
|
|
24
25
|
const sessionUsage = z.object({ percentage: z.number().int().nonnegative(), usedTokens: z.number().int().nonnegative(), windowTokens: z.number().int().positive(), zone: z.enum(ZONES), source: z.enum(USAGE_SOURCES), at: z.string() }).strict();
|
|
25
26
|
const activeSession = z.object({ harness: z.enum(HARNESS_IDS), sessionId: z.string().nullable(), lastActivityAt: z.string(), usage: sessionUsage.nullable() }).strict();
|
|
26
27
|
export const doctorReportSchema = z.object({ schemaVersion: z.literal(1), command: z.literal('doctor'), status: z.enum(['healthy', 'warnings', 'errors']), exitCode: z.union([z.literal(0), z.literal(1), z.literal(2)]), detections: z.array(detection), integrations: z.array(integration), findings: z.array(finding), snapshot: snapshotReport.optional(), contextWindow: contextWindowReportSchema.optional(), activeSessions: z.array(activeSession).optional(), debugMode: z.optional(z.literal(true)) }).strict();
|
|
@@ -13,11 +13,17 @@ export type ManagedEntry = {
|
|
|
13
13
|
path: string;
|
|
14
14
|
identity: string;
|
|
15
15
|
};
|
|
16
|
+
export type ConfigOrigin = {
|
|
17
|
+
path: string;
|
|
18
|
+
created: boolean;
|
|
19
|
+
addedKeys: readonly string[];
|
|
20
|
+
};
|
|
16
21
|
export type InstallationManifest = {
|
|
17
22
|
schemaVersion: 1;
|
|
18
23
|
packageVersion: string;
|
|
19
24
|
assets: readonly ManagedAsset[];
|
|
20
25
|
entries: readonly ManagedEntry[];
|
|
26
|
+
configOrigins?: readonly ConfigOrigin[] | undefined;
|
|
21
27
|
};
|
|
22
28
|
export interface ManifestStore {
|
|
23
29
|
load(): Promise<InstallationManifest | null>;
|
|
@@ -44,6 +50,11 @@ export declare const managedEntrySchema: z.ZodObject<{
|
|
|
44
50
|
path: z.ZodString;
|
|
45
51
|
identity: z.ZodString;
|
|
46
52
|
}, z.core.$strict>;
|
|
53
|
+
export declare const configOriginSchema: z.ZodObject<{
|
|
54
|
+
path: z.ZodString;
|
|
55
|
+
created: z.ZodBoolean;
|
|
56
|
+
addedKeys: z.ZodArray<z.ZodString>;
|
|
57
|
+
}, z.core.$strict>;
|
|
47
58
|
export declare const installationManifestSchema: z.ZodObject<{
|
|
48
59
|
schemaVersion: z.ZodLiteral<1>;
|
|
49
60
|
packageVersion: z.ZodString;
|
|
@@ -66,4 +77,9 @@ export declare const installationManifestSchema: z.ZodObject<{
|
|
|
66
77
|
path: z.ZodString;
|
|
67
78
|
identity: z.ZodString;
|
|
68
79
|
}, z.core.$strict>>;
|
|
80
|
+
configOrigins: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
81
|
+
path: z.ZodString;
|
|
82
|
+
created: z.ZodBoolean;
|
|
83
|
+
addedKeys: z.ZodArray<z.ZodString>;
|
|
84
|
+
}, z.core.$strict>>>;
|
|
69
85
|
}, z.core.$strict>;
|
|
@@ -12,9 +12,15 @@ export const managedEntrySchema = z.object({
|
|
|
12
12
|
path: z.string(),
|
|
13
13
|
identity: z.string().min(1),
|
|
14
14
|
}).strict();
|
|
15
|
+
export const configOriginSchema = z.object({
|
|
16
|
+
path: z.string(),
|
|
17
|
+
created: z.boolean(),
|
|
18
|
+
addedKeys: z.array(z.string().min(1)),
|
|
19
|
+
}).strict();
|
|
15
20
|
export const installationManifestSchema = z.object({
|
|
16
21
|
schemaVersion: z.literal(MANIFEST_SCHEMA_VERSION),
|
|
17
22
|
packageVersion: z.string().min(1),
|
|
18
23
|
assets: z.array(managedAssetSchema),
|
|
19
24
|
entries: z.array(managedEntrySchema),
|
|
25
|
+
configOrigins: z.array(configOriginSchema).optional(),
|
|
20
26
|
}).strict();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CapabilityDefinition, HarnessId } from './harness.js';
|
|
2
|
+
import type { SkillInvocationFormat } from './skill-invocation.js';
|
|
2
3
|
export type SessionKey = {
|
|
3
4
|
readonly harness: HarnessId;
|
|
4
5
|
readonly sessionId: string;
|
|
@@ -47,4 +48,5 @@ export type RuntimeDescriptor = {
|
|
|
47
48
|
readonly capabilities: readonly CapabilityDefinition[];
|
|
48
49
|
readonly estimation: EstimationConstants;
|
|
49
50
|
readonly newSessionCommand: string | null;
|
|
51
|
+
readonly skillInvocation: SkillInvocationFormat;
|
|
50
52
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { HarnessId } from './harness.js';
|
|
2
|
+
export type CatalogSkill = {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly description: string;
|
|
5
|
+
readonly folder: string;
|
|
6
|
+
};
|
|
7
|
+
export type SkillCatalog = {
|
|
8
|
+
readonly skills: readonly CatalogSkill[];
|
|
9
|
+
readonly warnings: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
export type SkillCatalogReader = (harness: HarnessId, projectRoot: string) => Promise<SkillCatalog>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { z } from 'zod/mini';
|
|
2
|
+
export declare const MAX_SKILL_NAME_LENGTH = 64;
|
|
3
|
+
export declare function isSkillName(value: string): boolean;
|
|
4
|
+
export declare function skillNameProblem(value: string): string | null;
|
|
5
|
+
export declare const skillNameSchema: z.ZodMiniString<string>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from 'zod/mini';
|
|
2
|
+
export const MAX_SKILL_NAME_LENGTH = 64;
|
|
3
|
+
const NAME_SEGMENT = '[a-z0-9]+(?:-[a-z0-9]+)*';
|
|
4
|
+
const NAMESPACE_SEPARATOR = ':';
|
|
5
|
+
const INVOCATION_CHARACTERS = ['/', '$', '@'];
|
|
6
|
+
const MAX_QUALIFIED_LENGTH = MAX_SKILL_NAME_LENGTH * 2 + NAMESPACE_SEPARATOR.length;
|
|
7
|
+
function segmentFits(end) {
|
|
8
|
+
return `(?=[^${NAMESPACE_SEPARATOR}]{1,${MAX_SKILL_NAME_LENGTH}}${end})`;
|
|
9
|
+
}
|
|
10
|
+
const PREFIX = `(?:${segmentFits(NAMESPACE_SEPARATOR)}${NAME_SEGMENT}${NAMESPACE_SEPARATOR})?`;
|
|
11
|
+
const SKILL_NAME_PATTERN = new RegExp(`^${PREFIX}${segmentFits('$')}${NAME_SEGMENT}$`);
|
|
12
|
+
const SHAPE_RULE = `lowercase letters, digits, and single hyphens, up to ${MAX_SKILL_NAME_LENGTH} characters, optionally prefixed by a plugin name and a colon`;
|
|
13
|
+
export function isSkillName(value) {
|
|
14
|
+
return SKILL_NAME_PATTERN.test(value);
|
|
15
|
+
}
|
|
16
|
+
export function skillNameProblem(value) {
|
|
17
|
+
if (value === '')
|
|
18
|
+
return 'must not be empty';
|
|
19
|
+
if (isSkillName(value))
|
|
20
|
+
return null;
|
|
21
|
+
const bare = INVOCATION_CHARACTERS.includes(value.charAt(0)) ? value.slice(1) : null;
|
|
22
|
+
if (bare !== null && isSkillName(bare))
|
|
23
|
+
return `"${value}" is not a skill name; use the bare name ${bare}`;
|
|
24
|
+
return `"${value}" is not a skill name; use ${SHAPE_RULE}`;
|
|
25
|
+
}
|
|
26
|
+
export const skillNameSchema = z.string().check((ctx) => {
|
|
27
|
+
const message = skillNameProblem(ctx.value);
|
|
28
|
+
if (message !== null)
|
|
29
|
+
ctx.issues.push({ code: 'custom', input: ctx.value, message });
|
|
30
|
+
});
|
|
31
|
+
z.globalRegistry.add(skillNameSchema, { pattern: SKILL_NAME_PATTERN.source, maxLength: MAX_QUALIFIED_LENGTH });
|
|
@@ -7,7 +7,7 @@ import { nextTurn, summarizeLedger } from './session-counters.js';
|
|
|
7
7
|
import { readZone } from './session-zone.js';
|
|
8
8
|
import { renderTelemetryBlock } from './telemetry-block.js';
|
|
9
9
|
import { redStartTurn } from './zone-classifier.js';
|
|
10
|
-
import { zoneAction } from './zone-guidance.js';
|
|
10
|
+
import { isResetDue, zoneAction } from './zone-guidance.js';
|
|
11
11
|
import { handleSessionReset } from './session-reset-handler.js';
|
|
12
12
|
const NEUTRAL = { kind: 'neutral' };
|
|
13
13
|
export function createBrakeEngine(options) { return { handle: (event, input) => handleEvent(options, event, input ?? {}) }; }
|
|
@@ -36,9 +36,10 @@ async function handlePreInvocation(options, event, input) {
|
|
|
36
36
|
return telemetryDecision(options, summary.turns, readZone(options, { summary, turns: summary.turns, observedCharacters: 0, measured: input.measured }));
|
|
37
37
|
}
|
|
38
38
|
async function telemetryDecision(options, turn, view) {
|
|
39
|
-
|
|
39
|
+
const resetDue = isResetDue({ zone: view.zone, usagePercentage: view.percentage }, options.config.snapshot);
|
|
40
|
+
if (!decideInjection({ telemetry: options.config.telemetry, zone: view.zone, usagePercentage: view.percentage, debug: isDebugModeInEffect(options.config), resetDue }))
|
|
40
41
|
return NEUTRAL;
|
|
41
|
-
const action = zoneAction(view.zone, options.config.snapshot, restartMode(options.config));
|
|
42
|
+
const action = zoneAction(view.zone, options.config.snapshot, { mode: restartMode(options.config), invoke: options.descriptor.skillInvocation, usagePercentage: view.percentage });
|
|
42
43
|
return { kind: 'context', block: renderTelemetryBlock({ turn, turnCeiling: redStartTurn(options.config.telemetry.zones), usagePercentage: view.percentage, usage: view.reading, zone: view.zone, action, debug: isDebugModeInEffect(options.config) }) };
|
|
43
44
|
}
|
|
44
45
|
async function handleResponseEnd(options, event) {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { ContextBrakeConfig } from '../contracts/configuration.js';
|
|
2
2
|
import type { ContextWindowReport } from '../contracts/context-window-report.js';
|
|
3
3
|
import type { DiagnosticFinding, DoctorReport } from '../contracts/diagnostics.js';
|
|
4
|
+
import type { SkillInvocationResolver } from '../contracts/skill-invocation.js';
|
|
4
5
|
import type { BuildDoctorReportInput } from './report-service.js';
|
|
5
6
|
export type DoctorExtrasInput = {
|
|
6
7
|
readonly config: ContextBrakeConfig | null;
|
|
7
8
|
readonly planPresent: boolean;
|
|
8
9
|
readonly contextWindow: ContextWindowReport | undefined;
|
|
9
10
|
readonly sessions: DoctorReport['activeSessions'];
|
|
11
|
+
readonly invocations?: SkillInvocationResolver | undefined;
|
|
10
12
|
};
|
|
11
13
|
export declare function doctorReportExtras(input: DoctorExtrasInput, findings: readonly DiagnosticFinding[]): Omit<BuildDoctorReportInput, 'detections' | 'integrations'>;
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { isDebugModeInEffect } from './debug-mode-merge.js';
|
|
2
2
|
export function doctorReportExtras(input, findings) {
|
|
3
3
|
return {
|
|
4
|
-
findings: [...findings, ...bridgeAbsentFindings(input.contextWindow)],
|
|
5
|
-
snapshot: snapshotReport(input.config), contextWindow: input.contextWindow, activeSessions: input.sessions,
|
|
4
|
+
findings: [...findings, ...modFindings(input.contextWindow), ...bridgeAbsentFindings(input.contextWindow)],
|
|
5
|
+
snapshot: snapshotReport(input.config, input.invocations), contextWindow: input.contextWindow, activeSessions: input.sessions,
|
|
6
6
|
debugMode: isDebugModeInEffect(input.config),
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
|
-
function snapshotReport(config) {
|
|
9
|
+
function snapshotReport(config, resolve) {
|
|
10
10
|
if (config === null)
|
|
11
11
|
return undefined;
|
|
12
|
-
|
|
12
|
+
const { triggerZone, triggerPercentage, command, resumeCommand } = config.snapshot;
|
|
13
|
+
const trigger = triggerPercentage === undefined ? {} : { triggerPercentage };
|
|
14
|
+
const report = { triggerZone, ...trigger, command: command ?? null, resumeCommand: resumeCommand ?? null };
|
|
15
|
+
if (command === undefined || resolve === undefined)
|
|
16
|
+
return report;
|
|
17
|
+
return { ...report, invocations: config.activeHarnesses.map((harness) => ({ harness, command: resolve(harness)(command), resumeCommand: resumeCommand === undefined ? null : resolve(harness)(resumeCommand) })) };
|
|
13
18
|
}
|
|
14
19
|
function bridgeAbsentFindings(contextWindow) {
|
|
15
|
-
if (contextWindow?.bridge !== 'absent')
|
|
20
|
+
if (contextWindow?.bridge !== 'absent' || contextWindow.mod !== 'absent')
|
|
16
21
|
return [];
|
|
17
22
|
return [{
|
|
18
23
|
code: 'STATUSLINE_BRIDGE_ABSENT', severity: 'warning', scope: 'harness', harness: 'claude-code', path: null,
|
|
@@ -21,3 +26,15 @@ function bridgeAbsentFindings(contextWindow) {
|
|
|
21
26
|
remediation: 'Run context-brake init. After --no-statusline-bridge, run context-brake init --statusline-bridge.',
|
|
22
27
|
}];
|
|
23
28
|
}
|
|
29
|
+
function modFindings(contextWindow) {
|
|
30
|
+
if (contextWindow?.mod === 'reporting')
|
|
31
|
+
return [{ code: 'CONTEXT_MOD_READY', severity: 'ok', scope: 'harness', harness: 'claude-code', path: null, message: 'The Claude Code mod reports the context window.', impact: null, remediation: null }];
|
|
32
|
+
if (contextWindow?.mod !== 'installed' || contextWindow.bridge === 'installed')
|
|
33
|
+
return [];
|
|
34
|
+
return [{
|
|
35
|
+
code: 'CONTEXT_MOD_SILENT', severity: 'warning', scope: 'harness', harness: 'claude-code', path: null,
|
|
36
|
+
message: 'The ContextBrake mod is installed, but no Claude Code session has reported the context yet.',
|
|
37
|
+
impact: 'The context window falls back to contextWindowCeiling and usage to the transcript or an estimate.',
|
|
38
|
+
remediation: 'Start Claude Code in this project and trust the folder. Mods stay off under disableAllHooks, --safe-mode, --bare, an organization managed policy, and in Desktop WSL sessions. Claude Code also keeps one marketplace named context-brake-local per user, so the mod of another repository can be the one that loads; run context-brake init in that repository too, or use the status line bridge with context-brake init --statusline-bridge.',
|
|
39
|
+
}];
|
|
40
|
+
}
|
|
@@ -5,6 +5,7 @@ import type { FileSnapshot } from '../contracts/changes.js';
|
|
|
5
5
|
import { type DetectionSources, type HarnessId } from '../contracts/harness.js';
|
|
6
6
|
import type { InstallationManifest } from '../contracts/manifest.js';
|
|
7
7
|
import { type RuntimeStateReading } from './runtime-error-checks.js';
|
|
8
|
+
import type { SkillInvocationResolver } from '../contracts/skill-invocation.js';
|
|
8
9
|
export type DoctorInput = {
|
|
9
10
|
projectRoot: string;
|
|
10
11
|
config: ContextBrakeConfig | null;
|
|
@@ -20,5 +21,6 @@ export type DoctorInput = {
|
|
|
20
21
|
runtimeState?: RuntimeStateReading | null;
|
|
21
22
|
contextWindow?: DoctorReport['contextWindow'];
|
|
22
23
|
now?: Date | undefined;
|
|
24
|
+
invocations?: SkillInvocationResolver;
|
|
23
25
|
};
|
|
24
26
|
export declare function diagnoseProject(input: DoctorInput): Promise<DoctorReport>;
|
|
@@ -78,5 +78,5 @@ export async function diagnoseProject(input) {
|
|
|
78
78
|
if (input.runtimeState)
|
|
79
79
|
allFindings.push(...runtimeErrorFindings(input.runtimeState));
|
|
80
80
|
const sessions = input.now === undefined ? [] : activeSessions(input.runtimeState?.ledgers ?? [], { now: input.now, zones: effective.telemetry.zones });
|
|
81
|
-
return buildDoctorReport({ detections, integrations, ...doctorReportExtras({ config: input.config, planPresent: false, contextWindow: targetIds.has('claude-code') ? input.contextWindow : undefined, sessions }, allFindings) });
|
|
81
|
+
return buildDoctorReport({ detections, integrations, ...doctorReportExtras({ config: input.config, planPresent: false, contextWindow: targetIds.has('claude-code') ? input.contextWindow : undefined, sessions, invocations: input.invocations }, allFindings) });
|
|
82
82
|
}
|
|
@@ -47,7 +47,7 @@ export async function resolveFailure(input) {
|
|
|
47
47
|
return { kind: 'neutral' };
|
|
48
48
|
}
|
|
49
49
|
function deadlineBootDecision(input) {
|
|
50
|
-
const text = resumeText((input.config ?? DEFAULT_CONFIG).snapshot);
|
|
50
|
+
const text = resumeText((input.config ?? DEFAULT_CONFIG).snapshot, input.descriptor?.skillInvocation);
|
|
51
51
|
return text === null ? { kind: 'neutral' } : { kind: 'context', block: text };
|
|
52
52
|
}
|
|
53
53
|
export async function recordRuntimeFailure(errors, record) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function decideInjection(input) {
|
|
2
|
-
if (input.debug || input.telemetry.injectionMode === 'always')
|
|
2
|
+
if (input.debug || input.resetDue === true || input.telemetry.injectionMode === 'always')
|
|
3
3
|
return true;
|
|
4
4
|
return input.zone !== 'GREEN' || input.usagePercentage >= input.telemetry.activationThresholdPercentage;
|
|
5
5
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type { HarnessAdapter, HarnessContext } from '../contracts/adapter.js';
|
|
2
2
|
import type { HarnessInstallPlan, PlannedChange, PlanConflict } from '../contracts/changes.js';
|
|
3
3
|
import type { HarnessDetection } from '../contracts/harness.js';
|
|
4
|
-
import type { ManagedAsset, ManagedEntry } from '../contracts/manifest.js';
|
|
4
|
+
import type { ConfigOrigin, ManagedAsset, ManagedEntry } from '../contracts/manifest.js';
|
|
5
5
|
export declare function planAdapters(adapters: readonly HarnessAdapter[], active: readonly HarnessDetection[], ctx: HarnessContext): Promise<{
|
|
6
6
|
changes: PlannedChange[];
|
|
7
7
|
conflicts: PlanConflict[];
|
|
8
8
|
entries: ManagedEntry[];
|
|
9
9
|
assets: ManagedAsset[];
|
|
10
|
+
configOrigins: ConfigOrigin[];
|
|
10
11
|
harnesses: HarnessInstallPlan[];
|
|
11
12
|
findings: {
|
|
12
13
|
code: string;
|
|
@@ -4,6 +4,7 @@ export async function planAdapters(adapters, active, ctx) {
|
|
|
4
4
|
const conflicts = [];
|
|
5
5
|
const entries = [];
|
|
6
6
|
const assets = [];
|
|
7
|
+
const configOrigins = [];
|
|
7
8
|
const harnesses = [];
|
|
8
9
|
const findings = [];
|
|
9
10
|
for (const d of active) {
|
|
@@ -15,6 +16,7 @@ export async function planAdapters(adapters, active, ctx) {
|
|
|
15
16
|
conflicts.push(...aPlan.conflicts);
|
|
16
17
|
findings.push(...(aPlan.findings ?? []));
|
|
17
18
|
entries.push(...aPlan.entries);
|
|
19
|
+
configOrigins.push(...(aPlan.configOrigins ?? []));
|
|
18
20
|
for (const c of aPlan.changes)
|
|
19
21
|
if (c.owner === 'runtime_asset' && c.content)
|
|
20
22
|
assets.push({ path: c.path, kind: 'runtime_asset', sha256: hashString(c.content) });
|
|
@@ -24,5 +26,5 @@ export async function planAdapters(adapters, active, ctx) {
|
|
|
24
26
|
const profile = adapter.capabilityProfile();
|
|
25
27
|
harnesses.push({ harness: d.harness, outcome, supportLevel: profile.supportLevel, limitations: [...profile.limitations] });
|
|
26
28
|
}
|
|
27
|
-
return { changes, conflicts, entries, assets, harnesses, findings };
|
|
29
|
+
return { changes, conflicts, entries, assets, configOrigins, harnesses, findings };
|
|
28
30
|
}
|
|
@@ -6,6 +6,7 @@ import { applyDebugMode } from './debug-mode-merge.js';
|
|
|
6
6
|
import { applyGitIgnore } from './gitignore-merge.js';
|
|
7
7
|
import { normalizeTurnLimits } from './config-legacy-checks.js';
|
|
8
8
|
import { applyExclusion } from './harness-exclusion.js';
|
|
9
|
+
import { describeTrigger } from './trigger-text.js';
|
|
9
10
|
const KEEP = { kind: 'keep' };
|
|
10
11
|
const CONFIG_KEY_ORDER = Object.keys(configurationSchema.shape);
|
|
11
12
|
const DEBUG_SUMMARY = { set: 'set the debug mode (agent prints context usage)', remove: 'remove the debug mode' };
|
|
@@ -60,7 +61,7 @@ function configSummary(updates) {
|
|
|
60
61
|
}
|
|
61
62
|
function snapshotSummary(section) {
|
|
62
63
|
if (section.command === undefined)
|
|
63
|
-
return `no snapshot command, so only zone headers will be injected (trigger: ${section
|
|
64
|
+
return `no snapshot command, so only zone headers will be injected (trigger: ${describeTrigger(section)})`;
|
|
64
65
|
const resume = section.resumeCommand === undefined ? '' : `, resume command ${section.resumeCommand}`;
|
|
65
|
-
return `snapshot command ${section.command} at ${section
|
|
66
|
+
return `snapshot command ${section.command} at ${describeTrigger(section)}${resume}`;
|
|
66
67
|
}
|
|
@@ -39,7 +39,7 @@ export async function planInstallation(input) {
|
|
|
39
39
|
const preservedAssets = (input.previousManifest?.assets ?? []).filter((a) => modifiedPaths.has(a.path));
|
|
40
40
|
const adapterAssets = [...ap.assets.filter((a) => !modifiedPaths.has(a.path)), ...preservedAssets, ...extras.assets];
|
|
41
41
|
const allAssets = buildManagedAssets(cfg.change.content ?? '', adapterAssets);
|
|
42
|
-
const manifestChanges = active.length === 0 && !input.previousManifest ? [] : [planManifestChange({ root: input.projectRoot, assets: allAssets, entries: ap.entries, prev: input.previousManifest ?? null, pkgVer: input.packageVersion, snapshot: input.allSnapshots.find((s) => s.path === MANIFEST_RELATIVE_PATH) })];
|
|
42
|
+
const manifestChanges = active.length === 0 && !input.previousManifest ? [] : [planManifestChange({ root: input.projectRoot, assets: allAssets, entries: ap.entries, configOrigins: ap.configOrigins, prev: input.previousManifest ?? null, pkgVer: input.packageVersion, snapshot: input.allSnapshots.find((s) => s.path === MANIFEST_RELATIVE_PATH) })];
|
|
43
43
|
const plannedChanges = [cfg.change, ...manifestChanges, ...protection.changes, ...extras.changes, ...removals.changes];
|
|
44
44
|
const ignore = planGitIgnoreForInstall({ root: input.projectRoot, enabled: cfg.config.gitIgnore !== false, insideGit: input.insideGit ?? false, hasInstall: manifestChanges.length > 0, assetPaths: allAssets.map((asset) => asset.path), changes: plannedChanges, snapshots: input.allSnapshots });
|
|
45
45
|
const conflicts = [...ap.conflicts, ...protection.conflicts, ...ignore.conflicts];
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { FileSnapshot, PlannedChange } from '../contracts/changes.js';
|
|
2
|
-
import { type InstallationManifest, type ManagedAsset, type ManagedEntry } from '../contracts/manifest.js';
|
|
2
|
+
import { type ConfigOrigin, type InstallationManifest, type ManagedAsset, type ManagedEntry } from '../contracts/manifest.js';
|
|
3
3
|
export type ManifestChangeInput = {
|
|
4
4
|
root: string;
|
|
5
5
|
assets: readonly ManagedAsset[];
|
|
6
6
|
entries: readonly ManagedEntry[];
|
|
7
|
+
configOrigins?: readonly ConfigOrigin[] | undefined;
|
|
7
8
|
prev: InstallationManifest | null;
|
|
8
9
|
pkgVer: string;
|
|
9
10
|
snapshot?: FileSnapshot | null | undefined;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { MANIFEST_RELATIVE_PATH } from '../contracts/manifest.js';
|
|
3
3
|
export function planManifestChange(input) {
|
|
4
|
-
const
|
|
4
|
+
const origins = input.configOrigins ?? [];
|
|
5
|
+
const manifest = { schemaVersion: 1, packageVersion: input.pkgVer, assets: input.assets, entries: input.entries, ...(origins.length > 0 ? { configOrigins: origins } : {}) };
|
|
5
6
|
const content = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
6
7
|
const defaultPath = resolve(input.root, MANIFEST_RELATIVE_PATH).replace(/\\/g, '/');
|
|
7
8
|
const realPath = (input.snapshot?.realPath ?? defaultPath).replace(/\\/g, '/');
|
|
@@ -13,7 +13,7 @@ export async function handleSessionReset(options, event, hooks = {}) {
|
|
|
13
13
|
if (event.reason === 'compact' && !COMPACTION_BOOT_HARNESSES.includes(options.descriptor.harness))
|
|
14
14
|
return NEUTRAL;
|
|
15
15
|
hooks.onPhase?.('guidance');
|
|
16
|
-
const text = resumeText(options.config.snapshot) ?? await claimHandoff(options, event.reason, hooks.deadline);
|
|
16
|
+
const text = resumeText(options.config.snapshot, options.descriptor.skillInvocation) ?? await claimHandoff(options, event.reason, hooks.deadline);
|
|
17
17
|
return text === null ? NEUTRAL : { kind: 'context', block: text };
|
|
18
18
|
}
|
|
19
19
|
async function claimHandoff(options, reason, deadline) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { HarnessId } from '../contracts/harness.js';
|
|
2
|
+
import type { CatalogSkill } from '../contracts/skill-catalog.js';
|
|
3
|
+
export type SkillOrigin = {
|
|
4
|
+
readonly harness: HarnessId;
|
|
5
|
+
readonly folder: string;
|
|
6
|
+
};
|
|
7
|
+
export type SkillCandidate = {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly description: string;
|
|
10
|
+
readonly origins: readonly SkillOrigin[];
|
|
11
|
+
};
|
|
12
|
+
export type HarnessCatalog = {
|
|
13
|
+
readonly harness: HarnessId;
|
|
14
|
+
readonly skills: readonly CatalogSkill[];
|
|
15
|
+
};
|
|
16
|
+
export declare function mergeCatalogs(catalogs: readonly HarnessCatalog[]): SkillCandidate[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function byHarness(left, right) {
|
|
2
|
+
return left.harness.localeCompare(right.harness) || left.folder.localeCompare(right.folder);
|
|
3
|
+
}
|
|
4
|
+
export function mergeCatalogs(catalogs) {
|
|
5
|
+
const byName = new Map();
|
|
6
|
+
for (const { harness, skills } of catalogs) {
|
|
7
|
+
for (const skill of skills) {
|
|
8
|
+
const known = byName.get(skill.name) ?? { description: skill.description, origins: [] };
|
|
9
|
+
known.origins.push({ harness, folder: skill.folder });
|
|
10
|
+
byName.set(skill.name, known);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return [...byName].map(([name, { description, origins }]) => ({ name, description, origins: [...origins].sort(byHarness) }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function slashInvocation(skill) {
|
|
2
|
+
return `/${skill}`;
|
|
3
|
+
}
|
|
4
|
+
export function dollarInvocation(skill) {
|
|
5
|
+
return `$${skill}`;
|
|
6
|
+
}
|
|
7
|
+
export function skillSlashInvocation(skill) {
|
|
8
|
+
return `/skill:${skill}`;
|
|
9
|
+
}
|
|
10
|
+
export function sentenceInvocation(skill) {
|
|
11
|
+
return `use the ${skill} skill`;
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SkillCandidate } from './skill-candidates.js';
|
|
2
|
+
export type SkillRole = 'snapshot' | 'resume';
|
|
3
|
+
export type RankOptions = {
|
|
4
|
+
readonly exclude?: string | undefined;
|
|
5
|
+
};
|
|
6
|
+
export declare function scoreSkill(role: SkillRole, candidate: SkillCandidate): number;
|
|
7
|
+
export declare function rankSkills(role: SkillRole, candidates: readonly SkillCandidate[], options?: RankOptions): SkillCandidate[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const ROLE_TERMS = {
|
|
2
|
+
snapshot: ['snapshot', 'handoff', 'checkpoint'],
|
|
3
|
+
resume: ['resume', 'continue', 'retomar', 'retomada', 'continuar', 'orchestrate', 'flow'],
|
|
4
|
+
};
|
|
5
|
+
const NAME_SCORE = 100;
|
|
6
|
+
const DESCRIPTION_SCORE = 10;
|
|
7
|
+
const DESCRIPTION_WINDOW = 300;
|
|
8
|
+
function mentions(text, terms) {
|
|
9
|
+
const lowered = text.toLowerCase();
|
|
10
|
+
return terms.some((term) => lowered.includes(term));
|
|
11
|
+
}
|
|
12
|
+
export function scoreSkill(role, candidate) {
|
|
13
|
+
const terms = ROLE_TERMS[role];
|
|
14
|
+
if (mentions(candidate.name, terms))
|
|
15
|
+
return NAME_SCORE;
|
|
16
|
+
return mentions(candidate.description.slice(0, DESCRIPTION_WINDOW), terms) ? DESCRIPTION_SCORE : 0;
|
|
17
|
+
}
|
|
18
|
+
export function rankSkills(role, candidates, options = {}) {
|
|
19
|
+
return candidates
|
|
20
|
+
.filter((candidate) => candidate.name !== options.exclude)
|
|
21
|
+
.map((candidate) => ({ candidate, score: scoreSkill(role, candidate) }))
|
|
22
|
+
.filter(({ score }) => score > 0)
|
|
23
|
+
.sort((left, right) => right.score - left.score || right.candidate.origins.length - left.candidate.origins.length || left.candidate.name.localeCompare(right.candidate.name))
|
|
24
|
+
.map(({ candidate }) => candidate);
|
|
25
|
+
}
|
|
@@ -2,6 +2,7 @@ import { type SnapshotConfig } from '../contracts/configuration.js';
|
|
|
2
2
|
export type SnapshotFlags = {
|
|
3
3
|
readonly command?: string | undefined;
|
|
4
4
|
readonly triggerZone?: string | undefined;
|
|
5
|
+
readonly triggerPercentage?: number | undefined;
|
|
5
6
|
readonly resumeCommand?: string | undefined;
|
|
6
7
|
readonly clearCommand: boolean;
|
|
7
8
|
};
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import { snapshotSchema } from '../contracts/configuration.js';
|
|
2
2
|
const SECTION_PATH = 'snapshot';
|
|
3
|
+
const CONFIG_FILE = 'context-brake.config.json';
|
|
4
|
+
const FLAG_BY_FIELD = { command: '--snapshot-command <skill>', resumeCommand: '--resume-command <skill>', triggerZone: '--snapshot-trigger <YELLOW|RED>', triggerPercentage: '--snapshot-trigger-percentage <1-100>' };
|
|
3
5
|
const CLEAR_CONFLICT = '--no-snapshot-command cannot be combined with --snapshot-command or --resume-command.';
|
|
4
6
|
export function mergeSnapshot(current, flags) {
|
|
5
7
|
if (flags.clearCommand && (flags.command !== undefined || flags.resumeCommand !== undefined))
|
|
6
8
|
return { error: CLEAR_CONFLICT };
|
|
7
|
-
if (!flags.clearCommand && [flags.command, flags.triggerZone, flags.resumeCommand].every((value) => value === undefined))
|
|
9
|
+
if (!flags.clearCommand && [flags.command, flags.triggerZone, flags.triggerPercentage, flags.resumeCommand].every((value) => value === undefined))
|
|
8
10
|
return { update: { kind: 'keep' } };
|
|
9
|
-
const
|
|
10
|
-
const result = snapshotSchema.safeParse(withoutUndefined({ ...base, ...definedFields(flags) }));
|
|
11
|
+
const result = snapshotSchema.safeParse(withoutUndefined({ ...baseSection(current, flags), ...definedFields(flags) }));
|
|
11
12
|
if (result.success)
|
|
12
13
|
return { update: { kind: 'set', section: result.data } };
|
|
13
14
|
const issue = result.error.issues[0];
|
|
14
15
|
const path = [SECTION_PATH, ...(issue?.path ?? [])].join('.');
|
|
15
|
-
|
|
16
|
+
const flag = FLAG_BY_FIELD[String(issue?.path[0])];
|
|
17
|
+
const where = flag === undefined ? '' : ` Set it with ${flag} or in ${CONFIG_FILE}.`;
|
|
18
|
+
return { error: `Invalid snapshot option: ${path} ${issue?.message ?? 'is invalid'}.${where}` };
|
|
16
19
|
}
|
|
17
20
|
export function applySnapshot(config, update) {
|
|
18
21
|
return update.kind === 'keep' ? config : { ...config, snapshot: update.section };
|
|
19
22
|
}
|
|
23
|
+
function baseSection(current, flags) {
|
|
24
|
+
const trigger = { triggerZone: current?.triggerZone, triggerPercentage: flags.triggerZone === undefined ? current?.triggerPercentage : undefined };
|
|
25
|
+
return flags.clearCommand ? trigger : { ...current, ...trigger };
|
|
26
|
+
}
|
|
20
27
|
function definedFields(flags) {
|
|
21
|
-
return withoutUndefined({ command: flags.command, triggerZone: flags.triggerZone, resumeCommand: flags.resumeCommand });
|
|
28
|
+
return withoutUndefined({ command: flags.command, triggerZone: flags.triggerZone, triggerPercentage: flags.triggerPercentage, resumeCommand: flags.resumeCommand });
|
|
22
29
|
}
|
|
23
30
|
function withoutUndefined(values) {
|
|
24
31
|
return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import type { SnapshotConfig } from '../contracts/configuration.js';
|
|
2
|
+
import type { SkillInvocationFormat } from '../contracts/skill-invocation.js';
|
|
2
3
|
import { type Zone } from '../contracts/zones.js';
|
|
3
4
|
import type { RestartMode } from './restart-mode.js';
|
|
4
|
-
export
|
|
5
|
+
export type GuidanceOptions = {
|
|
6
|
+
readonly mode?: RestartMode | undefined;
|
|
7
|
+
readonly invoke?: SkillInvocationFormat | undefined;
|
|
8
|
+
readonly usagePercentage?: number | undefined;
|
|
9
|
+
};
|
|
10
|
+
export type ZoneUsage = {
|
|
11
|
+
readonly zone: Zone;
|
|
12
|
+
readonly usagePercentage: number;
|
|
13
|
+
};
|
|
14
|
+
export declare function isResetDue(reading: ZoneUsage, snapshot: SnapshotConfig): boolean;
|
|
15
|
+
export declare function zoneAction(zone: Zone, snapshot: SnapshotConfig, options?: GuidanceOptions): string;
|
|
5
16
|
export declare function handoffResumeText(archivedPath: string): string;
|
|
6
|
-
export declare function resumeText(snapshot: SnapshotConfig): string | null;
|
|
17
|
+
export declare function resumeText(snapshot: SnapshotConfig, invoke?: SkillInvocationFormat): string | null;
|