@ryuenn3123/agentic-senior-core 3.0.10 → 3.0.12
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/prompts/bootstrap-design.md +7 -1
- package/.agent-context/state/memory-continuity-benchmark.json +1 -1
- package/.cursorrules +1 -1
- package/.windsurfrules +1 -1
- package/lib/cli/commands/init.mjs +2 -0
- package/lib/cli/commands/upgrade.mjs +5 -0
- package/lib/cli/compiler.mjs +9 -0
- package/lib/cli/constants.mjs +1 -0
- package/lib/cli/detector.mjs +422 -90
- package/lib/cli/project-scaffolder.mjs +122 -8
- package/package.json +1 -2
- package/scripts/frontend-usability-audit.mjs +4 -1
- package/scripts/mcp-server.mjs +200 -118
- package/scripts/ui-design-judge.mjs +21 -86
- package/scripts/validate.mjs +6 -1
- package/.agent-context/state/ui-design-judge-report.json +0 -25
|
@@ -315,7 +315,7 @@ const DESIGN_REQUIRED_SECTIONS = [
|
|
|
315
315
|
'Spacing, Layout Rhythm, and Density Strategy',
|
|
316
316
|
'Responsive Strategy and Cross-Viewport Adaptation Matrix',
|
|
317
317
|
'Interaction, Motion, and Feedback Rules',
|
|
318
|
-
'Component Language and Shared Patterns',
|
|
318
|
+
'Component Language, Morphology, and Shared Patterns',
|
|
319
319
|
'Accessibility Non-Negotiables',
|
|
320
320
|
'Anti-Patterns to Avoid',
|
|
321
321
|
'Implementation Notes for Future UI Tasks',
|
|
@@ -343,6 +343,17 @@ function inferDesignKeywords(discoveryAnswers) {
|
|
|
343
343
|
'Keep decision-critical information prominent while secondary merchandising stays quiet.',
|
|
344
344
|
'Let imagery and spacing create premium perception before decorative effects do.',
|
|
345
345
|
],
|
|
346
|
+
motionPurpose: 'Use restrained motion to reinforce buying confidence, product continuity, and feedback. Motion should feel premium, not promotional.',
|
|
347
|
+
motionChoreography: 'Favor short hover/focus transitions, sheet choreography, and product-media continuity. Avoid looping hero motion, autoplay spectacle, and parallax-heavy scenes.',
|
|
348
|
+
motionDurations: {
|
|
349
|
+
desktop: 180,
|
|
350
|
+
mobile: 240,
|
|
351
|
+
},
|
|
352
|
+
componentMorphology: {
|
|
353
|
+
mobile: 'Product cards should compress supporting metadata, pin purchase actions closer to the thumb zone, and move comparison into progressive disclosure or bottom sheets.',
|
|
354
|
+
tablet: 'Cards and merch modules should preserve comparison affordances while reducing tertiary chrome and keeping visual hierarchy stable.',
|
|
355
|
+
desktop: 'Cards can expand media, comparison, and reassurance copy while keeping buying cues dominant and visually disciplined.',
|
|
356
|
+
},
|
|
346
357
|
mutationRules: {
|
|
347
358
|
mobile: 'Convert browsing into vertically stacked product cards, move cart and filter actions into sticky or bottom-sheet patterns, and keep thumb-reach actions persistent.',
|
|
348
359
|
tablet: 'Preserve browsing flow with a two-column rhythm, collapse tertiary filters, and keep comparison moments visible without forcing desktop density.',
|
|
@@ -365,6 +376,17 @@ function inferDesignKeywords(discoveryAnswers) {
|
|
|
365
376
|
'Use visual weight to separate signal from operational noise.',
|
|
366
377
|
'Reserve strong accents for alerts, decisions, and state transitions only.',
|
|
367
378
|
],
|
|
379
|
+
motionPurpose: 'Use motion as operational feedback and state continuity, never as ambient decoration that competes with dense information.',
|
|
380
|
+
motionChoreography: 'Prefer fast transitions for filters, drawers, status reveals, and row expansion. Avoid floaty choreography and ornamental motion that slows scanning.',
|
|
381
|
+
motionDurations: {
|
|
382
|
+
desktop: 160,
|
|
383
|
+
mobile: 220,
|
|
384
|
+
},
|
|
385
|
+
componentMorphology: {
|
|
386
|
+
mobile: 'Data rows should become prioritized cards or grouped summaries, with filters and secondary tools moving into sheets or drawers.',
|
|
387
|
+
tablet: 'Operational panels should retain split-view logic where possible, while tertiary panels collapse behind explicit toggles.',
|
|
388
|
+
desktop: 'Dense tables, side panels, and comparison surfaces can remain visible simultaneously, with state treatments optimized for rapid scanning.',
|
|
389
|
+
},
|
|
368
390
|
mutationRules: {
|
|
369
391
|
mobile: 'Collapse dense tables into prioritized cards or row groups, move filters into drawers or sheets, and pin the most critical actions to the bottom reach zone.',
|
|
370
392
|
tablet: 'Keep two-column or split-pane workflows, collapse tertiary panels, and maintain fast scan paths for operators using touch or keyboard.',
|
|
@@ -387,6 +409,17 @@ function inferDesignKeywords(discoveryAnswers) {
|
|
|
387
409
|
'Use code-adjacent rhythm and hierarchy to build trust with technical users.',
|
|
388
410
|
'Keep complexity legible through spacing, grouping, and explicit interaction states.',
|
|
389
411
|
],
|
|
412
|
+
motionPurpose: 'Use motion to clarify causality, reveal system state, and preserve context during multi-pane technical workflows.',
|
|
413
|
+
motionChoreography: 'Prefer subtle panel transitions, command feedback, and disclosure motion. Avoid ornamental sweeps that make technical work feel imprecise.',
|
|
414
|
+
motionDurations: {
|
|
415
|
+
desktop: 170,
|
|
416
|
+
mobile: 230,
|
|
417
|
+
},
|
|
418
|
+
componentMorphology: {
|
|
419
|
+
mobile: 'Technical panes should flatten into sequential sections, with commands and diagnostics colocated near the content they affect.',
|
|
420
|
+
tablet: 'Split views should survive where useful, with explicit panel toggles and condensed chrome for code-adjacent tasks.',
|
|
421
|
+
desktop: 'Navigation, documentation, diagnostics, and active work surfaces can remain concurrently visible when it improves expert comprehension.',
|
|
422
|
+
},
|
|
390
423
|
mutationRules: {
|
|
391
424
|
mobile: 'Switch multi-pane technical layouts into stacked sections, turn secondary navigation into segmented or sheet-based controls, and keep commands near the content they affect.',
|
|
392
425
|
tablet: 'Retain split-view comprehension where possible, compress chrome, and keep documentation or diagnostics adjacent to the active task.',
|
|
@@ -409,6 +442,17 @@ function inferDesignKeywords(discoveryAnswers) {
|
|
|
409
442
|
'Use contrast and spacing to guide attention between creation, moderation, and discovery.',
|
|
410
443
|
'Give key interaction moments personality without sacrificing clarity.',
|
|
411
444
|
],
|
|
445
|
+
motionPurpose: 'Use motion to support reading rhythm, reveal structure, and reward contribution moments without overwhelming long-form consumption.',
|
|
446
|
+
motionChoreography: 'Favor reveal choreography for section transitions, lightweight feedback on participation, and measured media behavior. Avoid constant background motion.',
|
|
447
|
+
motionDurations: {
|
|
448
|
+
desktop: 190,
|
|
449
|
+
mobile: 250,
|
|
450
|
+
},
|
|
451
|
+
componentMorphology: {
|
|
452
|
+
mobile: 'Reading surfaces should dominate while secondary discovery and community tools collapse behind sheets, tabs, or segmented controls.',
|
|
453
|
+
tablet: 'Editorial modules can balance reading and discovery, provided the primary narrative flow remains obvious.',
|
|
454
|
+
desktop: 'Long-form content, secondary navigation, and related discovery modules can coexist without fragmenting the reading rhythm.',
|
|
455
|
+
},
|
|
412
456
|
mutationRules: {
|
|
413
457
|
mobile: 'Prioritize reading and contribution flows in a single-column narrative stack, tuck secondary discovery tools behind sheets, and keep primary creation actions within reach.',
|
|
414
458
|
tablet: 'Balance narrative reading with supporting discovery modules, using two-column compositions only where hierarchy stays obvious.',
|
|
@@ -430,6 +474,17 @@ function inferDesignKeywords(discoveryAnswers) {
|
|
|
430
474
|
'Use rhythm, hierarchy, and motion intentionally so the interface feels authored.',
|
|
431
475
|
'Keep the system flexible enough to evolve with product scope without losing identity.',
|
|
432
476
|
],
|
|
477
|
+
motionPurpose: 'Allow motion when it improves continuity, feedback, or perceived craft. Avoid banning motion outright, but reject decorative movement with no product value.',
|
|
478
|
+
motionChoreography: 'Use short, purposeful transitions and keep heavier choreography rare, opt-in, and explainable in product terms.',
|
|
479
|
+
motionDurations: {
|
|
480
|
+
desktop: 180,
|
|
481
|
+
mobile: 240,
|
|
482
|
+
},
|
|
483
|
+
componentMorphology: {
|
|
484
|
+
mobile: 'Primary components should simplify structure, prioritize direct tasks, and collapse supporting detail into explicit disclosure.',
|
|
485
|
+
tablet: 'Components should preserve hierarchy and task continuity while reducing density and compressing tertiary chrome.',
|
|
486
|
+
desktop: 'Components can expose richer states, denser supporting information, and broader navigation affordances without losing clarity.',
|
|
487
|
+
},
|
|
433
488
|
mutationRules: {
|
|
434
489
|
mobile: 'Stack primary tasks vertically, convert secondary navigation into thumb-friendly overlays or sheets, and simplify dense comparison layouts into progressive disclosure.',
|
|
435
490
|
tablet: 'Preserve hierarchy with fewer columns, condensed chrome, and adaptive navigation that maintains task continuity.',
|
|
@@ -491,11 +546,28 @@ function buildDesignIntentContractObject({
|
|
|
491
546
|
touchTargetMinPx: 44,
|
|
492
547
|
mutationRules: inferredKeywords.mutationRules,
|
|
493
548
|
},
|
|
549
|
+
motionSystem: {
|
|
550
|
+
allowMeaningfulMotion: true,
|
|
551
|
+
purpose: inferredKeywords.motionPurpose,
|
|
552
|
+
choreography: inferredKeywords.motionChoreography,
|
|
553
|
+
desktopDurationMs: inferredKeywords.motionDurations.desktop,
|
|
554
|
+
mobileDurationMs: inferredKeywords.motionDurations.mobile,
|
|
555
|
+
respectReducedMotion: true,
|
|
556
|
+
preferTransformAndOpacity: true,
|
|
557
|
+
avoidDecorativeMotionForItsOwnSake: true,
|
|
558
|
+
},
|
|
559
|
+
componentMorphology: {
|
|
560
|
+
requireStateBehaviorMatrix: true,
|
|
561
|
+
preserveIdentityAcrossViewports: true,
|
|
562
|
+
stateKeys: ['default', 'hover', 'focus', 'active', 'disabled', 'loading', 'error'],
|
|
563
|
+
viewportBehavior: inferredKeywords.componentMorphology,
|
|
564
|
+
},
|
|
494
565
|
experiencePrinciples: [
|
|
495
566
|
'Design must feel project-specific, not interchangeable with generic SaaS templates.',
|
|
496
567
|
'Major interface decisions must be explainable in product and user terms.',
|
|
497
568
|
'Accessibility, responsiveness, and implementation realism are non-negotiable.',
|
|
498
569
|
'Cross-viewport behavior must reorganize tasks and navigation, not just scale the desktop layout down.',
|
|
570
|
+
'Motion may add character and continuity when it improves the product experience, but it must stay purposeful, performant, and optional for reduced-motion users.',
|
|
499
571
|
],
|
|
500
572
|
forbiddenPatterns: [
|
|
501
573
|
'generic-saas-hero',
|
|
@@ -509,6 +581,8 @@ function buildDesignIntentContractObject({
|
|
|
509
581
|
requireViewportMutationRules: true,
|
|
510
582
|
requirePerceptualColorRationale: true,
|
|
511
583
|
allowHexDerivatives: true,
|
|
584
|
+
requireMotionRationale: true,
|
|
585
|
+
requireStateMorphology: true,
|
|
512
586
|
},
|
|
513
587
|
requiredDesignSections: DESIGN_REQUIRED_SECTIONS,
|
|
514
588
|
implementation: {
|
|
@@ -517,6 +591,7 @@ function buildDesignIntentContractObject({
|
|
|
517
591
|
requireDistinctVisualDirection: true,
|
|
518
592
|
requireMachineReadableContract: true,
|
|
519
593
|
requireViewportMutationRules: true,
|
|
594
|
+
requirePurposefulMotionGuidelines: true,
|
|
520
595
|
bootstrapPrompt: '.agent-context/prompts/bootstrap-design.md',
|
|
521
596
|
autoLoadedRuleFiles: [
|
|
522
597
|
'.agent-context/prompts/bootstrap-design.md',
|
|
@@ -589,6 +664,41 @@ export function validateDesignIntentContract(designIntentContract) {
|
|
|
589
664
|
}
|
|
590
665
|
}
|
|
591
666
|
|
|
667
|
+
if (!designIntentContract.motionSystem || typeof designIntentContract.motionSystem !== 'object') {
|
|
668
|
+
validationErrors.push('designIntent.motionSystem must exist.');
|
|
669
|
+
} else {
|
|
670
|
+
if (designIntentContract.motionSystem.allowMeaningfulMotion !== true) {
|
|
671
|
+
validationErrors.push('designIntent.motionSystem.allowMeaningfulMotion must equal true.');
|
|
672
|
+
}
|
|
673
|
+
if (!String(designIntentContract.motionSystem.purpose || '').trim()) {
|
|
674
|
+
validationErrors.push('designIntent.motionSystem.purpose must be a non-empty string.');
|
|
675
|
+
}
|
|
676
|
+
if (designIntentContract.motionSystem.respectReducedMotion !== true) {
|
|
677
|
+
validationErrors.push('designIntent.motionSystem.respectReducedMotion must equal true.');
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (!designIntentContract.componentMorphology || typeof designIntentContract.componentMorphology !== 'object') {
|
|
682
|
+
validationErrors.push('designIntent.componentMorphology must exist.');
|
|
683
|
+
} else {
|
|
684
|
+
if (designIntentContract.componentMorphology.requireStateBehaviorMatrix !== true) {
|
|
685
|
+
validationErrors.push('designIntent.componentMorphology.requireStateBehaviorMatrix must equal true.');
|
|
686
|
+
}
|
|
687
|
+
if (!Array.isArray(designIntentContract.componentMorphology.stateKeys) || designIntentContract.componentMorphology.stateKeys.length < 4) {
|
|
688
|
+
validationErrors.push('designIntent.componentMorphology.stateKeys must contain multiple interaction states.');
|
|
689
|
+
}
|
|
690
|
+
const viewportBehavior = designIntentContract.componentMorphology.viewportBehavior;
|
|
691
|
+
if (!viewportBehavior || typeof viewportBehavior !== 'object') {
|
|
692
|
+
validationErrors.push('designIntent.componentMorphology.viewportBehavior must exist.');
|
|
693
|
+
} else {
|
|
694
|
+
for (const viewportKey of ['mobile', 'tablet', 'desktop']) {
|
|
695
|
+
if (!String(viewportBehavior[viewportKey] || '').trim()) {
|
|
696
|
+
validationErrors.push(`designIntent.componentMorphology.viewportBehavior.${viewportKey} must be a non-empty string.`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
592
702
|
if (!Array.isArray(designIntentContract.requiredDesignSections) || designIntentContract.requiredDesignSections.length !== DESIGN_REQUIRED_SECTIONS.length) {
|
|
593
703
|
validationErrors.push('designIntent.requiredDesignSections must match the required design contract sections.');
|
|
594
704
|
} else {
|
|
@@ -764,7 +874,7 @@ function buildDesignBootstrapPrompt({
|
|
|
764
874
|
'6. Spacing, Layout Rhythm, and Density Strategy',
|
|
765
875
|
'7. Responsive Strategy and Cross-Viewport Adaptation Matrix',
|
|
766
876
|
'8. Motion and Interaction Principles',
|
|
767
|
-
'9. Component Language (cards, forms, nav, states)',
|
|
877
|
+
'9. Component Language and Morphology (cards, forms, nav, states)',
|
|
768
878
|
'10. Accessibility Non-Negotiables',
|
|
769
879
|
'11. Anti-Patterns to Avoid',
|
|
770
880
|
'12. Implementation Notes for Future UI Tasks',
|
|
@@ -780,11 +890,13 @@ function buildDesignBootstrapPrompt({
|
|
|
780
890
|
'8. mathSystems',
|
|
781
891
|
'9. colorTruth',
|
|
782
892
|
'10. crossViewportAdaptation',
|
|
783
|
-
'11.
|
|
784
|
-
'12.
|
|
785
|
-
'13.
|
|
786
|
-
'14.
|
|
787
|
-
'15.
|
|
893
|
+
'11. motionSystem',
|
|
894
|
+
'12. componentMorphology',
|
|
895
|
+
'13. experiencePrinciples',
|
|
896
|
+
'14. forbiddenPatterns',
|
|
897
|
+
'15. validationHints',
|
|
898
|
+
'16. requiredDesignSections',
|
|
899
|
+
'17. implementation',
|
|
788
900
|
'',
|
|
789
901
|
'## Hard Rules',
|
|
790
902
|
'1. No copy-paste from external style guides.',
|
|
@@ -795,7 +907,9 @@ function buildDesignBootstrapPrompt({
|
|
|
795
907
|
'6. Reject interchangeable hero layouts, generic SaaS gradients, and trend-chasing decoration unless the project context explicitly justifies them.',
|
|
796
908
|
'7. Encode color intent in perceptual terms first. Hex values may exist only as implementation derivatives.',
|
|
797
909
|
'8. Responsive guidance must include layout mutation rules for mobile, tablet, and desktop. Shrinking the desktop layout is not enough.',
|
|
798
|
-
'9.
|
|
910
|
+
'9. Motion is allowed when it reinforces feedback, continuity, or hierarchy. Do not remove motion entirely, but reject decorative motion that harms clarity or performance.',
|
|
911
|
+
'10. Define component morphology across interaction states and viewports so cards, forms, nav, and feedback surfaces adapt coherently instead of only resizing.',
|
|
912
|
+
'11. Keep UI-only requests context-isolated. Load frontend design rules first and do not eagerly load backend-only rules unless the task explicitly crosses those boundaries.',
|
|
799
913
|
'',
|
|
800
914
|
'## Project Inputs',
|
|
801
915
|
`- Project name: ${discoveryAnswers.projectName}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryuenn3123/agentic-senior-core",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Force your AI Agent to code like a Staff Engineer, not a Junior.",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,6 @@
|
|
|
43
43
|
"scripts": {
|
|
44
44
|
"init": "node ./bin/agentic-senior-core.js init",
|
|
45
45
|
"audit:frontend-usability": "node ./scripts/frontend-usability-audit.mjs",
|
|
46
|
-
"audit:ui-design-judge": "node ./scripts/ui-design-judge.mjs",
|
|
47
46
|
"audit:documentation-boundary": "node ./scripts/documentation-boundary-audit.mjs",
|
|
48
47
|
"audit:context-triggered": "node ./scripts/context-triggered-audit.mjs",
|
|
49
48
|
"audit:rules-guardian": "node ./scripts/rules-guardian-audit.mjs",
|
|
@@ -64,11 +64,14 @@ const REQUIRED_BOOTSTRAP_DESIGN_SNIPPETS = [
|
|
|
64
64
|
'Responsive Strategy and Cross-Viewport Adaptation Matrix',
|
|
65
65
|
'colorTruth.format',
|
|
66
66
|
'crossViewportAdaptation.mutationRules.mobile/tablet/desktop',
|
|
67
|
+
'motionSystem',
|
|
68
|
+
'componentMorphology',
|
|
67
69
|
];
|
|
68
70
|
|
|
69
71
|
const REQUIRED_UI_DESIGN_JUDGE_SNIPPETS = [
|
|
70
72
|
'Advisory-first UI design contract judge.',
|
|
71
|
-
'
|
|
73
|
+
'Repo-internal workflow audit; no user-facing runtime modes.',
|
|
74
|
+
'Runs only in advisory mode for this repository workflow.',
|
|
72
75
|
'Do not reward generic SaaS defaults or popular template patterns.',
|
|
73
76
|
'UI design judge only evaluates changed UI surfaces.',
|
|
74
77
|
];
|
package/scripts/mcp-server.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
6
|
import { dirname, resolve, sep } from 'node:path';
|
|
@@ -8,9 +8,6 @@ import { fileURLToPath } from 'node:url';
|
|
|
8
8
|
|
|
9
9
|
const SCRIPT_FILE_PATH = fileURLToPath(import.meta.url);
|
|
10
10
|
const REPOSITORY_ROOT = resolve(dirname(SCRIPT_FILE_PATH), '..');
|
|
11
|
-
const PACKAGE_VERSION = JSON.parse(
|
|
12
|
-
readFileSync(resolve(REPOSITORY_ROOT, 'package.json'), 'utf8')
|
|
13
|
-
).version;
|
|
14
11
|
const STATE_DIRECTORY = resolve(REPOSITORY_ROOT, '.agent-context', 'state');
|
|
15
12
|
const DEFAULT_PROTOCOL_VERSION = '2024-11-05';
|
|
16
13
|
const DEFAULT_FETCH_TIMEOUT_MS = 15000;
|
|
@@ -18,6 +15,24 @@ const DEFAULT_FETCH_MAX_CHARS = 6000;
|
|
|
18
15
|
const MAX_FETCH_MAX_CHARS = 20000;
|
|
19
16
|
const DEFAULT_TREND_WINDOW_DAYS = 90;
|
|
20
17
|
const MAX_TREND_PACKAGES = 10;
|
|
18
|
+
const FALLBACK_PACKAGE_VERSION = '0.0.0-local';
|
|
19
|
+
|
|
20
|
+
function resolvePackageVersion() {
|
|
21
|
+
try {
|
|
22
|
+
const parsedPackageManifest = JSON.parse(
|
|
23
|
+
readFileSync(resolve(REPOSITORY_ROOT, 'package.json'), 'utf8')
|
|
24
|
+
);
|
|
25
|
+
const rawVersion = typeof parsedPackageManifest?.version === 'string'
|
|
26
|
+
? parsedPackageManifest.version.trim()
|
|
27
|
+
: '';
|
|
28
|
+
|
|
29
|
+
return rawVersion || FALLBACK_PACKAGE_VERSION;
|
|
30
|
+
} catch {
|
|
31
|
+
return FALLBACK_PACKAGE_VERSION;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const PACKAGE_VERSION = resolvePackageVersion();
|
|
21
36
|
|
|
22
37
|
const TEST_SUITE_ARGS = {
|
|
23
38
|
full: ['--test', './tests/cli-smoke.test.mjs', './tests/mcp-server.test.mjs', './tests/llm-judge.test.mjs', './tests/enterprise-ops.test.mjs'],
|
|
@@ -26,132 +41,172 @@ const TEST_SUITE_ARGS = {
|
|
|
26
41
|
'llm-judge': ['--test', './tests/llm-judge.test.mjs'],
|
|
27
42
|
};
|
|
28
43
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
const INTERNAL_SCRIPT_PATHS = {
|
|
45
|
+
validate: resolve(REPOSITORY_ROOT, 'scripts', 'validate.mjs'),
|
|
46
|
+
release_gate: resolve(REPOSITORY_ROOT, 'scripts', 'release-gate.mjs'),
|
|
47
|
+
forbidden_content_check: resolve(REPOSITORY_ROOT, 'scripts', 'forbidden-content-check.mjs'),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function getAvailableTestSuites() {
|
|
51
|
+
return Object.entries(TEST_SUITE_ARGS)
|
|
52
|
+
.filter(([, commandArguments]) => (
|
|
53
|
+
Array.isArray(commandArguments)
|
|
54
|
+
&& commandArguments.length > 1
|
|
55
|
+
&& commandArguments
|
|
56
|
+
.slice(1)
|
|
57
|
+
.every((relativeTestPath) => existsSync(resolve(REPOSITORY_ROOT, relativeTestPath)))
|
|
58
|
+
))
|
|
59
|
+
.map(([suiteName]) => suiteName);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const AVAILABLE_TEST_SUITES = getAvailableTestSuites();
|
|
63
|
+
|
|
64
|
+
function buildToolDefinitions() {
|
|
65
|
+
const toolDefinitions = [];
|
|
66
|
+
|
|
67
|
+
if (existsSync(INTERNAL_SCRIPT_PATHS.validate)) {
|
|
68
|
+
toolDefinitions.push({
|
|
69
|
+
name: 'validate',
|
|
70
|
+
description: 'Run repository validation checks.',
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {},
|
|
74
|
+
additionalProperties: false,
|
|
50
75
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
{
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
inputSchema: {
|
|
67
|
-
type: 'object',
|
|
68
|
-
properties: {},
|
|
69
|
-
additionalProperties: false,
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
name: 'research_fetch',
|
|
74
|
-
description: 'Fetch external documentation/news content and return query-focused excerpts with citation metadata.',
|
|
75
|
-
inputSchema: {
|
|
76
|
-
type: 'object',
|
|
77
|
-
properties: {
|
|
78
|
-
url: {
|
|
79
|
-
type: 'string',
|
|
80
|
-
description: 'Absolute HTTP/HTTPS URL to fetch.',
|
|
81
|
-
},
|
|
82
|
-
query: {
|
|
83
|
-
type: 'string',
|
|
84
|
-
description: 'Optional search query used to extract focused excerpts.',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (AVAILABLE_TEST_SUITES.length > 0) {
|
|
80
|
+
toolDefinitions.push({
|
|
81
|
+
name: 'test',
|
|
82
|
+
description: 'Run test suites (full or targeted).',
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
suite: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
enum: AVAILABLE_TEST_SUITES,
|
|
89
|
+
description: 'Target test suite. Defaults to the first available suite.',
|
|
90
|
+
},
|
|
85
91
|
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (existsSync(INTERNAL_SCRIPT_PATHS.release_gate)) {
|
|
98
|
+
toolDefinitions.push({
|
|
99
|
+
name: 'release_gate',
|
|
100
|
+
description: 'Run release gate checks.',
|
|
101
|
+
inputSchema: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
properties: {},
|
|
104
|
+
additionalProperties: false,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (existsSync(INTERNAL_SCRIPT_PATHS.forbidden_content_check)) {
|
|
110
|
+
toolDefinitions.push({
|
|
111
|
+
name: 'forbidden_content_check',
|
|
112
|
+
description: 'Run forbidden content scan used by publish gate.',
|
|
113
|
+
inputSchema: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: {},
|
|
116
|
+
additionalProperties: false,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
toolDefinitions.push(
|
|
122
|
+
{
|
|
123
|
+
name: 'research_fetch',
|
|
124
|
+
description: 'Fetch external documentation/news content and return query-focused excerpts with citation metadata.',
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {
|
|
128
|
+
url: {
|
|
129
|
+
type: 'string',
|
|
130
|
+
description: 'Absolute HTTP/HTTPS URL to fetch.',
|
|
131
|
+
},
|
|
132
|
+
query: {
|
|
133
|
+
type: 'string',
|
|
134
|
+
description: 'Optional search query used to extract focused excerpts.',
|
|
135
|
+
},
|
|
136
|
+
maxChars: {
|
|
137
|
+
type: 'integer',
|
|
138
|
+
description: 'Maximum characters to return when query is not provided (default 6000, max 20000).',
|
|
139
|
+
},
|
|
89
140
|
},
|
|
141
|
+
required: ['url'],
|
|
142
|
+
additionalProperties: false,
|
|
90
143
|
},
|
|
91
|
-
required: ['url'],
|
|
92
|
-
additionalProperties: false,
|
|
93
144
|
},
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
145
|
+
{
|
|
146
|
+
name: 'trend_snapshot',
|
|
147
|
+
description: 'Generate ecosystem trend snapshot from npm registry metadata with source timestamps.',
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: 'object',
|
|
150
|
+
properties: {
|
|
151
|
+
packages: {
|
|
152
|
+
type: 'array',
|
|
153
|
+
items: { type: 'string' },
|
|
154
|
+
description: 'Package names to inspect (max 10).',
|
|
155
|
+
},
|
|
156
|
+
windowDays: {
|
|
157
|
+
type: 'integer',
|
|
158
|
+
description: 'Release activity window in days (default 90).',
|
|
159
|
+
},
|
|
109
160
|
},
|
|
161
|
+
required: ['packages'],
|
|
162
|
+
additionalProperties: false,
|
|
110
163
|
},
|
|
111
|
-
required: ['packages'],
|
|
112
|
-
additionalProperties: false,
|
|
113
164
|
},
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
165
|
+
{
|
|
166
|
+
name: 'state_read',
|
|
167
|
+
description: 'Read a file from .agent-context/state for cross-session continuity.',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
path: {
|
|
172
|
+
type: 'string',
|
|
173
|
+
description: 'Path relative to .agent-context/state (for example memory-continuity-benchmark.json).',
|
|
174
|
+
},
|
|
124
175
|
},
|
|
176
|
+
required: ['path'],
|
|
177
|
+
additionalProperties: false,
|
|
125
178
|
},
|
|
126
|
-
required: ['path'],
|
|
127
|
-
additionalProperties: false,
|
|
128
179
|
},
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
180
|
+
{
|
|
181
|
+
name: 'state_write',
|
|
182
|
+
description: 'Write a file under .agent-context/state for cross-session continuity updates.',
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: 'object',
|
|
185
|
+
properties: {
|
|
186
|
+
path: {
|
|
187
|
+
type: 'string',
|
|
188
|
+
description: 'Path relative to .agent-context/state.',
|
|
189
|
+
},
|
|
190
|
+
content: {
|
|
191
|
+
type: 'string',
|
|
192
|
+
description: 'UTF-8 content to write.',
|
|
193
|
+
},
|
|
194
|
+
mode: {
|
|
195
|
+
type: 'string',
|
|
196
|
+
enum: ['overwrite', 'append'],
|
|
197
|
+
description: 'Write mode. Defaults to overwrite.',
|
|
198
|
+
},
|
|
148
199
|
},
|
|
200
|
+
required: ['path', 'content'],
|
|
201
|
+
additionalProperties: false,
|
|
149
202
|
},
|
|
150
|
-
required: ['path', 'content'],
|
|
151
|
-
additionalProperties: false,
|
|
152
203
|
},
|
|
153
|
-
|
|
154
|
-
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
return toolDefinitions;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const TOOL_DEFINITIONS = buildToolDefinitions();
|
|
155
210
|
|
|
156
211
|
let incomingBuffer = Buffer.alloc(0);
|
|
157
212
|
|
|
@@ -539,23 +594,50 @@ function runNodeCommand(commandLabel, commandArguments) {
|
|
|
539
594
|
|
|
540
595
|
async function executeToolCall(toolName, toolArguments = {}) {
|
|
541
596
|
if (toolName === 'validate') {
|
|
597
|
+
if (!existsSync(INTERNAL_SCRIPT_PATHS.validate)) {
|
|
598
|
+
return buildJsonResult({
|
|
599
|
+
error: 'validate tool is unavailable because scripts/validate.mjs is missing in this workspace.',
|
|
600
|
+
}, true);
|
|
601
|
+
}
|
|
602
|
+
|
|
542
603
|
return runNodeCommand('validate', ['./scripts/validate.mjs']);
|
|
543
604
|
}
|
|
544
605
|
|
|
545
606
|
if (toolName === 'test') {
|
|
607
|
+
if (AVAILABLE_TEST_SUITES.length === 0) {
|
|
608
|
+
return buildJsonResult({
|
|
609
|
+
error: 'test tool is unavailable because the managed test suites are not present in this workspace.',
|
|
610
|
+
}, true);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const defaultSuite = AVAILABLE_TEST_SUITES[0];
|
|
546
614
|
const requestedSuite = typeof toolArguments.suite === 'string'
|
|
547
615
|
? toolArguments.suite
|
|
548
|
-
:
|
|
616
|
+
: defaultSuite;
|
|
549
617
|
|
|
550
|
-
const selectedSuite =
|
|
618
|
+
const selectedSuite = AVAILABLE_TEST_SUITES.includes(requestedSuite)
|
|
619
|
+
? requestedSuite
|
|
620
|
+
: defaultSuite;
|
|
551
621
|
return runNodeCommand(`test:${selectedSuite}`, TEST_SUITE_ARGS[selectedSuite]);
|
|
552
622
|
}
|
|
553
623
|
|
|
554
624
|
if (toolName === 'release_gate') {
|
|
625
|
+
if (!existsSync(INTERNAL_SCRIPT_PATHS.release_gate)) {
|
|
626
|
+
return buildJsonResult({
|
|
627
|
+
error: 'release_gate tool is unavailable because scripts/release-gate.mjs is missing in this workspace.',
|
|
628
|
+
}, true);
|
|
629
|
+
}
|
|
630
|
+
|
|
555
631
|
return runNodeCommand('release_gate', ['./scripts/release-gate.mjs']);
|
|
556
632
|
}
|
|
557
633
|
|
|
558
634
|
if (toolName === 'forbidden_content_check') {
|
|
635
|
+
if (!existsSync(INTERNAL_SCRIPT_PATHS.forbidden_content_check)) {
|
|
636
|
+
return buildJsonResult({
|
|
637
|
+
error: 'forbidden_content_check tool is unavailable because scripts/forbidden-content-check.mjs is missing in this workspace.',
|
|
638
|
+
}, true);
|
|
639
|
+
}
|
|
640
|
+
|
|
559
641
|
return runNodeCommand('forbidden_content_check', ['./scripts/forbidden-content-check.mjs']);
|
|
560
642
|
}
|
|
561
643
|
|