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
|
@@ -1,34 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { PLAIN_STYLE } from '../output/style.js';
|
|
2
|
+
import { renderCard } from '../output/card.js';
|
|
3
|
+
import { harnessRows, optionRows, snapshotRows } from './summary-rows.js';
|
|
4
|
+
const SUMMARY_TITLE = 'Summary of your choices:';
|
|
5
|
+
const COMMAND_LABEL = 'Equivalent command:';
|
|
6
|
+
const COMMAND_INDENT = ' ';
|
|
7
|
+
function commandLines(command, style) {
|
|
8
|
+
const label = style.paint('muted', COMMAND_LABEL);
|
|
9
|
+
if (command.length === 1)
|
|
10
|
+
return [`${label} ${style.paint('accent', command[0] ?? '')}`];
|
|
11
|
+
return [label, ...command.map((line) => `${COMMAND_INDENT}${style.paint('accent', line)}`)];
|
|
6
12
|
}
|
|
7
|
-
function
|
|
8
|
-
const state = facts.restartOn ? `on, at most ${facts.restartLimit ?? 'the default number of'} consecutive restarts` : 'off';
|
|
9
|
-
return [`Restart: ${state}`, ...facts.restartModes.map((fact) => ` ${fact.harness}: ${fact.mode}`)];
|
|
10
|
-
}
|
|
11
|
-
function bridgeLine(facts) {
|
|
12
|
-
if (facts.statuslineBridge === null)
|
|
13
|
-
return 'Status line bridge: not applicable (Claude Code not selected)';
|
|
14
|
-
return `Status line bridge: ${facts.statuslineBridge ? 'yes' : 'no'}`;
|
|
15
|
-
}
|
|
16
|
-
function gitIgnoreLine(facts) {
|
|
17
|
-
if (facts.gitIgnore === null)
|
|
18
|
-
return 'Git ignore: not applicable (not a Git repository)';
|
|
19
|
-
return `Git ignore: ${facts.gitIgnore ? 'yes' : 'no'}`;
|
|
20
|
-
}
|
|
21
|
-
export function renderSummary(facts, command) {
|
|
22
|
-
const excluded = facts.excluded.length === 0 ? '' : ` (turned off: ${facts.excluded.join(', ')})`;
|
|
23
|
-
const equivalent = command.length === 1 ? [`Equivalent command: ${command[0]}`] : ['Equivalent command:', ...command.map((line) => ` ${line}`)];
|
|
13
|
+
export function renderSummary(facts, command, style = PLAIN_STYLE) {
|
|
24
14
|
return [
|
|
25
|
-
'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
style.paint('title', SUMMARY_TITLE),
|
|
16
|
+
'',
|
|
17
|
+
...renderCard('Harnesses', harnessRows(facts), style),
|
|
18
|
+
'',
|
|
19
|
+
...renderCard('Snapshot', snapshotRows(facts), style),
|
|
20
|
+
'',
|
|
21
|
+
...renderCard('Options', optionRows(facts), style),
|
|
22
|
+
'',
|
|
23
|
+
...commandLines(command, style),
|
|
33
24
|
];
|
|
34
25
|
}
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
import type { HarnessAdapter } from '../../core/contracts/adapter.js';
|
|
2
2
|
import type { ContextBrakeConfig } from '../../core/contracts/configuration.js';
|
|
3
3
|
import type { HarnessDetection, HarnessId } from '../../core/contracts/harness.js';
|
|
4
|
+
import type { SkillCatalogReader } from '../../core/contracts/skill-catalog.js';
|
|
5
|
+
import type { SkillInvocationResolver } from '../../core/contracts/skill-invocation.js';
|
|
6
|
+
import type { ContextSource } from '../../infrastructure/harnesses/claude-code/context-source.js';
|
|
4
7
|
import type { HarnessRestartMode } from '../../core/services/restart-install-extras.js';
|
|
5
8
|
export type AssistantContext = {
|
|
6
9
|
readonly config: ContextBrakeConfig | null;
|
|
7
10
|
readonly detections: readonly HarnessDetection[];
|
|
8
11
|
readonly adapters: readonly HarnessAdapter[];
|
|
9
12
|
readonly hasStatuslineOptOut: boolean;
|
|
13
|
+
readonly contextSource: ContextSource;
|
|
10
14
|
readonly insideGit: boolean;
|
|
15
|
+
readonly invocations: SkillInvocationResolver;
|
|
16
|
+
readonly projectRoot: string;
|
|
17
|
+
readonly catalog: SkillCatalogReader;
|
|
18
|
+
};
|
|
19
|
+
export type HarnessInvocationFact = {
|
|
20
|
+
readonly harness: HarnessId;
|
|
21
|
+
readonly command: string;
|
|
22
|
+
readonly resumeCommand: string | null;
|
|
11
23
|
};
|
|
12
24
|
export type HarnessRestartFact = {
|
|
13
25
|
readonly harness: HarnessId;
|
|
@@ -18,11 +30,14 @@ export type AssistantFacts = {
|
|
|
18
30
|
readonly excluded: readonly HarnessId[];
|
|
19
31
|
readonly snapshotCommand: string | null;
|
|
20
32
|
readonly triggerZone: string;
|
|
33
|
+
readonly triggerPercentage: number | null;
|
|
21
34
|
readonly resumeCommand: string | null;
|
|
35
|
+
readonly invocations: readonly HarnessInvocationFact[];
|
|
22
36
|
readonly restartOn: boolean;
|
|
23
37
|
readonly restartLimit: number | null;
|
|
24
38
|
readonly restartModes: readonly HarnessRestartFact[];
|
|
25
39
|
readonly statuslineBridge: boolean | null;
|
|
40
|
+
readonly contextSource: ContextSource | null;
|
|
26
41
|
readonly debug: boolean;
|
|
27
42
|
readonly gitIgnore: boolean | null;
|
|
28
43
|
};
|
|
@@ -4,6 +4,7 @@ import { NodeManifestStore } from '../../infrastructure/storage/manifest-store.j
|
|
|
4
4
|
import { NodeOverheadMeasurer } from '../../infrastructure/diagnostics/overhead-measurer.js';
|
|
5
5
|
import { readPackageVersion } from '../../infrastructure/storage/package-metadata.js';
|
|
6
6
|
import { getAllAdapters } from '../../infrastructure/harnesses/registry.js';
|
|
7
|
+
import { resolveSkillInvocation } from '../../infrastructure/harnesses/skill-invocations.js';
|
|
7
8
|
import { systemClock } from '../../infrastructure/runtime/runtime-composition.js';
|
|
8
9
|
import { NodeRuntimeStateReader } from '../../infrastructure/runtime/runtime-state-reader.js';
|
|
9
10
|
import { diagnoseProject } from '../../core/services/doctor-service.js';
|
|
@@ -42,7 +43,7 @@ export async function runDoctor(args, env) {
|
|
|
42
43
|
projectRoot: env.projectRoot, config, configError, adapters, context: ctx, sources,
|
|
43
44
|
...(args.harness.length > 0 ? { explicitHarnesses: args.harness } : {}), measurer,
|
|
44
45
|
manifest, allSnapshots, packageVersion, contextWindow: await readClaudeContextWindow(env.projectRoot),
|
|
45
|
-
runtimeState: await new NodeRuntimeStateReader(env.projectRoot, systemClock).read(), now: systemClock.now(),
|
|
46
|
+
runtimeState: await new NodeRuntimeStateReader(env.projectRoot, systemClock).read(), now: systemClock.now(), invocations: resolveSkillInvocation,
|
|
46
47
|
});
|
|
47
48
|
const installed = adapters.filter((adapter) => !reportsRestart(diagnosed.findings, adapter.id) && diagnosed.integrations.some((integration) => integration.harness === adapter.id && integration.state === 'installed'));
|
|
48
49
|
const report = { ...diagnosed, findings: [...diagnosed.findings, ...await doctorRestartFindings(env.projectRoot, config, installed)] };
|
|
@@ -9,7 +9,7 @@ import { getAllAdapters } from '../../infrastructure/harnesses/registry.js';
|
|
|
9
9
|
import { planRemoval } from '../../core/services/removal-service.js';
|
|
10
10
|
import { buildInstallReport } from '../../core/services/report-service.js';
|
|
11
11
|
import { renderJsonOutput } from '../output/json.js';
|
|
12
|
-
import { renderInstallText } from '../output/text.js';
|
|
12
|
+
import { renderInstallText } from '../output/install-text.js';
|
|
13
13
|
import { collectProjectSnapshots } from '../snapshot-helper.js';
|
|
14
14
|
import { buildHarnessContext } from '../detection-collector.js';
|
|
15
15
|
import { authorizeWrite } from '../confirmation.js';
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { parseArgs } from 'node:util';
|
|
2
2
|
import { hasRestartMode } from '../core/services/restart-install-extras.js';
|
|
3
3
|
import { CliArgumentError, validateHarnessIds, validateInclusionExclusion } from './argument-validator.js';
|
|
4
|
-
import { assertGitIgnoreCompatible, assertInteractiveCompatible, assertMaxRestartsCompatible, parseMaxRestarts } from './init-option-rules.js';
|
|
4
|
+
import { assertGitIgnoreCompatible, assertInteractiveCompatible, assertMaxRestartsCompatible, assertTriggerCompatible, parseMaxRestarts, parseTriggerPercentage } from './init-option-rules.js';
|
|
5
5
|
const INIT_OPTIONS = {
|
|
6
6
|
'dry-run': { type: 'boolean', default: false }, yes: { type: 'boolean', short: 'y', default: false },
|
|
7
7
|
'json': { type: 'boolean', default: false }, harness: { type: 'string', multiple: true, default: [] },
|
|
8
8
|
'exclude-harness': { type: 'string', multiple: true, default: [] },
|
|
9
|
-
'snapshot-command': { type: 'string' }, 'snapshot-trigger': { type: 'string' }, 'resume-command': { type: 'string' },
|
|
9
|
+
'snapshot-command': { type: 'string' }, 'snapshot-trigger': { type: 'string' }, 'snapshot-trigger-percentage': { type: 'string' }, 'resume-command': { type: 'string' },
|
|
10
10
|
'no-snapshot-command': { type: 'boolean', default: false },
|
|
11
11
|
debug: { type: 'boolean', default: false }, 'no-debug': { type: 'boolean', default: false },
|
|
12
12
|
'statusline-bridge': { type: 'boolean', default: false }, 'no-statusline-bridge': { type: 'boolean', default: false },
|
|
@@ -21,6 +21,7 @@ export function parseInit(args) {
|
|
|
21
21
|
validateInclusionExclusion(harness, excludeHarness);
|
|
22
22
|
const maxRestarts = parseMaxRestarts(values['max-restarts']);
|
|
23
23
|
assertMaxRestartsCompatible(maxRestarts, values['no-auto-restart']);
|
|
24
|
+
assertTriggerCompatible({ zone: values['snapshot-trigger'], percentage: parseTriggerPercentage(values['snapshot-trigger-percentage']) });
|
|
24
25
|
assertGitIgnoreCompatible({ gitignore: values.gitignore, noGitignore: values['no-gitignore'] });
|
|
25
26
|
assertInteractiveCompatible({ interactive: values.interactive, yes: values.yes, json: values.json });
|
|
26
27
|
return {
|
|
@@ -33,12 +34,12 @@ export function parseInit(args) {
|
|
|
33
34
|
}
|
|
34
35
|
export function hasConfigurationFlag(args) {
|
|
35
36
|
const snapshot = args.snapshot;
|
|
36
|
-
const hasSnapshotFlag = snapshot !== undefined && (snapshot.clearCommand || [snapshot.command, snapshot.triggerZone, snapshot.resumeCommand].some((value) => value !== undefined));
|
|
37
|
+
const hasSnapshotFlag = snapshot !== undefined && (snapshot.clearCommand || [snapshot.command, snapshot.triggerZone, snapshot.triggerPercentage, snapshot.resumeCommand].some((value) => value !== undefined));
|
|
37
38
|
const flags = [args.debug, args.noDebug, args.autoRestart, args.noAutoRestart, args.gitignore, args.noGitignore, hasSnapshotFlag];
|
|
38
39
|
return args.harness.length > 0 || args.excludeHarness.length > 0 || args.statuslineBridge !== undefined || args.maxRestarts !== undefined || flags.some(Boolean);
|
|
39
40
|
}
|
|
40
41
|
function snapshotFlags(values) {
|
|
41
|
-
return { command: values['snapshot-command'], triggerZone: values['snapshot-trigger'], resumeCommand: values['resume-command'], clearCommand: values['no-snapshot-command'] };
|
|
42
|
+
return { command: values['snapshot-command'], triggerZone: values['snapshot-trigger'], triggerPercentage: parseTriggerPercentage(values['snapshot-trigger-percentage']), resumeCommand: values['resume-command'], clearCommand: values['no-snapshot-command'] };
|
|
42
43
|
}
|
|
43
44
|
const STATUSLINE_TARGET_ERROR = '--statusline-bridge and --no-statusline-bridge require claude-code among the target harnesses.';
|
|
44
45
|
function statuslineBridgeRequest(values, targets) {
|
|
@@ -10,7 +10,7 @@ import { NodeProcessRunner } from '../infrastructure/process/node-process-runner
|
|
|
10
10
|
import { planInstallation } from '../core/services/installation-service.js';
|
|
11
11
|
import { buildInstallReport } from '../core/services/report-service.js';
|
|
12
12
|
import { renderJsonOutput } from './output/json.js';
|
|
13
|
-
import { renderInstallText } from './output/text.js';
|
|
13
|
+
import { renderInstallText } from './output/install-text.js';
|
|
14
14
|
import { collectProjectSnapshots, collectRestartLogSnapshots } from './snapshot-helper.js';
|
|
15
15
|
import { buildHarnessContext, collectHarnessSources } from './detection-collector.js';
|
|
16
16
|
import { authorizeWrite } from './confirmation.js';
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export declare function parseMaxRestarts(value: string | undefined): number | undefined;
|
|
2
|
+
export declare function parseTriggerPercentage(value: string | undefined): number | undefined;
|
|
3
|
+
export declare function assertTriggerCompatible(flags: {
|
|
4
|
+
readonly zone: string | undefined;
|
|
5
|
+
readonly percentage: number | undefined;
|
|
6
|
+
}): void;
|
|
2
7
|
export declare function assertMaxRestartsCompatible(maxRestarts: number | undefined, noAutoRestart: boolean): void;
|
|
3
8
|
export declare function assertGitIgnoreCompatible(flags: {
|
|
4
9
|
readonly gitignore: boolean;
|
|
@@ -2,6 +2,10 @@ import { MAX_CONSECUTIVE_RESTARTS, MIN_CONSECUTIVE_RESTARTS } from '../core/cont
|
|
|
2
2
|
import { CliArgumentError } from './argument-validator.js';
|
|
3
3
|
const MAX_RESTARTS_RULE = `--max-restarts must be an integer from ${MIN_CONSECUTIVE_RESTARTS} to ${MAX_CONSECUTIVE_RESTARTS}.`;
|
|
4
4
|
const MAX_RESTARTS_CONFLICT = '--max-restarts cannot be combined with --no-auto-restart.';
|
|
5
|
+
const MIN_TRIGGER_PERCENTAGE = 1;
|
|
6
|
+
const MAX_TRIGGER_PERCENTAGE = 100;
|
|
7
|
+
const TRIGGER_PERCENTAGE_RULE = `--snapshot-trigger-percentage must be an integer from ${MIN_TRIGGER_PERCENTAGE} to ${MAX_TRIGGER_PERCENTAGE}.`;
|
|
8
|
+
const TRIGGER_CONFLICT = '--snapshot-trigger cannot be combined with --snapshot-trigger-percentage.';
|
|
5
9
|
const INTEGER_PATTERN = /^\d+$/;
|
|
6
10
|
export function parseMaxRestarts(value) {
|
|
7
11
|
if (value === undefined)
|
|
@@ -11,6 +15,18 @@ export function parseMaxRestarts(value) {
|
|
|
11
15
|
throw new CliArgumentError(MAX_RESTARTS_RULE);
|
|
12
16
|
return parsed;
|
|
13
17
|
}
|
|
18
|
+
export function parseTriggerPercentage(value) {
|
|
19
|
+
if (value === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
const parsed = INTEGER_PATTERN.test(value) ? Number(value) : Number.NaN;
|
|
22
|
+
if (!(parsed >= MIN_TRIGGER_PERCENTAGE && parsed <= MAX_TRIGGER_PERCENTAGE))
|
|
23
|
+
throw new CliArgumentError(TRIGGER_PERCENTAGE_RULE);
|
|
24
|
+
return parsed;
|
|
25
|
+
}
|
|
26
|
+
export function assertTriggerCompatible(flags) {
|
|
27
|
+
if (flags.zone !== undefined && flags.percentage !== undefined)
|
|
28
|
+
throw new CliArgumentError(TRIGGER_CONFLICT);
|
|
29
|
+
}
|
|
14
30
|
export function assertMaxRestartsCompatible(maxRestarts, noAutoRestart) {
|
|
15
31
|
if (maxRestarts !== undefined && noAutoRestart)
|
|
16
32
|
throw new CliArgumentError(MAX_RESTARTS_CONFLICT);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Style, Tone } from './style.js';
|
|
2
|
+
export type CardRow = {
|
|
3
|
+
readonly label: string;
|
|
4
|
+
readonly value: string;
|
|
5
|
+
readonly tone?: Tone;
|
|
6
|
+
readonly notes?: readonly string[];
|
|
7
|
+
};
|
|
8
|
+
export declare function renderCard(title: string, rows: readonly CardRow[], style: Style): string[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const COLUMN_GAP = ' ';
|
|
2
|
+
const NOTE_WIDTH = 76;
|
|
3
|
+
function wrapWords(text, width) {
|
|
4
|
+
const lines = [];
|
|
5
|
+
for (const word of text.split(' ')) {
|
|
6
|
+
const last = lines.at(-1);
|
|
7
|
+
if (last !== undefined && last.length + 1 + word.length <= width)
|
|
8
|
+
lines[lines.length - 1] = `${last} ${word}`;
|
|
9
|
+
else
|
|
10
|
+
lines.push(word);
|
|
11
|
+
}
|
|
12
|
+
return lines;
|
|
13
|
+
}
|
|
14
|
+
function railed(style, content) {
|
|
15
|
+
return `${style.paint('muted', style.glyphs.rail)}${COLUMN_GAP}${content}`;
|
|
16
|
+
}
|
|
17
|
+
function renderRow(row, labelWidth, style) {
|
|
18
|
+
const label = style.paint('muted', row.label.padEnd(labelWidth));
|
|
19
|
+
const notes = (row.notes ?? []).flatMap((note) => wrapWords(note, NOTE_WIDTH));
|
|
20
|
+
const noteLines = notes.map((note) => railed(style, `${' '.repeat(labelWidth)}${COLUMN_GAP}${style.paint('muted', note)}`));
|
|
21
|
+
return [railed(style, `${label}${COLUMN_GAP}${style.paint(row.tone ?? 'plain', row.value)}`), ...noteLines];
|
|
22
|
+
}
|
|
23
|
+
export function renderCard(title, rows, style) {
|
|
24
|
+
const labelWidth = Math.max(...rows.map((row) => row.label.length));
|
|
25
|
+
return [
|
|
26
|
+
`${style.paint('muted', style.glyphs.top)} ${style.paint('title', title)}`,
|
|
27
|
+
...rows.flatMap((row) => renderRow(row, labelWidth, style)),
|
|
28
|
+
style.paint('muted', style.glyphs.bottom),
|
|
29
|
+
];
|
|
30
|
+
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
import { describeTrigger } from '../../core/services/trigger-text.js';
|
|
2
|
+
function renderInvocationLine(invocation) {
|
|
3
|
+
const resume = invocation.resumeCommand === null ? '' : `, resume: ${invocation.resumeCommand}`;
|
|
4
|
+
return ` - ${invocation.harness}: ${invocation.command}${resume}\n`;
|
|
5
|
+
}
|
|
1
6
|
function renderSnapshotLine(snapshot) {
|
|
2
7
|
if (snapshot === undefined)
|
|
3
8
|
return '';
|
|
4
9
|
if (snapshot.command === null)
|
|
5
|
-
return ` - snapshot: not configured (zone headers only, trigger: ${snapshot
|
|
10
|
+
return ` - snapshot: not configured (zone headers only, trigger: ${describeTrigger(snapshot)})\n`;
|
|
6
11
|
const resume = snapshot.resumeCommand === null ? '' : `, resume: ${snapshot.resumeCommand}`;
|
|
7
|
-
|
|
12
|
+
const forms = (snapshot.invocations ?? []).map(renderInvocationLine).join('');
|
|
13
|
+
return ` - snapshot: ${snapshot.command} at ${describeTrigger(snapshot)}${resume}\n${forms}`;
|
|
8
14
|
}
|
|
9
15
|
export function renderModeLines(report) {
|
|
10
16
|
const debug = report.debugMode === true ? ' - debug mode: on\n' : '';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InstallReport } from '../../core/contracts/diagnostics.js';
|
|
2
|
+
import type { CardRow } from './card.js';
|
|
3
|
+
export declare function harnessRows(report: InstallReport): CardRow[];
|
|
4
|
+
export declare function changeRows(report: InstallReport): CardRow[];
|
|
5
|
+
export declare function conflictRows(report: InstallReport): CardRow[];
|
|
6
|
+
export declare function findingRows(report: InstallReport): CardRow[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const CHANGE_TONES = { create: 'good', update: 'accent', delete: 'plain' };
|
|
2
|
+
const SEVERITY_LABELS = {
|
|
3
|
+
ok: { label: '[OK]', tone: 'good' },
|
|
4
|
+
warning: { label: '[WARN]', tone: 'warn' },
|
|
5
|
+
error: { label: '[ERROR]', tone: 'bad' },
|
|
6
|
+
};
|
|
7
|
+
export function harnessRows(report) {
|
|
8
|
+
const planned = report.plan.harnesses.map((h) => ({
|
|
9
|
+
label: h.harness, value: `${h.supportLevel} support (${h.outcome})`, tone: h.outcome === 'conflict' ? 'bad' : 'good',
|
|
10
|
+
notes: h.limitations.map((limitation) => `${limitation.capability}: ${limitation.impact}`),
|
|
11
|
+
}));
|
|
12
|
+
const excluded = report.detections.filter((detection) => detection.state === 'excluded');
|
|
13
|
+
return [...planned, ...excluded.map((detection) => ({ label: detection.harness, value: 'excluded by configuration', tone: 'muted' }))];
|
|
14
|
+
}
|
|
15
|
+
export function changeRows(report) {
|
|
16
|
+
const showSummary = report.command === 'init';
|
|
17
|
+
return report.plan.changes.map((change) => ({
|
|
18
|
+
label: change.kind, value: `${change.path} (${change.owner})`, tone: CHANGE_TONES[change.kind],
|
|
19
|
+
notes: showSummary && (change.owner === 'config' || change.owner === 'gitignore') ? [change.preview.summary] : [],
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
export function conflictRows(report) {
|
|
23
|
+
return report.plan.conflicts.map((conflict) => ({ label: SEVERITY_LABELS.error.label, value: `${conflict.path}: ${conflict.detail}`, tone: 'bad' }));
|
|
24
|
+
}
|
|
25
|
+
export function findingRows(report) {
|
|
26
|
+
return report.findings.map((finding) => {
|
|
27
|
+
const { label, tone } = SEVERITY_LABELS[finding.severity];
|
|
28
|
+
const notes = [finding.impact ? `Impact: ${finding.impact}` : '', finding.remediation ? `Remediation: ${finding.remediation}` : ''];
|
|
29
|
+
return { label, value: `${finding.code}: ${finding.message}`, tone, notes: notes.filter((note) => note !== '') };
|
|
30
|
+
});
|
|
31
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { InstallReport } from '../../core/contracts/diagnostics.js';
|
|
2
|
+
import { type Style } from './style.js';
|
|
3
|
+
export declare function formatInstallReport(report: InstallReport, style: Style): string;
|
|
4
|
+
export declare function renderInstallText(report: InstallReport, style?: Style): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { renderCard } from './card.js';
|
|
2
|
+
import { changeRows, conflictRows, findingRows, harnessRows } from './install-rows.js';
|
|
3
|
+
import { detectStyle } from './style.js';
|
|
4
|
+
const STATUS_WORDS = {
|
|
5
|
+
success: { label: '[OK]', tone: 'good' },
|
|
6
|
+
warnings: { label: '[WARN]', tone: 'warn' },
|
|
7
|
+
errors: { label: '[ERROR]', tone: 'bad' },
|
|
8
|
+
};
|
|
9
|
+
function headerLine(report, style) {
|
|
10
|
+
const { label, tone } = STATUS_WORDS[report.status];
|
|
11
|
+
return `${style.paint(tone, label)} ${style.paint('title', `ContextBrake ${report.command} (${report.mode})`)}`;
|
|
12
|
+
}
|
|
13
|
+
function cardSections(report, style) {
|
|
14
|
+
const sections = [
|
|
15
|
+
['Harnesses', harnessRows(report)], ['Planned changes', changeRows(report)], ['Conflicts', conflictRows(report)], ['Findings', findingRows(report)],
|
|
16
|
+
];
|
|
17
|
+
return sections.filter(([, rows]) => rows.length > 0).flatMap(([title, rows]) => ['', ...renderCard(title, rows, style)]);
|
|
18
|
+
}
|
|
19
|
+
export function formatInstallReport(report, style) {
|
|
20
|
+
return `${[headerLine(report, style), ...cardSections(report, style)].join('\n')}\n`;
|
|
21
|
+
}
|
|
22
|
+
export function renderInstallText(report, style) {
|
|
23
|
+
const stream = report.status === 'errors' ? process.stderr : process.stdout;
|
|
24
|
+
const chosen = style ?? detectStyle({ env: process.env, platform: process.platform, isTty: stream.isTTY === true });
|
|
25
|
+
stream.write(formatInstallReport(report, chosen));
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type Tone = 'plain' | 'title' | 'accent' | 'good' | 'muted' | 'warn' | 'bad';
|
|
2
|
+
export type Glyphs = {
|
|
3
|
+
readonly top: string;
|
|
4
|
+
readonly rail: string;
|
|
5
|
+
readonly bottom: string;
|
|
6
|
+
};
|
|
7
|
+
export type Style = {
|
|
8
|
+
readonly paint: (tone: Tone, text: string) => string;
|
|
9
|
+
readonly glyphs: Glyphs;
|
|
10
|
+
};
|
|
11
|
+
export type StyleOptions = {
|
|
12
|
+
readonly color: boolean;
|
|
13
|
+
readonly unicode: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type StyleEnvironment = {
|
|
16
|
+
readonly env: NodeJS.ProcessEnv;
|
|
17
|
+
readonly platform: NodeJS.Platform;
|
|
18
|
+
readonly isTty: boolean;
|
|
19
|
+
};
|
|
20
|
+
export declare function createStyle(options: StyleOptions): Style;
|
|
21
|
+
export declare const PLAIN_STYLE: Style;
|
|
22
|
+
export declare function detectStyle(environment: StyleEnvironment): Style;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const ESCAPE = String.fromCharCode(27);
|
|
2
|
+
const RESET = `${ESCAPE}[0m`;
|
|
3
|
+
const DUMB_TERMINAL = 'dumb';
|
|
4
|
+
const LINUX_CONSOLE = 'linux';
|
|
5
|
+
const UNICODE_GLYPHS = { top: '┌─', rail: '│', bottom: '└─' };
|
|
6
|
+
const ASCII_GLYPHS = { top: '+-', rail: '|', bottom: '+-' };
|
|
7
|
+
const SGR_CODES = { plain: '', title: '1;36', accent: '36', good: '32', muted: '2', warn: '33', bad: '31' };
|
|
8
|
+
const UNICODE_WINDOWS_SIGNALS = ['WT_SESSION', 'WT_PROFILE_ID', 'TERM_PROGRAM', 'COLORTERM', 'TERM', 'ConEmuTask', 'TERMINUS_SUBLIME'];
|
|
9
|
+
function colorize(tone, text) {
|
|
10
|
+
return SGR_CODES[tone] === '' ? text : `${ESCAPE}[${SGR_CODES[tone]}m${text}${RESET}`;
|
|
11
|
+
}
|
|
12
|
+
export function createStyle(options) {
|
|
13
|
+
const glyphs = options.unicode ? UNICODE_GLYPHS : ASCII_GLYPHS;
|
|
14
|
+
return { paint: (tone, text) => (options.color ? colorize(tone, text) : text), glyphs };
|
|
15
|
+
}
|
|
16
|
+
export const PLAIN_STYLE = createStyle({ color: false, unicode: true });
|
|
17
|
+
function isUnicodeTerminal({ env, platform }) {
|
|
18
|
+
if (platform !== 'win32')
|
|
19
|
+
return env.TERM !== LINUX_CONSOLE;
|
|
20
|
+
return UNICODE_WINDOWS_SIGNALS.some((name) => (env[name] ?? '') !== '');
|
|
21
|
+
}
|
|
22
|
+
function isColorTerminal({ env, isTty }) {
|
|
23
|
+
return isTty && (env.NO_COLOR ?? '') === '' && env.TERM !== DUMB_TERMINAL;
|
|
24
|
+
}
|
|
25
|
+
export function detectStyle(environment) {
|
|
26
|
+
return createStyle({ color: isColorTerminal(environment), unicode: isUnicodeTerminal(environment) });
|
|
27
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { CliErrorDocument, DiagnosticFinding, DoctorReport
|
|
1
|
+
import type { CliErrorDocument, DiagnosticFinding, DoctorReport } from '../../core/contracts/diagnostics.js';
|
|
2
2
|
export declare function renderFinding(f: DiagnosticFinding): string;
|
|
3
|
-
export declare function renderInstallText(report: InstallReport): void;
|
|
4
3
|
export declare function renderDoctorText(report: DoctorReport): void;
|
|
5
4
|
export declare function renderCliErrorText(doc: CliErrorDocument): void;
|
|
@@ -10,33 +10,6 @@ export function renderFinding(f) {
|
|
|
10
10
|
lines.push(` Remediation: ${f.remediation}`);
|
|
11
11
|
return lines.join('\n');
|
|
12
12
|
}
|
|
13
|
-
export function renderInstallText(report) {
|
|
14
|
-
const stream = report.status === 'errors' ? process.stderr : process.stdout;
|
|
15
|
-
const label = report.status === 'errors' ? '[ERROR]' : report.status === 'warnings' ? '[WARN]' : '[OK]';
|
|
16
|
-
stream.write(`${label} ContextBrake ${report.command} (${report.mode})\n`);
|
|
17
|
-
for (const h of report.plan.harnesses) {
|
|
18
|
-
stream.write(` - ${h.harness}: ${h.supportLevel} support (${h.outcome})\n`);
|
|
19
|
-
for (const lim of h.limitations)
|
|
20
|
-
stream.write(` * ${lim.capability}: ${lim.impact}\n`);
|
|
21
|
-
}
|
|
22
|
-
for (const line of excludedDetectionLines(report.detections))
|
|
23
|
-
stream.write(line);
|
|
24
|
-
if (report.plan.changes.length > 0) {
|
|
25
|
-
stream.write(' Planned changes:\n');
|
|
26
|
-
for (const c of report.plan.changes) {
|
|
27
|
-
stream.write(` [${c.kind}] ${c.path} (${c.owner})\n`);
|
|
28
|
-
if (report.command === 'init' && (c.owner === 'config' || c.owner === 'gitignore'))
|
|
29
|
-
stream.write(` ${c.preview.summary}\n`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if (report.plan.conflicts.length > 0) {
|
|
33
|
-
stream.write(' Conflicts:\n');
|
|
34
|
-
for (const c of report.plan.conflicts)
|
|
35
|
-
stream.write(` [ERROR] ${c.path}: ${c.detail}\n`);
|
|
36
|
-
}
|
|
37
|
-
for (const f of report.findings)
|
|
38
|
-
stream.write(`${renderFinding(f)}\n`);
|
|
39
|
-
}
|
|
40
13
|
export function renderDoctorText(report) {
|
|
41
14
|
const stream = report.status === 'errors' ? process.stderr : process.stdout;
|
|
42
15
|
const label = report.status === 'errors' ? '[ERROR]' : report.status === 'warnings' ? '[WARN]' : '[OK]';
|
|
@@ -57,7 +30,7 @@ export function renderDoctorText(report) {
|
|
|
57
30
|
stream.write(line);
|
|
58
31
|
const window = report.contextWindow;
|
|
59
32
|
if (window)
|
|
60
|
-
stream.write(` - context window: ${window.source} (bridge: ${window.bridge}, last window: ${window.lastWindowTokens ?? 'unknown'})\n`);
|
|
33
|
+
stream.write(` - context window: ${window.source} (mod: ${window.mod}, bridge: ${window.bridge}, last window: ${window.lastWindowTokens ?? 'unknown'})\n`);
|
|
61
34
|
stream.write(renderModeLines(report));
|
|
62
35
|
stream.write(renderActiveSessionsText(report.activeSessions, new Date()));
|
|
63
36
|
for (const f of report.findings)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PlannedChange, PlanConflict } from './changes.js';
|
|
2
2
|
import type { DiagnosticFinding } from './diagnostics.js';
|
|
3
3
|
import type { CapabilityProfile, DetectionEvidence, HarnessId, VersionProbe } from './harness.js';
|
|
4
|
-
import type { InstallationManifest, ManagedAsset, ManagedEntry } from './manifest.js';
|
|
4
|
+
import type { ConfigOrigin, InstallationManifest, ManagedAsset, ManagedEntry } from './manifest.js';
|
|
5
5
|
import type { ProcessRunner } from './processes.js';
|
|
6
6
|
export type StatuslineBridgeRequest = 'install' | 'remove' | 'default';
|
|
7
7
|
export type HarnessContext = {
|
|
@@ -19,6 +19,7 @@ export type AdapterPlan = {
|
|
|
19
19
|
readonly entries: readonly ManagedEntry[];
|
|
20
20
|
readonly assets?: readonly ManagedAsset[];
|
|
21
21
|
readonly assetPaths?: readonly string[];
|
|
22
|
+
readonly configOrigins?: readonly ConfigOrigin[];
|
|
22
23
|
readonly findings?: readonly DiagnosticFinding[];
|
|
23
24
|
};
|
|
24
25
|
export type BenchmarkFixture = {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod/mini';
|
|
2
2
|
export declare const INJECTION_MODES: readonly ["threshold_only", "always"];
|
|
3
|
-
export declare const MAX_AGENT_COMMAND_LENGTH = 200;
|
|
4
3
|
export declare const snapshotSchema: z.ZodMiniObject<{
|
|
5
4
|
triggerZone: z.ZodMiniDefault<z.ZodMiniEnum<{
|
|
6
5
|
YELLOW: "YELLOW";
|
|
7
6
|
RED: "RED";
|
|
8
7
|
}>>;
|
|
8
|
+
triggerPercentage: z.ZodMiniOptional<z.ZodMiniInt>;
|
|
9
9
|
command: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
10
10
|
resumeCommand: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
11
11
|
}, z.core.$strict>;
|
|
@@ -65,6 +65,7 @@ export declare const configurationSchema: z.ZodMiniObject<{
|
|
|
65
65
|
YELLOW: "YELLOW";
|
|
66
66
|
RED: "RED";
|
|
67
67
|
}>>;
|
|
68
|
+
triggerPercentage: z.ZodMiniOptional<z.ZodMiniInt>;
|
|
68
69
|
command: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
69
70
|
resumeCommand: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
70
71
|
}, z.core.$strict>>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod/mini';
|
|
2
2
|
import { autoRestartSchema } from './auto-restart.js';
|
|
3
3
|
import { HARNESS_IDS } from './harness.js';
|
|
4
|
+
import { skillNameSchema } from './skill-name.js';
|
|
4
5
|
import { SNAPSHOT_TRIGGER_ZONES } from './zones.js';
|
|
5
6
|
export const INJECTION_MODES = ['threshold_only', 'always'];
|
|
6
7
|
const DUPLICATE_ENTRIES_RULE = 'must not contain duplicates';
|
|
@@ -13,17 +14,8 @@ function uniqueCheck(message) {
|
|
|
13
14
|
return (ctx) => { if (!isUnique(ctx.value))
|
|
14
15
|
addIssue(ctx, { code: 'custom', path: [], input: ctx.value, message }); };
|
|
15
16
|
}
|
|
16
|
-
export const MAX_AGENT_COMMAND_LENGTH = 200;
|
|
17
|
-
const TRIMMED_RULE = 'must not have leading or trailing whitespace';
|
|
18
17
|
const RESUME_REQUIRES_COMMAND_RULE = 'requires snapshot.command';
|
|
19
|
-
const
|
|
20
|
-
const agentCommand = z.string().check(z.minLength(1), z.maxLength(MAX_AGENT_COMMAND_LENGTH), (ctx) => {
|
|
21
|
-
if (ctx.value.trim() !== ctx.value)
|
|
22
|
-
addIssue(ctx, { code: 'custom', path: [], input: ctx.value, message: TRIMMED_RULE });
|
|
23
|
-
if (/[\r\n]/.test(ctx.value))
|
|
24
|
-
addIssue(ctx, { code: 'custom', path: [], input: ctx.value, message: SINGLE_LINE_RULE });
|
|
25
|
-
});
|
|
26
|
-
export const snapshotSchema = z.strictObject({ triggerZone: z._default(z.enum(SNAPSHOT_TRIGGER_ZONES), 'RED'), command: z.optional(agentCommand), resumeCommand: z.optional(agentCommand) }).check((ctx) => {
|
|
18
|
+
export const snapshotSchema = z.strictObject({ triggerZone: z._default(z.enum(SNAPSHOT_TRIGGER_ZONES), 'RED'), triggerPercentage: z.optional(z.int().check(z.minimum(1), z.maximum(100))), command: z.optional(skillNameSchema), resumeCommand: z.optional(skillNameSchema) }).check((ctx) => {
|
|
27
19
|
if (ctx.value.resumeCommand !== undefined && ctx.value.command === undefined)
|
|
28
20
|
addIssue(ctx, { code: 'custom', path: ['resumeCommand'], input: ctx.value.resumeCommand, message: RESUME_REQUIRES_COMMAND_RULE });
|
|
29
21
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod/mini';
|
|
2
|
+
import type { HarnessId } from './harness.js';
|
|
3
|
+
export declare const CONTEXT_READINGS_RELATIVE_DIR = ".context-brake/runtime/context-readings";
|
|
4
|
+
export declare const CONTEXT_READING_MAXIMUM_CHARACTERS = 1024;
|
|
5
|
+
export declare const RUNTIME_IGNORE_RELATIVE_PATH = ".context-brake/runtime/.gitignore";
|
|
6
|
+
export declare const RUNTIME_IGNORE_CONTENT = "*\n";
|
|
7
|
+
export declare const contextReadingSchema: z.ZodMiniObject<{
|
|
8
|
+
v: z.ZodMiniLiteral<1>;
|
|
9
|
+
at: z.ZodMiniString<string>;
|
|
10
|
+
windowTokens: z.ZodMiniNullable<z.ZodMiniInt>;
|
|
11
|
+
inputTokens: z.ZodMiniNullable<z.ZodMiniInt>;
|
|
12
|
+
usedPercentage: z.ZodMiniNullable<z.ZodMiniNumber<number>>;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
export type ContextReading = z.infer<typeof contextReadingSchema>;
|
|
15
|
+
export declare function isReadingSessionId(value: string | undefined): value is string;
|
|
16
|
+
export declare function contextReadingRelativePath(harness: HarnessId, sessionId: string): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod/mini';
|
|
2
|
+
export const CONTEXT_READINGS_RELATIVE_DIR = '.context-brake/runtime/context-readings';
|
|
3
|
+
export const CONTEXT_READING_MAXIMUM_CHARACTERS = 1024;
|
|
4
|
+
export const RUNTIME_IGNORE_RELATIVE_PATH = '.context-brake/runtime/.gitignore';
|
|
5
|
+
export const RUNTIME_IGNORE_CONTENT = '*\n';
|
|
6
|
+
const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
7
|
+
const PERCENTAGE_MAXIMUM = 100;
|
|
8
|
+
export const contextReadingSchema = z.strictObject({
|
|
9
|
+
v: z.literal(1),
|
|
10
|
+
at: z.string().check(z.minLength(1)),
|
|
11
|
+
windowTokens: z.nullable(z.int().check(z.positive())),
|
|
12
|
+
inputTokens: z.nullable(z.int().check(z.nonnegative())),
|
|
13
|
+
usedPercentage: z.nullable(z.number().check(z.gte(0), z.lte(PERCENTAGE_MAXIMUM))),
|
|
14
|
+
});
|
|
15
|
+
export function isReadingSessionId(value) {
|
|
16
|
+
return value !== undefined && SESSION_ID_PATTERN.test(value);
|
|
17
|
+
}
|
|
18
|
+
export function contextReadingRelativePath(harness, sessionId) {
|
|
19
|
+
return `${CONTEXT_READINGS_RELATIVE_DIR}/${harness}/${sessionId}.json`;
|
|
20
|
+
}
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export declare const CONTEXT_WINDOW_BRIDGE_STATES: readonly ["absent", "installed", "inactive"];
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const CONTEXT_MOD_STATES: readonly ["absent", "installed", "reporting"];
|
|
4
|
+
export declare const CONTEXT_WINDOW_SOURCES: readonly ["mod", "statusline", "contextWindowCeiling"];
|
|
4
5
|
export declare const contextWindowReportSchema: z.ZodObject<{
|
|
5
6
|
bridge: z.ZodEnum<{
|
|
6
7
|
inactive: "inactive";
|
|
7
8
|
installed: "installed";
|
|
8
9
|
absent: "absent";
|
|
9
10
|
}>;
|
|
11
|
+
mod: z.ZodEnum<{
|
|
12
|
+
installed: "installed";
|
|
13
|
+
absent: "absent";
|
|
14
|
+
reporting: "reporting";
|
|
15
|
+
}>;
|
|
10
16
|
source: z.ZodEnum<{
|
|
11
17
|
contextWindowCeiling: "contextWindowCeiling";
|
|
18
|
+
mod: "mod";
|
|
12
19
|
statusline: "statusline";
|
|
13
20
|
}>;
|
|
14
21
|
lastWindowTokens: z.ZodNullable<z.ZodNumber>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const CONTEXT_WINDOW_BRIDGE_STATES = ['absent', 'installed', 'inactive'];
|
|
3
|
-
export const
|
|
3
|
+
export const CONTEXT_MOD_STATES = ['absent', 'installed', 'reporting'];
|
|
4
|
+
export const CONTEXT_WINDOW_SOURCES = ['mod', 'statusline', 'contextWindowCeiling'];
|
|
4
5
|
export const contextWindowReportSchema = z.object({
|
|
5
6
|
bridge: z.enum(CONTEXT_WINDOW_BRIDGE_STATES),
|
|
7
|
+
mod: z.enum(CONTEXT_MOD_STATES),
|
|
6
8
|
source: z.enum(CONTEXT_WINDOW_SOURCES),
|
|
7
9
|
lastWindowTokens: z.number().int().positive().nullable(),
|
|
8
10
|
}).strict();
|