@ryuenn3123/agentic-senior-core 4.2.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.
- package/.agent-context/state/README.md +2 -1
- package/AGENTS.md +4 -4
- package/README.md +1 -1
- package/benchmarks/README.md +37 -17
- package/benchmarks/runtime-token-saver/fixtures.mjs +110 -9
- package/lib/cli/ascx/adapters/npm-run-build.mjs +99 -0
- package/lib/cli/ascx/adapters/rg.mjs +39 -0
- package/lib/cli/ascx/formatter.mjs +5 -4
- package/lib/cli/ascx/lexer.mjs +16 -0
- package/lib/cli/ascx/runtime.mjs +4 -0
- package/lib/cli/ascx/tee-writer.mjs +25 -0
- package/lib/cli/backup.mjs +37 -4
- package/lib/cli/commands/init.mjs +12 -1
- package/lib/cli/commands/upgrade/design-intent-seed.mjs +46 -0
- package/lib/cli/commands/upgrade/token-optimization-state.mjs +51 -0
- package/lib/cli/commands/upgrade.mjs +32 -45
- package/lib/cli/project-scaffolder/prompt-builders.mjs +1 -0
- package/package.json +1 -1
- package/scripts/build-release-benchmark-bundle.mjs +1 -3
- package/scripts/clean-local-artifacts.mjs +2 -0
- package/scripts/validate/config.mjs +5 -0
|
@@ -17,8 +17,9 @@ Tracked operational artifact:
|
|
|
17
17
|
|
|
18
18
|
Local-only/generated artifacts:
|
|
19
19
|
- `active-memory.json`
|
|
20
|
-
- `v3-purge-audit.json`
|
|
21
20
|
- `llm-judge-report.json`
|
|
21
|
+
- `token-saver/`
|
|
22
|
+
- `token-optimization-report.json`
|
|
22
23
|
- benchmark, trend, weekly governance, and quality report outputs
|
|
23
24
|
|
|
24
25
|
Do not treat generated reports or archived research as current project truth. Rerun the matching `npm run benchmark:*`, `npm run report:*`, or `npm run audit:*` command when fresh evidence is needed.
|
package/AGENTS.md
CHANGED
|
@@ -25,10 +25,10 @@ For non-trivial coding, review, planning, or governance work, run `agentic-senio
|
|
|
25
25
|
- `unreachable_files`: required files that could not be read
|
|
26
26
|
- `validation_plan`: expected checks before completion
|
|
27
27
|
|
|
28
|
-
Keep it short. Do not load every rule just to fill it out.
|
|
28
|
+
Keep it short. Do not load every rule just to fill it out. Skip this receipt and do not run the context command for trivial tasks (e.g., simple version bumps, minor typo fixes, or simple commits).
|
|
29
29
|
|
|
30
|
-
## Command Economy
|
|
31
|
-
|
|
30
|
+
## Default Activation And Command Economy
|
|
31
|
+
At session start, enforce default-on surfaces: run `agentic-senior-core context` for non-trivial work, prefer `ascx git status` / `ascx git diff` / `ascx npm test` when available, and apply `compact-natural-mode.md` for final replies. Avoid repeated command output; use raw commands for pipes, redirects, or unsupported commands.
|
|
32
32
|
|
|
33
33
|
## Layer Index
|
|
34
34
|
### Layer 1: Rules (21 Files) [SCOPE-RESOLVED]
|
|
@@ -175,4 +175,4 @@ Verify reachability when relevant: Layer 1 Rules, Layer 2 Runtime Decision Signa
|
|
|
175
175
|
- Before PR: run review checklists.
|
|
176
176
|
- Before deploy: check policy thresholds.
|
|
177
177
|
- Before major refactor: read `architecture-map.md`.
|
|
178
|
-
- Before UI implementation: confirm valid style context, design contract, and required docs.
|
|
178
|
+
- Before UI implementation: confirm valid style context, design contract, and required docs.
|
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ A coding agent that has read every framework tutorial still ships junior-grade w
|
|
|
68
68
|
### How It Works
|
|
69
69
|
|
|
70
70
|
1. Agent reads `AGENTS.md` at the start of each session.
|
|
71
|
-
2. `AGENTS.md`
|
|
71
|
+
2. `AGENTS.md` activates the default triad: Adaptive Context for scoped rule selection, ASCX wrappers for supported noisy commands, and Compact Natural Mode for final replies.
|
|
72
72
|
3. For shell commands, the agent uses `ascx` wrappers that compress noisy output while preserving debugging evidence (exit codes, file paths, line numbers, root errors, truncation markers, and raw tee paths).
|
|
73
73
|
4. For final replies, the agent applies `.agent-context/prompts/compact-natural-mode.md` so answers stay concise without losing commands, paths, errors, assumptions, validation status, risks, or next actions.
|
|
74
74
|
5. Detailed rules live under `.agent-context/rules/` and load by scope: 21 rule files covering architecture, security, performance, testing, database, API, frontend, Docker, observability, resilience, migrations, background jobs, configuration, and versioning.
|
package/benchmarks/README.md
CHANGED
|
@@ -1,40 +1,60 @@
|
|
|
1
1
|
# Benchmarks
|
|
2
2
|
|
|
3
|
-
Reproducible measurement suites
|
|
3
|
+
Reproducible measurement suites for this repository. Release-facing claims must point to tracked JSON evidence under `benchmarks/results/` or to deterministic benchmark commands that print machine-readable output.
|
|
4
4
|
|
|
5
5
|
## Folder Structure
|
|
6
6
|
|
|
7
|
-
```
|
|
7
|
+
```text
|
|
8
8
|
benchmarks/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
|-- anti-halu/ Provider-free anti-hallucination fixtures and scorer
|
|
10
|
+
|-- compact-natural-mode/ Response compression fixtures and scorer
|
|
11
|
+
|-- runtime-token-saver/ ASCX command-output compression fixtures
|
|
12
|
+
|-- token-usage/ Provider token-counting and cache simulation benchmark
|
|
13
|
+
|-- results/ Tracked release evidence JSON
|
|
14
|
+
`-- README.md
|
|
12
15
|
```
|
|
13
16
|
|
|
14
17
|
## Active Benchmarks
|
|
15
18
|
|
|
16
19
|
| Suite | Folder | Status | Output |
|
|
17
|
-
|
|
18
|
-
| Token usage baseline | `token-usage/` |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| Token usage baseline | `token-usage/` | Historical release evidence | `results/baseline-{YYYY-MM-DD}.json` |
|
|
22
|
+
| Cache simulation | `token-usage/` | Historical release evidence | `results/cache-phase-2-{YYYY-MM-DD}.json` |
|
|
23
|
+
| Anti-halu benchmark | `anti-halu/` | Active deterministic gate | `results/anti-halu-phase-3-{YYYY-MM-DD}.json` |
|
|
24
|
+
| ASCX runtime token saver | `runtime-token-saver/` | Active deterministic gate | stdout via `npm run benchmark:ascx` |
|
|
25
|
+
| Compact Natural Mode | `compact-natural-mode/` | Active deterministic gate | stdout via `npm run benchmark:compact-natural` |
|
|
26
|
+
| Release benchmark bundle | `results/` | Active integrity gate | `results/release-bundle-{semver}.json` |
|
|
19
27
|
|
|
20
28
|
## Running Benchmarks
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
Use the package scripts for active gates:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm run benchmark:adaptive-context
|
|
34
|
+
npm run benchmark:ascx
|
|
35
|
+
npm run benchmark:compact-natural
|
|
36
|
+
npm run benchmark:anti-halu
|
|
37
|
+
npm run build:release-bundle
|
|
38
|
+
npm run audit:release-bundle
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
See `benchmarks/token-usage/README.md` for the provider token-counting benchmark details.
|
|
23
42
|
|
|
24
43
|
## Reproducibility Requirements
|
|
25
44
|
|
|
26
|
-
1. Node
|
|
27
|
-
2.
|
|
28
|
-
3.
|
|
29
|
-
4.
|
|
45
|
+
1. Use Node 22 or the runtime version documented by the current package metadata.
|
|
46
|
+
2. Do not add runtime dependencies to the core package for benchmark-only work. Benchmark tooling belongs in `devDependencies`.
|
|
47
|
+
3. Keep JSON output deterministic where possible. If timestamps or host facts vary, isolate them in metadata fields.
|
|
48
|
+
4. Include schema version, timestamp, and tooling version when a result file is tracked so older evidence remains interpretable.
|
|
30
49
|
|
|
31
50
|
## Result Files Convention
|
|
32
51
|
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
52
|
+
- Track release evidence under `benchmarks/results/*.json`.
|
|
53
|
+
- Use `{suite-name}-{YYYY-MM-DD}.json` or `{suite-name}-{semver}.json`.
|
|
54
|
+
- Do not store raw API responses, secrets, tokens, or user data.
|
|
55
|
+
- Historical phase outcomes are consolidated in `docs/archive/HISTORY.md`.
|
|
56
|
+
- Live benchmark reporting rules are in `docs/benchmark-reference.md`.
|
|
37
57
|
|
|
38
58
|
## Why This Exists
|
|
39
59
|
|
|
40
|
-
|
|
60
|
+
Claims such as "40% token saving" or "rule adherence improved" need reproducible evidence. These suites are the comparator surface for README, changelog, release, and planning claims.
|
|
@@ -349,14 +349,14 @@ export const ASCX_RUNTIME_TOKEN_SAVER_FIXTURES = [
|
|
|
349
349
|
stderr: '',
|
|
350
350
|
},
|
|
351
351
|
expectCompressed: true,
|
|
352
|
-
expectTee:
|
|
352
|
+
expectTee: false,
|
|
353
353
|
requiredSubstrings: [
|
|
354
354
|
'npm test summary:',
|
|
355
355
|
'# tests 1',
|
|
356
356
|
'# pass 1',
|
|
357
357
|
'# fail 0',
|
|
358
358
|
'exit: 0',
|
|
359
|
-
'raw_output:',
|
|
359
|
+
'raw_output: none',
|
|
360
360
|
],
|
|
361
361
|
forbiddenSubstrings: [
|
|
362
362
|
'passing fixture 01',
|
|
@@ -364,18 +364,18 @@ export const ASCX_RUNTIME_TOKEN_SAVER_FIXTURES = [
|
|
|
364
364
|
continuationChecks: [
|
|
365
365
|
{
|
|
366
366
|
id: 'passing-tests-avoid-failure-hunt',
|
|
367
|
-
action: 'Decide that no failing-test triage is needed
|
|
367
|
+
action: 'Decide that no failing-test triage is needed and no raw tee was written.',
|
|
368
368
|
requiredSubstrings: [
|
|
369
369
|
'# tests 1',
|
|
370
370
|
'# pass 1',
|
|
371
371
|
'# fail 0',
|
|
372
372
|
'exit: 0',
|
|
373
|
-
'raw_output:',
|
|
373
|
+
'raw_output: none',
|
|
374
374
|
],
|
|
375
375
|
forbiddenSubstrings: [
|
|
376
376
|
'passing fixture 01',
|
|
377
377
|
],
|
|
378
|
-
expectTee:
|
|
378
|
+
expectTee: false,
|
|
379
379
|
},
|
|
380
380
|
],
|
|
381
381
|
},
|
|
@@ -399,14 +399,14 @@ export const ASCX_RUNTIME_TOKEN_SAVER_FIXTURES = [
|
|
|
399
399
|
stderr: '',
|
|
400
400
|
},
|
|
401
401
|
expectCompressed: true,
|
|
402
|
-
expectTee:
|
|
402
|
+
expectTee: false,
|
|
403
403
|
requiredSubstrings: [
|
|
404
404
|
'npm test summary:',
|
|
405
405
|
'# tests 2',
|
|
406
406
|
'# pass 2',
|
|
407
407
|
'# fail 0',
|
|
408
408
|
'exit: 0',
|
|
409
|
-
'raw_output:',
|
|
409
|
+
'raw_output: none',
|
|
410
410
|
],
|
|
411
411
|
forbiddenSubstrings: [
|
|
412
412
|
'failures:',
|
|
@@ -421,13 +421,13 @@ export const ASCX_RUNTIME_TOKEN_SAVER_FIXTURES = [
|
|
|
421
421
|
'# tests 2',
|
|
422
422
|
'# pass 2',
|
|
423
423
|
'# fail 0',
|
|
424
|
-
'raw_output:',
|
|
424
|
+
'raw_output: none',
|
|
425
425
|
],
|
|
426
426
|
forbiddenSubstrings: [
|
|
427
427
|
'failures:',
|
|
428
428
|
'[FATAL]',
|
|
429
429
|
],
|
|
430
|
-
expectTee:
|
|
430
|
+
expectTee: false,
|
|
431
431
|
},
|
|
432
432
|
],
|
|
433
433
|
},
|
|
@@ -610,4 +610,105 @@ export const ASCX_RUNTIME_TOKEN_SAVER_FIXTURES = [
|
|
|
610
610
|
},
|
|
611
611
|
],
|
|
612
612
|
},
|
|
613
|
+
{
|
|
614
|
+
id: 'npm-run-build-success',
|
|
615
|
+
commandArguments: ['npm', 'run', 'build'],
|
|
616
|
+
capture: {
|
|
617
|
+
exitCode: 0,
|
|
618
|
+
stdout: [
|
|
619
|
+
'> npm run build',
|
|
620
|
+
'> vite build',
|
|
621
|
+
'vite v4.0.0 building for production...',
|
|
622
|
+
'transforming...',
|
|
623
|
+
'✓ 124 modules transformed.',
|
|
624
|
+
'dist/index.html 1.2kB',
|
|
625
|
+
'dist/assets/index.js 24kB',
|
|
626
|
+
'built in 1.2s',
|
|
627
|
+
].join('\n'),
|
|
628
|
+
stderr: '',
|
|
629
|
+
},
|
|
630
|
+
expectCompressed: true,
|
|
631
|
+
expectTee: false,
|
|
632
|
+
requiredSubstrings: [
|
|
633
|
+
'npm run build summary:',
|
|
634
|
+
'result: passed',
|
|
635
|
+
'exit: 0',
|
|
636
|
+
],
|
|
637
|
+
continuationChecks: [
|
|
638
|
+
{
|
|
639
|
+
id: 'npm-run-build-success-compacts-noise',
|
|
640
|
+
action: 'Recognize the build passed and avoid reading raw noise.',
|
|
641
|
+
requiredSubstrings: [
|
|
642
|
+
'result: passed',
|
|
643
|
+
'exit: 0',
|
|
644
|
+
],
|
|
645
|
+
expectTee: false,
|
|
646
|
+
},
|
|
647
|
+
],
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
id: 'npm-run-build-failure',
|
|
651
|
+
commandArguments: ['npm', 'run', 'build'],
|
|
652
|
+
capture: {
|
|
653
|
+
exitCode: 1,
|
|
654
|
+
stdout: [
|
|
655
|
+
'> npm run build',
|
|
656
|
+
'> tsc --noEmit',
|
|
657
|
+
'src/components/Header.tsx:15:10 - error TS2322: Type \'string\' is not assignable to type \'number\'.',
|
|
658
|
+
'15 const age: number = "25";',
|
|
659
|
+
].join('\n'),
|
|
660
|
+
stderr: '',
|
|
661
|
+
},
|
|
662
|
+
expectCompressed: true,
|
|
663
|
+
expectTee: true,
|
|
664
|
+
requiredSubstrings: [
|
|
665
|
+
'npm run build summary:',
|
|
666
|
+
'failures:',
|
|
667
|
+
'error TS2322',
|
|
668
|
+
'Header.tsx:15:10',
|
|
669
|
+
'exit: 1',
|
|
670
|
+
],
|
|
671
|
+
continuationChecks: [
|
|
672
|
+
{
|
|
673
|
+
id: 'npm-run-build-failure-preserves-error',
|
|
674
|
+
action: 'Identify the build error and file path.',
|
|
675
|
+
requiredSubstrings: [
|
|
676
|
+
'failures:',
|
|
677
|
+
'error TS2322',
|
|
678
|
+
'Header.tsx:15:10',
|
|
679
|
+
],
|
|
680
|
+
expectTee: true,
|
|
681
|
+
},
|
|
682
|
+
],
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
id: 'rg-large-output',
|
|
686
|
+
commandArguments: ['rg', 'function'],
|
|
687
|
+
capture: {
|
|
688
|
+
exitCode: 0,
|
|
689
|
+
stdout: Array.from({ length: 120 }, (_, i) => `src/file-${i}.ts:10:function doSomething() {}`).join('\n'),
|
|
690
|
+
stderr: '',
|
|
691
|
+
},
|
|
692
|
+
expectCompressed: true,
|
|
693
|
+
expectTee: true, // We should write tee because it's truncated? Let's check formatter logic
|
|
694
|
+
requiredSubstrings: [
|
|
695
|
+
'ripgrep results:',
|
|
696
|
+
'src/file-0.ts',
|
|
697
|
+
'... truncated 40 more lines of matches',
|
|
698
|
+
'truncation: full search results available in the raw tee output',
|
|
699
|
+
'exit: 0',
|
|
700
|
+
],
|
|
701
|
+
continuationChecks: [
|
|
702
|
+
{
|
|
703
|
+
id: 'rg-truncates-massive-results',
|
|
704
|
+
action: 'Identify that ripgrep found many matches and read the truncated summary.',
|
|
705
|
+
requiredSubstrings: [
|
|
706
|
+
'ripgrep results:',
|
|
707
|
+
'... truncated 40 more lines of matches',
|
|
708
|
+
'truncation: full search results available in the raw tee output',
|
|
709
|
+
],
|
|
710
|
+
expectTee: true, // Should tee if truncated
|
|
711
|
+
},
|
|
712
|
+
],
|
|
713
|
+
},
|
|
613
714
|
];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
const FAILURE_LINE_PATTERN = /(?:^Error:|^TypeError:|^SyntaxError|^ReferenceError|^TS\d+:\s|^Failed to compile|Module not found|ERR!|error\b|ERROR\b|×|✖|failure|FAIL\b)/iu;
|
|
2
|
+
const FILE_LINE_PATTERN = /(?:[A-Za-z]:)?[^:\s]+?\.(?:cjs|mjs|js|jsx|ts|tsx|vue|svelte|css|scss|html|json):\d+(?::\d+)?/u;
|
|
3
|
+
|
|
4
|
+
function pushUniqueLine(lines, nextLine) {
|
|
5
|
+
const normalizedLine = String(nextLine || '').trimEnd();
|
|
6
|
+
if (normalizedLine && !lines.includes(normalizedLine)) {
|
|
7
|
+
lines.push(normalizedLine);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function extractFailureLines(lines) {
|
|
12
|
+
const keptLines = [];
|
|
13
|
+
let contextBuffer = [];
|
|
14
|
+
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const trimmedLine = line.trim();
|
|
17
|
+
|
|
18
|
+
// Preserve empty lines in context buffer to maintain readability if we decide to flush it
|
|
19
|
+
if (!trimmedLine) {
|
|
20
|
+
if (contextBuffer.length > 0) {
|
|
21
|
+
contextBuffer.push(line);
|
|
22
|
+
}
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const isFailureLine = FAILURE_LINE_PATTERN.test(trimmedLine) || FILE_LINE_PATTERN.test(trimmedLine);
|
|
27
|
+
|
|
28
|
+
if (isFailureLine) {
|
|
29
|
+
// If we found a failure, flush the preceding short context (e.g., file paths that didn't match the regex but preceded an error)
|
|
30
|
+
for (const ctxLine of contextBuffer) {
|
|
31
|
+
pushUniqueLine(keptLines, ctxLine);
|
|
32
|
+
}
|
|
33
|
+
contextBuffer = [];
|
|
34
|
+
pushUniqueLine(keptLines, line);
|
|
35
|
+
} else {
|
|
36
|
+
// Keep a small rolling buffer of context lines (max 2) before an error
|
|
37
|
+
contextBuffer.push(line);
|
|
38
|
+
if (contextBuffer.length > 2) {
|
|
39
|
+
contextBuffer.shift();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return keptLines;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function compressNpmRunBuildOutput({ stdout, stderr, exitCode }) {
|
|
48
|
+
const rawOutput = [stdout, stderr].filter(Boolean).join('\n');
|
|
49
|
+
const lines = rawOutput.split(/\r?\n/u);
|
|
50
|
+
|
|
51
|
+
// If exit code is 0, build probably succeeded, just return a short success message.
|
|
52
|
+
if (exitCode === 0) {
|
|
53
|
+
return {
|
|
54
|
+
filterName: 'npm-run-build-summary',
|
|
55
|
+
confident: true,
|
|
56
|
+
truncated: false,
|
|
57
|
+
output: 'npm run build summary:\nresult: passed',
|
|
58
|
+
preservedFields: {
|
|
59
|
+
exitCode: true,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const failureLines = extractFailureLines(lines);
|
|
65
|
+
const outputLines = ['npm run build summary:'];
|
|
66
|
+
|
|
67
|
+
if (failureLines.length > 0) {
|
|
68
|
+
outputLines.push('failures:');
|
|
69
|
+
outputLines.push(...failureLines.slice(0, 100));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const truncated = failureLines.length > 100;
|
|
73
|
+
if (truncated) {
|
|
74
|
+
outputLines.push(`... truncated ${failureLines.length - 100} more failure evidence lines`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (failureLines.length === 0) {
|
|
78
|
+
// We couldn't parse the errors clearly, fallback to raw
|
|
79
|
+
return {
|
|
80
|
+
filterName: 'npm-run-build-raw-parse-uncertain',
|
|
81
|
+
confident: false,
|
|
82
|
+
truncated: false,
|
|
83
|
+
output: rawOutput,
|
|
84
|
+
preservedFields: {},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
filterName: 'npm-run-build-summary',
|
|
90
|
+
confident: true,
|
|
91
|
+
truncated,
|
|
92
|
+
output: outputLines.join('\n'),
|
|
93
|
+
preservedFields: {
|
|
94
|
+
rootError: failureLines.length > 0,
|
|
95
|
+
filePath: failureLines.some((line) => FILE_LINE_PATTERN.test(line)),
|
|
96
|
+
exitCode: true,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function compressRgOutput({ stdout, stderr, exitCode }) {
|
|
2
|
+
const rawOutput = [stdout, stderr].filter(Boolean).join('\n');
|
|
3
|
+
const lines = rawOutput.split(/\r?\n/u);
|
|
4
|
+
|
|
5
|
+
// If exit code is non-zero and no output, rg found nothing or errored
|
|
6
|
+
if (lines.length === 0 || (lines.length === 1 && lines[0] === '')) {
|
|
7
|
+
return {
|
|
8
|
+
filterName: 'rg-summary',
|
|
9
|
+
confident: true,
|
|
10
|
+
truncated: false,
|
|
11
|
+
output: exitCode === 0 ? 'No matches found.' : `ripgrep exited with code ${exitCode}`,
|
|
12
|
+
preservedFields: {
|
|
13
|
+
exitCode: true,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const outputLines = ['ripgrep results:'];
|
|
19
|
+
const maxLines = 80;
|
|
20
|
+
|
|
21
|
+
if (lines.length > maxLines) {
|
|
22
|
+
outputLines.push(...lines.slice(0, maxLines));
|
|
23
|
+
outputLines.push(`... truncated ${lines.length - maxLines} more lines of matches`);
|
|
24
|
+
outputLines.push(`truncation: full search results available in the raw tee output`);
|
|
25
|
+
} else {
|
|
26
|
+
outputLines.push(...lines);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
filterName: 'rg-summary',
|
|
31
|
+
confident: true,
|
|
32
|
+
truncated: lines.length > maxLines,
|
|
33
|
+
output: outputLines.join('\n'),
|
|
34
|
+
preservedFields: {
|
|
35
|
+
exitCode: true,
|
|
36
|
+
filePath: true, // We assume rg output contains file paths
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -39,8 +39,9 @@ export function buildAscxFooter({
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
export function shouldWriteSafetyTee({ adapterResult, exitCode, reductionPercent }) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
if (exitCode !== 0) return true;
|
|
43
|
+
if (adapterResult.truncated === true) return true;
|
|
44
|
+
if (adapterResult.confident === false) return true;
|
|
45
|
+
if (adapterResult.confident !== true && reductionPercent >= HIGH_RISK_REDUCTION_PERCENT) return true;
|
|
46
|
+
return false;
|
|
46
47
|
}
|
package/lib/cli/ascx/lexer.mjs
CHANGED
|
@@ -105,6 +105,22 @@ export function classifyAscxInvocation(parsedCommand) {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
if (parsedCommand.executable === 'npm' && parsedCommand.args[0] === 'run' && parsedCommand.args[1] === 'build') {
|
|
109
|
+
return {
|
|
110
|
+
kind: 'compressible',
|
|
111
|
+
adapterName: 'npm-run-build',
|
|
112
|
+
reason: 'supported npm run build adapter',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (parsedCommand.executable === 'rg') {
|
|
117
|
+
return {
|
|
118
|
+
kind: 'compressible',
|
|
119
|
+
adapterName: 'rg',
|
|
120
|
+
reason: 'supported rg (ripgrep) adapter',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
108
124
|
return {
|
|
109
125
|
kind: 'passthrough',
|
|
110
126
|
adapterName: null,
|
package/lib/cli/ascx/runtime.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import { spawn } from 'node:child_process';
|
|
|
3
3
|
import { compressGitDiffOutput } from './adapters/git-diff.mjs';
|
|
4
4
|
import { compressGitStatusOutput } from './adapters/git-status.mjs';
|
|
5
5
|
import { compressNpmTestOutput } from './adapters/npm-test.mjs';
|
|
6
|
+
import { compressNpmRunBuildOutput } from './adapters/npm-run-build.mjs';
|
|
7
|
+
import { compressRgOutput } from './adapters/rg.mjs';
|
|
6
8
|
import { buildAscxFooter, shouldWriteSafetyTee } from './formatter.mjs';
|
|
7
9
|
import { classifyAscxInvocation, parseAscxCommand } from './lexer.mjs';
|
|
8
10
|
import { writeRawTeeFile } from './tee-writer.mjs';
|
|
@@ -11,6 +13,8 @@ const ADAPTERS = {
|
|
|
11
13
|
'git-diff': compressGitDiffOutput,
|
|
12
14
|
'git-status': compressGitStatusOutput,
|
|
13
15
|
'npm-test': compressNpmTestOutput,
|
|
16
|
+
'npm-run-build': compressNpmRunBuildOutput,
|
|
17
|
+
'rg': compressRgOutput,
|
|
14
18
|
};
|
|
15
19
|
|
|
16
20
|
function buildCommandEnvironment(baseEnvironment, parsedCommand) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
+
export const MAX_TEE_FILES = 20;
|
|
5
|
+
|
|
4
6
|
function sanitizeFileNamePart(rawValue) {
|
|
5
7
|
return String(rawValue || 'command')
|
|
6
8
|
.toLowerCase()
|
|
@@ -13,6 +15,27 @@ export function getDefaultTeeDirectory(cwd = process.cwd()) {
|
|
|
13
15
|
return path.resolve(cwd, '.agent-context', 'state', 'token-saver', 'tee');
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
async function sweepOldTeeFiles(directoryPath, maxFiles = MAX_TEE_FILES) {
|
|
19
|
+
let entries;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
entries = await fs.readdir(directoryPath);
|
|
23
|
+
} catch {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const logFiles = entries.filter((name) => name.endsWith('.log')).sort();
|
|
28
|
+
const excess = logFiles.length - maxFiles;
|
|
29
|
+
|
|
30
|
+
for (let i = 0; i < excess; i++) {
|
|
31
|
+
try {
|
|
32
|
+
await fs.unlink(path.join(directoryPath, logFiles[i]));
|
|
33
|
+
} catch {
|
|
34
|
+
// Best-effort cleanup; do not fail the write path.
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
16
39
|
export async function writeRawTeeFile({
|
|
17
40
|
commandText,
|
|
18
41
|
cwd = process.cwd(),
|
|
@@ -33,6 +56,8 @@ export async function writeRawTeeFile({
|
|
|
33
56
|
|
|
34
57
|
await fs.mkdir(path.dirname(teeFilePath), { recursive: true });
|
|
35
58
|
await fs.writeFile(teeFilePath, fileContent, 'utf8');
|
|
59
|
+
await sweepOldTeeFiles(teeDirectoryPath);
|
|
36
60
|
|
|
37
61
|
return teeFilePath;
|
|
38
62
|
}
|
|
63
|
+
|
package/lib/cli/backup.mjs
CHANGED
|
@@ -6,6 +6,11 @@ import { entryPointFiles, BACKUP_DIR_NAME } from './constants.mjs';
|
|
|
6
6
|
|
|
7
7
|
const BACKUP_GITIGNORE_ENTRY = `${BACKUP_DIR_NAME}/`;
|
|
8
8
|
const BACKUP_GITIGNORE_COMMENT = '# agentic-senior-core: local backup artifacts';
|
|
9
|
+
const RUNTIME_ARTIFACT_GITIGNORE_ENTRIES = [
|
|
10
|
+
'.agent-context/state/token-saver/',
|
|
11
|
+
'.agent-context/state/token-optimization-report.json',
|
|
12
|
+
];
|
|
13
|
+
const RUNTIME_ARTIFACT_GITIGNORE_COMMENT = '# agentic-senior-core: local runtime artifacts';
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
16
|
* Calculates a SHA-256 hash of a file's contents.
|
|
@@ -127,18 +132,23 @@ export async function createBackup(targetDirectoryPath) {
|
|
|
127
132
|
};
|
|
128
133
|
}
|
|
129
134
|
|
|
130
|
-
|
|
135
|
+
async function ensureGitignoreEntries(targetDirectoryPath, { comment, entries, aliasesByEntry = {} }) {
|
|
131
136
|
const gitignorePath = path.join(targetDirectoryPath, '.gitignore');
|
|
132
137
|
const existingContent = await pathExists(gitignorePath)
|
|
133
138
|
? await fs.readFile(gitignorePath, 'utf8')
|
|
134
139
|
: '';
|
|
135
140
|
const existingLines = existingContent.split(/\r?\n/).map((line) => line.trim());
|
|
141
|
+
const missingEntries = entries.filter((entry) => {
|
|
142
|
+
const equivalentEntries = [entry, ...(aliasesByEntry[entry] || [])];
|
|
143
|
+
return !equivalentEntries.some((equivalentEntry) => existingLines.includes(equivalentEntry));
|
|
144
|
+
});
|
|
136
145
|
|
|
137
|
-
if (
|
|
146
|
+
if (missingEntries.length === 0) {
|
|
138
147
|
return {
|
|
139
148
|
status: 'unchanged',
|
|
140
149
|
gitignorePath,
|
|
141
|
-
|
|
150
|
+
entries,
|
|
151
|
+
addedEntries: [],
|
|
142
152
|
};
|
|
143
153
|
}
|
|
144
154
|
|
|
@@ -149,13 +159,36 @@ export async function ensureBackupGitignoreEntry(targetDirectoryPath) {
|
|
|
149
159
|
if (nextContent.length > 0 && !nextContent.endsWith('\n\n')) {
|
|
150
160
|
nextContent += '\n';
|
|
151
161
|
}
|
|
152
|
-
nextContent += `${
|
|
162
|
+
nextContent += `${comment}\n${missingEntries.join('\n')}\n`;
|
|
153
163
|
|
|
154
164
|
await fs.writeFile(gitignorePath, nextContent, 'utf8');
|
|
155
165
|
|
|
156
166
|
return {
|
|
157
167
|
status: existingContent ? 'updated' : 'created',
|
|
158
168
|
gitignorePath,
|
|
169
|
+
entries,
|
|
170
|
+
addedEntries: missingEntries,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function ensureBackupGitignoreEntry(targetDirectoryPath) {
|
|
175
|
+
const result = await ensureGitignoreEntries(targetDirectoryPath, {
|
|
176
|
+
comment: BACKUP_GITIGNORE_COMMENT,
|
|
177
|
+
entries: [BACKUP_GITIGNORE_ENTRY],
|
|
178
|
+
aliasesByEntry: {
|
|
179
|
+
[BACKUP_GITIGNORE_ENTRY]: [BACKUP_DIR_NAME],
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
...result,
|
|
159
185
|
entry: BACKUP_GITIGNORE_ENTRY,
|
|
160
186
|
};
|
|
161
187
|
}
|
|
188
|
+
|
|
189
|
+
export async function ensureRuntimeArtifactGitignoreEntries(targetDirectoryPath) {
|
|
190
|
+
return ensureGitignoreEntries(targetDirectoryPath, {
|
|
191
|
+
comment: RUNTIME_ARTIFACT_GITIGNORE_COMMENT,
|
|
192
|
+
entries: RUNTIME_ARTIFACT_GITIGNORE_ENTRIES,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
@@ -45,7 +45,11 @@ import {
|
|
|
45
45
|
resolveDetectedSetupDecision,
|
|
46
46
|
} from '../init-detection-flow.mjs';
|
|
47
47
|
import { runPreflightChecks } from '../preflight.mjs';
|
|
48
|
-
import {
|
|
48
|
+
import {
|
|
49
|
+
createBackup,
|
|
50
|
+
ensureBackupGitignoreEntry,
|
|
51
|
+
ensureRuntimeArtifactGitignoreEntries,
|
|
52
|
+
} from '../backup.mjs';
|
|
49
53
|
import {
|
|
50
54
|
runProjectDiscovery,
|
|
51
55
|
generateProjectDocumentation,
|
|
@@ -373,6 +377,12 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
373
377
|
if (backupGitignoreResult.status !== 'unchanged') {
|
|
374
378
|
console.log(`Local backup artifacts ignored in .gitignore (${backupGitignoreResult.entry}).`);
|
|
375
379
|
}
|
|
380
|
+
const runtimeArtifactGitignoreResult = await ensureRuntimeArtifactGitignoreEntries(resolvedTargetDirectoryPath);
|
|
381
|
+
if (runtimeArtifactGitignoreResult.status !== 'unchanged') {
|
|
382
|
+
console.log(
|
|
383
|
+
`Local runtime artifacts ignored in .gitignore (${runtimeArtifactGitignoreResult.addedEntries.join(', ')}).`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
376
386
|
|
|
377
387
|
await copyGovernanceAssetsToTarget(resolvedTargetDirectoryPath, {
|
|
378
388
|
includeMcpTemplate: shouldIncludeMcpTemplate,
|
|
@@ -565,6 +575,7 @@ export async function runInitCommand(targetDirectoryArgument, initOptions = {})
|
|
|
565
575
|
console.log(`- Review thresholds: ${formatBlockingSeverities(selectedPolicyProfile.blockingSeverities)}`);
|
|
566
576
|
console.log(`- Setup time: ${formatDuration(setupDurationMs)}`);
|
|
567
577
|
console.log('- Generated files: AGENTS.md, CLAUDE.md, GEMINI.md, .agent-context/, and .agent-context/state/onboarding-report.json');
|
|
578
|
+
console.log('- Default activation cues: Adaptive Context bootstrap, ASCX command wrappers, Compact Natural final replies');
|
|
568
579
|
console.log('- Default response mode: Compact Natural Mode enabled (.agent-context/prompts/compact-natural-mode.md)');
|
|
569
580
|
if (scaffoldingResult?.bootstrapMode === 'ai-synthesis') {
|
|
570
581
|
console.log(`- Bootstrap prompts: ${(scaffoldingResult.generatedPromptFileNames || []).length} files generated in .agent-context/prompts/`);
|
|
@@ -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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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,8 @@ 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}`);
|
|
305
277
|
console.log('- Default response mode: Compact Natural Mode enabled');
|
|
306
278
|
console.log(`- Managed surface stale files: ${managedSurfacePlan.staleFiles.length}`);
|
|
307
279
|
console.log(`- Managed surface stale directories: ${managedSurfacePlan.staleDirectories.length}`);
|
|
@@ -374,6 +346,12 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
|
|
|
374
346
|
if (backupGitignoreResult.status !== 'unchanged') {
|
|
375
347
|
console.log(`Local backup artifacts ignored in .gitignore (${backupGitignoreResult.entry}).`);
|
|
376
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
|
+
}
|
|
377
355
|
|
|
378
356
|
try {
|
|
379
357
|
const governanceSyncResult = await copyGovernanceAssetsToTarget(resolvedTargetDirectoryPath, {
|
|
@@ -383,6 +361,13 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
|
|
|
383
361
|
});
|
|
384
362
|
const supplementalCreatedFileNames = [];
|
|
385
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
|
+
}
|
|
386
371
|
|
|
387
372
|
if (shouldSeedDesignIntentOnApply && designIntentSeedContent) {
|
|
388
373
|
const docsDirectoryPath = path.join(resolvedTargetDirectoryPath, 'docs');
|
|
@@ -428,6 +413,7 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
|
|
|
428
413
|
projectDetection,
|
|
429
414
|
runtimeEnvironment: existingOnboardingReport?.runtimeEnvironment || null,
|
|
430
415
|
operationMode: 'upgrade',
|
|
416
|
+
tokenOptimization: tokenOptimizationApplyResult.reportState,
|
|
431
417
|
detectionTransparency,
|
|
432
418
|
uiScopeSignals,
|
|
433
419
|
});
|
|
@@ -465,6 +451,7 @@ export async function runUpgradeCommand(targetDirectoryArgument, upgradeOptions
|
|
|
465
451
|
}
|
|
466
452
|
|
|
467
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.');
|
|
468
455
|
console.log('Default response mode remains Compact Natural Mode through .agent-context/prompts/compact-natural-mode.md.');
|
|
469
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.');
|
|
470
457
|
} catch (error) {
|
|
@@ -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
|
|
package/package.json
CHANGED
|
@@ -148,9 +148,7 @@ export function buildReleaseBenchmarkBundle(options = {}) {
|
|
|
148
148
|
research_foundation: 'docs/architecture/decisions-foundation.md',
|
|
149
149
|
d4_caching_scope_matrix: 'docs/architecture/decisions-foundation.md#d4',
|
|
150
150
|
caching_reporting_format: 'docs/benchmark-reference.md',
|
|
151
|
-
|
|
152
|
-
phase_3_outcome: 'docs/archive/phase-3-outcome.md',
|
|
153
|
-
phase_5_plan: 'docs/archive/phase-5-hardening.md',
|
|
151
|
+
historical_record: 'docs/archive/HISTORY.md',
|
|
154
152
|
},
|
|
155
153
|
integrity: {
|
|
156
154
|
hash_algorithm: 'SHA-256',
|
|
@@ -25,6 +25,8 @@ const LOCAL_ARTIFACT_PATHS = [
|
|
|
25
25
|
'.agent-context/state/docs-quality-drift-report.json',
|
|
26
26
|
'.agent-context/state/memory-continuity-benchmark.json',
|
|
27
27
|
'.agent-context/state/quality-trend-report.json',
|
|
28
|
+
'.agent-context/state/token-saver',
|
|
29
|
+
'.agent-context/state/token-optimization-report.json',
|
|
28
30
|
'.agent-context/state/token-optimization-benchmark.json',
|
|
29
31
|
'.agent-context/state/weekly-governance-report.json',
|
|
30
32
|
];
|
|
@@ -281,6 +281,11 @@ export const REQUIRED_UPGRADE_UI_CONTRACT_WARNING_SNIPPETS = [
|
|
|
281
281
|
'Planned seed on apply: docs/design-intent.json',
|
|
282
282
|
'Upgrade synchronizes governance assets and can seed docs/design-intent.json, but it does not author project-specific docs/DESIGN.md automatically.',
|
|
283
283
|
'detectUiScopeSignals',
|
|
284
|
+
],
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
path: 'lib/cli/commands/upgrade/design-intent-seed.mjs',
|
|
288
|
+
snippets: [
|
|
284
289
|
'seed-generated-during-upgrade',
|
|
285
290
|
],
|
|
286
291
|
},
|