@ryuenn3123/agentic-senior-core 4.1.0 → 4.2.1

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.
Files changed (50) hide show
  1. package/.agent-context/prompts/compact-natural-mode.md +100 -0
  2. package/.agent-context/prompts/init-project.md +1 -0
  3. package/.agent-context/prompts/refactor.md +1 -0
  4. package/.agent-context/review-checklists/pr-checklist.md +1 -0
  5. package/.agent-context/rules/architecture.md +10 -0
  6. package/.agent-context/rules/naming-conv.md +6 -3
  7. package/.agent-context/state/README.md +2 -1
  8. package/AGENTS.md +6 -8
  9. package/README.md +95 -117
  10. package/benchmarks/README.md +60 -0
  11. package/benchmarks/compact-natural-mode/fixtures.mjs +359 -0
  12. package/benchmarks/compact-natural-mode/scorer.mjs +331 -0
  13. package/benchmarks/runtime-token-saver/fixtures.mjs +714 -0
  14. package/bin/agentic-senior-core.js +6 -0
  15. package/bin/ascx.js +23 -0
  16. package/lib/cli/adaptive-context/catalog.mjs +428 -0
  17. package/lib/cli/adaptive-context/file-signals.mjs +100 -0
  18. package/lib/cli/adaptive-context/implications.mjs +44 -0
  19. package/lib/cli/adaptive-context.mjs +365 -0
  20. package/lib/cli/ascx/adapters/git-diff.mjs +223 -0
  21. package/lib/cli/ascx/adapters/git-status.mjs +145 -0
  22. package/lib/cli/ascx/adapters/npm-run-build.mjs +99 -0
  23. package/lib/cli/ascx/adapters/npm-test.mjs +120 -0
  24. package/lib/cli/ascx/adapters/rg.mjs +39 -0
  25. package/lib/cli/ascx/fixture-evaluator.mjs +180 -0
  26. package/lib/cli/ascx/formatter.mjs +47 -0
  27. package/lib/cli/ascx/lexer.mjs +129 -0
  28. package/lib/cli/ascx/runtime.mjs +192 -0
  29. package/lib/cli/ascx/tee-writer.mjs +63 -0
  30. package/lib/cli/ascx/token-estimate.mjs +15 -0
  31. package/lib/cli/backup.mjs +37 -4
  32. package/lib/cli/commands/context.mjs +140 -0
  33. package/lib/cli/commands/init.mjs +14 -2
  34. package/lib/cli/commands/optimize.mjs +143 -2
  35. package/lib/cli/commands/upgrade/design-intent-seed.mjs +46 -0
  36. package/lib/cli/commands/upgrade/token-optimization-state.mjs +51 -0
  37. package/lib/cli/commands/upgrade.mjs +34 -45
  38. package/lib/cli/compiler.mjs +9 -0
  39. package/lib/cli/project-scaffolder/prompt-builders.mjs +1 -0
  40. package/lib/cli/token-optimization.mjs +161 -6
  41. package/lib/cli/utils.mjs +15 -1
  42. package/package.json +10 -3
  43. package/scripts/adaptive-context/fixtures.mjs +188 -0
  44. package/scripts/adaptive-context-benchmark.mjs +9 -0
  45. package/scripts/ascx-runtime-token-saver-benchmark.mjs +9 -0
  46. package/scripts/build-release-benchmark-bundle.mjs +1 -3
  47. package/scripts/clean-local-artifacts.mjs +2 -0
  48. package/scripts/compact-natural-mode-benchmark.mjs +9 -0
  49. package/scripts/validate/config.mjs +6 -0
  50. package/scripts/validate.mjs +2 -0
@@ -7,6 +7,11 @@ import {
7
7
  TOKEN_OPTIMIZATION_REPORT_FILE_NAME,
8
8
  normalizeAgentName,
9
9
  detectRtkBinary,
10
+ detectAscxRuntime,
11
+ checkAscxTeeReadiness,
12
+ resolveRuntimeTokenSaverMode,
13
+ buildRuntimeTokenSaverWarnings,
14
+ buildRuntimeTokenSaverNextAction,
10
15
  buildRtkInstallHint,
11
16
  buildRtkHookCommand,
12
17
  createTokenOptimizationState,
@@ -19,12 +24,43 @@ export function parseOptimizeArguments(commandArguments) {
19
24
  targetDirectory: '.',
20
25
  agent: 'copilot',
21
26
  enabled: true,
27
+ mode: 'configure',
22
28
  show: false,
23
29
  };
24
30
 
31
+ function setOptimizeMode(nextMode) {
32
+ if (parsedOptimizeOptions.mode !== 'configure' && parsedOptimizeOptions.mode !== nextMode) {
33
+ throw new Error(`Conflicting optimize modes: ${parsedOptimizeOptions.mode} and ${nextMode}`);
34
+ }
35
+
36
+ parsedOptimizeOptions.mode = nextMode;
37
+ }
38
+
25
39
  for (let argumentIndex = 0; argumentIndex < commandArguments.length; argumentIndex++) {
26
40
  const currentArgument = commandArguments[argumentIndex];
27
41
 
42
+ if (currentArgument === 'install') {
43
+ setOptimizeMode('install');
44
+ parsedOptimizeOptions.enabled = true;
45
+ continue;
46
+ }
47
+
48
+ if (currentArgument === 'off') {
49
+ setOptimizeMode('off');
50
+ parsedOptimizeOptions.enabled = false;
51
+ continue;
52
+ }
53
+
54
+ if (currentArgument === 'status') {
55
+ setOptimizeMode('status');
56
+ continue;
57
+ }
58
+
59
+ if (currentArgument === 'doctor') {
60
+ setOptimizeMode('doctor');
61
+ continue;
62
+ }
63
+
28
64
  if (!currentArgument.startsWith('--')) {
29
65
  parsedOptimizeOptions.targetDirectory = currentArgument;
30
66
  continue;
@@ -43,19 +79,36 @@ export function parseOptimizeArguments(commandArguments) {
43
79
 
44
80
  if (currentArgument === '--enable') {
45
81
  parsedOptimizeOptions.enabled = true;
82
+ if (parsedOptimizeOptions.mode === 'off') {
83
+ setOptimizeMode('install');
84
+ }
46
85
  continue;
47
86
  }
48
87
 
49
88
  if (currentArgument === '--disable') {
50
89
  parsedOptimizeOptions.enabled = false;
90
+ if (parsedOptimizeOptions.mode === 'install') {
91
+ setOptimizeMode('off');
92
+ }
51
93
  continue;
52
94
  }
53
95
 
54
96
  if (currentArgument === '--show') {
97
+ setOptimizeMode('show');
55
98
  parsedOptimizeOptions.show = true;
56
99
  continue;
57
100
  }
58
101
 
102
+ if (currentArgument === '--status') {
103
+ setOptimizeMode('status');
104
+ continue;
105
+ }
106
+
107
+ if (currentArgument === '--doctor') {
108
+ setOptimizeMode('doctor');
109
+ continue;
110
+ }
111
+
59
112
  throw new Error(`Unknown option: ${currentArgument}`);
60
113
  }
61
114
 
@@ -63,6 +116,78 @@ export function parseOptimizeArguments(commandArguments) {
63
116
  return parsedOptimizeOptions;
64
117
  }
65
118
 
119
+ function formatStatusLine(label, value) {
120
+ return `${label}: ${value}`;
121
+ }
122
+
123
+ async function buildRuntimeTokenSaverStatus(resolvedTargetDirectoryPath, options = {}) {
124
+ const onboardingReport = await loadOnboardingReportIfExists(resolvedTargetDirectoryPath);
125
+ const existingOptimizationState = await readTokenOptimizationState(resolvedTargetDirectoryPath);
126
+ const ascxDetection = detectAscxRuntime();
127
+ const rtkDetection = detectRtkBinary();
128
+ const teeReadiness = await checkAscxTeeReadiness(resolvedTargetDirectoryPath, {
129
+ writeProbe: options.writeProbe === true,
130
+ });
131
+ const mode = resolveRuntimeTokenSaverMode({
132
+ tokenOptimizationState: existingOptimizationState,
133
+ ascxDetection,
134
+ rtkDetection,
135
+ });
136
+ const warnings = buildRuntimeTokenSaverWarnings({
137
+ mode,
138
+ onboardingReport,
139
+ ascxDetection,
140
+ teeReadiness,
141
+ rtkDetection,
142
+ });
143
+
144
+ return {
145
+ targetDirectory: resolvedTargetDirectoryPath,
146
+ initialized: Boolean(onboardingReport),
147
+ mode,
148
+ ascx: ascxDetection,
149
+ tee: teeReadiness,
150
+ rtk: rtkDetection,
151
+ nineRouter: {
152
+ status: 'not-checked',
153
+ reason: 'localhost probing is intentionally deferred',
154
+ },
155
+ warnings,
156
+ nextAction: buildRuntimeTokenSaverNextAction({ mode, warnings }),
157
+ tokenOptimizationState: existingOptimizationState,
158
+ };
159
+ }
160
+
161
+ function printRuntimeTokenSaverStatus(statusReport, options = {}) {
162
+ const title = options.title || 'Runtime token saver status';
163
+ const ascxStatus = statusReport.ascx.isAvailable
164
+ ? `found (${statusReport.ascx.source})`
165
+ : 'missing';
166
+ const rtkStatus = statusReport.rtk.isAvailable
167
+ ? `detected${statusReport.rtk.version ? ` (${statusReport.rtk.version})` : ''}`
168
+ : 'not-detected';
169
+
170
+ console.log(title);
171
+ console.log(formatStatusLine('target', statusReport.targetDirectory));
172
+ console.log(formatStatusLine('initialized', statusReport.initialized ? 'yes' : 'no'));
173
+ console.log(formatStatusLine('mode', statusReport.mode));
174
+ console.log(formatStatusLine('ascx', ascxStatus));
175
+ console.log(formatStatusLine('tee', `${statusReport.tee.status} (${statusReport.tee.path})`));
176
+ console.log(formatStatusLine('rtk', rtkStatus));
177
+ console.log(formatStatusLine('9router', statusReport.nineRouter.status));
178
+
179
+ if (statusReport.warnings.length > 0) {
180
+ console.log('warnings:');
181
+ for (const warning of statusReport.warnings) {
182
+ console.log(`- ${warning}`);
183
+ }
184
+ } else {
185
+ console.log('warnings: none');
186
+ }
187
+
188
+ console.log(formatStatusLine('next_action', statusReport.nextAction));
189
+ }
190
+
66
191
  export async function runOptimizeCommand(targetDirectoryArgument, optimizeOptions = {}) {
67
192
  const optimizationStartedAt = Date.now();
68
193
  const resolvedTargetDirectoryPath = path.resolve(targetDirectoryArgument || '.');
@@ -72,7 +197,23 @@ export async function runOptimizeCommand(targetDirectoryArgument, optimizeOption
72
197
  const selectedAgentName = normalizeAgentName(optimizeOptions.agent || 'copilot');
73
198
  const rtkDetection = detectRtkBinary();
74
199
 
75
- if (optimizeOptions.show) {
200
+ if (optimizeOptions.mode === 'status') {
201
+ const statusReport = await buildRuntimeTokenSaverStatus(resolvedTargetDirectoryPath);
202
+ printRuntimeTokenSaverStatus(statusReport);
203
+ return;
204
+ }
205
+
206
+ if (optimizeOptions.mode === 'doctor') {
207
+ const statusReport = await buildRuntimeTokenSaverStatus(resolvedTargetDirectoryPath, {
208
+ writeProbe: true,
209
+ });
210
+ printRuntimeTokenSaverStatus(statusReport, {
211
+ title: 'ASCX runtime token saver doctor',
212
+ });
213
+ return;
214
+ }
215
+
216
+ if (optimizeOptions.show || optimizeOptions.mode === 'show') {
76
217
  const existingOptimizationState = await readTokenOptimizationState(resolvedTargetDirectoryPath);
77
218
  console.log(
78
219
  JSON.stringify(
@@ -97,7 +238,7 @@ export async function runOptimizeCommand(targetDirectoryArgument, optimizeOption
97
238
  }
98
239
 
99
240
  const tokenOptimizationState = createTokenOptimizationState({
100
- isEnabled: optimizeOptions.enabled,
241
+ isEnabled: optimizeOptions.mode === 'off' ? false : optimizeOptions.enabled,
101
242
  selectedAgentName,
102
243
  rtkDetection,
103
244
  });
@@ -0,0 +1,46 @@
1
+ import path from 'node:path';
2
+
3
+ import { buildDesignIntentSeedFromSignals } from '../../project-scaffolder.mjs';
4
+
5
+ export function buildUpgradeDesignIntentSeed({
6
+ targetDirectoryPath,
7
+ packageManifest,
8
+ selectedStackFileName,
9
+ selectedBlueprintFileName,
10
+ uiScopeSignals,
11
+ }) {
12
+ const projectName = String(packageManifest?.name || path.basename(targetDirectoryPath)).trim()
13
+ || 'existing-ui-project';
14
+ const isMobileUiProject = String(selectedStackFileName || '').toLowerCase().includes('react-native')
15
+ || String(selectedStackFileName || '').toLowerCase().includes('flutter')
16
+ || uiScopeSignals.signalReasons.some((signalReason) => {
17
+ return signalReason.includes('android') || signalReason.includes('ios');
18
+ });
19
+ const resolvedDomain = isMobileUiProject ? 'Mobile app' : 'Web application';
20
+ const projectDescription = String(packageManifest?.description || '').trim()
21
+ || `Existing ${resolvedDomain.toLowerCase()} detected during upgrade. Create a project-specific dynamic design contract before shipping new UI work.`;
22
+
23
+ return buildDesignIntentSeedFromSignals({
24
+ projectName,
25
+ projectDescription,
26
+ primaryDomain: resolvedDomain,
27
+ features: [],
28
+ initContext: {
29
+ stackFileName: selectedStackFileName,
30
+ blueprintFileName: selectedBlueprintFileName,
31
+ },
32
+ status: 'seed-generated-during-upgrade',
33
+ supplementalFields: {
34
+ upgradeSignals: {
35
+ detectedFrom: uiScopeSignals.signalReasons,
36
+ generatedBy: 'upgrade-seed',
37
+ },
38
+ repoEvidence: {
39
+ uiSignalReasons: uiScopeSignals.signalReasons,
40
+ frontendMetrics: uiScopeSignals.frontendEvidenceMetrics || null,
41
+ designEvidenceSummary: uiScopeSignals.designEvidenceSummary || null,
42
+ workspaceUiEntries: uiScopeSignals.workspaceUiEntries || [],
43
+ },
44
+ },
45
+ });
46
+ }
@@ -0,0 +1,51 @@
1
+ import {
2
+ createTokenOptimizationState,
3
+ detectRtkBinary,
4
+ normalizeAgentName,
5
+ readTokenOptimizationState,
6
+ writeTokenOptimizationState,
7
+ } from '../../token-optimization.mjs';
8
+
9
+ export async function resolveUpgradeTokenOptimizationPlan(targetDirectoryPath, existingOnboardingReport) {
10
+ const existingState = await readTokenOptimizationState(targetDirectoryPath);
11
+ const optedOut = existingOnboardingReport?.tokenOptimization?.enabled === false;
12
+ const selectedAgentName = normalizeAgentName(
13
+ existingOnboardingReport?.tokenOptimization?.selectedAgent || 'copilot'
14
+ );
15
+
16
+ return {
17
+ existingState,
18
+ optedOut,
19
+ selectedAgentName,
20
+ shouldSeed: !existingState && !optedOut,
21
+ previewLabel: optedOut
22
+ ? 'disabled (preserved opt-out)'
23
+ : existingState
24
+ ? 'enabled'
25
+ : 'enabled (will seed missing ASCX state)',
26
+ reportState: existingState
27
+ || (optedOut ? existingOnboardingReport?.tokenOptimization || { enabled: false } : null),
28
+ };
29
+ }
30
+
31
+ export async function applyUpgradeTokenOptimizationPlan(targetDirectoryPath, plan) {
32
+ if (!plan.shouldSeed) {
33
+ return {
34
+ createdFileName: null,
35
+ reportState: plan.reportState,
36
+ };
37
+ }
38
+
39
+ const tokenOptimizationState = createTokenOptimizationState({
40
+ isEnabled: true,
41
+ selectedAgentName: plan.selectedAgentName,
42
+ rtkDetection: detectRtkBinary(),
43
+ });
44
+
45
+ await writeTokenOptimizationState(targetDirectoryPath, tokenOptimizationState);
46
+
47
+ return {
48
+ createdFileName: '.agent-context/state/token-optimization.json',
49
+ reportState: tokenOptimizationState,
50
+ };
51
+ }
@@ -39,15 +39,21 @@ import {
39
39
  } from '../compiler.mjs';
40
40
 
41
41
  import { runPreflightChecks } from '../preflight.mjs';
42
- import { createBackup, ensureBackupGitignoreEntry } from '../backup.mjs';
43
- import { performRollback } from '../rollback.mjs';
44
42
  import {
45
- detectProjectDocTemplateStaleness,
46
- buildDesignIntentSeedFromSignals,
47
- } from '../project-scaffolder.mjs';
43
+ createBackup,
44
+ ensureBackupGitignoreEntry,
45
+ ensureRuntimeArtifactGitignoreEntries,
46
+ } from '../backup.mjs';
47
+ import { performRollback } from '../rollback.mjs';
48
+ import { detectProjectDocTemplateStaleness } from '../project-scaffolder.mjs';
48
49
  import { migrateExistingDesignIntentToResearchDossierSchema } from '../project-scaffolder/design-contract/research-dossier-migration.mjs';
49
50
  import { ensureActiveMemorySnapshot } from '../memory-continuity.mjs';
50
51
  import { buildExistingProjectMajorConstraints } from '../init-detection-flow.mjs';
52
+ import { buildUpgradeDesignIntentSeed } from './upgrade/design-intent-seed.mjs';
53
+ import {
54
+ applyUpgradeTokenOptimizationPlan,
55
+ resolveUpgradeTokenOptimizationPlan,
56
+ } from './upgrade/token-optimization-state.mjs';
51
57
 
52
58
  export function parseUpgradeArguments(commandArguments) {
53
59
  const parsedUpgradeOptions = {
@@ -102,46 +108,6 @@ export function parseUpgradeArguments(commandArguments) {
102
108
  return parsedUpgradeOptions;
103
109
  }
104
110
 
105
- function buildUpgradeDesignIntentSeed({
106
- targetDirectoryPath,
107
- packageManifest,
108
- selectedStackFileName,
109
- selectedBlueprintFileName,
110
- uiScopeSignals,
111
- }) {
112
- const projectName = String(packageManifest?.name || path.basename(targetDirectoryPath)).trim() || 'existing-ui-project';
113
- const isMobileUiProject = String(selectedStackFileName || '').toLowerCase().includes('react-native')
114
- || String(selectedStackFileName || '').toLowerCase().includes('flutter')
115
- || uiScopeSignals.signalReasons.some((signalReason) => signalReason.includes('android') || signalReason.includes('ios'));
116
- const resolvedDomain = isMobileUiProject ? 'Mobile app' : 'Web application';
117
- const projectDescription = String(packageManifest?.description || '').trim()
118
- || `Existing ${resolvedDomain.toLowerCase()} detected during upgrade. Create a project-specific dynamic design contract before shipping new UI work.`;
119
-
120
- return buildDesignIntentSeedFromSignals({
121
- projectName,
122
- projectDescription,
123
- primaryDomain: resolvedDomain,
124
- features: [],
125
- initContext: {
126
- stackFileName: selectedStackFileName,
127
- blueprintFileName: selectedBlueprintFileName,
128
- },
129
- status: 'seed-generated-during-upgrade',
130
- supplementalFields: {
131
- upgradeSignals: {
132
- detectedFrom: uiScopeSignals.signalReasons,
133
- generatedBy: 'upgrade-seed',
134
- },
135
- repoEvidence: {
136
- uiSignalReasons: uiScopeSignals.signalReasons,
137
- frontendMetrics: uiScopeSignals.frontendEvidenceMetrics || null,
138
- designEvidenceSummary: uiScopeSignals.designEvidenceSummary || null,
139
- workspaceUiEntries: uiScopeSignals.workspaceUiEntries || [],
140
- },
141
- },
142
- });
143
- }
144
-
145
111
  export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions = {}) {
146
112
  const resolvedTargetDirectoryPath = path.resolve(targetDirectoryArgument || '.');
147
113
 
@@ -175,6 +141,10 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
175
141
  const stackFileNames = await collectFileNames(path.join(AGENT_CONTEXT_DIR, 'stacks'));
176
142
  const blueprintFileNames = await collectFileNames(path.join(AGENT_CONTEXT_DIR, 'blueprints'));
177
143
  const existingOnboardingReport = await loadOnboardingReportIfExists(resolvedTargetDirectoryPath);
144
+ const tokenOptimizationPlan = await resolveUpgradeTokenOptimizationPlan(
145
+ resolvedTargetDirectoryPath,
146
+ existingOnboardingReport
147
+ );
178
148
  const projectDetection = await detectProjectContext(resolvedTargetDirectoryPath);
179
149
  const selectedProfileName = PROFILE_PRESETS[existingOnboardingReport?.selectedProfile]
180
150
  ? existingOnboardingReport.selectedProfile
@@ -302,6 +272,9 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
302
272
  }
303
273
  console.log(`- CI/CD quality checks (guardrails): ${includeCiGuardrails ? 'enabled' : 'disabled'}`);
304
274
  console.log('- Instruction surface: AGENTS.md canonical with CLAUDE.md and GEMINI.md import bridges');
275
+ console.log('- Default activation cues: Adaptive Context bootstrap, ASCX wrappers, Compact Natural replies');
276
+ console.log(`- Token optimization policy: ${tokenOptimizationPlan.previewLabel}`);
277
+ console.log('- Default response mode: Compact Natural Mode enabled');
305
278
  console.log(`- Managed surface stale files: ${managedSurfacePlan.staleFiles.length}`);
306
279
  console.log(`- Managed surface stale directories: ${managedSurfacePlan.staleDirectories.length}`);
307
280
  console.log(`- Managed surface sync mode: 1:1 (prune enabled)`);
@@ -373,6 +346,12 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
373
346
  if (backupGitignoreResult.status !== 'unchanged') {
374
347
  console.log(`Local backup artifacts ignored in .gitignore (${backupGitignoreResult.entry}).`);
375
348
  }
349
+ const runtimeArtifactGitignoreResult = await ensureRuntimeArtifactGitignoreEntries(resolvedTargetDirectoryPath);
350
+ if (runtimeArtifactGitignoreResult.status !== 'unchanged') {
351
+ console.log(
352
+ `Local runtime artifacts ignored in .gitignore (${runtimeArtifactGitignoreResult.addedEntries.join(', ')}).`
353
+ );
354
+ }
376
355
 
377
356
  try {
378
357
  const governanceSyncResult = await copyGovernanceAssetsToTarget(resolvedTargetDirectoryPath, {
@@ -382,6 +361,13 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
382
361
  });
383
362
  const supplementalCreatedFileNames = [];
384
363
  const shouldEnsureActiveMemorySnapshot = existingOnboardingReport?.memoryContinuity?.enabled !== false;
364
+ const tokenOptimizationApplyResult = await applyUpgradeTokenOptimizationPlan(
365
+ resolvedTargetDirectoryPath,
366
+ tokenOptimizationPlan
367
+ );
368
+ if (tokenOptimizationApplyResult.createdFileName) {
369
+ supplementalCreatedFileNames.push(tokenOptimizationApplyResult.createdFileName);
370
+ }
385
371
 
386
372
  if (shouldSeedDesignIntentOnApply && designIntentSeedContent) {
387
373
  const docsDirectoryPath = path.join(resolvedTargetDirectoryPath, 'docs');
@@ -427,6 +413,7 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
427
413
  projectDetection,
428
414
  runtimeEnvironment: existingOnboardingReport?.runtimeEnvironment || null,
429
415
  operationMode: 'upgrade',
416
+ tokenOptimization: tokenOptimizationApplyResult.reportState,
430
417
  detectionTransparency,
431
418
  uiScopeSignals,
432
419
  });
@@ -464,6 +451,8 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
464
451
  }
465
452
 
466
453
  console.log('\nRefreshed files: AGENTS.md, CLAUDE.md, GEMINI.md, .agent-context/, and .agent-context/state/onboarding-report.json');
454
+ console.log('Default activation cues remain Adaptive Context bootstrap, ASCX command wrappers, and Compact Natural final replies.');
455
+ console.log('Default response mode remains Compact Natural Mode through .agent-context/prompts/compact-natural-mode.md.');
467
456
  console.log('\nNext-step suggestion (UI scope): run `npx @ryuenn3123/agentic-senior-core audit:design-anti-repeat` to scan CSS, SCSS, SASS, LESS, Tailwind config, and design-token files in this project for typography or palette values that match the anti-repeat ledger in docs/design-intent.json. Add it to your CI alongside `npm test` once the design dossier is populated.');
468
457
  } catch (error) {
469
458
  console.error('\n[FATAL] An error occurred during upgrade. Attempting automatic rollback...');
@@ -202,6 +202,14 @@ export async function writeOnboardingReport({
202
202
  containerizationStrategy: buildContainerizationStrategySnapshot(dockerStrategy),
203
203
  tokenOptimization: resolvedTokenOptimization,
204
204
  memoryContinuity: resolvedMemoryContinuity,
205
+ responseCompression: {
206
+ enabled: true,
207
+ mode: 'compact-natural-mode',
208
+ defaultOn: true,
209
+ promptFile: '.agent-context/prompts/compact-natural-mode.md',
210
+ appliesTo: 'agent-final-responses',
211
+ commandOutputBoundary: 'ASCX handles command-output compression separately',
212
+ },
205
213
  autoDetection: {
206
214
  detectedStack: projectDetection.detectedStackFileName,
207
215
  detectedAdditionalStacks: projectDetection.secondaryStackFileNames || [],
@@ -383,6 +391,7 @@ export async function buildCompiledRulesContent({
383
391
  [
384
392
  '## LAYER 5: EXECUTION PROMPTS AND UI TRIGGERS',
385
393
  'Load these prompt contracts only when their trigger matches the user request:',
394
+ 'Default. .agent-context/prompts/compact-natural-mode.md -> final response shape and evidence-preserving compact prose',
386
395
  '0. Documentation-first mode -> docs, documentation, dokumen, docs/*, architecture docs, flow docs, API docs, lengkapkan docs',
387
396
  '1. .agent-context/prompts/init-project.md -> create, build, new project, scaffold',
388
397
  '2. .agent-context/prompts/refactor.md -> refactor, improve, clean up, fix',
@@ -31,6 +31,7 @@ function buildDockerStrategyExecutionBlock(dockerStrategy) {
31
31
  '- Required asset floor: ' + requiredAssetFloor + '.',
32
32
  '- Keep development and production lanes separate when both are selected.',
33
33
  '- If the user asks to create files without commands, write the files and documented commands, but do not execute Docker build, Compose, or registry commands.',
34
+ '- If Docker is enabled for development, local development setup and first-build instructions in the README must use Docker (e.g., `docker compose up`) rather than fallback local commands like `npm run dev`.',
34
35
  ];
35
36
  }
36
37
 
@@ -1,8 +1,10 @@
1
+ import { existsSync } from 'node:fs';
1
2
  import fs from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import { spawnSync } from 'node:child_process';
4
5
  import { platform } from 'node:process';
5
6
 
7
+ import { REPOSITORY_ROOT } from './constants.mjs';
6
8
  import { pathExists } from './utils.mjs';
7
9
 
8
10
  export const TOKEN_OPTIMIZATION_STATE_FILE_NAME = 'token-optimization.json';
@@ -44,13 +46,13 @@ const COMMAND_REWRITE_MAPPINGS = [
44
46
  },
45
47
  {
46
48
  rawCommand: 'git status',
47
- optimizedCommand: 'rtk git status',
48
- reason: 'Status output is condensed to actionable file changes.',
49
+ optimizedCommand: 'ascx git status',
50
+ reason: 'ASCX condenses status output while preserving changed-file evidence and raw tee safety.',
49
51
  },
50
52
  {
51
53
  rawCommand: 'git diff',
52
- optimizedCommand: 'rtk git diff',
53
- reason: 'Diff noise is trimmed while preserving code-review context.',
54
+ optimizedCommand: 'ascx git diff',
55
+ reason: 'ASCX preserves changed files, hunk anchors, binary/deleted markers, truncation notes, and raw tee safety.',
54
56
  },
55
57
  {
56
58
  rawCommand: 'git log -n 10',
@@ -59,8 +61,8 @@ const COMMAND_REWRITE_MAPPINGS = [
59
61
  },
60
62
  {
61
63
  rawCommand: 'npm test',
62
- optimizedCommand: 'rtk test npm test',
63
- reason: 'Test results emphasize failures and suppress repetitive pass logs.',
64
+ optimizedCommand: 'ascx npm test',
65
+ reason: 'ASCX preserves exit code, failing tests, assertions, file paths, and raw tee safety.',
64
66
  },
65
67
  {
66
68
  rawCommand: 'npm run build',
@@ -179,6 +181,157 @@ export function detectRtkBinary() {
179
181
  }
180
182
  }
181
183
 
184
+ export function detectAscxRuntime() {
185
+ const localBinPath = path.join(REPOSITORY_ROOT, 'bin', 'ascx.js');
186
+ const pathDetection = spawnSync('ascx', [], {
187
+ encoding: 'utf8',
188
+ stdio: ['ignore', 'pipe', 'pipe'],
189
+ });
190
+ const pathBinaryLooksAvailable = pathDetection.error == null
191
+ && typeof pathDetection.status === 'number'
192
+ && /command is required/i.test(`${pathDetection.stdout || ''}\n${pathDetection.stderr || ''}`);
193
+
194
+ if (pathBinaryLooksAvailable) {
195
+ return {
196
+ isAvailable: true,
197
+ source: 'path',
198
+ command: 'ascx',
199
+ localBinPath,
200
+ detectionError: null,
201
+ };
202
+ }
203
+
204
+ if (existsSync(localBinPath)) {
205
+ return {
206
+ isAvailable: true,
207
+ source: 'package-bin',
208
+ command: `node ${localBinPath}`,
209
+ localBinPath,
210
+ detectionError: pathDetection.error ? pathDetection.error.message : null,
211
+ };
212
+ }
213
+
214
+ return {
215
+ isAvailable: false,
216
+ source: 'missing',
217
+ command: null,
218
+ localBinPath,
219
+ detectionError: pathDetection.error
220
+ ? pathDetection.error.message
221
+ : (pathDetection.stderr || pathDetection.stdout || 'ascx binary was not detected').trim(),
222
+ };
223
+ }
224
+
225
+ export async function checkAscxTeeReadiness(targetDirectoryPath, options = {}) {
226
+ const shouldWriteProbe = options.writeProbe === true;
227
+ const stateDirectoryPath = path.join(targetDirectoryPath, '.agent-context', 'state');
228
+ const teeDirectoryPath = path.join(stateDirectoryPath, 'token-saver', 'tee');
229
+
230
+ try {
231
+ if (shouldWriteProbe) {
232
+ await fs.mkdir(teeDirectoryPath, { recursive: true });
233
+ const probeFilePath = path.join(teeDirectoryPath, `.ascx-probe-${process.pid}.tmp`);
234
+ await fs.writeFile(probeFilePath, 'ascx tee probe\n', 'utf8');
235
+ await fs.rm(probeFilePath, { force: true });
236
+ } else {
237
+ await fs.access(stateDirectoryPath);
238
+ }
239
+
240
+ return {
241
+ status: 'writable',
242
+ path: teeDirectoryPath,
243
+ error: null,
244
+ };
245
+ } catch (readinessError) {
246
+ return {
247
+ status: 'not-writable',
248
+ path: teeDirectoryPath,
249
+ error: readinessError instanceof Error ? readinessError.message : String(readinessError),
250
+ };
251
+ }
252
+ }
253
+
254
+ export function resolveRuntimeTokenSaverMode({
255
+ tokenOptimizationState,
256
+ ascxDetection,
257
+ rtkDetection,
258
+ }) {
259
+ const stateEnabled = tokenOptimizationState?.enabled === true;
260
+ const rtkDetected = rtkDetection?.isAvailable === true;
261
+
262
+ if (stateEnabled && ascxDetection?.isAvailable && rtkDetected) {
263
+ return 'conflict-risk';
264
+ }
265
+
266
+ if (stateEnabled && ascxDetection?.isAvailable) {
267
+ return 'runtime-on';
268
+ }
269
+
270
+ if (stateEnabled) {
271
+ return 'policy-only';
272
+ }
273
+
274
+ if (rtkDetected) {
275
+ return 'external-runtime-detected';
276
+ }
277
+
278
+ return 'runtime-off';
279
+ }
280
+
281
+ export function buildRuntimeTokenSaverWarnings({
282
+ mode,
283
+ onboardingReport,
284
+ ascxDetection,
285
+ teeReadiness,
286
+ rtkDetection,
287
+ }) {
288
+ const warnings = [];
289
+
290
+ if (!onboardingReport) {
291
+ warnings.push('project is not initialized; run init before enabling runtime token saving');
292
+ }
293
+
294
+ if (!ascxDetection?.isAvailable) {
295
+ warnings.push('ascx binary is missing; supported commands will not be compressed');
296
+ }
297
+
298
+ if (teeReadiness?.status !== 'writable') {
299
+ warnings.push('raw tee folder is not writable; failing commands may lose safety logs');
300
+ }
301
+
302
+ if (mode === 'conflict-risk') {
303
+ warnings.push('ASCX and an external runtime compressor both appear active; avoid double compression');
304
+ } else if (rtkDetection?.isAvailable) {
305
+ warnings.push('external runtime compressor detected; keep only one runtime compressor enabled by default');
306
+ }
307
+
308
+ return warnings;
309
+ }
310
+
311
+ export function buildRuntimeTokenSaverNextAction({ mode, warnings }) {
312
+ if (warnings.some((warning) => warning.includes('not initialized'))) {
313
+ return 'Run agentic-senior-core init before enabling ASCX runtime saving.';
314
+ }
315
+
316
+ if (warnings.some((warning) => warning.includes('tee folder'))) {
317
+ return 'Fix .agent-context/state write access, then rerun optimize doctor.';
318
+ }
319
+
320
+ if (mode === 'conflict-risk') {
321
+ return 'Disable either ASCX runtime saving or the external compressor before using compressed command output.';
322
+ }
323
+
324
+ if (mode === 'runtime-on') {
325
+ return 'Use ascx git status, ascx git diff, and ascx npm test for supported high-volume command output.';
326
+ }
327
+
328
+ if (mode === 'policy-only') {
329
+ return 'Install or expose the ascx binary, then rerun optimize doctor.';
330
+ }
331
+
332
+ return 'Run agentic-senior-core optimize install to enable ASCX guidance for this repository.';
333
+ }
334
+
182
335
  export function buildRtkInstallHint() {
183
336
  if (platform === 'win32') {
184
337
  return 'Install the external token optimizer binary for Windows, extract it, and ensure the executable is on PATH.';
@@ -298,6 +451,7 @@ export function buildTokenOptimizationGuidanceBlock(tokenOptimizationState) {
298
451
  );
299
452
 
300
453
  const fallbackGuidance = [
454
+ '- Use ascx git status, ascx git diff, and ascx npm test when the ascx binary is available.',
301
455
  '- Use command variants with bounded output such as git diff --stat, rg --max-count, and npm test -- --reporter=dot.',
302
456
  '- Request only the lines or sections required for the current decision.',
303
457
  '- If shell output is still large, summarize and continue iteratively instead of dumping full logs.',
@@ -319,6 +473,7 @@ export function buildTokenOptimizationGuidanceBlock(tokenOptimizationState) {
319
473
  ...rewriteLines,
320
474
  '',
321
475
  'Important scope note:',
476
+ '- ASCX wrappers are explicit local command wrappers, not provider gateways.',
322
477
  '- Shell rewrite hooks affect shell tool calls only.',
323
478
  '- Built-in read/grep/glob style tools may bypass shell rewrites, so explicit compact shell commands should be preferred in high-volume sessions.',
324
479
  '',