devflow-kit 2.0.1 → 2.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/CHANGELOG.md +22 -0
- package/dist/cli/agents-view/render.js +9 -42
- package/dist/cli/agents-view/terminal.js +29 -153
- package/dist/cli/commands/agents.js +11 -1
- package/dist/cli/commands/flags.js +501 -82
- package/dist/cli/commands/init-seed.js +60 -42
- package/dist/cli/commands/init.js +36 -79
- package/dist/cli/commands/proxy.js +42 -13
- package/dist/cli/commands/uninstall.js +2 -3
- package/dist/cli/flags-view/index.js +9 -0
- package/dist/cli/flags-view/render.js +271 -0
- package/dist/cli/flags-view/state.js +478 -0
- package/dist/cli/flags-view/terminal.js +63 -0
- package/dist/cli/tui/cells.js +47 -0
- package/dist/cli/tui/terminal.js +329 -0
- package/dist/cli.js +3 -2
- package/dist/core/ansi.js +84 -0
- package/dist/core/flags.js +938 -123
- package/dist/core/manifest.js +97 -13
- package/dist/core/teammate-mode-cleanup.js +2 -2
- package/dist/hud/colors.js +6 -70
- package/package.json +2 -2
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Computes the initial state (seed) for init prompts from:
|
|
5
5
|
* - The existing manifest (from a prior install)
|
|
6
6
|
* - The project feature config (.devflow/config.json)
|
|
7
|
-
* - The current settings.json snapshot (for
|
|
7
|
+
* - The current settings.json snapshot (for view-mode resolution)
|
|
8
8
|
* - The plugin registry
|
|
9
9
|
*
|
|
10
10
|
* All exported functions are pure — no I/O, no side effects.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* beside init.ts in src/cli/commands/ rather than in src/core/ (which holds
|
|
14
14
|
* agent-neutral, target-agnostic utilities).
|
|
15
15
|
*/
|
|
16
|
-
import { resolveExistingViewMode, FLAG_REGISTRY } from '../../core/flags.js';
|
|
16
|
+
import { resolveExistingViewMode, FLAG_REGISTRY, defaultValueOf, readViewMode, } from '../../core/flags.js';
|
|
17
17
|
import { partitionSelectablePlugins } from '../../core/plugins.js';
|
|
18
18
|
/** Registry defaults — all features enabled except proxy (advanced-only, off by default). */
|
|
19
19
|
export const FEATURE_DEFAULTS = {
|
|
@@ -63,41 +63,43 @@ export function resolveSeedFeatures(manifest, projectConfig) {
|
|
|
63
63
|
return { ambient, memory, hud, knowledge, learning, rules, proxy, compliance };
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
* Resolve the
|
|
66
|
+
* Resolve the flag record for the init seed.
|
|
67
67
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* @param registry
|
|
68
|
+
* Returns a FlagsRecord containing ALL registry flags at their resolved values.
|
|
69
|
+
* FlagsRecord key-presence encodes the "known" concept (ADR-014): present key =
|
|
70
|
+
* known at last install, absent key = new to this install → adopt default on seed.
|
|
71
|
+
*
|
|
72
|
+
* @param manifestFlags - FlagsRecord from the manifest, or null for fresh install.
|
|
73
|
+
* @param registry - Flag registry to consult; injectable for tests.
|
|
74
74
|
*
|
|
75
75
|
* Rules:
|
|
76
|
-
* - null
|
|
77
|
-
* -
|
|
78
|
-
*
|
|
79
|
-
* -
|
|
80
|
-
*
|
|
81
|
-
*
|
|
76
|
+
* - null manifestFlags (fresh install) → all flags at registry defaults
|
|
77
|
+
* - Entry present → keep (coerceFlagValue is applied at read
|
|
78
|
+
* time via sanitizeFlagsRecord; PF-023)
|
|
79
|
+
* - Entry absent → adopt registry default (ADR-014)
|
|
80
|
+
* - Unknown IDs from old manifest → pass through unchanged (forward-compat)
|
|
81
|
+
*
|
|
82
|
+
* Applies ADR-014: absent key = unknown to this install → adopt default.
|
|
83
|
+
* view-mode is not set here; resolveInitSeed sets flags['view-mode'] after composing.
|
|
82
84
|
*/
|
|
83
|
-
export function resolveSeedFlags(
|
|
84
|
-
// Fresh install → all
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return
|
|
85
|
+
export function resolveSeedFlags(manifestFlags, registry = FLAG_REGISTRY) {
|
|
86
|
+
// Fresh install → all flags at registry defaults
|
|
87
|
+
if (manifestFlags === null) {
|
|
88
|
+
const result = {};
|
|
89
|
+
for (const flag of registry) {
|
|
90
|
+
result[flag.id] = defaultValueOf(flag); // single default-rule source (CONS-M2)
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
91
93
|
}
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
const result =
|
|
94
|
+
// Existing install: copy present entries then adopt defaults for absent flags.
|
|
95
|
+
// Unknown IDs from the old manifest pass through unchanged (forward-compat).
|
|
96
|
+
const result = { ...manifestFlags };
|
|
95
97
|
for (const flag of registry) {
|
|
96
|
-
if (flag.
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
if (flag.id in result)
|
|
99
|
+
continue; // known → keep
|
|
100
|
+
result[flag.id] = defaultValueOf(flag); // single default-rule source (CONS-M2)
|
|
99
101
|
}
|
|
100
|
-
return
|
|
102
|
+
return result;
|
|
101
103
|
}
|
|
102
104
|
/**
|
|
103
105
|
* Resolve the plugin selection buckets for the init seed.
|
|
@@ -161,23 +163,40 @@ export function resolveSeedPlugins(manifestPlugins, knownPlugins, allPlugins) {
|
|
|
161
163
|
/**
|
|
162
164
|
* Compose the full init seed from manifest, project config, settings, and registry.
|
|
163
165
|
*
|
|
164
|
-
*
|
|
166
|
+
* view-mode priority: existing settings.json (non-default) → manifest → 'default'.
|
|
167
|
+
* The resolved view mode is encoded into flags['view-mode'] so all flag state lives
|
|
168
|
+
* in one FlagsRecord (applying PF-015: fold before strip — the fold happens here).
|
|
165
169
|
*
|
|
166
170
|
* This is the single composition point; callers (init.ts hoist block) call this
|
|
167
|
-
* once and pass `seed` down to
|
|
171
|
+
* once and pass `seed` down to prompt wiring.
|
|
168
172
|
*/
|
|
169
173
|
export function resolveInitSeed(seedManifest, seedConfig, settingsSnapshot, plugins) {
|
|
170
174
|
const features = resolveSeedFeatures(seedManifest, seedConfig);
|
|
171
|
-
// null for
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
// features.flags is FlagsRecord; null for fresh install (no manifest).
|
|
176
|
+
// seedManifest?.features.flags may be absent at runtime on very old manifests not yet
|
|
177
|
+
// healed — ?? null collapses to fresh-install behavior (all flags at registry defaults).
|
|
178
|
+
const manifestFlags = seedManifest?.features.flags ?? null;
|
|
179
|
+
const flags = resolveSeedFlags(manifestFlags);
|
|
174
180
|
const manifestPlugins = seedManifest !== null ? seedManifest.plugins : null;
|
|
175
181
|
const { workflowPlugins, languagePlugins } = resolveSeedPlugins(manifestPlugins, seedManifest?.knownPlugins, plugins);
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
// Encode the resolved view mode into flags['view-mode'] (PF-015: all flag state in FlagsRecord).
|
|
183
|
+
// Priority: existing settings.json (non-default) → flags['view-mode'] from manifest → 'default'.
|
|
184
|
+
// resolveExistingViewMode returns undefined when absent or 'default' — treated as no-opinion.
|
|
185
|
+
const existingViewMode = resolveExistingViewMode(settingsSnapshot);
|
|
186
|
+
const manifestViewMode = readViewMode(flags); // already in flags via resolveSeedFlags spread
|
|
187
|
+
let resolvedViewMode;
|
|
188
|
+
if (existingViewMode !== undefined) {
|
|
189
|
+
resolvedViewMode = existingViewMode; // settings.json non-default wins
|
|
190
|
+
}
|
|
191
|
+
else if (manifestViewMode !== 'default') {
|
|
192
|
+
resolvedViewMode = manifestViewMode; // manifest non-default wins
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
resolvedViewMode = 'default'; // fall back to neutral
|
|
196
|
+
}
|
|
197
|
+
// Return a fresh spread rather than mutating flags in place — keeps this function pure
|
|
198
|
+
// per the module docblock and avoids aliasing if the caller inspects seed.flags.
|
|
199
|
+
return { features, flags: { ...flags, 'view-mode': resolvedViewMode }, workflowPlugins, languagePlugins };
|
|
181
200
|
}
|
|
182
201
|
/**
|
|
183
202
|
* Resolve the three seed inputs under the --reset gate.
|
|
@@ -208,8 +227,7 @@ export function resolveResetGatedInputs(reset, manifest, projectConfig, settings
|
|
|
208
227
|
* Per-key: `toggles.X ?? base.X` — an explicit CLI value (true/false) wins;
|
|
209
228
|
* undefined means "user did not specify this flag, keep the seed value".
|
|
210
229
|
*
|
|
211
|
-
*
|
|
212
|
-
* --recommended.
|
|
230
|
+
* Applies explicit --ambient/--no-ambient etc. passed alongside --recommended.
|
|
213
231
|
*/
|
|
214
232
|
export function applyCliToggles(base, toggles) {
|
|
215
233
|
return {
|
|
@@ -24,7 +24,7 @@ import { stripDevflowTeammateModeFromJson } from '../../core/teammate-mode-clean
|
|
|
24
24
|
import { addHudStatusLine, removeHudStatusLine } from './hud.js';
|
|
25
25
|
import { loadConfig as loadHudConfig, saveConfig as saveHudConfig } from '../../hud/config.js';
|
|
26
26
|
import { readManifest, writeManifest, resolvePluginList, detectUpgrade } from '../../core/manifest.js';
|
|
27
|
-
import {
|
|
27
|
+
import { convergeFlagsIntoSettings, countActiveFlags, readViewMode } from '../../core/flags.js';
|
|
28
28
|
import { addContextHook, removeContextHook, hasContextHook } from './context.js';
|
|
29
29
|
import { writeConfig, readConfigIfPresent } from '../../core/feature-config.js';
|
|
30
30
|
import { resolveInitSeed, applyCliToggles, resolveResetGatedInputs } from './init-seed.js';
|
|
@@ -276,7 +276,7 @@ export const initCommand = new Command('init')
|
|
|
276
276
|
scope,
|
|
277
277
|
features: {
|
|
278
278
|
ambient: false, memory: false, hud: true, knowledge: false,
|
|
279
|
-
learning: false, rules: false, flags:
|
|
279
|
+
learning: false, rules: false, flags: {}, proxy: false,
|
|
280
280
|
compliance: existingHudManifest?.features.compliance ?? { enabled: false, frameworks: [] },
|
|
281
281
|
},
|
|
282
282
|
installedAt: now,
|
|
@@ -505,12 +505,11 @@ export const initCommand = new Command('init')
|
|
|
505
505
|
// CLI override applied below in both Recommended and Advanced paths.
|
|
506
506
|
let complianceEnabled = seed.features.compliance.enabled;
|
|
507
507
|
let complianceFrameworks = seed.features.compliance.frameworks;
|
|
508
|
-
let enabledFlags = seed.flags;
|
|
509
|
-
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
// so seed.viewMode collapses to 'default', and explicit=true makes that 'default' win at write time.
|
|
508
|
+
let enabledFlags = { ...seed.flags };
|
|
509
|
+
// viewModeExplicit: true when --reset is passed; signals resolveFinalViewMode to let the
|
|
510
|
+
// seed-time view-mode win over an externally-set value in settings.json.
|
|
511
|
+
// --reset empties the settings snapshot via resolveResetGatedInputs so seed.flags['view-mode']
|
|
512
|
+
// collapses to 'default', and explicit=true makes it take effect at settings write time.
|
|
514
513
|
let viewModeExplicit = !!options.reset;
|
|
515
514
|
let claudeignoreEnabled = !!earlyGitRoot;
|
|
516
515
|
let discoveredProjects = [];
|
|
@@ -574,7 +573,7 @@ export const initCommand = new Command('init')
|
|
|
574
573
|
proxyEnabled = effectiveFeatures.proxy;
|
|
575
574
|
complianceEnabled = effectiveFeatures.compliance.enabled;
|
|
576
575
|
complianceFrameworks = effectiveFeatures.compliance.frameworks;
|
|
577
|
-
// enabledFlags
|
|
576
|
+
// enabledFlags is already initialised to seed.flags above.
|
|
578
577
|
// Compute safe-delete block synchronously so we know whether to fetch installed version
|
|
579
578
|
if (profilePath && safeDeleteAvailable) {
|
|
580
579
|
const trashCmd = safeDeleteInfo.command;
|
|
@@ -598,7 +597,7 @@ export const initCommand = new Command('init')
|
|
|
598
597
|
safeDeleteAction = 'install';
|
|
599
598
|
}
|
|
600
599
|
// Print summary
|
|
601
|
-
const defaultFlagCount = enabledFlags
|
|
600
|
+
const defaultFlagCount = countActiveFlags(enabledFlags);
|
|
602
601
|
const complianceSummary = formatComplianceSummary(complianceEnabled, complianceFrameworks);
|
|
603
602
|
const summaryLines = [
|
|
604
603
|
`Ambient mode: ${ambientEnabled ? 'enabled' : 'disabled'}`,
|
|
@@ -609,8 +608,8 @@ export const initCommand = new Command('init')
|
|
|
609
608
|
`Knowledge bases: ${knowledgeEnabled ? 'enabled' : 'disabled'}`,
|
|
610
609
|
`Ext model routing: ${proxyEnabled ? 'enabled' : 'disabled'}`,
|
|
611
610
|
`Compliance: ${complianceSummary}`,
|
|
612
|
-
`View mode: ${
|
|
613
|
-
`Claude Code flags: ${defaultFlagCount}
|
|
611
|
+
`View mode: ${readViewMode(enabledFlags)}`,
|
|
612
|
+
`Claude Code flags: ${defaultFlagCount} configured`,
|
|
614
613
|
`${claudeignoreEnabled ? '.claudeignore: created' : ''}`,
|
|
615
614
|
`${safeDeleteAction !== 'skip' ? 'Safe delete: installed' : ''}`,
|
|
616
615
|
].filter(l => l.trim()).join('\n');
|
|
@@ -794,59 +793,15 @@ export const initCommand = new Command('init')
|
|
|
794
793
|
// No third case in practice: on this path the predicate only returns false for a
|
|
795
794
|
// CLI override (isTTY is guaranteed true by the non-TTY guard above). If it ever
|
|
796
795
|
// did, the seed values assigned at declaration stand — which is the right default.
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
})),
|
|
806
|
-
{ value: '_separator', label: color.dim('── Optional (skip if unsure) ──'), hint: '' },
|
|
807
|
-
...optional.map(f => ({
|
|
808
|
-
value: f.id,
|
|
809
|
-
label: f.label,
|
|
810
|
-
hint: f.hint,
|
|
811
|
-
})),
|
|
812
|
-
];
|
|
813
|
-
p.note('Recommended flags are pre-selected. Optional flags are for\n' +
|
|
814
|
-
'advanced users — if you don\'t recognize one, skip it.', 'Claude Code Flags');
|
|
815
|
-
const flagSelection = await p.multiselect({
|
|
816
|
-
message: 'Claude Code flags',
|
|
817
|
-
options: flagChoices,
|
|
818
|
-
// Pre-seeded from prior state; fresh installs start with all default-ON flags.
|
|
819
|
-
initialValues: seed.flags,
|
|
820
|
-
required: false,
|
|
821
|
-
});
|
|
822
|
-
if (p.isCancel(flagSelection)) {
|
|
823
|
-
p.cancel('Installation cancelled.');
|
|
824
|
-
process.exit(0);
|
|
825
|
-
}
|
|
826
|
-
enabledFlags = flagSelection.filter(id => id !== '_separator');
|
|
827
|
-
// View mode selector (advanced only)
|
|
828
|
-
p.note('Controls how much detail Claude Code shows in the transcript.\n' +
|
|
829
|
-
'• default — normal display with expandable tool output\n' +
|
|
830
|
-
'• verbose — shows everything including thinking blocks\n' +
|
|
831
|
-
'• focus — minimal: prompt, one-line tool summaries, final response', 'View Mode');
|
|
832
|
-
const viewModeChoice = await p.select({
|
|
833
|
-
message: 'View mode',
|
|
834
|
-
options: [
|
|
835
|
-
{ value: 'default', label: 'Default', hint: 'expandable tool output · recommended' },
|
|
836
|
-
{ value: 'verbose', label: 'Verbose', hint: 'shows everything including thinking' },
|
|
837
|
-
{ value: 'focus', label: 'Focus', hint: 'minimal output, one-line summaries' },
|
|
838
|
-
],
|
|
839
|
-
// Pre-seeded from prior state (fresh installs default to 'default').
|
|
840
|
-
initialValue: seed.viewMode,
|
|
841
|
-
});
|
|
842
|
-
if (p.isCancel(viewModeChoice)) {
|
|
843
|
-
p.cancel('Installation cancelled.');
|
|
844
|
-
process.exit(0);
|
|
796
|
+
/**
|
|
797
|
+
* D40: init applies seeded flag defaults non-interactively. Flags are customized
|
|
798
|
+
* exclusively via `devflow flags`; re-init preserves existing values and adopts
|
|
799
|
+
* registry defaults only for absent flags (ADR-014). No TUI is opened during init.
|
|
800
|
+
*/
|
|
801
|
+
{
|
|
802
|
+
const activeCount = countActiveFlags(enabledFlags);
|
|
803
|
+
p.log.info(`Flags: ${activeCount} active — customize any time with 'devflow flags'`);
|
|
845
804
|
}
|
|
846
|
-
viewMode = viewModeChoice;
|
|
847
|
-
// Mark as explicit: user actively selected this mode, so resolveFinalViewMode will
|
|
848
|
-
// let it win over an externally-set /focus value.
|
|
849
|
-
viewModeExplicit = true;
|
|
850
805
|
// .claudeignore prompt
|
|
851
806
|
if (earlyGitRoot) {
|
|
852
807
|
if (scope === 'user') {
|
|
@@ -1431,17 +1386,21 @@ export const initCommand = new Command('init')
|
|
|
1431
1386
|
content = removeDreamHook(content);
|
|
1432
1387
|
// Strip Devflow-managed teammateMode ("auto"). User-set values (e.g. "tmux") are preserved.
|
|
1433
1388
|
content = stripDevflowTeammateModeFromJson(content);
|
|
1434
|
-
// Claude Code flags —
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
//
|
|
1438
|
-
//
|
|
1439
|
-
//
|
|
1440
|
-
//
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1389
|
+
// Claude Code flags — convergeFlagsIntoSettings is the single pipeline entry point
|
|
1390
|
+
// (ARCH-H1, applies PF-015/PF-017/ADR-014): fold valued flags and view-mode from
|
|
1391
|
+
// existing settings before strip, then strip all managed keys and apply the folded
|
|
1392
|
+
// record. ownedRecord=existingManifest?.features.flags??null distinguishes keys
|
|
1393
|
+
// devflow previously wrote (must not be overridden by fold) from keys newly adopted
|
|
1394
|
+
// by resolveSeedFlags from registry defaults (may be overridden by fold to preserve
|
|
1395
|
+
// user-set hand values — e.g., a hand-set concurrency of '8' survives upgrade).
|
|
1396
|
+
{
|
|
1397
|
+
const { settings: flaggedContent, record: foldedFlags } = convergeFlagsIntoSettings(content, enabledFlags, {
|
|
1398
|
+
viewModeExplicit,
|
|
1399
|
+
ownedRecord: existingManifest?.features.flags ?? null,
|
|
1400
|
+
});
|
|
1401
|
+
content = flaggedContent;
|
|
1402
|
+
enabledFlags = foldedFlags;
|
|
1403
|
+
}
|
|
1445
1404
|
// Proxy hooks (SessionStart + UserPromptSubmit) — strip-then-add, idempotent.
|
|
1446
1405
|
// Parse Settings once for the hook mutation; env mutation stays in string space.
|
|
1447
1406
|
{
|
|
@@ -1755,11 +1714,9 @@ export const initCommand = new Command('init')
|
|
|
1755
1714
|
knowledge: knowledgeEnabled,
|
|
1756
1715
|
learning: learningEnabled,
|
|
1757
1716
|
rules: rulesEnabled,
|
|
1717
|
+
// FlagsRecord written directly — key-presence encodes "known" (ADR-014).
|
|
1718
|
+
// view-mode is encoded as flags['view-mode'] (the resolved final value).
|
|
1758
1719
|
flags: enabledFlags,
|
|
1759
|
-
// Snapshot of known flag ids at this install — used by resolveSeedFlags on next init
|
|
1760
|
-
// to detect new default-ON flags and auto-adopt them.
|
|
1761
|
-
knownFlags: FLAG_REGISTRY.map(f => f.id),
|
|
1762
|
-
viewMode,
|
|
1763
1720
|
security: securityMode,
|
|
1764
1721
|
// Final resolved value — may be forced off by preflight failure.
|
|
1765
1722
|
proxy: proxyEnabled,
|
|
@@ -98,22 +98,37 @@ function getDevflowVersion() {
|
|
|
98
98
|
}
|
|
99
99
|
// ─── Internal object helpers (used for single-pass atomic settings write) ────
|
|
100
100
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
101
|
+
* D-P4-1: Env var paired with ANTHROPIC_BASE_URL so Claude Code does not enforce
|
|
102
|
+
* its conservative context-window limit on relay-routed (non-Claude-model) sessions.
|
|
103
|
+
* Stripped together with the URL — always governed by URL ownership, never independently.
|
|
104
|
+
*/
|
|
105
|
+
const UNKNOWN_MODEL_WINDOW_ENV = 'CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT';
|
|
106
|
+
/**
|
|
107
|
+
* Mutate a parsed Settings object in place: set ANTHROPIC_BASE_URL to our relay
|
|
108
|
+
* and set UNKNOWN_MODEL_WINDOW_ENV to '1'.
|
|
109
|
+
*
|
|
110
|
+
* D-P4-1: Each condition is evaluated independently (PF-015 — no short-circuit that
|
|
111
|
+
* skips the second write when the first reports no change).
|
|
112
|
+
*
|
|
113
|
+
* Returns true when the object was changed by either assignment.
|
|
103
114
|
*/
|
|
104
115
|
function _applyProxyEnvToObject(settings, port) {
|
|
105
116
|
const s = settings;
|
|
106
117
|
s.env = s.env ?? {};
|
|
107
118
|
const env = s.env;
|
|
108
119
|
const newUrl = proxyBaseUrl(port);
|
|
109
|
-
|
|
110
|
-
|
|
120
|
+
// D-P4-1: evaluate each condition independently before combining (avoids PF-015 short-circuit)
|
|
121
|
+
const urlChanged = env.ANTHROPIC_BASE_URL !== newUrl;
|
|
122
|
+
const windowVarChanged = env[UNKNOWN_MODEL_WINDOW_ENV] !== '1';
|
|
111
123
|
env.ANTHROPIC_BASE_URL = newUrl;
|
|
112
|
-
|
|
124
|
+
env[UNKNOWN_MODEL_WINDOW_ENV] = '1';
|
|
125
|
+
return urlChanged || windowVarChanged;
|
|
113
126
|
}
|
|
114
127
|
/**
|
|
115
|
-
* Mutate a parsed Settings object in place: remove
|
|
116
|
-
* its
|
|
128
|
+
* Mutate a parsed Settings object in place: remove UNKNOWN_MODEL_WINDOW_ENV
|
|
129
|
+
* unconditionally (Devflow is its only producer — there is no foreign value to protect),
|
|
130
|
+
* and remove ANTHROPIC_BASE_URL only when it exactly matches our relay on the given
|
|
131
|
+
* managed port.
|
|
117
132
|
*
|
|
118
133
|
* Scoped to `managedPort` so a user's own localhost gateway (LiteLLM,
|
|
119
134
|
* local Ollama proxy, etc.) on ANY other port is never clobbered.
|
|
@@ -123,19 +138,31 @@ function _applyProxyEnvToObject(settings, port) {
|
|
|
123
138
|
* - enable path → the new port being applied (followed immediately by _applyProxyEnvToObject)
|
|
124
139
|
* - uninstall → proxy.json.port (or DEFAULT_PROXY_PORT)
|
|
125
140
|
*
|
|
126
|
-
*
|
|
141
|
+
* D-P4-1: URL ownership gates the URL delete only; the window var is always ours to
|
|
142
|
+
* remove (applies PF-015, ADR-003). Each outcome is evaluated into a local and OR-ed
|
|
143
|
+
* afterwards — never short-circuit composed inline (PF-015).
|
|
144
|
+
*
|
|
145
|
+
* Returns true when the object was changed by either deletion.
|
|
127
146
|
*/
|
|
128
147
|
function _stripProxyEnvFromObject(settings, managedPort) {
|
|
129
148
|
const s = settings;
|
|
130
149
|
const env = s.env;
|
|
131
|
-
if (
|
|
132
|
-
return false;
|
|
133
|
-
if (env.ANTHROPIC_BASE_URL !== proxyBaseUrl(managedPort))
|
|
150
|
+
if (!env)
|
|
134
151
|
return false;
|
|
135
|
-
|
|
152
|
+
// Devflow is the only producer of this var — always remove it, regardless of whether
|
|
153
|
+
// the URL is still ours. Port-scoping protects a FOREIGN url value; there is no
|
|
154
|
+
// foreign value of this key to protect. (applies PF-015, ADR-003)
|
|
155
|
+
const hadWindowVar = env[UNKNOWN_MODEL_WINDOW_ENV] !== undefined;
|
|
156
|
+
delete env[UNKNOWN_MODEL_WINDOW_ENV];
|
|
157
|
+
let removedUrl = false;
|
|
158
|
+
if (typeof env.ANTHROPIC_BASE_URL === 'string' &&
|
|
159
|
+
env.ANTHROPIC_BASE_URL === proxyBaseUrl(managedPort)) {
|
|
160
|
+
delete env.ANTHROPIC_BASE_URL;
|
|
161
|
+
removedUrl = true;
|
|
162
|
+
}
|
|
136
163
|
if (Object.keys(env).length === 0)
|
|
137
164
|
delete s.env;
|
|
138
|
-
return
|
|
165
|
+
return removedUrl || hadWindowVar; // OR the locals — never compose with || inline (PF-015)
|
|
139
166
|
}
|
|
140
167
|
/** Internal: add ensure-proxy hook to one event. Returns true when added. */
|
|
141
168
|
function _ensureProxyHook(settings, eventName, hookCmd) {
|
|
@@ -1327,6 +1354,8 @@ async function runEnable(portOption) {
|
|
|
1327
1354
|
s.message('Warming model cache...');
|
|
1328
1355
|
await discoverExternalModels(cacheDir, logPath).catch(() => { });
|
|
1329
1356
|
s.stop(color.green('External model routing enabled'));
|
|
1357
|
+
// D-P4-1 / PF-022: applies-on-restart — env var takes effect only for new sessions
|
|
1358
|
+
p.log.info(color.dim('Context-window enforcement disabled for relay-routed models — applies to new Claude Code sessions'));
|
|
1330
1359
|
if (adopted) {
|
|
1331
1360
|
p.log.info(`Relay already running on port ${port} — adopted`);
|
|
1332
1361
|
}
|
|
@@ -23,7 +23,7 @@ import { detectShell, getProfilePath } from '../../core/safe-delete.js';
|
|
|
23
23
|
import { isAlreadyInstalled, removeFromProfile } from '../../core/safe-delete-install.js';
|
|
24
24
|
import { removeManagedSettings, stripUserDenyList, detectDenyState, DEVFLOW_HISTORICAL_DENY } from '../../targets/claude-code/post-install.js';
|
|
25
25
|
import { writeFileAtomicExclusive } from '../../core/fs-atomic.js';
|
|
26
|
-
import { stripFlags
|
|
26
|
+
import { stripFlags } from '../../core/flags.js';
|
|
27
27
|
import { stripDevflowTeammateModeFromJson } from '../../core/teammate-mode-cleanup.js';
|
|
28
28
|
import { getPackageRoot, isContainedIn } from '../../core/paths.js';
|
|
29
29
|
/**
|
|
@@ -765,8 +765,7 @@ export async function runCleanupPhase(opts) {
|
|
|
765
765
|
settingsContent = removeDreamHook(settingsContent);
|
|
766
766
|
settingsContent = removeHudStatusLine(settingsContent);
|
|
767
767
|
settingsContent = removeContextHook(settingsContent);
|
|
768
|
-
settingsContent = stripFlags(settingsContent);
|
|
769
|
-
settingsContent = stripViewMode(settingsContent);
|
|
768
|
+
settingsContent = stripFlags(settingsContent); // also strips viewMode via view-mode registry entry
|
|
770
769
|
settingsContent = stripDevflowTeammateModeFromJson(settingsContent);
|
|
771
770
|
// Remove proxy hooks and ANTHROPIC_BASE_URL env in a single parse-mutate-serialize pass.
|
|
772
771
|
// REG-1: scope the URL strip to the port Devflow manages — use the pre-captured port
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* flags-view public barrel.
|
|
3
|
+
*
|
|
4
|
+
* Commands should import from this barrel to avoid coupling to internals.
|
|
5
|
+
*/
|
|
6
|
+
export { runFlagsTui } from './terminal.js';
|
|
7
|
+
export { buildFlagRows, collectFlagRecord } from './state.js';
|
|
8
|
+
export { computeViewportHeight, FIXED_ROWS } from './render.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|